pktsock_linux.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package dhcp4client
  2. import (
  3. "crypto/rand"
  4. "encoding/binary"
  5. "net"
  6. "time"
  7. "golang.org/x/sys/unix"
  8. )
  9. const (
  10. minIPHdrLen = 20
  11. maxIPHdrLen = 60
  12. udpHdrLen = 8
  13. ip4Ver = 0x40
  14. ttl = 16
  15. srcPort = 68
  16. dstPort = 67
  17. )
  18. var (
  19. bcastMAC = []byte{255, 255, 255, 255, 255, 255}
  20. )
  21. // abstracts AF_PACKET
  22. type packetSock struct {
  23. fd int
  24. ifindex int
  25. }
  26. func NewPacketSock(ifindex int) (*packetSock, error) {
  27. fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, int(swap16(unix.ETH_P_IP)))
  28. if err != nil {
  29. return nil, err
  30. }
  31. addr := unix.SockaddrLinklayer{
  32. Ifindex: ifindex,
  33. Protocol: swap16(unix.ETH_P_IP),
  34. }
  35. if err = unix.Bind(fd, &addr); err != nil {
  36. return nil, err
  37. }
  38. return &packetSock{
  39. fd: fd,
  40. ifindex: ifindex,
  41. }, nil
  42. }
  43. func (pc *packetSock) Close() error {
  44. return unix.Close(pc.fd)
  45. }
  46. func (pc *packetSock) Write(packet []byte) error {
  47. lladdr := unix.SockaddrLinklayer{
  48. Ifindex: pc.ifindex,
  49. Protocol: swap16(unix.ETH_P_IP),
  50. Halen: uint8(len(bcastMAC)),
  51. }
  52. copy(lladdr.Addr[:], bcastMAC)
  53. pkt := make([]byte, minIPHdrLen+udpHdrLen+len(packet))
  54. fillIPHdr(pkt[0:minIPHdrLen], udpHdrLen+uint16(len(packet)))
  55. fillUDPHdr(pkt[minIPHdrLen:minIPHdrLen+udpHdrLen], uint16(len(packet)))
  56. // payload
  57. copy(pkt[minIPHdrLen+udpHdrLen:len(pkt)], packet)
  58. return unix.Sendto(pc.fd, pkt, 0, &lladdr)
  59. }
  60. func (pc *packetSock) ReadFrom() ([]byte, net.IP, error) {
  61. pkt := make([]byte, maxIPHdrLen+udpHdrLen+MaxDHCPLen)
  62. n, _, err := unix.Recvfrom(pc.fd, pkt, 0)
  63. if err != nil {
  64. return nil, nil, err
  65. }
  66. // IP hdr len
  67. ihl := int(pkt[0]&0x0F) * 4
  68. // Source IP address
  69. src := net.IP(pkt[12:16])
  70. return pkt[ihl+udpHdrLen : n], src, nil
  71. }
  72. func (pc *packetSock) SetReadTimeout(t time.Duration) error {
  73. tv := unix.NsecToTimeval(t.Nanoseconds())
  74. return unix.SetsockoptTimeval(pc.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv)
  75. }
  76. // compute's 1's complement checksum
  77. func chksum(p []byte, csum []byte) {
  78. cklen := len(p)
  79. s := uint32(0)
  80. for i := 0; i < (cklen - 1); i += 2 {
  81. s += uint32(p[i+1])<<8 | uint32(p[i])
  82. }
  83. if cklen&1 == 1 {
  84. s += uint32(p[cklen-1])
  85. }
  86. s = (s >> 16) + (s & 0xffff)
  87. s = s + (s >> 16)
  88. s = ^s
  89. csum[0] = uint8(s & 0xff)
  90. csum[1] = uint8(s >> 8)
  91. }
  92. func fillIPHdr(hdr []byte, payloadLen uint16) {
  93. // version + IHL
  94. hdr[0] = ip4Ver | (minIPHdrLen / 4)
  95. // total length
  96. binary.BigEndian.PutUint16(hdr[2:4], uint16(len(hdr))+payloadLen)
  97. // identification
  98. if _, err := rand.Read(hdr[4:5]); err != nil {
  99. panic(err)
  100. }
  101. // TTL
  102. hdr[8] = 16
  103. // Protocol
  104. hdr[9] = unix.IPPROTO_UDP
  105. // dst IP
  106. copy(hdr[16:20], net.IPv4bcast.To4())
  107. // compute IP hdr checksum
  108. chksum(hdr[0:len(hdr)], hdr[10:12])
  109. }
  110. func fillUDPHdr(hdr []byte, payloadLen uint16) {
  111. // src port
  112. binary.BigEndian.PutUint16(hdr[0:2], srcPort)
  113. // dest port
  114. binary.BigEndian.PutUint16(hdr[2:4], dstPort)
  115. // length
  116. binary.BigEndian.PutUint16(hdr[4:6], udpHdrLen+payloadLen)
  117. }
  118. func swap16(x uint16) uint16 {
  119. var b [2]byte
  120. binary.BigEndian.PutUint16(b[:], x)
  121. return binary.LittleEndian.Uint16(b[:])
  122. }