Skip to content

Commit 1a30de3

Browse files
authored
Merge branch 'master' into exponential-backoff
2 parents 44f475c + b259fbf commit 1a30de3

15 files changed

+432
-10
lines changed

dhcpv4/client4/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,15 @@ func (c *Client) SendReceive(sendFd, recvFd int, packet *dhcpv4.DHCPv4, messageT
314314
}
315315
dstPort := int(binary.BigEndian.Uint16(udph[2:4]))
316316
expectedDstPort := dhcpv4.ClientPort
317-
if c.RemoteAddr != nil {
317+
if c.LocalAddr != nil {
318318
expectedDstPort = c.LocalAddr.(*net.UDPAddr).Port
319319
}
320320
if dstPort != expectedDstPort {
321321
continue
322322
}
323323
// UDP checksum is not checked
324324
pLen := int(binary.BigEndian.Uint16(udph[4:6]))
325-
payload := buf[iph.Len+8 : iph.Len+8+pLen]
325+
payload := buf[iph.Len+8 : iph.Len+pLen]
326326

327327
response, innerErr = dhcpv4.FromBytes(payload)
328328
if innerErr != nil {

dhcpv4/option_relay_agent_information.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const (
7070
VendorSpecificInformationSubOption raiSubOptionCode = 9 // RFC 4243
7171
RelayAgentFlagsSubOption raiSubOptionCode = 10 // RFC 5010
7272
ServerIdentifierOverrideSubOption raiSubOptionCode = 11 // RFC 5107
73+
RelaySourcePortSubOption raiSubOptionCode = 19 // RFC 8357
7374
VirtualSubnetSelectionSubOption raiSubOptionCode = 151 // RFC 6607
7475
VirtualSubnetSelectionControlSubOption raiSubOptionCode = 152 // RFC 6607
7576
)
@@ -85,6 +86,7 @@ var raiSubOptionCodeToString = map[raiSubOptionCode]string{
8586
VendorSpecificInformationSubOption: "Vendor Specific Sub-option",
8687
RelayAgentFlagsSubOption: "Relay Agent Flags Sub-option",
8788
ServerIdentifierOverrideSubOption: "Server Identifier Override Sub-option",
89+
RelaySourcePortSubOption: "Relay Source Port Sub-option",
8890
VirtualSubnetSelectionSubOption: "Virtual Subnet Selection Sub-option",
8991
VirtualSubnetSelectionControlSubOption: "Virtual Subnet Selection Control Sub-option",
9092
}

dhcpv4/ztpv4/ztp.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package ztpv4
33
import (
44
"bytes"
55
"errors"
6+
"fmt"
7+
"strconv"
68
"strings"
79

810
"github.com/insomniacslk/dhcp/dhcpv4"
@@ -16,6 +18,7 @@ type VendorData struct {
1618
}
1719

1820
var errVendorOptionMalformed = errors.New("malformed vendor option")
21+
var errClientIDOptionMissing = errors.New("client identifier option is missing")
1922

2023
func parseClassIdentifier(packet *dhcpv4.DHCPv4) (*VendorData, error) {
2124
vd := &VendorData{}
@@ -65,8 +68,37 @@ func parseClassIdentifier(packet *dhcpv4.DHCPv4) (*VendorData, error) {
6568

6669
vd.VendorName = p[0]
6770
return vd, nil
68-
}
6971

72+
// For Ciena the class identifier (opt 60) is written in the following format:
73+
// {vendor iana code}-{product}-{type}
74+
// For Ciena the iana code is 1271
75+
// The product type is a number that maps to a Ciena product
76+
// The type is used to identified different subtype of the product.
77+
// An example can be ‘1271-23422Z11-123’.
78+
case strings.HasPrefix(vc, strconv.Itoa(int(iana.EntIDCienaCorporation))):
79+
v := strings.Split(vc, "-")
80+
if len(v) != 3 {
81+
return nil, fmt.Errorf("%w got '%s'", errVendorOptionMalformed, vc)
82+
}
83+
vd.VendorName = iana.EntIDCienaCorporation.String()
84+
vd.Model = v[1] + "-" + v[2]
85+
vd.Serial = dhcpv4.GetString(dhcpv4.OptionClientIdentifier, packet.Options)
86+
if len(vd.Serial) == 0 {
87+
return nil, errClientIDOptionMissing
88+
}
89+
return vd, nil
90+
91+
// Cisco Firepower FPR4100/9300 models use Opt 60 for model info
92+
// and Opt 61 contains the serial number
93+
case vc == "FPR4100" || vc == "FPR9300":
94+
vd.VendorName = iana.EntIDCiscoSystems.String()
95+
vd.Model = vc
96+
vd.Serial = dhcpv4.GetString(dhcpv4.OptionClientIdentifier, packet.Options)
97+
if len(vd.Serial) == 0 {
98+
return nil, errClientIDOptionMissing
99+
}
100+
return vd, nil
101+
}
70102
return nil, nil
71103
}
72104

dhcpv4/ztpv4/ztp_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ func TestParseClassIdentifier(t *testing.T) {
1212
tt := []struct {
1313
name string
1414
vc, hostname string
15+
ci []byte // Client Identifier
1516
want *VendorData
1617
fail bool
1718
}{
@@ -45,6 +46,21 @@ func TestParseClassIdentifier(t *testing.T) {
4546
vc: "ZPESystems:NSC:001234567",
4647
want: &VendorData{VendorName: "ZPESystems", Model: "NSC", Serial: "001234567"},
4748
},
49+
{
50+
name: "cisco",
51+
vc: "FPR4100",
52+
ci: []byte("JMX2525X0BW"),
53+
want: &VendorData{VendorName: "Cisco Systems", Model: "FPR4100", Serial: "JMX2525X0BW"},
54+
},
55+
{name: "ciscoNoSerial", vc: "FPR4100", fail: true},
56+
{
57+
name: "ciena",
58+
vc: "1271-00011E00-032",
59+
ci: []byte("JUSTASN"),
60+
want: &VendorData{VendorName: "Ciena Corporation", Model: "00011E00-032", Serial: "JUSTASN"},
61+
},
62+
{name: "cienaInvalidVendorClass", vc: "127100011E00032", fail: true},
63+
{name: "cienaNoSerial", vc: "1271-00011E00-032", fail: true},
4864
}
4965

5066
for _, tc := range tt {
@@ -60,6 +76,9 @@ func TestParseClassIdentifier(t *testing.T) {
6076
if tc.hostname != "" {
6177
packet.UpdateOption(dhcpv4.OptHostName(tc.hostname))
6278
}
79+
if tc.ci != nil {
80+
packet.UpdateOption(dhcpv4.OptClientIdentifier(tc.ci))
81+
}
6382

6483
vd, err := ParseVendorData(packet)
6584
if tc.fail {

dhcpv6/dhcpv6message.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (mo MessageOptions) OneIANA() *OptIANA {
6969

7070
// IATA returns all Identity Association for Temporary Address options.
7171
func (mo MessageOptions) IATA() []*OptIATA {
72-
opts := mo.Get(OptionIANA)
72+
opts := mo.Get(OptionIATA)
7373
var iatas []*OptIATA
7474
for _, o := range opts {
7575
iatas = append(iatas, o.(*OptIATA))
@@ -288,6 +288,32 @@ func (mo MessageOptions) DHCP4oDHCP6Server() *OptDHCP4oDHCP6Server {
288288
return nil
289289
}
290290

291+
// NTPServers returns the NTP server addresses contained in the
292+
// NTP_SUBOPTION_SRV_ADDR of an OPTION_NTP_SERVER.
293+
// If multiple NTP server options exist, the function will return all the NTP
294+
// server addresses it finds, as defined by RFC 5908.
295+
func (mo MessageOptions) NTPServers() []net.IP {
296+
opts := mo.Options.Get(OptionNTPServer)
297+
if opts == nil {
298+
return nil
299+
}
300+
addrs := make([]net.IP, 0)
301+
for _, opt := range opts {
302+
ntp, ok := opt.(*OptNTPServer)
303+
if !ok {
304+
continue
305+
}
306+
for _, subopt := range ntp.Suboptions {
307+
so, ok := subopt.(*NTPSuboptionSrvAddr)
308+
if !ok {
309+
continue
310+
}
311+
addrs = append(addrs, net.IP(*so))
312+
}
313+
}
314+
return addrs
315+
}
316+
291317
// Message represents a DHCPv6 Message as defined by RFC 3315 Section 6.
292318
type Message struct {
293319
MessageType MessageType

dhcpv6/modifiers.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,17 @@ func WithIAID(iaid [4]byte) Modifier {
9191
}
9292
}
9393

94-
// WithIATA adds or updates an OptIANA option with the provided IAAddress
95-
// options
96-
func WithIATA(addrs ...OptIAAddress) Modifier {
94+
// WithIATA adds or updates an OptIATA option with the provided IAID,
95+
// and IAAddress options
96+
func WithIATA(iaid [4]byte, addrs ...OptIAAddress) Modifier {
9797
return func(d DHCPv6) {
9898
if msg, ok := d.(*Message); ok {
9999
iata := msg.Options.OneIATA()
100100
if iata == nil {
101101
iata = &OptIATA{}
102102
}
103+
copy(iata.IaId[:], iaid[:])
104+
103105
for _, addr := range addrs {
104106
iata.Options.Add(&addr)
105107
}

dhcpv6/modifiers_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"log"
55
"net"
66
"testing"
7+
"time"
78

89
"github.com/insomniacslk/dhcp/iana"
910
"github.com/stretchr/testify/require"
@@ -134,3 +135,24 @@ func TestWithClientLinkLayerAddress(t *testing.T) {
134135
require.Equal(t, iana.HWTypeEthernet, llt)
135136
require.Equal(t, mac, lla)
136137
}
138+
139+
func TestWithIATA(t *testing.T) {
140+
var d Message
141+
WithIATA([4]byte{1, 2, 3, 4}, OptIAAddress{
142+
IPv6Addr: net.ParseIP("2001:DB8:7689::1234"),
143+
PreferredLifetime: 3600,
144+
ValidLifetime: 5200,
145+
})(&d)
146+
require.Equal(t, 1, len(d.Options.Options))
147+
148+
iata := d.Options.OneIATA()
149+
iataOpts := iata.Options.Get(OptionIAAddr)
150+
iaAddr := iataOpts[0].(*OptIAAddress)
151+
152+
require.Equal(t, OptionIATA, iata.Code())
153+
require.Equal(t, [4]byte{1, 2, 3, 4}, iata.IaId)
154+
require.Equal(t, net.ParseIP("2001:DB8:7689::1234"),
155+
iaAddr.IPv6Addr)
156+
require.Equal(t, time.Duration(3600), iaAddr.PreferredLifetime)
157+
require.Equal(t, time.Duration(5200), iaAddr.ValidLifetime)
158+
}

dhcpv6/option_ntp_server.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package dhcpv6
2+
3+
import (
4+
"fmt"
5+
"net"
6+
7+
"github.com/insomniacslk/dhcp/rfc1035label"
8+
"github.com/u-root/uio/uio"
9+
)
10+
11+
// NTPSuboptionSrvAddr is NTP_SUBOPTION_SRV_ADDR according to RFC 5908.
12+
type NTPSuboptionSrvAddr net.IP
13+
14+
// Code returns the suboption code.
15+
func (n *NTPSuboptionSrvAddr) Code() OptionCode {
16+
return NTPSuboptionSrvAddrCode
17+
}
18+
19+
// ToBytes returns the byte serialization of the suboption.
20+
func (n *NTPSuboptionSrvAddr) ToBytes() []byte {
21+
buf := uio.NewBigEndianBuffer(nil)
22+
buf.Write16(uint16(NTPSuboptionSrvAddrCode))
23+
buf.Write16(uint16(net.IPv6len))
24+
buf.WriteBytes(net.IP(*n).To16())
25+
return buf.Data()
26+
}
27+
28+
func (n *NTPSuboptionSrvAddr) String() string {
29+
return fmt.Sprintf("Server Address: %s", net.IP(*n).String())
30+
}
31+
32+
// NTPSuboptionMCAddr is NTP_SUBOPTION_MC_ADDR according to RFC 5908.
33+
type NTPSuboptionMCAddr net.IP
34+
35+
// Code returns the suboption code.
36+
func (n *NTPSuboptionMCAddr) Code() OptionCode {
37+
return NTPSuboptionMCAddrCode
38+
}
39+
40+
// ToBytes returns the byte serialization of the suboption.
41+
func (n *NTPSuboptionMCAddr) ToBytes() []byte {
42+
buf := uio.NewBigEndianBuffer(nil)
43+
buf.Write16(uint16(NTPSuboptionMCAddrCode))
44+
buf.Write16(uint16(net.IPv6len))
45+
buf.WriteBytes(net.IP(*n).To16())
46+
return buf.Data()
47+
}
48+
49+
func (n *NTPSuboptionMCAddr) String() string {
50+
return fmt.Sprintf("Multicast Address: %s", net.IP(*n).String())
51+
}
52+
53+
// NTPSuboptionSrvFQDN is NTP_SUBOPTION_SRV_FQDN according to RFC 5908.
54+
type NTPSuboptionSrvFQDN rfc1035label.Labels
55+
56+
// Code returns the suboption code.
57+
func (n *NTPSuboptionSrvFQDN) Code() OptionCode {
58+
return NTPSuboptionSrvFQDNCode
59+
}
60+
61+
// ToBytes returns the byte serialization of the suboption.
62+
func (n *NTPSuboptionSrvFQDN) ToBytes() []byte {
63+
buf := uio.NewBigEndianBuffer(nil)
64+
buf.Write16(uint16(NTPSuboptionSrvFQDNCode))
65+
l := rfc1035label.Labels(*n)
66+
buf.Write16(uint16(l.Length()))
67+
buf.WriteBytes(l.ToBytes())
68+
return buf.Data()
69+
}
70+
71+
func (n *NTPSuboptionSrvFQDN) String() string {
72+
l := rfc1035label.Labels(*n)
73+
return fmt.Sprintf("Server FQDN: %s", l.String())
74+
}
75+
76+
// NTPSuboptionSrvAddr is the value of NTP_SUBOPTION_SRV_ADDR according to RFC 5908.
77+
const (
78+
NTPSuboptionSrvAddrCode = OptionCode(1)
79+
NTPSuboptionMCAddrCode = OptionCode(2)
80+
NTPSuboptionSrvFQDNCode = OptionCode(3)
81+
)
82+
83+
// parseNTPSuboption implements the OptionParser interface.
84+
func parseNTPSuboption(code OptionCode, data []byte) (Option, error) {
85+
//var o Options
86+
buf := uio.NewBigEndianBuffer(data)
87+
length := len(data)
88+
data, err := buf.ReadN(length)
89+
if err != nil {
90+
return nil, fmt.Errorf("failed to read %d bytes for suboption: %w", length, err)
91+
}
92+
switch code {
93+
case NTPSuboptionSrvAddrCode, NTPSuboptionMCAddrCode:
94+
if length != net.IPv6len {
95+
return nil, fmt.Errorf("invalid suboption length, want %d, got %d", net.IPv6len, length)
96+
}
97+
var so Option
98+
switch code {
99+
case NTPSuboptionSrvAddrCode:
100+
sos := NTPSuboptionSrvAddr(data)
101+
so = &sos
102+
case NTPSuboptionMCAddrCode:
103+
som := NTPSuboptionMCAddr(data)
104+
so = &som
105+
}
106+
return so, nil
107+
case NTPSuboptionSrvFQDNCode:
108+
l, err := rfc1035label.FromBytes(data)
109+
if err != nil {
110+
return nil, fmt.Errorf("failed to parse rfc1035 labels: %w", err)
111+
}
112+
// TODO according to rfc3315, this label must not be compressed.
113+
// Need to add support for compression detection to the
114+
// `rfc1035label` package in order to do that.
115+
so := NTPSuboptionSrvFQDN(*l)
116+
return &so, nil
117+
default:
118+
gopt := OptionGeneric{OptionCode: code, OptionData: data}
119+
return &gopt, nil
120+
}
121+
}
122+
123+
// ParseOptNTPServer parses a sequence of bytes into an OptNTPServer object.
124+
func ParseOptNTPServer(data []byte) (*OptNTPServer, error) {
125+
var so Options
126+
if err := so.FromBytesWithParser(data, parseNTPSuboption); err != nil {
127+
return nil, err
128+
}
129+
return &OptNTPServer{
130+
Suboptions: so,
131+
}, nil
132+
}
133+
134+
// OptNTPServer is an option NTP server as defined by RFC 5908.
135+
type OptNTPServer struct {
136+
Suboptions Options
137+
}
138+
139+
// Code returns the option code
140+
func (op *OptNTPServer) Code() OptionCode {
141+
return OptionNTPServer
142+
}
143+
144+
// ToBytes returns the option serialized to bytes.
145+
func (op *OptNTPServer) ToBytes() []byte {
146+
buf := uio.NewBigEndianBuffer(nil)
147+
for _, so := range op.Suboptions {
148+
buf.WriteBytes(so.ToBytes())
149+
}
150+
return buf.Data()
151+
}
152+
153+
func (op *OptNTPServer) String() string {
154+
return fmt.Sprintf("NTP: %v", op.Suboptions)
155+
}

0 commit comments

Comments
 (0)