diff options
-rw-r--r-- | packet/bgp/bgp.go | 132 | ||||
-rw-r--r-- | packet/bgp/bgp_test.go | 84 |
2 files changed, 214 insertions, 2 deletions
diff --git a/packet/bgp/bgp.go b/packet/bgp/bgp.go index 6e58d5e2..3cd09ecf 100644 --- a/packet/bgp/bgp.go +++ b/packet/bgp/bgp.go @@ -1981,6 +1981,138 @@ func (esi *EthernetSegmentIdentifier) String() string { return s.String() } +// Decode Ethernet Segment Identifier (ESI) from string slice. +// +// The first element of args should be the Type field (e.g., "ARBITRARY", +// "arbitrary", "ESI_ARBITRARY" or "esi_arbitrary") and "single-homed" is +// the special keyword for all zeroed ESI. +// For the "ARBITRARY" Value field (Type 0), it should be the colon separated +// hex values and the number of elements should be 9 at most. +// e.g.) args := []string{"ARBITRARY", "11:22:33:44:55:66:77:88:99"} +// For the other types, the Value field format is the similar to the string +// format of ESI. +// e.g.) args := []string{"lacp", "aa:bb:cc:dd:ee:ff", "100"} +func ParseEthernetSegmentIdentifier(args []string) (EthernetSegmentIdentifier, error) { + esi := EthernetSegmentIdentifier{} + argLen := len(args) + if argLen == 0 || args[0] == "single-homed" { + return esi, nil + } + + typeStr := strings.TrimPrefix(strings.ToUpper(args[0]), "ESI_") + switch typeStr { + case "ARBITRARY": + esi.Type = ESI_ARBITRARY + case "LACP": + esi.Type = ESI_LACP + case "MSTP": + esi.Type = ESI_MSTP + case "MAC": + esi.Type = ESI_MAC + case "ROUTERID": + esi.Type = ESI_ROUTERID + case "AS": + esi.Type = ESI_AS + default: + typ, err := strconv.Atoi(args[0]) + if err != nil { + return esi, fmt.Errorf("invalid esi type: %s", args[0]) + } + esi.Type = ESIType(typ) + } + + invalidEsiValuesError := fmt.Errorf("invalid esi values for type %s: %s", esi.Type.String(), args[1:]) + esi.Value = make([]byte, 9, 9) + switch esi.Type { + case ESI_LACP: + fallthrough + case ESI_MSTP: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // MAC + mac, err := net.ParseMAC(args[1]) + if err != nil { + return esi, invalidEsiValuesError + } + copy(esi.Value[0:6], mac) + // Port Key or Bridge Priority + i, err := strconv.Atoi(args[2]) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint16(esi.Value[6:8], uint16(i)) + case ESI_MAC: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // MAC + mac, err := net.ParseMAC(args[1]) + if err != nil { + return esi, invalidEsiValuesError + } + copy(esi.Value[0:6], mac) + // Local Discriminator + i, err := strconv.Atoi(args[2]) + if err != nil { + return esi, invalidEsiValuesError + } + iBuf := make([]byte, 4, 4) + binary.BigEndian.PutUint32(iBuf, uint32(i)) + copy(esi.Value[6:9], iBuf[1:4]) + case ESI_ROUTERID: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // Router ID + ip := net.ParseIP(args[1]) + if ip == nil || ip.To4() == nil { + return esi, invalidEsiValuesError + } + copy(esi.Value[0:4], ip.To4()) + // Local Discriminator + i, err := strconv.Atoi(args[2]) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint32(esi.Value[4:8], uint32(i)) + case ESI_AS: + if argLen < 3 { + return esi, invalidEsiValuesError + } + // AS + as, err := strconv.Atoi(args[1]) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint32(esi.Value[0:4], uint32(as)) + // Local Discriminator + i, err := strconv.Atoi(args[2]) + if err != nil { + return esi, invalidEsiValuesError + } + binary.BigEndian.PutUint32(esi.Value[4:8], uint32(i)) + case ESI_ARBITRARY: + fallthrough + default: + if argLen < 2 { + // Assumes the Value field is omitted + break + } + values := make([]byte, 0, 9) + for _, e := range strings.SplitN(args[1], ":", 9) { + v, err := strconv.ParseUint(e, 16, 16) + if err != nil { + return esi, invalidEsiValuesError + } + values = append(values, byte(v)) + } + copy(esi.Value, values) + } + + return esi, nil +} + // // I-D bess-evpn-overlay-01 // diff --git a/packet/bgp/bgp_test.go b/packet/bgp/bgp_test.go index 7afda3bc..c3833fa8 100644 --- a/packet/bgp/bgp_test.go +++ b/packet/bgp/bgp_test.go @@ -18,10 +18,12 @@ package bgp import ( "bytes" "encoding/binary" - "github.com/stretchr/testify/assert" "net" "reflect" + "strconv" "testing" + + "github.com/stretchr/testify/assert" ) func keepalive() *BGPMessage { @@ -1038,7 +1040,7 @@ func Test_MpReachNLRIWithIPv4PrefixWithIPv6Nexthop(t *testing.T) { assert.Equal(bufin, bufout) } -func Test_ParseRouteDistingusher(t *testing.T) { +func Test_ParseRouteDistinguisher(t *testing.T) { assert := assert.New(t) rd, _ := ParseRouteDistinguisher("100:1000") @@ -1068,3 +1070,81 @@ func Test_ParseRouteDistingusher(t *testing.T) { assert.Equal(uint32((100<<16)|1000), rdType2.Admin) assert.Equal(uint16(10000), rdType2.Assigned) } + +func Test_ParseEthernetSegmentIdentifier(t *testing.T) { + assert := assert.New(t) + + // "single-homed" + esiZero := EthernetSegmentIdentifier{} + args := make([]string, 0, 0) + esi, err := ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(esiZero, esi) + args = []string{"single-homed"} + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(esiZero, esi) + + // ESI_ARBITRARY + args = []string{"ARBITRARY", "11:22:33:44:55:66:77:88:99"} // omit "ESI_" + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(EthernetSegmentIdentifier{ + Type: ESI_ARBITRARY, + Value: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99}, + }, esi) + + // ESI_LACP + args = []string{"lacp", "aa:bb:cc:dd:ee:ff", strconv.Itoa(0x1122)} // lower case + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(EthernetSegmentIdentifier{ + Type: ESI_LACP, + Value: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x00}, + }, esi) + + // ESI_MSTP + args = []string{"esi_mstp", "aa:bb:cc:dd:ee:ff", strconv.Itoa(0x1122)} // omit "ESI_" + lower case + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(EthernetSegmentIdentifier{ + Type: ESI_MSTP, + Value: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x00}, + }, esi) + + // ESI_MAC + args = []string{"ESI_MAC", "aa:bb:cc:dd:ee:ff", strconv.Itoa(0x112233)} + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(EthernetSegmentIdentifier{ + Type: ESI_MAC, + Value: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33}, + }, esi) + + // ESI_ROUTERID + args = []string{"ESI_ROUTERID", "1.1.1.1", strconv.Itoa(0x11223344)} + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(EthernetSegmentIdentifier{ + Type: ESI_ROUTERID, + Value: []byte{0x01, 0x01, 0x01, 0x01, 0x11, 0x22, 0x33, 0x44, 0x00}, + }, esi) + + // ESI_AS + args = []string{"ESI_AS", strconv.Itoa(0xaabbccdd), strconv.Itoa(0x11223344)} + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(EthernetSegmentIdentifier{ + Type: ESI_AS, + Value: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44, 0x00}, + }, esi) + + // Other + args = []string{"99", "11:22:33:44:55:66:77:88:99"} + esi, err = ParseEthernetSegmentIdentifier(args) + assert.Nil(err) + assert.Equal(EthernetSegmentIdentifier{ + Type: ESIType(99), + Value: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99}, + }, esi) +} |