| // Copyright 2017 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. |
| |
| // Ad-hoc unsafe encoding/decoding of C data types used by the |
| // protocol libc talks to netstack. |
| |
| package netstack |
| |
| import ( |
| "encoding/binary" |
| "fmt" |
| "reflect" |
| "unsafe" |
| |
| "github.com/google/netstack/tcpip" |
| "github.com/google/netstack/tcpip/header" |
| "github.com/google/netstack/tcpip/network/ipv4" |
| "github.com/google/netstack/tcpip/network/ipv6" |
| ) |
| |
| // #cgo CFLAGS: -D_GNU_SOURCE |
| // #cgo CFLAGS: -I${SRCDIR}/../../../../zircon/system/ulib/zxs/include |
| // #cgo CFLAGS: -I${SRCDIR}/../../../../zircon/third_party/ulib/musl/include/ |
| // #cgo CFLAGS: -I${SRCDIR}/../../../public |
| // #include <lib/zxs/protocol.h> |
| // #include <netinet/tcp.h> |
| // #include <lib/netstack/c/netconfig.h> |
| import "C" |
| |
| // copyAsBytes exists because of a combination of issues: |
| // |
| // 1: cgo omits bitfields of sizes that don't have a corresponding Go type. |
| // Note that padding is still consistent with C, which is why reflect.Type.Size |
| // produces the correct result. |
| // |
| // 2: encoding/binary.{Size,Write} ignores padding. |
| // |
| // Therefore, since we know our endianness is the same as the decoder's, we can |
| // do a terrible thing and just interpret the struct as a byte array. |
| func copyAsBytes(b []byte, val interface{}) int { |
| l := int(reflect.TypeOf(val).Size()) |
| v := reflect.Indirect(reflect.ValueOf(&val)) |
| return copy(b, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ |
| Data: v.InterfaceData()[1], |
| Len: l, |
| Cap: l, |
| }))) |
| } |
| |
| func (v *C.struct_fdio_socket_msg) Unmarshal(data []byte) error { |
| const size = C.FDIO_SOCKET_MSG_HEADER_SIZE |
| |
| if n := copy((*[size]byte)(unsafe.Pointer(v))[:], data); n < size { |
| return fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return nil |
| } |
| |
| func (v *C.struct_fdio_socket_msg) MarshalTo(data []byte) (int, error) { |
| const size = C.FDIO_SOCKET_MSG_HEADER_SIZE |
| |
| n := copy(data, (*[size]byte)(unsafe.Pointer(v))[:]) |
| if n < size { |
| return 0, fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return n, nil |
| } |
| |
| func (v *C.struct_zxrio_sockopt_req_reply) opt() []byte { |
| b := v.optval[:] |
| return *(*[]byte)(unsafe.Pointer(&b)) |
| } |
| |
| func (v *C.struct_zxrio_sockopt_req_reply) Unmarshal(data []byte) error { |
| const size = C.sizeof_struct_zxrio_sockopt_req_reply |
| |
| if n := copy((*[size]byte)(unsafe.Pointer(v))[:], data); n < size { |
| return fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return nil |
| } |
| |
| func (v *C.struct_zxrio_sockopt_req_reply) MarshalTo(data []byte) (int, error) { |
| const size = C.sizeof_struct_zxrio_sockopt_req_reply |
| |
| n := copy(data, (*[size]byte)(unsafe.Pointer(v))[:]) |
| if n < size { |
| return 0, fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return n, nil |
| } |
| |
| func (v *C.struct_zxrio_sockaddr_reply) MarshalTo(data []byte) (int, error) { |
| const size = C.sizeof_struct_zxrio_sockaddr_reply |
| |
| n := copy(data, (*[size]byte)(unsafe.Pointer(v))[:]) |
| if n < size { |
| return 0, fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return n, nil |
| } |
| |
| func (v *C.struct_ip_mreq) Unmarshal(data []byte) error { |
| const size = C.sizeof_struct_ip_mreq |
| |
| if n := copy((*[size]byte)(unsafe.Pointer(v))[:], data); n < size { |
| return fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return nil |
| } |
| |
| func (v *C.struct_ip_mreqn) Unmarshal(data []byte) error { |
| const size = C.sizeof_struct_ip_mreqn |
| |
| if n := copy((*[size]byte)(unsafe.Pointer(v))[:], data); n < size { |
| return fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return nil |
| } |
| |
| func (v *C.struct_ipv6_mreq) Unmarshal(data []byte) error { |
| const size = C.sizeof_struct_ipv6_mreq |
| |
| if n := copy((*[size]byte)(unsafe.Pointer(v))[:], data); n < size { |
| return fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return nil |
| } |
| |
| func (v *C.netc_if_info_t) Marshal() []byte { |
| const size = C.sizeof_netc_if_info_t |
| |
| b := make([]byte, size) |
| if n := copy(b, (*[size]byte)(unsafe.Pointer(v))[:]); n < size { |
| panic(fmt.Sprintf("short %T: %d/%d", v, n, size)) |
| } |
| return b |
| } |
| |
| func (v *C.netc_if_info_t) MarshalTo(data []byte) (int, error) { |
| const size = C.sizeof_netc_if_info_t |
| |
| n := copy(data, (*[size]byte)(unsafe.Pointer(v))[:]) |
| if n < size { |
| return 0, fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| return n, nil |
| } |
| |
| func (v *C.struct_in_addr) Bytes() []byte { |
| return (*[C.sizeof_struct_in_addr]byte)(unsafe.Pointer(v))[:] |
| } |
| |
| func (v *C.struct_in6_addr) Bytes() []byte { |
| return (*[C.sizeof_struct_in6_addr]byte)(unsafe.Pointer(v))[:] |
| } |
| |
| func (v *C.in_port_t) Bytes() []byte { |
| return (*[C.sizeof_in_port_t]byte)(unsafe.Pointer(v))[:] |
| } |
| |
| func (v *C.struct_sockaddr_storage) Unmarshal(data []byte) error { |
| const size = C.sizeof_struct_sockaddr_storage |
| |
| n := copy((*[size]byte)(unsafe.Pointer(v))[:], data) |
| switch v.ss_family { |
| case C.AF_INET: |
| if size := C.sizeof_struct_sockaddr_in; n < size { |
| return fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| case C.AF_INET6: |
| if size := C.sizeof_struct_sockaddr_in6; n < size { |
| return fmt.Errorf("short %T: %d/%d", v, n, size) |
| } |
| default: |
| return fmt.Errorf("unknown sockaddr_storage.ss_family: %d", v.ss_family) |
| } |
| return nil |
| } |
| |
| func isZeros(b []byte) bool { |
| for _, b := range b { |
| if b != 0 { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func (v *C.struct_sockaddr_storage) Decode() (tcpip.FullAddress, error) { |
| switch v.ss_family { |
| case C.AF_INET: |
| v := (*C.struct_sockaddr_in)(unsafe.Pointer(v)) |
| out := tcpip.FullAddress{ |
| Port: binary.BigEndian.Uint16(v.sin_port.Bytes()), |
| } |
| if b := v.sin_addr.Bytes(); !isZeros(b) { |
| out.Addr = tcpip.Address(b) |
| } |
| return out, nil |
| case C.AF_INET6: |
| v := (*C.struct_sockaddr_in6)(unsafe.Pointer(v)) |
| out := tcpip.FullAddress{ |
| Port: binary.BigEndian.Uint16(v.sin6_port.Bytes()), |
| } |
| if b := v.sin6_addr.Bytes(); !isZeros(b) { |
| out.Addr = tcpip.Address(b) |
| } |
| if isLinkLocal(out.Addr) { |
| out.NIC = tcpip.NICID(v.sin6_scope_id) |
| } |
| return out, nil |
| default: |
| return tcpip.FullAddress{}, fmt.Errorf("unknown sockaddr_storage.ss_family: %d", v.ss_family) |
| } |
| } |
| |
| func (v *C.struct_sockaddr_storage) Encode(netProto tcpip.NetworkProtocolNumber, addr tcpip.FullAddress) int { |
| switch netProto { |
| case ipv4.ProtocolNumber: |
| v := (*C.struct_sockaddr_in)(unsafe.Pointer(v)) |
| copy(v.sin_addr.Bytes(), addr.Addr) |
| v.sin_family = C.AF_INET |
| binary.BigEndian.PutUint16(v.sin_port.Bytes(), addr.Port) |
| return C.sizeof_struct_sockaddr_in |
| case ipv6.ProtocolNumber: |
| v := (*C.struct_sockaddr_in6)(unsafe.Pointer(v)) |
| if len(addr.Addr) == header.IPv4AddressSize { |
| // Copy address in v4-mapped format. |
| copy(v.sin6_addr.Bytes()[header.IPv6AddressSize-header.IPv4AddressSize:], addr.Addr) |
| v.sin6_addr.Bytes()[header.IPv6AddressSize-header.IPv4AddressSize-1] = 0xff |
| v.sin6_addr.Bytes()[header.IPv6AddressSize-header.IPv4AddressSize-2] = 0xff |
| } else { |
| copy(v.sin6_addr.Bytes(), addr.Addr) |
| } |
| v.sin6_family = C.AF_INET6 |
| binary.BigEndian.PutUint16(v.sin6_port.Bytes(), addr.Port) |
| if isLinkLocal(addr.Addr) { |
| v.sin6_scope_id = C.uint32_t(addr.NIC) |
| } |
| return C.sizeof_struct_sockaddr_in6 |
| default: |
| panic(fmt.Sprintf("unknown network protocol number: %v", netProto)) |
| } |
| } |
| |
| func encodeAddr(netProto tcpip.NetworkProtocolNumber, addr tcpip.FullAddress) []uint8 { |
| var v C.struct_sockaddr_storage |
| n := v.Encode(netProto, addr) |
| return (*[C.sizeof_struct_sockaddr_storage]byte)(unsafe.Pointer(&v))[:n] |
| } |