├── cmd ├── candump │ ├── candump │ └── candump.go └── cansend │ ├── cansend │ └── cansend.go ├── pkg └── socketcan │ ├── byteorder.go │ ├── interface.go │ ├── interface_isotp.go │ ├── interface_raw.go │ └── socketcan.go └── test ├── canraw_test.go └── isotp_test.go /cmd/candump/candump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linklayer/go-socketcan/e9fc2da5204c97d71cebc4ea34091ffda44378ff/cmd/candump/candump -------------------------------------------------------------------------------- /cmd/candump/candump.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/linklayer/go-socketcan/pkg/socketcan" 9 | ) 10 | 11 | func dataToString(data []byte) string { 12 | str := "" 13 | for i := 0; i < len(data); i++ { 14 | str = fmt.Sprintf("%s%02X ", str, data[i]) 15 | } 16 | return str 17 | } 18 | 19 | func usage() { 20 | fmt.Fprintf(flag.CommandLine.Output(), 21 | "Usage: %s [interface]\n", 22 | os.Args[0]) 23 | flag.PrintDefaults() 24 | } 25 | 26 | func main() { 27 | flag.Usage = usage 28 | flag.Parse() 29 | if len(flag.Args()) != 1 { 30 | usage() 31 | os.Exit(1) 32 | } 33 | 34 | canIf := flag.Args()[0] 35 | 36 | device, err := socketcan.NewRawInterface(canIf) 37 | if err != nil { 38 | fmt.Printf("could not open interface %s: %v\n", 39 | canIf, err) 40 | os.Exit(1) 41 | } 42 | defer device.Close() 43 | 44 | for { 45 | frame, err := device.RecvFrame() 46 | if err != nil { 47 | fmt.Printf("error receiving frame: %v", err) 48 | os.Exit(1) 49 | } 50 | dataStr := dataToString(frame.Data) 51 | fmt.Printf(" %s\t%03X\t[%d]\t%s\n", device.IfName, frame.ArbId, frame.Dlc, dataStr) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cmd/cansend/cansend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linklayer/go-socketcan/e9fc2da5204c97d71cebc4ea34091ffda44378ff/cmd/cansend/cansend -------------------------------------------------------------------------------- /cmd/cansend/cansend.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/linklayer/go-socketcan/pkg/socketcan" 11 | ) 12 | 13 | func usage() { 14 | fmt.Fprintf(flag.CommandLine.Output(), 15 | "Usage: %s [interface] [arbid]#[data]\n", 16 | os.Args[0]) 17 | flag.PrintDefaults() 18 | } 19 | 20 | func parseFrame(frameStr string) (socketcan.CanFrame, error) { 21 | frame := socketcan.CanFrame{} 22 | fields := strings.Split(frameStr, "#") 23 | arbId, err := strconv.ParseUint(fields[0], 16, 32) 24 | if err != nil { 25 | return frame, err 26 | } 27 | frame.ArbId = uint32(arbId) 28 | 29 | if len(fields[1])%2 != 0 { 30 | return frame, fmt.Errorf("invalid frame bytes") 31 | } 32 | frame.Dlc = byte(len(fields[1]) / 2) 33 | 34 | frame.Data = make([]byte, frame.Dlc) 35 | for i := byte(0); i < frame.Dlc; i++ { 36 | var val, err = strconv.ParseInt(fields[1][i*2:i*2+2], 16, 9) 37 | if err != nil { 38 | return frame, err 39 | } 40 | frame.Data[i] = byte(val) 41 | } 42 | 43 | return frame, nil 44 | } 45 | 46 | func main() { 47 | flag.Usage = usage 48 | flag.Parse() 49 | if len(flag.Args()) != 2 { 50 | usage() 51 | os.Exit(1) 52 | } 53 | 54 | canIf := flag.Args()[0] 55 | frameStr := flag.Args()[1] 56 | 57 | frame, err := parseFrame(frameStr) 58 | if err != nil { 59 | fmt.Printf("could not parse frame: %v\n", err) 60 | os.Exit(1) 61 | } 62 | 63 | var device socketcan.Interface 64 | device, err = socketcan.NewRawInterface(canIf) 65 | if err != nil { 66 | fmt.Printf("could not open interface %s: %v\n", 67 | canIf, err) 68 | os.Exit(1) 69 | } 70 | defer device.Close() 71 | 72 | device.SendFrame(frame) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/socketcan/byteorder.go: -------------------------------------------------------------------------------- 1 | package socketcan 2 | 3 | import ( 4 | "unsafe" 5 | "encoding/binary" 6 | ) 7 | 8 | func getEndianness() binary.ByteOrder { 9 | buf := [2]byte{} 10 | *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) 11 | 12 | switch buf { 13 | case [2]byte{0xCD, 0xAB}: 14 | return binary.LittleEndian 15 | case [2]byte{0xAB, 0xCD}: 16 | return binary.BigEndian 17 | default: 18 | panic("Could not determine native endianness.") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkg/socketcan/interface.go: -------------------------------------------------------------------------------- 1 | package socketcan 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | "unsafe" 8 | 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | const ( 13 | CAN_RAW = 1 14 | CAN_ISOTP = 6 15 | 16 | CAN_RAW_RECV_OWN_MSGS = 4 17 | SOL_CAN_RAW = 101 18 | ) 19 | 20 | const ( 21 | IF_TYPE_RAW = 0 22 | IF_TYPE_ISOTP = 1 23 | ) 24 | 25 | type Interface struct { 26 | IfName string 27 | SocketFd int 28 | ifType int 29 | } 30 | 31 | func getIfIndex(fd int, ifName string) (int, error) { 32 | ifNameRaw, err := unix.ByteSliceFromString(ifName) 33 | if err != nil { 34 | return 0, err 35 | } 36 | if len(ifNameRaw) > 16 { 37 | return 0, errors.New("maximum ifname length is 16 characters") 38 | } 39 | 40 | ifReq := ifreqIndex{} 41 | copy(ifReq.Name[:], ifNameRaw) 42 | err = ioctlIfreq(fd, &ifReq) 43 | return ifReq.Index, err 44 | } 45 | 46 | type ifreqIndex struct { 47 | Name [16]byte 48 | Index int 49 | } 50 | 51 | func ioctlIfreq(fd int, ifreq *ifreqIndex) (err error) { 52 | _, _, errno := unix.Syscall( 53 | unix.SYS_IOCTL, 54 | uintptr(fd), 55 | unix.SIOCGIFINDEX, 56 | uintptr(unsafe.Pointer(ifreq)), 57 | ) 58 | if errno != 0 { 59 | err = fmt.Errorf("ioctl: %v", errno) 60 | } 61 | return 62 | } 63 | 64 | func (i Interface) SetLoopback(enable bool) error { 65 | value := 0 66 | if enable { 67 | value = 1 68 | } 69 | err := unix.SetsockoptInt(i.SocketFd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, value) 70 | return err 71 | } 72 | 73 | func (i Interface) SetRecvTimeout(timeout time.Duration) error { 74 | tv := unix.NsecToTimeval(timeout.Nanoseconds()) 75 | err := unix.SetsockoptTimeval(i.SocketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv) 76 | return err 77 | } 78 | 79 | func (i Interface) SetSendTimeout(timeout time.Duration) error { 80 | tv := unix.NsecToTimeval(timeout.Nanoseconds()) 81 | err := unix.SetsockoptTimeval(i.SocketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &tv) 82 | return err 83 | } 84 | 85 | func (i Interface) Close() error { 86 | return unix.Close(i.SocketFd) 87 | } 88 | -------------------------------------------------------------------------------- /pkg/socketcan/interface_isotp.go: -------------------------------------------------------------------------------- 1 | package socketcan 2 | 3 | import ( 4 | "fmt" 5 | "encoding/binary" 6 | "bytes" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | type CanIsotpOptions struct { 12 | flags uint32 13 | frame_txtime uint32 14 | ext_address uint8 15 | txpad_content uint8 16 | rxpad_content uint8 17 | rx_ext_address uint8 18 | } 19 | 20 | 21 | const SYS_SETSOCKOPT = 54 22 | const SOL_CAN_BASE = 100 23 | const SOL_CAN_ISOTP = 106 24 | const CAN_ISOTP_OPTS = 1 25 | const CAN_EFF_FLAG = 0x80000000 26 | 27 | const CAN_ISOTP_EXTEND_ADDR = 0x002 28 | const CAN_ISOTP_TX_PADDING = 0x004 29 | const CAN_ISOTP_RX_PADDING = 0x008 30 | 31 | func NewIsotpInterface(ifName string, rxID uint32, txID uint32) (Interface, error) { 32 | canIf := Interface{} 33 | canIf.ifType = IF_TYPE_ISOTP 34 | 35 | fd, err := unix.Socket(unix.AF_CAN, unix.SOCK_DGRAM, CAN_ISOTP) 36 | if err != nil { 37 | return canIf, err 38 | } 39 | 40 | ifIndex, err := getIfIndex(fd, ifName) 41 | if err != nil { 42 | return canIf, err 43 | } 44 | 45 | // set extended ID flags if required 46 | if rxID > 0x7FF { 47 | rxID |= CAN_EFF_FLAG 48 | } 49 | if txID > 0x7FF { 50 | txID |= CAN_EFF_FLAG 51 | } 52 | 53 | addr := &unix.SockaddrCAN{Ifindex: ifIndex, RxID: rxID, TxID: txID} 54 | if err = unix.Bind(fd, addr); err != nil { 55 | return canIf, err 56 | } 57 | 58 | canIf.IfName = ifName 59 | canIf.SocketFd = fd 60 | 61 | return canIf, nil 62 | } 63 | 64 | func (i Interface) Rebind(rxID uint32, txID uint32) error { 65 | ifIndex, err := getIfIndex(i.SocketFd, i.IfName) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | // set extended ID flags if required 71 | if rxID > 0x7FF { 72 | rxID |= CAN_EFF_FLAG 73 | } 74 | if txID > 0x7FF { 75 | txID |= CAN_EFF_FLAG 76 | } 77 | 78 | addr := &unix.SockaddrCAN{Ifindex: ifIndex, RxID: rxID, TxID: txID} 79 | if err = unix.Bind(i.SocketFd, addr); err != nil { 80 | return err 81 | } 82 | return nil 83 | } 84 | 85 | func (i Interface) SendBuf(data []byte) error { 86 | if i.ifType != IF_TYPE_ISOTP { 87 | return fmt.Errorf("interface is not isotp type") 88 | } 89 | 90 | _, err := unix.Write(i.SocketFd, data) 91 | return err 92 | } 93 | 94 | func (i Interface) RecvBuf() ([]byte, error) { 95 | if i.ifType != IF_TYPE_ISOTP { 96 | return []byte{}, fmt.Errorf("interface is not isotp type") 97 | } 98 | 99 | data := make([]byte, 4095) 100 | len, err := unix.Read(i.SocketFd, data) 101 | if err != nil { 102 | return data, err 103 | } 104 | 105 | // only return the valid bytes (0 to length received) 106 | return data[:len], nil 107 | } 108 | 109 | func (i Interface) SetTxPadding(on bool, value uint8) error { 110 | var buf bytes.Buffer 111 | opts := CanIsotpOptions{} 112 | if on { 113 | opts.flags = CAN_ISOTP_TX_PADDING 114 | } 115 | opts.txpad_content = value 116 | 117 | err := binary.Write(&buf, getEndianness(), opts) 118 | if err != nil { 119 | return err 120 | } 121 | 122 | err = unix.SetsockoptString(i.SocketFd, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, buf.String()) 123 | return err 124 | } 125 | -------------------------------------------------------------------------------- /pkg/socketcan/interface_raw.go: -------------------------------------------------------------------------------- 1 | package socketcan 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | 7 | "golang.org/x/sys/unix" 8 | ) 9 | 10 | func NewRawInterface(ifName string) (Interface, error) { 11 | canIf := Interface{} 12 | canIf.ifType = IF_TYPE_RAW 13 | 14 | fd, err := unix.Socket(unix.AF_CAN, unix.SOCK_RAW, CAN_RAW) 15 | if err != nil { 16 | return canIf, err 17 | } 18 | 19 | ifIndex, err := getIfIndex(fd, ifName) 20 | if err != nil { 21 | return canIf, err 22 | } 23 | 24 | addr := &unix.SockaddrCAN{Ifindex: ifIndex} 25 | if err = unix.Bind(fd, addr); err != nil { 26 | return canIf, err 27 | } 28 | 29 | canIf.IfName = ifName 30 | canIf.SocketFd = fd 31 | 32 | return canIf, nil 33 | } 34 | 35 | func (i Interface) SendFrame(f CanFrame) error { 36 | if i.ifType != IF_TYPE_RAW { 37 | return fmt.Errorf("interface is not raw type") 38 | } 39 | 40 | // assemble a SocketCAN frame 41 | frameBytes := make([]byte, 16) 42 | // bytes 0-3: arbitration ID 43 | if f.ArbId < 0x800 { 44 | // standard ID 45 | binary.LittleEndian.PutUint32(frameBytes[0:4], f.ArbId) 46 | } else { 47 | // extended ID 48 | // set bit 31 (frame format flag (0 = standard 11 bit, 1 = extended 29 bit) 49 | binary.LittleEndian.PutUint32(frameBytes[0:4], f.ArbId|1<<31) 50 | } 51 | 52 | // byte 4: data length code 53 | frameBytes[4] = f.Dlc 54 | // data 55 | copy(frameBytes[8:], f.Data) 56 | 57 | _, err := unix.Write(i.SocketFd, frameBytes) 58 | return err 59 | } 60 | 61 | func (i Interface) RecvFrame() (CanFrame, error) { 62 | f := CanFrame{} 63 | 64 | if i.ifType != IF_TYPE_RAW { 65 | return f, fmt.Errorf("interface is not raw type") 66 | } 67 | 68 | // read SocketCAN frame from device 69 | frameBytes := make([]byte, 16) 70 | _, err := unix.Read(i.SocketFd, frameBytes) 71 | if err != nil { 72 | return f, err 73 | } 74 | 75 | // bytes 0-3: arbitration ID 76 | f.ArbId = uint32(binary.LittleEndian.Uint32(frameBytes[0:4])) 77 | // remove bit 31: extended ID flag 78 | f.ArbId = f.ArbId & 0x7FFFFFFF 79 | // byte 4: data length code 80 | f.Dlc = frameBytes[4] 81 | // data 82 | f.Data = make([]byte, 8) 83 | copy(f.Data, frameBytes[8:]) 84 | 85 | return f, nil 86 | } 87 | -------------------------------------------------------------------------------- /pkg/socketcan/socketcan.go: -------------------------------------------------------------------------------- 1 | package socketcan 2 | 3 | type CanFrame struct { 4 | ArbId uint32 5 | Dlc byte 6 | Data []byte 7 | Extended bool 8 | } 9 | -------------------------------------------------------------------------------- /test/canraw_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | 8 | "github.com/linklayer/go-socketcan/pkg/socketcan" 9 | ) 10 | 11 | func openRawInterface(t *testing.T) socketcan.Interface { 12 | dev, err := socketcan.NewRawInterface("vcan0") 13 | if err != nil { 14 | t.Errorf("could not create CAN device: %v", err) 15 | } 16 | return dev 17 | } 18 | 19 | func closeRawInterface(t *testing.T, dev socketcan.Interface) { 20 | err := dev.Close() 21 | if err != nil { 22 | t.Errorf("could not close CAN device: %v", err) 23 | } 24 | } 25 | 26 | func TestRawOpenClose(t *testing.T) { 27 | dev := openRawInterface(t) 28 | closeRawInterface(t, dev) 29 | } 30 | 31 | func TestRawLoopback(t *testing.T) { 32 | tx, rx := socketcan.CanFrame{}, socketcan.CanFrame{} 33 | tx.ArbId = 0x100 34 | tx.Dlc = 8 35 | tx.Data = []byte{1, 2, 3, 4, 5, 6, 7, 8} 36 | 37 | dev := openRawInterface(t) 38 | 39 | err := dev.SetLoopback(true) 40 | if err != nil { 41 | t.Errorf("could not enable loopback: %v", err) 42 | } 43 | 44 | go func() { 45 | time.Sleep(50 * time.Millisecond) 46 | err = dev.SendFrame(tx) 47 | if err != nil { 48 | t.Errorf("error sending frame: %v", err) 49 | } 50 | }() 51 | 52 | rx, err = dev.RecvFrame() 53 | if err != nil { 54 | t.Errorf("error receiving frame: %v", err) 55 | } 56 | 57 | if !reflect.DeepEqual(rx, tx) { 58 | t.Error("sent and received frames do not match") 59 | } 60 | 61 | closeRawInterface(t, dev) 62 | } 63 | 64 | func TestRawRecvTimeout(t *testing.T) { 65 | dev := openRawInterface(t) 66 | 67 | err := dev.SetRecvTimeout(50 * time.Millisecond) 68 | if err != nil { 69 | t.Errorf("error setting raw timeout: %v", err) 70 | } 71 | 72 | _, err = dev.RecvFrame() 73 | if err == nil { 74 | t.Error("did not timeout") 75 | } 76 | 77 | closeRawInterface(t, dev) 78 | } 79 | 80 | func TestRawWrongFunctions(t *testing.T) { 81 | dev := openRawInterface(t) 82 | 83 | err := dev.SendBuf([]byte{1, 2, 3}) 84 | if err == nil { 85 | t.Errorf("no error trying to SendBuf with raw interface") 86 | } 87 | 88 | _, err = dev.RecvBuf() 89 | if err == nil { 90 | t.Errorf("no error trying to RecvBuf with raw interface") 91 | } 92 | 93 | closeRawInterface(t, dev) 94 | } 95 | -------------------------------------------------------------------------------- /test/isotp_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | "time" 7 | 8 | "github.com/linklayer/go-socketcan/pkg/socketcan" 9 | ) 10 | 11 | func openIsotpInterface(t *testing.T, rxID uint32, txID uint32) socketcan.Interface { 12 | dev, err := socketcan.NewIsotpInterface("vcan0", rxID, txID) 13 | if err != nil { 14 | t.Errorf("could not create CAN device: %v", err) 15 | } 16 | return dev 17 | } 18 | 19 | func closeIsotpInterface(t *testing.T, dev socketcan.Interface) { 20 | err := dev.Close() 21 | if err != nil { 22 | t.Errorf("could not close CAN device: %v", err) 23 | } 24 | } 25 | 26 | func TestIsotpOpenClose(t *testing.T) { 27 | dev := openIsotpInterface(t, 0x100, 0x200) 28 | closeIsotpInterface(t, dev) 29 | } 30 | 31 | func TestIsotpRecvTimeout(t *testing.T) { 32 | dev := openIsotpInterface(t, 0x100, 0x200) 33 | err := dev.SetRecvTimeout(50 * time.Millisecond) 34 | if err != nil { 35 | t.Errorf("error setting isotp timeout: %v", err) 36 | } 37 | 38 | _, err = dev.RecvBuf() 39 | if err == nil { 40 | t.Error("did not timeout") 41 | } 42 | } 43 | 44 | func TestIsotpTxRxSingleFrame(t *testing.T) { 45 | txData := []byte{1, 2, 3, 4, 5, 6, 7} 46 | 47 | txDev := openIsotpInterface(t, 0x100, 0x200) 48 | rxDev := openIsotpInterface(t, 0x200, 0x100) 49 | 50 | go func() { 51 | time.Sleep(50 * time.Millisecond) 52 | err := txDev.SendBuf(txData) 53 | if err != nil { 54 | t.Errorf("error sending frame: %v", err) 55 | } 56 | }() 57 | 58 | rxData, err := rxDev.RecvBuf() 59 | if err != nil { 60 | t.Errorf("error receiving frame: %v", err) 61 | } 62 | 63 | if !bytes.Equal(rxData, txData) { 64 | t.Error("sent and received data does not match") 65 | } 66 | 67 | closeIsotpInterface(t, txDev) 68 | closeIsotpInterface(t, rxDev) 69 | } 70 | 71 | func TestIsotpTxRxMultiFrame(t *testing.T) { 72 | txData := make([]byte, 4095) 73 | for i := 0; i < 4095; i++ { 74 | txData[i] = byte(i) 75 | } 76 | 77 | txDev := openIsotpInterface(t, 0x100, 0x200) 78 | rxDev := openIsotpInterface(t, 0x200, 0x100) 79 | 80 | go func() { 81 | time.Sleep(50 * time.Millisecond) 82 | err := txDev.SendBuf(txData) 83 | if err != nil { 84 | t.Errorf("error sending frame: %v", err) 85 | } 86 | }() 87 | 88 | rxData, err := rxDev.RecvBuf() 89 | if err != nil { 90 | t.Errorf("error receiving frame: %v", err) 91 | } 92 | 93 | if !bytes.Equal(rxData, txData) { 94 | t.Error("sent and received data does not match") 95 | } 96 | 97 | closeIsotpInterface(t, txDev) 98 | closeIsotpInterface(t, rxDev) 99 | } 100 | 101 | func TestIsotpWrongFunctions(t *testing.T) { 102 | dev := openIsotpInterface(t, 0x100, 0x200) 103 | 104 | err := dev.SendFrame(socketcan.CanFrame{}) 105 | if err == nil { 106 | t.Errorf("no error trying to SendFrame with isotp interface") 107 | } 108 | 109 | _, err = dev.RecvFrame() 110 | if err == nil { 111 | t.Errorf("no error trying to RecvFrame with raw interface") 112 | } 113 | 114 | closeRawInterface(t, dev) 115 | } 116 | 117 | func TestIsotpTxPadding(t *testing.T) { 118 | txData := make([]byte, 10) 119 | for i := 0; i < 10; i++ { 120 | txData[i] = byte(i) 121 | } 122 | 123 | txDev := openIsotpInterface(t, 0x100, 0x200) 124 | rxDev := openIsotpInterface(t, 0x200, 0x100) 125 | txDev.SetTxPadding(true, 0xAA) 126 | rxDev.SetTxPadding(true, 0xBB) 127 | 128 | go func() { 129 | time.Sleep(50 * time.Millisecond) 130 | err := txDev.SendBuf(txData) 131 | if err != nil { 132 | t.Errorf("error sending frame: %v", err) 133 | } 134 | }() 135 | 136 | rxData, err := rxDev.RecvBuf() 137 | if err != nil { 138 | t.Errorf("error receiving frame: %v", err) 139 | } 140 | 141 | if !bytes.Equal(rxData, txData) { 142 | t.Error("sent and received data does not match") 143 | } 144 | 145 | closeIsotpInterface(t, txDev) 146 | closeIsotpInterface(t, rxDev) 147 | } 148 | --------------------------------------------------------------------------------