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
}