summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dhcpv6/duid_test.go88
-rw-r--r--dhcpv6/option_iaaddress.go78
-rw-r--r--dhcpv6/option_iaaddress_test.go69
-rw-r--r--dhcpv6/option_nontemporaryaddress.go10
-rw-r--r--dhcpv6/options.go6
-rw-r--r--netboot/netconf.go6
6 files changed, 189 insertions, 68 deletions
diff --git a/dhcpv6/duid_test.go b/dhcpv6/duid_test.go
index 23ce709..f28b26b 100644
--- a/dhcpv6/duid_test.go
+++ b/dhcpv6/duid_test.go
@@ -2,27 +2,93 @@ package dhcpv6
import (
"bytes"
+ "net"
"testing"
+
+ "github.com/insomniacslk/dhcp/iana"
+ "github.com/stretchr/testify/require"
)
-func TestDuidUuid(t *testing.T) {
+func TestDuidInvalidTooShort(t *testing.T) {
+ // too short DUID at all (must be at least 2 bytes)
+ _, err := DuidFromBytes([]byte{0})
+ require.Error(t, err)
+
+ // too short DUID_LL (must be at least 4 bytes)
+ _, err = DuidFromBytes([]byte{0, 3, 0xa})
+ require.Error(t, err)
+
+ // too short DUID_EN (must be at least 6 bytes)
+ _, err = DuidFromBytes([]byte{0, 2, 0xa, 0xb, 0xc})
+ require.Error(t, err)
+
+ // too short DUID_LLT (must be at least 8 bytes)
+ _, err = DuidFromBytes([]byte{0, 1, 0xa, 0xb, 0xc, 0xd, 0xe})
+ require.Error(t, err)
+
+ // too short DUID_UUID (must be at least 18 bytes)
+ _, err = DuidFromBytes([]byte{0, 4, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf})
+ require.Error(t, err)
+}
+
+func TestDuidLLTFromBytes(t *testing.T) {
buf := []byte{
- 0x00, 0x04, // type
- 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, // uuid
+ 0, 1, // DUID_LLT
+ 0, 1, // HwTypeEthernet
+ 0x01, 0x02, 0x03, 0x04, // time
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // link-layer addr
}
duid, err := DuidFromBytes(buf)
- if err != nil {
- t.Fatal(err)
+ require.NoError(t, err)
+ require.Equal(t, 14, duid.Length())
+ require.Equal(t, DUID_LLT, duid.Type)
+ require.Equal(t, uint32(0x01020304), duid.Time)
+ require.Equal(t, iana.HwTypeEthernet, duid.HwType)
+ require.Equal(t, net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, duid.LinkLayerAddr)
+}
+
+func TestDuidLLFromBytes(t *testing.T) {
+ buf := []byte{
+ 0, 3, // DUID_LL
+ 0, 1, // HwTypeEthernet
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // link-layer addr
}
- if dt := duid.Type; dt != DUID_UUID {
- t.Fatalf("Invalid Preferred Lifetime. Expected 4, got %d", dt)
+ duid, err := DuidFromBytes(buf)
+ require.NoError(t, err)
+ require.Equal(t, 10, duid.Length())
+ require.Equal(t, DUID_LL, duid.Type)
+ require.Equal(t, iana.HwTypeEthernet, duid.HwType)
+ require.Equal(t, net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, duid.LinkLayerAddr)
+}
+
+func TestDuidUuidFromBytes(t *testing.T) {
+ buf := []byte{
+ 0x00, 0x04, // DUID_UUID
}
- if uuid := duid.Uuid; !bytes.Equal(uuid, buf[2:]) {
- t.Fatalf("Invalid UUID. Expected %v, got %v", buf[2:], uuid)
+ uuid := []byte{0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08}
+ buf = append(buf, uuid...)
+ duid, err := DuidFromBytes(buf)
+ require.NoError(t, err)
+ require.Equal(t, 18, duid.Length())
+ require.Equal(t, DUID_UUID, duid.Type)
+ require.Equal(t, uuid, duid.Uuid)
+}
+
+func TestDuidLLTToBytes(t *testing.T) {
+ expected := []byte{
+ 0, 1, // DUID_LLT
+ 0, 1, // HwTypeEthernet
+ 0x01, 0x02, 0x03, 0x04, // time
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // link-layer addr
}
- if mac := duid.LinkLayerAddr; mac != nil {
- t.Fatalf("Invalid MAC. Expected nil, got %v", mac)
+ duid := Duid{
+ Type: DUID_LLT,
+ HwType: iana.HwTypeEthernet,
+ Time: uint32(0x01020304),
+ LinkLayerAddr: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
}
+ toBytes := duid.ToBytes()
+ require.Equal(t, expected, toBytes)
}
func TestDuidUuidToBytes(t *testing.T) {
diff --git a/dhcpv6/option_iaaddress.go b/dhcpv6/option_iaaddress.go
index 7d0f4a6..1a89a25 100644
--- a/dhcpv6/option_iaaddress.go
+++ b/dhcpv6/option_iaaddress.go
@@ -9,78 +9,62 @@ import (
"net"
)
+// OptIAAddress represents an OPTION_IAADDR
type OptIAAddress struct {
- ipv6Addr [16]byte
- preferredLifetime uint32
- validLifetime uint32
- options []byte
+ IPv6Addr net.IP
+ PreferredLifetime uint32
+ ValidLifetime uint32
+ Options []Option
}
+// Code returns the option's code
func (op *OptIAAddress) Code() OptionCode {
return OPTION_IAADDR
}
+// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptIAAddress) ToBytes() []byte {
buf := make([]byte, 28)
binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IAADDR))
binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- copy(buf[4:20], op.ipv6Addr[:])
- binary.BigEndian.PutUint32(buf[20:24], op.preferredLifetime)
- binary.BigEndian.PutUint32(buf[24:28], op.validLifetime)
- buf = append(buf, op.options...)
+ copy(buf[4:20], op.IPv6Addr[:])
+ binary.BigEndian.PutUint32(buf[20:24], op.PreferredLifetime)
+ binary.BigEndian.PutUint32(buf[24:28], op.ValidLifetime)
+ for _, opt := range op.Options {
+ buf = append(buf, opt.ToBytes()...)
+ }
return buf
}
-func (op *OptIAAddress) IPv6Addr() []byte {
- return op.ipv6Addr[:]
-}
-
-func (op *OptIAAddress) SetIPv6Addr(addr [16]byte) {
- op.ipv6Addr = addr
-}
-
-func (op *OptIAAddress) PreferredLifetime() uint32 {
- return op.preferredLifetime
-}
-
-func (op *OptIAAddress) SetPreferredLifetime(pl uint32) {
- op.preferredLifetime = pl
-}
-
-func (op *OptIAAddress) ValidLifetime() uint32 {
- return op.validLifetime
-}
-
-func (op *OptIAAddress) SetValidLifetime(vl uint32) {
- op.validLifetime = vl
-}
-func (op *OptIAAddress) Options() []byte {
- return op.options
-}
-
-func (op *OptIAAddress) SetOptions(options []byte) {
- op.options = options
-}
-
+// Length returns the option length
func (op *OptIAAddress) Length() int {
- return 24 + len(op.options)
+ opLen := 24
+ for _, opt := range op.Options {
+ opLen += 4 + opt.Length()
+ }
+ return opLen
}
func (op *OptIAAddress) String() string {
return fmt.Sprintf("OptIAAddress{ipv6addr=%v, preferredlifetime=%v, validlifetime=%v, options=%v}",
- net.IP(op.ipv6Addr[:]), op.preferredLifetime, op.validLifetime, op.options)
+ net.IP(op.IPv6Addr[:]), op.PreferredLifetime, op.ValidLifetime, op.Options)
}
-// build an OptIAAddress structure from a sequence of bytes.
-// The input data does not include option code and length bytes.
+// ParseOptIAAddress builds an OptIAAddress structure from a sequence
+// of bytes. The input data does not include option code and length
+// bytes.
func ParseOptIAAddress(data []byte) (*OptIAAddress, error) {
+ var err error
opt := OptIAAddress{}
if len(data) < 24 {
return nil, fmt.Errorf("Invalid IA Address data length. Expected at least 24 bytes, got %v", len(data))
}
- copy(opt.ipv6Addr[:], data[:16])
- opt.preferredLifetime = binary.BigEndian.Uint32(data[16:20])
- opt.validLifetime = binary.BigEndian.Uint32(data[20:24])
- copy(opt.options, data[24:])
+ opt.IPv6Addr = net.IP(data[:16])
+ opt.PreferredLifetime = binary.BigEndian.Uint32(data[16:20])
+ opt.ValidLifetime = binary.BigEndian.Uint32(data[20:24])
+ opt.Options, err = OptionsFromBytes(data[24:])
+ if err != nil {
+ return nil, err
+ }
return &opt, nil
}
diff --git a/dhcpv6/option_iaaddress_test.go b/dhcpv6/option_iaaddress_test.go
new file mode 100644
index 0000000..ad4f69c
--- /dev/null
+++ b/dhcpv6/option_iaaddress_test.go
@@ -0,0 +1,69 @@
+package dhcpv6
+
+import (
+ "net"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestOptIAAddressParse(t *testing.T) {
+ ipaddr := []byte{0x24, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+ data := append(ipaddr, []byte{
+ 0xa, 0xb, 0xc, 0xd, // preferred lifetime
+ 0xe, 0xf, 0x1, 0x2, // valid lifetime
+ 0, 8, 0, 2, 0xaa, 0xbb, // options
+ }...)
+ opt, err := ParseOptIAAddress(data)
+ require.NoError(t, err)
+ require.Equal(t, 30, opt.Length())
+ require.Equal(t, net.IP(ipaddr), opt.IPv6Addr)
+ require.Equal(t, uint32(0x0a0b0c0d), opt.PreferredLifetime)
+ require.Equal(t, uint32(0x0e0f0102), opt.ValidLifetime)
+}
+
+func TestOptIAAddressParseInvalidTooShort(t *testing.T) {
+ data := []byte{
+ 0x24, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 0xa, 0xb, 0xc, 0xd, // preferred lifetime
+ // truncated here
+ }
+ _, err := ParseOptIAAddress(data)
+ require.Error(t, err)
+}
+
+func TestOptIAAddressParseInvalidBrokenOptions(t *testing.T) {
+ data := []byte{
+ 0x24, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 0xa, 0xb, 0xc, 0xd, // preferred lifetime
+ 0xe, 0xf, 0x1, 0x2, // valid lifetime
+ 0, 8, 0, 2, 0xaa, // broken options
+ }
+ _, err := ParseOptIAAddress(data)
+ require.Error(t, err)
+}
+
+func TestOptIAAddressToBytes(t *testing.T) {
+ expected := []byte{
+ 0, 5, // OPTION_IAADDR
+ 0, 30, // length
+ }
+ ipBytes := []byte{0x24, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+ expected = append(expected, ipBytes...)
+ expected = append(expected, []byte{
+ 0xa, 0xb, 0xc, 0xd, // preferred lifetime
+ 0xe, 0xf, 0x1, 0x2, // valid lifetime
+ 0, 8, 0, 2, 0xaa, 0xbb, // options
+ }...)
+ opt := OptIAAddress{
+ IPv6Addr: net.IP(ipBytes),
+ PreferredLifetime: 0x0a0b0c0d,
+ ValidLifetime: 0x0e0f0102,
+ Options: []Option{
+ &OptElapsedTime{
+ ElapsedTime: 0xaabb,
+ },
+ },
+ }
+ require.Equal(t, expected, opt.ToBytes())
+}
diff --git a/dhcpv6/option_nontemporaryaddress.go b/dhcpv6/option_nontemporaryaddress.go
index e303ae7..cefca64 100644
--- a/dhcpv6/option_nontemporaryaddress.go
+++ b/dhcpv6/option_nontemporaryaddress.go
@@ -48,6 +48,7 @@ func (op *OptIANA) String() string {
// build an OptIANA structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptIANA(data []byte) (*OptIANA, error) {
+ var err error
opt := OptIANA{}
if len(data) < 12 {
return nil, fmt.Errorf("Invalid IA for Non-temporary Addresses data length. Expected at least 12 bytes, got %v", len(data))
@@ -55,12 +56,9 @@ func ParseOptIANA(data []byte) (*OptIANA, error) {
copy(opt.IaId[:], data[:4])
opt.T1 = binary.BigEndian.Uint32(data[4:8])
opt.T2 = binary.BigEndian.Uint32(data[8:12])
- var err error
- if len(data[12:]) > 0 {
- opt.Options, err = OptionsFromBytes(data[12:])
- if err != nil {
- return nil, err
- }
+ opt.Options, err = OptionsFromBytes(data[12:])
+ if err != nil {
+ return nil, err
}
return &opt, nil
}
diff --git a/dhcpv6/options.go b/dhcpv6/options.go
index 3ee5dad..d64d1d9 100644
--- a/dhcpv6/options.go
+++ b/dhcpv6/options.go
@@ -120,11 +120,15 @@ func ParseOption(dataStart []byte) (Option, error) {
func OptionsFromBytes(data []byte) ([]Option, error) {
// Parse a sequence of bytes until the end and build a list of options from
// it. Returns an error if any invalid option or length is found.
+ options := make([]Option, 0, 10)
+ if len(data) == 0 {
+ // no options, no party
+ return options, nil
+ }
if len(data) < 4 {
// cannot be shorter than option code (2 bytes) + length (2 bytes)
return nil, fmt.Errorf("Invalid options: shorter than 4 bytes")
}
- options := make([]Option, 0, 10)
idx := 0
for {
if idx == len(data) {
diff --git a/netboot/netconf.go b/netboot/netconf.go
index a9751dd..f7a117c 100644
--- a/netboot/netconf.go
+++ b/netboot/netconf.go
@@ -47,11 +47,11 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) {
for _, iaaddr := range iaaddrs {
netconf.Addresses = append(netconf.Addresses, AddrConf{
IPNet: net.IPNet{
- IP: iaaddr.IPv6Addr(),
+ IP: iaaddr.IPv6Addr,
Mask: netmask,
},
- PreferredLifetime: int(iaaddr.PreferredLifetime()),
- ValidLifetime: int(iaaddr.ValidLifetime()),
+ PreferredLifetime: int(iaaddr.PreferredLifetime),
+ ValidLifetime: int(iaaddr.ValidLifetime),
})
}
// get DNS configuration