From 09f6357e9117743012d95388156bc2f6ab606116 Mon Sep 17 00:00:00 2001 From: Tom McPhail Date: Mon, 1 Jun 2020 23:17:56 +1000 Subject: Add Client Link-Layer Address option for RFC 6939 support. Signed-off-by: Tom McPhail --- dhcpv6/dhcpv6relay.go | 14 +++++++++ dhcpv6/modifiers.go | 6 ++++ dhcpv6/modifiers_test.go | 13 ++++++++ dhcpv6/option_clientlinklayeraddress.go | 47 ++++++++++++++++++++++++++++ dhcpv6/option_clientlinklayeraddress_test.go | 40 +++++++++++++++++++++++ dhcpv6/options.go | 2 ++ 6 files changed, 122 insertions(+) create mode 100644 dhcpv6/option_clientlinklayeraddress.go create mode 100644 dhcpv6/option_clientlinklayeraddress_test.go (limited to 'dhcpv6') 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: -- cgit v1.2.3