unix: extend AF_CAN socket support

Fixes golang/go#40842

Change-Id: Icbb8f89e241d6990c06ee805e3dabeeb99750c8e
GitHub-Last-Rev: 360962020364460efe5128bdf07c545ba9561e94
GitHub-Pull-Request: golang/sys#78
Reviewed-on: https://go-review.googlesource.com/c/sys/+/248977
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
diff --git a/unix/syscall_internal_linux_test.go b/unix/syscall_internal_linux_test.go
index 40d3aca..8af671e 100644
--- a/unix/syscall_internal_linux_test.go
+++ b/unix/syscall_internal_linux_test.go
@@ -169,6 +169,24 @@
 			},
 		},
 		{
+			name: "AF_CAN",
+			rsa: sockaddrCANToAny(RawSockaddrCAN{
+				Family:  AF_CAN,
+				Ifindex: 12345678,
+				Addr: [16]byte{
+					0x89, 0x67, 0x45, 0x23,
+					0x90, 0x78, 0x56, 0x34,
+					0x0, 0x0, 0x0, 0x0,
+					0x0, 0x0, 0x0, 0x0,
+				},
+			}),
+			sa: &SockaddrCAN{
+				Ifindex: 12345678,
+				RxID:    0x23456789,
+				TxID:    0x34567890,
+			},
+		},
+		{
 			name: "AF_MAX EAFNOSUPPORT",
 			rsa: &RawSockaddrAny{
 				Addr: RawSockaddr{
@@ -544,6 +562,62 @@
 	}
 }
 
+func TestSockaddrCAN_sockaddr(t *testing.T) {
+	tests := []struct {
+		name string
+		sa   *SockaddrCAN
+		raw  *RawSockaddrCAN
+		err  error
+	}{
+		{
+			name: "with ids",
+			sa: &SockaddrCAN{
+				Ifindex: 12345678,
+				RxID:    0x23456789,
+				TxID:    0x34567890,
+			},
+			raw: &RawSockaddrCAN{
+				Family:  AF_CAN,
+				Ifindex: 12345678,
+				Addr: [16]byte{
+					0x89, 0x67, 0x45, 0x23,
+					0x90, 0x78, 0x56, 0x34,
+					0x0, 0x0, 0x0, 0x0,
+					0x0, 0x0, 0x0, 0x0,
+				},
+			},
+		},
+		{
+			name: "negative ifindex",
+			sa: &SockaddrCAN{
+				Ifindex: -1,
+			},
+			err: EINVAL,
+		},
+	}
+
+	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 != SizeofSockaddrCAN) {
+				t.Fatalf("unexpected Socklen: %d", l)
+			}
+
+			if out != nil {
+				raw := (*RawSockaddrCAN)(out)
+				if !reflect.DeepEqual(raw, tt.raw) {
+					t.Fatalf("unexpected RawSockaddrCAN:\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.
@@ -591,3 +665,12 @@
 	)
 	return &out
 }
+
+func sockaddrCANToAny(in RawSockaddrCAN) *RawSockaddrAny {
+	var out RawSockaddrAny
+	copy(
+		(*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:],
+		(*(*[SizeofSockaddrCAN]byte)(unsafe.Pointer(&in)))[:],
+	)
+	return &out
+}
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index c48f5dd..3e20fcf 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -1111,6 +1111,21 @@
 		}
 		return sa, nil
 
+	case AF_CAN:
+		pp := (*RawSockaddrCAN)(unsafe.Pointer(rsa))
+		sa := &SockaddrCAN{
+			Ifindex: int(pp.Ifindex),
+		}
+		rx := (*[4]byte)(unsafe.Pointer(&sa.RxID))
+		for i := 0; i < 4; i++ {
+			rx[i] = pp.Addr[i]
+		}
+		tx := (*[4]byte)(unsafe.Pointer(&sa.TxID))
+		for i := 0; i < 4; i++ {
+			tx[i] = pp.Addr[i+4]
+		}
+		return sa, nil
+
 	}
 	return nil, EAFNOSUPPORT
 }