unix: implement L2TPIP socket address on Linux

Add 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.

Fixes golang/go#37787

Change-Id: I16ae1e24dcced4ccc6ce6a79a90a5a2f6a560967
GitHub-Last-Rev: ca554ad1b6d3a5e55c70f05cf0e542968fa2418c
GitHub-Pull-Request: golang/sys#60
Reviewed-on: https://go-review.googlesource.com/c/sys/+/223157
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/unix/linux/types.go b/unix/linux/types.go
index b12f9d1..2cdb7dd 100644
--- a/unix/linux/types.go
+++ b/unix/linux/types.go
@@ -205,6 +205,8 @@
 	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 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 @@
 	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
diff --git a/unix/mkerrors.sh b/unix/mkerrors.sh
index 96bf2a9..c5d0c06 100755
--- a/unix/mkerrors.sh
+++ b/unix/mkerrors.sh
@@ -280,6 +280,11 @@
 // 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='
diff --git a/unix/syscall_internal_linux_test.go b/unix/syscall_internal_linux_test.go
index 8998203..4dafac8 100644
--- a/unix/syscall_internal_linux_test.go
+++ b/unix/syscall_internal_linux_test.go
@@ -12,12 +12,20 @@
 	"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",
@@ -90,6 +98,45 @@
 			},
 		},
 		{
+			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{
 				Addr: RawSockaddr{
@@ -103,8 +150,21 @@
 
 	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)
+				// Some sockaddr types need specific kernel modules running: if these
+				// are not present we'll get EPROTONOSUPPORT back when trying to create
+				// the socket.  Skip the test in this situation.
+				if err == EPROTONOSUPPORT {
+					t.Skip("socket family/protocol not supported by kernel")
+				} else if err != nil {
+					t.Fatalf("socket(%v): %v", tt.skt, err)
+				}
+				defer Close(fd)
+			}
+			sa, err := anyToSockaddr(fd, tt.rsa)
 			if err != tt.err {
 				t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
 			}
@@ -215,16 +275,130 @@
 	}
 }
 
+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
 }
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index 95f7a15..bbe1abb 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -839,6 +839,40 @@
 	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 +923,58 @@
 		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
 		}
-		return sa, nil
+
+		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
+		}
 
 	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
 		}
-		return sa, nil
+
+		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
+		}
 
 	case AF_VSOCK:
 		pp := (*RawSockaddrVM)(unsafe.Pointer(rsa))
diff --git a/unix/zerrors_linux.go b/unix/zerrors_linux.go
index 5be454c..84c599c 100644
--- a/unix/zerrors_linux.go
+++ b/unix/zerrors_linux.go
@@ -890,6 +890,7 @@
 	IPPROTO_IP                                  = 0x0
 	IPPROTO_IPIP                                = 0x4
 	IPPROTO_IPV6                                = 0x29
+	IPPROTO_L2TP                                = 0x73
 	IPPROTO_MH                                  = 0x87
 	IPPROTO_MPLS                                = 0x89
 	IPPROTO_MTP                                 = 0x5c
diff --git a/unix/ztypes_linux.go b/unix/ztypes_linux.go
index 6c81e75..cb5e06c 100644
--- a/unix/ztypes_linux.go
+++ b/unix/ztypes_linux.go
@@ -243,6 +243,23 @@
 	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 @@
 	SizeofSockaddrXDP       = 0x10
 	SizeofSockaddrPPPoX     = 0x1e
 	SizeofSockaddrTIPC      = 0x10
+	SizeofSockaddrL2TPIP    = 0x10
+	SizeofSockaddrL2TPIP6   = 0x20
 	SizeofLinger            = 0x8
 	SizeofIPMreq            = 0x8
 	SizeofIPMreqn           = 0xc