github.com/blueinnovationsgroup/can-go@v0.0.0-20230518195432-d0567cda0028/pkg/socketcan/udp.go (about) 1 package socketcan 2 3 import ( 4 "fmt" 5 "net" 6 "strconv" 7 "time" 8 9 "golang.org/x/net/ipv4" 10 "golang.org/x/net/nettest" 11 ) 12 13 // udpTxRx emulates a single `net.Conn` that can be used for both transmitting 14 // and receiving UDP multicast packets. 15 type udpTxRx struct { 16 tx *ipv4.PacketConn 17 rx *ipv4.PacketConn 18 groupAddr *net.UDPAddr 19 } 20 21 func (utr *udpTxRx) Close() error { 22 if err := utr.tx.Close(); err != nil { 23 _ = utr.rx.Close() 24 return err 25 } 26 return utr.rx.Close() 27 } 28 29 func (utr *udpTxRx) LocalAddr() net.Addr { 30 return utr.rx.LocalAddr() 31 } 32 33 func (utr *udpTxRx) SetDeadline(t time.Time) error { 34 if err := utr.rx.SetReadDeadline(t); err != nil { 35 return err 36 } 37 return utr.tx.SetWriteDeadline(t) 38 } 39 40 func (utr *udpTxRx) SetReadDeadline(t time.Time) error { 41 return utr.rx.SetReadDeadline(t) 42 } 43 44 func (utr *udpTxRx) SetWriteDeadline(t time.Time) error { 45 return utr.tx.SetWriteDeadline(t) 46 } 47 48 func (utr *udpTxRx) Read(b []byte) (n int, err error) { 49 n, _, _, err = utr.rx.ReadFrom(b) 50 return 51 } 52 53 func (utr *udpTxRx) Write(b []byte) (n int, err error) { 54 return utr.tx.WriteTo(b, nil, nil) 55 } 56 57 func (utr *udpTxRx) RemoteAddr() net.Addr { 58 return utr.groupAddr 59 } 60 61 func udpTransceiver(network, address string) (*udpTxRx, error) { 62 if network != udp { 63 return nil, fmt.Errorf("[%v] is not a udp network", network) 64 } 65 ifi, err := getMulticastInterface() 66 if err != nil { 67 return nil, fmt.Errorf("new UDP transceiver: %w", err) 68 } 69 rx, groupAddr, err := udpReceiver(address, ifi) 70 if err != nil { 71 return nil, fmt.Errorf("new UDP transceiver: %w", err) 72 } 73 tx, err := udpTransmitter(groupAddr, ifi) 74 if err != nil { 75 return nil, fmt.Errorf("new UDP transceiver: %w", err) 76 } 77 return &udpTxRx{rx: rx, tx: tx, groupAddr: groupAddr}, nil 78 } 79 80 func getMulticastInterface() (*net.Interface, error) { 81 ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) 82 if err == nil { 83 return ifi, nil 84 } 85 return nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast) 86 } 87 88 func hostPortToUDPAddr(hostport string) (*net.UDPAddr, error) { 89 host, portStr, err := net.SplitHostPort(hostport) 90 if err != nil { 91 return nil, fmt.Errorf("convert hostport to udp addr: %w", err) 92 } 93 port, err := strconv.Atoi(portStr) 94 if err != nil { 95 return nil, fmt.Errorf("convert hostport to udp addr: %w", err) 96 } 97 ip := net.ParseIP(host) 98 return &net.UDPAddr{Port: port, IP: ip}, nil 99 } 100 101 func setMulticastOpts(p *ipv4.PacketConn, ifi *net.Interface, groupAddr net.Addr) error { 102 if err := p.JoinGroup(ifi, groupAddr); err != nil { 103 return err 104 } 105 if err := p.SetMulticastInterface(ifi); err != nil { 106 return err 107 } 108 if err := p.SetMulticastLoopback(true); err != nil { 109 return err 110 } 111 if err := p.SetMulticastTTL(0); err != nil { 112 return err 113 } 114 return p.SetTOS(0x0) 115 } 116 117 func udpReceiver(address string, ifi *net.Interface) (*ipv4.PacketConn, *net.UDPAddr, error) { 118 c, err := net.ListenPacket("udp4", address) 119 if err != nil { 120 return nil, nil, fmt.Errorf("create udp receiver: %w", err) 121 } 122 groupAddr, err := hostPortToUDPAddr(address) 123 if err != nil { 124 return nil, nil, fmt.Errorf("create udp receiver: %w", err) 125 } 126 // If requested port is 0, one is provided when creating the packet listener 127 if groupAddr.Port == 0 { 128 localAddr, err := hostPortToUDPAddr(c.LocalAddr().String()) 129 if err != nil { 130 return nil, nil, fmt.Errorf("create udp receiver: %w", err) 131 } 132 groupAddr.Port = localAddr.Port 133 } 134 rx := ipv4.NewPacketConn(c) 135 if err := setMulticastOpts(rx, ifi, groupAddr); err != nil { 136 return nil, nil, fmt.Errorf("new UDP transceiver: %w", err) 137 } 138 return rx, groupAddr, nil 139 } 140 141 func udpTransmitter(groupAddr *net.UDPAddr, ifi *net.Interface) (*ipv4.PacketConn, error) { 142 c, err := net.DialUDP("udp4", nil, groupAddr) 143 if err != nil { 144 return nil, fmt.Errorf("new UDP transmitter: %w", err) 145 } 146 tx := ipv4.NewPacketConn(c) 147 if err := tx.SetMulticastInterface(ifi); err != nil { 148 return nil, fmt.Errorf("new UDP transmitter: %w", err) 149 } 150 return tx, nil 151 }