summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6/ztpv6
diff options
context:
space:
mode:
authorEmanuele Fia <name29@fb.com>2022-03-31 20:38:34 +0100
committerEmanuele Fia <name29@fb.com>2022-04-01 16:51:08 +0100
commitcef7f4a9829be517745c0c07658f1344989d150f (patch)
tree8da20e3c66df0b3243effa07291f204eef15027f /dhcpv6/ztpv6
parent07cc76ec259f5fff3e81e11b340167a690bca9a1 (diff)
Adding support for DHCPv6 for Ciena
Signed-off-by: Emanuele Fia <name29@fb.com>
Diffstat (limited to 'dhcpv6/ztpv6')
-rw-r--r--dhcpv6/ztpv6/parse_remote_id.go78
-rw-r--r--dhcpv6/ztpv6/parse_remote_id_test.go53
-rw-r--r--dhcpv6/ztpv6/parse_vendor_options.go8
-rw-r--r--dhcpv6/ztpv6/parse_vendor_options_test.go69
4 files changed, 58 insertions, 150 deletions
diff --git a/dhcpv6/ztpv6/parse_remote_id.go b/dhcpv6/ztpv6/parse_remote_id.go
index 8b1488a..9dd72b1 100644
--- a/dhcpv6/ztpv6/parse_remote_id.go
+++ b/dhcpv6/ztpv6/parse_remote_id.go
@@ -8,32 +8,12 @@ import (
"github.com/insomniacslk/dhcp/dhcpv6"
)
-var circuitRegexs = []*regexp.Regexp{
+var (
// Arista Port, Vlan Pattern
- regexp.MustCompile("Ethernet(?P<port>[0-9]+):(?P<vlan>[0-9]+)"),
+ aristaPVPattern = regexp.MustCompile("Ethernet(?P<port>[0-9]+):(?P<vlan>[0-9]+)")
// Arista Slot, Mod, Port Pattern
- regexp.MustCompile("Ethernet(?P<slot>[0-9]+)/(?P<mod>[0-9]+)/(?P<port>[0-9]+)"),
- // Juniper QFX et-0/0/0:0.0 and xe-0/0/0:0.0
- regexp.MustCompile("^(et|xe)-(?P<slot>[0-9]+)/(?P<mod>[0-9]+)/(?P<port>[0-9]+):(?P<subport>[0-9]+).*$"),
- // Juniper PTX et-0/0/0.0
- regexp.MustCompile("^et-(?P<slot>[0-9]+)/(?P<mod>[0-9]+)/(?P<port>[0-9]+).(?P<subport>[0-9]+)$"),
- // Juniper EX ge-0/0/0.0
- regexp.MustCompile("^ge-(?P<slot>[0-9]+)/(?P<mod>[0-9]+)/(?P<port>[0-9]+).(?P<subport>[0-9]+).*"),
- // Arista Ethernet3/17/1
- // Sometimes Arista prepend circuit id type(1 byte) and length(1 byte) not using ^
- regexp.MustCompile("Ethernet(?P<slot>[0-9]+)/(?P<mod>[0-9]+)/(?P<port>[0-9]+)$"),
- // Juniper QFX et-1/0/61
- regexp.MustCompile("^et-(?P<slot>[0-9]+)/(?P<mod>[0-9]+)/(?P<port>[0-9]+)$"),
- // Arista Ethernet14:Vlan2001
- // Arista Ethernet10:2020
- regexp.MustCompile("Ethernet(?P<port>[0-9]+):(?P<vlan>.*)$"),
- // Cisco Gi1/10:2020
- regexp.MustCompile("^Gi(?P<slot>[0-9]+)/(?P<port>[0-9]+):(?P<vlan>.*)$"),
- // Nexus Ethernet1/3
- regexp.MustCompile("^Ethernet(?P<slot>[0-9]+)/(?P<port>[0-9]+)$"),
- // Juniper bundle interface ae52.0
- regexp.MustCompile("^ae(?P<port>[0-9]+).(?P<subport>[0-9])$"),
-}
+ aristaSMPPattern = regexp.MustCompile("Ethernet(?P<slot>[0-9]+)/(?P<module>[0-9]+)/(?P<port>[0-9]+)")
+)
// CircuitID represents the structure of network vendor interface formats
type CircuitID struct {
@@ -73,32 +53,38 @@ func ParseRemoteID(packet dhcpv6.DHCPv6) (*CircuitID, error) {
}
func matchCircuitId(circuitInfo string) (*CircuitID, error) {
- for _, re := range circuitRegexs {
+ var names, matches []string
- match := re.FindStringSubmatch(circuitInfo)
- if len(match) == 0 {
- continue
- }
+ switch {
+ case aristaPVPattern.MatchString(circuitInfo):
+ matches = aristaPVPattern.FindStringSubmatch(circuitInfo)
+ names = aristaPVPattern.SubexpNames()
+ case aristaSMPPattern.MatchString(circuitInfo):
+ matches = aristaSMPPattern.FindStringSubmatch(circuitInfo)
+ names = aristaSMPPattern.SubexpNames()
+ }
- c := CircuitID{}
- for i, k := range re.SubexpNames() {
- switch k {
- case "slot":
- c.Slot = match[i]
- case "mod":
- c.Module = match[i]
- case "port":
- c.Port = match[i]
- case "subport":
- c.SubPort = match[i]
- case "vlan":
- c.Vlan = match[i]
- }
- }
+ if len(matches) == 0 {
+ return nil, fmt.Errorf("no circuitId regex matches for %v", circuitInfo)
+ }
- return &c, nil
+ var circuit CircuitID
+ for i, match := range matches {
+ switch names[i] {
+ case "port":
+ circuit.Port = match
+ case "slot":
+ circuit.Slot = match
+ case "module":
+ circuit.Module = match
+ case "subport":
+ circuit.SubPort = match
+ case "vlan":
+ circuit.Vlan = match
+ }
}
- return nil, fmt.Errorf("Unable to match circuit id : %s with listed regexes of interface types", circuitInfo)
+
+ return &circuit, nil
}
// FormatCircuitID is the CircuitID format we send in our Bootfile URL for ZTP devices
diff --git a/dhcpv6/ztpv6/parse_remote_id_test.go b/dhcpv6/ztpv6/parse_remote_id_test.go
index 8d17c80..6147cec 100644
--- a/dhcpv6/ztpv6/parse_remote_id_test.go
+++ b/dhcpv6/ztpv6/parse_remote_id_test.go
@@ -15,20 +15,9 @@ func TestCircuitID(t *testing.T) {
fail bool
}{
{name: "Bogus string", circuit: "ope/1/2/3:ope", fail: true, want: nil},
- {name: "Bogus test", circuit: "bogusInterface", fail: true, want: nil},
- {name: "juniperQFX pattern et", circuit: "et-0/0/0:0.0", want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
- {name: "juniperQFX pattern xe", circuit: "xe-0/0/14:2", want: &CircuitID{Slot: "0", Module: "0", Port: "14", SubPort: "2"}},
- {name: "juniperPTX pattern", circuit: "et-0/0/0.0", want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
- {name: "Juniper QFX pattern", circuit: "et-1/0/61", want: &CircuitID{Slot: "1", Module: "0", Port: "61"}},
- {name: "Arista Vlan pattern 1", circuit: "Ethernet14:Vlan2001", want: &CircuitID{Port: "14", Vlan: "Vlan2001"}},
- {name: "Arista Vlan pattern 2", circuit: "Ethernet10:2020", want: &CircuitID{Port: "10", Vlan: "2020"}},
+ {name: "Arista Port Vlan Pattern", circuit: "Ethernet13:2001", want: &CircuitID{Port: "13", Vlan: "2001"}},
{name: "Arista Slot Module Port Pattern", circuit: "Ethernet1/3/4", want: &CircuitID{Slot: "1", Module: "3", Port: "4"}},
{name: "Arista Slot Module Port Pattern InterfaceID", circuit: "Ethernet1/3/4:default", want: &CircuitID{Slot: "1", Module: "3", Port: "4"}},
- {name: "Cisco pattern", circuit: "Gi1/10:2020", want: &CircuitID{Slot: "1", Port: "10", Vlan: "2020"}},
- {name: "Cisco Nexus pattern", circuit: "Ethernet1/3", want: &CircuitID{Slot: "1", Port: "3"}},
- {name: "Juniper Bundle Pattern", circuit: "ae52.0", want: &CircuitID{Port: "52", SubPort: "0"}},
- {name: "Arista Vlan pattern 1 with circuitid type and length", circuit: "\x00\x0fEthernet14:2001", want: &CircuitID{Port: "14", Vlan: "2001"}},
- {name: "juniperEX pattern", circuit: "ge-0/0/0.0:RANDOMCHAR", want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
@@ -50,16 +39,8 @@ func TestFormatCircuitID(t *testing.T) {
want string
}{
{name: "empty", circuit: &CircuitID{}, want: ",,,,"},
- {name: "empty", circuit: &CircuitID{}, want: ",,,,"},
- {name: "juniperQFX pattern et", circuit: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}, want: "0,0,0,0,"},
- {name: "juniperQFX pattern xe", circuit: &CircuitID{Slot: "0", Module: "0", Port: "14", SubPort: "2"}, want: "0,0,14,2,"},
- {name: "juniperPTX pattern", circuit: &CircuitID{Slot: "0", Module: "0", Port: "0"}, want: "0,0,0,,"},
- {name: "Arista pattern", circuit: &CircuitID{Slot: "3", Module: "17", Port: "1"}, want: "3,17,1,,"},
- {name: "Juniper QFX pattern", circuit: &CircuitID{Slot: "1", Module: "0", Port: "61"}, want: "1,0,61,,"},
- {name: "Arista Vlan pattern 1", circuit: &CircuitID{Port: "14", Vlan: "Vlan2001"}, want: ",,14,,Vlan2001"},
- {name: "Arista Vlan pattern 2", circuit: &CircuitID{Port: "10", Vlan: "2020"}, want: ",,10,,2020"},
- {name: "Cisco Nexus pattern", circuit: &CircuitID{Slot: "1", Port: "3"}, want: "1,,3,,"},
- {name: "Juniper Bundle Pattern", circuit: &CircuitID{Port: "52", SubPort: "0"}, want: ",,52,0,"},
+ {name: "Arista format Port/Vlan", circuit: &CircuitID{Port: "13", Vlan: "2001"}, want: ",,13,,2001"},
+ {name: "Arista format Slot/Module/Port", circuit: &CircuitID{Slot: "1", Module: "3", Port: "4"}, want: "1,3,4,,"},
}
for _, tc := range tt {
@@ -79,19 +60,8 @@ func TestParseRemoteID(t *testing.T) {
fail bool
}{
{name: "Bogus string", circuit: []byte("ope/1/2/3:ope.1"), fail: true, want: nil},
- {name: "Bogus test", circuit: []byte("bogusInterface"), fail: true, want: nil},
- {name: "juniperQFX pattern et", circuit: []byte("et-0/0/0:0.0"), want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
- {name: "juniperQFX pattern xe", circuit: []byte("xe-0/0/14:2"), want: &CircuitID{Slot: "0", Module: "0", Port: "14", SubPort: "2"}},
- {name: "juniperPTX pattern", circuit: []byte("et-0/0/0.0"), want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
- {name: "Arista pattern", circuit: []byte("Ethernet3/17/1"), want: &CircuitID{Slot: "3", Module: "17", Port: "1"}},
- {name: "Juniper QFX pattern", circuit: []byte("et-1/0/61"), want: &CircuitID{Slot: "1", Module: "0", Port: "61"}},
- {name: "Arista Vlan pattern 1", circuit: []byte("Ethernet14:Vlan2001"), want: &CircuitID{Port: "14", Vlan: "Vlan2001"}},
- {name: "Arista Vlan pattern 2", circuit: []byte("Ethernet10:2020"), want: &CircuitID{Port: "10", Vlan: "2020"}},
- {name: "Cisco pattern", circuit: []byte("Gi1/10:2020"), want: &CircuitID{Slot: "1", Port: "10", Vlan: "2020"}},
- {name: "Cisco Nexus pattern", circuit: []byte("Ethernet1/3"), want: &CircuitID{Slot: "1", Port: "3"}},
- {name: "Juniper Bundle Pattern", circuit: []byte("ae52.0"), want: &CircuitID{Port: "52", SubPort: "0"}},
- {name: "Arista Vlan pattern 1 with circuitid type and length", circuit: []byte("\x00\x0fEthernet14:2001"), want: &CircuitID{Port: "14", Vlan: "2001"}},
- {name: "juniperEX pattern", circuit: []byte("ge-0/0/0.0:RANDOMCHAR"), want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
+ {name: "Arista Port Vlan Pattern", circuit: []byte("Ethernet13:2001"), want: &CircuitID{Port: "13", Vlan: "2001"}},
+ {name: "Arista Slot Module Port Pattern", circuit: []byte("Ethernet1/3/4"), want: &CircuitID{Slot: "1", Module: "3", Port: "4"}},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
@@ -123,19 +93,6 @@ func TestParseRemoteIDWithInterfaceID(t *testing.T) {
fail bool
}{
{name: "Bogus string", circuit: []byte("ope/1/2/3:ope.1"), fail: true, want: nil},
- {name: "Bogus test", circuit: []byte("bogusInterface"), fail: true, want: nil},
- {name: "juniperQFX pattern et", circuit: []byte("et-0/0/0:0.0"), want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
- {name: "juniperQFX pattern xe", circuit: []byte("xe-0/0/14:2"), want: &CircuitID{Slot: "0", Module: "0", Port: "14", SubPort: "2"}},
- {name: "juniperPTX pattern", circuit: []byte("et-0/0/0.0"), want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
- {name: "Arista pattern", circuit: []byte("Ethernet3/17/1"), want: &CircuitID{Slot: "3", Module: "17", Port: "1"}},
- {name: "Juniper QFX pattern", circuit: []byte("et-1/0/61"), want: &CircuitID{Slot: "1", Module: "0", Port: "61"}},
- {name: "Arista Vlan pattern 1", circuit: []byte("Ethernet14:Vlan2001"), want: &CircuitID{Port: "14", Vlan: "Vlan2001"}},
- {name: "Arista Vlan pattern 2", circuit: []byte("Ethernet10:2020"), want: &CircuitID{Port: "10", Vlan: "2020"}},
- {name: "Cisco pattern", circuit: []byte("Gi1/10:2020"), want: &CircuitID{Slot: "1", Port: "10", Vlan: "2020"}},
- {name: "Cisco Nexus pattern", circuit: []byte("Ethernet1/3"), want: &CircuitID{Slot: "1", Port: "3"}},
- {name: "Juniper Bundle Pattern", circuit: []byte("ae52.0"), want: &CircuitID{Port: "52", SubPort: "0"}},
- {name: "Arista Vlan pattern 1 with circuitid type and length", circuit: []byte("\x00\x0fEthernet14:2001"), want: &CircuitID{Port: "14", Vlan: "2001"}},
- {name: "juniperEX pattern", circuit: []byte("ge-0/0/0.0:RANDOMCHAR"), want: &CircuitID{Slot: "0", Module: "0", Port: "0", SubPort: "0"}},
{name: "Arista Slot Module Port Pattern", circuit: []byte("Ethernet1/3/4:default"), want: &CircuitID{Slot: "1", Module: "3", Port: "4"}},
}
for _, tc := range tt {
diff --git a/dhcpv6/ztpv6/parse_vendor_options.go b/dhcpv6/ztpv6/parse_vendor_options.go
index 3aef837..13983f4 100644
--- a/dhcpv6/ztpv6/parse_vendor_options.go
+++ b/dhcpv6/ztpv6/parse_vendor_options.go
@@ -81,11 +81,11 @@ func ParseVendorData(packet dhcpv6.DHCPv6) (*VendorData, error) {
if len(v) < 3 {
return nil, errVendorOptionMalformed
}
- duid := packet.(*dhcpv6.Message).Options.ClientID()
- if duid.Type != dhcpv6.DUID_EN {
- return nil, errors.New(fmt.Sprintf("Unexpected DUID type %d for Ciena", duid.Type))
+ innerMessage, err := packet.GetInnerMessage()
+ if err != nil {
+ return nil, fmt.Errorf("Unable to get inner message: %v", err)
}
-
+ duid := innerMessage.Options.ClientID()
vd.VendorName = iana.EnterpriseIDCienaCorporation.String()
vd.Model = v[1] + "-" + v[2]
vd.Serial = string(duid.EnterpriseIdentifier)
diff --git a/dhcpv6/ztpv6/parse_vendor_options_test.go b/dhcpv6/ztpv6/parse_vendor_options_test.go
index ac5fc81..cd2fb3d 100644
--- a/dhcpv6/ztpv6/parse_vendor_options_test.go
+++ b/dhcpv6/ztpv6/parse_vendor_options_test.go
@@ -57,10 +57,11 @@ func TestParseVendorDataWithVendorOpts(t *testing.T) {
func TestParseVendorDataWithVendorClass(t *testing.T) {
tt := []struct {
- name string
- vc string
- want *VendorData
- fail bool
+ name string
+ vc string
+ clientId *dhcpv6.Duid
+ want *VendorData
+ fail bool
}{
{name: "empty", fail: true},
{name: "unknownVendor", vc: "VendorX;BFR10K;XX12345", fail: true, want: nil},
@@ -75,45 +76,14 @@ func TestParseVendorDataWithVendorClass(t *testing.T) {
vc: "ZPESystems:NSC:001234567",
want: &VendorData{VendorName: "ZPESystems", Model: "NSC", Serial: "001234567"},
},
- }
-
- for _, tc := range tt {
- t.Run(tc.name, func(t *testing.T) {
- packet, err := dhcpv6.NewMessage()
- if err != nil {
- t.Fatalf("failed to creat dhcpv6 packet object: %v", err)
- }
-
- packet.AddOption(&dhcpv6.OptVendorClass{
- EnterpriseNumber: 0000, Data: [][]byte{[]byte(tc.vc)}})
-
- vd, err := ParseVendorData(packet)
- if err != nil && !tc.fail {
- t.Errorf("unexpected failure: %v", err)
- }
-
- if vd != nil {
- require.Equal(t, *tc.want, *vd, "comparing vendor class data")
- } else {
- require.Equal(t, tc.want, vd, "comparing vendor class data")
- }
- })
- }
-}
-
-func TestParseVendorDataWithClientId(t *testing.T) {
- tt := []struct {
- name string
- vc string
- serial string
- want *VendorData
- fail bool
- }{
{
- name: "Ciena",
- vc: "1271-23422Z11-123",
- serial: "001234567",
- want: &VendorData{VendorName: iana.EnterpriseIDCienaCorporation.String(), Model: "23422Z11-123", Serial: "001234567"},
+ name: "Ciena",
+ vc: "1271-23422Z11-123",
+ clientId: &dhcpv6.Duid{
+ Type: dhcpv6.DUID_EN,
+ EnterpriseIdentifier: []byte("001234567"),
+ },
+ want: &VendorData{VendorName: iana.EnterpriseIDCienaCorporation.String(), Model: "23422Z11-123", Serial: "001234567"},
},
}
@@ -126,23 +96,18 @@ func TestParseVendorDataWithClientId(t *testing.T) {
packet.AddOption(&dhcpv6.OptVendorClass{
EnterpriseNumber: 0000, Data: [][]byte{[]byte(tc.vc)}})
- packet.AddOption(dhcpv6.OptClientID(
- dhcpv6.Duid{
- Type: dhcpv6.DUID_EN,
- EnterpriseIdentifier: []byte(tc.serial),
- },
- ),
- )
-
+ if tc.clientId != nil {
+ packet.AddOption(dhcpv6.OptClientID(*tc.clientId))
+ }
vd, err := ParseVendorData(packet)
if err != nil && !tc.fail {
t.Errorf("unexpected failure: %v", err)
}
if vd != nil {
- require.Equal(t, *tc.want, *vd, "comparing vendor option data")
+ require.Equal(t, *tc.want, *vd, "comparing vendor class data")
} else {
- require.Equal(t, tc.want, vd, "comparing vendor option data")
+ require.Equal(t, tc.want, vd, "comparing vendor class data")
}
})
}