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
This commit is contained in:
Pat Whittingslow
2026-05-10 12:16:30 -03:00
committed by GitHub
parent bdbd38ab44
commit 7d5830d7ab
22 changed files with 2785 additions and 75 deletions
+84
View File
@@ -0,0 +1,84 @@
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) }