working on adding lease handouts
This commit is contained in:
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"options.go",
|
||||
"packet.go",
|
||||
"read_options.go",
|
||||
],
|
||||
importpath = "git.wntrmute.dev/kyle/kdhcp/dhcp",
|
||||
visibility = ["//visibility:public"],
|
||||
|
||||
@@ -1,57 +1,72 @@
|
||||
package dhcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type OptionTag uint8
|
||||
|
||||
func (opt OptionTag) String() string {
|
||||
s, ok := optionStrings[opt]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("no string for option %d", opt))
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type Option func(req *BootRequest, r io.Reader) error
|
||||
|
||||
const (
|
||||
OptionTagPadding OptionTag = 0
|
||||
OptionTagSubnetMask OptionTag = 1
|
||||
OptionTagTimeOffset OptionTag = 2
|
||||
OptionTagRouter OptionTag = 3
|
||||
OptionTagDomainNameServer OptionTag = 6
|
||||
OptionTagHostName OptionTag = 12
|
||||
OptionTagDomainName OptionTag = 15
|
||||
OptionTagInterfaceMTU OptionTag = 26
|
||||
OptionTagBroadcastAddress OptionTag = 28
|
||||
OptionTagNTPServers OptionTag = 42
|
||||
OptionTagNBNS OptionTag = 44
|
||||
OptionTagNBScope OptionTag = 47
|
||||
OptionTagMessageType OptionTag = 53
|
||||
OptionTagParameterRequestList OptionTag = 55
|
||||
OptionTagDomainSearch OptionTag = 119
|
||||
OptionTagClasslessStaticRoute OptionTag = 121
|
||||
OptionTagEnd OptionTag = 255
|
||||
)
|
||||
|
||||
var optionRegistry = map[OptionTag]Option{
|
||||
OptionTagPadding: OptionPad,
|
||||
OptionTagHostName: OptionHostName,
|
||||
OptionTagMessageType: OptionMessageType,
|
||||
OptionTagParameterRequestList: OptionParameterRequestList,
|
||||
OptionTagEnd: OptionEnd,
|
||||
var optionStrings = map[OptionTag]string{
|
||||
OptionTagPadding: "pad",
|
||||
OptionTagSubnetMask: "subnet mask",
|
||||
OptionTagTimeOffset: "time offset from UTC",
|
||||
OptionTagRouter: "routers",
|
||||
OptionTagDomainNameServer: "domain name servers",
|
||||
OptionTagHostName: "host name",
|
||||
OptionTagDomainName: "domain name",
|
||||
OptionTagInterfaceMTU: "interface MTU",
|
||||
OptionTagBroadcastAddress: "broadcast address",
|
||||
OptionTagNTPServers: "NTP servers",
|
||||
OptionTagNBNS: "NetBIOS name servers",
|
||||
OptionTagNBScope: "NetBIOS scope",
|
||||
OptionTagMessageType: "message type",
|
||||
OptionTagParameterRequestList: "parameter request list",
|
||||
OptionTagDomainSearch: "search domain",
|
||||
OptionTagClasslessStaticRoute: "classless static route",
|
||||
OptionTagEnd: "end",
|
||||
}
|
||||
|
||||
func OptionPad(req *BootRequest, r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
type DHCPMessageType uint8
|
||||
|
||||
func OptionHostName(req *BootRequest, r io.Reader) error {
|
||||
return errors.New("dhcp: option not implemented yet")
|
||||
}
|
||||
|
||||
func OptionMessageType(req *BootRequest, r io.Reader) error {
|
||||
return errors.New("dhcp: option not implemented yet")
|
||||
}
|
||||
|
||||
func OptionParameterRequestList(req *BootRequest, r io.Reader) error {
|
||||
return errors.New("dhcp: option not implemented yet")
|
||||
}
|
||||
|
||||
func OptionEnd(req *BootRequest, r io.Reader) error {
|
||||
return errors.New("dhcp: option not implemented yet")
|
||||
}
|
||||
|
||||
func ReadOption(req *BootRequest, tag byte, r io.Reader) error {
|
||||
opt := OptionTag(tag)
|
||||
if f, ok := optionRegistry[opt]; ok {
|
||||
return f(req, r)
|
||||
|
||||
}
|
||||
|
||||
return fmt.Errorf("dhcp: unknown/unhandled option %d", opt)
|
||||
}
|
||||
const (
|
||||
DHCPMessageTypeDiscover DHCPMessageType = 1
|
||||
DHCPMessageTypeOffer DHCPMessageType = 2
|
||||
DHCPMessageTypeRequest DHCPMessageType = 3
|
||||
DHCPMessageTypeDecline DHCPMessageType = 4
|
||||
DHCPMessageTypeAck DHCPMessageType = 5
|
||||
DHCPMessageTypeNAK DHCPMessageType = 6
|
||||
DHCPMessageTypeRelease DHCPMessageType = 7
|
||||
DHCPMessageTypeInform DHCPMessageType = 8
|
||||
)
|
||||
|
||||
@@ -44,16 +44,21 @@ type BootRequest struct {
|
||||
NextIP net.IP
|
||||
RelayIP net.IP
|
||||
|
||||
DHCPType uint8 // option 53
|
||||
HostName string // option 12
|
||||
ParameterRequests []string
|
||||
DHCPType DHCPMessageType // option 53
|
||||
HostName string // option 12
|
||||
ParameterRequests []OptionTag
|
||||
endOptions bool
|
||||
}
|
||||
|
||||
func newPacketReaderFunc(r io.Reader) func(v any) error {
|
||||
return func(v any) error {
|
||||
return binary.Read(r, binary.BigEndian, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (req *BootRequest) Read(packet []byte) error {
|
||||
buf := bytes.NewBuffer(packet)
|
||||
read := func(v any) error {
|
||||
return binary.Read(buf, binary.BigEndian, v)
|
||||
}
|
||||
read := newPacketReaderFunc(buf)
|
||||
|
||||
if err := read(&req.MessageType); err != nil {
|
||||
return err
|
||||
@@ -113,7 +118,6 @@ func (req *BootRequest) Read(packet []byte) error {
|
||||
if _, err := buf.Read(hwaPad); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("padding: %x", hwaPad)
|
||||
|
||||
tempBuf := make([]byte, maxServerName)
|
||||
if _, err := buf.Read(tempBuf); err != nil {
|
||||
@@ -127,7 +131,15 @@ func (req *BootRequest) Read(packet []byte) error {
|
||||
}
|
||||
req.FileName = string(bytes.Trim(tempBuf, "\x00"))
|
||||
|
||||
if err := ReadMagicCookie(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
if req.endOptions {
|
||||
break
|
||||
}
|
||||
|
||||
tag, err := buf.ReadByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -138,6 +150,8 @@ func (req *BootRequest) Read(packet []byte) error {
|
||||
|
||||
err = ReadOption(req, tag, buf)
|
||||
if err != nil {
|
||||
log.Spew(*req)
|
||||
log.Spew(packet)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
133
dhcp/read_options.go
Normal file
133
dhcp/read_options.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package dhcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
log "git.wntrmute.dev/kyle/goutils/syslog"
|
||||
)
|
||||
|
||||
var optionRegistry = map[OptionTag]Option{
|
||||
OptionTagPadding: OptionPad,
|
||||
OptionTagHostName: OptionHostName,
|
||||
OptionTagMessageType: OptionMessageType,
|
||||
OptionTagParameterRequestList: OptionParameterRequestList,
|
||||
OptionTagEnd: OptionEnd,
|
||||
}
|
||||
|
||||
func OptionPad(req *BootRequest, r io.Reader) error {
|
||||
// The padding option is a single 0 byte octet.
|
||||
return nil
|
||||
}
|
||||
|
||||
// OptionHostName reads a DHCP host name option.
|
||||
//
|
||||
// 3.14. Host Name Option
|
||||
//
|
||||
// This option specifies the name of the client. The name may or may
|
||||
// not be qualified with the local domain name (see section 3.17 for the
|
||||
// preferred way to retrieve the domain name). See RFC 1035 for
|
||||
// character set restrictions.
|
||||
|
||||
// The code for this option is 12, and its minimum length is 1.
|
||||
func OptionHostName(req *BootRequest, r io.Reader) error {
|
||||
read := newPacketReaderFunc(r)
|
||||
|
||||
var length uint8
|
||||
if err := read(&length); err != nil {
|
||||
return fmt.Errorf("dhcp: reading option length for DHCP Message Type")
|
||||
} else if length == 0 {
|
||||
return errors.New("dhcp: read option length 0, but expected option length for DHCP Host Name is >= 1")
|
||||
}
|
||||
|
||||
hostName := make([]byte, int(length))
|
||||
if n, err := r.Read(hostName); err != nil {
|
||||
return fmt.Errorf("dhcp: while reading hostname: %w", err)
|
||||
} else if n != int(length) {
|
||||
return fmt.Errorf("dhcp: only read %d bytes of hostname, expected %d bytes", n, length)
|
||||
}
|
||||
|
||||
req.HostName = string(hostName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func OptionMessageType(req *BootRequest, r io.Reader) error {
|
||||
read := newPacketReaderFunc(r)
|
||||
|
||||
var length uint8
|
||||
if err := read(&length); err != nil {
|
||||
return fmt.Errorf("dhcp: reading option length for DHCP Message Type")
|
||||
} else if length != 1 {
|
||||
return fmt.Errorf("dhcp: read option length %d, but expected option length for DHCP Message Type is 1", length)
|
||||
}
|
||||
|
||||
if err := read(&req.DHCPType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func OptionParameterRequestList(req *BootRequest, r io.Reader) error {
|
||||
read := newPacketReaderFunc(r)
|
||||
|
||||
var length uint8
|
||||
if err := read(&length); err != nil {
|
||||
return fmt.Errorf("dhcp: reading option length for DHCP Message Type")
|
||||
} else if length == 0 {
|
||||
return fmt.Errorf("dhcp: read option length %d, but expected option length for DHCP Parameter Request is >= 1", length)
|
||||
}
|
||||
|
||||
var parameters = make([]byte, int(length))
|
||||
if n, err := r.Read(parameters); err != nil {
|
||||
return fmt.Errorf("dhcp: while reading parameters: %w", err)
|
||||
} else if n != int(length) {
|
||||
return fmt.Errorf("dhcp: only read %d octets of requested parameters, expected %d octets", n, length)
|
||||
}
|
||||
|
||||
for _, parameter := range parameters {
|
||||
opt := OptionTag(parameter)
|
||||
log.Debugf("client is requesting %s", opt)
|
||||
req.ParameterRequests = append(req.ParameterRequests, opt)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func OptionEnd(req *BootRequest, r io.Reader) error {
|
||||
req.endOptions = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadOption(req *BootRequest, tag byte, r io.Reader) error {
|
||||
opt := OptionTag(tag)
|
||||
if f, ok := optionRegistry[opt]; ok {
|
||||
log.Debugf("dhcp: reading option=%s", opt)
|
||||
return f(req, r)
|
||||
}
|
||||
|
||||
return fmt.Errorf("dhcp: unknown/unhandled option %d", opt)
|
||||
}
|
||||
|
||||
const magicCookieLength = 4
|
||||
|
||||
var magicCookie = []byte{99, 130, 83, 99}
|
||||
|
||||
func ReadMagicCookie(r io.Reader) error {
|
||||
var cookie = make([]byte, magicCookieLength)
|
||||
n, err := r.Read(cookie)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dhcp: failed to read magic cookie: %w", err)
|
||||
} else if n != magicCookieLength {
|
||||
return fmt.Errorf("dhcp: read %d bytes, expected to read %d bytes for the magic cookie",
|
||||
n, magicCookieLength)
|
||||
}
|
||||
|
||||
if !bytes.Equal(cookie, magicCookie) {
|
||||
return fmt.Errorf("dhcp: read magic cookie %x, expected %x", cookie, magicCookie)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user