summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorWataru Ishida <ishida.wataru@lab.ntt.co.jp>2016-09-15 08:17:22 +0000
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-09-15 14:32:34 -0700
commit0db1cd4af26a2c5025517ae30c9c16b11d5bb8d6 (patch)
treeb06750b6047064a598fad3eef1997a7423c97daa
parent035b129966b5e91e50a96027e375df577e09194a (diff)
bgp/cli: support evpn route type 5 (ip prefix advertisement)
see draft-ietf-bess-evpn-prefix-advertisement-03 $ gobgp g ri -a evpn add prefix 10.0.0.0/24 etag 20 rd 100:100 rt 100:100 gw 10.10.10.10 label 100 $ gobgp g ri -a evpn add prefix 200::/64 etag 20 rd 100:100 rt 100:100 gw 200::1 label 10000 close #1082 Signed-off-by: Wataru Ishida <ishida.wataru@lab.ntt.co.jp>
-rw-r--r--gobgp/cmd/global.go64
-rw-r--r--packet/bgp/bgp.go134
-rw-r--r--packet/bgp/bgp_test.go36
3 files changed, 223 insertions, 11 deletions
diff --git a/gobgp/cmd/global.go b/gobgp/cmd/global.go
index 5b1e60ef..9d571a21 100644
--- a/gobgp/cmd/global.go
+++ b/gobgp/cmd/global.go
@@ -419,6 +419,61 @@ func ParseEvpnMulticastArgs(args []string) (bgp.AddrPrefixInterface, []string, e
}
+func ParseEVPNIPPrefixArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
+ if len(args) < 6 {
+ return nil, nil, fmt.Errorf("lack of number of args needs 6 at least but got %d", len(args))
+ }
+ m := extractReserved(args, []string{"gw", "rd", "rt", "encap", "etag", "label"})
+ if len(m[""]) < 1 {
+ return nil, nil, fmt.Errorf("specify prefix")
+ }
+ ip, n, err := net.ParseCIDR(m[""][0])
+ if err != nil {
+ return nil, nil, err
+ }
+ ones, _ := n.Mask.Size()
+ var gw net.IP
+ if len(m["gw"]) > 0 {
+ gw = net.ParseIP(m["gw"][0])
+ }
+
+ if len(m["rd"]) < 1 {
+ return nil, nil, fmt.Errorf("specify RD")
+ }
+ rd, err := bgp.ParseRouteDistinguisher(m["rd"][0])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var etag uint32
+ if len(m["etag"]) > 0 {
+ e, err := strconv.Atoi(m["etag"][0])
+ if err != nil {
+ return nil, nil, err
+ }
+ etag = uint32(e)
+ }
+
+ var label uint32
+ if len(m["label"]) > 0 {
+ e, err := strconv.Atoi(m["label"][0])
+ if err != nil {
+ return nil, nil, err
+ }
+ label = uint32(e)
+ }
+
+ r := &bgp.EVPNIPPrefixRoute{
+ RD: rd,
+ ETag: etag,
+ IPPrefixLength: uint8(ones),
+ IPPrefix: ip,
+ GWIPAddress: gw,
+ Label: label,
+ }
+ return bgp.NewEVPNNLRI(bgp.EVPN_IP_PREFIX, 0, r), nil, nil
+}
+
func ParseEvpnArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
if len(args) < 1 {
return nil, nil, fmt.Errorf("lack of args. need 1 but %d", len(args))
@@ -430,8 +485,10 @@ func ParseEvpnArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
return ParseEvpnMacAdvArgs(args)
case "multicast":
return ParseEvpnMulticastArgs(args)
+ case "prefix":
+ return ParseEVPNIPPrefixArgs(args)
}
- return nil, nil, fmt.Errorf("invalid subtype. expect [macadv|multicast] but %s", subtype)
+ return nil, nil, fmt.Errorf("invalid subtype. expect [macadv|multicast|prefix] but %s", subtype)
}
func extractOrigin(args []string) ([]string, bgp.PathAttributeInterface, error) {
@@ -857,9 +914,10 @@ usage: %s rib %s%%smatch <MATCH_EXPR> then <THEN_EXPR> -a %%s
etherTypes,
)
helpErrMap[bgp.RF_FS_L2_VPN] = fmt.Errorf(fsHelpMsgFmt, "l2vpn-flowspec", macFsMatchExpr)
- helpErrMap[bgp.RF_EVPN] = fmt.Errorf(`usage: %s rib %s { macadv <MACADV> | multicast <MULTICAST> } -a evpn
+ helpErrMap[bgp.RF_EVPN] = fmt.Errorf(`usage: %s rib %s { macadv <MACADV> | multicast <MULTICAST> | prefix <PREFIX> } -a evpn
<MACADV> : <mac address> <ip address> <etag> <label> rd <rd> rt <rt>... [encap <encap type>]
- <MULTICAST> : <ip address> <etag> rd <rd> rt <rt>... [encap <encap type>]`, cmdstr, modtype)
+ <MULTICAST> : <ip address> <etag> rd <rd> rt <rt>... [encap <encap type>]
+ <PREFIX> : <ip prefix> [gw <gateway>] etag <etag> rd <rd> rt <rt>... [encap <encap type>]`, cmdstr, modtype)
helpErrMap[bgp.RF_OPAQUE] = fmt.Errorf(`usage: %s rib %s key <KEY> [value <VALUE>]`, cmdstr, modtype)
if err, ok := helpErrMap[rf]; ok {
return err
diff --git a/packet/bgp/bgp.go b/packet/bgp/bgp.go
index ce594128..a3f137c4 100644
--- a/packet/bgp/bgp.go
+++ b/packet/bgp/bgp.go
@@ -1893,14 +1893,6 @@ func (er *EVPNEthernetSegmentRoute) Serialize() ([]byte, error) {
return buf, nil
}
-type EVPNRouteTypeInterface interface {
- DecodeFromBytes([]byte) error
- Serialize() ([]byte, error)
- String() string
- rd() RouteDistinguisherInterface
- MarshalJSON() ([]byte, error)
-}
-
func (er *EVPNEthernetSegmentRoute) String() string {
return fmt.Sprintf("[type:esi][rd:%s][esi:%d][ip:%s]", er.RD, er.ESI, er.IPAddress)
}
@@ -1921,6 +1913,129 @@ func (er *EVPNEthernetSegmentRoute) rd() RouteDistinguisherInterface {
return er.RD
}
+type EVPNIPPrefixRoute struct {
+ RD RouteDistinguisherInterface
+ ESI EthernetSegmentIdentifier
+ ETag uint32
+ IPPrefixLength uint8
+ IPPrefix net.IP
+ GWIPAddress net.IP
+ Label uint32
+}
+
+func (er *EVPNIPPrefixRoute) DecodeFromBytes(data []byte) error {
+ if len(data) < 30 { // rd + esi + etag + prefix-len + ipv4 addr + label
+ return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPN IP Prefix Route bytes available")
+ }
+ er.RD = GetRouteDistinguisher(data)
+ data = data[er.RD.Len():]
+ err := er.ESI.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ data = data[10:]
+ er.ETag = binary.BigEndian.Uint32(data[0:4])
+ data = data[4:]
+ er.IPPrefixLength = data[0]
+ addrLen := 4
+ data = data[1:]
+ if len(data) > 19 { // ipv6 addr + label
+ addrLen = 16
+ }
+ er.IPPrefix = net.IP(data[:addrLen])
+ data = data[addrLen:]
+ switch {
+ case len(data) == 3:
+ er.Label = labelDecode(data)
+ case len(data) == addrLen+3:
+ er.GWIPAddress = net.IP(data[:addrLen])
+ er.Label = labelDecode(data[addrLen:])
+ default:
+ return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPN IP Prefix Route bytes available")
+ }
+ return nil
+}
+
+func (er *EVPNIPPrefixRoute) Serialize() ([]byte, error) {
+ var buf []byte
+ var err error
+ if er.RD != nil {
+ buf, err = er.RD.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ buf = make([]byte, 8)
+ }
+ tbuf, err := er.ESI.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, tbuf...)
+
+ tbuf = make([]byte, 4)
+ binary.BigEndian.PutUint32(tbuf, er.ETag)
+ buf = append(buf, tbuf...)
+
+ buf = append(buf, er.IPPrefixLength)
+
+ if er.IPPrefix == nil {
+ return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("IP Prefix is nil"))
+ } else if er.IPPrefix.To4() != nil {
+ buf = append(buf, []byte(er.IPPrefix.To4())...)
+ } else {
+ buf = append(buf, []byte(er.IPPrefix)...)
+ }
+
+ if er.GWIPAddress != nil {
+ if er.GWIPAddress.To4() != nil {
+ buf = append(buf, []byte(er.GWIPAddress.To4())...)
+ } else {
+ buf = append(buf, []byte(er.GWIPAddress.To16())...)
+ }
+ }
+
+ tbuf = make([]byte, 3)
+ labelSerialize(er.Label, tbuf)
+ buf = append(buf, tbuf...)
+
+ return buf, nil
+}
+
+func (er *EVPNIPPrefixRoute) String() string {
+ return fmt.Sprintf("[type:Prefix][rd:%s][esi:%s][etag:%d][prefix:%s/%d][gw:%s][label:%d]", er.RD, er.ESI.String(), er.ETag, er.IPPrefix, er.IPPrefixLength, er.GWIPAddress, er.Label)
+}
+
+func (er *EVPNIPPrefixRoute) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ RD RouteDistinguisherInterface `json:"rd"`
+ ESI string `json:"esi"`
+ Etag uint32 `json:"etag"`
+ Prefix string `json:"prefix"`
+ Gateway string `json:"gateway"`
+ Label uint32 `json:"label"`
+ }{
+ RD: er.RD,
+ ESI: er.ESI.String(),
+ Etag: er.ETag,
+ Prefix: fmt.Sprintf("%s/%d", er.IPPrefix, er.IPPrefixLength),
+ Gateway: er.GWIPAddress.String(),
+ Label: er.Label,
+ })
+}
+
+func (er *EVPNIPPrefixRoute) rd() RouteDistinguisherInterface {
+ return er.RD
+}
+
+type EVPNRouteTypeInterface interface {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+ String() string
+ rd() RouteDistinguisherInterface
+ MarshalJSON() ([]byte, error)
+}
+
func getEVPNRouteType(t uint8) (EVPNRouteTypeInterface, error) {
switch t {
case EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY:
@@ -1931,6 +2046,8 @@ func getEVPNRouteType(t uint8) (EVPNRouteTypeInterface, error) {
return &EVPNMulticastEthernetTagRoute{}, nil
case EVPN_ETHERNET_SEGMENT_ROUTE:
return &EVPNEthernetSegmentRoute{}, nil
+ case EVPN_IP_PREFIX:
+ return &EVPNIPPrefixRoute{}, nil
}
return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Unknown EVPN Route type: %d", t))
}
@@ -1940,6 +2057,7 @@ const (
EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT = 2
EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG = 3
EVPN_ETHERNET_SEGMENT_ROUTE = 4
+ EVPN_IP_PREFIX = 5
)
type EVPNNLRI struct {
diff --git a/packet/bgp/bgp_test.go b/packet/bgp/bgp_test.go
index 80dcc5af..0fcef12d 100644
--- a/packet/bgp/bgp_test.go
+++ b/packet/bgp/bgp_test.go
@@ -480,3 +480,39 @@ func Test_FlowSpecNlriVPN(t *testing.T) {
t.Log(bytes.Equal(buf1, buf2))
}
}
+
+func Test_EVPNIPPrefixRoute(t *testing.T) {
+ assert := assert.New(t)
+ rd, _ := ParseRouteDistinguisher("100:100")
+ r := &EVPNIPPrefixRoute{
+ RD: rd,
+ ESI: EthernetSegmentIdentifier{
+ Type: ESI_ARBITRARY,
+ Value: make([]byte, 9),
+ },
+ ETag: 10,
+ IPPrefixLength: 24,
+ IPPrefix: net.IP{10, 10, 10, 0},
+ GWIPAddress: net.IP{10, 10, 10, 10},
+ Label: 1000,
+ }
+ n1 := NewEVPNNLRI(EVPN_IP_PREFIX, 0, r)
+ buf1, err := n1.Serialize()
+ assert.Nil(err)
+ n2, err := NewPrefixFromRouteFamily(RouteFamilyToAfiSafi(RF_EVPN))
+ assert.Nil(err)
+ err = n2.DecodeFromBytes(buf1)
+ assert.Nil(err)
+ buf2, _ := n2.Serialize()
+ t.Log(n1.RouteTypeData.(*EVPNIPPrefixRoute).ESI.Value, n2.(*EVPNNLRI).RouteTypeData.(*EVPNIPPrefixRoute).ESI.Value)
+ t.Log(reflect.DeepEqual(n1.RouteTypeData.(*EVPNIPPrefixRoute).ESI.Value, n2.(*EVPNNLRI).RouteTypeData.(*EVPNIPPrefixRoute).ESI.Value))
+ if reflect.DeepEqual(n1, n2) {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), n1, buf1)
+ t.Error(len(buf2), n2, buf2)
+ t.Log(bytes.Equal(buf1, buf2))
+ }
+
+}