From 80a7d3b7fdf4b16cbacd8106e3cde7e25a52b78d Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:10:02 +0800 Subject: [PATCH] dhcpd: use unicast to reply messages --- go.mod | 4 +- go.sum | 13 +++++-- service/dhcpd/dhcpd.go | 9 +++-- service/dhcpd/reply.go | 84 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 service/dhcpd/reply.go diff --git a/go.mod b/go.mod index 09f1398..b7251a1 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152 - github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 + github.com/insomniacslk/dhcp v0.0.0-20240129002554-15c9b8791914 github.com/nadoo/conflag v0.3.1 github.com/nadoo/ipset v0.5.0 github.com/xtaci/kcp-go/v5 v5.6.7 @@ -19,7 +19,7 @@ require ( github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect - github.com/klauspost/reedsolomon v1.12.0 // indirect + github.com/klauspost/reedsolomon v1.12.1 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/templexxx/cpu v0.1.0 // indirect diff --git a/go.sum b/go.sum index 29217b9..eeb7cc3 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,10 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/insomniacslk/dhcp v0.0.0-20240129002554-15c9b8791914 h1:kD8PseueGeYiid/Mmcv17Q0Qqicc4F46jcX22L/e/Hs= +github.com/insomniacslk/dhcp v0.0.0-20240129002554-15c9b8791914/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -44,8 +46,8 @@ github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= -github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno= -github.com/klauspost/reedsolomon v1.12.0/go.mod h1:EPLZJeh4l27pUGC3aXOjheaoh1I9yut7xTURiW3LQ9Y= +github.com/klauspost/reedsolomon v1.12.1 h1:NhWgum1efX1x58daOBGCFWcxtEhOhXKKl1HAPQUp03Q= +github.com/klauspost/reedsolomon v1.12.1/go.mod h1:nEi5Kjb6QqtbofI6s+cbG/j1da11c96IBYBSnVGtuBs= github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= @@ -95,6 +97,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -135,6 +139,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/service/dhcpd/dhcpd.go b/service/dhcpd/dhcpd.go index d6dceb7..58d971f 100644 --- a/service/dhcpd/dhcpd.go +++ b/service/dhcpd/dhcpd.go @@ -163,12 +163,13 @@ func (d *dhcpd) handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4 replyType = dhcpv4.MessageTypeNak } - reply, err := dhcpv4.NewReplyFromRequest(m, + resp, err := dhcpv4.NewReplyFromRequest(m, dhcpv4.WithMessageType(replyType), dhcpv4.WithNetmask(mask), dhcpv4.WithYourIP(replyIP.AsSlice()), dhcpv4.WithRouter(serverIP), dhcpv4.WithDNS(serverIP), + dhcpv4.WithServerIP(serverIP), // // RFC 2131, Section 4.3.1. IP lease time: MUST dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(d.lease)), // RFC 2131, Section 4.3.1. Server Identifier: MUST @@ -179,14 +180,14 @@ func (d *dhcpd) handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4 return } - if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil { + if err := reply(d.iface, resp); err != nil { log.F("[dpcpd] %s: could not write to %v(%v): %s", - d.name, reply.ClientHWAddr, peer, err) + d.name, resp.ClientHWAddr, peer, err) return } log.F("[dpcpd] %s: %s to %v for %v", - d.name, replyType, reply.ClientHWAddr, replyIP) + d.name, replyType, resp.ClientHWAddr, replyIP) } } diff --git a/service/dhcpd/reply.go b/service/dhcpd/reply.go new file mode 100644 index 0000000..39f6949 --- /dev/null +++ b/service/dhcpd/reply.go @@ -0,0 +1,84 @@ +package dhcpd + +import ( + "encoding/binary" + "fmt" + "net" + "syscall" + + "github.com/insomniacslk/dhcp/dhcpv4" + + "github.com/nadoo/glider/pkg/log" +) + +func reply(iface *net.Interface, resp *dhcpv4.DHCPv4) error { + p := [590]byte{12: 0x08, //ethernet layer: 14 bytes + 14: 0x45, 16: 0x02, 17: 0x40, 22: 0x40, 23: 0x11, //ip layer: 20 bytes + 35: 67, 37: 68, 38: 0x02, 39: 0x2c, //udp layer: 8 bytes + } + + copy(p[0:], resp.ClientHWAddr[0:6]) + copy(p[6:], iface.HardwareAddr[0:6]) + copy(p[26:], resp.ServerIPAddr[0:4]) + copy(p[30:], resp.YourIPAddr[0:4]) + + // ip layer checksum + checksum := checksum(p[14:34]) + binary.BigEndian.PutUint16(p[24:], checksum) + + // dhcp payload + copy(p[42:], resp.ToBytes()) + + // udp layer checksum, set to zero + // https://datatracker.ietf.org/doc/html/rfc768 + // An all zero transmitted checksum value means that the transmitter generated no + // checksum (for debugging or for higher level protocols that don't care). + + fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, 0) + if err != nil { + return fmt.Errorf("cannot open socket: %v", err) + } + defer func() { + err = syscall.Close(fd) + if err != nil { + log.F("dhcpd: cannot close socket: %v", err) + } + }() + + err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + if err != nil { + log.F("dhcpd: cannot set option for socket: %v", err) + } + + var hwAddr [8]byte + copy(hwAddr[0:6], resp.ClientHWAddr[0:6]) + ethAddr := syscall.SockaddrLinklayer{ + Protocol: 0, + Ifindex: iface.Index, + Halen: 6, + Addr: hwAddr, + } + err = syscall.Sendto(fd, p[:], 0, ðAddr) + if err != nil { + return fmt.Errorf("cannot send frame via socket: %v", err) + } + return nil +} + +func checksum(bytes []byte) uint16 { + var csum uint32 + for i := 0; i < len(bytes); i += 2 { + csum += uint32(bytes[i]) << 8 + csum += uint32(bytes[i+1]) + } + for { + // Break when sum is less or equals to 0xFFFF + if csum <= 65535 { + break + } + // Add carry to the sum + csum = (csum >> 16) + uint32(uint16(csum)) + } + // Flip all the bits + return ^uint16(csum) +}