blob: fb859aadca6a7c73399ab56ed4c09b5ec5c2d618 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//go:build !build_with_native_toolchain
package udp_serde
import (
fnet "fidl/fuchsia/net"
"fmt"
"math"
"reflect"
"time"
"unsafe"
"go.fuchsia.dev/fuchsia/src/connectivity/network/netstack/fidlconv"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
// #cgo LDFLAGS: -ludp_serde_for_cgo -lc++ -lzircon
// #include <ifaddrs.h>
// #include <string.h>
// #include "udp_serde.h"
import "C"
func TxUdpPreludeSize() uint32 {
return uint32(C.kTxUdpPreludeSize)
}
func RxUdpPreludeSize() uint32 {
return uint32(C.kRxUdpPreludeSize)
}
func convertDeserializeSendMsgMetaErr(err C.DeserializeSendMsgMetaError) error {
switch err {
case C.DeserializeSendMsgMetaErrorInputBufferNull:
return &InputBufferNullErr{}
case C.DeserializeSendMsgMetaErrorInputBufferTooSmall:
return &InputBufferTooSmallErr{}
case C.DeserializeSendMsgMetaErrorFailedToDecode:
return &FailedToDecodeErr{}
default:
panic(fmt.Sprintf("unknown deserialization result %#v", err))
}
}
func getFidlAddrTypeAndSlice(fidlAddr fnet.SocketAddress) (C.IpAddrType, []byte) {
switch w := fidlAddr.Which(); w {
case fnet.SocketAddressIpv4:
return C.Ipv4, fidlAddr.Ipv4.Address.Addr[:]
case fnet.SocketAddressIpv6:
return C.Ipv6, fidlAddr.Ipv6.Address.Addr[:]
default:
panic(fmt.Sprintf("unrecognized socket address %d", w))
}
}
// DeserializeSendMsgMeta deserializes metadata contained within `buf`
// as a SendMsgMeta FIDL message using the LLCPP bindings.
//
// If the deserialized metadata contains an address, returns that address (else returns nil).
// Returns any found control messages present within a `tcpip.SendableControlMessages` struct.
func DeserializeSendMsgMeta(buf []byte) (*tcpip.FullAddress, tcpip.SendableControlMessages, error) {
bufIn := C.Buffer{
buf: (*C.uchar)(unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(&buf))).Data)),
buf_size: C.ulong(len(buf)),
}
res := C.deserialize_send_msg_meta(bufIn)
if res.err != C.DeserializeSendMsgMetaErrorNone {
return nil, tcpip.SendableControlMessages{}, convertDeserializeSendMsgMetaErr(res.err)
}
addr := func() *tcpip.FullAddress {
if res.has_addr {
var addr tcpip.Address
switch res.addr.addr_type {
case C.Ipv4:
src := res.addr.addr[:header.IPv4AddressSize]
addr = fidlconv.BytesToAddressDroppingUnspecified(*(*[]byte)(unsafe.Pointer(&src)))
case C.Ipv6:
src := res.addr.addr[:]
addr = fidlconv.BytesToAddressDroppingUnspecified(*(*[]byte)(unsafe.Pointer(&src)))
}
return &tcpip.FullAddress{
Addr: addr,
Port: uint16(res.port),
NIC: tcpip.NICID(res.zone_index),
}
}
return nil
}()
var cmsgSet tcpip.SendableControlMessages
if res.cmsg_set.has_ip_ttl {
cmsgSet.HasTTL = true
cmsgSet.TTL = uint8(res.cmsg_set.ip_ttl)
}
if res.cmsg_set.has_ipv6_hoplimit {
cmsgSet.HasHopLimit = true
cmsgSet.HopLimit = uint8(res.cmsg_set.ipv6_hoplimit)
}
if res.cmsg_set.has_ipv6_pktinfo {
cmsgSet.HasIPv6PacketInfo = true
src := res.cmsg_set.ipv6_pktinfo.addr[:]
cmsgSet.IPv6PacketInfo = tcpip.IPv6PacketInfo{
NIC: tcpip.NICID(res.cmsg_set.ipv6_pktinfo.if_index),
Addr: fidlconv.BytesToAddressDroppingUnspecified(*(*[]byte)(unsafe.Pointer(&src))),
}
}
return addr, cmsgSet, nil
}
func convertSerializeRecvMsgMetaErr(err C.SerializeRecvMsgMetaError) error {
switch err {
case C.SerializeRecvMsgMetaErrorNone:
return nil
case C.SerializeRecvMsgMetaErrorOutputBufferNull:
return &InputBufferNullErr{}
case C.SerializeRecvMsgMetaErrorOutputBufferTooSmall:
return &InputBufferTooSmallErr{}
case C.SerializeRecvMsgMetaErrorFromAddrBufferNull:
panic(fmt.Sprintf("got unexpected C.SerializeRecvMsgMetaErrorFromAddrBufferNull error"))
case C.SerializeRecvMsgMetaErrorFromAddrBufferTooSmall:
panic(fmt.Sprintf("got unexpected C.SerializeRecvMsgMetaErrorFromAddrBufferTooSmall error"))
case C.SerializeRecvMsgMetaErrorFailedToEncode:
return &FailedToEncodeErr{}
default:
panic(fmt.Sprintf("unknown deserialization result %#v", err))
}
}
// SerializeRecvMsgMeta serializes metadata contained within `res` into `buf`
// as a RecvMsgMeta FIDL message using the LLCPP bindings.
func SerializeRecvMsgMeta(protocol tcpip.NetworkProtocolNumber, res tcpip.ReadResult, buf []byte) error {
fidlAddr := fidlconv.ToNetSocketAddressWithProto(protocol, res.RemoteAddr)
fromAddrType, addrSlice := getFidlAddrTypeAndSlice(fidlAddr)
addrBuf := C.ConstBuffer{
buf: (*C.uchar)(unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(&addrSlice))).Data)),
buf_size: C.ulong(len(addrSlice)),
}
bufOut := C.Buffer{
buf: (*C.uchar)(unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(&buf))).Data)),
buf_size: C.ulong(len(buf)),
}
if res.Count > int(math.MaxUint16) {
return &PayloadSizeExceedsMaxAllowedErr{payloadSize: res.Count, maxAllowed: math.MaxUint16}
}
recv_meta := C.RecvMsgMeta{
cmsg_set: C.RecvCmsgSet{
send_and_recv: C.SendAndRecvCmsgSet{
has_ip_ttl: C.bool(res.ControlMessages.HasTTL),
ip_ttl: C.uchar(res.ControlMessages.TTL),
has_ipv6_hoplimit: C.bool(res.ControlMessages.HasHopLimit),
ipv6_hoplimit: C.uchar(res.ControlMessages.HopLimit),
has_ipv6_pktinfo: C.bool(res.ControlMessages.HasIPv6PacketInfo),
ipv6_pktinfo: C.Ipv6PktInfo{
if_index: C.ulong(res.ControlMessages.IPv6PacketInfo.NIC),
},
},
has_timestamp_nanos: C.bool(res.ControlMessages.HasTimestamp),
timestamp_nanos: C.long(res.ControlMessages.Timestamp.UnixNano()),
has_ip_tos: C.bool(res.ControlMessages.HasTOS),
ip_tos: C.uchar(res.ControlMessages.TOS),
has_ipv6_tclass: C.bool(res.ControlMessages.HasTClass),
ipv6_tclass: C.uchar(res.ControlMessages.TClass),
},
addr_type: fromAddrType,
payload_size: C.ushort(res.Count),
port: C.ushort(res.RemoteAddr.Port),
}
if fidlAddr.Which() == fnet.SocketAddressIpv6 {
recv_meta.zone_index = C.ulong(fidlAddr.Ipv6.ZoneIndex)
}
if res.ControlMessages.HasIPv6PacketInfo {
dst := recv_meta.cmsg_set.send_and_recv.ipv6_pktinfo.addr[:]
copy(*(*[]byte)(unsafe.Pointer(&dst)), res.ControlMessages.IPv6PacketInfo.Addr.AsSlice())
}
return convertSerializeRecvMsgMetaErr(C.serialize_recv_msg_meta(&recv_meta, addrBuf, bufOut))
}
func convertDeserializeRecvMsgMetaErr(err C.DeserializeRecvMsgMetaError) error {
switch err {
case C.DeserializeRecvMsgMetaErrorNone:
return nil
case C.DeserializeRecvMsgMetaErrorInputBufferNull:
return &InputBufferNullErr{}
case C.DeserializeRecvMsgMetaErrorUnspecifiedDecodingFailure:
return &UnspecifiedDecodingFailure{}
default:
panic(fmt.Sprintf("unknown deserialization error: %#v", err))
}
}
type RecvMsgMeta struct {
addr *tcpip.FullAddress
control tcpip.ReceivableControlMessages
payloadSize uint16
}
// DeserializeRecvMsgMeta deserializes metadata contained within `buf`
// as a RecvMsgMeta FIDL message using the LLCPP bindings.
//
// If the deserialized metadata contains an address, returns that address (else returns nil).
// Returns any found control messages present within a `tcpip.ReceiveableControlMessages` struct.
//
// This method is only intended to be used in tests.
// TODO(https://fxbug.dev/42059260): Isolate testonly methods.
func DeserializeRecvMsgMeta(buf []byte) (RecvMsgMeta, error) {
bufIn := C.Buffer{
buf: (*C.uchar)(unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(&buf))).Data)),
buf_size: C.ulong(len(buf)),
}
res := C.deserialize_recv_msg_meta(bufIn)
if res.err != C.DeserializeRecvMsgMetaErrorNone {
return RecvMsgMeta{}, convertDeserializeRecvMsgMetaErr(res.err)
}
addr := func() *tcpip.FullAddress {
if res.has_addr {
var addr tcpip.Address
switch res.addr.addr_type {
case C.Ipv4:
src := res.addr.addr[:header.IPv4AddressSize]
addr = fidlconv.BytesToAddressDroppingUnspecified(*(*[]byte)(unsafe.Pointer(&src)))
case C.Ipv6:
src := res.addr.addr[:]
addr = fidlconv.BytesToAddressDroppingUnspecified(*(*[]byte)(unsafe.Pointer(&src)))
}
return &tcpip.FullAddress{
Addr: addr,
Port: uint16(res.port),
NIC: tcpip.NICID(res.zone_index),
}
}
return nil
}
var cmsgSet tcpip.ReceivableControlMessages
if res.cmsg_set.has_timestamp_nanos {
cmsgSet.HasTimestamp = true
cmsgSet.Timestamp = time.Unix(0, int64(res.cmsg_set.timestamp_nanos))
}
if res.cmsg_set.has_ip_tos {
cmsgSet.HasTOS = true
cmsgSet.TOS = uint8(res.cmsg_set.ip_tos)
}
if res.cmsg_set.has_ipv6_tclass {
cmsgSet.HasTClass = true
cmsgSet.TClass = uint32(res.cmsg_set.ipv6_tclass)
}
if res.cmsg_set.send_and_recv.has_ip_ttl {
cmsgSet.HasTTL = true
cmsgSet.TTL = uint8(res.cmsg_set.send_and_recv.ip_ttl)
}
if res.cmsg_set.send_and_recv.has_ipv6_hoplimit {
cmsgSet.HasHopLimit = true
cmsgSet.HopLimit = uint8(res.cmsg_set.send_and_recv.ipv6_hoplimit)
}
if res.cmsg_set.send_and_recv.has_ipv6_pktinfo {
cmsgSet.HasIPv6PacketInfo = true
src := res.cmsg_set.send_and_recv.ipv6_pktinfo.addr[:]
cmsgSet.IPv6PacketInfo = tcpip.IPv6PacketInfo{
NIC: tcpip.NICID(res.cmsg_set.send_and_recv.ipv6_pktinfo.if_index),
Addr: fidlconv.BytesToAddressDroppingUnspecified(*(*[]byte)(unsafe.Pointer(&src))),
}
}
return RecvMsgMeta{
addr: addr(),
control: cmsgSet,
payloadSize: uint16(res.payload_size),
}, nil
}
func convertSerializeSendMsgMetaErr(err C.SerializeSendMsgMetaError) error {
switch err {
case C.SerializeSendMsgMetaErrorNone:
return nil
case C.SerializeSendMsgMetaErrorOutputBufferNull:
return &InputBufferNullErr{}
case C.SerializeSendMsgMetaErrorOutputBufferTooSmall:
return &InputBufferTooSmallErr{}
case C.SerializeSendMsgMetaErrorFailedToEncode:
return &FailedToEncodeErr{}
case C.SerializeSendMsgMetaErrorAddrBufferNull:
panic(fmt.Sprintf("got unexpected C.SerializeSendMsgMetaErrorAddrBufferNull error"))
case C.SerializeSendMsgMetaErrorAddrBufferSizeMismatch:
panic(fmt.Sprintf("got unexpected C.SerializeSendMsgMetaErrorAddrBufferSizeMismatch error"))
default:
panic(fmt.Sprintf("unknown serialization result %#v", err))
}
}
// SerializeSendMsgMeta serializes `addr` and `cmsg_set` into `buf` as a SendMsgMeta FIDL message
// using the LLCPP bindings.
//
// This method is only intended to be used in tests.
// TODO(https://fxbug.dev/42059260): Isolate testonly methods.
func SerializeSendMsgMeta(protocol tcpip.NetworkProtocolNumber, addr tcpip.FullAddress, cmsgSet tcpip.SendableControlMessages, buf []byte) error {
fidlAddr := fidlconv.ToNetSocketAddressWithProto(protocol, addr)
fromAddrType, addrSlice := getFidlAddrTypeAndSlice(fidlAddr)
meta := C.SendMsgMeta{
addr_type: fromAddrType,
port: C.ushort(addr.Port),
cmsg_set: C.SendAndRecvCmsgSet{
has_ip_ttl: C.bool(cmsgSet.HasTTL),
ip_ttl: C.uchar(cmsgSet.TTL),
has_ipv6_hoplimit: C.bool(cmsgSet.HasHopLimit),
ipv6_hoplimit: C.uchar(cmsgSet.HopLimit),
has_ipv6_pktinfo: C.bool(cmsgSet.HasIPv6PacketInfo),
ipv6_pktinfo: C.Ipv6PktInfo{
if_index: C.ulong(cmsgSet.IPv6PacketInfo.NIC),
},
},
}
if fidlAddr.Which() == fnet.SocketAddressIpv6 {
meta.zone_index = C.ulong(fidlAddr.Ipv6.ZoneIndex)
}
addrBuf := C.ConstBuffer{
buf: (*C.uchar)(unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(&addrSlice))).Data)),
buf_size: C.ulong(len(addrSlice)),
}
if cmsgSet.HasIPv6PacketInfo {
dst := meta.cmsg_set.ipv6_pktinfo.addr[:]
copy(*(*[]byte)(unsafe.Pointer(&dst)), cmsgSet.IPv6PacketInfo.Addr.AsSlice())
}
bufOut := C.Buffer{
buf: (*C.uchar)(unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(&buf))).Data)),
buf_size: C.ulong(len(buf)),
}
return convertSerializeSendMsgMetaErr(C.serialize_send_msg_meta(&meta, addrBuf, bufOut))
}