added some option handling functions

This commit is contained in:
2025-09-26 19:58:22 +09:00
parent 90365bfdd4
commit 6ce8f1a5fe
5 changed files with 473 additions and 84 deletions

208
pkt.go
View File

@ -1,10 +1,12 @@
package haza
import "errors"
import "fmt"
import "io"
import "net"
type Dhcp4Op = uint8
type Dhcp4Htype = uint8
type Dhcp4Msg = uint8
const (
_ Dhcp4Op = iota
@ -26,32 +28,6 @@ const (
DHCP4_HTYPE_PUREIP = 35
)
const (
_ Dhcp4Msg = iota
DHCP4_MSG_DISCOVER = 1
DHCP4_MSG_OFFER = 2
DHCP4_MSG_REQUEST = 3
DHCP4_MSG_DECLINE = 4
DHCP4_MSG_ACK = 5
DHCP4_MSG_NAK = 6
DHCP4_MSG_RELEASE = 7
DHCP4_MSG_INFORM = 8
DHCP4_MSG_FORCE_RENEW = 9
DHCP4_MSG_LEASE_QUERY = 10
DHCP4_MSG_LEASE_UNASSIGNED = 11
DHCP4_MSG_LEASE_UNKNOWN = 12
DHCP4_MSG_LEASE_ACTIVE = 13
DHCP4_MSG_BULK_LEASE_QUERY = 14
DHCP4_MSG_LEASE_QUERY_DONE = 15
DHCP4_MSG_ACTIVE_LEASE_QUERY = 16
DHCP4_MSG_LEASE_QUERY_STATUS = 17
DHCP4_MSG_TLS = 18
)
type Dhcp4Pkt struct {
Op Dhcp4Op
Htype Dhcp4Htype
@ -72,72 +48,170 @@ type Dhcp4Pkt struct {
File [128]byte // boot file name
// options are placed after the header.
// the first four bytes of the options compose a magic cookie
// 0x63 0x82 0x53 0x63
// the first four bytes of the options compose a magic cookie
// 0x63 0x82 0x53 0x63
Options map[Dhcp4OptCode][]byte
// internal fields
msg_type Dhcp4MsgType
}
var dhcp4_magic_cookie [4]byte = [4]byte{ 0x63, 0x82, 0x53, 0x63 }
func NewDhcp4Pkt(msg_type Dhcp4MsgType) *Dhcp4Pkt {
var pkt *Dhcp4Pkt
var opt Dhcp4Opt
pkt = &Dhcp4Pkt{
Op: DHCP4_OP_BOOTREQUEST, // TODO: choose between request and reply based on the msg type..
Htype: DHCP4_HTYPE_ETHER,
Hlen: 6,
Xid: 0, // TODO: generate it?
Ciaddr: net.IPv4zero,
Yiaddr: net.IPv4zero,
Siaddr: net.IPv4zero,
Gwaddr: net.IPv4zero,
//Chaddr:
//Sname:
//File:
// fill the internal fields
msg_type: msg_type,
}
// add the message type option
opt = Dhcp4OptMsgType(msg_type)
pkt.Options[opt.Code()] = opt.Bytes()
return pkt
}
func (pkt *Dhcp4Pkt) AddOption(opt Dhcp4Opt) {
pkt.Options[opt.Code()] = opt.Bytes();
}
func (pkt *Dhcp4Pkt) Decode(b []byte) error {
// fill the packet with data from the bytes
var r *ByteReader
var nbr *NetByteReader
var u8 byte
var u16 uint16
var u32 uint32
var p Dhcp4Pkt
var cookie [4]byte
var ov []byte
var ok bool
var err error
r = NewByteReader(b)
u8, err = r.ReadByte()
nbr = NewNetByteReader(b)
u8, err = nbr.ReadByte()
if err != nil { return err }
p.Op = Dhcp4Op(u8)
u8, err = r.ReadByte()
u8, err = nbr.ReadByte()
if err != nil { return err }
p.Htype = Dhcp4Htype(u8)
u8, err = r.ReadByte()
p.Hlen, err = nbr.ReadByte()
if err != nil { return err }
p.Hlen = u8
if p.Hlen > 16 { return fmt.Errorf("invalid hlen %d", p.Hlen) }
u8, err = r.ReadByte()
if err != nil { return err }
p.Hops = u8
u32, err = r.ReadUint32()
if err != nil { return err }
p.Xid = u32
u16, err = r.ReadUint16()
if err != nil { return err }
p.Secs = u16
u16, err = r.ReadUint16()
if err != nil { return err }
p.Flags = u16
p.Ciaddr, err = r.ReadIp4()
p.Hops, err = nbr.ReadByte()
if err != nil { return err }
p.Yiaddr, err = r.ReadIp4()
p.Xid, err = nbr.ReadUint32()
if err != nil { return err }
p.Siaddr, err = r.ReadIp4()
p.Secs, err = nbr.ReadUint16()
if err != nil { return err }
p.Gwaddr, err = r.ReadIp4()
p.Flags, err = nbr.ReadUint16()
if err != nil { return err }
err = r.ReadAllBytes(p.Chaddr[:])
p.Ciaddr, err = nbr.ReadIp4()
if err != nil { return err }
err = r.ReadAllBytes(p.Sname[:])
p.Yiaddr, err = nbr.ReadIp4()
if err != nil { return err }
err = r.ReadAllBytes(p.File[:])
p.Siaddr, err = nbr.ReadIp4()
if err != nil { return err }
// magic
// options..
p.Gwaddr, err = nbr.ReadIp4()
if err != nil { return err }
err = nbr.ReadBytesFull(p.Chaddr[:])
if err != nil { return err }
err = nbr.ReadBytesFull(p.Sname[:])
if err != nil { return err }
err = nbr.ReadBytesFull(p.File[:])
if err != nil { return err }
err = nbr.ReadBytesFull(cookie[:])
if err != nil {
if errors.Is(err, io.EOF) {
// TODO: no options
}
return err
}
if cookie != dhcp4_magic_cookie {
return fmt.Errorf("invalid magic cookie %v", cookie)
}
// load options
p.Options = make(map[Dhcp4OptCode][]byte)
for {
var oc uint8
var ol uint8
var optval [255]byte
oc, err = nbr.ReadByte() // option code
if err != nil {
if errors.Is(err, io.EOF) { break }
return err
}
if oc == DHCP4_OPT_PADDING { continue }
if oc == DHCP4_OPT_END { break }
ol, err = nbr.ReadByte() // option length
if err != nil { return err }
err = nbr.ReadBytesAtLeast(optval[:], int(ol))
if err != nil { return err }
// RFC 3396 mandates that when multiple occurrences of a concatenation-requiring
// option appear in a DHCPv4 packet, their data MUST be concatenated prior to
// processing. For non-concatenation-requiring (e.g. fixed-length) options, concatenation
// is not defined. but let's not make an exception and let the processor check
// validity.
p.Options[oc] = append(p.Options[oc], optval[:ol]...)
}
//for oc, ov = range p.Optons {
// ot = oc_to_type(oc)
// switch (ot) {
// case OT_UINT8:
// case OT_UINT16:
// case OT_UINT18:
// case OT_UINT32:
// case OT_IP4ADDR:
// case OT_IP46DDR:
// case OT_STRING:
// default:
// leave it as is
// }
//
//}
ov, ok = p.Options[DHCP4_OPT_MSG_TYPE]
if !ok { return fmt.Errorf("no message type") }
if len(ov) != 1 { return fmt.Errorf("invalid message type option") }
p.msg_type = ov[0]
*pkt = p
return nil
@ -146,3 +220,7 @@ func (pkt *Dhcp4Pkt) Decode(b []byte) error {
func (pkt *Dhcp4Pkt) Encode() []byte {
return nil
}
func (pkt *Dhcp4Pkt) MsgType() Dhcp4MsgType {
return pkt.msg_type
}