diff options
author | Gerardo Poggio <gepoggio@fb.com> | 2023-06-12 09:42:15 +0100 |
---|---|---|
committer | Gerardo Poggio <gepoggio@fb.com> | 2023-06-12 14:33:46 +0100 |
commit | cb5419b1e557224267460a7ca12d0d32bd4882a9 (patch) | |
tree | 71fe52ce0bcea53d58e3c3d0b398c406a24e8c6c /dhcpv6 | |
parent | 49801966e6cb2cfc58a2bd98f69a9c182c1d4c18 (diff) |
Add MLNX-OS ZTPv6 vendor data parsing
Signed-off-by: Gerardo Poggio <gepoggio@fb.com>
Diffstat (limited to 'dhcpv6')
-rw-r--r-- | dhcpv6/ztpv6/mellanox.go | 37 | ||||
-rw-r--r-- | dhcpv6/ztpv6/mellanox_test.go | 66 | ||||
-rw-r--r-- | dhcpv6/ztpv6/parse_vendor_options.go | 12 |
3 files changed, 113 insertions, 2 deletions
diff --git a/dhcpv6/ztpv6/mellanox.go b/dhcpv6/ztpv6/mellanox.go new file mode 100644 index 0000000..3ed8b43 --- /dev/null +++ b/dhcpv6/ztpv6/mellanox.go @@ -0,0 +1,37 @@ +package ztpv6 + +import ( + "errors" + + "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/insomniacslk/dhcp/iana" +) + +type MlnxSubOption uint16 + +const ( + MlnxSubOptionModel MlnxSubOption = 1 + MlnxSubOptionPartNum MlnxSubOption = 2 + MlnxSubOptionSerial MlnxSubOption = 3 + MlnxSubOptionMac MlnxSubOption = 4 + MlnxSubOptionProfile MlnxSubOption = 5 + MlnxSubOptionRelease MlnxSubOption = 6 +) + +func getMellanoxVendorData(vendorOptsOption *dhcpv6.OptVendorOpts) (*VendorData, error) { + vd := VendorData{} + vd.VendorName = iana.EnterpriseIDMellanoxTechnologiesLTD.String() + for _, opt := range vendorOptsOption.VendorOpts { + switch MlnxSubOption(opt.Code()) { + case MlnxSubOptionSerial: + vd.Serial = string(opt.ToBytes()) + case MlnxSubOptionModel: + vd.Model = string(opt.ToBytes()) + } + } + if (vd.Serial == "") || (vd.Model == "") { + return nil, errors.New("couldn't parse Mellanox sub-option for serial or model") + } + + return &vd, nil +} diff --git a/dhcpv6/ztpv6/mellanox_test.go b/dhcpv6/ztpv6/mellanox_test.go new file mode 100644 index 0000000..29293d1 --- /dev/null +++ b/dhcpv6/ztpv6/mellanox_test.go @@ -0,0 +1,66 @@ +package ztpv6 + +import ( + "testing" + + "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/insomniacslk/dhcp/iana" + "github.com/stretchr/testify/require" +) + +func TestParseMellanoxVendorData(t *testing.T) { + tt := []struct { + name string + vendorOpts []dhcpv6.Option + want *VendorData + fail bool + }{ + {name: "empty", fail: true}, + { + name: "ok", + fail: false, + vendorOpts: []dhcpv6.Option{ + &dhcpv6.OptionGeneric{OptionData: []byte("SomeModel"), OptionCode: dhcpv6.OptionCode(MlnxSubOptionModel)}, + &dhcpv6.OptionGeneric{OptionData: []byte("SomeModel-1234"), OptionCode: dhcpv6.OptionCode(MlnxSubOptionPartNum)}, + &dhcpv6.OptionGeneric{OptionData: []byte("ABC1234"), OptionCode: dhcpv6.OptionCode(MlnxSubOptionSerial)}, + &dhcpv6.OptionGeneric{OptionData: []byte("1.2.3"), OptionCode: dhcpv6.OptionCode(MlnxSubOptionRelease)}, + }, + want: &VendorData{ + VendorName: iana.EnterpriseIDMellanoxTechnologiesLTD.String(), + Model: "SomeModel", + Serial: "ABC1234", + }, + }, + { + name: "no model", + fail: true, + vendorOpts: []dhcpv6.Option{ + &dhcpv6.OptionGeneric{OptionData: []byte("ABC1234"), OptionCode: dhcpv6.OptionCode(MlnxSubOptionSerial)}, + }, + want: nil, + }, + } + + 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.OptVendorOpts{ + VendorOpts: tc.vendorOpts, EnterpriseNumber: uint32(iana.EnterpriseIDMellanoxTechnologiesLTD)}) + + 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") + } else { + require.Equal(t, tc.want, vd, "comparing vendor option data") + } + }) + } +} diff --git a/dhcpv6/ztpv6/parse_vendor_options.go b/dhcpv6/ztpv6/parse_vendor_options.go index f1508fd..caa4e2f 100644 --- a/dhcpv6/ztpv6/parse_vendor_options.go +++ b/dhcpv6/ztpv6/parse_vendor_options.go @@ -33,8 +33,16 @@ func ParseVendorData(packet dhcpv6.DHCPv6) (*VendorData, error) { vData := []string{} if opt17 != nil { - vo := opt17.(*dhcpv6.OptVendorOpts).VendorOpts - for _, opt := range vo { + vendorOptsOption := opt17.(*dhcpv6.OptVendorOpts) + + // MLNX-OS has the relevant information spread over different sub-options + // of option 17 so the usual approach doesn't work + if vendorOptsOption.EnterpriseNumber == uint32(iana.EnterpriseIDMellanoxTechnologiesLTD) { + return getMellanoxVendorData(vendorOptsOption) + } + + // rest of vendors use a single sub-option so we stringify them and parse them below + for _, opt := range vendorOptsOption.VendorOpts { vData = append(vData, string(opt.(*dhcpv6.OptionGeneric).OptionData)) } } else { |