fix(dhcp): can not renew an ip address
The dhcp server is systemd-networkd, and the dhcp plugin can request an ip but can not renew it. The systemd-networkd just ignore the renew request. ``` 2024/09/14 21:46:00 no DHCP packet received within 10s 2024/09/14 21:46:00 retrying in 31.529038 seconds 2024/09/14 21:46:42 no DHCP packet received within 10s 2024/09/14 21:46:42 retrying in 63.150490 seconds 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: no more tries 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: renewal time expired, rebinding 2024/09/14 21:47:45 Link "eth1" down. Attempting to set up 2024/09/14 21:47:45 98184616c91f15419f5cacd012697f85afaa2daeb5d3233e28b0ec21589fb45a/iot/eth1: lease rebound, expiration is 2024-09-14 22:47:45.309270751 +0800 CST m=+11730.048516519 ``` Follow the https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.6, following options must not be sent in renew - Requested IP Address - Server Identifier Since the upstream code has been inactive for 6 years, we should switch to another dhcpv4 library. The new selected one is https://github.com/insomniacslk/dhcp. Signed-off-by: Songmin Li <lisongmin@protonmail.com>
This commit is contained in:

committed by
Casey Callendrello

parent
e4950728ce
commit
d61e7e5e1f
856
vendor/github.com/insomniacslk/dhcp/dhcpv4/dhcpv4.go
generated
vendored
Normal file
856
vendor/github.com/insomniacslk/dhcp/dhcpv4/dhcpv4.go
generated
vendored
Normal file
@ -0,0 +1,856 @@
|
||||
// Package dhcpv4 provides encoding and decoding of DHCPv4 packets and options.
|
||||
//
|
||||
// Example Usage:
|
||||
//
|
||||
// p, err := dhcpv4.New(
|
||||
// dhcpv4.WithClientIP(net.IP{192, 168, 0, 1}),
|
||||
// dhcpv4.WithMessageType(dhcpv4.MessageTypeInform),
|
||||
// )
|
||||
// p.UpdateOption(dhcpv4.OptServerIdentifier(net.IP{192, 110, 110, 110}))
|
||||
//
|
||||
// // Retrieve the DHCP Message Type option.
|
||||
// m := p.MessageType()
|
||||
//
|
||||
// bytesOnTheWire := p.ToBytes()
|
||||
// longSummary := p.Summary()
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
"github.com/u-root/uio/rand"
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
const (
|
||||
// minPacketLen is the minimum DHCP header length.
|
||||
minPacketLen = 236
|
||||
|
||||
// MaxHWAddrLen is the maximum hardware address length of the ClientHWAddr
|
||||
// (client hardware address) according to RFC 2131, Section 2. This is the
|
||||
// link-layer destination a server must send responses to.
|
||||
MaxHWAddrLen = 16
|
||||
|
||||
// MaxMessageSize is the maximum size in bytes that a DHCPv4 packet can hold.
|
||||
MaxMessageSize = 576
|
||||
|
||||
// Per RFC 951, the minimum length of a packet is 300 bytes.
|
||||
bootpMinLen = 300
|
||||
)
|
||||
|
||||
// RandomTimeout is the amount of time to wait until random number generation
|
||||
// is canceled.
|
||||
var RandomTimeout = 2 * time.Minute
|
||||
|
||||
// magicCookie is the magic 4-byte value at the beginning of the list of options
|
||||
// in a DHCPv4 packet.
|
||||
var magicCookie = [4]byte{99, 130, 83, 99}
|
||||
|
||||
// DHCPv4 represents a DHCPv4 packet header and options. See the New* functions
|
||||
// to build DHCPv4 packets.
|
||||
type DHCPv4 struct {
|
||||
OpCode OpcodeType
|
||||
HWType iana.HWType
|
||||
HopCount uint8
|
||||
TransactionID TransactionID
|
||||
NumSeconds uint16
|
||||
Flags uint16
|
||||
ClientIPAddr net.IP
|
||||
YourIPAddr net.IP
|
||||
ServerIPAddr net.IP
|
||||
GatewayIPAddr net.IP
|
||||
ClientHWAddr net.HardwareAddr
|
||||
ServerHostName string
|
||||
BootFileName string
|
||||
Options Options
|
||||
}
|
||||
|
||||
// Modifier defines the signature for functions that can modify DHCPv4
|
||||
// structures. This is used to simplify packet manipulation
|
||||
type Modifier func(d *DHCPv4)
|
||||
|
||||
// IPv4AddrsForInterface obtains the currently-configured, non-loopback IPv4
|
||||
// addresses for iface.
|
||||
func IPv4AddrsForInterface(iface *net.Interface) ([]net.IP, error) {
|
||||
if iface == nil {
|
||||
return nil, errors.New("IPv4AddrsForInterface: iface cannot be nil")
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetExternalIPv4Addrs(addrs)
|
||||
}
|
||||
|
||||
// GetExternalIPv4Addrs obtains the currently-configured, non-loopback IPv4
|
||||
// addresses from `addrs` coming from a particular interface (e.g.
|
||||
// net.Interface.Addrs).
|
||||
func GetExternalIPv4Addrs(addrs []net.Addr) ([]net.IP, error) {
|
||||
var v4addrs []net.IP
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
v4addrs = append(v4addrs, ip)
|
||||
}
|
||||
return v4addrs, nil
|
||||
}
|
||||
|
||||
// GenerateTransactionID generates a random 32-bits number suitable for use as
|
||||
// TransactionID
|
||||
func GenerateTransactionID() (TransactionID, error) {
|
||||
var xid TransactionID
|
||||
ctx, cancel := context.WithTimeout(context.Background(), RandomTimeout)
|
||||
defer cancel()
|
||||
n, err := rand.ReadContext(ctx, xid[:])
|
||||
if err != nil {
|
||||
return xid, fmt.Errorf("could not get random number: %v", err)
|
||||
}
|
||||
if n != 4 {
|
||||
return xid, errors.New("invalid random sequence for transaction ID: smaller than 32 bits")
|
||||
}
|
||||
return xid, err
|
||||
}
|
||||
|
||||
// New creates a new DHCPv4 structure and fill it up with default values. It
|
||||
// won't be a valid DHCPv4 message so you will need to adjust its fields.
|
||||
// See also NewDiscovery, NewRequest, NewAcknowledge, NewInform and NewRelease.
|
||||
func New(modifiers ...Modifier) (*DHCPv4, error) {
|
||||
xid, err := GenerateTransactionID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := DHCPv4{
|
||||
OpCode: OpcodeBootRequest,
|
||||
HWType: iana.HWTypeEthernet,
|
||||
ClientHWAddr: make(net.HardwareAddr, 6),
|
||||
HopCount: 0,
|
||||
TransactionID: xid,
|
||||
NumSeconds: 0,
|
||||
Flags: 0,
|
||||
ClientIPAddr: net.IPv4zero,
|
||||
YourIPAddr: net.IPv4zero,
|
||||
ServerIPAddr: net.IPv4zero,
|
||||
GatewayIPAddr: net.IPv4zero,
|
||||
Options: make(Options),
|
||||
}
|
||||
for _, mod := range modifiers {
|
||||
mod(&d)
|
||||
}
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// NewDiscoveryForInterface builds a new DHCPv4 Discovery message, with a default
|
||||
// Ethernet HW type and the hardware address obtained from the specified
|
||||
// interface.
|
||||
func NewDiscoveryForInterface(ifname string, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewDiscovery(iface.HardwareAddr, modifiers...)
|
||||
}
|
||||
|
||||
// NewDiscovery builds a new DHCPv4 Discovery message, with a default Ethernet
|
||||
// HW type and specified hardware address.
|
||||
func NewDiscovery(hwaddr net.HardwareAddr, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithHwAddr(hwaddr),
|
||||
WithRequestedOptions(
|
||||
OptionSubnetMask,
|
||||
OptionRouter,
|
||||
OptionDomainName,
|
||||
OptionDomainNameServer,
|
||||
),
|
||||
WithMessageType(MessageTypeDiscover),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewInformForInterface builds a new DHCPv4 Informational message with default
|
||||
// Ethernet HW type and the hardware address obtained from the specified
|
||||
// interface.
|
||||
func NewInformForInterface(ifname string, needsBroadcast bool) (*DHCPv4, error) {
|
||||
// get hw addr
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set Client IP as iface's currently-configured IP.
|
||||
localIPs, err := IPv4AddrsForInterface(iface)
|
||||
if err != nil || len(localIPs) == 0 {
|
||||
return nil, fmt.Errorf("could not get local IPs for iface %s", ifname)
|
||||
}
|
||||
pkt, err := NewInform(iface.HardwareAddr, localIPs[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if needsBroadcast {
|
||||
pkt.SetBroadcast()
|
||||
} else {
|
||||
pkt.SetUnicast()
|
||||
}
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
// PrependModifiers prepends other to m.
|
||||
func PrependModifiers(m []Modifier, other ...Modifier) []Modifier {
|
||||
return append(other, m...)
|
||||
}
|
||||
|
||||
// NewInform builds a new DHCPv4 Informational message with the specified
|
||||
// hardware address.
|
||||
func NewInform(hwaddr net.HardwareAddr, localIP net.IP, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithHwAddr(hwaddr),
|
||||
WithMessageType(MessageTypeInform),
|
||||
WithClientIP(localIP),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewRequestFromOffer builds a DHCPv4 request from an offer.
|
||||
// It assumes the SELECTING state by default, see Section 4.3.2 in RFC 2131 for more details.
|
||||
func NewRequestFromOffer(offer *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithReply(offer),
|
||||
WithMessageType(MessageTypeRequest),
|
||||
WithClientIP(offer.ClientIPAddr),
|
||||
WithOption(OptRequestedIPAddress(offer.YourIPAddr)),
|
||||
// This is usually the server IP.
|
||||
WithOptionCopied(offer, OptionServerIdentifier),
|
||||
WithRequestedOptions(
|
||||
OptionSubnetMask,
|
||||
OptionRouter,
|
||||
OptionDomainName,
|
||||
OptionDomainNameServer,
|
||||
),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewRenewFromAck builds a DHCPv4 RENEW-style request from the ACK of a lease. RENEW requests have
|
||||
// minor changes to their options compared to SELECT requests as specified by RFC 2131, section 4.3.2.
|
||||
func NewRenewFromAck(ack *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithReply(ack),
|
||||
WithMessageType(MessageTypeRequest),
|
||||
// The client IP must be filled in with the IP offered to the client
|
||||
WithClientIP(ack.YourIPAddr),
|
||||
// The renewal request must use unicast
|
||||
WithBroadcast(false),
|
||||
WithRequestedOptions(
|
||||
OptionSubnetMask,
|
||||
OptionRouter,
|
||||
OptionDomainName,
|
||||
OptionDomainNameServer,
|
||||
),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewReplyFromRequest builds a DHCPv4 reply from a request.
|
||||
func NewReplyFromRequest(request *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithReply(request),
|
||||
WithGatewayIP(request.GatewayIPAddr),
|
||||
WithOptionCopied(request, OptionRelayAgentInformation),
|
||||
|
||||
// RFC 6842 states the Client Identifier option must be copied
|
||||
// from the request if a client specified it.
|
||||
WithOptionCopied(request, OptionClientIdentifier),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NewReleaseFromACK creates a DHCPv4 Release message from ACK.
|
||||
// default Release message without any Modifer is created as following:
|
||||
// - option Message Type is Release
|
||||
// - ClientIP is set to ack.YourIPAddr
|
||||
// - ClientHWAddr is set to ack.ClientHWAddr
|
||||
// - Unicast
|
||||
// - option Server Identifier is set to ack's ServerIdentifier
|
||||
func NewReleaseFromACK(ack *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) {
|
||||
return New(PrependModifiers(modifiers,
|
||||
WithMessageType(MessageTypeRelease),
|
||||
WithClientIP(ack.YourIPAddr),
|
||||
WithHwAddr(ack.ClientHWAddr),
|
||||
WithBroadcast(false),
|
||||
WithOptionCopied(ack, OptionServerIdentifier),
|
||||
)...)
|
||||
}
|
||||
|
||||
// FromBytes decodes a DHCPv4 packet from a sequence of bytes, and returns an
|
||||
// error if the packet is not valid.
|
||||
func FromBytes(q []byte) (*DHCPv4, error) {
|
||||
var p DHCPv4
|
||||
buf := uio.NewBigEndianBuffer(q)
|
||||
|
||||
p.OpCode = OpcodeType(buf.Read8())
|
||||
p.HWType = iana.HWType(buf.Read8())
|
||||
|
||||
hwAddrLen := buf.Read8()
|
||||
|
||||
p.HopCount = buf.Read8()
|
||||
buf.ReadBytes(p.TransactionID[:])
|
||||
p.NumSeconds = buf.Read16()
|
||||
p.Flags = buf.Read16()
|
||||
|
||||
p.ClientIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
p.YourIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
p.ServerIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
p.GatewayIPAddr = net.IP(buf.CopyN(net.IPv4len))
|
||||
|
||||
if hwAddrLen > 16 {
|
||||
hwAddrLen = 16
|
||||
}
|
||||
// Always read 16 bytes, but only use hwaddrlen of them.
|
||||
p.ClientHWAddr = make(net.HardwareAddr, 16)
|
||||
buf.ReadBytes(p.ClientHWAddr)
|
||||
p.ClientHWAddr = p.ClientHWAddr[:hwAddrLen]
|
||||
|
||||
var sname [64]byte
|
||||
buf.ReadBytes(sname[:])
|
||||
length := strings.Index(string(sname[:]), "\x00")
|
||||
if length == -1 {
|
||||
length = 64
|
||||
}
|
||||
p.ServerHostName = string(sname[:length])
|
||||
|
||||
var file [128]byte
|
||||
buf.ReadBytes(file[:])
|
||||
length = strings.Index(string(file[:]), "\x00")
|
||||
if length == -1 {
|
||||
length = 128
|
||||
}
|
||||
p.BootFileName = string(file[:length])
|
||||
|
||||
var cookie [4]byte
|
||||
buf.ReadBytes(cookie[:])
|
||||
|
||||
if err := buf.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cookie != magicCookie {
|
||||
return nil, fmt.Errorf("malformed DHCP packet: got magic cookie %v, want %v", cookie[:], magicCookie[:])
|
||||
}
|
||||
|
||||
p.Options = make(Options)
|
||||
if err := p.Options.fromBytesCheckEnd(buf.Data(), true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// FlagsToString returns a human-readable representation of the flags field.
|
||||
func (d *DHCPv4) FlagsToString() string {
|
||||
flags := ""
|
||||
if d.IsBroadcast() {
|
||||
flags += "Broadcast"
|
||||
} else {
|
||||
flags += "Unicast"
|
||||
}
|
||||
if d.Flags&0xfe != 0 {
|
||||
flags += " (reserved bits not zeroed)"
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
// IsBroadcast indicates whether the packet is a broadcast packet.
|
||||
func (d *DHCPv4) IsBroadcast() bool {
|
||||
return d.Flags&0x8000 == 0x8000
|
||||
}
|
||||
|
||||
// SetBroadcast sets the packet to be a broadcast packet.
|
||||
func (d *DHCPv4) SetBroadcast() {
|
||||
d.Flags |= 0x8000
|
||||
}
|
||||
|
||||
// IsUnicast indicates whether the packet is a unicast packet.
|
||||
func (d *DHCPv4) IsUnicast() bool {
|
||||
return d.Flags&0x8000 == 0
|
||||
}
|
||||
|
||||
// SetUnicast sets the packet to be a unicast packet.
|
||||
func (d *DHCPv4) SetUnicast() {
|
||||
d.Flags &= ^uint16(0x8000)
|
||||
}
|
||||
|
||||
// GetOneOption returns the option that matches the given option code.
|
||||
//
|
||||
// According to RFC 3396, options that are specified more than once are
|
||||
// concatenated, and hence this should always just return one option.
|
||||
func (d *DHCPv4) GetOneOption(code OptionCode) []byte {
|
||||
return d.Options.Get(code)
|
||||
}
|
||||
|
||||
// DeleteOption deletes an existing option with the given option code.
|
||||
func (d *DHCPv4) DeleteOption(code OptionCode) {
|
||||
if d.Options != nil {
|
||||
d.Options.Del(code)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateOption replaces an existing option with the same option code with the
|
||||
// given one, adding it if not already present.
|
||||
func (d *DHCPv4) UpdateOption(opt Option) {
|
||||
if d.Options == nil {
|
||||
d.Options = make(Options)
|
||||
}
|
||||
d.Options.Update(opt)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (d *DHCPv4) String() string {
|
||||
return fmt.Sprintf("DHCPv4(xid=%s hwaddr=%s msg_type=%s, your_ip=%s, server_ip=%s)",
|
||||
d.TransactionID, d.ClientHWAddr, d.MessageType(), d.YourIPAddr, d.ServerIPAddr)
|
||||
}
|
||||
|
||||
// SummaryWithVendor prints a summary of the packet, interpreting the
|
||||
// vendor-specific info option using the given parser (can be nil).
|
||||
func (d *DHCPv4) SummaryWithVendor(vendorDecoder OptionDecoder) string {
|
||||
ret := fmt.Sprintf(
|
||||
"DHCPv4 Message\n"+
|
||||
" opcode: %s\n"+
|
||||
" hwtype: %s\n"+
|
||||
" hopcount: %v\n"+
|
||||
" transaction ID: %s\n"+
|
||||
" num seconds: %v\n"+
|
||||
" flags: %v (0x%02x)\n"+
|
||||
" client IP: %s\n"+
|
||||
" your IP: %s\n"+
|
||||
" server IP: %s\n"+
|
||||
" gateway IP: %s\n"+
|
||||
" client MAC: %s\n"+
|
||||
" server hostname: %s\n"+
|
||||
" bootfile name: %s\n",
|
||||
d.OpCode,
|
||||
d.HWType,
|
||||
d.HopCount,
|
||||
d.TransactionID,
|
||||
d.NumSeconds,
|
||||
d.FlagsToString(),
|
||||
d.Flags,
|
||||
d.ClientIPAddr,
|
||||
d.YourIPAddr,
|
||||
d.ServerIPAddr,
|
||||
d.GatewayIPAddr,
|
||||
d.ClientHWAddr,
|
||||
d.ServerHostName,
|
||||
d.BootFileName,
|
||||
)
|
||||
ret += " options:\n"
|
||||
ret += d.Options.Summary(vendorDecoder)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Summary prints detailed information about the packet.
|
||||
func (d *DHCPv4) Summary() string {
|
||||
return d.SummaryWithVendor(nil)
|
||||
}
|
||||
|
||||
// IsOptionRequested returns true if that option is within the requested
|
||||
// options of the DHCPv4 message.
|
||||
func (d *DHCPv4) IsOptionRequested(requested OptionCode) bool {
|
||||
rq := d.ParameterRequestList()
|
||||
if rq == nil {
|
||||
// RFC2131§3.5
|
||||
// Not all clients require initialization of all parameters [...]
|
||||
// Two techniques are used to reduce the number of parameters transmitted from
|
||||
// the server to the client. [...] Second, in its initial DHCPDISCOVER or
|
||||
// DHCPREQUEST message, a client may provide the server with a list of specific
|
||||
// parameters the client is interested in.
|
||||
// We interpret this to say that all available parameters should be sent if
|
||||
// the parameter request list is not sent at all.
|
||||
return true
|
||||
}
|
||||
|
||||
for _, o := range rq {
|
||||
if o.Code() == requested.Code() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// In case somebody forgets to set an IP, just write 0s as default values.
|
||||
func writeIP(b *uio.Lexer, ip net.IP) {
|
||||
var zeros [net.IPv4len]byte
|
||||
if ip == nil {
|
||||
b.WriteBytes(zeros[:])
|
||||
} else {
|
||||
// Converting IP to 4 byte format
|
||||
ip = ip.To4()
|
||||
b.WriteBytes(ip[:net.IPv4len])
|
||||
}
|
||||
}
|
||||
|
||||
// ToBytes writes the packet to binary.
|
||||
func (d *DHCPv4) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(make([]byte, 0, minPacketLen))
|
||||
buf.Write8(uint8(d.OpCode))
|
||||
buf.Write8(uint8(d.HWType))
|
||||
|
||||
// HwAddrLen
|
||||
hlen := uint8(len(d.ClientHWAddr))
|
||||
buf.Write8(hlen)
|
||||
buf.Write8(d.HopCount)
|
||||
buf.WriteBytes(d.TransactionID[:])
|
||||
buf.Write16(d.NumSeconds)
|
||||
buf.Write16(d.Flags)
|
||||
|
||||
writeIP(buf, d.ClientIPAddr)
|
||||
writeIP(buf, d.YourIPAddr)
|
||||
writeIP(buf, d.ServerIPAddr)
|
||||
writeIP(buf, d.GatewayIPAddr)
|
||||
copy(buf.WriteN(16), d.ClientHWAddr)
|
||||
|
||||
var sname [64]byte
|
||||
copy(sname[:63], []byte(d.ServerHostName))
|
||||
buf.WriteBytes(sname[:])
|
||||
|
||||
var file [128]byte
|
||||
copy(file[:127], []byte(d.BootFileName))
|
||||
buf.WriteBytes(file[:])
|
||||
|
||||
// The magic cookie.
|
||||
buf.WriteBytes(magicCookie[:])
|
||||
|
||||
// Write all options.
|
||||
d.Options.Marshal(buf)
|
||||
|
||||
// Finish the options.
|
||||
buf.Write8(OptionEnd.Code())
|
||||
|
||||
// DHCP is based on BOOTP, and BOOTP messages have a minimum length of
|
||||
// 300 bytes per RFC 951. This not stated explicitly, but if you sum up
|
||||
// all the bytes in the message layout, you'll get 300 bytes.
|
||||
//
|
||||
// Some DHCP servers and relay agents care about this BOOTP legacy B.S.
|
||||
// and "conveniently" drop messages that are less than 300 bytes long.
|
||||
if buf.Len() < bootpMinLen {
|
||||
buf.WriteBytes(bytes.Repeat([]byte{OptionPad.Code()}, bootpMinLen-buf.Len()))
|
||||
}
|
||||
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// GetBroadcastAddress returns the DHCPv4 Broadcast Address value in d.
|
||||
//
|
||||
// The broadcast address option is described in RFC 2132, Section 5.3.
|
||||
func (d *DHCPv4) BroadcastAddress() net.IP {
|
||||
return GetIP(OptionBroadcastAddress, d.Options)
|
||||
}
|
||||
|
||||
// RequestedIPAddress returns the DHCPv4 Requested IP Address value in d.
|
||||
//
|
||||
// The requested IP address option is described by RFC 2132, Section 9.1.
|
||||
func (d *DHCPv4) RequestedIPAddress() net.IP {
|
||||
return GetIP(OptionRequestedIPAddress, d.Options)
|
||||
}
|
||||
|
||||
// ServerIdentifier returns the DHCPv4 Server Identifier value in d.
|
||||
//
|
||||
// The server identifier option is described by RFC 2132, Section 9.7.
|
||||
func (d *DHCPv4) ServerIdentifier() net.IP {
|
||||
return GetIP(OptionServerIdentifier, d.Options)
|
||||
}
|
||||
|
||||
// Router parses the DHCPv4 Router option if present.
|
||||
//
|
||||
// The Router option is described by RFC 2132, Section 3.5.
|
||||
func (d *DHCPv4) Router() []net.IP {
|
||||
return GetIPs(OptionRouter, d.Options)
|
||||
}
|
||||
|
||||
// ClasslessStaticRoute parses the DHCPv4 Classless Static Route option if present.
|
||||
//
|
||||
// The Classless Static Route option is described by RFC 3442.
|
||||
func (d *DHCPv4) ClasslessStaticRoute() []*Route {
|
||||
v := d.Options.Get(OptionClasslessStaticRoute)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var routes Routes
|
||||
if err := routes.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// NTPServers parses the DHCPv4 NTP Servers option if present.
|
||||
//
|
||||
// The NTP servers option is described by RFC 2132, Section 8.3.
|
||||
func (d *DHCPv4) NTPServers() []net.IP {
|
||||
return GetIPs(OptionNTPServers, d.Options)
|
||||
}
|
||||
|
||||
// DNS parses the DHCPv4 Domain Name Server option if present.
|
||||
//
|
||||
// The DNS server option is described by RFC 2132, Section 3.8.
|
||||
func (d *DHCPv4) DNS() []net.IP {
|
||||
return GetIPs(OptionDomainNameServer, d.Options)
|
||||
}
|
||||
|
||||
// DomainName parses the DHCPv4 Domain Name option if present.
|
||||
//
|
||||
// The Domain Name option is described by RFC 2132, Section 3.17.
|
||||
func (d *DHCPv4) DomainName() string {
|
||||
return GetString(OptionDomainName, d.Options)
|
||||
}
|
||||
|
||||
// HostName parses the DHCPv4 Host Name option if present.
|
||||
//
|
||||
// The Host Name option is described by RFC 2132, Section 3.14.
|
||||
func (d *DHCPv4) HostName() string {
|
||||
name := GetString(OptionHostName, d.Options)
|
||||
return strings.TrimRight(name, "\x00")
|
||||
}
|
||||
|
||||
// RootPath parses the DHCPv4 Root Path option if present.
|
||||
//
|
||||
// The Root Path option is described by RFC 2132, Section 3.19.
|
||||
func (d *DHCPv4) RootPath() string {
|
||||
return GetString(OptionRootPath, d.Options)
|
||||
}
|
||||
|
||||
// BootFileNameOption parses the DHCPv4 Bootfile Name option if present.
|
||||
//
|
||||
// The Bootfile Name option is described by RFC 2132, Section 9.5.
|
||||
func (d *DHCPv4) BootFileNameOption() string {
|
||||
name := GetString(OptionBootfileName, d.Options)
|
||||
return strings.TrimRight(name, "\x00")
|
||||
}
|
||||
|
||||
// TFTPServerName parses the DHCPv4 TFTP Server Name option if present.
|
||||
//
|
||||
// The TFTP Server Name option is described by RFC 2132, Section 9.4.
|
||||
func (d *DHCPv4) TFTPServerName() string {
|
||||
name := GetString(OptionTFTPServerName, d.Options)
|
||||
return strings.TrimRight(name, "\x00")
|
||||
}
|
||||
|
||||
// ClassIdentifier parses the DHCPv4 Class Identifier option if present.
|
||||
//
|
||||
// The Vendor Class Identifier option is described by RFC 2132, Section 9.13.
|
||||
func (d *DHCPv4) ClassIdentifier() string {
|
||||
return GetString(OptionClassIdentifier, d.Options)
|
||||
}
|
||||
|
||||
// ClientArch returns the Client System Architecture Type option.
|
||||
func (d *DHCPv4) ClientArch() []iana.Arch {
|
||||
v := d.Options.Get(OptionClientSystemArchitectureType)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var archs iana.Archs
|
||||
if err := archs.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return archs
|
||||
}
|
||||
|
||||
// DomainSearch returns the domain search list if present.
|
||||
//
|
||||
// The domain search option is described by RFC 3397, Section 2.
|
||||
func (d *DHCPv4) DomainSearch() *rfc1035label.Labels {
|
||||
v := d.Options.Get(OptionDNSDomainSearchList)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
labels, err := rfc1035label.FromBytes(v)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
// IPAddressLeaseTime returns the IP address lease time or the given
|
||||
// default duration if not present.
|
||||
//
|
||||
// The IP address lease time option is described by RFC 2132, Section 9.2.
|
||||
func (d *DHCPv4) IPAddressLeaseTime(def time.Duration) time.Duration {
|
||||
v := d.Options.Get(OptionIPAddressLeaseTime)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return def
|
||||
}
|
||||
return time.Duration(dur)
|
||||
}
|
||||
|
||||
// IPAddressRenewalTime returns the IP address renewal time or the given
|
||||
// default duration if not present.
|
||||
//
|
||||
// The IP address renewal time option is described by RFC 2132, Section 9.11.
|
||||
func (d *DHCPv4) IPAddressRenewalTime(def time.Duration) time.Duration {
|
||||
v := d.Options.Get(OptionRenewTimeValue)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return def
|
||||
}
|
||||
return time.Duration(dur)
|
||||
}
|
||||
|
||||
// IPAddressRebindingTime returns the IP address rebinding time or the given
|
||||
// default duration if not present.
|
||||
//
|
||||
// The IP address rebinding time option is described by RFC 2132, Section 9.12.
|
||||
func (d *DHCPv4) IPAddressRebindingTime(def time.Duration) time.Duration {
|
||||
v := d.Options.Get(OptionRebindingTimeValue)
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return def
|
||||
}
|
||||
return time.Duration(dur)
|
||||
}
|
||||
|
||||
// IPv6OnlyPreferred returns the V6ONLY_WAIT duration, and a boolean
|
||||
// indicating whether this option was present.
|
||||
//
|
||||
// The IPv6-Only Preferred option is described by RFC 8925, Section 3.1.
|
||||
func (d *DHCPv4) IPv6OnlyPreferred() (time.Duration, bool) {
|
||||
v := d.Options.Get(OptionIPv6OnlyPreferred)
|
||||
if v == nil {
|
||||
return 0, false
|
||||
}
|
||||
var dur Duration
|
||||
if err := dur.FromBytes(v); err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return time.Duration(dur), true
|
||||
}
|
||||
|
||||
// MaxMessageSize returns the DHCP Maximum Message Size if present.
|
||||
//
|
||||
// The Maximum DHCP Message Size option is described by RFC 2132, Section 9.10.
|
||||
func (d *DHCPv4) MaxMessageSize() (uint16, error) {
|
||||
return GetUint16(OptionMaximumDHCPMessageSize, d.Options)
|
||||
}
|
||||
|
||||
// AutoConfigure returns the value of the AutoConfigure option, and a
|
||||
// boolean indicating if it was present.
|
||||
//
|
||||
// The AutoConfigure option is described by RFC 2563, Section 2.
|
||||
func (d *DHCPv4) AutoConfigure() (AutoConfiguration, bool) {
|
||||
v, err := GetByte(OptionAutoConfigure, d.Options)
|
||||
return AutoConfiguration(v), err == nil
|
||||
}
|
||||
|
||||
// MessageType returns the DHCPv4 Message Type option.
|
||||
func (d *DHCPv4) MessageType() MessageType {
|
||||
v := d.Options.Get(OptionDHCPMessageType)
|
||||
if v == nil {
|
||||
return MessageTypeNone
|
||||
}
|
||||
var m MessageType
|
||||
if err := m.FromBytes(v); err != nil {
|
||||
return MessageTypeNone
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Message returns the DHCPv4 (Error) Message option.
|
||||
//
|
||||
// The message options is described in RFC 2132, Section 9.9.
|
||||
func (d *DHCPv4) Message() string {
|
||||
return GetString(OptionMessage, d.Options)
|
||||
}
|
||||
|
||||
// ParameterRequestList returns the DHCPv4 Parameter Request List.
|
||||
//
|
||||
// The parameter request list option is described by RFC 2132, Section 9.8.
|
||||
func (d *DHCPv4) ParameterRequestList() OptionCodeList {
|
||||
v := d.Options.Get(OptionParameterRequestList)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var codes OptionCodeList
|
||||
if err := codes.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return codes
|
||||
}
|
||||
|
||||
// RelayAgentInfo returns options embedded by the relay agent.
|
||||
//
|
||||
// The relay agent info option is described by RFC 3046.
|
||||
func (d *DHCPv4) RelayAgentInfo() *RelayOptions {
|
||||
v := d.Options.Get(OptionRelayAgentInformation)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var relayOptions RelayOptions
|
||||
if err := relayOptions.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &relayOptions
|
||||
}
|
||||
|
||||
// SubnetMask returns a subnet mask option contained if present.
|
||||
//
|
||||
// The subnet mask option is described by RFC 2132, Section 3.3.
|
||||
func (d *DHCPv4) SubnetMask() net.IPMask {
|
||||
v := d.Options.Get(OptionSubnetMask)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var im IPMask
|
||||
if err := im.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return net.IPMask(im)
|
||||
}
|
||||
|
||||
// UserClass returns the user class if present.
|
||||
//
|
||||
// The user class information option is defined by RFC 3004.
|
||||
func (d *DHCPv4) UserClass() []string {
|
||||
v := d.Options.Get(OptionUserClassInformation)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var uc Strings
|
||||
if err := uc.FromBytes(v); err != nil {
|
||||
return []string{GetString(OptionUserClassInformation, d.Options)}
|
||||
}
|
||||
return uc
|
||||
}
|
||||
|
||||
// VIVC returns the vendor-identifying vendor class option if present.
|
||||
func (d *DHCPv4) VIVC() VIVCIdentifiers {
|
||||
v := d.Options.Get(OptionVendorIdentifyingVendorClass)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var ids VIVCIdentifiers
|
||||
if err := ids.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ids
|
||||
}
|
Reference in New Issue
Block a user