github.com/jordan-bonecutter/can-go@v0.0.0-20230901155856-d83995b18e50/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  }