Skip to content

Commit

Permalink
Merge branch 'master' into exponential-backoff
Browse files Browse the repository at this point in the history
  • Loading branch information
pmazzini authored Oct 25, 2021
2 parents 44f475c + b259fbf commit 1a30de3
Show file tree
Hide file tree
Showing 15 changed files with 432 additions and 10 deletions.
4 changes: 2 additions & 2 deletions dhcpv4/client4/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,15 +314,15 @@ 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 {
continue
}
// 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 {
Expand Down
2 changes: 2 additions & 0 deletions dhcpv4/option_relay_agent_information.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -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",
}
34 changes: 33 additions & 1 deletion dhcpv4/ztpv4/ztp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package ztpv4
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"

"github.com/insomniacslk/dhcp/dhcpv4"
Expand All @@ -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{}
Expand Down Expand Up @@ -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
}

Expand Down
19 changes: 19 additions & 0 deletions dhcpv4/ztpv4/ztp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func TestParseClassIdentifier(t *testing.T) {
tt := []struct {
name string
vc, hostname string
ci []byte // Client Identifier
want *VendorData
fail bool
}{
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
28 changes: 27 additions & 1 deletion dhcpv6/dhcpv6message.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions dhcpv6/modifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
22 changes: 22 additions & 0 deletions dhcpv6/modifiers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"log"
"net"
"testing"
"time"

"github.com/insomniacslk/dhcp/iana"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -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)
}
155 changes: 155 additions & 0 deletions dhcpv6/option_ntp_server.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading

0 comments on commit 1a30de3

Please sign in to comment.