diff --git a/dhcpv4/client4/client.go b/dhcpv4/client4/client.go index 5c5c18d1..f2747147 100644 --- a/dhcpv4/client4/client.go +++ b/dhcpv4/client4/client.go @@ -314,7 +314,7 @@ func (c *Client) SendReceive(sendFd, recvFd int, packet *dhcpv4.DHCPv4, messageT } dstPort := int(binary.BigEndian.Uint16(udph[2:4])) expectedDstPort := dhcpv4.ClientPort - if c.RemoteAddr != nil { + if c.LocalAddr != nil { expectedDstPort = c.LocalAddr.(*net.UDPAddr).Port } if dstPort != expectedDstPort { @@ -322,7 +322,7 @@ func (c *Client) SendReceive(sendFd, recvFd int, packet *dhcpv4.DHCPv4, messageT } // UDP checksum is not checked pLen := int(binary.BigEndian.Uint16(udph[4:6])) - payload := buf[iph.Len+8 : iph.Len+8+pLen] + payload := buf[iph.Len+8 : iph.Len+pLen] response, innerErr = dhcpv4.FromBytes(payload) if innerErr != nil { diff --git a/dhcpv4/option_relay_agent_information.go b/dhcpv4/option_relay_agent_information.go index a338902f..4f974dd9 100644 --- a/dhcpv4/option_relay_agent_information.go +++ b/dhcpv4/option_relay_agent_information.go @@ -70,6 +70,7 @@ const ( VendorSpecificInformationSubOption raiSubOptionCode = 9 // RFC 4243 RelayAgentFlagsSubOption raiSubOptionCode = 10 // RFC 5010 ServerIdentifierOverrideSubOption raiSubOptionCode = 11 // RFC 5107 + RelaySourcePortSubOption raiSubOptionCode = 19 // RFC 8357 VirtualSubnetSelectionSubOption raiSubOptionCode = 151 // RFC 6607 VirtualSubnetSelectionControlSubOption raiSubOptionCode = 152 // RFC 6607 ) @@ -85,6 +86,7 @@ var raiSubOptionCodeToString = map[raiSubOptionCode]string{ VendorSpecificInformationSubOption: "Vendor Specific Sub-option", RelayAgentFlagsSubOption: "Relay Agent Flags Sub-option", ServerIdentifierOverrideSubOption: "Server Identifier Override Sub-option", + RelaySourcePortSubOption: "Relay Source Port Sub-option", VirtualSubnetSelectionSubOption: "Virtual Subnet Selection Sub-option", VirtualSubnetSelectionControlSubOption: "Virtual Subnet Selection Control Sub-option", } diff --git a/dhcpv4/ztpv4/ztp.go b/dhcpv4/ztpv4/ztp.go index 43bc0e67..c57d958c 100644 --- a/dhcpv4/ztpv4/ztp.go +++ b/dhcpv4/ztpv4/ztp.go @@ -3,6 +3,8 @@ package ztpv4 import ( "bytes" "errors" + "fmt" + "strconv" "strings" "github.com/insomniacslk/dhcp/dhcpv4" @@ -16,6 +18,7 @@ type VendorData struct { } var errVendorOptionMalformed = errors.New("malformed vendor option") +var errClientIDOptionMissing = errors.New("client identifier option is missing") func parseClassIdentifier(packet *dhcpv4.DHCPv4) (*VendorData, error) { vd := &VendorData{} @@ -65,8 +68,37 @@ func parseClassIdentifier(packet *dhcpv4.DHCPv4) (*VendorData, error) { vd.VendorName = p[0] return vd, nil - } + // For Ciena the class identifier (opt 60) is written in the following format: + // {vendor iana code}-{product}-{type} + // For Ciena the iana code is 1271 + // The product type is a number that maps to a Ciena product + // The type is used to identified different subtype of the product. + // An example can be ‘1271-23422Z11-123’. + case strings.HasPrefix(vc, strconv.Itoa(int(iana.EntIDCienaCorporation))): + v := strings.Split(vc, "-") + if len(v) != 3 { + return nil, fmt.Errorf("%w got '%s'", errVendorOptionMalformed, vc) + } + vd.VendorName = iana.EntIDCienaCorporation.String() + vd.Model = v[1] + "-" + v[2] + vd.Serial = dhcpv4.GetString(dhcpv4.OptionClientIdentifier, packet.Options) + if len(vd.Serial) == 0 { + return nil, errClientIDOptionMissing + } + return vd, nil + + // Cisco Firepower FPR4100/9300 models use Opt 60 for model info + // and Opt 61 contains the serial number + case vc == "FPR4100" || vc == "FPR9300": + vd.VendorName = iana.EntIDCiscoSystems.String() + vd.Model = vc + vd.Serial = dhcpv4.GetString(dhcpv4.OptionClientIdentifier, packet.Options) + if len(vd.Serial) == 0 { + return nil, errClientIDOptionMissing + } + return vd, nil + } return nil, nil } diff --git a/dhcpv4/ztpv4/ztp_test.go b/dhcpv4/ztpv4/ztp_test.go index d0b002f7..bee22305 100644 --- a/dhcpv4/ztpv4/ztp_test.go +++ b/dhcpv4/ztpv4/ztp_test.go @@ -12,6 +12,7 @@ func TestParseClassIdentifier(t *testing.T) { tt := []struct { name string vc, hostname string + ci []byte // Client Identifier want *VendorData fail bool }{ @@ -45,6 +46,21 @@ func TestParseClassIdentifier(t *testing.T) { vc: "ZPESystems:NSC:001234567", want: &VendorData{VendorName: "ZPESystems", Model: "NSC", Serial: "001234567"}, }, + { + name: "cisco", + vc: "FPR4100", + ci: []byte("JMX2525X0BW"), + want: &VendorData{VendorName: "Cisco Systems", Model: "FPR4100", Serial: "JMX2525X0BW"}, + }, + {name: "ciscoNoSerial", vc: "FPR4100", fail: true}, + { + name: "ciena", + vc: "1271-00011E00-032", + ci: []byte("JUSTASN"), + want: &VendorData{VendorName: "Ciena Corporation", Model: "00011E00-032", Serial: "JUSTASN"}, + }, + {name: "cienaInvalidVendorClass", vc: "127100011E00032", fail: true}, + {name: "cienaNoSerial", vc: "1271-00011E00-032", fail: true}, } for _, tc := range tt { @@ -60,6 +76,9 @@ func TestParseClassIdentifier(t *testing.T) { if tc.hostname != "" { packet.UpdateOption(dhcpv4.OptHostName(tc.hostname)) } + if tc.ci != nil { + packet.UpdateOption(dhcpv4.OptClientIdentifier(tc.ci)) + } vd, err := ParseVendorData(packet) if tc.fail { diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go index e18fd590..e2210fd6 100644 --- a/dhcpv6/dhcpv6message.go +++ b/dhcpv6/dhcpv6message.go @@ -69,7 +69,7 @@ func (mo MessageOptions) OneIANA() *OptIANA { // IATA returns all Identity Association for Temporary Address options. func (mo MessageOptions) IATA() []*OptIATA { - opts := mo.Get(OptionIANA) + opts := mo.Get(OptionIATA) var iatas []*OptIATA for _, o := range opts { iatas = append(iatas, o.(*OptIATA)) @@ -288,6 +288,32 @@ func (mo MessageOptions) DHCP4oDHCP6Server() *OptDHCP4oDHCP6Server { return nil } +// NTPServers returns the NTP server addresses contained in the +// NTP_SUBOPTION_SRV_ADDR of an OPTION_NTP_SERVER. +// If multiple NTP server options exist, the function will return all the NTP +// server addresses it finds, as defined by RFC 5908. +func (mo MessageOptions) NTPServers() []net.IP { + opts := mo.Options.Get(OptionNTPServer) + if opts == nil { + return nil + } + addrs := make([]net.IP, 0) + for _, opt := range opts { + ntp, ok := opt.(*OptNTPServer) + if !ok { + continue + } + for _, subopt := range ntp.Suboptions { + so, ok := subopt.(*NTPSuboptionSrvAddr) + if !ok { + continue + } + addrs = append(addrs, net.IP(*so)) + } + } + return addrs +} + // 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 8c708d9a..1d8c49f5 100644 --- a/dhcpv6/modifiers.go +++ b/dhcpv6/modifiers.go @@ -91,15 +91,17 @@ func WithIAID(iaid [4]byte) Modifier { } } -// WithIATA adds or updates an OptIANA option with the provided IAAddress -// options -func WithIATA(addrs ...OptIAAddress) Modifier { +// WithIATA adds or updates an OptIATA option with the provided IAID, +// and IAAddress options +func WithIATA(iaid [4]byte, addrs ...OptIAAddress) Modifier { return func(d DHCPv6) { if msg, ok := d.(*Message); ok { iata := msg.Options.OneIATA() if iata == nil { iata = &OptIATA{} } + copy(iata.IaId[:], iaid[:]) + for _, addr := range addrs { iata.Options.Add(&addr) } diff --git a/dhcpv6/modifiers_test.go b/dhcpv6/modifiers_test.go index 3c3204d9..1d480864 100644 --- a/dhcpv6/modifiers_test.go +++ b/dhcpv6/modifiers_test.go @@ -4,6 +4,7 @@ import ( "log" "net" "testing" + "time" "github.com/insomniacslk/dhcp/iana" "github.com/stretchr/testify/require" @@ -134,3 +135,24 @@ func TestWithClientLinkLayerAddress(t *testing.T) { require.Equal(t, iana.HWTypeEthernet, llt) require.Equal(t, mac, lla) } + +func TestWithIATA(t *testing.T) { + var d Message + WithIATA([4]byte{1, 2, 3, 4}, OptIAAddress{ + IPv6Addr: net.ParseIP("2001:DB8:7689::1234"), + PreferredLifetime: 3600, + ValidLifetime: 5200, + })(&d) + require.Equal(t, 1, len(d.Options.Options)) + + iata := d.Options.OneIATA() + iataOpts := iata.Options.Get(OptionIAAddr) + iaAddr := iataOpts[0].(*OptIAAddress) + + require.Equal(t, OptionIATA, iata.Code()) + require.Equal(t, [4]byte{1, 2, 3, 4}, iata.IaId) + require.Equal(t, net.ParseIP("2001:DB8:7689::1234"), + iaAddr.IPv6Addr) + require.Equal(t, time.Duration(3600), iaAddr.PreferredLifetime) + require.Equal(t, time.Duration(5200), iaAddr.ValidLifetime) +} diff --git a/dhcpv6/option_ntp_server.go b/dhcpv6/option_ntp_server.go new file mode 100644 index 00000000..a7aafb76 --- /dev/null +++ b/dhcpv6/option_ntp_server.go @@ -0,0 +1,155 @@ +package dhcpv6 + +import ( + "fmt" + "net" + + "github.com/insomniacslk/dhcp/rfc1035label" + "github.com/u-root/uio/uio" +) + +// NTPSuboptionSrvAddr is NTP_SUBOPTION_SRV_ADDR according to RFC 5908. +type NTPSuboptionSrvAddr net.IP + +// Code returns the suboption code. +func (n *NTPSuboptionSrvAddr) Code() OptionCode { + return NTPSuboptionSrvAddrCode +} + +// ToBytes returns the byte serialization of the suboption. +func (n *NTPSuboptionSrvAddr) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + buf.Write16(uint16(NTPSuboptionSrvAddrCode)) + buf.Write16(uint16(net.IPv6len)) + buf.WriteBytes(net.IP(*n).To16()) + return buf.Data() +} + +func (n *NTPSuboptionSrvAddr) String() string { + return fmt.Sprintf("Server Address: %s", net.IP(*n).String()) +} + +// NTPSuboptionMCAddr is NTP_SUBOPTION_MC_ADDR according to RFC 5908. +type NTPSuboptionMCAddr net.IP + +// Code returns the suboption code. +func (n *NTPSuboptionMCAddr) Code() OptionCode { + return NTPSuboptionMCAddrCode +} + +// ToBytes returns the byte serialization of the suboption. +func (n *NTPSuboptionMCAddr) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + buf.Write16(uint16(NTPSuboptionMCAddrCode)) + buf.Write16(uint16(net.IPv6len)) + buf.WriteBytes(net.IP(*n).To16()) + return buf.Data() +} + +func (n *NTPSuboptionMCAddr) String() string { + return fmt.Sprintf("Multicast Address: %s", net.IP(*n).String()) +} + +// NTPSuboptionSrvFQDN is NTP_SUBOPTION_SRV_FQDN according to RFC 5908. +type NTPSuboptionSrvFQDN rfc1035label.Labels + +// Code returns the suboption code. +func (n *NTPSuboptionSrvFQDN) Code() OptionCode { + return NTPSuboptionSrvFQDNCode +} + +// ToBytes returns the byte serialization of the suboption. +func (n *NTPSuboptionSrvFQDN) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + buf.Write16(uint16(NTPSuboptionSrvFQDNCode)) + l := rfc1035label.Labels(*n) + buf.Write16(uint16(l.Length())) + buf.WriteBytes(l.ToBytes()) + return buf.Data() +} + +func (n *NTPSuboptionSrvFQDN) String() string { + l := rfc1035label.Labels(*n) + return fmt.Sprintf("Server FQDN: %s", l.String()) +} + +// NTPSuboptionSrvAddr is the value of NTP_SUBOPTION_SRV_ADDR according to RFC 5908. +const ( + NTPSuboptionSrvAddrCode = OptionCode(1) + NTPSuboptionMCAddrCode = OptionCode(2) + NTPSuboptionSrvFQDNCode = OptionCode(3) +) + +// parseNTPSuboption implements the OptionParser interface. +func parseNTPSuboption(code OptionCode, data []byte) (Option, error) { + //var o Options + buf := uio.NewBigEndianBuffer(data) + length := len(data) + data, err := buf.ReadN(length) + if err != nil { + return nil, fmt.Errorf("failed to read %d bytes for suboption: %w", length, err) + } + switch code { + case NTPSuboptionSrvAddrCode, NTPSuboptionMCAddrCode: + if length != net.IPv6len { + return nil, fmt.Errorf("invalid suboption length, want %d, got %d", net.IPv6len, length) + } + var so Option + switch code { + case NTPSuboptionSrvAddrCode: + sos := NTPSuboptionSrvAddr(data) + so = &sos + case NTPSuboptionMCAddrCode: + som := NTPSuboptionMCAddr(data) + so = &som + } + return so, nil + case NTPSuboptionSrvFQDNCode: + l, err := rfc1035label.FromBytes(data) + if err != nil { + return nil, fmt.Errorf("failed to parse rfc1035 labels: %w", err) + } + // TODO according to rfc3315, this label must not be compressed. + // Need to add support for compression detection to the + // `rfc1035label` package in order to do that. + so := NTPSuboptionSrvFQDN(*l) + return &so, nil + default: + gopt := OptionGeneric{OptionCode: code, OptionData: data} + return &gopt, nil + } +} + +// ParseOptNTPServer parses a sequence of bytes into an OptNTPServer object. +func ParseOptNTPServer(data []byte) (*OptNTPServer, error) { + var so Options + if err := so.FromBytesWithParser(data, parseNTPSuboption); err != nil { + return nil, err + } + return &OptNTPServer{ + Suboptions: so, + }, nil +} + +// OptNTPServer is an option NTP server as defined by RFC 5908. +type OptNTPServer struct { + Suboptions Options +} + +// Code returns the option code +func (op *OptNTPServer) Code() OptionCode { + return OptionNTPServer +} + +// ToBytes returns the option serialized to bytes. +func (op *OptNTPServer) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + for _, so := range op.Suboptions { + buf.WriteBytes(so.ToBytes()) + } + return buf.Data() +} + +func (op *OptNTPServer) String() string { + return fmt.Sprintf("NTP: %v", op.Suboptions) +} diff --git a/dhcpv6/option_ntp_server_test.go b/dhcpv6/option_ntp_server_test.go new file mode 100644 index 00000000..105a753e --- /dev/null +++ b/dhcpv6/option_ntp_server_test.go @@ -0,0 +1,88 @@ +package dhcpv6 + +import ( + "net" + "testing" + + "github.com/insomniacslk/dhcp/rfc1035label" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSuboptionSrvAddr(t *testing.T) { + ip := net.ParseIP("2a03:2880:fffe:c:face:b00c:0:35") + so := NTPSuboptionSrvAddr(ip) + assert.Equal(t, NTPSuboptionSrvAddrCode, so.Code()) + expected := append([]byte{0x00, 0x01, 0x00, 0x10}, ip...) + assert.Equal(t, expected, so.ToBytes()) +} + +func TestSuboptionMCAddr(t *testing.T) { + ip := net.ParseIP("2a03:2880:fffe:c:face:b00c:0:35") + so := NTPSuboptionMCAddr(ip) + assert.Equal(t, NTPSuboptionMCAddrCode, so.Code()) + expected := append([]byte{0x00, 0x02, 0x00, 0x10}, ip...) + assert.Equal(t, expected, so.ToBytes()) +} + +func TestSuboptionSrvFQDN(t *testing.T) { + fqdn, err := rfc1035label.FromBytes([]byte("\x03ntp\x07example\x03com")) + require.NoError(t, err) + so := NTPSuboptionSrvFQDN(*fqdn) + assert.Equal(t, NTPSuboptionSrvFQDNCode, so.Code()) + expected := append([]byte{0x00, 0x03, 0x00, 0x10}, fqdn.ToBytes()...) + assert.Equal(t, expected, so.ToBytes()) +} + +func TestSuboptionGeneric(t *testing.T) { + data := []byte{ + 0xff, 0xff, // unknown sub-option type + 0x00, 0x04, // length, 4 bytes + 0x74, 0x65, 0x73, 0x74, // the ASCII bytes for the string "test" + } + o, err := ParseOptNTPServer(data) + require.NoError(t, err) + require.Equal(t, 1, len(o.Suboptions)) + assert.IsType(t, &OptionGeneric{}, o.Suboptions[0]) + og := o.Suboptions[0].(*OptionGeneric) + assert.Equal(t, []byte("test"), og.ToBytes()) +} + +func TestParseOptNTPServer(t *testing.T) { + ip := net.ParseIP("2a03:2880:fffe:c:face:b00c:0:35") + fqdn, err := rfc1035label.FromBytes([]byte("\x03ntp\x07example\x03com")) + require.NoError(t, err) + + // add server address sub-option + data := []byte{ + 0x00, 0x01, // sub-option type + 0x00, 0x10, // length (16, IPv6 address) + } + data = append(data, []byte(ip)...) + + // add server FQDN sub-option + data = append(data, []byte{ + 0x00, 0x03, // sub-option type + 0x00, 0x10, // length (16, the FQDN "ntp.example.com." as rfc1035 label) + }...) + data = append(data, fqdn.ToBytes()...) + + o, err := ParseOptNTPServer(data) + require.NoError(t, err) + require.NotNil(t, o) + assert.Equal(t, 2, len(o.Suboptions)) + + optAddr, ok := o.Suboptions[0].(*NTPSuboptionSrvAddr) + require.True(t, ok) + assert.Equal(t, ip, net.IP(*optAddr)) + + optFQDN, ok := o.Suboptions[1].(*NTPSuboptionSrvFQDN) + require.True(t, ok) + assert.Equal(t, *fqdn, rfc1035label.Labels(*optFQDN)) + + var mo MessageOptions + assert.Nil(t, mo.NTPServers()) + mo.Add(o) + // MessageOptions.NTPServers only returns server address values. + assert.Equal(t, []net.IP{ip}, mo.NTPServers()) +} diff --git a/dhcpv6/option_relayport.go b/dhcpv6/option_relayport.go new file mode 100644 index 00000000..fd51948f --- /dev/null +++ b/dhcpv6/option_relayport.go @@ -0,0 +1,42 @@ +// This module defines the optRelayPort structure. +// https://www.ietf.org/rfc/rfc8357.txt + +package dhcpv6 + +import ( + "fmt" + + "github.com/u-root/uio/uio" +) + +// OptRelayPort specifies an UDP port to use for the downstream relay +func OptRelayPort(port uint16) Option { + return &optRelayPort{DownstreamSourcePort: port} +} + +type optRelayPort struct { + DownstreamSourcePort uint16 +} + +func (op *optRelayPort) Code() OptionCode { + return OptionRelayPort +} + +func (op *optRelayPort) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + buf.Write16(op.DownstreamSourcePort) + return buf.Data() +} + +func (op *optRelayPort) String() string { + return fmt.Sprintf("RelayPort: %d", op.DownstreamSourcePort) +} + +// build an optRelayPort structure from a sequence of bytes. +// The input data does not include option code and length bytes. +func parseOptRelayPort(data []byte) (*optRelayPort, error) { + var opt optRelayPort + buf := uio.NewBigEndianBuffer(data) + opt.DownstreamSourcePort = buf.Read16() + return &opt, buf.FinError() +} diff --git a/dhcpv6/option_relayport_test.go b/dhcpv6/option_relayport_test.go new file mode 100644 index 00000000..124af62a --- /dev/null +++ b/dhcpv6/option_relayport_test.go @@ -0,0 +1,18 @@ +package dhcpv6 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseRelayPort(t *testing.T) { + opt, err := parseOptRelayPort([]byte{0x12, 0x32}) + require.NoError(t, err) + require.Equal(t, &optRelayPort{DownstreamSourcePort: 0x1232}, opt) +} + +func TestRelayPortToBytes(t *testing.T) { + op := OptRelayPort(0x3845) + require.Equal(t, []byte{0x38, 0x45}, op.ToBytes()) +} diff --git a/dhcpv6/options.go b/dhcpv6/options.go index 16e2d8c0..f8445bb4 100644 --- a/dhcpv6/options.go +++ b/dhcpv6/options.go @@ -81,6 +81,8 @@ func ParseOption(code OptionCode, optData []byte) (Option, error) { opt, err = ParseOptRemoteID(optData) case OptionFQDN: opt, err = ParseOptFQDN(optData) + case OptionNTPServer: + opt, err = ParseOptNTPServer(optData) case OptionBootfileURL: opt, err = parseOptBootFileURL(optData) case OptionBootfileParam: @@ -103,6 +105,8 @@ func ParseOption(code OptionCode, optData []byte) (Option, error) { opt, err = ParseOpt4RDMapRule(optData) case Option4RDNonMapRule: opt, err = ParseOpt4RDNonMapRule(optData) + case OptionRelayPort: + opt, err = parseOptRelayPort(optData) default: opt = &OptionGeneric{OptionCode: code, OptionData: optData} } diff --git a/iana/entid.go b/iana/entid.go index df2ca13a..dbcb51e2 100644 --- a/iana/entid.go +++ b/iana/entid.go @@ -5,11 +5,13 @@ type EntID int // See https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers for values const ( - EntIDCiscoSystems EntID = 9 + EntIDCiscoSystems EntID = 9 + EntIDCienaCorporation EntID = 1271 ) var entIDToStringMap = map[EntID]string{ - EntIDCiscoSystems: "Cisco Systems", + EntIDCiscoSystems: "Cisco Systems", + EntIDCienaCorporation: "Ciena Corporation", } // String returns the vendor name for a given Enterprise ID diff --git a/interfaces/bindtodevice_bsd.go b/interfaces/bindtodevice_bsd.go index a0cc0b5d..b79c4485 100644 --- a/interfaces/bindtodevice_bsd.go +++ b/interfaces/bindtodevice_bsd.go @@ -15,5 +15,5 @@ func BindToInterface(fd int, ifname string) error { if err != nil { return err } - return syscall.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_RECVIF, iface.Index) + return unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_RECVIF, iface.Index) } diff --git a/netboot/netconf.go b/netboot/netconf.go index 85cfafb2..698c0d19 100644 --- a/netboot/netconf.go +++ b/netboot/netconf.go @@ -30,6 +30,7 @@ type NetConf struct { DNSServers []net.IP DNSSearchList []string Routers []net.IP + NTPServers []net.IP } // GetNetConfFromPacketv6 extracts network configuration information from a DHCPv6 @@ -63,10 +64,15 @@ func GetNetConfFromPacketv6(d *dhcpv6.Message) (*NetConf, error) { // get DNS configuration netconf.DNSServers = d.Options.DNS() + // get domain search list domains := d.Options.DomainSearchList() if domains != nil { netconf.DNSSearchList = domains.Labels } + + // get NTP servers + netconf.NTPServers = d.Options.NTPServers() + return &netconf, nil } @@ -123,6 +129,10 @@ func GetNetConfFromPacketv4(d *dhcpv4.DHCPv4) (*NetConf, error) { return nil, errors.New("no routers specified in the corresponding option") } netconf.Routers = routersList + + // get NTP servers + netconf.NTPServers = d.NTPServers() + return &netconf, nil }