2025-09-17 19:31:37 +09:00
|
|
|
package haza
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
import "errors"
|
|
|
|
import "fmt"
|
|
|
|
import "io"
|
2025-09-17 19:31:37 +09:00
|
|
|
import "net"
|
|
|
|
|
|
|
|
type Dhcp4Op = uint8
|
|
|
|
type Dhcp4Htype = uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
_ Dhcp4Op = iota
|
|
|
|
DHCP4_OP_BOOTREQUEST = 1
|
|
|
|
DHCP4_OP_BOOTREPLY = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
_ Dhcp4Htype = iota
|
|
|
|
DHCP4_HTYPE_ETHER = 1
|
|
|
|
DHCP4_HTYPE_IEEE802 = 6
|
|
|
|
DHCP4_HTYPE_ARCNET = 7
|
|
|
|
DHCP4_HTYPE_APPLETALK = 8
|
|
|
|
DHCP4_HTYPE_HDLC = 17
|
|
|
|
DHCP4_HTYPE_ATM = 19
|
|
|
|
DHCP4_HTYPE_ARPSEC = 30
|
|
|
|
DHCP4_HTYPE_IPSEC = 31
|
|
|
|
DHCP4_HTYPE_INFINIBAND = 32
|
|
|
|
DHCP4_HTYPE_PUREIP = 35
|
|
|
|
)
|
|
|
|
|
|
|
|
type Dhcp4Pkt struct {
|
|
|
|
Op Dhcp4Op
|
|
|
|
Htype Dhcp4Htype
|
|
|
|
Hlen uint8 // length of Chaddr
|
|
|
|
|
|
|
|
Hops uint8
|
|
|
|
Xid uint32
|
|
|
|
Secs uint16
|
|
|
|
Flags uint16
|
|
|
|
|
|
|
|
Ciaddr net.IP // uint32
|
|
|
|
Yiaddr net.IP // uint32
|
|
|
|
Siaddr net.IP // uint32
|
|
|
|
Gwaddr net.IP // uint32
|
|
|
|
Chaddr [16]byte
|
|
|
|
|
|
|
|
Sname [64]byte // server host name
|
|
|
|
File [128]byte // boot file name
|
|
|
|
|
|
|
|
// options are placed after the header.
|
2025-09-26 19:58:22 +09:00
|
|
|
// 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();
|
2025-09-17 19:31:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pkt *Dhcp4Pkt) Decode(b []byte) error {
|
|
|
|
// fill the packet with data from the bytes
|
2025-09-26 19:58:22 +09:00
|
|
|
var nbr *NetByteReader
|
2025-09-17 19:31:37 +09:00
|
|
|
var u8 byte
|
|
|
|
var p Dhcp4Pkt
|
2025-09-26 19:58:22 +09:00
|
|
|
var cookie [4]byte
|
|
|
|
var ov []byte
|
|
|
|
var ok bool
|
2025-09-17 19:31:37 +09:00
|
|
|
var err error
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
nbr = NewNetByteReader(b)
|
|
|
|
|
|
|
|
u8, err = nbr.ReadByte()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
p.Op = Dhcp4Op(u8)
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
u8, err = nbr.ReadByte()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
p.Htype = Dhcp4Htype(u8)
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Hlen, err = nbr.ReadByte()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
2025-09-26 19:58:22 +09:00
|
|
|
if p.Hlen > 16 { return fmt.Errorf("invalid hlen %d", p.Hlen) }
|
2025-09-17 19:31:37 +09:00
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Hops, err = nbr.ReadByte()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Xid, err = nbr.ReadUint32()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Secs, err = nbr.ReadUint16()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Flags, err = nbr.ReadUint16()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Ciaddr, err = nbr.ReadIp4()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Yiaddr, err = nbr.ReadIp4()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Siaddr, err = nbr.ReadIp4()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
p.Gwaddr, err = nbr.ReadIp4()
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
err = nbr.ReadBytesFull(p.Chaddr[:])
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
|
|
|
|
2025-09-26 19:58:22 +09:00
|
|
|
err = nbr.ReadBytesFull(p.Sname[:])
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
2025-09-26 19:58:22 +09:00
|
|
|
|
|
|
|
err = nbr.ReadBytesFull(p.File[:])
|
2025-09-17 19:31:37 +09:00
|
|
|
if err != nil { return err }
|
2025-09-26 19:58:22 +09:00
|
|
|
|
|
|
|
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]
|
2025-09-17 19:31:37 +09:00
|
|
|
|
|
|
|
*pkt = p
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pkt *Dhcp4Pkt) Encode() []byte {
|
|
|
|
return nil
|
|
|
|
}
|
2025-09-26 19:58:22 +09:00
|
|
|
|
|
|
|
func (pkt *Dhcp4Pkt) MsgType() Dhcp4MsgType {
|
|
|
|
return pkt.msg_type
|
|
|
|
}
|