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
10
vendor/github.com/insomniacslk/dhcp/CONTRIBUTORS.md
generated
vendored
Normal file
10
vendor/github.com/insomniacslk/dhcp/CONTRIBUTORS.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
## Contributors
|
||||
|
||||
* Andrea Barberio (main author)
|
||||
* Pablo Mazzini (tons of fixes and new options)
|
||||
* Sean Karlage (BSDP package, and of tons of improvements to the DHCPv4 package)
|
||||
* Owen Mooney (several option fixes and modifiers)
|
||||
* Mikolaj Walczak (asynchronous DHCPv6 client)
|
||||
* Chris Koch (tons of improvements in DHCPv4 and DHCPv6 internals and interface)
|
||||
* Akshay Navale, Brandon Bennett and Chris Gorham (ZTPv6 and ZTPv4 packages)
|
||||
* Anatole Denis (tons of fixes and new options)
|
29
vendor/github.com/insomniacslk/dhcp/LICENSE
generated
vendored
Normal file
29
vendor/github.com/insomniacslk/dhcp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, Andrea Barberio
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
10
vendor/github.com/insomniacslk/dhcp/dhcpv4/bindtointerface.go
generated
vendored
Normal file
10
vendor/github.com/insomniacslk/dhcp/dhcpv4/bindtointerface.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"github.com/insomniacslk/dhcp/interfaces"
|
||||
)
|
||||
|
||||
// BindToInterface (deprecated) redirects to interfaces.BindToInterface
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
return interfaces.BindToInterface(fd, ifname)
|
||||
}
|
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/defaults.go
generated
vendored
Normal file
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/defaults.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
package dhcpv4
|
||||
|
||||
const (
|
||||
ServerPort = 67
|
||||
ClientPort = 68
|
||||
)
|
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
|
||||
}
|
176
vendor/github.com/insomniacslk/dhcp/dhcpv4/modifiers.go
generated
vendored
Normal file
176
vendor/github.com/insomniacslk/dhcp/dhcpv4/modifiers.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
)
|
||||
|
||||
// WithTransactionID sets the Transaction ID for the DHCPv4 packet
|
||||
func WithTransactionID(xid TransactionID) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.TransactionID = xid
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientIP sets the Client IP for a DHCPv4 packet.
|
||||
func WithClientIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.ClientIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithYourIP sets the Your IP for a DHCPv4 packet.
|
||||
func WithYourIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.YourIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithServerIP sets the Server IP for a DHCPv4 packet.
|
||||
func WithServerIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.ServerIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithGatewayIP sets the Gateway IP for the DHCPv4 packet.
|
||||
func WithGatewayIP(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.GatewayIPAddr = ip
|
||||
}
|
||||
}
|
||||
|
||||
// WithOptionCopied copies the value of option opt from request.
|
||||
func WithOptionCopied(request *DHCPv4, opt OptionCode) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
if val := request.Options.Get(opt); val != nil {
|
||||
d.UpdateOption(OptGeneric(opt, val))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithReply fills in opcode, hwtype, xid, clienthwaddr, and flags from the given packet.
|
||||
func WithReply(request *DHCPv4) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
if request.OpCode == OpcodeBootRequest {
|
||||
d.OpCode = OpcodeBootReply
|
||||
} else {
|
||||
d.OpCode = OpcodeBootRequest
|
||||
}
|
||||
d.HWType = request.HWType
|
||||
d.TransactionID = request.TransactionID
|
||||
d.ClientHWAddr = request.ClientHWAddr
|
||||
d.Flags = request.Flags
|
||||
}
|
||||
}
|
||||
|
||||
// WithHWType sets the Hardware Type for a DHCPv4 packet.
|
||||
func WithHWType(hwt iana.HWType) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.HWType = hwt
|
||||
}
|
||||
}
|
||||
|
||||
// WithBroadcast sets the packet to be broadcast or unicast
|
||||
func WithBroadcast(broadcast bool) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
if broadcast {
|
||||
d.SetBroadcast()
|
||||
} else {
|
||||
d.SetUnicast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHwAddr sets the hardware address for a packet
|
||||
func WithHwAddr(hwaddr net.HardwareAddr) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.ClientHWAddr = hwaddr
|
||||
}
|
||||
}
|
||||
|
||||
// WithOption appends a DHCPv4 option provided by the user
|
||||
func WithOption(opt Option) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.UpdateOption(opt)
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutOption removes the DHCPv4 option with the given code
|
||||
func WithoutOption(code OptionCode) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.DeleteOption(code)
|
||||
}
|
||||
}
|
||||
|
||||
// WithUserClass adds a user class option to the packet.
|
||||
// The rfc parameter allows you to specify if the userclass should be
|
||||
// rfc compliant or not. More details in issue #113
|
||||
func WithUserClass(uc string, rfc bool) Modifier {
|
||||
// TODO let the user specify multiple user classes
|
||||
return func(d *DHCPv4) {
|
||||
if rfc {
|
||||
d.UpdateOption(OptRFC3004UserClass([]string{uc}))
|
||||
} else {
|
||||
d.UpdateOption(OptUserClass(uc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithNetboot adds bootfile URL and bootfile param options to a DHCPv4 packet.
|
||||
func WithNetboot(d *DHCPv4) {
|
||||
WithRequestedOptions(OptionTFTPServerName, OptionBootfileName)(d)
|
||||
}
|
||||
|
||||
// WithMessageType adds the DHCPv4 message type m to a packet.
|
||||
func WithMessageType(m MessageType) Modifier {
|
||||
return WithOption(OptMessageType(m))
|
||||
}
|
||||
|
||||
// WithRequestedOptions adds requested options to the packet.
|
||||
func WithRequestedOptions(optionCodes ...OptionCode) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
cl := d.ParameterRequestList()
|
||||
cl.Add(optionCodes...)
|
||||
d.UpdateOption(OptParameterRequestList(cl...))
|
||||
}
|
||||
}
|
||||
|
||||
// WithRelay adds parameters required for DHCPv4 to be relayed by the relay
|
||||
// server with given ip
|
||||
func WithRelay(ip net.IP) Modifier {
|
||||
return func(d *DHCPv4) {
|
||||
d.SetUnicast()
|
||||
d.GatewayIPAddr = ip
|
||||
d.HopCount++
|
||||
}
|
||||
}
|
||||
|
||||
// WithNetmask adds or updates an OptSubnetMask
|
||||
func WithNetmask(mask net.IPMask) Modifier {
|
||||
return WithOption(OptSubnetMask(mask))
|
||||
}
|
||||
|
||||
// WithLeaseTime adds or updates an OptIPAddressLeaseTime
|
||||
func WithLeaseTime(leaseTime uint32) Modifier {
|
||||
return WithOption(OptIPAddressLeaseTime(time.Duration(leaseTime) * time.Second))
|
||||
}
|
||||
|
||||
// WithIPv6OnlyPreferred adds or updates an OptIPv6OnlyPreferred
|
||||
func WithIPv6OnlyPreferred(v6OnlyWait uint32) Modifier {
|
||||
return WithOption(OptIPv6OnlyPreferred(time.Duration(v6OnlyWait) * time.Second))
|
||||
}
|
||||
|
||||
// WithDomainSearchList adds or updates an OptionDomainSearch
|
||||
func WithDomainSearchList(searchList ...string) Modifier {
|
||||
return WithOption(OptDomainSearch(&rfc1035label.Labels{
|
||||
Labels: searchList,
|
||||
}))
|
||||
}
|
||||
|
||||
func WithGeneric(code OptionCode, value []byte) Modifier {
|
||||
return WithOption(OptGeneric(code, value))
|
||||
}
|
669
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/client.go
generated
vendored
Normal file
669
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/client.go
generated
vendored
Normal file
@ -0,0 +1,669 @@
|
||||
// Copyright 2018 the u-root Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.12
|
||||
|
||||
// Package nclient4 is a small, minimum-functionality client for DHCPv4.
|
||||
//
|
||||
// It only supports the 4-way DHCPv4 Discover-Offer-Request-Ack handshake as
|
||||
// well as the Request-Ack renewal process.
|
||||
package nclient4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBufferCap = 5
|
||||
|
||||
// DefaultTimeout is the default value for read-timeout if option WithTimeout is not set
|
||||
DefaultTimeout = 5 * time.Second
|
||||
|
||||
// DefaultRetries is amount of retries will be done if no answer was received within read-timeout amount of time
|
||||
DefaultRetries = 3
|
||||
|
||||
// MaxMessageSize is the value to be used for DHCP option "MaxMessageSize".
|
||||
MaxMessageSize = 1500
|
||||
|
||||
// ClientPort is the port that DHCP clients listen on.
|
||||
ClientPort = 68
|
||||
|
||||
// ServerPort is the port that DHCP servers and relay agents listen on.
|
||||
ServerPort = 67
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultServers is the address of all link-local DHCP servers and
|
||||
// relay agents.
|
||||
DefaultServers = &net.UDPAddr{
|
||||
IP: net.IPv4bcast,
|
||||
Port: ServerPort,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoResponse is returned when no response packet is received.
|
||||
ErrNoResponse = errors.New("no matching response packet received")
|
||||
|
||||
// ErrNoConn is returned when NewWithConn is called with nil-value as conn.
|
||||
ErrNoConn = errors.New("conn is nil")
|
||||
|
||||
// ErrNoIfaceHWAddr is returned when NewWithConn is called with nil-value as ifaceHWAddr
|
||||
ErrNoIfaceHWAddr = errors.New("ifaceHWAddr is nil")
|
||||
)
|
||||
|
||||
// pendingCh is a channel associated with a pending TransactionID.
|
||||
type pendingCh struct {
|
||||
// SendAndRead closes done to indicate that it wishes for no more
|
||||
// messages for this particular XID.
|
||||
done <-chan struct{}
|
||||
|
||||
// ch is used by the receive loop to distribute DHCP messages.
|
||||
ch chan<- *dhcpv4.DHCPv4
|
||||
}
|
||||
|
||||
// Logger is a handler which will be used to output logging messages
|
||||
type Logger interface {
|
||||
// PrintMessage print _all_ DHCP messages
|
||||
PrintMessage(prefix string, message *dhcpv4.DHCPv4)
|
||||
|
||||
// Printf is use to print the rest debugging information
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// EmptyLogger prints nothing
|
||||
type EmptyLogger struct{}
|
||||
|
||||
// Printf is just a dummy function that does nothing
|
||||
func (e EmptyLogger) Printf(format string, v ...interface{}) {}
|
||||
|
||||
// PrintMessage is just a dummy function that does nothing
|
||||
func (e EmptyLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {}
|
||||
|
||||
// Printfer is used for actual output of the logger. For example *log.Logger is a Printfer.
|
||||
type Printfer interface {
|
||||
// Printf is the function for logging output. Arguments are handled in the manner of fmt.Printf.
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// ShortSummaryLogger is a wrapper for Printfer to implement interface Logger.
|
||||
// DHCP messages are printed in the short format.
|
||||
type ShortSummaryLogger struct {
|
||||
// Printfer is used for actual output of the logger
|
||||
Printfer
|
||||
}
|
||||
|
||||
// Printf prints a log message as-is via predefined Printfer
|
||||
func (s ShortSummaryLogger) Printf(format string, v ...interface{}) {
|
||||
s.Printfer.Printf(format, v...)
|
||||
}
|
||||
|
||||
// PrintMessage prints a DHCP message in the short format via predefined Printfer
|
||||
func (s ShortSummaryLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {
|
||||
s.Printf("%s: %s", prefix, message)
|
||||
}
|
||||
|
||||
// DebugLogger is a wrapper for Printfer to implement interface Logger.
|
||||
// DHCP messages are printed in the long format.
|
||||
type DebugLogger struct {
|
||||
// Printfer is used for actual output of the logger
|
||||
Printfer
|
||||
}
|
||||
|
||||
// Printf prints a log message as-is via predefined Printfer
|
||||
func (d DebugLogger) Printf(format string, v ...interface{}) {
|
||||
d.Printfer.Printf(format, v...)
|
||||
}
|
||||
|
||||
// PrintMessage prints a DHCP message in the long format via predefined Printfer
|
||||
func (d DebugLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {
|
||||
d.Printf("%s: %s", prefix, message.Summary())
|
||||
}
|
||||
|
||||
// Client is an IPv4 DHCP client.
|
||||
type Client struct {
|
||||
ifaceHWAddr net.HardwareAddr
|
||||
conn net.PacketConn
|
||||
timeout time.Duration
|
||||
retry int
|
||||
logger Logger
|
||||
|
||||
// bufferCap is the channel capacity for each TransactionID.
|
||||
bufferCap int
|
||||
|
||||
// serverAddr is the UDP address to send all packets to.
|
||||
//
|
||||
// This may be an actual broadcast address, or a unicast address.
|
||||
serverAddr *net.UDPAddr
|
||||
|
||||
// closed is an atomic bool set to 1 when done is closed.
|
||||
closed uint32
|
||||
|
||||
// done is closed to unblock the receive loop.
|
||||
done chan struct{}
|
||||
|
||||
// wg protects any spawned goroutines, namely the receiveLoop.
|
||||
wg sync.WaitGroup
|
||||
|
||||
pendingMu sync.Mutex
|
||||
// pending stores the distribution channels for each pending
|
||||
// TransactionID. receiveLoop uses this map to determine which channel
|
||||
// to send a new DHCP message to.
|
||||
pending map[dhcpv4.TransactionID]*pendingCh
|
||||
}
|
||||
|
||||
// New returns a client usable with an unconfigured interface.
|
||||
func New(iface string, opts ...ClientOpt) (*Client, error) {
|
||||
return new(iface, nil, nil, opts...)
|
||||
}
|
||||
|
||||
// NewWithConn creates a new DHCP client that sends and receives packets on the
|
||||
// given interface.
|
||||
func NewWithConn(conn net.PacketConn, ifaceHWAddr net.HardwareAddr, opts ...ClientOpt) (*Client, error) {
|
||||
return new(``, conn, ifaceHWAddr, opts...)
|
||||
}
|
||||
|
||||
func new(iface string, conn net.PacketConn, ifaceHWAddr net.HardwareAddr, opts ...ClientOpt) (*Client, error) {
|
||||
c := &Client{
|
||||
ifaceHWAddr: ifaceHWAddr,
|
||||
timeout: DefaultTimeout,
|
||||
retry: DefaultRetries,
|
||||
serverAddr: DefaultServers,
|
||||
bufferCap: defaultBufferCap,
|
||||
conn: conn,
|
||||
logger: EmptyLogger{},
|
||||
|
||||
done: make(chan struct{}),
|
||||
pending: make(map[dhcpv4.TransactionID]*pendingCh),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to apply option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.ifaceHWAddr == nil {
|
||||
if iface == `` {
|
||||
return nil, ErrNoIfaceHWAddr
|
||||
}
|
||||
|
||||
i, err := net.InterfaceByName(iface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get interface information: %w", err)
|
||||
}
|
||||
|
||||
c.ifaceHWAddr = i.HardwareAddr
|
||||
}
|
||||
|
||||
if c.conn == nil {
|
||||
var err error
|
||||
if iface == `` {
|
||||
return nil, ErrNoConn
|
||||
}
|
||||
c.conn, err = NewRawUDPConn(iface, ClientPort) // broadcast
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open a broadcasting socket: %w", err)
|
||||
}
|
||||
}
|
||||
c.wg.Add(1)
|
||||
go c.receiveLoop()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Close closes the underlying connection.
|
||||
func (c *Client) Close() error {
|
||||
// Make sure not to close done twice.
|
||||
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := c.conn.Close()
|
||||
|
||||
// Closing c.done sets off a chain reaction:
|
||||
//
|
||||
// Any SendAndRead unblocks trying to receive more messages, which
|
||||
// means rem() gets called.
|
||||
//
|
||||
// rem() should be unblocking receiveLoop if it is blocked.
|
||||
//
|
||||
// receiveLoop should then exit gracefully.
|
||||
close(c.done)
|
||||
|
||||
// Wait for receiveLoop to stop.
|
||||
c.wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) isClosed() bool {
|
||||
return atomic.LoadUint32(&c.closed) != 0
|
||||
}
|
||||
|
||||
func (c *Client) receiveLoop() {
|
||||
defer c.wg.Done()
|
||||
for {
|
||||
// TODO: Clients can send a "max packet size" option in their
|
||||
// packets, IIRC. Choose a reasonable size and set it.
|
||||
b := make([]byte, MaxMessageSize)
|
||||
n, _, err := c.conn.ReadFrom(b)
|
||||
if err != nil {
|
||||
if !c.isClosed() {
|
||||
c.logger.Printf("error reading from UDP connection: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := dhcpv4.FromBytes(b[:n])
|
||||
if err != nil {
|
||||
// Not a valid DHCP packet; keep listening.
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.OpCode != dhcpv4.OpcodeBootReply {
|
||||
// Not a response message.
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a somewhat non-standard check, by the looks
|
||||
// of RFC 2131. It should work as long as the DHCP
|
||||
// server is spec-compliant for the HWAddr field.
|
||||
if c.ifaceHWAddr != nil && !bytes.Equal(c.ifaceHWAddr, msg.ClientHWAddr) {
|
||||
// Not for us.
|
||||
continue
|
||||
}
|
||||
|
||||
c.pendingMu.Lock()
|
||||
p, ok := c.pending[msg.TransactionID]
|
||||
if ok {
|
||||
select {
|
||||
case <-p.done:
|
||||
close(p.ch)
|
||||
delete(c.pending, msg.TransactionID)
|
||||
|
||||
// This send may block.
|
||||
case p.ch <- msg:
|
||||
}
|
||||
}
|
||||
c.pendingMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOpt is a function that configures the Client.
|
||||
type ClientOpt func(c *Client) error
|
||||
|
||||
// WithTimeout configures the retransmission timeout.
|
||||
//
|
||||
// Default is 5 seconds.
|
||||
func WithTimeout(d time.Duration) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.timeout = d
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithSummaryLogger logs one-line DHCPv4 message summaries when sent & received.
|
||||
func WithSummaryLogger() ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.logger = ShortSummaryLogger{
|
||||
Printfer: log.New(os.Stderr, "[dhcpv4] ", log.LstdFlags),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithDebugLogger logs multi-line full DHCPv4 messages when sent & received.
|
||||
func WithDebugLogger() ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.logger = DebugLogger{
|
||||
Printfer: log.New(os.Stderr, "[dhcpv4] ", log.LstdFlags),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger set the logger (see interface Logger).
|
||||
func WithLogger(newLogger Logger) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.logger = newLogger
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithUnicast forces client to send messages as unicast frames.
|
||||
// By default client sends messages as broadcast frames even if server address is defined.
|
||||
//
|
||||
// srcAddr is both:
|
||||
// * The source address of outgoing frames.
|
||||
// * The address to be listened for incoming frames.
|
||||
func WithUnicast(srcAddr *net.UDPAddr) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
if srcAddr == nil {
|
||||
srcAddr = &net.UDPAddr{Port: ClientPort}
|
||||
}
|
||||
c.conn, err = net.ListenUDP("udp4", srcAddr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to start listening UDP port: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithHWAddr tells to the Client to receive messages destinated to selected
|
||||
// hardware address
|
||||
func WithHWAddr(hwAddr net.HardwareAddr) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.ifaceHWAddr = hwAddr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func withBufferCap(n int) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.bufferCap = n
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetry configures the number of retransmissions to attempt.
|
||||
//
|
||||
// Default is 3.
|
||||
func WithRetry(r int) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.retry = r
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithServerAddr configures the address to send messages to.
|
||||
func WithServerAddr(n *net.UDPAddr) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.serverAddr = n
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Matcher matches DHCP packets.
|
||||
type Matcher func(*dhcpv4.DHCPv4) bool
|
||||
|
||||
// IsMessageType returns a matcher that checks for the message types.
|
||||
func IsMessageType(t dhcpv4.MessageType, tt ...dhcpv4.MessageType) Matcher {
|
||||
return func(p *dhcpv4.DHCPv4) bool {
|
||||
if p.MessageType() == t {
|
||||
return true
|
||||
}
|
||||
for _, mt := range tt {
|
||||
if p.MessageType() == mt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsCorrectServer returns a matcher that checks for the correct ServerAddress.
|
||||
func IsCorrectServer(s net.IP) Matcher {
|
||||
return func(p *dhcpv4.DHCPv4) bool {
|
||||
return p.ServerIdentifier().Equal(s)
|
||||
}
|
||||
}
|
||||
|
||||
// IsAll returns a matcher that checks for all given matchers to be true.
|
||||
func IsAll(ms ...Matcher) Matcher {
|
||||
return func(p *dhcpv4.DHCPv4) bool {
|
||||
for _, m := range ms {
|
||||
if !m(p) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// RemoteAddr is the default DHCP server address this client sends messages to.
|
||||
func (c *Client) RemoteAddr() *net.UDPAddr {
|
||||
// Make a copy so the caller cannot modify the address once the client
|
||||
// is running.
|
||||
cop := *c.serverAddr
|
||||
return &cop
|
||||
}
|
||||
|
||||
// InterfaceAddr returns the MAC address of the client's interface.
|
||||
func (c *Client) InterfaceAddr() net.HardwareAddr {
|
||||
b := make(net.HardwareAddr, len(c.ifaceHWAddr))
|
||||
copy(b, c.ifaceHWAddr)
|
||||
return b
|
||||
}
|
||||
|
||||
// DiscoverOffer sends a DHCPDiscover message and returns the first valid offer
|
||||
// received.
|
||||
func (c *Client) DiscoverOffer(ctx context.Context, modifiers ...dhcpv4.Modifier) (offer *dhcpv4.DHCPv4, err error) {
|
||||
// RFC 2131, Section 4.4.1, Table 5 details what a DISCOVER packet should
|
||||
// contain.
|
||||
discover, err := dhcpv4.NewDiscovery(c.ifaceHWAddr, dhcpv4.PrependModifiers(modifiers,
|
||||
dhcpv4.WithOption(dhcpv4.OptMaxMessageSize(MaxMessageSize)))...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a discovery request: %w", err)
|
||||
}
|
||||
|
||||
offer, err = c.SendAndRead(ctx, c.serverAddr, discover, IsMessageType(dhcpv4.MessageTypeOffer))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got an error while the discovery request: %w", err)
|
||||
}
|
||||
return offer, nil
|
||||
}
|
||||
|
||||
// Request completes the 4-way Discover-Offer-Request-Ack handshake.
|
||||
//
|
||||
// Note that modifiers will be applied *both* to Discover and Request packets.
|
||||
func (c *Client) Request(ctx context.Context, modifiers ...dhcpv4.Modifier) (lease *Lease, err error) {
|
||||
offer, err := c.DiscoverOffer(ctx, modifiers...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to receive an offer: %w", err)
|
||||
return
|
||||
}
|
||||
return c.RequestFromOffer(ctx, offer, modifiers...)
|
||||
}
|
||||
|
||||
// Inform sends an INFORM request using the given local IP.
|
||||
// Returns the ACK response from the server on success.
|
||||
func (c *Client) Inform(ctx context.Context, localIP net.IP, modifiers ...dhcpv4.Modifier) (*dhcpv4.DHCPv4, error) {
|
||||
request, err := dhcpv4.NewInform(c.ifaceHWAddr, localIP, modifiers...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// DHCP clients must not fill in the server identifier in an INFORM request as per RFC 2131 Section 4.4.1 Table 5,
|
||||
// however, they may still unicast the request to the target server if the address is known (c.serverAddr), as per
|
||||
// Section 4.4.3. The server must then respond with an ACK, as per Section 4.3.5.
|
||||
response, err := c.SendAndRead(ctx, c.serverAddr, request, IsMessageType(dhcpv4.MessageTypeAck))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got an error while processing the request: %w", err)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ErrNak is returned if a DHCP server rejected our Request.
|
||||
type ErrNak struct {
|
||||
Offer *dhcpv4.DHCPv4
|
||||
Nak *dhcpv4.DHCPv4
|
||||
}
|
||||
|
||||
// Error implements error.Error.
|
||||
func (e *ErrNak) Error() string {
|
||||
if msg := e.Nak.Message(); len(msg) > 0 {
|
||||
return fmt.Sprintf("server rejected request with Nak (msg: %s)", msg)
|
||||
}
|
||||
return "server rejected request with Nak"
|
||||
}
|
||||
|
||||
// RequestFromOffer sends a Request message and waits for an response.
|
||||
// It assumes the SELECTING state by default, see Section 4.3.2 in RFC 2131 for more details.
|
||||
func (c *Client) RequestFromOffer(ctx context.Context, offer *dhcpv4.DHCPv4, modifiers ...dhcpv4.Modifier) (*Lease, error) {
|
||||
// TODO(chrisko): should this be unicast to the server?
|
||||
request, err := dhcpv4.NewRequestFromOffer(offer, 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(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: offer,
|
||||
Nak: response,
|
||||
}
|
||||
}
|
||||
lease := &Lease{}
|
||||
lease.ACK = response
|
||||
lease.Offer = offer
|
||||
lease.CreationTime = time.Now()
|
||||
return lease, nil
|
||||
}
|
||||
|
||||
// ErrTransactionIDInUse is returned if there were an attempt to send a message
|
||||
// with the same TransactionID as we are already waiting an answer for.
|
||||
type ErrTransactionIDInUse struct {
|
||||
// TransactionID is the transaction ID of the message which the error is related to.
|
||||
TransactionID dhcpv4.TransactionID
|
||||
}
|
||||
|
||||
// Error is just the method to comply interface "error".
|
||||
func (err *ErrTransactionIDInUse) Error() string {
|
||||
return fmt.Sprintf("transaction ID %s already in use", err.TransactionID)
|
||||
}
|
||||
|
||||
// send sends p to destination and returns a response channel.
|
||||
//
|
||||
// Responses will be matched by transaction ID and ClientHWAddr.
|
||||
//
|
||||
// The returned lambda function must be called after all desired responses have
|
||||
// been received in order to return the Transaction ID to the usable pool.
|
||||
func (c *Client) send(dest *net.UDPAddr, msg *dhcpv4.DHCPv4) (resp <-chan *dhcpv4.DHCPv4, cancel func(), err error) {
|
||||
c.pendingMu.Lock()
|
||||
if _, ok := c.pending[msg.TransactionID]; ok {
|
||||
c.pendingMu.Unlock()
|
||||
return nil, nil, &ErrTransactionIDInUse{msg.TransactionID}
|
||||
}
|
||||
|
||||
ch := make(chan *dhcpv4.DHCPv4, c.bufferCap)
|
||||
done := make(chan struct{})
|
||||
c.pending[msg.TransactionID] = &pendingCh{done: done, ch: ch}
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
cancel = func() {
|
||||
// Why can't we just close ch here?
|
||||
//
|
||||
// Because receiveLoop may potentially be blocked trying to
|
||||
// send on ch. We gotta unblock it first, and then we can take
|
||||
// the lock and remove the XID from the pending transaction
|
||||
// map.
|
||||
close(done)
|
||||
|
||||
c.pendingMu.Lock()
|
||||
if p, ok := c.pending[msg.TransactionID]; ok {
|
||||
close(p.ch)
|
||||
delete(c.pending, msg.TransactionID)
|
||||
}
|
||||
c.pendingMu.Unlock()
|
||||
}
|
||||
|
||||
if _, err := c.conn.WriteTo(msg.ToBytes(), dest); err != nil {
|
||||
cancel()
|
||||
return nil, nil, fmt.Errorf("error writing packet to connection: %w", err)
|
||||
}
|
||||
return ch, cancel, nil
|
||||
}
|
||||
|
||||
// This error should never be visible to users.
|
||||
// It is used only to increase the timeout in retryFn.
|
||||
var errDeadlineExceeded = errors.New("INTERNAL ERROR: deadline exceeded")
|
||||
|
||||
// SendAndRead sends a packet p to a destination dest and waits for the first
|
||||
// response matching `match` as well as its Transaction ID and ClientHWAddr.
|
||||
//
|
||||
// If match is nil, the first packet matching the Transaction ID and
|
||||
// ClientHWAddr is returned.
|
||||
func (c *Client) SendAndRead(ctx context.Context, dest *net.UDPAddr, p *dhcpv4.DHCPv4, match Matcher) (*dhcpv4.DHCPv4, error) {
|
||||
var response *dhcpv4.DHCPv4
|
||||
err := c.retryFn(func(timeout time.Duration) error {
|
||||
ch, rem, err := c.send(dest, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.logger.PrintMessage("sent message", p)
|
||||
defer rem()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.done:
|
||||
return ErrNoResponse
|
||||
|
||||
case <-time.After(timeout):
|
||||
return errDeadlineExceeded
|
||||
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
|
||||
case packet := <-ch:
|
||||
if match == nil || match(packet) {
|
||||
c.logger.PrintMessage("received message", packet)
|
||||
response = packet
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if err == errDeadlineExceeded {
|
||||
return nil, ErrNoResponse
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) retryFn(fn func(timeout time.Duration) error) error {
|
||||
timeout := c.timeout
|
||||
|
||||
// Each retry takes the amount of timeout at worst.
|
||||
for i := 0; i < c.retry || c.retry < 0; i++ { // TODO: why is this called "retry" if this is "tries" ("retries"+1)?
|
||||
switch err := fn(timeout); err {
|
||||
case nil:
|
||||
// Got it!
|
||||
return nil
|
||||
|
||||
case errDeadlineExceeded:
|
||||
// Double timeout, then retry.
|
||||
timeout *= 2
|
||||
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return errDeadlineExceeded
|
||||
}
|
154
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/conn_unix.go
generated
vendored
Normal file
154
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/conn_unix.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2018 the u-root Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.12 && (darwin || freebsd || linux || netbsd || openbsd || dragonfly)
|
||||
// +build go1.12
|
||||
// +build darwin freebsd linux netbsd openbsd dragonfly
|
||||
|
||||
package nclient4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/mdlayher/packet"
|
||||
"github.com/u-root/uio/uio"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
// BroadcastMac is the broadcast MAC address.
|
||||
//
|
||||
// Any UDP packet sent to this address is broadcast on the subnet.
|
||||
BroadcastMac = net.HardwareAddr([]byte{255, 255, 255, 255, 255, 255})
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUDPAddrIsRequired is an error used when a passed argument is not of type "*net.UDPAddr".
|
||||
ErrUDPAddrIsRequired = errors.New("must supply UDPAddr")
|
||||
)
|
||||
|
||||
// NewRawUDPConn returns a UDP connection bound to the interface and port
|
||||
// given based on a raw packet socket. All packets are broadcasted.
|
||||
//
|
||||
// The interface can be completely unconfigured.
|
||||
func NewRawUDPConn(iface string, port int) (net.PacketConn, error) {
|
||||
ifc, err := net.InterfaceByName(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawConn, err := packet.Listen(ifc, packet.Datagram, unix.ETH_P_IP, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewBroadcastUDPConn(rawConn, &net.UDPAddr{Port: port}), nil
|
||||
}
|
||||
|
||||
// BroadcastRawUDPConn uses a raw socket to send UDP packets to the broadcast
|
||||
// MAC address.
|
||||
type BroadcastRawUDPConn struct {
|
||||
// PacketConn is a raw DGRAM socket.
|
||||
net.PacketConn
|
||||
|
||||
// boundAddr is the address this RawUDPConn is "bound" to.
|
||||
//
|
||||
// Calls to ReadFrom will only return packets destined to this address.
|
||||
boundAddr *net.UDPAddr
|
||||
}
|
||||
|
||||
// NewBroadcastUDPConn returns a PacketConn that marshals and unmarshals UDP
|
||||
// packets, sending them to the broadcast MAC at on rawPacketConn.
|
||||
//
|
||||
// Calls to ReadFrom will only return packets destined to boundAddr.
|
||||
func NewBroadcastUDPConn(rawPacketConn net.PacketConn, boundAddr *net.UDPAddr) net.PacketConn {
|
||||
return &BroadcastRawUDPConn{
|
||||
PacketConn: rawPacketConn,
|
||||
boundAddr: boundAddr,
|
||||
}
|
||||
}
|
||||
|
||||
func udpMatch(addr *net.UDPAddr, bound *net.UDPAddr) bool {
|
||||
if bound == nil {
|
||||
return true
|
||||
}
|
||||
if bound.IP != nil && !bound.IP.Equal(addr.IP) {
|
||||
return false
|
||||
}
|
||||
return bound.Port == addr.Port
|
||||
}
|
||||
|
||||
// ReadFrom implements net.PacketConn.ReadFrom.
|
||||
//
|
||||
// ReadFrom reads raw IP packets and will try to match them against
|
||||
// upc.boundAddr. Any matching packets are returned via the given buffer.
|
||||
func (upc *BroadcastRawUDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
ipHdrMaxLen := ipv4MaximumHeaderSize
|
||||
udpHdrLen := udpMinimumSize
|
||||
|
||||
for {
|
||||
pkt := make([]byte, ipHdrMaxLen+udpHdrLen+len(b))
|
||||
n, _, err := upc.PacketConn.ReadFrom(pkt)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
pkt = pkt[:n]
|
||||
buf := uio.NewBigEndianBuffer(pkt)
|
||||
|
||||
ipHdr := ipv4(buf.Data())
|
||||
|
||||
if !ipHdr.isValid(n) {
|
||||
continue
|
||||
}
|
||||
|
||||
ipHdr = ipv4(buf.Consume(int(ipHdr.headerLength())))
|
||||
|
||||
if ipHdr.transportProtocol() != udpProtocolNumber {
|
||||
continue
|
||||
}
|
||||
|
||||
if !buf.Has(udpHdrLen) {
|
||||
continue
|
||||
}
|
||||
|
||||
udpHdr := udp(buf.Consume(udpHdrLen))
|
||||
|
||||
addr := &net.UDPAddr{
|
||||
IP: ipHdr.destinationAddress(),
|
||||
Port: int(udpHdr.destinationPort()),
|
||||
}
|
||||
if !udpMatch(addr, upc.boundAddr) {
|
||||
continue
|
||||
}
|
||||
srcAddr := &net.UDPAddr{
|
||||
IP: ipHdr.sourceAddress(),
|
||||
Port: int(udpHdr.sourcePort()),
|
||||
}
|
||||
// Extra padding after end of IP packet should be ignored,
|
||||
// if not dhcp option parsing will fail.
|
||||
dhcpLen := int(ipHdr.payloadLength()) - udpHdrLen
|
||||
return copy(b, buf.Consume(dhcpLen)), srcAddr, nil
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo implements net.PacketConn.WriteTo and broadcasts all packets at the
|
||||
// raw socket level.
|
||||
//
|
||||
// WriteTo wraps the given packet in the appropriate UDP and IP header before
|
||||
// sending it on the packet conn.
|
||||
func (upc *BroadcastRawUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return 0, ErrUDPAddrIsRequired
|
||||
}
|
||||
|
||||
// Using the boundAddr is not quite right here, but it works.
|
||||
pkt := udp4pkt(b, udpAddr, upc.boundAddr)
|
||||
|
||||
// Broadcasting is not always right, but hell, what the ARP do I know.
|
||||
return upc.PacketConn.WriteTo(pkt, &packet.Addr{HardwareAddr: BroadcastMac})
|
||||
}
|
360
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/ipv4.go
generated
vendored
Normal file
360
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/ipv4.go
generated
vendored
Normal file
@ -0,0 +1,360 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// This file contains code taken from gVisor.
|
||||
|
||||
//go:build go1.12
|
||||
// +build go1.12
|
||||
|
||||
package nclient4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
const (
|
||||
versIHL = 0
|
||||
tos = 1
|
||||
totalLen = 2
|
||||
id = 4
|
||||
flagsFO = 6
|
||||
ttl = 8
|
||||
protocol = 9
|
||||
checksumOff = 10
|
||||
srcAddr = 12
|
||||
dstAddr = 16
|
||||
)
|
||||
|
||||
// transportProtocolNumber is the number of a transport protocol.
|
||||
type transportProtocolNumber uint32
|
||||
|
||||
// ipv4Fields contains the fields of an IPv4 packet. It is used to describe the
|
||||
// fields of a packet that needs to be encoded.
|
||||
type ipv4Fields struct {
|
||||
// IHL is the "internet header length" field of an IPv4 packet.
|
||||
IHL uint8
|
||||
|
||||
// TOS is the "type of service" field of an IPv4 packet.
|
||||
TOS uint8
|
||||
|
||||
// TotalLength is the "total length" field of an IPv4 packet.
|
||||
TotalLength uint16
|
||||
|
||||
// ID is the "identification" field of an IPv4 packet.
|
||||
ID uint16
|
||||
|
||||
// Flags is the "flags" field of an IPv4 packet.
|
||||
Flags uint8
|
||||
|
||||
// FragmentOffset is the "fragment offset" field of an IPv4 packet.
|
||||
FragmentOffset uint16
|
||||
|
||||
// TTL is the "time to live" field of an IPv4 packet.
|
||||
TTL uint8
|
||||
|
||||
// Protocol is the "protocol" field of an IPv4 packet.
|
||||
Protocol uint8
|
||||
|
||||
// checksum is the "checksum" field of an IPv4 packet.
|
||||
checksum uint16
|
||||
|
||||
// SrcAddr is the "source ip address" of an IPv4 packet.
|
||||
SrcAddr net.IP
|
||||
|
||||
// DstAddr is the "destination ip address" of an IPv4 packet.
|
||||
DstAddr net.IP
|
||||
}
|
||||
|
||||
// ipv4 represents an ipv4 header stored in a byte array.
|
||||
// Most of the methods of IPv4 access to the underlying slice without
|
||||
// checking the boundaries and could panic because of 'index out of range'.
|
||||
// Always call IsValid() to validate an instance of IPv4 before using other methods.
|
||||
type ipv4 []byte
|
||||
|
||||
const (
|
||||
// ipv4MinimumSize is the minimum size of a valid IPv4 packet.
|
||||
ipv4MinimumSize = 20
|
||||
|
||||
// ipv4MaximumHeaderSize is the maximum size of an IPv4 header. Given
|
||||
// that there are only 4 bits to represents the header length in 32-bit
|
||||
// units, the header cannot exceed 15*4 = 60 bytes.
|
||||
ipv4MaximumHeaderSize = 60
|
||||
|
||||
// ipv4AddressSize is the size, in bytes, of an IPv4 address.
|
||||
ipv4AddressSize = 4
|
||||
|
||||
// IPv4Version is the version of the IPv4 protocol.
|
||||
ipv4Version = 4
|
||||
)
|
||||
|
||||
// ipVersion returns the version of IP used in the given packet. It returns -1
|
||||
// if the packet is not large enough to contain the version field.
|
||||
func ipVersion(b []byte) int {
|
||||
// Length must be at least offset+length of version field.
|
||||
if len(b) < versIHL+1 {
|
||||
return -1
|
||||
}
|
||||
return int(b[versIHL] >> ipVersionShift)
|
||||
}
|
||||
|
||||
const (
|
||||
ipVersionShift = 4
|
||||
)
|
||||
|
||||
// headerLength returns the value of the "header length" field of the ipv4
|
||||
// header.
|
||||
func (b ipv4) headerLength() uint8 {
|
||||
return (b[versIHL] & 0xf) * 4
|
||||
}
|
||||
|
||||
// protocol returns the value of the protocol field of the ipv4 header.
|
||||
func (b ipv4) protocol() uint8 {
|
||||
return b[protocol]
|
||||
}
|
||||
|
||||
// sourceAddress returns the "source address" field of the ipv4 header.
|
||||
func (b ipv4) sourceAddress() net.IP {
|
||||
return net.IP(b[srcAddr : srcAddr+ipv4AddressSize])
|
||||
}
|
||||
|
||||
// destinationAddress returns the "destination address" field of the ipv4
|
||||
// header.
|
||||
func (b ipv4) destinationAddress() net.IP {
|
||||
return net.IP(b[dstAddr : dstAddr+ipv4AddressSize])
|
||||
}
|
||||
|
||||
// transportProtocol implements Network.transportProtocol.
|
||||
func (b ipv4) transportProtocol() transportProtocolNumber {
|
||||
return transportProtocolNumber(b.protocol())
|
||||
}
|
||||
|
||||
// payloadLength returns the length of the payload portion of the ipv4 packet.
|
||||
func (b ipv4) payloadLength() uint16 {
|
||||
return b.totalLength() - uint16(b.headerLength())
|
||||
}
|
||||
|
||||
// totalLength returns the "total length" field of the ipv4 header.
|
||||
func (b ipv4) totalLength() uint16 {
|
||||
return binary.BigEndian.Uint16(b[totalLen:])
|
||||
}
|
||||
|
||||
// setTotalLength sets the "total length" field of the ipv4 header.
|
||||
func (b ipv4) setTotalLength(totalLength uint16) {
|
||||
binary.BigEndian.PutUint16(b[totalLen:], totalLength)
|
||||
}
|
||||
|
||||
// setChecksum sets the checksum field of the ipv4 header.
|
||||
func (b ipv4) setChecksum(v uint16) {
|
||||
binary.BigEndian.PutUint16(b[checksumOff:], v)
|
||||
}
|
||||
|
||||
// setFlagsFragmentOffset sets the "flags" and "fragment offset" fields of the
|
||||
// ipv4 header.
|
||||
func (b ipv4) setFlagsFragmentOffset(flags uint8, offset uint16) {
|
||||
v := (uint16(flags) << 13) | (offset >> 3)
|
||||
binary.BigEndian.PutUint16(b[flagsFO:], v)
|
||||
}
|
||||
|
||||
// calculateChecksum calculates the checksum of the ipv4 header.
|
||||
func (b ipv4) calculateChecksum() uint16 {
|
||||
return checksum(b[:b.headerLength()], 0)
|
||||
}
|
||||
|
||||
// encode encodes all the fields of the ipv4 header.
|
||||
func (b ipv4) encode(i *ipv4Fields) {
|
||||
b[versIHL] = (4 << 4) | ((i.IHL / 4) & 0xf)
|
||||
b[tos] = i.TOS
|
||||
b.setTotalLength(i.TotalLength)
|
||||
binary.BigEndian.PutUint16(b[id:], i.ID)
|
||||
b.setFlagsFragmentOffset(i.Flags, i.FragmentOffset)
|
||||
b[ttl] = i.TTL
|
||||
b[protocol] = i.Protocol
|
||||
b.setChecksum(i.checksum)
|
||||
copy(b[srcAddr:srcAddr+ipv4AddressSize], i.SrcAddr)
|
||||
copy(b[dstAddr:dstAddr+ipv4AddressSize], i.DstAddr)
|
||||
}
|
||||
|
||||
// isValid performs basic validation on the packet.
|
||||
func (b ipv4) isValid(pktSize int) bool {
|
||||
if len(b) < ipv4MinimumSize {
|
||||
return false
|
||||
}
|
||||
|
||||
hlen := int(b.headerLength())
|
||||
tlen := int(b.totalLength())
|
||||
if hlen < ipv4MinimumSize || hlen > tlen || tlen > pktSize {
|
||||
return false
|
||||
}
|
||||
|
||||
if ipVersion(b) != ipv4Version {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const (
|
||||
udpSrcPort = 0
|
||||
udpDstPort = 2
|
||||
udpLength = 4
|
||||
udpchecksum = 6
|
||||
)
|
||||
|
||||
// udpFields contains the fields of a udp packet. It is used to describe the
|
||||
// fields of a packet that needs to be encoded.
|
||||
type udpFields struct {
|
||||
// SrcPort is the "source port" field of a udp packet.
|
||||
SrcPort uint16
|
||||
|
||||
// DstPort is the "destination port" field of a UDP packet.
|
||||
DstPort uint16
|
||||
|
||||
// Length is the "length" field of a UDP packet.
|
||||
Length uint16
|
||||
|
||||
// checksum is the "checksum" field of a UDP packet.
|
||||
checksum uint16
|
||||
}
|
||||
|
||||
// udp represents a udp header stored in a byte array.
|
||||
type udp []byte
|
||||
|
||||
const (
|
||||
// udpMinimumSize is the minimum size of a valid udp packet.
|
||||
udpMinimumSize = 8
|
||||
|
||||
// udpProtocolNumber is udp's transport protocol number.
|
||||
udpProtocolNumber transportProtocolNumber = 17
|
||||
)
|
||||
|
||||
// sourcePort returns the "source port" field of the udp header.
|
||||
func (b udp) sourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpSrcPort:])
|
||||
}
|
||||
|
||||
// DestinationPort returns the "destination port" field of the udp header.
|
||||
func (b udp) destinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpDstPort:])
|
||||
}
|
||||
|
||||
// Length returns the "length" field of the udp header.
|
||||
func (b udp) length() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpLength:])
|
||||
}
|
||||
|
||||
// setChecksum sets the "checksum" field of the udp header.
|
||||
func (b udp) setChecksum(checksum uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpchecksum:], checksum)
|
||||
}
|
||||
|
||||
// calculateChecksum calculates the checksum of the udp packet, given the total
|
||||
// length of the packet and the checksum of the network-layer pseudo-header
|
||||
// (excluding the total length) and the checksum of the payload.
|
||||
func (b udp) calculateChecksum(partialchecksum uint16, totalLen uint16) uint16 {
|
||||
// Add the length portion of the checksum to the pseudo-checksum.
|
||||
tmp := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(tmp, totalLen)
|
||||
xsum := checksum(tmp, partialchecksum)
|
||||
|
||||
// Calculate the rest of the checksum.
|
||||
return checksum(b[:udpMinimumSize], xsum)
|
||||
}
|
||||
|
||||
// encode encodes all the fields of the udp header.
|
||||
func (b udp) encode(u *udpFields) {
|
||||
binary.BigEndian.PutUint16(b[udpSrcPort:], u.SrcPort)
|
||||
binary.BigEndian.PutUint16(b[udpDstPort:], u.DstPort)
|
||||
binary.BigEndian.PutUint16(b[udpLength:], u.Length)
|
||||
binary.BigEndian.PutUint16(b[udpchecksum:], u.checksum)
|
||||
}
|
||||
|
||||
func calculateChecksum(buf []byte, initial uint32) uint16 {
|
||||
v := initial
|
||||
|
||||
l := len(buf)
|
||||
if l&1 != 0 {
|
||||
l--
|
||||
v += uint32(buf[l]) << 8
|
||||
}
|
||||
|
||||
for i := 0; i < l; i += 2 {
|
||||
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
|
||||
}
|
||||
|
||||
return checksumCombine(uint16(v), uint16(v>>16))
|
||||
}
|
||||
|
||||
// checksum calculates the checksum (as defined in RFC 1071) of the bytes in the
|
||||
// given byte array.
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func checksum(buf []byte, initial uint16) uint16 {
|
||||
return calculateChecksum(buf, uint32(initial))
|
||||
}
|
||||
|
||||
// checksumCombine combines the two uint16 to form their checksum. This is done
|
||||
// by adding them and the carry.
|
||||
//
|
||||
// Note that checksum a must have been computed on an even number of bytes.
|
||||
func checksumCombine(a, b uint16) uint16 {
|
||||
v := uint32(a) + uint32(b)
|
||||
return uint16(v + v>>16)
|
||||
}
|
||||
|
||||
// pseudoHeaderchecksum calculates the pseudo-header checksum for the
|
||||
// given destination protocol and network address, ignoring the length
|
||||
// field. pseudo-headers are needed by transport layers when calculating
|
||||
// their own checksum.
|
||||
func pseudoHeaderchecksum(protocol transportProtocolNumber, srcAddr net.IP, dstAddr net.IP) uint16 {
|
||||
xsum := checksum([]byte(srcAddr), 0)
|
||||
xsum = checksum([]byte(dstAddr), xsum)
|
||||
return checksum([]byte{0, uint8(protocol)}, xsum)
|
||||
}
|
||||
|
||||
func udp4pkt(packet []byte, dest *net.UDPAddr, src *net.UDPAddr) []byte {
|
||||
ipLen := ipv4MinimumSize
|
||||
udpLen := udpMinimumSize
|
||||
|
||||
h := make([]byte, 0, ipLen+udpLen+len(packet))
|
||||
hdr := uio.NewBigEndianBuffer(h)
|
||||
|
||||
ipv4fields := &ipv4Fields{
|
||||
IHL: ipv4MinimumSize,
|
||||
TotalLength: uint16(ipLen + udpLen + len(packet)),
|
||||
TTL: 64, // Per RFC 1700's recommendation for IP time to live
|
||||
Protocol: uint8(udpProtocolNumber),
|
||||
SrcAddr: src.IP.To4(),
|
||||
DstAddr: dest.IP.To4(),
|
||||
}
|
||||
ipv4hdr := ipv4(hdr.WriteN(ipLen))
|
||||
ipv4hdr.encode(ipv4fields)
|
||||
ipv4hdr.setChecksum(^ipv4hdr.calculateChecksum())
|
||||
|
||||
udphdr := udp(hdr.WriteN(udpLen))
|
||||
udphdr.encode(&udpFields{
|
||||
SrcPort: uint16(src.Port),
|
||||
DstPort: uint16(dest.Port),
|
||||
Length: uint16(udpLen + len(packet)),
|
||||
})
|
||||
|
||||
xsum := checksum(packet, pseudoHeaderchecksum(
|
||||
ipv4hdr.transportProtocol(), ipv4fields.SrcAddr, ipv4fields.DstAddr))
|
||||
udphdr.setChecksum(^udphdr.calculateChecksum(xsum, udphdr.length()))
|
||||
|
||||
hdr.WriteBytes(packet)
|
||||
return hdr.Data()
|
||||
}
|
79
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/lease.go
generated
vendored
Normal file
79
vendor/github.com/insomniacslk/dhcp/dhcpv4/nclient4/lease.go
generated
vendored
Normal file
@ -0,0 +1,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
|
||||
}
|
61
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_autoconfigure.go
generated
vendored
Normal file
61
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_autoconfigure.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AutoConfiguration implements encoding and decoding functions for a
|
||||
// byte enumeration as used in RFC 2563, Section 2.
|
||||
type AutoConfiguration byte
|
||||
|
||||
const (
|
||||
DoNotAutoConfigure AutoConfiguration = 0
|
||||
AutoConfigure AutoConfiguration = 1
|
||||
)
|
||||
|
||||
var autoConfigureToString = map[AutoConfiguration]string{
|
||||
DoNotAutoConfigure: "DoNotAutoConfigure",
|
||||
AutoConfigure: "AutoConfigure",
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (o AutoConfiguration) ToBytes() []byte {
|
||||
return []byte{byte(o)}
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (o AutoConfiguration) String() string {
|
||||
s := autoConfigureToString[o]
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("UNKNOWN (%d)", byte(o))
|
||||
}
|
||||
|
||||
// FromBytes parses a a single byte into AutoConfiguration
|
||||
func (o *AutoConfiguration) FromBytes(data []byte) error {
|
||||
if len(data) == 1 {
|
||||
*o = AutoConfiguration(data[0])
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid buffer length (%d)", len(data))
|
||||
}
|
||||
|
||||
// GetByte parses any single-byte option
|
||||
func GetByte(code OptionCode, o Options) (byte, error) {
|
||||
data := o.Get(code)
|
||||
if data == nil {
|
||||
return 0, fmt.Errorf("option not present")
|
||||
}
|
||||
if len(data) != 1 {
|
||||
return 0, fmt.Errorf("Invalid buffer length (%d)", len(data))
|
||||
}
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
// OptAutoConfigure returns a new AutoConfigure option.
|
||||
//
|
||||
// The AutoConfigure option is described by RFC 2563, Section 2.
|
||||
func OptAutoConfigure(autoconf AutoConfiguration) Option {
|
||||
return Option{Code: OptionAutoConfigure, Value: autoconf}
|
||||
}
|
56
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_duration.go
generated
vendored
Normal file
56
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_duration.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// MaxLeaseTime is the maximum lease time that can be encoded.
|
||||
var MaxLeaseTime = math.MaxUint32 * time.Second
|
||||
|
||||
// Duration implements the IP address lease time option described by RFC 2132,
|
||||
// Section 9.2.
|
||||
type Duration time.Duration
|
||||
|
||||
// FromBytes parses a duration from a byte stream according to RFC 2132, Section 9.2.
|
||||
func (d *Duration) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*d = Duration(time.Duration(buf.Read32()) * time.Second)
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (d Duration) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
buf.Write32(uint32(time.Duration(d) / time.Second))
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (d Duration) String() string {
|
||||
return time.Duration(d).String()
|
||||
}
|
||||
|
||||
// OptIPAddressLeaseTime returns a new IP address lease time option.
|
||||
//
|
||||
// The IP address lease time option is described by RFC 2132, Section 9.2.
|
||||
func OptIPAddressLeaseTime(d time.Duration) Option {
|
||||
return Option{Code: OptionIPAddressLeaseTime, Value: Duration(d)}
|
||||
}
|
||||
|
||||
// The IP address renew time option as described by RFC 2132, Section 9.11.
|
||||
func OptRenewTimeValue(d time.Duration) Option {
|
||||
return Option{Code: OptionRenewTimeValue, Value: Duration(d)}
|
||||
}
|
||||
|
||||
// The IP address rebinding time option as described by RFC 2132, Section 9.12.
|
||||
func OptRebindingTimeValue(d time.Duration) Option {
|
||||
return Option{Code: OptionRebindingTimeValue, Value: Duration(d)}
|
||||
}
|
||||
|
||||
// The IPv6-Only Preferred option is described by RFC 8925, Section 3.1
|
||||
func OptIPv6OnlyPreferred(d time.Duration) Option {
|
||||
return Option{Code: OptionIPv6OnlyPreferred, Value: Duration(d)}
|
||||
}
|
27
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_generic.go
generated
vendored
Normal file
27
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_generic.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// OptionGeneric is an option that only contains the option code and associated
|
||||
// data. Every option that does not have a specific implementation will fall
|
||||
// back to this option.
|
||||
type OptionGeneric struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized generic option as a slice of bytes.
|
||||
func (o OptionGeneric) ToBytes() []byte {
|
||||
return o.Data
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a generic option.
|
||||
func (o OptionGeneric) String() string {
|
||||
return fmt.Sprintf("%v", o.Data)
|
||||
}
|
||||
|
||||
// OptGeneric returns a generic option.
|
||||
func OptGeneric(code OptionCode, value []byte) Option {
|
||||
return Option{Code: code, Value: OptionGeneric{value}}
|
||||
}
|
62
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ip.go
generated
vendored
Normal file
62
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ip.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// IP implements DHCPv4 IP option marshaling and unmarshaling as described by
|
||||
// RFC 2132, Sections 5.3, 9.1, 9.7, and others.
|
||||
type IP net.IP
|
||||
|
||||
// FromBytes parses an IP from data in binary form.
|
||||
func (i *IP) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*i = IP(buf.CopyN(net.IPv4len))
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (i IP) ToBytes() []byte {
|
||||
return []byte(net.IP(i).To4())
|
||||
}
|
||||
|
||||
// String returns a human-readable IP.
|
||||
func (i IP) String() string {
|
||||
return net.IP(i).String()
|
||||
}
|
||||
|
||||
// GetIP returns code out of o parsed as an IP.
|
||||
func GetIP(code OptionCode, o Options) net.IP {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var ip IP
|
||||
if err := ip.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return net.IP(ip)
|
||||
}
|
||||
|
||||
// OptBroadcastAddress returns a new DHCPv4 Broadcast Address option.
|
||||
//
|
||||
// The broadcast address option is described in RFC 2132, Section 5.3.
|
||||
func OptBroadcastAddress(ip net.IP) Option {
|
||||
return Option{Code: OptionBroadcastAddress, Value: IP(ip)}
|
||||
}
|
||||
|
||||
// OptRequestedIPAddress returns a new DHCPv4 Requested IP Address option.
|
||||
//
|
||||
// The requested IP address option is described by RFC 2132, Section 9.1.
|
||||
func OptRequestedIPAddress(ip net.IP) Option {
|
||||
return Option{Code: OptionRequestedIPAddress, Value: IP(ip)}
|
||||
}
|
||||
|
||||
// OptServerIdentifier returns a new DHCPv4 Server Identifier option.
|
||||
//
|
||||
// The server identifier option is described by RFC 2132, Section 9.7.
|
||||
func OptServerIdentifier(ip net.IP) Option {
|
||||
return Option{Code: OptionServerIdentifier, Value: IP(ip)}
|
||||
}
|
103
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ips.go
generated
vendored
Normal file
103
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_ips.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// IPs are IPv4 addresses from a DHCP packet as used and specified by options
|
||||
// in RFC 2132, Sections 3.5 through 3.13, 8.2, 8.3, 8.5, 8.6, 8.9, and 8.10.
|
||||
//
|
||||
// IPs implements the OptionValue type.
|
||||
type IPs []net.IP
|
||||
|
||||
// FromBytes parses an IPv4 address from a DHCP packet as used and specified by
|
||||
// options in RFC 2132, Sections 3.5 through 3.13, 8.2, 8.3, 8.5, 8.6, 8.9, and
|
||||
// 8.10.
|
||||
func (i *IPs) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
if buf.Len() == 0 {
|
||||
return fmt.Errorf("IP DHCP options must always list at least one IP")
|
||||
}
|
||||
|
||||
*i = make(IPs, 0, buf.Len()/net.IPv4len)
|
||||
for buf.Has(net.IPv4len) {
|
||||
*i = append(*i, net.IP(buf.CopyN(net.IPv4len)))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes marshals IPv4 addresses to a DHCP packet as specified by RFC 2132,
|
||||
// Section 3.5 et al.
|
||||
func (i IPs) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, ip := range i {
|
||||
buf.WriteBytes(ip.To4())
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a list of IPs.
|
||||
func (i IPs) String() string {
|
||||
s := make([]string, 0, len(i))
|
||||
for _, ip := range i {
|
||||
s = append(s, ip.String())
|
||||
}
|
||||
return strings.Join(s, ", ")
|
||||
}
|
||||
|
||||
// GetIPs parses a list of IPs from code in o.
|
||||
func GetIPs(code OptionCode, o Options) []net.IP {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var ips IPs
|
||||
if err := ips.FromBytes(v); err != nil {
|
||||
return nil
|
||||
}
|
||||
return []net.IP(ips)
|
||||
}
|
||||
|
||||
// OptRouter returns a new DHCPv4 Router option.
|
||||
//
|
||||
// The Router option is described by RFC 2132, Section 3.5.
|
||||
func OptRouter(routers ...net.IP) Option {
|
||||
return Option{
|
||||
Code: OptionRouter,
|
||||
Value: IPs(routers),
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter updates a packet with the DHCPv4 Router option.
|
||||
func WithRouter(routers ...net.IP) Modifier {
|
||||
return WithOption(OptRouter(routers...))
|
||||
}
|
||||
|
||||
// OptNTPServers returns a new DHCPv4 NTP Server option.
|
||||
//
|
||||
// The NTP servers option is described by RFC 2132, Section 8.3.
|
||||
func OptNTPServers(ntpServers ...net.IP) Option {
|
||||
return Option{
|
||||
Code: OptionNTPServers,
|
||||
Value: IPs(ntpServers),
|
||||
}
|
||||
}
|
||||
|
||||
// OptDNS returns a new DHCPv4 Domain Name Server option.
|
||||
//
|
||||
// The DNS server option is described by RFC 2132, Section 3.8.
|
||||
func OptDNS(servers ...net.IP) Option {
|
||||
return Option{
|
||||
Code: OptionDomainNameServer,
|
||||
Value: IPs(servers),
|
||||
}
|
||||
}
|
||||
|
||||
// WithDNS modifies a packet with the DHCPv4 Domain Name Server option.
|
||||
func WithDNS(servers ...net.IP) Modifier {
|
||||
return WithOption(OptDNS(servers...))
|
||||
}
|
50
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_maximum_dhcp_message_size.go
generated
vendored
Normal file
50
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_maximum_dhcp_message_size.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Uint16 implements encoding and decoding functions for a uint16 as used in
|
||||
// RFC 2132, Section 9.10.
|
||||
type Uint16 uint16
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (o Uint16) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
buf.Write16(uint16(o))
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (o Uint16) String() string {
|
||||
return fmt.Sprintf("%d", uint16(o))
|
||||
}
|
||||
|
||||
// FromBytes decodes data into o as per RFC 2132, Section 9.10.
|
||||
func (o *Uint16) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*o = Uint16(buf.Read16())
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// GetUint16 parses a uint16 from code in o.
|
||||
func GetUint16(code OptionCode, o Options) (uint16, error) {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return 0, fmt.Errorf("option not present")
|
||||
}
|
||||
var u Uint16
|
||||
if err := u.FromBytes(v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint16(u), nil
|
||||
}
|
||||
|
||||
// OptMaxMessageSize returns a new DHCP Maximum Message Size option.
|
||||
//
|
||||
// The Maximum DHCP Message Size option is described by RFC 2132, Section 9.10.
|
||||
func OptMaxMessageSize(size uint16) Option {
|
||||
return Option{Code: OptionMaximumDHCPMessageSize, Value: Uint16(size)}
|
||||
}
|
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_message_type.go
generated
vendored
Normal file
6
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_message_type.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
package dhcpv4
|
||||
|
||||
// OptMessageType returns a new DHCPv4 Message Type option.
|
||||
func OptMessageType(m MessageType) Option {
|
||||
return Option{Code: OptionDHCPMessageType, Value: m}
|
||||
}
|
23
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_misc.go
generated
vendored
Normal file
23
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_misc.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
)
|
||||
|
||||
// OptDomainSearch returns a new domain search option.
|
||||
//
|
||||
// The domain search option is described by RFC 3397, Section 2.
|
||||
func OptDomainSearch(labels *rfc1035label.Labels) Option {
|
||||
return Option{Code: OptionDNSDomainSearchList, Value: labels}
|
||||
}
|
||||
|
||||
// OptClientArch returns a new Client System Architecture Type option.
|
||||
func OptClientArch(archs ...iana.Arch) Option {
|
||||
return Option{Code: OptionClientSystemArchitectureType, Value: iana.Archs(archs)}
|
||||
}
|
||||
|
||||
// OptClientIdentifier returns a new Client Identifier option.
|
||||
func OptClientIdentifier(ident []byte) Option {
|
||||
return OptGeneric(OptionClientIdentifier, ident)
|
||||
}
|
72
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_parameter_request_list.go
generated
vendored
Normal file
72
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_parameter_request_list.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// OptionCodeList is a list of DHCP option codes.
|
||||
type OptionCodeList []OptionCode
|
||||
|
||||
// Has returns whether c is in the list.
|
||||
func (ol OptionCodeList) Has(c OptionCode) bool {
|
||||
for _, code := range ol {
|
||||
if code == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Add adds option codes in cs to ol.
|
||||
func (ol *OptionCodeList) Add(cs ...OptionCode) {
|
||||
for _, c := range cs {
|
||||
if !ol.Has(c) {
|
||||
*ol = append(*ol, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ol OptionCodeList) sort() {
|
||||
sort.Slice(ol, func(i, j int) bool { return ol[i].Code() < ol[j].Code() })
|
||||
}
|
||||
|
||||
// String returns a human-readable string for the option names.
|
||||
func (ol OptionCodeList) String() string {
|
||||
var names []string
|
||||
ol.sort()
|
||||
for _, code := range ol {
|
||||
names = append(names, code.String())
|
||||
}
|
||||
return strings.Join(names, ", ")
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option as defined by
|
||||
// RFC 2132, Section 9.8.
|
||||
func (ol OptionCodeList) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, req := range ol {
|
||||
buf.Write8(req.Code())
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// FromBytes parses a byte stream for this option as described by RFC 2132,
|
||||
// Section 9.8.
|
||||
func (ol *OptionCodeList) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*ol = make(OptionCodeList, 0, buf.Len())
|
||||
for buf.Has(1) {
|
||||
*ol = append(*ol, optionCode(buf.Read8()))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// OptParameterRequestList returns a new DHCPv4 Parameter Request List.
|
||||
//
|
||||
// The parameter request list option is described by RFC 2132, Section 9.8.
|
||||
func OptParameterRequestList(codes ...OptionCode) Option {
|
||||
return Option{Code: OptionParameterRequestList, Value: OptionCodeList(codes)}
|
||||
}
|
100
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_relay_agent_information.go
generated
vendored
Normal file
100
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_relay_agent_information.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RelayOptions is like Options, but stringifies using the Relay Agent Specific
|
||||
// option space.
|
||||
type RelayOptions struct {
|
||||
Options
|
||||
}
|
||||
|
||||
var relayHumanizer = OptionHumanizer{
|
||||
ValueHumanizer: func(code OptionCode, data []byte) fmt.Stringer {
|
||||
var d OptionDecoder
|
||||
switch code {
|
||||
case LinkSelectionSubOption, ServerIdentifierOverrideSubOption:
|
||||
d = &IPs{}
|
||||
}
|
||||
if d != nil && d.FromBytes(data) == nil {
|
||||
return d
|
||||
}
|
||||
return raiSubOptionValue{data}
|
||||
},
|
||||
CodeHumanizer: func(c uint8) OptionCode {
|
||||
return raiSubOptionCode(c)
|
||||
},
|
||||
}
|
||||
|
||||
// String prints the contained options using Relay Agent-specific option code parsing.
|
||||
func (r RelayOptions) String() string {
|
||||
return "\n" + r.Options.ToString(relayHumanizer)
|
||||
}
|
||||
|
||||
// FromBytes parses relay agent options from data.
|
||||
func (r *RelayOptions) FromBytes(data []byte) error {
|
||||
r.Options = make(Options)
|
||||
return r.Options.FromBytes(data)
|
||||
}
|
||||
|
||||
// OptRelayAgentInfo returns a new DHCP Relay Agent Info option.
|
||||
//
|
||||
// The relay agent info option is described by RFC 3046.
|
||||
func OptRelayAgentInfo(o ...Option) Option {
|
||||
return Option{Code: OptionRelayAgentInformation, Value: RelayOptions{OptionsFromList(o...)}}
|
||||
}
|
||||
|
||||
type raiSubOptionValue struct {
|
||||
val []byte
|
||||
}
|
||||
|
||||
func (rv raiSubOptionValue) String() string {
|
||||
return fmt.Sprintf("%q (%v)", string(rv.val), rv.val)
|
||||
}
|
||||
|
||||
type raiSubOptionCode uint8
|
||||
|
||||
func (o raiSubOptionCode) Code() uint8 {
|
||||
return uint8(o)
|
||||
}
|
||||
|
||||
func (o raiSubOptionCode) String() string {
|
||||
if s, ok := raiSubOptionCodeToString[o]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", o)
|
||||
}
|
||||
|
||||
// Option 82 Relay Agention Information Sub Options
|
||||
const (
|
||||
AgentCircuitIDSubOption raiSubOptionCode = 1 // RFC 3046
|
||||
AgentRemoteIDSubOption raiSubOptionCode = 2 // RFC 3046
|
||||
DOCSISDeviceClassSubOption raiSubOptionCode = 4 // RFC 3256
|
||||
LinkSelectionSubOption raiSubOptionCode = 5 // RFC 3527
|
||||
SubscriberIDSubOption raiSubOptionCode = 6 // RFC 3993
|
||||
RADIUSAttributesSubOption raiSubOptionCode = 7 // RFC 4014
|
||||
AuthenticationSubOption raiSubOptionCode = 8 // RFC 4030
|
||||
VendorSpecificInformationSubOption raiSubOptionCode = 9 // RFC 4243
|
||||
RelayAgentFlagsSubOption raiSubOptionCode = 10 // RFC 5010
|
||||
ServerIdentifierOverrideSubOption raiSubOptionCode = 11 // RFC 5107
|
||||
RelaySourcePortSubOption raiSubOptionCode = 19 // RFC 8357
|
||||
VirtualSubnetSelectionSubOption raiSubOptionCode = 151 // RFC 6607
|
||||
VirtualSubnetSelectionControlSubOption raiSubOptionCode = 152 // RFC 6607
|
||||
)
|
||||
|
||||
var raiSubOptionCodeToString = map[raiSubOptionCode]string{
|
||||
AgentCircuitIDSubOption: "Agent Circuit ID Sub-option",
|
||||
AgentRemoteIDSubOption: "Agent Remote ID Sub-option",
|
||||
DOCSISDeviceClassSubOption: "DOCSIS Device Class Sub-option",
|
||||
LinkSelectionSubOption: "Link Selection Sub-option",
|
||||
SubscriberIDSubOption: "Subscriber ID Sub-option",
|
||||
RADIUSAttributesSubOption: "RADIUS Attributes Sub-option",
|
||||
AuthenticationSubOption: "Authentication Sub-option",
|
||||
VendorSpecificInformationSubOption: "Vendor Specific Sub-option",
|
||||
RelayAgentFlagsSubOption: "Relay Agent Flags Sub-option",
|
||||
ServerIdentifierOverrideSubOption: "Server Identifier Override Sub-option",
|
||||
RelaySourcePortSubOption: "Relay Source Port Sub-option",
|
||||
VirtualSubnetSelectionSubOption: "Virtual Subnet Selection Sub-option",
|
||||
VirtualSubnetSelectionControlSubOption: "Virtual Subnet Selection Control Sub-option",
|
||||
}
|
104
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_routes.go
generated
vendored
Normal file
104
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_routes.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Route is a classless static route as per RFC 3442.
|
||||
type Route struct {
|
||||
// Dest is the destination network.
|
||||
Dest *net.IPNet
|
||||
|
||||
// Router is the router to use for the given destination network.
|
||||
Router net.IP
|
||||
}
|
||||
|
||||
// Marshal implements uio.Marshaler.
|
||||
//
|
||||
// Format described in RFC 3442:
|
||||
//
|
||||
// <size of mask in number of bits>
|
||||
// <destination address, omitting octets that must be zero per mask>
|
||||
// <route IP>
|
||||
func (r Route) Marshal(buf *uio.Lexer) {
|
||||
ones, _ := r.Dest.Mask.Size()
|
||||
buf.Write8(uint8(ones))
|
||||
|
||||
// Only write the non-zero octets.
|
||||
dstLen := (ones + 7) / 8
|
||||
buf.WriteBytes(r.Dest.IP.To4()[:dstLen])
|
||||
|
||||
buf.WriteBytes(r.Router.To4())
|
||||
}
|
||||
|
||||
// Unmarshal implements uio.Unmarshaler.
|
||||
func (r *Route) Unmarshal(buf *uio.Lexer) error {
|
||||
maskSize := buf.Read8()
|
||||
if maskSize > 32 {
|
||||
return fmt.Errorf("invalid mask length %d in route option", maskSize)
|
||||
}
|
||||
r.Dest = &net.IPNet{
|
||||
IP: make([]byte, net.IPv4len),
|
||||
Mask: net.CIDRMask(int(maskSize), 32),
|
||||
}
|
||||
|
||||
dstLen := (maskSize + 7) / 8
|
||||
buf.ReadBytes(r.Dest.IP[:dstLen])
|
||||
|
||||
r.Router = buf.CopyN(net.IPv4len)
|
||||
return buf.Error()
|
||||
}
|
||||
|
||||
// String prints the destination network and router IP.
|
||||
func (r *Route) String() string {
|
||||
return fmt.Sprintf("route to %s via %s", r.Dest, r.Router)
|
||||
}
|
||||
|
||||
// Routes is a collection of network routes.
|
||||
type Routes []*Route
|
||||
|
||||
// FromBytes parses routes from a set of bytes as described by RFC 3442.
|
||||
func (r *Routes) FromBytes(p []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(p)
|
||||
for buf.Has(1) {
|
||||
var route Route
|
||||
if err := route.Unmarshal(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = append(*r, &route)
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes marshals a set of routes as described by RFC 3442.
|
||||
func (r Routes) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, route := range r {
|
||||
route.Marshal(buf)
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String prints all routes.
|
||||
func (r Routes) String() string {
|
||||
s := make([]string, 0, len(r))
|
||||
for _, route := range r {
|
||||
s = append(s, route.String())
|
||||
}
|
||||
return strings.Join(s, "; ")
|
||||
}
|
||||
|
||||
// OptClasslessStaticRoute returns a new DHCPv4 Classless Static Route
|
||||
// option.
|
||||
//
|
||||
// The Classless Static Route option is described by RFC 3442.
|
||||
func OptClasslessStaticRoute(routes ...*Route) Option {
|
||||
return Option{
|
||||
Code: OptionClasslessStaticRoute,
|
||||
Value: Routes(routes),
|
||||
}
|
||||
}
|
84
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_string.go
generated
vendored
Normal file
84
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_string.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
package dhcpv4
|
||||
|
||||
// String represents an option encapsulating a string in IPv4 DHCP.
|
||||
//
|
||||
// This representation is shared by multiple options specified by RFC 2132,
|
||||
// Sections 3.14, 3.16, 3.17, 3.19, and 3.20.
|
||||
type String string
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (o String) ToBytes() []byte {
|
||||
return []byte(o)
|
||||
}
|
||||
|
||||
// String returns a human-readable string.
|
||||
func (o String) String() string {
|
||||
return string(o)
|
||||
}
|
||||
|
||||
// FromBytes parses a serialized stream of bytes into o.
|
||||
func (o *String) FromBytes(data []byte) error {
|
||||
*o = String(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetString parses an RFC 2132 string from o[code].
|
||||
func GetString(code OptionCode, o Options) string {
|
||||
v := o.Get(code)
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// OptDomainName returns a new DHCPv4 Domain Name option.
|
||||
//
|
||||
// The Domain Name option is described by RFC 2132, Section 3.17.
|
||||
func OptDomainName(name string) Option {
|
||||
return Option{Code: OptionDomainName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptHostName returns a new DHCPv4 Host Name option.
|
||||
//
|
||||
// The Host Name option is described by RFC 2132, Section 3.14.
|
||||
func OptHostName(name string) Option {
|
||||
return Option{Code: OptionHostName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptRootPath returns a new DHCPv4 Root Path option.
|
||||
//
|
||||
// The Root Path option is described by RFC 2132, Section 3.19.
|
||||
func OptRootPath(name string) Option {
|
||||
return Option{Code: OptionRootPath, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptBootFileName returns a new DHCPv4 Boot File Name option.
|
||||
//
|
||||
// The Bootfile Name option is described by RFC 2132, Section 9.5.
|
||||
func OptBootFileName(name string) Option {
|
||||
return Option{Code: OptionBootfileName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptTFTPServerName returns a new DHCPv4 TFTP Server Name option.
|
||||
//
|
||||
// The TFTP Server Name option is described by RFC 2132, Section 9.4.
|
||||
func OptTFTPServerName(name string) Option {
|
||||
return Option{Code: OptionTFTPServerName, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptClassIdentifier returns a new DHCPv4 Class Identifier option.
|
||||
//
|
||||
// The Vendor Class Identifier option is described by RFC 2132, Section 9.13.
|
||||
func OptClassIdentifier(name string) Option {
|
||||
return Option{Code: OptionClassIdentifier, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptUserClass returns a new DHCPv4 User Class option.
|
||||
func OptUserClass(name string) Option {
|
||||
return Option{Code: OptionUserClassInformation, Value: String(name)}
|
||||
}
|
||||
|
||||
// OptMessage returns a new DHCPv4 (Error) Message option.
|
||||
func OptMessage(msg string) Option {
|
||||
return Option{Code: OptionMessage, Value: String(msg)}
|
||||
}
|
55
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_strings.go
generated
vendored
Normal file
55
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_strings.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Strings represents an option encapsulating a list of strings in IPv4 DHCP as
|
||||
// specified in RFC 3004
|
||||
//
|
||||
// Strings implements the OptionValue type.
|
||||
type Strings []string
|
||||
|
||||
// FromBytes parses Strings from a DHCP packet as specified by RFC 3004.
|
||||
func (o *Strings) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
if buf.Len() == 0 {
|
||||
return fmt.Errorf("Strings DHCP option must always list at least one String")
|
||||
}
|
||||
|
||||
*o = make(Strings, 0)
|
||||
for buf.Has(1) {
|
||||
ucLen := buf.Read8()
|
||||
if ucLen == 0 {
|
||||
return fmt.Errorf("DHCP Strings must have length greater than 0")
|
||||
}
|
||||
*o = append(*o, string(buf.CopyN(int(ucLen))))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes marshals Strings to a DHCP packet as specified by RFC 3004.
|
||||
func (o Strings) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, uc := range o {
|
||||
buf.Write8(uint8(len(uc)))
|
||||
buf.WriteBytes([]byte(uc))
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a list of Strings.
|
||||
func (o Strings) String() string {
|
||||
return strings.Join(o, ", ")
|
||||
}
|
||||
|
||||
// OptRFC3004UserClass returns a new user class option according to RFC 3004.
|
||||
func OptRFC3004UserClass(v []string) Option {
|
||||
return Option{
|
||||
Code: OptionUserClassInformation,
|
||||
Value: Strings(v),
|
||||
}
|
||||
}
|
40
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_subnet_mask.go
generated
vendored
Normal file
40
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_subnet_mask.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// IPMask represents an option encapsulating the subnet mask.
|
||||
//
|
||||
// This option implements the subnet mask option in RFC 2132, Section 3.3.
|
||||
type IPMask net.IPMask
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (im IPMask) ToBytes() []byte {
|
||||
if len(im) > net.IPv4len {
|
||||
return im[:net.IPv4len]
|
||||
}
|
||||
return im
|
||||
}
|
||||
|
||||
// String returns a human-readable string.
|
||||
func (im IPMask) String() string {
|
||||
return net.IPMask(im).String()
|
||||
}
|
||||
|
||||
// FromBytes parses im from data per RFC 2132.
|
||||
func (im *IPMask) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*im = IPMask(buf.CopyN(net.IPv4len))
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// OptSubnetMask returns a new DHCPv4 SubnetMask option per RFC 2132, Section 3.3.
|
||||
func OptSubnetMask(mask net.IPMask) Option {
|
||||
return Option{
|
||||
Code: OptionSubnetMask,
|
||||
Value: IPMask(mask),
|
||||
}
|
||||
}
|
65
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_vivc.go
generated
vendored
Normal file
65
vendor/github.com/insomniacslk/dhcp/dhcpv4/option_vivc.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// VIVCIdentifier implements the vendor-identifying vendor class option
|
||||
// described by RFC 3925.
|
||||
type VIVCIdentifier struct {
|
||||
// EntID is the enterprise ID.
|
||||
EntID iana.EnterpriseID
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// OptVIVC returns a new vendor-identifying vendor class option.
|
||||
//
|
||||
// The option is described by RFC 3925.
|
||||
func OptVIVC(identifiers ...VIVCIdentifier) Option {
|
||||
return Option{
|
||||
Code: OptionVendorIdentifyingVendorClass,
|
||||
Value: VIVCIdentifiers(identifiers),
|
||||
}
|
||||
}
|
||||
|
||||
// VIVCIdentifiers implements encoding and decoding methods for a DHCP option
|
||||
// described in RFC 3925.
|
||||
type VIVCIdentifiers []VIVCIdentifier
|
||||
|
||||
// FromBytes parses data into ids per RFC 3925.
|
||||
func (ids *VIVCIdentifiers) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
for buf.Has(5) {
|
||||
entID := iana.EnterpriseID(buf.Read32())
|
||||
idLen := int(buf.Read8())
|
||||
*ids = append(*ids, VIVCIdentifier{EntID: entID, Data: buf.CopyN(idLen)})
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
// ToBytes returns a serialized stream of bytes for this option.
|
||||
func (ids VIVCIdentifiers) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, id := range ids {
|
||||
buf.Write32(uint32(id.EntID))
|
||||
buf.Write8(uint8(len(id.Data)))
|
||||
buf.WriteBytes(id.Data)
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns a human-readable string for this option.
|
||||
func (ids VIVCIdentifiers) String() string {
|
||||
if len(ids) == 0 {
|
||||
return ""
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
for _, id := range ids {
|
||||
fmt.Fprintf(&buf, " %d:'%s',", id.EntID, id.Data)
|
||||
}
|
||||
return buf.String()[1 : buf.Len()-1]
|
||||
}
|
380
vendor/github.com/insomniacslk/dhcp/dhcpv4/options.go
generated
vendored
Normal file
380
vendor/github.com/insomniacslk/dhcp/dhcpv4/options.go
generated
vendored
Normal file
@ -0,0 +1,380 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
"github.com/insomniacslk/dhcp/rfc1035label"
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrShortByteStream is an error that is thrown any time a short byte stream is
|
||||
// detected during option parsing.
|
||||
ErrShortByteStream = errors.New("short byte stream")
|
||||
|
||||
// ErrZeroLengthByteStream is an error that is thrown any time a zero-length
|
||||
// byte stream is encountered.
|
||||
ErrZeroLengthByteStream = errors.New("zero-length byte stream")
|
||||
)
|
||||
|
||||
// OptionValue is an interface that all DHCP v4 options adhere to.
|
||||
type OptionValue interface {
|
||||
ToBytes() []byte
|
||||
String() string
|
||||
}
|
||||
|
||||
// Option is a DHCPv4 option and consists of a 1-byte option code and a value
|
||||
// stream of bytes.
|
||||
//
|
||||
// The value is to be interpreted based on the option code.
|
||||
type Option struct {
|
||||
Code OptionCode
|
||||
Value OptionValue
|
||||
}
|
||||
|
||||
// String returns a human-readable version of this option.
|
||||
func (o Option) String() string {
|
||||
v := o.Value.String()
|
||||
if strings.Contains(v, "\n") {
|
||||
return fmt.Sprintf("%s:\n%s", o.Code, v)
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", o.Code, v)
|
||||
}
|
||||
|
||||
// Options is a collection of options.
|
||||
type Options map[uint8][]byte
|
||||
|
||||
// OptionsFromList adds all given options to an options map.
|
||||
func OptionsFromList(o ...Option) Options {
|
||||
opts := make(Options)
|
||||
for _, opt := range o {
|
||||
opts.Update(opt)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// Get will attempt to get all options that match a DHCPv4 option
|
||||
// from its OptionCode. If the option was not found it will return an
|
||||
// empty list.
|
||||
//
|
||||
// According to RFC 3396, options that are specified more than once are
|
||||
// concatenated, and hence this should always just return one option. This
|
||||
// currently returns a list to be API compatible.
|
||||
func (o Options) Get(code OptionCode) []byte {
|
||||
return o[code.Code()]
|
||||
}
|
||||
|
||||
// Has checks whether o has the given opcode.
|
||||
func (o Options) Has(opcode OptionCode) bool {
|
||||
_, ok := o[opcode.Code()]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Del deletes the option matching the option code.
|
||||
func (o Options) Del(opcode OptionCode) {
|
||||
delete(o, opcode.Code())
|
||||
}
|
||||
|
||||
// Update updates the existing options with the passed option, adding it
|
||||
// at the end if not present already
|
||||
func (o Options) Update(option Option) {
|
||||
o[option.Code.Code()] = option.Value.ToBytes()
|
||||
}
|
||||
|
||||
// ToBytes makes Options usable as an OptionValue as well.
|
||||
//
|
||||
// Used in the case of vendor-specific and relay agent options.
|
||||
func (o Options) ToBytes() []byte {
|
||||
return uio.ToBigEndian(o)
|
||||
}
|
||||
|
||||
// FromBytes parses a sequence of bytes until the end and builds a list of
|
||||
// options from it.
|
||||
//
|
||||
// The sequence should not contain the DHCP magic cookie.
|
||||
//
|
||||
// Returns an error if any invalid option or length is found.
|
||||
func (o Options) FromBytes(data []byte) error {
|
||||
return o.fromBytesCheckEnd(data, false)
|
||||
}
|
||||
|
||||
const (
|
||||
optPad = 0
|
||||
optAgentInfo = 82
|
||||
optEnd = 255
|
||||
)
|
||||
|
||||
// FromBytesCheckEnd parses Options from byte sequences using the
|
||||
// parsing function that is passed in as a paremeter
|
||||
func (o Options) fromBytesCheckEnd(data []byte, checkEndOption bool) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
|
||||
var end bool
|
||||
for buf.Len() >= 1 {
|
||||
// 1 byte: option code
|
||||
// 1 byte: option length n
|
||||
// n bytes: data
|
||||
code := buf.Read8()
|
||||
|
||||
if code == optPad {
|
||||
continue
|
||||
} else if code == optEnd {
|
||||
end = true
|
||||
break
|
||||
}
|
||||
length := int(buf.Read8())
|
||||
|
||||
// N bytes: option data
|
||||
data := buf.Consume(length)
|
||||
if data == nil {
|
||||
return fmt.Errorf("error collecting options: %v", buf.Error())
|
||||
}
|
||||
data = data[:length:length]
|
||||
|
||||
// RFC 2131, Section 4.1 "Options may appear only once, [...].
|
||||
// The client concatenates the values of multiple instances of
|
||||
// the same option into a single parameter list for
|
||||
// configuration."
|
||||
//
|
||||
// See also RFC 3396 for concatenation order and options longer
|
||||
// than 255 bytes.
|
||||
o[code] = append(o[code], data...)
|
||||
}
|
||||
|
||||
// If we never read the End option, the sender of this packet screwed
|
||||
// up.
|
||||
if !end && checkEndOption {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortedKeys returns an ordered slice of option keys from the Options map, for
|
||||
// use in serializing options to binary.
|
||||
func (o Options) sortedKeys() []int {
|
||||
// Send all values for a given key
|
||||
var codes []int
|
||||
var hasOptAgentInfo, hasOptEnd bool
|
||||
for k := range o {
|
||||
// RFC 3046 section 2.1 states that option 82 SHALL come last (ignoring End).
|
||||
if k == optAgentInfo {
|
||||
hasOptAgentInfo = true
|
||||
continue
|
||||
}
|
||||
if k == optEnd {
|
||||
hasOptEnd = true
|
||||
continue
|
||||
}
|
||||
codes = append(codes, int(k))
|
||||
}
|
||||
|
||||
sort.Ints(codes)
|
||||
|
||||
if hasOptAgentInfo {
|
||||
codes = append(codes, optAgentInfo)
|
||||
}
|
||||
if hasOptEnd {
|
||||
codes = append(codes, optEnd)
|
||||
}
|
||||
return codes
|
||||
}
|
||||
|
||||
// Marshal writes options binary representations to b.
|
||||
func (o Options) Marshal(b *uio.Lexer) {
|
||||
for _, c := range o.sortedKeys() {
|
||||
code := uint8(c)
|
||||
// Even if the End option is in there, don't marshal it until
|
||||
// the end.
|
||||
// Don't write padding either, since the options are sorted
|
||||
// it would always be written first which isn't useful
|
||||
if code == optEnd || code == optPad {
|
||||
continue
|
||||
}
|
||||
|
||||
data := o[code]
|
||||
|
||||
// Ensure even 0-length options are written out
|
||||
if len(data) == 0 {
|
||||
b.Write8(code)
|
||||
b.Write8(0)
|
||||
continue
|
||||
}
|
||||
// RFC 3396: If more than 256 bytes of data are given, the
|
||||
// option is simply listed multiple times.
|
||||
for len(data) > 0 {
|
||||
// 1 byte: option code
|
||||
b.Write8(code)
|
||||
|
||||
n := len(data)
|
||||
if n > math.MaxUint8 {
|
||||
n = math.MaxUint8
|
||||
}
|
||||
|
||||
// 1 byte: option length
|
||||
b.Write8(uint8(n))
|
||||
|
||||
// N bytes: option data
|
||||
b.WriteBytes(data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String prints options using DHCP-specified option codes.
|
||||
func (o Options) String() string {
|
||||
return o.ToString(dhcpHumanizer)
|
||||
}
|
||||
|
||||
// Summary prints options in human-readable values.
|
||||
//
|
||||
// Summary uses vendorParser to interpret the OptionVendorSpecificInformation option.
|
||||
func (o Options) Summary(vendorDecoder OptionDecoder) string {
|
||||
return o.ToString(OptionHumanizer{
|
||||
ValueHumanizer: parserFor(vendorDecoder),
|
||||
CodeHumanizer: func(c uint8) OptionCode {
|
||||
return optionCode(c)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// OptionParser gives a human-legible interpretation of data for the given option code.
|
||||
type OptionParser func(code OptionCode, data []byte) fmt.Stringer
|
||||
|
||||
// OptionHumanizer is used to interpret a set of Options for their option code
|
||||
// name and values.
|
||||
//
|
||||
// There should be separate OptionHumanizers for each Option "space": DHCP,
|
||||
// BSDP, Relay Agent Info, and others.
|
||||
type OptionHumanizer struct {
|
||||
ValueHumanizer OptionParser
|
||||
CodeHumanizer func(code uint8) OptionCode
|
||||
}
|
||||
|
||||
// Stringify returns a human-readable interpretation of the option code and its
|
||||
// associated data.
|
||||
func (oh OptionHumanizer) Stringify(code uint8, data []byte) string {
|
||||
c := oh.CodeHumanizer(code)
|
||||
val := oh.ValueHumanizer(c, data)
|
||||
return fmt.Sprintf("%s: %s", c, val)
|
||||
}
|
||||
|
||||
// dhcpHumanizer humanizes the set of DHCP option codes.
|
||||
var dhcpHumanizer = OptionHumanizer{
|
||||
ValueHumanizer: parseOption,
|
||||
CodeHumanizer: func(c uint8) OptionCode {
|
||||
return optionCode(c)
|
||||
},
|
||||
}
|
||||
|
||||
// ToString uses parse to parse options into human-readable values.
|
||||
func (o Options) ToString(humanizer OptionHumanizer) string {
|
||||
var ret string
|
||||
for _, c := range o.sortedKeys() {
|
||||
code := uint8(c)
|
||||
v := o[code]
|
||||
optString := humanizer.Stringify(code, v)
|
||||
// If this option has sub structures, offset them accordingly.
|
||||
if strings.Contains(optString, "\n") {
|
||||
optString = strings.Replace(optString, "\n ", "\n ", -1)
|
||||
}
|
||||
ret += fmt.Sprintf(" %v\n", optString)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseOption(code OptionCode, data []byte) fmt.Stringer {
|
||||
return parserFor(nil)(code, data)
|
||||
}
|
||||
|
||||
func parserFor(vendorParser OptionDecoder) OptionParser {
|
||||
return func(code OptionCode, data []byte) fmt.Stringer {
|
||||
return getOption(code, data, vendorParser)
|
||||
}
|
||||
}
|
||||
|
||||
// OptionDecoder can decode a byte stream into a human-readable option.
|
||||
type OptionDecoder interface {
|
||||
fmt.Stringer
|
||||
FromBytes([]byte) error
|
||||
}
|
||||
|
||||
func getOption(code OptionCode, data []byte, vendorDecoder OptionDecoder) fmt.Stringer {
|
||||
var d OptionDecoder
|
||||
switch code {
|
||||
case OptionRouter, OptionDomainNameServer, OptionNTPServers, OptionServerIdentifier:
|
||||
d = &IPs{}
|
||||
|
||||
case OptionBroadcastAddress, OptionRequestedIPAddress:
|
||||
d = &IP{}
|
||||
|
||||
case OptionClientSystemArchitectureType:
|
||||
d = &iana.Archs{}
|
||||
|
||||
case OptionSubnetMask:
|
||||
d = &IPMask{}
|
||||
|
||||
case OptionDHCPMessageType:
|
||||
var mt MessageType
|
||||
d = &mt
|
||||
|
||||
case OptionParameterRequestList:
|
||||
d = &OptionCodeList{}
|
||||
|
||||
case OptionHostName, OptionDomainName, OptionRootPath,
|
||||
OptionClassIdentifier, OptionTFTPServerName, OptionBootfileName,
|
||||
OptionMessage, OptionReferenceToTZDatabase:
|
||||
var s String
|
||||
d = &s
|
||||
|
||||
case OptionRelayAgentInformation:
|
||||
d = &RelayOptions{}
|
||||
|
||||
case OptionDNSDomainSearchList:
|
||||
d = &rfc1035label.Labels{}
|
||||
|
||||
case OptionIPAddressLeaseTime, OptionRenewTimeValue,
|
||||
OptionRebindingTimeValue, OptionIPv6OnlyPreferred, OptionArpCacheTimeout,
|
||||
OptionTimeOffset:
|
||||
var dur Duration
|
||||
d = &dur
|
||||
|
||||
case OptionMaximumDHCPMessageSize:
|
||||
var u Uint16
|
||||
d = &u
|
||||
|
||||
case OptionUserClassInformation:
|
||||
var s Strings
|
||||
d = &s
|
||||
if s.FromBytes(data) != nil {
|
||||
var s String
|
||||
d = &s
|
||||
}
|
||||
|
||||
case OptionAutoConfigure:
|
||||
var a AutoConfiguration
|
||||
d = &a
|
||||
|
||||
case OptionVendorIdentifyingVendorClass:
|
||||
d = &VIVCIdentifiers{}
|
||||
|
||||
case OptionVendorSpecificInformation:
|
||||
d = vendorDecoder
|
||||
|
||||
case OptionClasslessStaticRoute:
|
||||
d = &Routes{}
|
||||
}
|
||||
if d != nil && d.FromBytes(data) == nil {
|
||||
return d
|
||||
}
|
||||
return OptionGeneric{data}
|
||||
}
|
467
vendor/github.com/insomniacslk/dhcp/dhcpv4/types.go
generated
vendored
Normal file
467
vendor/github.com/insomniacslk/dhcp/dhcpv4/types.go
generated
vendored
Normal file
@ -0,0 +1,467 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// values from http://www.networksorcery.com/enp/protocol/dhcp.htm and
|
||||
// http://www.networksorcery.com/enp/protocol/bootp/options.htm
|
||||
|
||||
// TransactionID represents a 4-byte DHCP transaction ID as defined in RFC 951,
|
||||
// Section 3.
|
||||
//
|
||||
// The TransactionID is used to match DHCP replies to their original request.
|
||||
type TransactionID [4]byte
|
||||
|
||||
// String prints a hex transaction ID.
|
||||
func (xid TransactionID) String() string {
|
||||
return fmt.Sprintf("0x%x", xid[:])
|
||||
}
|
||||
|
||||
// MessageType represents the possible DHCP message types - DISCOVER, OFFER, etc
|
||||
type MessageType byte
|
||||
|
||||
// DHCP message types
|
||||
const (
|
||||
// MessageTypeNone is not a real message type, it is used by certain
|
||||
// functions to signal that no explicit message type is requested
|
||||
MessageTypeNone MessageType = 0
|
||||
MessageTypeDiscover MessageType = 1
|
||||
MessageTypeOffer MessageType = 2
|
||||
MessageTypeRequest MessageType = 3
|
||||
MessageTypeDecline MessageType = 4
|
||||
MessageTypeAck MessageType = 5
|
||||
MessageTypeNak MessageType = 6
|
||||
MessageTypeRelease MessageType = 7
|
||||
MessageTypeInform MessageType = 8
|
||||
)
|
||||
|
||||
// ToBytes returns the serialized version of this option described by RFC 2132,
|
||||
// Section 9.6.
|
||||
func (m MessageType) ToBytes() []byte {
|
||||
return []byte{byte(m)}
|
||||
}
|
||||
|
||||
// String prints a human-readable message type name.
|
||||
func (m MessageType) String() string {
|
||||
if s, ok := messageTypeToString[m]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", byte(m))
|
||||
}
|
||||
|
||||
// FromBytes reads a message type from data as described by RFC 2132, Section
|
||||
// 9.6.
|
||||
func (m *MessageType) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
*m = MessageType(buf.Read8())
|
||||
return buf.FinError()
|
||||
}
|
||||
|
||||
var messageTypeToString = map[MessageType]string{
|
||||
MessageTypeDiscover: "DISCOVER",
|
||||
MessageTypeOffer: "OFFER",
|
||||
MessageTypeRequest: "REQUEST",
|
||||
MessageTypeDecline: "DECLINE",
|
||||
MessageTypeAck: "ACK",
|
||||
MessageTypeNak: "NAK",
|
||||
MessageTypeRelease: "RELEASE",
|
||||
MessageTypeInform: "INFORM",
|
||||
}
|
||||
|
||||
// OpcodeType represents a DHCPv4 opcode.
|
||||
type OpcodeType uint8
|
||||
|
||||
// constants that represent valid values for OpcodeType
|
||||
const (
|
||||
OpcodeBootRequest OpcodeType = 1
|
||||
OpcodeBootReply OpcodeType = 2
|
||||
)
|
||||
|
||||
func (o OpcodeType) String() string {
|
||||
if s, ok := opcodeToString[o]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", uint8(o))
|
||||
}
|
||||
|
||||
var opcodeToString = map[OpcodeType]string{
|
||||
OpcodeBootRequest: "BootRequest",
|
||||
OpcodeBootReply: "BootReply",
|
||||
}
|
||||
|
||||
// OptionCode is a single byte representing the code for a given Option.
|
||||
//
|
||||
// OptionCode is an interface purely to support different stringers on options
|
||||
// with the same Code value, as vendor-specific options use option codes that
|
||||
// have the same value, but mean a different thing.
|
||||
type OptionCode interface {
|
||||
// Code is the 1 byte option code for the wire.
|
||||
Code() uint8
|
||||
|
||||
// String returns the option's name.
|
||||
String() string
|
||||
}
|
||||
|
||||
// optionCode is a DHCP option code.
|
||||
type optionCode uint8
|
||||
|
||||
// Code implements OptionCode.Code.
|
||||
func (o optionCode) Code() uint8 {
|
||||
return uint8(o)
|
||||
}
|
||||
|
||||
// String returns an option name.
|
||||
func (o optionCode) String() string {
|
||||
if s, ok := optionCodeToString[o]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown (%d)", uint8(o))
|
||||
}
|
||||
|
||||
// GenericOptionCode is an unnamed option code.
|
||||
type GenericOptionCode uint8
|
||||
|
||||
// Code implements OptionCode.Code.
|
||||
func (o GenericOptionCode) Code() uint8 {
|
||||
return uint8(o)
|
||||
}
|
||||
|
||||
// String returns the option's name.
|
||||
func (o GenericOptionCode) String() string {
|
||||
return fmt.Sprintf("unknown (%d)", uint8(o))
|
||||
}
|
||||
|
||||
// DHCPv4 Options
|
||||
const (
|
||||
OptionPad optionCode = 0
|
||||
OptionSubnetMask optionCode = 1
|
||||
OptionTimeOffset optionCode = 2
|
||||
OptionRouter optionCode = 3
|
||||
OptionTimeServer optionCode = 4
|
||||
OptionNameServer optionCode = 5
|
||||
OptionDomainNameServer optionCode = 6
|
||||
OptionLogServer optionCode = 7
|
||||
OptionQuoteServer optionCode = 8
|
||||
OptionLPRServer optionCode = 9
|
||||
OptionImpressServer optionCode = 10
|
||||
OptionResourceLocationServer optionCode = 11
|
||||
OptionHostName optionCode = 12
|
||||
OptionBootFileSize optionCode = 13
|
||||
OptionMeritDumpFile optionCode = 14
|
||||
OptionDomainName optionCode = 15
|
||||
OptionSwapServer optionCode = 16
|
||||
OptionRootPath optionCode = 17
|
||||
OptionExtensionsPath optionCode = 18
|
||||
OptionIPForwarding optionCode = 19
|
||||
OptionNonLocalSourceRouting optionCode = 20
|
||||
OptionPolicyFilter optionCode = 21
|
||||
OptionMaximumDatagramAssemblySize optionCode = 22
|
||||
OptionDefaultIPTTL optionCode = 23
|
||||
OptionPathMTUAgingTimeout optionCode = 24
|
||||
OptionPathMTUPlateauTable optionCode = 25
|
||||
OptionInterfaceMTU optionCode = 26
|
||||
OptionAllSubnetsAreLocal optionCode = 27
|
||||
OptionBroadcastAddress optionCode = 28
|
||||
OptionPerformMaskDiscovery optionCode = 29
|
||||
OptionMaskSupplier optionCode = 30
|
||||
OptionPerformRouterDiscovery optionCode = 31
|
||||
OptionRouterSolicitationAddress optionCode = 32
|
||||
OptionStaticRoutingTable optionCode = 33
|
||||
OptionTrailerEncapsulation optionCode = 34
|
||||
OptionArpCacheTimeout optionCode = 35
|
||||
OptionEthernetEncapsulation optionCode = 36
|
||||
OptionDefaulTCPTTL optionCode = 37
|
||||
OptionTCPKeepaliveInterval optionCode = 38
|
||||
OptionTCPKeepaliveGarbage optionCode = 39
|
||||
OptionNetworkInformationServiceDomain optionCode = 40
|
||||
OptionNetworkInformationServers optionCode = 41
|
||||
OptionNTPServers optionCode = 42
|
||||
OptionVendorSpecificInformation optionCode = 43
|
||||
OptionNetBIOSOverTCPIPNameServer optionCode = 44
|
||||
OptionNetBIOSOverTCPIPDatagramDistributionServer optionCode = 45
|
||||
OptionNetBIOSOverTCPIPNodeType optionCode = 46
|
||||
OptionNetBIOSOverTCPIPScope optionCode = 47
|
||||
OptionXWindowSystemFontServer optionCode = 48
|
||||
OptionXWindowSystemDisplayManger optionCode = 49
|
||||
OptionRequestedIPAddress optionCode = 50
|
||||
OptionIPAddressLeaseTime optionCode = 51
|
||||
OptionOptionOverload optionCode = 52
|
||||
OptionDHCPMessageType optionCode = 53
|
||||
OptionServerIdentifier optionCode = 54
|
||||
OptionParameterRequestList optionCode = 55
|
||||
OptionMessage optionCode = 56
|
||||
OptionMaximumDHCPMessageSize optionCode = 57
|
||||
OptionRenewTimeValue optionCode = 58
|
||||
OptionRebindingTimeValue optionCode = 59
|
||||
OptionClassIdentifier optionCode = 60
|
||||
OptionClientIdentifier optionCode = 61
|
||||
OptionNetWareIPDomainName optionCode = 62
|
||||
OptionNetWareIPInformation optionCode = 63
|
||||
OptionNetworkInformationServicePlusDomain optionCode = 64
|
||||
OptionNetworkInformationServicePlusServers optionCode = 65
|
||||
OptionTFTPServerName optionCode = 66
|
||||
OptionBootfileName optionCode = 67
|
||||
OptionMobileIPHomeAgent optionCode = 68
|
||||
OptionSimpleMailTransportProtocolServer optionCode = 69
|
||||
OptionPostOfficeProtocolServer optionCode = 70
|
||||
OptionNetworkNewsTransportProtocolServer optionCode = 71
|
||||
OptionDefaultWorldWideWebServer optionCode = 72
|
||||
OptionDefaultFingerServer optionCode = 73
|
||||
OptionDefaultInternetRelayChatServer optionCode = 74
|
||||
OptionStreetTalkServer optionCode = 75
|
||||
OptionStreetTalkDirectoryAssistanceServer optionCode = 76
|
||||
OptionUserClassInformation optionCode = 77
|
||||
OptionSLPDirectoryAgent optionCode = 78
|
||||
OptionSLPServiceScope optionCode = 79
|
||||
OptionRapidCommit optionCode = 80
|
||||
OptionFQDN optionCode = 81
|
||||
OptionRelayAgentInformation optionCode = 82
|
||||
OptionInternetStorageNameService optionCode = 83
|
||||
// Option 84 returned in RFC 3679
|
||||
OptionNDSServers optionCode = 85
|
||||
OptionNDSTreeName optionCode = 86
|
||||
OptionNDSContext optionCode = 87
|
||||
OptionBCMCSControllerDomainNameList optionCode = 88
|
||||
OptionBCMCSControllerIPv4AddressList optionCode = 89
|
||||
OptionAuthentication optionCode = 90
|
||||
OptionClientLastTransactionTime optionCode = 91
|
||||
OptionAssociatedIP optionCode = 92
|
||||
OptionClientSystemArchitectureType optionCode = 93
|
||||
OptionClientNetworkInterfaceIdentifier optionCode = 94
|
||||
OptionLDAP optionCode = 95
|
||||
// Option 96 returned in RFC 3679
|
||||
OptionClientMachineIdentifier optionCode = 97
|
||||
OptionOpenGroupUserAuthentication optionCode = 98
|
||||
OptionGeoConfCivic optionCode = 99
|
||||
OptionIEEE10031TZString optionCode = 100
|
||||
OptionReferenceToTZDatabase optionCode = 101
|
||||
// Option 108 returned in RFC 8925
|
||||
OptionIPv6OnlyPreferred optionCode = 108
|
||||
// Options 102-111 returned in RFC 3679
|
||||
OptionNetInfoParentServerAddress optionCode = 112
|
||||
OptionNetInfoParentServerTag optionCode = 113
|
||||
OptionURL optionCode = 114
|
||||
// Option 115 returned in RFC 3679
|
||||
OptionAutoConfigure optionCode = 116
|
||||
OptionNameServiceSearch optionCode = 117
|
||||
OptionSubnetSelection optionCode = 118
|
||||
OptionDNSDomainSearchList optionCode = 119
|
||||
OptionSIPServers optionCode = 120
|
||||
OptionClasslessStaticRoute optionCode = 121
|
||||
OptionCCC optionCode = 122
|
||||
OptionGeoConf optionCode = 123
|
||||
OptionVendorIdentifyingVendorClass optionCode = 124
|
||||
OptionVendorIdentifyingVendorSpecific optionCode = 125
|
||||
// Options 126-127 returned in RFC 3679
|
||||
OptionTFTPServerIPAddress optionCode = 128
|
||||
OptionCallServerIPAddress optionCode = 129
|
||||
OptionDiscriminationString optionCode = 130
|
||||
OptionRemoteStatisticsServerIPAddress optionCode = 131
|
||||
Option8021PVLANID optionCode = 132
|
||||
Option8021QL2Priority optionCode = 133
|
||||
OptionDiffservCodePoint optionCode = 134
|
||||
OptionHTTPProxyForPhoneSpecificApplications optionCode = 135
|
||||
OptionPANAAuthenticationAgent optionCode = 136
|
||||
OptionLoSTServer optionCode = 137
|
||||
OptionCAPWAPAccessControllerAddresses optionCode = 138
|
||||
OptionOPTIONIPv4AddressMoS optionCode = 139
|
||||
OptionOPTIONIPv4FQDNMoS optionCode = 140
|
||||
OptionSIPUAConfigurationServiceDomains optionCode = 141
|
||||
OptionOPTIONIPv4AddressANDSF optionCode = 142
|
||||
OptionOPTIONIPv6AddressANDSF optionCode = 143
|
||||
// Options 144-149 returned in RFC 3679
|
||||
OptionTFTPServerAddress optionCode = 150
|
||||
OptionStatusCode optionCode = 151
|
||||
OptionBaseTime optionCode = 152
|
||||
OptionStartTimeOfState optionCode = 153
|
||||
OptionQueryStartTime optionCode = 154
|
||||
OptionQueryEndTime optionCode = 155
|
||||
OptionDHCPState optionCode = 156
|
||||
OptionDataSource optionCode = 157
|
||||
// Options 158-174 returned in RFC 3679
|
||||
OptionEtherboot optionCode = 175
|
||||
OptionIPTelephone optionCode = 176
|
||||
OptionEtherbootPacketCableAndCableHome optionCode = 177
|
||||
// Options 178-207 returned in RFC 3679
|
||||
OptionPXELinuxMagicString optionCode = 208
|
||||
OptionPXELinuxConfigFile optionCode = 209
|
||||
OptionPXELinuxPathPrefix optionCode = 210
|
||||
OptionPXELinuxRebootTime optionCode = 211
|
||||
OptionOPTION6RD optionCode = 212
|
||||
OptionOPTIONv4AccessDomain optionCode = 213
|
||||
// Options 214-219 returned in RFC 3679
|
||||
OptionSubnetAllocation optionCode = 220
|
||||
OptionVirtualSubnetAllocation optionCode = 221
|
||||
// Options 222-223 returned in RFC 3679
|
||||
// Options 224-254 are reserved for private use
|
||||
OptionEnd optionCode = 255
|
||||
)
|
||||
|
||||
var optionCodeToString = map[OptionCode]string{
|
||||
OptionPad: "Pad",
|
||||
OptionSubnetMask: "Subnet Mask",
|
||||
OptionTimeOffset: "Time Offset",
|
||||
OptionRouter: "Router",
|
||||
OptionTimeServer: "Time Server",
|
||||
OptionNameServer: "Name Server",
|
||||
OptionDomainNameServer: "Domain Name Server",
|
||||
OptionLogServer: "Log Server",
|
||||
OptionQuoteServer: "Quote Server",
|
||||
OptionLPRServer: "LPR Server",
|
||||
OptionImpressServer: "Impress Server",
|
||||
OptionResourceLocationServer: "Resource Location Server",
|
||||
OptionHostName: "Host Name",
|
||||
OptionBootFileSize: "Boot File Size",
|
||||
OptionMeritDumpFile: "Merit Dump File",
|
||||
OptionDomainName: "Domain Name",
|
||||
OptionSwapServer: "Swap Server",
|
||||
OptionRootPath: "Root Path",
|
||||
OptionExtensionsPath: "Extensions Path",
|
||||
OptionIPForwarding: "IP Forwarding enable/disable",
|
||||
OptionNonLocalSourceRouting: "Non-local Source Routing enable/disable",
|
||||
OptionPolicyFilter: "Policy Filter",
|
||||
OptionMaximumDatagramAssemblySize: "Maximum Datagram Reassembly Size",
|
||||
OptionDefaultIPTTL: "Default IP Time-to-live",
|
||||
OptionPathMTUAgingTimeout: "Path MTU Aging Timeout",
|
||||
OptionPathMTUPlateauTable: "Path MTU Plateau Table",
|
||||
OptionInterfaceMTU: "Interface MTU",
|
||||
OptionAllSubnetsAreLocal: "All Subnets Are Local",
|
||||
OptionBroadcastAddress: "Broadcast Address",
|
||||
OptionPerformMaskDiscovery: "Perform Mask Discovery",
|
||||
OptionMaskSupplier: "Mask Supplier",
|
||||
OptionPerformRouterDiscovery: "Perform Router Discovery",
|
||||
OptionRouterSolicitationAddress: "Router Solicitation Address",
|
||||
OptionStaticRoutingTable: "Static Routing Table",
|
||||
OptionTrailerEncapsulation: "Trailer Encapsulation",
|
||||
OptionArpCacheTimeout: "ARP Cache Timeout",
|
||||
OptionEthernetEncapsulation: "Ethernet Encapsulation",
|
||||
OptionDefaulTCPTTL: "Default TCP TTL",
|
||||
OptionTCPKeepaliveInterval: "TCP Keepalive Interval",
|
||||
OptionTCPKeepaliveGarbage: "TCP Keepalive Garbage",
|
||||
OptionNetworkInformationServiceDomain: "Network Information Service Domain",
|
||||
OptionNetworkInformationServers: "Network Information Servers",
|
||||
OptionNTPServers: "NTP Servers",
|
||||
OptionVendorSpecificInformation: "Vendor Specific Information",
|
||||
OptionNetBIOSOverTCPIPNameServer: "NetBIOS over TCP/IP Name Server",
|
||||
OptionNetBIOSOverTCPIPDatagramDistributionServer: "NetBIOS over TCP/IP Datagram Distribution Server",
|
||||
OptionNetBIOSOverTCPIPNodeType: "NetBIOS over TCP/IP Node Type",
|
||||
OptionNetBIOSOverTCPIPScope: "NetBIOS over TCP/IP Scope",
|
||||
OptionXWindowSystemFontServer: "X Window System Font Server",
|
||||
OptionXWindowSystemDisplayManger: "X Window System Display Manager",
|
||||
OptionRequestedIPAddress: "Requested IP Address",
|
||||
OptionIPAddressLeaseTime: "IP Addresses Lease Time",
|
||||
OptionOptionOverload: "Option Overload",
|
||||
OptionDHCPMessageType: "DHCP Message Type",
|
||||
OptionServerIdentifier: "Server Identifier",
|
||||
OptionParameterRequestList: "Parameter Request List",
|
||||
OptionMessage: "Message",
|
||||
OptionMaximumDHCPMessageSize: "Maximum DHCP Message Size",
|
||||
OptionRenewTimeValue: "Renew Time Value",
|
||||
OptionRebindingTimeValue: "Rebinding Time Value",
|
||||
OptionClassIdentifier: "Class Identifier",
|
||||
OptionClientIdentifier: "Client identifier",
|
||||
OptionNetWareIPDomainName: "NetWare/IP Domain Name",
|
||||
OptionNetWareIPInformation: "NetWare/IP Information",
|
||||
OptionNetworkInformationServicePlusDomain: "Network Information Service+ Domain",
|
||||
OptionNetworkInformationServicePlusServers: "Network Information Service+ Servers",
|
||||
OptionTFTPServerName: "TFTP Server Name",
|
||||
OptionBootfileName: "Bootfile Name",
|
||||
OptionMobileIPHomeAgent: "Mobile IP Home Agent",
|
||||
OptionSimpleMailTransportProtocolServer: "SMTP Server",
|
||||
OptionPostOfficeProtocolServer: "POP Server",
|
||||
OptionNetworkNewsTransportProtocolServer: "NNTP Server",
|
||||
OptionDefaultWorldWideWebServer: "Default WWW Server",
|
||||
OptionDefaultFingerServer: "Default Finger Server",
|
||||
OptionDefaultInternetRelayChatServer: "Default IRC Server",
|
||||
OptionStreetTalkServer: "StreetTalk Server",
|
||||
OptionStreetTalkDirectoryAssistanceServer: "StreetTalk Directory Assistance Server",
|
||||
OptionUserClassInformation: "User Class Information",
|
||||
OptionSLPDirectoryAgent: "SLP DIrectory Agent",
|
||||
OptionSLPServiceScope: "SLP Service Scope",
|
||||
OptionRapidCommit: "Rapid Commit",
|
||||
OptionFQDN: "FQDN",
|
||||
OptionRelayAgentInformation: "Relay Agent Information",
|
||||
OptionInternetStorageNameService: "Internet Storage Name Service",
|
||||
// Option 84 returned in RFC 3679
|
||||
OptionNDSServers: "NDS Servers",
|
||||
OptionNDSTreeName: "NDS Tree Name",
|
||||
OptionNDSContext: "NDS Context",
|
||||
OptionBCMCSControllerDomainNameList: "BCMCS Controller Domain Name List",
|
||||
OptionBCMCSControllerIPv4AddressList: "BCMCS Controller IPv4 Address List",
|
||||
OptionAuthentication: "Authentication",
|
||||
OptionClientLastTransactionTime: "Client Last Transaction Time",
|
||||
OptionAssociatedIP: "Associated IP",
|
||||
OptionClientSystemArchitectureType: "Client System Architecture Type",
|
||||
OptionClientNetworkInterfaceIdentifier: "Client Network Interface Identifier",
|
||||
OptionLDAP: "LDAP",
|
||||
// Option 96 returned in RFC 3679
|
||||
OptionClientMachineIdentifier: "Client Machine Identifier",
|
||||
OptionOpenGroupUserAuthentication: "OpenGroup's User Authentication",
|
||||
OptionGeoConfCivic: "GEOCONF_CIVIC",
|
||||
OptionIEEE10031TZString: "IEEE 1003.1 TZ String",
|
||||
OptionReferenceToTZDatabase: "Reference to the TZ Database",
|
||||
// Option 108 returned in RFC 8925
|
||||
OptionIPv6OnlyPreferred: "IPv6-Only Preferred",
|
||||
// Options 102-111 returned in RFC 3679
|
||||
OptionNetInfoParentServerAddress: "NetInfo Parent Server Address",
|
||||
OptionNetInfoParentServerTag: "NetInfo Parent Server Tag",
|
||||
OptionURL: "URL",
|
||||
// Option 115 returned in RFC 3679
|
||||
OptionAutoConfigure: "Auto-Configure",
|
||||
OptionNameServiceSearch: "Name Service Search",
|
||||
OptionSubnetSelection: "Subnet Selection",
|
||||
OptionDNSDomainSearchList: "DNS Domain Search List",
|
||||
OptionSIPServers: "SIP Servers",
|
||||
OptionClasslessStaticRoute: "Classless Static Route",
|
||||
OptionCCC: "CCC, CableLabs Client Configuration",
|
||||
OptionGeoConf: "GeoConf",
|
||||
OptionVendorIdentifyingVendorClass: "Vendor-Identifying Vendor Class",
|
||||
OptionVendorIdentifyingVendorSpecific: "Vendor-Identifying Vendor-Specific",
|
||||
// Options 126-127 returned in RFC 3679
|
||||
OptionTFTPServerIPAddress: "TFTP Server IP Address",
|
||||
OptionCallServerIPAddress: "Call Server IP Address",
|
||||
OptionDiscriminationString: "Discrimination String",
|
||||
OptionRemoteStatisticsServerIPAddress: "RemoteStatistics Server IP Address",
|
||||
Option8021PVLANID: "802.1P VLAN ID",
|
||||
Option8021QL2Priority: "802.1Q L2 Priority",
|
||||
OptionDiffservCodePoint: "Diffserv Code Point",
|
||||
OptionHTTPProxyForPhoneSpecificApplications: "HTTP Proxy for phone-specific applications",
|
||||
OptionPANAAuthenticationAgent: "PANA Authentication Agent",
|
||||
OptionLoSTServer: "LoST Server",
|
||||
OptionCAPWAPAccessControllerAddresses: "CAPWAP Access Controller Addresses",
|
||||
OptionOPTIONIPv4AddressMoS: "OPTION-IPv4_Address-MoS",
|
||||
OptionOPTIONIPv4FQDNMoS: "OPTION-IPv4_FQDN-MoS",
|
||||
OptionSIPUAConfigurationServiceDomains: "SIP UA Configuration Service Domains",
|
||||
OptionOPTIONIPv4AddressANDSF: "OPTION-IPv4_Address-ANDSF",
|
||||
OptionOPTIONIPv6AddressANDSF: "OPTION-IPv6_Address-ANDSF",
|
||||
// Options 144-149 returned in RFC 3679
|
||||
OptionTFTPServerAddress: "TFTP Server Address",
|
||||
OptionStatusCode: "Status Code",
|
||||
OptionBaseTime: "Base Time",
|
||||
OptionStartTimeOfState: "Start Time of State",
|
||||
OptionQueryStartTime: "Query Start Time",
|
||||
OptionQueryEndTime: "Query End Time",
|
||||
OptionDHCPState: "DHCP Staet",
|
||||
OptionDataSource: "Data Source",
|
||||
// Options 158-174 returned in RFC 3679
|
||||
OptionEtherboot: "Etherboot",
|
||||
OptionIPTelephone: "IP Telephone",
|
||||
OptionEtherbootPacketCableAndCableHome: "Etherboot / PacketCable and CableHome",
|
||||
// Options 178-207 returned in RFC 3679
|
||||
OptionPXELinuxMagicString: "PXELinux Magic String",
|
||||
OptionPXELinuxConfigFile: "PXELinux Config File",
|
||||
OptionPXELinuxPathPrefix: "PXELinux Path Prefix",
|
||||
OptionPXELinuxRebootTime: "PXELinux Reboot Time",
|
||||
OptionOPTION6RD: "OPTION_6RD",
|
||||
OptionOPTIONv4AccessDomain: "OPTION_V4_ACCESS_DOMAIN",
|
||||
// Options 214-219 returned in RFC 3679
|
||||
OptionSubnetAllocation: "Subnet Allocation",
|
||||
OptionVirtualSubnetAllocation: "Virtual Subnet Selection",
|
||||
// Options 222-223 returned in RFC 3679
|
||||
// Options 224-254 are reserved for private use
|
||||
|
||||
OptionEnd: "End",
|
||||
}
|
148
vendor/github.com/insomniacslk/dhcp/iana/archtype.go
generated
vendored
Normal file
148
vendor/github.com/insomniacslk/dhcp/iana/archtype.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
package iana
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/u-root/uio/uio"
|
||||
)
|
||||
|
||||
// Arch encodes an architecture type per RFC 4578, Section 2.1.
|
||||
type Arch uint16
|
||||
|
||||
// See RFC 4578, 5970, and http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#processor-architecture
|
||||
const (
|
||||
INTEL_X86PC Arch = 0
|
||||
NEC_PC98 Arch = 1
|
||||
EFI_ITANIUM Arch = 2
|
||||
DEC_ALPHA Arch = 3
|
||||
ARC_X86 Arch = 4
|
||||
INTEL_LEAN_CLIENT Arch = 5
|
||||
EFI_IA32 Arch = 6
|
||||
EFI_X86_64 Arch = 7
|
||||
EFI_XSCALE Arch = 8
|
||||
EFI_BC Arch = 9
|
||||
EFI_ARM32 Arch = 10
|
||||
EFI_ARM64 Arch = 11
|
||||
PPC_OPEN_FIRMWARE Arch = 12
|
||||
PPC_EPAPR Arch = 13
|
||||
PPC_OPAL Arch = 14
|
||||
EFI_X86_HTTP Arch = 15
|
||||
EFI_X86_64_HTTP Arch = 16
|
||||
EFI_BC_HTTP Arch = 17
|
||||
EFI_ARM32_HTTP Arch = 18
|
||||
EFI_ARM64_HTTP Arch = 19
|
||||
INTEL_X86PC_HTTP Arch = 20
|
||||
UBOOT_ARM32 Arch = 21
|
||||
UBOOT_ARM64 Arch = 22
|
||||
UBOOT_ARM32_HTTP Arch = 23
|
||||
UBOOT_ARM64_HTTP Arch = 24
|
||||
EFI_RISCV32 Arch = 25
|
||||
EFI_RISCV32_HTTP Arch = 26
|
||||
EFI_RISCV64 Arch = 27
|
||||
EFI_RISCV64_HTTP Arch = 28
|
||||
EFI_RISCV128 Arch = 29
|
||||
EFI_RISCV128_HTTP Arch = 30
|
||||
S390_BASIC Arch = 31
|
||||
S390_EXTENDED Arch = 32
|
||||
EFI_MIPS32 Arch = 33
|
||||
EFI_MIPS64 Arch = 34
|
||||
EFI_SUNWAY32 Arch = 35
|
||||
EFI_SUNWAY64 Arch = 36
|
||||
)
|
||||
|
||||
// archTypeToStringMap maps an Arch to a mnemonic name
|
||||
var archTypeToStringMap = map[Arch]string{
|
||||
INTEL_X86PC: "Intel x86PC",
|
||||
NEC_PC98: "NEC/PC98",
|
||||
EFI_ITANIUM: "EFI Itanium",
|
||||
DEC_ALPHA: "DEC Alpha",
|
||||
ARC_X86: "Arc x86",
|
||||
INTEL_LEAN_CLIENT: "Intel Lean Client",
|
||||
EFI_IA32: "EFI IA32",
|
||||
EFI_XSCALE: "EFI Xscale",
|
||||
EFI_X86_64: "EFI x86-64",
|
||||
EFI_BC: "EFI BC",
|
||||
EFI_ARM32: "EFI ARM32",
|
||||
EFI_ARM64: "EFI ARM64",
|
||||
PPC_OPEN_FIRMWARE: "PowerPC Open Firmware",
|
||||
PPC_EPAPR: "PowerPC ePAPR",
|
||||
PPC_OPAL: "POWER OPAL v3",
|
||||
EFI_X86_HTTP: "EFI x86 boot from HTTP",
|
||||
EFI_X86_64_HTTP: "EFI x86-64 boot from HTTP",
|
||||
EFI_BC_HTTP: "EFI BC boot from HTTP",
|
||||
EFI_ARM32_HTTP: "EFI ARM32 boot from HTTP",
|
||||
EFI_ARM64_HTTP: "EFI ARM64 boot from HTTP",
|
||||
INTEL_X86PC_HTTP: "Intel x86PC boot from HTTP",
|
||||
UBOOT_ARM32: "U-Boot ARM32",
|
||||
UBOOT_ARM64: "U-Boot ARM64",
|
||||
UBOOT_ARM32_HTTP: "U-boot ARM32 boot from HTTP",
|
||||
UBOOT_ARM64_HTTP: "U-Boot ARM64 boot from HTTP",
|
||||
EFI_RISCV32: "EFI RISC-V 32-bit",
|
||||
EFI_RISCV32_HTTP: "EFI RISC-V 32-bit boot from HTTP",
|
||||
EFI_RISCV64: "EFI RISC-V 64-bit",
|
||||
EFI_RISCV64_HTTP: "EFI RISC-V 64-bit boot from HTTP",
|
||||
EFI_RISCV128: "EFI RISC-V 128-bit",
|
||||
EFI_RISCV128_HTTP: "EFI RISC-V 128-bit boot from HTTP",
|
||||
S390_BASIC: "s390 Basic",
|
||||
S390_EXTENDED: "s390 Extended",
|
||||
EFI_MIPS32: "EFI MIPS32",
|
||||
EFI_MIPS64: "EFI MIPS64",
|
||||
EFI_SUNWAY32: "EFI Sunway 32-bit",
|
||||
EFI_SUNWAY64: "EFI Sunway 64-bit",
|
||||
}
|
||||
|
||||
// String returns a mnemonic name for a given architecture type.
|
||||
func (a Arch) String() string {
|
||||
if at := archTypeToStringMap[a]; at != "" {
|
||||
return at
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// Archs represents multiple Arch values.
|
||||
type Archs []Arch
|
||||
|
||||
// Contains returns whether b is one of the Archs in a.
|
||||
func (a Archs) Contains(b Arch) bool {
|
||||
for _, t := range a {
|
||||
if t == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ToBytes returns the serialized option defined by RFC 4578 (DHCPv4) and RFC
|
||||
// 5970 (DHCPv6) as the Client System Architecture Option.
|
||||
func (a Archs) ToBytes() []byte {
|
||||
buf := uio.NewBigEndianBuffer(nil)
|
||||
for _, at := range a {
|
||||
buf.Write16(uint16(at))
|
||||
}
|
||||
return buf.Data()
|
||||
}
|
||||
|
||||
// String returns the list of archs in a human-readable manner.
|
||||
func (a Archs) String() string {
|
||||
s := make([]string, 0, len(a))
|
||||
for _, arch := range a {
|
||||
s = append(s, arch.String())
|
||||
}
|
||||
return strings.Join(s, ", ")
|
||||
}
|
||||
|
||||
// FromBytes parses a DHCP list of architecture types as defined by RFC 4578
|
||||
// and RFC 5970.
|
||||
func (a *Archs) FromBytes(data []byte) error {
|
||||
buf := uio.NewBigEndianBuffer(data)
|
||||
if buf.Len() == 0 {
|
||||
return fmt.Errorf("must have at least one archtype if option is present")
|
||||
}
|
||||
|
||||
*a = make([]Arch, 0, buf.Len()/2)
|
||||
for buf.Has(2) {
|
||||
*a = append(*a, Arch(buf.Read16()))
|
||||
}
|
||||
return buf.FinError()
|
||||
}
|
25
vendor/github.com/insomniacslk/dhcp/iana/entid.go
generated
vendored
Normal file
25
vendor/github.com/insomniacslk/dhcp/iana/entid.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package iana
|
||||
|
||||
// EnterpriseID represents the Enterprise IDs as set by IANA
|
||||
type EnterpriseID int
|
||||
|
||||
// See https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers for values
|
||||
const (
|
||||
EnterpriseIDCiscoSystems EnterpriseID = 9
|
||||
EnterpriseIDCienaCorporation EnterpriseID = 1271
|
||||
EnterpriseIDMellanoxTechnologiesLTD EnterpriseID = 33049
|
||||
)
|
||||
|
||||
var enterpriseIDToStringMap = map[EnterpriseID]string{
|
||||
EnterpriseIDCiscoSystems: "Cisco Systems",
|
||||
EnterpriseIDCienaCorporation: "Ciena Corporation",
|
||||
EnterpriseIDMellanoxTechnologiesLTD: "Mellanox Technologies LTD",
|
||||
}
|
||||
|
||||
// String returns the vendor name for a given Enterprise ID
|
||||
func (e EnterpriseID) String() string {
|
||||
if vendor := enterpriseIDToStringMap[e]; vendor != "" {
|
||||
return vendor
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
91
vendor/github.com/insomniacslk/dhcp/iana/hwtypes.go
generated
vendored
Normal file
91
vendor/github.com/insomniacslk/dhcp/iana/hwtypes.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
package iana
|
||||
|
||||
// HWType is a hardware type as per RFC 2132 and defined by the IANA.
|
||||
type HWType uint16
|
||||
|
||||
// See IANA for values.
|
||||
const (
|
||||
_ HWType = iota // skip 0
|
||||
HWTypeEthernet
|
||||
HWTypeExperimentalEthernet
|
||||
HWTypeAmateurRadioAX25
|
||||
HWTypeProteonTokenRing
|
||||
HWTypeChaos
|
||||
HWTypeIEEE802
|
||||
HWTypeARCNET
|
||||
HWTypeHyperchannel
|
||||
HWTypeLanstar
|
||||
HWTypeAutonet
|
||||
HWTypeLocalTalk
|
||||
HWTypeLocalNet
|
||||
HWTypeUltraLink
|
||||
HWTypeSMDS
|
||||
HWTypeFrameRelay
|
||||
HWTypeATM
|
||||
HWTypeHDLC
|
||||
HWTypeFibreChannel
|
||||
HWTypeATM2
|
||||
HWTypeSerialLine
|
||||
HWTypeATM3
|
||||
HWTypeMILSTD188220
|
||||
HWTypeMetricom
|
||||
HWTypeIEEE1394
|
||||
HWTypeMAPOS
|
||||
HWTypeTwinaxial
|
||||
HWTypeEUI64
|
||||
HWTypeHIPARP
|
||||
HWTypeISO7816
|
||||
HWTypeARPSec
|
||||
HWTypeIPsec
|
||||
HWTypeInfiniband
|
||||
HWTypeCAI
|
||||
HWTypeWiegandInterface
|
||||
HWTypePureIP
|
||||
)
|
||||
|
||||
var hwTypeToString = map[HWType]string{
|
||||
HWTypeEthernet: "Ethernet",
|
||||
HWTypeExperimentalEthernet: "Experimental Ethernet",
|
||||
HWTypeAmateurRadioAX25: "Amateur Radio AX.25",
|
||||
HWTypeProteonTokenRing: "Proteon ProNET Token Ring",
|
||||
HWTypeChaos: "Chaos",
|
||||
HWTypeIEEE802: "IEEE 802",
|
||||
HWTypeARCNET: "ARCNET",
|
||||
HWTypeHyperchannel: "Hyperchannel",
|
||||
HWTypeLanstar: "Lanstar",
|
||||
HWTypeAutonet: "Autonet Short Address",
|
||||
HWTypeLocalTalk: "LocalTalk",
|
||||
HWTypeLocalNet: "LocalNet",
|
||||
HWTypeUltraLink: "Ultra link",
|
||||
HWTypeSMDS: "SMDS",
|
||||
HWTypeFrameRelay: "Frame Relay",
|
||||
HWTypeATM: "ATM",
|
||||
HWTypeHDLC: "HDLC",
|
||||
HWTypeFibreChannel: "Fibre Channel",
|
||||
HWTypeATM2: "ATM 2",
|
||||
HWTypeSerialLine: "Serial Line",
|
||||
HWTypeATM3: "ATM 3",
|
||||
HWTypeMILSTD188220: "MIL-STD-188-220",
|
||||
HWTypeMetricom: "Metricom",
|
||||
HWTypeIEEE1394: "IEEE 1394.1995",
|
||||
HWTypeMAPOS: "MAPOS",
|
||||
HWTypeTwinaxial: "Twinaxial",
|
||||
HWTypeEUI64: "EUI-64",
|
||||
HWTypeHIPARP: "HIPARP",
|
||||
HWTypeISO7816: "IP and ARP over ISO 7816-3",
|
||||
HWTypeARPSec: "ARPSec",
|
||||
HWTypeIPsec: "IPsec tunnel",
|
||||
HWTypeInfiniband: "Infiniband",
|
||||
HWTypeCAI: "CAI, TIA-102 Project 125 Common Air Interface",
|
||||
HWTypeWiegandInterface: "Wiegand Interface",
|
||||
HWTypePureIP: "Pure IP",
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (h HWType) String() string {
|
||||
hwtype := hwTypeToString[h]
|
||||
if hwtype == "" {
|
||||
hwtype = "unknown"
|
||||
}
|
||||
return hwtype
|
||||
}
|
2
vendor/github.com/insomniacslk/dhcp/iana/iana.go
generated
vendored
Normal file
2
vendor/github.com/insomniacslk/dhcp/iana/iana.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package iana contains constants defined by IANA.
|
||||
package iana
|
77
vendor/github.com/insomniacslk/dhcp/iana/statuscodes.go
generated
vendored
Normal file
77
vendor/github.com/insomniacslk/dhcp/iana/statuscodes.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package iana
|
||||
|
||||
// StatusCode represents a IANA status code for DHCPv6
|
||||
//
|
||||
// IANA Status Codes for DHCPv6
|
||||
// https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
|
||||
type StatusCode uint16
|
||||
|
||||
// IANA status codes
|
||||
const (
|
||||
// RFC 3315 par. 24..4
|
||||
StatusSuccess StatusCode = 0
|
||||
StatusUnspecFail StatusCode = 1
|
||||
StatusNoAddrsAvail StatusCode = 2
|
||||
StatusNoBinding StatusCode = 3
|
||||
StatusNotOnLink StatusCode = 4
|
||||
StatusUseMulticast StatusCode = 5
|
||||
StatusNoPrefixAvail StatusCode = 6
|
||||
// RFC 5007
|
||||
StatusUnknownQueryType StatusCode = 7
|
||||
StatusMalformedQuery StatusCode = 8
|
||||
StatusNotConfigured StatusCode = 9
|
||||
StatusNotAllowed StatusCode = 10
|
||||
// RFC 5460
|
||||
StatusQueryTerminated StatusCode = 11
|
||||
// RFC 7653
|
||||
StatusDataMissing StatusCode = 12
|
||||
StatusCatchUpComplete StatusCode = 13
|
||||
StatusNotSupported StatusCode = 14
|
||||
StatusTLSConnectionRefused StatusCode = 15
|
||||
// RFC 8156
|
||||
StatusAddressInUse StatusCode = 16
|
||||
StatusConfigurationConflict StatusCode = 17
|
||||
StatusMissingBindingInformation StatusCode = 18
|
||||
StatusOutdatedBindingInformation StatusCode = 19
|
||||
StatusServerShuttingDown StatusCode = 20
|
||||
StatusDNSUpdateNotSupported StatusCode = 21
|
||||
StatusExcessiveTimeSkew StatusCode = 22
|
||||
)
|
||||
|
||||
// String returns a mnemonic name for a given status code
|
||||
func (s StatusCode) String() string {
|
||||
if sc := statusCodeToStringMap[s]; sc != "" {
|
||||
return sc
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
var statusCodeToStringMap = map[StatusCode]string{
|
||||
StatusSuccess: "Success",
|
||||
StatusUnspecFail: "UnspecFail",
|
||||
StatusNoAddrsAvail: "NoAddrsAvail",
|
||||
StatusNoBinding: "NoBinding",
|
||||
StatusNotOnLink: "NotOnLink",
|
||||
StatusUseMulticast: "UseMulticast",
|
||||
StatusNoPrefixAvail: "NoPrefixAvail",
|
||||
// RFC 5007
|
||||
StatusUnknownQueryType: "UnknownQueryType",
|
||||
StatusMalformedQuery: "MalformedQuery",
|
||||
StatusNotConfigured: "NotConfigured",
|
||||
StatusNotAllowed: "NotAllowed",
|
||||
// RFC 5460
|
||||
StatusQueryTerminated: "QueryTerminated",
|
||||
// RFC 7653
|
||||
StatusDataMissing: "DataMissing",
|
||||
StatusCatchUpComplete: "CatchUpComplete",
|
||||
StatusNotSupported: "NotSupported",
|
||||
StatusTLSConnectionRefused: "TLSConnectionRefused",
|
||||
// RFC 8156
|
||||
StatusAddressInUse: "AddressInUse",
|
||||
StatusConfigurationConflict: "ConfigurationConflict",
|
||||
StatusMissingBindingInformation: "MissingBindingInformation",
|
||||
StatusOutdatedBindingInformation: "OutdatedBindingInformation",
|
||||
StatusServerShuttingDown: "ServerShuttingDown",
|
||||
StatusDNSUpdateNotSupported: "DNSUpdateNotSupported",
|
||||
StatusExcessiveTimeSkew: "ExcessiveTimeSkew",
|
||||
}
|
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_bsd.go
generated
vendored
Normal file
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_bsd.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// +build aix freebsd openbsd netbsd dragonfly
|
||||
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// BindToInterface emulates linux's SO_BINDTODEVICE option for a socket by using
|
||||
// IP_RECVIF.
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_RECVIF, iface.Index)
|
||||
}
|
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_darwin.go
generated
vendored
Normal file
19
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_darwin.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// +build darwin
|
||||
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// BindToInterface emulates linux's SO_BINDTODEVICE option for a socket by using
|
||||
// IP_BOUND_IF.
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
iface, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index)
|
||||
}
|
9
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_linux.go
generated
vendored
Normal file
9
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_linux.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// +build linux
|
||||
|
||||
package interfaces
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
return unix.BindToDevice(fd, ifname)
|
||||
}
|
8
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_windows.go
generated
vendored
Normal file
8
vendor/github.com/insomniacslk/dhcp/interfaces/bindtodevice_windows.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package interfaces
|
||||
|
||||
import "errors"
|
||||
|
||||
// BindToInterface fails on Windows.
|
||||
func BindToInterface(fd int, ifname string) error {
|
||||
return errors.New("not implemented on Windows")
|
||||
}
|
41
vendor/github.com/insomniacslk/dhcp/interfaces/interfaces.go
generated
vendored
Normal file
41
vendor/github.com/insomniacslk/dhcp/interfaces/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package interfaces
|
||||
|
||||
import "net"
|
||||
|
||||
// InterfaceMatcher is a function type used to match the interfaces we want. See
|
||||
// GetInterfacesFunc below for usage.
|
||||
type InterfaceMatcher func(net.Interface) bool
|
||||
|
||||
// interfaceGetter is used for testing purposes
|
||||
var interfaceGetter = net.Interfaces
|
||||
|
||||
// GetInterfacesFunc loops through the available network interfaces, and returns
|
||||
// a list of interfaces for which the passed InterfaceMatcher function returns
|
||||
// true.
|
||||
func GetInterfacesFunc(matcher InterfaceMatcher) ([]net.Interface, error) {
|
||||
ifaces, err := interfaceGetter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]net.Interface, 0)
|
||||
for _, iface := range ifaces {
|
||||
if matcher(iface) {
|
||||
ret = append(ret, iface)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetLoopbackInterfaces returns a list of loopback interfaces.
|
||||
func GetLoopbackInterfaces() ([]net.Interface, error) {
|
||||
return GetInterfacesFunc(func(iface net.Interface) bool {
|
||||
return iface.Flags&net.FlagLoopback != 0
|
||||
})
|
||||
}
|
||||
|
||||
// GetNonLoopbackInterfaces returns a list of non-loopback interfaces.
|
||||
func GetNonLoopbackInterfaces() ([]net.Interface, error) {
|
||||
return GetInterfacesFunc(func(iface net.Interface) bool {
|
||||
return iface.Flags&net.FlagLoopback == 0
|
||||
})
|
||||
}
|
173
vendor/github.com/insomniacslk/dhcp/rfc1035label/label.go
generated
vendored
Normal file
173
vendor/github.com/insomniacslk/dhcp/rfc1035label/label.go
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
package rfc1035label
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Labels represents RFC1035 labels
|
||||
//
|
||||
// This implements RFC 1035 labels, including compression.
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.1.4
|
||||
type Labels struct {
|
||||
// original contains the original bytes if the object was parsed from a byte
|
||||
// sequence, or nil otherwise. The `original` field is necessary to deal
|
||||
// with compressed labels. If the labels are further modified, the original
|
||||
// content is invalidated and no compression will be used.
|
||||
original []byte
|
||||
// Labels contains the parsed labels. A change here invalidates the
|
||||
// `original` object.
|
||||
Labels []string
|
||||
}
|
||||
|
||||
// same compares two string arrays
|
||||
func same(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String prints labels.
|
||||
func (l *Labels) String() string {
|
||||
return fmt.Sprintf("%v", l.Labels)
|
||||
}
|
||||
|
||||
// ToBytes returns a byte sequence representing the labels. If the original
|
||||
// sequence is modified, the labels are parsed again, otherwise the original
|
||||
// byte sequence is returned.
|
||||
func (l *Labels) ToBytes() []byte {
|
||||
// if the original byte sequence has been modified, invalidate it and
|
||||
// serialize again.
|
||||
// NOTE: this function is not thread-safe. If multiple threads modify
|
||||
// the `Labels` field, the result may be wrong.
|
||||
originalLabels, err := labelsFromBytes(l.original)
|
||||
// if the original object has not been modified, or we cannot parse it,
|
||||
// return the original bytes.
|
||||
if err != nil || (l.original != nil && same(originalLabels, l.Labels)) {
|
||||
return l.original
|
||||
}
|
||||
return labelsToBytes(l.Labels)
|
||||
}
|
||||
|
||||
// Length returns the length in bytes of the serialized labels
|
||||
func (l *Labels) Length() int {
|
||||
return len(l.ToBytes())
|
||||
}
|
||||
|
||||
// NewLabels returns an initialized Labels object.
|
||||
func NewLabels() *Labels {
|
||||
return &Labels{
|
||||
Labels: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// FromBytes reads labels from a bytes stream according to RFC 1035.
|
||||
func (l *Labels) FromBytes(data []byte) error {
|
||||
labs, err := labelsFromBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.original = data
|
||||
l.Labels = labs
|
||||
return nil
|
||||
}
|
||||
|
||||
// FromBytes returns a Labels object from the given byte sequence, or an error if
|
||||
// any.
|
||||
func FromBytes(data []byte) (*Labels, error) {
|
||||
var l Labels
|
||||
if err := l.FromBytes(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
// ErrBufferTooShort is returned when the label cannot be parsed due to a wrong
|
||||
// length or missing bytes.
|
||||
var ErrBufferTooShort = errors.New("rfc1035label: buffer too short")
|
||||
|
||||
// fromBytes decodes a serialized stream and returns a list of labels
|
||||
func labelsFromBytes(buf []byte) ([]string, error) {
|
||||
var (
|
||||
labels = make([]string, 0)
|
||||
pos, oldPos int
|
||||
label string
|
||||
handlingPointer bool
|
||||
)
|
||||
|
||||
for {
|
||||
if pos >= len(buf) {
|
||||
// interpret label without trailing zero-length byte as a partial
|
||||
// domain name field as per RFC 4704 Section 4.2
|
||||
if label != "" {
|
||||
labels = append(labels, label)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
length := int(buf[pos])
|
||||
pos++
|
||||
var chunk string
|
||||
if length == 0 {
|
||||
labels = append(labels, label)
|
||||
label = ""
|
||||
if handlingPointer {
|
||||
pos = oldPos
|
||||
handlingPointer = false
|
||||
}
|
||||
} else if length&0xc0 == 0xc0 {
|
||||
// compression pointer
|
||||
if handlingPointer {
|
||||
return nil, errors.New("rfc1035label: cannot handle nested pointers")
|
||||
}
|
||||
handlingPointer = true
|
||||
if pos+1 > len(buf) {
|
||||
return nil, errors.New("rfc1035label: pointer buffer too short")
|
||||
}
|
||||
off := int(buf[pos-1]&^0xc0)<<8 + int(buf[pos])
|
||||
oldPos = pos + 1
|
||||
pos = off
|
||||
} else {
|
||||
if pos+length > len(buf) {
|
||||
return nil, ErrBufferTooShort
|
||||
}
|
||||
chunk = string(buf[pos : pos+length])
|
||||
if label != "" {
|
||||
label += "."
|
||||
}
|
||||
label += chunk
|
||||
pos += length
|
||||
}
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
// labelToBytes encodes a label and returns a serialized stream of bytes
|
||||
func labelToBytes(label string) []byte {
|
||||
var encodedLabel []byte
|
||||
if len(label) == 0 {
|
||||
return []byte{0}
|
||||
}
|
||||
for _, part := range strings.Split(label, ".") {
|
||||
encodedLabel = append(encodedLabel, byte(len(part)))
|
||||
encodedLabel = append(encodedLabel, []byte(part)...)
|
||||
}
|
||||
return append(encodedLabel, 0)
|
||||
}
|
||||
|
||||
// labelsToBytes encodes a list of labels and returns a serialized stream of
|
||||
// bytes
|
||||
func labelsToBytes(labels []string) []byte {
|
||||
var encodedLabels []byte
|
||||
for _, label := range labels {
|
||||
encodedLabels = append(encodedLabels, labelToBytes(label)...)
|
||||
}
|
||||
return encodedLabels
|
||||
}
|
Reference in New Issue
Block a user