1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
|
// This is lease support for nclient4
package nclient4
import (
"context"
"fmt"
"net"
"time"
"github.com/insomniacslk/dhcp/dhcpv4"
)
// Lease contains a DHCPv4 lease after DORA.
// note: Lease doesn't include binding interface name
type Lease struct {
Offer *dhcpv4.DHCPv4
ACK *dhcpv4.DHCPv4
CreationTime time.Time
}
// Release send DHCPv4 release messsage to server, based on specified lease.
// release is sent as unicast per RFC2131, section 4.4.4.
// Note: some DHCP server requries of using assigned IP address as source IP,
// use nclient4.WithUnicast to create client for such case.
func (c *Client) Release(lease *Lease, modifiers ...dhcpv4.Modifier) error {
if lease == nil {
return fmt.Errorf("lease is nil")
}
req, err := dhcpv4.NewReleaseFromACK(lease.ACK, modifiers...)
if err != nil {
return fmt.Errorf("fail to create release request,%w", err)
}
_, err = c.conn.WriteTo(req.ToBytes(), &net.UDPAddr{IP: lease.ACK.Options.Get(dhcpv4.OptionServerIdentifier), Port: ServerPort})
if err == nil {
c.logger.PrintMessage("sent message:", req)
}
return err
}
// Renew sends a DHCPv4 request to the server to renew the given lease. The renewal information is
// sourced from the initial offer in the lease, and the ACK of the lease is updated to the ACK of
// the latest renewal. This avoids issues with DHCP servers that omit information needed to build a
// completely new lease from their renewal ACK (such as the Windows DHCP Server).
func (c *Client) Renew(ctx context.Context, lease *Lease, modifiers ...dhcpv4.Modifier) (*Lease, error) {
if lease == nil {
return nil, fmt.Errorf("lease is nil")
}
request, err := dhcpv4.NewRenewFromAck(lease.ACK, dhcpv4.PrependModifiers(modifiers,
dhcpv4.WithOption(dhcpv4.OptMaxMessageSize(MaxMessageSize)))...)
if err != nil {
return nil, fmt.Errorf("unable to create a request: %w", err)
}
// Servers are supposed to only respond to Requests containing their server identifier,
// but sometimes non-compliant servers respond anyway.
// Clients are not required to validate this field, but servers are required to
// include the server identifier in their Offer per RFC 2131 Section 4.3.1 Table 3.
response, err := c.SendAndRead(ctx, c.serverAddr, request, IsAll(
IsCorrectServer(lease.Offer.ServerIdentifier()),
IsMessageType(dhcpv4.MessageTypeAck, dhcpv4.MessageTypeNak)))
if err != nil {
return nil, fmt.Errorf("got an error while processing the request: %w", err)
}
if response.MessageType() == dhcpv4.MessageTypeNak {
return nil, &ErrNak{
Offer: lease.Offer,
Nak: response,
}
}
// Return a new lease with the latest ACK and updated creation time
return &Lease{
Offer: lease.Offer,
ACK: response,
CreationTime: time.Now(),
}, nil
}
|