unix: add J1939 protocol support on socketcan interface

Fixes golang/go#42797

Change-Id: Ia7be73e53438dec80903c95de0e6829005741a0f
GitHub-Last-Rev: bc3583533928d549ea6bedd4dae13e0643da2a44
GitHub-Pull-Request: golang/sys#91
Reviewed-on: https://go-review.googlesource.com/c/sys/+/272767
Run-TryBot: Matt Layher <mdlayher@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Matt Layher <mdlayher@gmail.com>
Trust: Tobias Klauser <tobias.klauser@gmail.com>
diff --git a/unix/syscall_internal_linux_test.go b/unix/syscall_internal_linux_test.go
index 84f6257..5a4f4f6 100644
--- a/unix/syscall_internal_linux_test.go
+++ b/unix/syscall_internal_linux_test.go
@@ -169,7 +169,7 @@
 			},
 		},
 		{
-			name: "AF_CAN",
+			name: "AF_CAN CAN_RAW",
 			rsa: sockaddrCANToAny(RawSockaddrCAN{
 				Family:  AF_CAN,
 				Ifindex: 12345678,
@@ -185,6 +185,27 @@
 				RxID:    0xAAAAAAAA,
 				TxID:    0xBBBBBBBB,
 			},
+			skt: SocketSpec{domain: AF_CAN, typ: SOCK_RAW, protocol: CAN_RAW},
+		},
+		{
+			name: "AF_CAN CAN_J1939",
+			rsa: sockaddrCANToAny(RawSockaddrCAN{
+				Family:  AF_CAN,
+				Ifindex: 12345678,
+				Addr: [16]byte{
+					0xAA, 0xAA, 0xAA, 0xAA,
+					0xAA, 0xAA, 0xAA, 0xAA,
+					0xBB, 0xBB, 0xBB, 0xBB,
+					0xCC, 0x00, 0x00, 0x00,
+				},
+			}),
+			sa: &SockaddrCANJ1939{
+				Ifindex: 12345678,
+				Name:    0xAAAAAAAAAAAAAAAA,
+				PGN:     0xBBBBBBBB,
+				Addr:    0xCC,
+			},
+			skt: SocketSpec{domain: AF_CAN, typ: SOCK_DGRAM, protocol: CAN_J1939},
 		},
 		{
 			name: "AF_MAX EAFNOSUPPORT",
@@ -205,10 +226,16 @@
 			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.
+				// are not present we'll get EPROTONOSUPPORT/EAFNOSUPPORT 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 == EAFNOSUPPORT {
+					t.Skip("socket address family not supported by kernel")
+				} else if err == EACCES {
+					// Some platforms might require elevated privileges to perform
+					// actions on sockets. Skip the test in this situation.
+					t.Skip("socket operation not permitted")
 				} else if err != nil {
 					t.Fatalf("socket(%v): %v", tt.skt, err)
 				}
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index 84a9e52..1fdfe48 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -641,6 +641,36 @@
 	return unsafe.Pointer(&sa.raw), SizeofSockaddrCAN, nil
 }
 
+// SockaddrCANJ1939 implements the Sockaddr interface for AF_CAN using J1939
+// protocol (https://en.wikipedia.org/wiki/SAE_J1939). For more information
+// on the purposes of the fields, check the official linux kernel documentation
+// available here: https://www.kernel.org/doc/Documentation/networking/j1939.rst
+type SockaddrCANJ1939 struct {
+	Ifindex int
+	Name    uint64
+	PGN     uint32
+	Addr    uint8
+	raw     RawSockaddrCAN
+}
+
+func (sa *SockaddrCANJ1939) sockaddr() (unsafe.Pointer, _Socklen, error) {
+	if sa.Ifindex < 0 || sa.Ifindex > 0x7fffffff {
+		return nil, 0, EINVAL
+	}
+	sa.raw.Family = AF_CAN
+	sa.raw.Ifindex = int32(sa.Ifindex)
+	n := (*[8]byte)(unsafe.Pointer(&sa.Name))
+	for i := 0; i < 8; i++ {
+		sa.raw.Addr[i] = n[i]
+	}
+	p := (*[4]byte)(unsafe.Pointer(&sa.PGN))
+	for i := 0; i < 4; i++ {
+		sa.raw.Addr[i+8] = p[i]
+	}
+	sa.raw.Addr[12] = sa.Addr
+	return unsafe.Pointer(&sa.raw), SizeofSockaddrCAN, nil
+}
+
 // SockaddrALG implements the Sockaddr interface for AF_ALG type sockets.
 // SockaddrALG enables userspace access to the Linux kernel's cryptography
 // subsystem. The Type and Name fields specify which type of hash or cipher
@@ -1150,20 +1180,43 @@
 		return sa, nil
 
 	case AF_CAN:
-		pp := (*RawSockaddrCAN)(unsafe.Pointer(rsa))
-		sa := &SockaddrCAN{
-			Ifindex: int(pp.Ifindex),
+		proto, err := GetsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL)
+		if err != nil {
+			return nil, err
 		}
-		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
 
+		pp := (*RawSockaddrCAN)(unsafe.Pointer(rsa))
+
+		switch proto {
+		case CAN_J1939:
+			sa := &SockaddrCANJ1939{
+				Ifindex: int(pp.Ifindex),
+			}
+			name := (*[8]byte)(unsafe.Pointer(&sa.Name))
+			for i := 0; i < 8; i++ {
+				name[i] = pp.Addr[i]
+			}
+			pgn := (*[4]byte)(unsafe.Pointer(&sa.PGN))
+			for i := 0; i < 4; i++ {
+				pgn[i] = pp.Addr[i+8]
+			}
+			addr := (*[1]byte)(unsafe.Pointer(&sa.Addr))
+			addr[0] = pp.Addr[12]
+			return sa, nil
+		default:
+			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
 }