unix: implement L2TPIP socket address on Linux

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.
This commit is contained in:
Tom Parkin
2020-03-11 16:16:20 +00:00
parent 5c8b2ff675
commit c0880b5844
6 changed files with 296 additions and 22 deletions

View File

@@ -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 {
@@ -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
@@ -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

View File

@@ -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='

View File

@@ -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",
@@ -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: 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: 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{
@@ -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)
}
@@ -215,16 +272,130 @@ func TestSockaddrTIPC_sockaddr(t *testing.T) {
}
}
func TestSockaddrL2TPIP_sockaddr(t *testing.T) {
tests := []struct {
name string
sa *SockaddrL2TPIP
raw *RawSockaddrL2TPIP
err error
}{
{
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: 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: "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: 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)
}
}
})
}
}
// 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
}

View File

@@ -839,6 +839,43 @@ func (sa *SockaddrTIPC) sockaddr() (unsafe.Pointer, _Socklen, error) {
return unsafe.Pointer(&sa.raw), SizeofSockaddrTIPC, nil
}
// SockaddrL2TPIP implements the Sockaddr interface for IPPROTO_L2TP/AF_INET sockets.
type SockaddrL2TPIP struct {
Addr [4]byte
ConnId uint32
raw RawSockaddrL2TPIP
}
func (sa *SockaddrL2TPIP) sockaddr() (unsafe.Pointer, _Socklen, error) {
sa.raw.Family = AF_INET
sa.raw.Conn_id = sa.ConnId
for i := 0; i < len(sa.Addr); i++ {
sa.raw.Addr[i] = sa.Addr[i]
}
return unsafe.Pointer(&sa.raw), SizeofSockaddrL2TPIP, nil
}
// SockaddrL2TPIP6 implements the Sockaddr interface for IPPROTO_L2TP/AF_INET6 sockets.
type SockaddrL2TPIP6 struct {
Addr [16]byte
ZoneId uint32
ConnId uint32
raw RawSockaddrL2TPIP6
}
func (sa *SockaddrL2TPIP6) sockaddr() (unsafe.Pointer, _Socklen, error) {
sa.raw.Family = AF_INET6
sa.raw.Conn_id = sa.ConnId
sa.raw.Scope_id = sa.ZoneId
for i := 0; i < len(sa.Addr); i++ {
sa.raw.Addr[i] = sa.Addr[i]
}
return unsafe.Pointer(&sa.raw), SizeofSockaddrL2TPIP6, nil
}
func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
switch rsa.Addr.Family {
case AF_NETLINK:
@@ -889,25 +926,58 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
return sa, nil
case AF_INET:
pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
sa := new(SockaddrInet4)
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
sa.Port = int(p[0])<<8 + int(p[1])
for i := 0; i < len(sa.Addr); i++ {
sa.Addr[i] = pp.Addr[i]
proto, err := GetsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL)
if err != nil {
return nil, err
}
switch proto {
case IPPROTO_L2TP:
pp := (*RawSockaddrL2TPIP)(unsafe.Pointer(rsa))
sa := new(SockaddrL2TPIP)
sa.ConnId = pp.Conn_id
for i := 0; i < len(sa.Addr); i++ {
sa.Addr[i] = pp.Addr[i]
}
return sa, nil
default:
pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
sa := new(SockaddrInet4)
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
sa.Port = int(p[0])<<8 + int(p[1])
for i := 0; i < len(sa.Addr); i++ {
sa.Addr[i] = pp.Addr[i]
}
return sa, nil
}
return sa, nil
case AF_INET6:
pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
sa := new(SockaddrInet6)
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
sa.Port = int(p[0])<<8 + int(p[1])
sa.ZoneId = pp.Scope_id
for i := 0; i < len(sa.Addr); i++ {
sa.Addr[i] = pp.Addr[i]
proto, err := GetsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL)
if err != nil {
return nil, err
}
switch proto {
case IPPROTO_L2TP:
pp := (*RawSockaddrL2TPIP6)(unsafe.Pointer(rsa))
sa := new(SockaddrL2TPIP6)
sa.ConnId = pp.Conn_id
sa.ZoneId = pp.Scope_id
for i := 0; i < len(sa.Addr); i++ {
sa.Addr[i] = pp.Addr[i]
}
return sa, nil
default:
pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
sa := new(SockaddrInet6)
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
sa.Port = int(p[0])<<8 + int(p[1])
sa.ZoneId = pp.Scope_id
for i := 0; i < len(sa.Addr); i++ {
sa.Addr[i] = pp.Addr[i]
}
return sa, nil
}
return sa, nil
case AF_VSOCK:
pp := (*RawSockaddrVM)(unsafe.Pointer(rsa))

View File

@@ -890,6 +890,7 @@ const (
IPPROTO_IP = 0x0
IPPROTO_IPIP = 0x4
IPPROTO_IPV6 = 0x29
IPPROTO_L2TP = 0x73
IPPROTO_MH = 0x87
IPPROTO_MPLS = 0x89
IPPROTO_MTP = 0x5c

View File

@@ -243,6 +243,23 @@ type RawSockaddrTIPC struct {
Addr [12]byte
}
type RawSockaddrL2TPIP struct {
Family uint16
Unused uint16
Addr [4]byte /* in_addr */
Conn_id uint32
_ [4]uint8
}
type RawSockaddrL2TPIP6 struct {
Family uint16
Unused uint16
Flowinfo uint32
Addr [16]byte /* in6_addr */
Scope_id uint32
Conn_id uint32
}
type _Socklen uint32
type Linger struct {
@@ -353,6 +370,8 @@ const (
SizeofSockaddrXDP = 0x10
SizeofSockaddrPPPoX = 0x1e
SizeofSockaddrTIPC = 0x10
SizeofSockaddrL2TPIP = 0x10
SizeofSockaddrL2TPIP6 = 0x20
SizeofLinger = 0x8
SizeofIPMreq = 0x8
SizeofIPMreqn = 0xc