summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6
diff options
context:
space:
mode:
Diffstat (limited to 'dhcpv6')
-rw-r--r--dhcpv6/dhcpv6relay.go14
-rw-r--r--dhcpv6/modifiers.go6
-rw-r--r--dhcpv6/modifiers_test.go13
-rw-r--r--dhcpv6/option_clientlinklayeraddress.go47
-rw-r--r--dhcpv6/option_clientlinklayeraddress_test.go40
-rw-r--r--dhcpv6/options.go2
6 files changed, 122 insertions, 0 deletions
diff --git a/dhcpv6/dhcpv6relay.go b/dhcpv6/dhcpv6relay.go
index 9c08e00..26d6ed4 100644
--- a/dhcpv6/dhcpv6relay.go
+++ b/dhcpv6/dhcpv6relay.go
@@ -5,6 +5,7 @@ import (
"fmt"
"net"
+ "github.com/insomniacslk/dhcp/iana"
"github.com/u-root/u-root/pkg/uio"
)
@@ -54,6 +55,19 @@ func (ro RelayOptions) RemoteID() *OptRemoteID {
return nil
}
+// ClientLinkLayerAddress returns the Hardware Type and
+// Link Layer Address of the requesting client in this relay message.
+func (ro RelayOptions) ClientLinkLayerAddress() (iana.HWType, net.HardwareAddr) {
+ opt := ro.Options.GetOne(OptionClientLinkLayerAddr)
+ if opt == nil {
+ return 0, nil
+ }
+ if lla, ok := opt.(*optClientLinkLayerAddress); ok {
+ return lla.LinkLayerType, lla.LinkLayerAddress
+ }
+ return 0, nil
+}
+
// RelayMessage is a DHCPv6 relay agent message as defined by RFC 3315 Section
// 7.
type RelayMessage struct {
diff --git a/dhcpv6/modifiers.go b/dhcpv6/modifiers.go
index 96fbf4b..8099795 100644
--- a/dhcpv6/modifiers.go
+++ b/dhcpv6/modifiers.go
@@ -152,3 +152,9 @@ func WithIAPD(iaid [4]byte, prefixes ...*OptIAPrefix) Modifier {
}
}
}
+
+// WithClientLinkLayerAddress adds or updates the ClientLinkLayerAddress
+// option with provided HWType and HWAddress on a DHCPv6 packet
+func WithClientLinkLayerAddress(ht iana.HWType, lla net.HardwareAddr) Modifier {
+ return WithOption(OptClientLinkLayerAddress(ht, lla))
+}
diff --git a/dhcpv6/modifiers_test.go b/dhcpv6/modifiers_test.go
index 2354bd0..3c3204d 100644
--- a/dhcpv6/modifiers_test.go
+++ b/dhcpv6/modifiers_test.go
@@ -121,3 +121,16 @@ func TestWithIAPD(t *testing.T) {
require.Equal(t, 1, len(opt))
require.Equal(t, OptionIAPD, opt[0].Code())
}
+
+func TestWithClientLinkLayerAddress(t *testing.T) {
+ var d RelayMessage
+ mac, _ := net.ParseMAC("a4:83:e7:e3:df:88")
+ WithClientLinkLayerAddress(iana.HWTypeEthernet, mac)(&d)
+
+ opt := d.Options.GetOne(OptionClientLinkLayerAddr)
+ require.Equal(t, OptionClientLinkLayerAddr, opt.Code())
+
+ llt, lla := d.Options.ClientLinkLayerAddress()
+ require.Equal(t, iana.HWTypeEthernet, llt)
+ require.Equal(t, mac, lla)
+}
diff --git a/dhcpv6/option_clientlinklayeraddress.go b/dhcpv6/option_clientlinklayeraddress.go
new file mode 100644
index 0000000..91d8a23
--- /dev/null
+++ b/dhcpv6/option_clientlinklayeraddress.go
@@ -0,0 +1,47 @@
+package dhcpv6
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/insomniacslk/dhcp/iana"
+ "github.com/u-root/u-root/pkg/uio"
+)
+
+// OptClientLinkLayerAddress implements OptionClientLinkLayerAddr option.
+// https://tools.ietf.org/html/rfc6939
+func OptClientLinkLayerAddress(ht iana.HWType, lla net.HardwareAddr) *optClientLinkLayerAddress {
+ return &optClientLinkLayerAddress{LinkLayerType: ht, LinkLayerAddress: lla}
+}
+
+type optClientLinkLayerAddress struct {
+ LinkLayerType iana.HWType
+ LinkLayerAddress net.HardwareAddr
+}
+
+// Code returns the option code.
+func (op *optClientLinkLayerAddress) Code() OptionCode {
+ return OptionClientLinkLayerAddr
+}
+
+// ToBytes serializes the option and returns it as a sequence of bytes
+func (op *optClientLinkLayerAddress) ToBytes() []byte {
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(op.LinkLayerType))
+ buf.WriteBytes(op.LinkLayerAddress)
+ return buf.Data()
+}
+
+func (op *optClientLinkLayerAddress) String() string {
+ return fmt.Sprintf("ClientLinkLayerAddress: Type=%s LinkLayerAddress=%s", op.LinkLayerType, op.LinkLayerAddress)
+}
+
+// parseOptClientLinkLayerAddress deserializes from bytes
+// to build an optClientLinkLayerAddress structure.
+func parseOptClientLinkLayerAddress(data []byte) (*optClientLinkLayerAddress, error) {
+ var opt optClientLinkLayerAddress
+ buf := uio.NewBigEndianBuffer(data)
+ opt.LinkLayerType = iana.HWType(buf.Read16())
+ opt.LinkLayerAddress = buf.ReadAll()
+ return &opt, buf.FinError()
+}
diff --git a/dhcpv6/option_clientlinklayeraddress_test.go b/dhcpv6/option_clientlinklayeraddress_test.go
new file mode 100644
index 0000000..a25e882
--- /dev/null
+++ b/dhcpv6/option_clientlinklayeraddress_test.go
@@ -0,0 +1,40 @@
+package dhcpv6
+
+import (
+ "bytes"
+ "net"
+ "testing"
+
+ "github.com/insomniacslk/dhcp/iana"
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseOptClientLinkLayerAddress(t *testing.T) {
+ data := []byte{
+ 0, 1, // LinkLayerType
+ 164, 131, 231, 227, 223, 136,
+ }
+ opt, err := parseOptClientLinkLayerAddress(data)
+
+ require.NoError(t, err)
+ require.Equal(t, OptionClientLinkLayerAddr, opt.Code())
+ require.Equal(t, iana.HWTypeEthernet, opt.LinkLayerType)
+ require.Equal(t, net.HardwareAddr(data[2:]), opt.LinkLayerAddress)
+ require.Equal(t, "ClientLinkLayerAddress: Type=Ethernet LinkLayerAddress=a4:83:e7:e3:df:88", opt.String())
+}
+
+func TestOptClientLinkLayerAddressToBytes(t *testing.T) {
+ mac, _ := net.ParseMAC("a4:83:e7:e3:df:88")
+ opt := optClientLinkLayerAddress{
+ LinkLayerType: iana.HWTypeEthernet,
+ LinkLayerAddress: mac,
+ }
+ want := []byte{
+ 0, 1, // LinkLayerType
+ 164, 131, 231, 227, 223, 136,
+ }
+ b := opt.ToBytes()
+ if !bytes.Equal(b, want) {
+ t.Fatalf("opt.ToBytes()=%v, want %v", b, want)
+ }
+}
diff --git a/dhcpv6/options.go b/dhcpv6/options.go
index aefdfc8..a3afde0 100644
--- a/dhcpv6/options.go
+++ b/dhcpv6/options.go
@@ -87,6 +87,8 @@ func ParseOption(code OptionCode, optData []byte) (Option, error) {
var o OptNetworkInterfaceID
err = o.FromBytes(optData)
opt = &o
+ case OptionClientLinkLayerAddr:
+ opt, err = parseOptClientLinkLayerAddress(optData)
case OptionDHCPv4Msg:
opt, err = ParseOptDHCPv4Msg(optData)
case OptionDHCP4oDHCP6Server: