diff options
author | Pablo Mazzini <pmazzini@gmail.com> | 2020-03-09 18:18:25 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-09 18:18:25 +0000 |
commit | 34e3d10db3f3eda7b4c484d4c674537fb83a769e (patch) | |
tree | 208b013067fd3fcb1c1de071f128697eed065b17 /dhcpv6 | |
parent | 0ef7af55f53d22f97650f2d4a299567977da57fb (diff) | |
parent | bd34b7c6963c8c124b45759423d41987d428668e (diff) |
Merge branch 'master' into vendors
Diffstat (limited to 'dhcpv6')
-rw-r--r-- | dhcpv6/dhcpv6message.go | 13 | ||||
-rw-r--r-- | dhcpv6/modifiers.go | 14 | ||||
-rw-r--r-- | dhcpv6/modifiers_test.go | 15 | ||||
-rw-r--r-- | dhcpv6/option_dhcpv4_msg.go | 39 | ||||
-rw-r--r-- | dhcpv6/option_dhcpv4_msg_test.go | 105 | ||||
-rw-r--r-- | dhcpv6/option_dhcpv4_o_dhcpv6_server.go | 46 | ||||
-rw-r--r-- | dhcpv6/option_dhcpv4_o_dhcpv6_server_test.go | 55 | ||||
-rw-r--r-- | dhcpv6/option_iaaddress.go | 24 | ||||
-rw-r--r-- | dhcpv6/option_iaaddress_test.go | 10 | ||||
-rw-r--r-- | dhcpv6/option_nontemporaryaddress.go | 57 | ||||
-rw-r--r-- | dhcpv6/option_nontemporaryaddress_test.go | 50 | ||||
-rw-r--r-- | dhcpv6/options.go | 4 | ||||
-rw-r--r-- | dhcpv6/types.go | 6 |
13 files changed, 390 insertions, 48 deletions
diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go index 0927038..2067fcc 100644 --- a/dhcpv6/dhcpv6message.go +++ b/dhcpv6/dhcpv6message.go @@ -241,6 +241,19 @@ func (mo MessageOptions) FQDN() *OptFQDN { return nil } +// DHCP4oDHCP6Server returns the DHCP 4o6 Server Address option as +// defined by RFC 7341. +func (mo MessageOptions) DHCP4oDHCP6Server() *OptDHCP4oDHCP6Server { + opt := mo.Options.GetOne(OptionDHCP4oDHCP6Server) + if opt == nil { + return nil + } + if server, ok := opt.(*OptDHCP4oDHCP6Server); ok { + return server + } + return nil +} + // Message represents a DHCPv6 Message as defined by RFC 3315 Section 6. type Message struct { MessageType MessageType diff --git a/dhcpv6/modifiers.go b/dhcpv6/modifiers.go index 14bfe51..ffea9ed 100644 --- a/dhcpv6/modifiers.go +++ b/dhcpv6/modifiers.go @@ -63,7 +63,7 @@ func WithIANA(addrs ...OptIAAddress) Modifier { iana = &OptIANA{} } for _, addr := range addrs { - iana.AddOption(&addr) + iana.Options.Add(&addr) } msg.UpdateOption(iana) } @@ -77,7 +77,7 @@ func WithIAID(iaid [4]byte) Modifier { iana := msg.Options.OneIANA() if iana == nil { iana = &OptIANA{ - Options: Options{}, + Options: IdentityOptions{Options: []Option{}}, } } copy(iana.IaId[:], iaid[:]) @@ -119,3 +119,13 @@ func WithRequestedOptions(codes ...OptionCode) Modifier { } } } + +// WithDHCP4oDHCP6Server adds or updates an OptDHCP4oDHCP6Server +func WithDHCP4oDHCP6Server(addrs ...net.IP) Modifier { + return func(d DHCPv6) { + opt := OptDHCP4oDHCP6Server{ + DHCP4oDHCP6Servers: addrs, + } + d.UpdateOption(&opt) + } +} diff --git a/dhcpv6/modifiers_test.go b/dhcpv6/modifiers_test.go index b99d4a2..c240067 100644 --- a/dhcpv6/modifiers_test.go +++ b/dhcpv6/modifiers_test.go @@ -92,3 +92,18 @@ func TestWithFQDN(t *testing.T) { require.Equal(t, uint8(4), ofqdn.Flags) require.Equal(t, "cnos.localhost", ofqdn.DomainName) } + +func TestWithDHCP4oDHCP6Server(t *testing.T) { + var d Message + WithDHCP4oDHCP6Server([]net.IP{ + net.ParseIP("fe80::1"), + net.ParseIP("fe80::2"), + }...)(&d) + require.Equal(t, 1, len(d.Options.Options)) + opt := d.Options.DHCP4oDHCP6Server() + require.Equal(t, OptionDHCP4oDHCP6Server, opt.Code()) + require.Equal(t, 2, len(opt.DHCP4oDHCP6Servers)) + require.Equal(t, net.ParseIP("fe80::1"), opt.DHCP4oDHCP6Servers[0]) + require.Equal(t, net.ParseIP("fe80::2"), opt.DHCP4oDHCP6Servers[1]) + require.NotEqual(t, net.ParseIP("fe80::1"), opt.DHCP4oDHCP6Servers[1]) +} diff --git a/dhcpv6/option_dhcpv4_msg.go b/dhcpv6/option_dhcpv4_msg.go new file mode 100644 index 0000000..0a1a2b3 --- /dev/null +++ b/dhcpv6/option_dhcpv4_msg.go @@ -0,0 +1,39 @@ +package dhcpv6 + +import ( + "fmt" + + "github.com/insomniacslk/dhcp/dhcpv4" +) + +// OptDHCPv4Msg represents a OptionDHCPv4Msg option +// +// This module defines the OptDHCPv4Msg structure. +// https://www.ietf.org/rfc/rfc7341.txt +type OptDHCPv4Msg struct { + Msg *dhcpv4.DHCPv4 +} + +// Code returns the option code +func (op *OptDHCPv4Msg) Code() OptionCode { + return OptionDHCPv4Msg +} + +// ToBytes returns the option serialized to bytes. +func (op *OptDHCPv4Msg) ToBytes() []byte { + return op.Msg.ToBytes() +} + +func (op *OptDHCPv4Msg) String() string { + return fmt.Sprintf("OptDHCPv4Msg{%v}", op.Msg) +} + +// ParseOptDHCPv4Msg builds an OptDHCPv4Msg structure +// from a sequence of bytes. The input data does not include option code and length +// bytes. +func ParseOptDHCPv4Msg(data []byte) (*OptDHCPv4Msg, error) { + var opt OptDHCPv4Msg + var err error + opt.Msg, err = dhcpv4.FromBytes(data) + return &opt, err +} diff --git a/dhcpv6/option_dhcpv4_msg_test.go b/dhcpv6/option_dhcpv4_msg_test.go new file mode 100644 index 0000000..1ffa17a --- /dev/null +++ b/dhcpv6/option_dhcpv4_msg_test.go @@ -0,0 +1,105 @@ +package dhcpv6 + +import ( + "bytes" + "net" + "testing" + + "github.com/insomniacslk/dhcp/dhcpv4" + "github.com/insomniacslk/dhcp/iana" + "github.com/stretchr/testify/require" +) + +var magicCookie = [4]byte{99, 130, 83, 99} + +func TestParseOptDHCPv4Msg(t *testing.T) { + data := []byte{ + 1, // dhcp request + 1, // ethernet hw type + 6, // hw addr length + 3, // hop count + 0xaa, 0xbb, 0xcc, 0xdd, // transaction ID, big endian (network) + 0, 3, // number of seconds + 0, 1, // broadcast + 0, 0, 0, 0, // client IP address + 0, 0, 0, 0, // your IP address + 0, 0, 0, 0, // server IP address + 0, 0, 0, 0, // gateway IP address + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // client MAC address + padding + } + + // server host name + expectedHostname := []byte{} + for i := 0; i < 64; i++ { + expectedHostname = append(expectedHostname, 0) + } + data = append(data, expectedHostname...) + // boot file name + expectedBootfilename := []byte{} + for i := 0; i < 128; i++ { + expectedBootfilename = append(expectedBootfilename, 0) + } + data = append(data, expectedBootfilename...) + // magic cookie, then no options + data = append(data, magicCookie[:]...) + + opt, err := ParseOptDHCPv4Msg(data) + d := opt.Msg + require.NoError(t, err) + require.Equal(t, d.OpCode, dhcpv4.OpcodeBootRequest) + require.Equal(t, d.HWType, iana.HWTypeEthernet) + require.Equal(t, d.HopCount, byte(3)) + require.Equal(t, d.TransactionID, dhcpv4.TransactionID{0xaa, 0xbb, 0xcc, 0xdd}) + require.Equal(t, d.NumSeconds, uint16(3)) + require.Equal(t, d.Flags, uint16(1)) + require.True(t, d.ClientIPAddr.Equal(net.IPv4zero)) + require.True(t, d.YourIPAddr.Equal(net.IPv4zero)) + require.True(t, d.GatewayIPAddr.Equal(net.IPv4zero)) + require.Equal(t, d.ClientHWAddr, net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}) + require.Equal(t, d.ServerHostName, "") + require.Equal(t, d.BootFileName, "") + // no need to check Magic Cookie as it is already validated in FromBytes + // above +} + +func TestOptDHCPv4MsgToBytes(t *testing.T) { + // the following bytes match what dhcpv4.New would create. Keep them in + // sync! + expected := []byte{ + 1, // Opcode BootRequest + 1, // HwType Ethernet + 6, // HwAddrLen + 0, // HopCount + 0x11, 0x22, 0x33, 0x44, // TransactionID + 0, 0, // NumSeconds + 0, 0, // Flags + 0, 0, 0, 0, // ClientIPAddr + 0, 0, 0, 0, // YourIPAddr + 0, 0, 0, 0, // ServerIPAddr + 0, 0, 0, 0, // GatewayIPAddr + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ClientHwAddr + } + // ServerHostName + expected = append(expected, bytes.Repeat([]byte{0}, 64)...) + // BootFileName + expected = append(expected, bytes.Repeat([]byte{0}, 128)...) + + // Magic Cookie + expected = append(expected, magicCookie[:]...) + + // Minimum message length padding. + // + // 236 + 4 byte cookie + 59 bytes padding + 1 byte end. + expected = append(expected, bytes.Repeat([]byte{0}, 59)...) + + // End + expected = append(expected, 0xff) + + d, err := dhcpv4.New() + require.NoError(t, err) + // fix TransactionID to match the expected one, since it's randomly + // generated in New() + d.TransactionID = dhcpv4.TransactionID{0x11, 0x22, 0x33, 0x44} + opt := OptDHCPv4Msg{Msg: d} + require.Equal(t, expected, opt.ToBytes()) +} diff --git a/dhcpv6/option_dhcpv4_o_dhcpv6_server.go b/dhcpv6/option_dhcpv4_o_dhcpv6_server.go new file mode 100644 index 0000000..a46ecac --- /dev/null +++ b/dhcpv6/option_dhcpv4_o_dhcpv6_server.go @@ -0,0 +1,46 @@ +package dhcpv6 + +import ( + "fmt" + "net" + + "github.com/u-root/u-root/pkg/uio" +) + +// OptDHCP4oDHCP6Server represents a OptionDHCP4oDHCP6Server option +// +// This module defines the OptDHCP4oDHCP6Server structure. +// https://www.ietf.org/rfc/rfc7341.txt +type OptDHCP4oDHCP6Server struct { + DHCP4oDHCP6Servers []net.IP +} + +// Code returns the option code +func (op *OptDHCP4oDHCP6Server) Code() OptionCode { + return OptionDHCP4oDHCP6Server +} + +// ToBytes returns the option serialized to bytes. +func (op *OptDHCP4oDHCP6Server) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + for _, addr := range op.DHCP4oDHCP6Servers { + buf.WriteBytes(addr.To16()) + } + return buf.Data() +} + +func (op *OptDHCP4oDHCP6Server) String() string { + return fmt.Sprintf("OptDHCP4oDHCP6Server{4o6-servers=%v}", op.DHCP4oDHCP6Servers) +} + +// ParseOptDHCP4oDHCP6Server builds an OptDHCP4oDHCP6Server structure +// from a sequence of bytes. The input data does not include option code and length +// bytes. +func ParseOptDHCP4oDHCP6Server(data []byte) (*OptDHCP4oDHCP6Server, error) { + var opt OptDHCP4oDHCP6Server + buf := uio.NewBigEndianBuffer(data) + for buf.Has(net.IPv6len) { + opt.DHCP4oDHCP6Servers = append(opt.DHCP4oDHCP6Servers, buf.CopyN(net.IPv6len)) + } + return &opt, buf.FinError() +} diff --git a/dhcpv6/option_dhcpv4_o_dhcpv6_server_test.go b/dhcpv6/option_dhcpv4_o_dhcpv6_server_test.go new file mode 100644 index 0000000..de86594 --- /dev/null +++ b/dhcpv6/option_dhcpv4_o_dhcpv6_server_test.go @@ -0,0 +1,55 @@ +package dhcpv6 + +import ( + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseOptDHCP4oDHCP6Server(t *testing.T) { + data := []byte{ + 0x2a, 0x03, 0x28, 0x80, 0xff, 0xfe, 0x00, 0x0c, 0xfa, 0xce, 0xb0, 0x0c, 0x00, 0x00, 0x00, 0x35, + } + expected := []net.IP{ + net.IP(data), + } + opt, err := ParseOptDHCP4oDHCP6Server(data) + require.NoError(t, err) + require.Equal(t, expected, opt.DHCP4oDHCP6Servers) + require.Equal(t, OptionDHCP4oDHCP6Server, opt.Code()) + require.Contains(t, opt.String(), "4o6-servers=[2a03:2880:fffe:c:face:b00c:0:35]", "String() should contain the correct DHCP4-over-DHCP6 server output") +} + +func TestOptDHCP4oDHCP6ServerToBytes(t *testing.T) { + ip1 := net.ParseIP("2a03:2880:fffe:c:face:b00c:0:35") + ip2 := net.ParseIP("2001:4860:4860::8888") + servers := []net.IP{ip1, ip2} + expected := append([]byte{}, []byte(ip1)...) + expected = append(expected, []byte(ip2)...) + opt := OptDHCP4oDHCP6Server{DHCP4oDHCP6Servers: servers} + require.Equal(t, expected, opt.ToBytes()) +} + +func TestParseOptDHCP4oDHCP6ServerParseNoAddr(t *testing.T) { + data := []byte{ + } + var expected []net.IP + opt, err := ParseOptDHCP4oDHCP6Server(data) + require.NoError(t, err) + require.Equal(t, expected, opt.DHCP4oDHCP6Servers) +} + +func TestOptDHCP4oDHCP6ServerToBytesNoAddr(t *testing.T) { + expected := []byte(nil) + opt := OptDHCP4oDHCP6Server{} + require.Equal(t, expected, opt.ToBytes()) +} + +func TestParseOptDHCP4oDHCP6ServerParseBogus(t *testing.T) { + data := []byte{ + 0x2a, 0x03, 0x28, 0x80, 0xff, 0xfe, 0x00, 0x0c, // invalid IPv6 address + } + _, err := ParseOptDHCP4oDHCP6Server(data) + require.Error(t, err, "An invalid IPv6 address should return an error") +} diff --git a/dhcpv6/option_iaaddress.go b/dhcpv6/option_iaaddress.go index c4184e1..9752ee7 100644 --- a/dhcpv6/option_iaaddress.go +++ b/dhcpv6/option_iaaddress.go @@ -8,6 +8,26 @@ import ( "github.com/u-root/u-root/pkg/uio" ) +// AddressOptions are options valid for the IAAddress option field. +// +// RFC 8415 Appendix C lists only the Status Code option as valid. +type AddressOptions struct { + Options +} + +// Status returns the status code associated with this option. +func (ao AddressOptions) Status() *OptStatusCode { + opt := ao.Options.GetOne(OptionStatusCode) + if opt == nil { + return nil + } + sc, ok := opt.(*OptStatusCode) + if !ok { + return nil + } + return sc +} + // OptIAAddress represents an OptionIAAddr. // // This module defines the OptIAAddress structure. @@ -16,7 +36,7 @@ type OptIAAddress struct { IPv6Addr net.IP PreferredLifetime time.Duration ValidLifetime time.Duration - Options Options + Options AddressOptions } // Code returns the option's code @@ -39,7 +59,7 @@ func (op *OptIAAddress) ToBytes() []byte { } func (op *OptIAAddress) String() string { - return fmt.Sprintf("OptIAAddress{ipv6addr=%v, preferredlifetime=%v, validlifetime=%v, options=%v}", + return fmt.Sprintf("IAAddress: IP=%v PreferredLifetime=%v ValidLifetime=%v Options=%v", op.IPv6Addr, op.PreferredLifetime, op.ValidLifetime, op.Options) } diff --git a/dhcpv6/option_iaaddress_test.go b/dhcpv6/option_iaaddress_test.go index 4db999c..900973c 100644 --- a/dhcpv6/option_iaaddress_test.go +++ b/dhcpv6/option_iaaddress_test.go @@ -54,9 +54,9 @@ func TestOptIAAddressToBytes(t *testing.T) { IPv6Addr: net.IP(ipBytes), PreferredLifetime: 0x0a0b0c0d * time.Second, ValidLifetime: 0x0e0f0102 * time.Second, - Options: []Option{ + Options: AddressOptions{[]Option{ OptElapsedTime(10 * time.Millisecond), - }, + }}, } require.Equal(t, expected, opt.ToBytes()) } @@ -74,17 +74,17 @@ func TestOptIAAddressString(t *testing.T) { str := opt.String() require.Contains( t, str, - "ipv6addr=2401:203:405:607:809:a0b:c0d:e0f", + "IP=2401:203:405:607:809:a0b:c0d:e0f", "String() should return the ipv6addr", ) require.Contains( t, str, - "preferredlifetime=1m10s", + "PreferredLifetime=1m10s", "String() should return the preferredlifetime", ) require.Contains( t, str, - "validlifetime=50s", + "ValidLifetime=50s", "String() should return the validlifetime", ) } diff --git a/dhcpv6/option_nontemporaryaddress.go b/dhcpv6/option_nontemporaryaddress.go index 3ea46fe..0b6012e 100644 --- a/dhcpv6/option_nontemporaryaddress.go +++ b/dhcpv6/option_nontemporaryaddress.go @@ -25,6 +25,45 @@ func (d *Duration) Unmarshal(buf *uio.Lexer) { d.Duration = time.Duration(t) * time.Second } +// IdentityOptions implement the options allowed for IA_NA and IA_TA messages. +// +// The allowed options are identified in RFC 3315 Appendix B. +type IdentityOptions struct { + Options +} + +// Addresses returns the addresses assigned to the identity. +func (io IdentityOptions) Addresses() []*OptIAAddress { + opts := io.Options.Get(OptionIAAddr) + var iaAddrs []*OptIAAddress + for _, o := range opts { + iaAddrs = append(iaAddrs, o.(*OptIAAddress)) + } + return iaAddrs +} + +// OneAddress returns one address (of potentially many) assigned to the identity. +func (io IdentityOptions) OneAddress() *OptIAAddress { + a := io.Addresses() + if len(a) == 0 { + return nil + } + return a[0] +} + +// Status returns the status code associated with this option. +func (io IdentityOptions) Status() *OptStatusCode { + opt := io.Options.GetOne(OptionStatusCode) + if opt == nil { + return nil + } + sc, ok := opt.(*OptStatusCode) + if !ok { + return nil + } + return sc +} + // OptIANA implements the identity association for non-temporary addresses // option. // @@ -34,7 +73,7 @@ type OptIANA struct { IaId [4]byte T1 time.Duration T2 time.Duration - Options Options + Options IdentityOptions } func (op *OptIANA) Code() OptionCode { @@ -58,22 +97,6 @@ func (op *OptIANA) String() string { op.IaId, op.T1, op.T2, op.Options) } -// AddOption adds an option at the end of the IA_NA options -func (op *OptIANA) AddOption(opt Option) { - op.Options.Add(opt) -} - -// GetOneOption will get an option of the give type from the Options field, if -// it is present. It will return `nil` otherwise -func (op *OptIANA) GetOneOption(code OptionCode) Option { - return op.Options.GetOne(code) -} - -// DelOption will remove all the options that match a Option code. -func (op *OptIANA) DelOption(code OptionCode) { - op.Options.Del(code) -} - // ParseOptIANA builds 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) { diff --git a/dhcpv6/option_nontemporaryaddress_test.go b/dhcpv6/option_nontemporaryaddress_test.go index ceee9ec..50cb11b 100644 --- a/dhcpv6/option_nontemporaryaddress_test.go +++ b/dhcpv6/option_nontemporaryaddress_test.go @@ -46,16 +46,16 @@ func TestOptIANAGetOneOption(t *testing.T) { IPv6Addr: net.ParseIP("::1"), } opt := OptIANA{ - Options: []Option{OptElapsedTime(0), oaddr}, + Options: IdentityOptions{[]Option{&OptStatusCode{}, oaddr}}, } - require.Equal(t, oaddr, opt.GetOneOption(OptionIAAddr)) + require.Equal(t, oaddr, opt.Options.OneAddress()) } func TestOptIANAAddOption(t *testing.T) { opt := OptIANA{} - opt.AddOption(OptElapsedTime(0)) - require.Equal(t, 1, len(opt.Options)) - require.Equal(t, OptionElapsedTime, opt.Options[0].Code()) + opt.Options.Add(OptElapsedTime(0)) + require.Equal(t, 1, len(opt.Options.Options)) + require.Equal(t, OptionElapsedTime, opt.Options.Options[0].Code()) } func TestOptIANAGetOneOptionMissingOpt(t *testing.T) { @@ -63,28 +63,34 @@ func TestOptIANAGetOneOptionMissingOpt(t *testing.T) { IPv6Addr: net.ParseIP("::1"), } opt := OptIANA{ - Options: []Option{OptElapsedTime(0), oaddr}, + Options: IdentityOptions{[]Option{&OptStatusCode{}, oaddr}}, } - require.Equal(t, nil, opt.GetOneOption(OptionDNSRecursiveNameServer)) + require.Equal(t, nil, opt.Options.GetOne(OptionDNSRecursiveNameServer)) } func TestOptIANADelOption(t *testing.T) { - optiana1 := OptIANA{} - optiana2 := OptIANA{} optiaaddr := OptIAAddress{} optsc := OptStatusCode{} - optiana1.Options = append(optiana1.Options, &optsc) - optiana1.Options = append(optiana1.Options, &optiaaddr) - optiana1.Options = append(optiana1.Options, &optiaaddr) - optiana1.DelOption(OptionIAAddr) - require.Equal(t, optiana1.Options, Options{&optsc}) + iana1 := OptIANA{ + Options: IdentityOptions{[]Option{ + &optsc, + &optiaaddr, + &optiaaddr, + }}, + } + iana1.Options.Del(OptionIAAddr) + require.Equal(t, iana1.Options.Options, Options{&optsc}) - optiana2.Options = append(optiana2.Options, &optiaaddr) - optiana2.Options = append(optiana2.Options, &optsc) - optiana2.Options = append(optiana2.Options, &optiaaddr) - optiana2.DelOption(OptionIAAddr) - require.Equal(t, optiana2.Options, Options{&optsc}) + iana2 := OptIANA{ + Options: IdentityOptions{[]Option{ + &optiaaddr, + &optsc, + &optiaaddr, + }}, + } + iana2.Options.Del(OptionIAAddr) + require.Equal(t, iana2.Options.Options, Options{&optsc}) } func TestOptIANAToBytes(t *testing.T) { @@ -92,9 +98,9 @@ func TestOptIANAToBytes(t *testing.T) { IaId: [4]byte{1, 2, 3, 4}, T1: 12345 * time.Second, T2: 54321 * time.Second, - Options: []Option{ + Options: IdentityOptions{[]Option{ OptElapsedTime(10 * time.Millisecond), - }, + }}, } expected := []byte{ 1, 2, 3, 4, // IA ID @@ -128,7 +134,7 @@ func TestOptIANAString(t *testing.T) { ) require.Contains( t, str, - "options=[", + "options={", "String() should return a list of options", ) } diff --git a/dhcpv6/options.go b/dhcpv6/options.go index 9b76f6d..87a33ff 100644 --- a/dhcpv6/options.go +++ b/dhcpv6/options.go @@ -87,6 +87,10 @@ func ParseOption(code OptionCode, optData []byte) (Option, error) { var o OptNetworkInterfaceID err = o.FromBytes(optData) opt = &o + case OptionDHCPv4Msg: + opt, err = ParseOptDHCPv4Msg(optData) + case OptionDHCP4oDHCP6Server: + opt, err = ParseOptDHCP4oDHCP6Server(optData) case Option4RD: opt, err = ParseOpt4RD(optData) case Option4RDMapRule: diff --git a/dhcpv6/types.go b/dhcpv6/types.go index 560581c..7c4052f 100644 --- a/dhcpv6/types.go +++ b/dhcpv6/types.go @@ -36,6 +36,10 @@ const ( MessageTypeLeaseQueryReply MessageType = 15 MessageTypeLeaseQueryDone MessageType = 16 MessageTypeLeaseQueryData MessageType = 17 + _ MessageType = 18 + _ MessageType = 19 + MessageTypeDHCPv4Query MessageType = 20 + MessageTypeDHCPv4Response MessageType = 21 ) // String prints the message type name. @@ -66,6 +70,8 @@ var messageTypeToStringMap = map[MessageType]string{ MessageTypeLeaseQueryReply: "LEASEQUERY-REPLY", MessageTypeLeaseQueryDone: "LEASEQUERY-DONE", MessageTypeLeaseQueryData: "LEASEQUERY-DATA", + MessageTypeDHCPv4Query: "DHCPv4-QUERY", + MessageTypeDHCPv4Response: "DHCPv4-RESPONSE", } // OptionCode is a single byte representing the code for a given Option. |