summaryrefslogtreecommitdiffhomepage
path: root/packet
diff options
context:
space:
mode:
Diffstat (limited to 'packet')
-rw-r--r--packet/bgp/bgp.go132
-rw-r--r--packet/bgp/bgp_test.go84
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)
+}