We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Must add a reliable DHCP Server implementation. Below is work so far not present on any branch.
package stacks import ( "encoding/binary" "errors" "io" "net/netip" "time" "github.com/soypat/seqs/eth" "github.com/soypat/seqs/eth/dhcp" ) type dhcpclientv2 struct { mac [6]byte lastMsg time.Time addr [4]byte state uint8 port uint16 requestlist [10]byte } type clientcache struct { sli []dhcpclientv2 } func (cc *clientcache) get(mac [6]byte) *dhcpclientv2 { for i := range cc.sli { if cc.sli[i].mac == mac { return &cc.sli[i] } } return nil } func (cc *clientcache) put(newClient dhcpclientv2) error { for i := range cc.sli { if cc.sli[i].mac == ([6]byte{}) { cc.sli[i] = newClient break } } return errors.New("no more dhcp space") } func (cc *clientcache) delete(mac [6]byte) error { for i := range cc.sli { if cc.sli[i].mac == mac { cc.sli[i] = dhcpclientv2{} break } } return errors.New("no more dhcp space") } type DHCPServerV2 struct { stack *PortStack nextAddr netip.Addr siaddr netip.Addr port uint16 clients clientcache aborted bool lastPacket UDPPacket hasPacket bool // Aux variables used to store intermediate client options. auxMsgType dhcp.MessageType auxreqlist [10]byte auxreqip [4]byte } type DHCPServerConfigV2 struct { Address netip.AddrPort MaxClients int } func NewDHCPServerV2(ps *PortStack, cfg DHCPServerConfigV2) (*DHCPServerV2, error) { if ps == nil || cfg.Address.Port() == 0 { panic("nil portstack or local port") } return &DHCPServerV2{ stack: ps, clients: clientcache{sli: make([]dhcpclientv2, cfg.MaxClients)}, port: cfg.Address.Port(), siaddr: cfg.Address.Addr(), }, nil } func (d *DHCPServerV2) Start() (err error) { err = d.stack.OpenUDP(d.port, d) if d.aborted && err == nil { d.aborted = false // Clear abort on succesful open. } return err } func (d *DHCPServerV2) recv(pkt *UDPPacket) (err error) { if d.isAborted() { return io.EOF // Signal to close socket. } payload := pkt.Payload() if len(payload) <= dhcp.OptionsOffset { return errors.New("small dhcp packet") } rcvHdr := dhcp.DecodeHeaderV4(payload) if rcvHdr.SIAddr != d.stack.ip { return errors.New("dhcp addr mismatch") } d.auxMsgType = 0 d.auxreqip = [4]byte{} d.auxreqlist = [10]byte{} err = dhcp.ForEachOption(payload, d.parseopts) if err != nil { return err } client := d.clients.get(pkt.Eth.Source) switch d.auxMsgType { case dhcp.MsgDiscover: if client == nil { client = d.clients.get([6]byte{}) // Get next free client. if client == nil { return errors.New("dhcp server: no free client spaces") } } client.addr = pkt.IP.Source client.lastMsg = pkt.Rx client.state = dhcpStateGotOffer default: return errors.New("unexpected or no dhcp msgtype") } d.hasPacket = true d.lastPacket = *pkt return nil } func (d *DHCPServerV2) parseopts(opt dhcp.Option) error { switch opt.Num { case dhcp.OptMessageType: if len(opt.Data) == 1 { d.auxMsgType = dhcp.MessageType(opt.Data[0]) } case dhcp.OptParameterRequestList: d.auxreqlist = [10]byte{} copy(d.auxreqlist[:], opt.Data) case dhcp.OptRequestedIPaddress: if len(opt.Data) == 4 { d.auxreqip = [4]byte(opt.Data) } } return nil } func (d *DHCPServerV2) send(dst []byte) (int, error) { if d.isAborted() { return 0, io.EOF // Signal to close socket. } if !d.hasPacket { return 0, nil } n, err := d.handleUDP(dst, &d.lastPacket) d.hasPacket = false return n, err } func (d *DHCPServerV2) isPendingHandling() bool { return d.port != 0 && d.hasPacket } func (d *DHCPServerV2) isAborted() bool { return d.aborted } func (d *DHCPServerV2) abort() { d.stack.trace("dhcpserver:abort") for k := range d.hosts { delete(d.hosts, k) } *d = DHCPServerV2{ hosts: d.hosts, stack: d.stack, siaddr: d.siaddr, port: d.port, aborted: true, } } // handleUDP is a legacy packet handling routine. Used because it works. func (d *DHCPServerV2) handleUDP(resp []byte, packet *UDPPacket) (_ int, err error) { // First action is used to send data without having received a packet // so hasPacket will be false. hasPacket := d.hasPacket incpayload := packet.Payload() switch { case len(resp) < dhcp.SizeHeader: return 0, errors.New("short payload to marshall DHCP") case hasPacket && len(incpayload) < eth.SizeDHCPHeader: return 0, errors.New("short payload to parse DHCP") case !hasPacket: return 0, nil } rcvHdr := dhcp.DecodeHeaderV4(incpayload) mac := packet.Eth.Source client := d.hosts[mac] var msgType dhcp.MessageType err = dhcp.ForEachOption(incpayload, func(opt dhcp.Option) error { switch opt.Num { case dhcp.OptMessageType: if len(opt.Data) == 1 { msgType = dhcp.MessageType(opt.Data[0]) } case dhcp.OptParameterRequestList: client.requestlist = [10]byte{} copy(client.requestlist[:], opt.Data) case dhcp.OptRequestedIPaddress: if len(opt.Data) == 4 && client.state == dhcpStateNone { client.addr = netip.AddrFrom4([4]byte(opt.Data)) } } return nil }) if err != nil || (msgType != 1 && rcvHdr.SIAddr != d.siaddr.As4()) { return 0, err } var Options []dhcp.Option switch msgType { case dhcp.MsgDiscover: if client.state != dhcpStateNone { err = errors.New("DHCP Discover on initialized client") break } rcvHdr.YIAddr = d.next(client.addr.As4()) Options = []dhcp.Option{ {Num: dhcp.OptMessageType, Data: []byte{byte(dhcp.MsgOffer)}}, } rcvHdr.SIAddr = d.siaddr.As4() client.port = packet.UDP.SourcePort client.state = dhcpStateWaitOffer case dhcp.MsgRequest: if client.state != dhcpStateWaitOffer { err = errors.New("unexpected DHCP Request") break } Options = []dhcp.Option{ {Num: dhcp.OptMessageType, Data: []byte{byte(dhcp.MsgAck)}}, // DHCP Message Type: ACK } } if err != nil { return 0, nil } d.hosts[mac] = client const dhcpOffset = eth.SizeEthernetHeader + eth.SizeIPv4Header + eth.SizeUDPHeader for i := dhcpOffset + 14; i < len(resp); i++ { resp[i] = 0 // Zero out BOOTP and options fields. } rcvHdr.Put(resp[dhcpOffset:]) // Encode DHCP header + options. const magicCookie = 0x63825363 ptr := dhcpOffset + dhcp.MagicCookieOffset binary.BigEndian.PutUint32(resp[ptr:], magicCookie) ptr = dhcpOffset + dhcp.OptionsOffset for _, opt := range Options { n, err := opt.Encode(resp[ptr:]) if err != nil { return n, err } ptr += n } resp[ptr] = 0xff // endmark ptr++ // Set Ethernet+IP+UDP headers. payload := resp[dhcpOffset:ptr] d.setResponseUDP(client.port, packet, payload) packet.PutHeaders(resp) return ptr, nil } func (d *DHCPServer) next(requested [4]byte) [4]byte { if requested != [4]byte{} { return requested } return [4]byte{192, 168, 1, 2} } func (d *DHCPServer) setResponseUDP(clientport uint16, packet *UDPPacket, payload []byte) { const ipLenInWords = 5 // Ethernet frame. packet.Eth.Destination = eth.BroadcastHW6() packet.Eth.Source = d.stack.HardwareAddr6() packet.Eth.SizeOrEtherType = uint16(eth.EtherTypeIPv4) // IPv4 frame. packet.IP.Destination = [4]byte{} packet.IP.Source = d.siaddr.As4() // Source IP is always zeroed when client sends. packet.IP.Protocol = 17 // UDP packet.IP.TTL = 64 packet.IP.ID = prand16(packet.IP.ID) packet.IP.VersionAndIHL = ipLenInWords // Sets IHL: No IP options. Version set automatically. packet.IP.TotalLength = 4*ipLenInWords + eth.SizeUDPHeader + uint16(len(payload)) packet.IP.Checksum = packet.IP.CalculateChecksum() // TODO(soypat): Document why disabling ToS used by DHCP server may cause Request to fail. // Apparently server sets ToS=192. Uncommenting this line causes DHCP to fail on my setup. // If left fixed at 192, DHCP does not work. // If left fixed at 0, DHCP does not work. // Apparently ToS is a function of which state of DHCP one is in. Not sure why code below works. packet.IP.ToS = 192 packet.IP.Flags = 0 // UDP frame. packet.UDP.DestinationPort = clientport packet.UDP.SourcePort = d.port packet.UDP.Length = packet.IP.TotalLength - 4*ipLenInWords packet.UDP.Checksum = packet.UDP.CalculateChecksumIPv4(&packet.IP, payload) }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Must add a reliable DHCP Server implementation. Below is work so far not present on any branch.
The text was updated successfully, but these errors were encountered: