diff --git a/unix/syscall_internal_linux_test.go b/unix/syscall_internal_linux_test.go index 84f6257e..5a4f4f6d 100644 --- a/unix/syscall_internal_linux_test.go +++ b/unix/syscall_internal_linux_test.go @@ -169,7 +169,7 @@ func Test_anyToSockaddr(t *testing.T) { }, }, { - name: "AF_CAN", + name: "AF_CAN CAN_RAW", rsa: sockaddrCANToAny(RawSockaddrCAN{ Family: AF_CAN, Ifindex: 12345678, @@ -185,6 +185,27 @@ func Test_anyToSockaddr(t *testing.T) { 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 @@ func Test_anyToSockaddr(t *testing.T) { 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 84a9e527..1fdfe48e 100644 --- a/unix/syscall_linux.go +++ b/unix/syscall_linux.go @@ -641,6 +641,36 @@ func (sa *SockaddrCAN) sockaddr() (unsafe.Pointer, _Socklen, error) { 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 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { 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 }