/* * Lets do a Full DHCP Request. */ func (c *Client) Request() (bool, dhcp4.Packet, error) { discoveryPacket, err := c.SendDiscoverPacket() if err != nil { return false, discoveryPacket, err } offerPacket, err := c.GetOffer(&discoveryPacket) if err != nil { return false, offerPacket, err } requestPacket, err := c.SendRequest(&offerPacket) if err != nil { return false, requestPacket, err } acknowledgement, err := c.GetAcknowledgement(&requestPacket) if err != nil { return false, acknowledgement, err } acknowledgementOptions := acknowledgement.ParseOptions() if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK { return false, acknowledgement, nil } return true, acknowledgement, 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 } }
func (c *client) renew(cl *dhcp4client.Client) (dhcp4.Packet, error) { defer trace.End(trace.Begin("")) rp := cl.RenewalRequestPacket(&c.ack) rp, err := c.setOptions(rp) if err != nil { return nil, err } rp.PadToMinSize() if err = cl.SendPacket(rp); err != nil { return nil, err } newack, err := cl.GetAcknowledgement(&rp) if err != nil { return nil, err } opts := newack.ParseOptions() if dhcp4.MessageType(opts[dhcp4.OptionDHCPMessageType][0]) == dhcp4.NAK { return nil, fmt.Errorf("received NAK from DHCP server") } return newack, nil }
func (c *client) request(cl *dhcp4client.Client) (bool, dhcp4.Packet, error) { defer trace.End(trace.Begin("")) dp, err := c.discoverPacket(cl) if err != nil { return false, nil, err } dp.PadToMinSize() if err = cl.SendPacket(dp); err != nil { return false, nil, err } var op dhcp4.Packet for { op, err = cl.GetOffer(&dp) if err != nil { return false, nil, err } if c.isCompletePacket(dhcp.NewPacket([]byte(op))) { break } } rp, err := c.requestPacket(cl, &op) if err != nil { return false, nil, err } rp.PadToMinSize() if err = cl.SendPacket(rp); err != nil { return false, nil, err } ack, err := cl.GetAcknowledgement(&rp) if err != nil { return false, nil, err } opts := ack.ParseOptions() if dhcp4.MessageType(opts[dhcp4.OptionDHCPMessageType][0]) == dhcp4.NAK { return false, nil, fmt.Errorf("Got NAK from DHCP server") } return true, ack, nil }
/* * Renew a lease backed on the Acknowledgement Packet. * Returns Sucessfull, The AcknoledgementPacket, Any Errors */ func (c *Client) Renew(acknowledgement dhcp4.Packet) (bool, dhcp4.Packet, error) { renewRequest := c.RenewalRequestPacket(&acknowledgement) renewRequest.PadToMinSize() err := c.SendPacket(renewRequest) if err != nil { return false, renewRequest, err } newAcknowledgement, err := c.GetAcknowledgement(&renewRequest) if err != nil { return false, newAcknowledgement, err } newAcknowledgementOptions := newAcknowledgement.ParseOptions() if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK { return false, newAcknowledgement, nil } return true, newAcknowledgement, nil }
func (c *client) renew(id ID, ack dhcp4.Packet, cl *dhcp4client.Client) (dhcp4.Packet, error) { rp := cl.RenewalRequestPacket(&ack) _, err := c.appendOptions(&rp, id) if err != nil { return nil, err } rp.PadToMinSize() if err = cl.SendPacket(rp); err != nil { return nil, err } newack, err := cl.GetAcknowledgement(&rp) if err != nil { return nil, err } opts := newack.ParseOptions() if dhcp4.MessageType(opts[dhcp4.OptionDHCPMessageType][0]) == dhcp4.NAK { return nil, fmt.Errorf("received NAK from DHCP server") } return newack, nil }
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 }
/* * Test Discovering a Lease That's not Within Our Lease Range. * This Happens When a devce switches network. * Example: Mobile Phone on Mobile internet Has IP 100.123.123.123 Switch To Home Wifi * The device requests 100.123.123.123 on Home Wifi which is out of range... */ func TestDiscoverOutOfRangeLease(test *testing.T) { //Setup the Server myServer, err := dhcp4server.New( net.IPv4(192, 168, 1, 201), getTestLeasePool(), dhcp4server.SetLocalAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1067}), dhcp4server.SetRemoteAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1068}), ) if err != nil { test.Error("Error: Can't Configure Server " + err.Error()) } var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() err := myServer.ListenAndServe() if err != nil { log.Fatalln("Error Starting Server:" + err.Error()) } }() time.Sleep(time.Duration(5) * time.Second) //Generate Hardware Address HardwareMACAddress, err := hardwareaddr.GenerateEUI48() if err != nil { test.Error("Error: Can't Generate Valid MACAddress" + err.Error()) } //Lets Be A Client //We need to set the connection ports to 1068 and 1067 so we don't need root access c, err := dhcp4client.NewInetSock(dhcp4client.SetLocalAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1068}), dhcp4client.SetRemoteAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1067})) if err != nil { test.Error("Client Conection Generation:" + err.Error()) } client, err := dhcp4client.New(dhcp4client.HardwareAddr(HardwareMACAddress), dhcp4client.Connection(c)) defer client.Close() if err != nil { test.Error("Conection Error:" + err.Error()) } discoveryPacket := client.DiscoverPacket() discoveryPacket.SetCIAddr(net.IPv4(100, 102, 96, 123)) discoveryPacket.PadToMinSize() err = client.SendPacket(discoveryPacket) if err != nil { test.Error("Error: Sending Discover Packet" + err.Error()) } test.Log("--Discovery Packet--") test.Logf("Client IP : %v\n", discoveryPacket.CIAddr().String()) test.Logf("Your IP : %v\n", discoveryPacket.YIAddr().String()) test.Logf("Server IP : %v\n", discoveryPacket.SIAddr().String()) test.Logf("Gateway IP: %v\n", discoveryPacket.GIAddr().String()) test.Logf("Client Mac: %v\n", discoveryPacket.CHAddr().String()) if !bytes.Equal(discoveryPacket.CHAddr(), HardwareMACAddress) { test.Error("MACAddresses Don't Match??") } offerPacket, err := client.GetOffer(&discoveryPacket) if err != nil { test.Error("Error Getting Offer:" + err.Error()) } test.Log("--Offer Packet--") test.Logf("Client IP : %v\n", offerPacket.CIAddr().String()) test.Logf("Your IP : %v\n", offerPacket.YIAddr().String()) test.Logf("Server IP : %v\n", offerPacket.SIAddr().String()) test.Logf("Gateway IP: %v\n", offerPacket.GIAddr().String()) test.Logf("Client Mac: %v\n", offerPacket.CHAddr().String()) requestPacket, err := client.SendRequest(&offerPacket) if err != nil { test.Error("Error Sending Request:" + err.Error()) } test.Log("--Request Packet--") test.Logf("Client IP : %v\n", requestPacket.CIAddr().String()) test.Logf("Your IP : %v\n", requestPacket.YIAddr().String()) test.Logf("Server IP : %v\n", requestPacket.SIAddr().String()) test.Logf("Gateway IP: %v\n", requestPacket.GIAddr().String()) test.Logf("Client Mac: %v\n", requestPacket.CHAddr().String()) acknowledgement, err := client.GetAcknowledgement(&requestPacket) if err != nil { test.Error("Error Getting Acknowledgement:" + err.Error()) } test.Log("--Acknowledgement Packet--") test.Logf("Client IP : %v\n", acknowledgement.CIAddr().String()) test.Logf("Your IP : %v\n", acknowledgement.YIAddr().String()) test.Logf("Server IP : %v\n", acknowledgement.SIAddr().String()) test.Logf("Gateway IP: %v\n", acknowledgement.GIAddr().String()) test.Logf("Client Mac: %v\n", acknowledgement.CHAddr().String()) acknowledgementOptions := acknowledgement.ParseOptions() if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK { test.Error("Didn't get ACK?:" + err.Error()) } test.Log("Shutting Down Server") myServer.Shutdown() wg.Wait() }
/* * Try Renewing A Lease From A Different Network. */ func TestRequestOutOfRangeLease(test *testing.T) { //Setup the Server myServer, err := dhcp4server.New( net.IPv4(192, 168, 1, 201), getTestLeasePool(), dhcp4server.SetLocalAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1067}), dhcp4server.SetRemoteAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1068}), ) if err != nil { test.Error("Error: Can't Configure Server " + err.Error()) } var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() err := myServer.ListenAndServe() if err != nil { log.Fatalln("Error Starting Server:" + err.Error()) } }() //Sleep some so the server starts.... time.Sleep(time.Duration(5) * time.Second) //Generate Hardware Address HardwareMACAddress, err := hardwareaddr.GenerateEUI48() if err != nil { test.Error("Error: Can't Generate Valid MACAddress" + err.Error()) } HardwareMACAddress, err = net.ParseMAC("58-94-6B-73-57-0C") if err != nil { log.Printf("MAC Error:%v\n", err) } //Lets Be A Client c, err := dhcp4client.NewInetSock(dhcp4client.SetLocalAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1068}), dhcp4client.SetRemoteAddr(net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1067})) if err != nil { test.Error("Client Conection Generation:" + err.Error()) } client, err := dhcp4client.New(dhcp4client.HardwareAddr(HardwareMACAddress), dhcp4client.Connection(c)) defer client.Close() if err != nil { test.Error("Conection Error:" + err.Error()) } //Create a dummy offer packet offerPacket := client.DiscoverPacket() offerPacket.SetCIAddr(net.IPv4(100, 102, 96, 123)) offerPacket.SetSIAddr(net.IPv4(192, 168, 1, 201)) offerPacket.SetYIAddr(net.IPv4(100, 102, 96, 123)) offerPacket.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(dhcp4.Offer)}) requestPacket, err := client.SendRequest(&offerPacket) if err != nil { test.Error("Error Sending Request:" + err.Error()) } test.Log("--Request Packet--") test.Logf("Client IP : %v\n", requestPacket.CIAddr().String()) test.Logf("Your IP : %v\n", requestPacket.YIAddr().String()) test.Logf("Server IP : %v\n", requestPacket.SIAddr().String()) test.Logf("Gateway IP: %v\n", requestPacket.GIAddr().String()) test.Logf("Client Mac: %v\n", requestPacket.CHAddr().String()) acknowledgement, err := client.GetAcknowledgement(&requestPacket) if err != nil { test.Error("Error Getting Acknowledgement:" + err.Error()) } test.Log("--Acknowledgement Packet--") test.Logf("Client IP : %v\n", acknowledgement.CIAddr().String()) test.Logf("Your IP : %v\n", acknowledgement.YIAddr().String()) test.Logf("Server IP : %v\n", acknowledgement.SIAddr().String()) test.Logf("Gateway IP: %v\n", acknowledgement.GIAddr().String()) test.Logf("Client Mac: %v\n", acknowledgement.CHAddr().String()) acknowledgementOptions := acknowledgement.ParseOptions() if len(acknowledgementOptions[dhcp4.OptionDHCPMessageType]) <= 0 || dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.NAK { test.Errorf("Didn't get NAK got DHCP4 Message Type:%v\n", dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0])) } test.Log("Shutting Down Server") myServer.Shutdown() wg.Wait() }
/* * 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 } }