packetinfo_linux.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. //go:build linux
  2. package discovery
  3. import (
  4. "encoding/binary"
  5. "net"
  6. "syscall"
  7. )
  8. func enableLocalAddrControl(conn *net.UDPConn) error {
  9. rawConn, err := conn.SyscallConn()
  10. if err != nil {
  11. return err
  12. }
  13. var controlErr error
  14. err = rawConn.Control(func(fd uintptr) {
  15. controlErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1)
  16. })
  17. if err != nil {
  18. return err
  19. }
  20. return controlErr
  21. }
  22. func readFromUDP(conn *net.UDPConn, buf []byte) (int, *net.UDPAddr, udpPacketInfo, error) {
  23. oob := make([]byte, 128)
  24. n, oobn, _, remote, err := conn.ReadMsgUDP(buf, oob)
  25. if err != nil {
  26. return 0, nil, udpPacketInfo{}, err
  27. }
  28. packetInfo := parsePacketInfo(oob[:oobn])
  29. return n, remote, packetInfo, nil
  30. }
  31. func parsePacketInfo(oob []byte) udpPacketInfo {
  32. messages, err := syscall.ParseSocketControlMessage(oob)
  33. if err != nil {
  34. return udpPacketInfo{}
  35. }
  36. for _, message := range messages {
  37. if message.Header.Level != syscall.IPPROTO_IP || message.Header.Type != syscall.IP_PKTINFO || len(message.Data) < 12 {
  38. continue
  39. }
  40. specDst := [4]byte(message.Data[4:8])
  41. addr := [4]byte(message.Data[8:12])
  42. return udpPacketInfo{
  43. localIP: packetInfoIP(specDst, addr),
  44. ifIndex: int(int32(binary.LittleEndian.Uint32(message.Data[0:4]))),
  45. }
  46. }
  47. return udpPacketInfo{}
  48. }
  49. func packetInfoIP(specDst [4]byte, addr [4]byte) net.IP {
  50. if ip := ipv4FromBytes(specDst); ip != nil {
  51. return ip
  52. }
  53. return ipv4FromBytes(addr)
  54. }
  55. func ipv4FromBytes(value [4]byte) net.IP {
  56. if value == [4]byte{} || value == [4]byte{255, 255, 255, 255} {
  57. return nil
  58. }
  59. return net.IPv4(value[0], value[1], value[2], value[3]).To4()
  60. }