Skip to content

Commit

Permalink
unix: implement L2TPIP socket address on Linux
Browse files Browse the repository at this point in the history
Adds constants and types to support IP-encapsulated L2TP/RFC3931
tunnels on Linux systems.

The L2TP subsystem for IP encapsulated tunnels hooks into the inet
kernel code using a specific IP protocol value.  In order to handle
this, anyToSockaddr now has to query the socket protocol type using
GetsockoptInt for the AF_INET and AF_INET6 address families.

Although this change is reasonably simple, unit tests have been added
to validate handling of the new types.
  • Loading branch information
tomparkin committed Mar 11, 2020
1 parent 5c8b2ff commit ec1d125
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 21 deletions.
8 changes: 8 additions & 0 deletions unix/linux/types.go
Expand Up @@ -205,6 +205,8 @@ union sockaddr_all {
struct sockaddr_ll s5;
struct sockaddr_nl s6;
struct sockaddr_pppox s7;
struct sockaddr_l2tpip s8;
struct sockaddr_l2tpip6 s9;
};
struct sockaddr_any {
Expand Down Expand Up @@ -509,6 +511,10 @@ type RawSockaddrPPPoX [C.sizeof_struct_sockaddr_pppox]byte

type RawSockaddrTIPC C.struct_sockaddr_tipc

type RawSockaddrL2TPIP C.struct_sockaddr_l2tpip

type RawSockaddrL2TPIP6 C.struct_sockaddr_l2tpip6

type RawSockaddr C.struct_sockaddr

type RawSockaddrAny C.struct_sockaddr_any
Expand Down Expand Up @@ -561,6 +567,8 @@ const (
SizeofSockaddrXDP = C.sizeof_struct_sockaddr_xdp
SizeofSockaddrPPPoX = C.sizeof_struct_sockaddr_pppox
SizeofSockaddrTIPC = C.sizeof_struct_sockaddr_tipc
SizeofSockaddrL2TPIP = C.sizeof_struct_sockaddr_l2tpip
SizeofSockaddrL2TPIP6 = C.sizeof_struct_sockaddr_l2tpip6
SizeofLinger = C.sizeof_struct_linger
SizeofIovec = C.sizeof_struct_iovec
SizeofIPMreq = C.sizeof_struct_ip_mreq
Expand Down
5 changes: 5 additions & 0 deletions unix/mkerrors.sh
Expand Up @@ -280,6 +280,11 @@ struct ltchars {
// for the tipc_subscr timeout __u32 field.
#undef TIPC_WAIT_FOREVER
#define TIPC_WAIT_FOREVER 0xffffffff
// Copied from linux/l2tp.h
// Including linux/l2tp.h here causes conflicts between linux/in.h
// and netinet/in.h included via net/route.h above.
#define IPPROTO_L2TP 115
'

includes_NetBSD='
Expand Down
202 changes: 196 additions & 6 deletions unix/syscall_internal_linux_test.go
Expand Up @@ -12,12 +12,20 @@ import (
"unsafe"
)

// as per socket(2)
type SocketSpec struct {
domain int
typ int
protocol int
}

func Test_anyToSockaddr(t *testing.T) {
tests := []struct {
name string
rsa *RawSockaddrAny
sa Sockaddr
err error
skt SocketSpec
}{
{
name: "AF_TIPC bad addrtype",
Expand Down Expand Up @@ -89,6 +97,45 @@ func Test_anyToSockaddr(t *testing.T) {
},
},
},
{
name: "AF_INET IPPROTO_L2TP",
rsa: sockaddrL2TPIPToAny(RawSockaddrL2TPIP{
Family: AF_INET,
Addr: [4]byte{0xef, 0x10, 0x5b, 0xa2},
Conn_id: uint32ToBE(0x1234abcd),
}),
sa: &SockaddrL2TPIP{
Addr: [4]byte{0xef, 0x10, 0x5b, 0xa2},
ConnId: 0x1234abcd,
},
skt: SocketSpec{domain: AF_INET, typ: SOCK_DGRAM, protocol: IPPROTO_L2TP},
},
{
name: "AF_INET6 IPPROTO_L2TP",
rsa: sockaddrL2TPIP6ToAny(RawSockaddrL2TPIP6{
Family: AF_INET6,
Flowinfo: 42,
Addr: [16]byte{
0x20, 0x01, 0x0d, 0xb8,
0x85, 0xa3, 0x00, 0x00,
0x00, 0x00, 0x8a, 0x2e,
0x03, 0x70, 0x73, 0x34,
},
Scope_id: 90210,
Conn_id: uint32ToBE(0x1234abcd),
}),
sa: &SockaddrL2TPIP6{
Addr: [16]byte{
0x20, 0x01, 0x0d, 0xb8,
0x85, 0xa3, 0x00, 0x00,
0x00, 0x00, 0x8a, 0x2e,
0x03, 0x70, 0x73, 0x34,
},
ZoneId: 90210,
ConnId: 0x1234abcd,
},
skt: SocketSpec{domain: AF_INET6, typ: SOCK_DGRAM, protocol: IPPROTO_L2TP},
},
{
name: "AF_MAX EAFNOSUPPORT",
rsa: &RawSockaddrAny{
Expand All @@ -103,8 +150,18 @@ func Test_anyToSockaddr(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// TODO: parameterize fd (and its setup) when needed.
sa, err := anyToSockaddr(0, tt.rsa)
fd := int(0)
var err error
if tt.skt.domain != 0 {
fd, err = Socket(tt.skt.domain, tt.skt.typ, tt.skt.protocol)
if err != nil {
t.Fatalf("socket(%v): %v", tt.skt, err)
}
defer func() {
Close(fd)
}()
}
sa, err := anyToSockaddr(fd, tt.rsa)
if err != tt.err {
t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
}
Expand Down Expand Up @@ -215,16 +272,149 @@ func TestSockaddrTIPC_sockaddr(t *testing.T) {
}
}

func TestSockaddrL2TPIP_sockaddr(t *testing.T) {
tests := []struct {
name string
sa *SockaddrL2TPIP
raw *RawSockaddrL2TPIP
err error
}{
{
name: "no fields set",
sa: &SockaddrL2TPIP{},
err: EINVAL,
},
{
name: "L2TPIP",
sa: &SockaddrL2TPIP{
Addr: [4]byte{0xef, 0x10, 0x5b, 0xa2},
ConnId: 0x1234abcd,
},
raw: &RawSockaddrL2TPIP{
Family: AF_INET,
Addr: [4]byte{0xef, 0x10, 0x5b, 0xa2},
Conn_id: uint32ToBE(0x1234abcd),
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, l, err := tt.sa.sockaddr()
if err != tt.err {
t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
}

// Must be 0 on error or a fixed size otherwise.
if (tt.err != nil && l != 0) || (tt.raw != nil && l != SizeofSockaddrL2TPIP) {
t.Fatalf("unexpected Socklen: %d", l)
}

if out != nil {
raw := (*RawSockaddrL2TPIP)(out)
if !reflect.DeepEqual(raw, tt.raw) {
t.Fatalf("unexpected RawSockaddrL2TPIP:\n got: %#v\nwant: %#v", raw, tt.raw)
}
}
})
}
}

func TestSockaddrL2TPIP6_sockaddr(t *testing.T) {
tests := []struct {
name string
sa *SockaddrL2TPIP6
raw *RawSockaddrL2TPIP6
err error
}{
{
name: "no fields set",
sa: &SockaddrL2TPIP6{},
err: EINVAL,
},
{
name: "L2TPIP6",
sa: &SockaddrL2TPIP6{
Addr: [16]byte{
0x20, 0x01, 0x0d, 0xb8,
0x85, 0xa3, 0x00, 0x00,
0x00, 0x00, 0x8a, 0x2e,
0x03, 0x70, 0x73, 0x34,
},
ZoneId: 90210,
ConnId: 0x1234abcd,
},
raw: &RawSockaddrL2TPIP6{
Family: AF_INET6,
Addr: [16]byte{
0x20, 0x01, 0x0d, 0xb8,
0x85, 0xa3, 0x00, 0x00,
0x00, 0x00, 0x8a, 0x2e,
0x03, 0x70, 0x73, 0x34,
},
Scope_id: 90210,
Conn_id: uint32ToBE(0x1234abcd),
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, l, err := tt.sa.sockaddr()
if err != tt.err {
t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
}

// Must be 0 on error or a fixed size otherwise.
if (tt.err != nil && l != 0) || (tt.raw != nil && l != SizeofSockaddrL2TPIP6) {
t.Fatalf("unexpected Socklen: %d", l)
}

if out != nil {
raw := (*RawSockaddrL2TPIP6)(out)
if !reflect.DeepEqual(raw, tt.raw) {
t.Fatalf("unexpected RawSockaddrL2TPIP6:\n got: %#v\nwant: %#v", raw, tt.raw)
}
}
})
}
}

func uint32ToBE(in uint32) (out uint32) {
p := (*[4]byte)(unsafe.Pointer(&out))
p[0] = byte(in >> 24)
p[1] = byte(in >> 16)
p[2] = byte(in >> 8)
p[3] = byte(in >> 0)
return out
}

// These helpers explicitly copy the contents of in into out to produce
// the correct sockaddr structure, without relying on unsafe casting to
// a type of a larger size.
func sockaddrTIPCToAny(in RawSockaddrTIPC) *RawSockaddrAny {
var out RawSockaddrAny

// Explicitly copy the contents of in into out to produce the correct
// sockaddr structure, without relying on unsafe casting to a type of a
// larger size.
copy(
(*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:],
(*(*[SizeofSockaddrTIPC]byte)(unsafe.Pointer(&in)))[:],
)
return &out
}

func sockaddrL2TPIPToAny(in RawSockaddrL2TPIP) *RawSockaddrAny {
var out RawSockaddrAny
copy(
(*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:],
(*(*[SizeofSockaddrL2TPIP]byte)(unsafe.Pointer(&in)))[:],
)
return &out
}

func sockaddrL2TPIP6ToAny(in RawSockaddrL2TPIP6) *RawSockaddrAny {
var out RawSockaddrAny
copy(
(*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:],
(*(*[SizeofSockaddrL2TPIP6]byte)(unsafe.Pointer(&in)))[:],
)
return &out
}

0 comments on commit ec1d125

Please sign in to comment.