diff options
author | Wataru Ishida <ishida.wataru@lab.ntt.co.jp> | 2016-09-15 08:17:22 +0000 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2016-09-15 14:32:34 -0700 |
commit | 0db1cd4af26a2c5025517ae30c9c16b11d5bb8d6 (patch) | |
tree | b06750b6047064a598fad3eef1997a7423c97daa | |
parent | 035b129966b5e91e50a96027e375df577e09194a (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.go | 64 | ||||
-rw-r--r-- | packet/bgp/bgp.go | 134 | ||||
-rw-r--r-- | packet/bgp/bgp_test.go | 36 |
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)) + } + +} |