func (l *DHCPLease) commit(ack *dhcp4.Packet) error { opts := ack.ParseOptions() leaseTime, err := parseLeaseTime(opts) if err != nil { return err } rebindingTime, err := parseRebindingTime(opts) if err != nil || rebindingTime > leaseTime { // Per RFC 2131 Section 4.4.5, it should default to 85% of lease time rebindingTime = leaseTime * 85 / 100 } renewalTime, err := parseRenewalTime(opts) if err != nil || renewalTime > rebindingTime { // Per RFC 2131 Section 4.4.5, it should default to 50% of lease time renewalTime = leaseTime / 2 } now := time.Now() l.expireTime = now.Add(leaseTime) l.renewalTime = now.Add(renewalTime) l.rebindingTime = now.Add(rebindingTime) l.ack = ack l.opts = opts return nil }
/* * Retreive Acknowledgement * Wait for the offer for a specific Request Packet. */ func (c *Client) GetAcknowledgement(requestPacket *dhcp4.Packet) (dhcp4.Packet, error) { for { c.connection.SetReadTimeout(c.timeout) readBuffer, source, err := c.connection.ReadFrom() if err != nil { return dhcp4.Packet{}, err } acknowledgementPacket := dhcp4.Packet(readBuffer) acknowledgementPacketOptions := acknowledgementPacket.ParseOptions() // Ignore Servers in my Ignore list for _, ignoreServer := range c.ignoreServers { if source.Equal(ignoreServer) { continue } if acknowledgementPacket.SIAddr().Equal(ignoreServer) { continue } } if !bytes.Equal(requestPacket.XId(), acknowledgementPacket.XId()) || len(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || (dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK && dhcp4.MessageType(acknowledgementPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.NAK) { continue } return acknowledgementPacket, nil } }
func (c *client) appendOptions(p *dhcp4.Packet, id ID) (*dhcp4.Packet, error) { p.AddOption( dhcp4.OptionParameterRequestList, []byte{ byte(dhcp4.OptionSubnetMask), byte(dhcp4.OptionRouter), byte(dhcp4.OptionDomainNameServer), }, ) b, err := id.MarshalBinary() if err != nil { return nil, err } p.AddOption(dhcp4.OptionClientIdentifier, b) return p, nil }
/* * Create DHCP Decline */ func (s *Server) DeclinePacket(requestPacket dhcp4.Packet) dhcp4.Packet { declinePacket := dhcp4.NewPacket(dhcp4.BootReply) declinePacket.SetXId(requestPacket.XId()) declinePacket.SetFlags(requestPacket.Flags()) declinePacket.SetGIAddr(requestPacket.GIAddr()) declinePacket.SetCHAddr(requestPacket.CHAddr()) declinePacket.SetSecs(requestPacket.Secs()) declinePacket.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.NAK)}) declinePacket.AddOption(dhcp4.OptionSubnetMask, s.subnetMask.To4()) declinePacket.AddOption(dhcp4.OptionRouter, s.defaultGateway.To4()) declinePacket.AddOption(dhcp4.OptionDomainNameServer, dhcp4.JoinIPs(s.dnsServers)) declinePacket.AddOption(dhcp4.OptionServerIdentifier, s.ip.To4()) return declinePacket }
/* * Create Request Packet */ func (c *Client) RequestPacket(offerPacket *dhcp4.Packet) dhcp4.Packet { offerOptions := offerPacket.ParseOptions() packet := dhcp4.NewPacket(dhcp4.BootRequest) packet.SetCHAddr(c.hardwareAddr) packet.SetXId(offerPacket.XId()) packet.SetCIAddr(offerPacket.CIAddr()) packet.SetSIAddr(offerPacket.SIAddr()) packet.SetBroadcast(c.broadcast) packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)}) packet.AddOption(dhcp4.OptionRequestedIPAddress, (offerPacket.YIAddr()).To4()) packet.AddOption(dhcp4.OptionServerIdentifier, offerOptions[dhcp4.OptionServerIdentifier]) //packet.PadToMinSize() return packet }
func DHCPDeclinePacket(hw net.HardwareAddr, ip net.IP, acknowledgement dhcp4.Packet) dhcp4.Packet { messageid := make([]byte, 4) if _, err := rand.Read(messageid); err != nil { panic(err) } acknowledgementOptions := acknowledgement.ParseOptions() packet := dhcp4.NewPacket(dhcp4.BootRequest) packet.SetCHAddr(hw) packet.SetCIAddr(ip) packet.SetXId(messageid) packet.SetBroadcast(false) packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Decline)}) packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier]) return packet }
/* * Create Request Packet For a Renew */ func (c *Client) RenewalRequestPacket(acknowledgement *dhcp4.Packet) dhcp4.Packet { messageid := make([]byte, 4) if _, err := rand.Read(messageid); err != nil { panic(err) } acknowledgementOptions := acknowledgement.ParseOptions() packet := dhcp4.NewPacket(dhcp4.BootRequest) packet.SetCHAddr(acknowledgement.CHAddr()) packet.SetXId(messageid) packet.SetCIAddr(acknowledgement.YIAddr()) packet.SetSIAddr(acknowledgement.SIAddr()) packet.SetBroadcast(c.broadcast) packet.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Request)}) packet.AddOption(dhcp4.OptionRequestedIPAddress, (acknowledgement.YIAddr()).To4()) packet.AddOption(dhcp4.OptionServerIdentifier, acknowledgementOptions[dhcp4.OptionServerIdentifier]) //packet.PadToMinSize() return packet }
// setOptions sets dhcp options on a dhcp packet func (c *client) setOptions(p dhcp4.Packet) (dhcp4.Packet, error) { defer trace.End(trace.Begin("")) dirty := false opts := p.ParseOptions() // the current parameter request list rl := opts[dhcp4.OptionParameterRequestList] // figure out if there are any new parameters for _, p := range c.params { if bytes.IndexByte(rl, p) == -1 { dirty = true rl = append(rl, p) } } opts[dhcp4.OptionParameterRequestList] = rl if _, ok := opts[dhcp4.OptionClientIdentifier]; !ok { b, err := c.id.MarshalBinary() if err != nil { return p, err } opts[dhcp4.OptionClientIdentifier] = b dirty = true } // finally reset the options on the packet, if necessary if dirty { // strip out all options, and add them back in with the new changed options; // this is the only way currently to delete/modify a packet option p.StripOptions() // have to copy since values in opts (of type []byte) are still pointing into p var newp dhcp4.Packet newp = make([]byte, len(p)) copy(newp, p) log.Debugf("opts=%#v", opts) for o, v := range opts { newp.AddOption(o, v) } p = newp } return p, nil }
/* * Get Lease tries to work out the best lease for the packet supplied. * Taking into account all Requested IP, Exisitng MACAddresses and Free leases. */ func (s *Server) GetLease(packet dhcp4.Packet) (found bool, lease leasepool.Lease, err error) { packetOptions := packet.ParseOptions() //Requested an IP if (len(packetOptions) > 0) && packetOptions[dhcp4.OptionRequestedIPAddress] != nil && !net.IP(packetOptions[dhcp4.OptionRequestedIPAddress]).Equal(net.IP{}) { //An IP Has Been Requested Let's Try and Get that One. found, lease, err = s.leasePool.GetLease(net.IP(packetOptions[dhcp4.OptionRequestedIPAddress])) if err != nil { return } if found { if lease.Status == leasepool.Free { //Lease Is Free you Can Have it. return } if lease.Status != leasepool.Free && bytes.Equal(lease.MACAddress, packet.CHAddr()) { //Lease isn't free but it's yours return } } } //Ok Even if you requested an IP you can't have it. found, lease, err = s.leasePool.GetLeaseForHardwareAddress(packet.CHAddr()) if found || err != nil { return } //Just get the next free lease if you can. found, lease, err = s.leasePool.GetNextFreeLease() return }
/* * Create DHCP Offer Packet */ func (s *Server) OfferPacket(discoverPacket dhcp4.Packet) dhcp4.Packet { offerPacket := dhcp4.NewPacket(dhcp4.BootReply) offerPacket.SetXId(discoverPacket.XId()) offerPacket.SetFlags(discoverPacket.Flags()) offerPacket.SetCHAddr(discoverPacket.CHAddr()) offerPacket.SetGIAddr(discoverPacket.GIAddr()) offerPacket.SetSecs(discoverPacket.Secs()) //53 offerPacket.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Offer)}) //54 offerPacket.AddOption(dhcp4.OptionServerIdentifier, s.ip.To4()) //51 offerPacket.AddOption(dhcp4.OptionIPAddressLeaseTime, dhcp4.OptionsLeaseTime(s.leaseDuration)) //Other options go in requested order... discoverPacketOptions := discoverPacket.ParseOptions() ourOptions := make(dhcp4.Options) //1 ourOptions[dhcp4.OptionSubnetMask] = s.subnetMask.To4() //3 ourOptions[dhcp4.OptionRouter] = s.defaultGateway.To4() //6 ourOptions[dhcp4.OptionDomainNameServer] = dhcp4.JoinIPs(s.dnsServers) if discoverPacketOptions[dhcp4.OptionParameterRequestList] != nil { //Loop through the requested options and if we have them add them. for _, optionCode := range discoverPacketOptions[dhcp4.OptionParameterRequestList] { if !bytes.Equal(ourOptions[dhcp4.OptionCode(optionCode)], []byte{}) { offerPacket.AddOption(dhcp4.OptionCode(optionCode), ourOptions[dhcp4.OptionCode(optionCode)]) delete(ourOptions, dhcp4.OptionCode(optionCode)) } } } //Add all the options not requested. for optionCode, optionValue := range ourOptions { offerPacket.AddOption(optionCode, optionValue) } return offerPacket }
func (s *Server) ServeDHCP(packet dhcp4.Packet) (dhcp4.Packet, error) { packetOptions := packet.ParseOptions() switch dhcp4.MessageType(packetOptions[dhcp4.OptionDHCPMessageType][0]) { case dhcp4.Discover: //Discover Received from client //Lets get the lease we're going to send them found, lease, err := s.GetLease(packet) if err != nil { return dhcp4.Packet{}, err } if !found { log.Println("Warning: It Looks Like Our Leases Are Depleted...") return dhcp4.Packet{}, nil } offerPacket := s.OfferPacket(packet) offerPacket.SetYIAddr(lease.IP) //Sort out the packet options offerPacket.PadToMinSize() lease.Status = leasepool.Reserved lease.MACAddress = packet.CHAddr() //If the lease expires within the next 5 Mins increase the lease expiary (Giving the Client 5 mins to complete) if lease.Expiry.Before(time.Now().Add(time.Minute * 5)) { lease.Expiry = time.Now().Add(time.Minute * 5) } if packetOptions[dhcp4.OptionHostName] != nil && string(packetOptions[dhcp4.OptionHostName]) != "" { lease.Hostname = string(packetOptions[dhcp4.OptionHostName]) } updated, err := s.leasePool.UpdateLease(lease) if err != nil { return dhcp4.Packet{}, err } if !updated { //Unable to reserve lease (It's now active else where maybe?) return dhcp4.Packet{}, errors.New("Unable to Reserve Lease:" + lease.IP.String()) } return offerPacket, nil case dhcp4.Request: //Request Received from client //Lets get the lease we're going to send them found, lease, err := s.GetLease(packet) if err != nil { return dhcp4.Packet{}, err } if !found { log.Println("Warning: It Looks Like Our Leases Are Depleted...") return dhcp4.Packet{}, nil } //If the lease is not the one requested We should send a NAK.. if len(packetOptions) > 0 && !net.IP(packetOptions[dhcp4.OptionRequestedIPAddress]).Equal(lease.IP) { //NAK declinePacket := s.DeclinePacket(packet) declinePacket.PadToMinSize() return declinePacket, nil } else { lease.Status = leasepool.Active lease.MACAddress = packet.CHAddr() lease.Expiry = time.Now().Add(s.leaseDuration) if packetOptions[dhcp4.OptionHostName] != nil && string(packetOptions[dhcp4.OptionHostName]) != "" { lease.Hostname = string(packetOptions[dhcp4.OptionHostName]) } updated, err := s.leasePool.UpdateLease(lease) if err != nil { return dhcp4.Packet{}, err } if updated { //ACK acknowledgementPacket := s.AcknowledgementPacket(packet) acknowledgementPacket.SetYIAddr(lease.IP) //Lease time. acknowledgementPacket.AddOption(dhcp4.OptionIPAddressLeaseTime, dhcp4.OptionsLeaseTime(lease.Expiry.Sub(time.Now()))) acknowledgementPacket.PadToMinSize() return acknowledgementPacket, nil } else { //NAK declinePacket := s.DeclinePacket(packet) declinePacket.PadToMinSize() return declinePacket, nil } } case dhcp4.Decline: //Decline from the client: log.Printf("Debug: Decline Message:%v\n", packet) case dhcp4.Release: //Decline from the client: log.Printf("Debug: Release Message:%v\n", packet) default: log.Printf("Debug: Unexpected Packet Type:%v\n", dhcp4.MessageType(packetOptions[dhcp4.OptionDHCPMessageType][0])) } return dhcp4.Packet{}, nil }
/* * Retreive Offer... * Wait for the offer for a specific Discovery Packet. */ func (c *Client) GetOffer(discoverPacket *dhcp4.Packet) (dhcp4.Packet, error) { for { c.connection.SetReadTimeout(c.timeout) readBuffer, source, err := c.connection.ReadFrom() if err != nil { return dhcp4.Packet{}, err } offerPacket := dhcp4.Packet(readBuffer) offerPacketOptions := offerPacket.ParseOptions() // Ignore Servers in my Ignore list for _, ignoreServer := range c.ignoreServers { if source.Equal(ignoreServer) { continue } if offerPacket.SIAddr().Equal(ignoreServer) { continue } } if len(offerPacketOptions[dhcp4.OptionDHCPMessageType]) < 1 || dhcp4.MessageType(offerPacketOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.Offer || !bytes.Equal(discoverPacket.XId(), offerPacket.XId()) { continue } return offerPacket, nil } }