Files
lneto/dhcp/dhcpv6/frame.go
T
Pat Whittingslow 7d5830d7ab V2 Netbird integration - UDP MIMO/SIMO, ICMPv6, DHCPv6 implementations (#106)
* begin adding udp.MuxHandler

* add udp MuxHandlerSIMO/MIMO

* add tcp rx shutdown

* icmpv6 client

* icmpv6 Client shared NDP/Echo preparation

* icmpv6 client ndp/echo split

* icmpv6 client ndp/echo split done

* icmpv6 adjustments

* add dhcpv6 stubs

* dhcpv4 preliminary revision

* add dns.NextLabel

* dns label name tweaks

* dns begin work on TCP client

* add dnstcp package

* apply gofmt changes

* add udp mux tests

* clean up, remove StackBig for now

* remove dnstcp so as to merged confident parts and we continue dnstcp work elsewhere
2026-05-10 12:16:30 -03:00

85 lines
2.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package dhcpv6
import (
"encoding/binary"
"io"
"github.com/soypat/lneto"
)
// NewFrame returns a Frame backed by buf.
// Returns an error if buf is shorter than [OptionsOffset] bytes.
func NewFrame(buf []byte) (Frame, error) {
if len(buf) < OptionsOffset {
return Frame{}, lneto.ErrTruncatedFrame
}
return Frame{buf: buf}, nil
}
// Frame encapsulates the raw bytes of a DHCPv6 client-server message (RFC 8415 §8)
// and provides methods for accessing and modifying its fields.
//
// Layout:
//
// Byte 0: msg-type
// Bytes 1-3: transaction-id (24-bit big-endian)
// Bytes 4+: options (code(2) + length(2) + data)
type Frame struct {
buf []byte
}
// MsgType returns the message type field.
func (frm Frame) MsgType() MsgType { return MsgType(frm.buf[0]) }
// SetMsgType sets the message type field.
func (frm Frame) SetMsgType(t MsgType) { frm.buf[0] = byte(t) }
// TransactionID returns the 24-bit transaction ID as a uint32 (upper byte is always zero).
func (frm Frame) TransactionID() uint32 {
return uint32(frm.buf[1])<<16 | uint32(frm.buf[2])<<8 | uint32(frm.buf[3])
}
// SetTransactionID writes the lower 24 bits of id into bytes 13.
func (frm Frame) SetTransactionID(id uint32) {
frm.buf[1] = byte(id >> 16)
frm.buf[2] = byte(id >> 8)
frm.buf[3] = byte(id)
}
// Options returns the options section of the frame (bytes from [OptionsOffset] onward).
func (frm Frame) Options() []byte { return frm.buf[OptionsOffset:] }
// ForEachOption iterates over all DHCPv6 options in the frame's options section.
// Each option is passed to fn as (byteOffset, optionCode, optionData).
// Iteration stops early if fn returns [io.EOF]; any other non-nil error is returned directly.
// If fn is nil, the function only validates the structure.
func (frm Frame) ForEachOption(fn func(off int, code OptCode, data []byte) error) error {
buf := frm.buf
ptr := OptionsOffset
for ptr+4 <= len(buf) {
code := OptCode(binary.BigEndian.Uint16(buf[ptr:]))
optlen := int(binary.BigEndian.Uint16(buf[ptr+2:]))
if ptr+4+optlen > len(buf) {
return lneto.ErrInvalidLengthField
}
if fn != nil {
err := fn(ptr, code, buf[ptr+4:ptr+4+optlen])
if err == io.EOF {
return nil
}
if err != nil {
return err
}
}
ptr += 4 + optlen
}
if ptr != len(buf) {
// 13 trailing bytes that cannot form a valid option header.
return lneto.ErrTruncatedFrame
}
return nil
}
// ValidateSize validates the structure of the options section without invoking a callback.
func (frm Frame) ValidateSize() error { return frm.ForEachOption(nil) }