diff options
-rw-r--r-- | cmd/gobgp/global.go | 56 | ||||
-rw-r--r-- | docs/sources/evpn.md | 31 | ||||
-rw-r--r-- | pkg/packet/bgp/bgp.go | 7 |
3 files changed, 92 insertions, 2 deletions
diff --git a/cmd/gobgp/global.go b/cmd/gobgp/global.go index fe53242f..9017c0d3 100644 --- a/cmd/gobgp/global.go +++ b/cmd/gobgp/global.go @@ -829,6 +829,60 @@ func parseEvpnIPPrefixArgs(args []string) (bgp.AddrPrefixInterface, []string, er return bgp.NewEVPNNLRI(bgp.EVPN_IP_PREFIX, r), extcomms, nil } +func parseEvpnIPMSIArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { + // Format: + // etag <etag> rd <rd> [rt <rt>...] [encap <encap type>] + req := 4 + if len(args) < req { + return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args)) + } + m, err := extractReserved(args, map[string]int{ + "etag": paramSingle, + "rd": paramSingle, + "rt": paramSingle, + "encap": paramSingle}) + if err != nil { + return nil, nil, err + } + for _, f := range []string{"etag", "rd"} { + for len(m[f]) == 0 { + return nil, nil, fmt.Errorf("specify %s", f) + } + } + + rd, err := bgp.ParseRouteDistinguisher(m["rd"][0]) + if err != nil { + return nil, nil, err + } + + e, err := strconv.ParseUint(m["etag"][0], 10, 32) + if err != nil { + return nil, nil, fmt.Errorf("invalid etag: %s: %s", m["etag"][0], err) + } + etag := uint32(e) + + extcomms := make([]string, 0) + if len(m["rt"]) > 0 { + extcomms = append(extcomms, "rt") + extcomms = append(extcomms, m["rt"]...) + } + ec, err := bgp.ParseExtendedCommunity(bgp.EC_SUBTYPE_SOURCE_AS, m["rt"][0]) + if err != nil { + return nil, nil, fmt.Errorf("route target parse failed") + } + + if len(m["encap"]) > 0 { + extcomms = append(extcomms, "encap", m["encap"][0]) + } + + r := &bgp.EVPNIPMSIRoute{ + RD: rd, + ETag: etag, + EC: ec, + } + return bgp.NewEVPNNLRI(bgp.EVPN_I_PMSI, r), extcomms, 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)) @@ -846,6 +900,8 @@ func parseEvpnArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { return parseEvpnEthernetSegmentArgs(args) case "prefix": return parseEvpnIPPrefixArgs(args) + case "i-pmsi": + return parseEvpnIPMSIArgs(args) } return nil, nil, fmt.Errorf("invalid subtype. expect [macadv|multicast|prefix] but %s", subtype) } diff --git a/docs/sources/evpn.md b/docs/sources/evpn.md index db139231..e84b615d 100644 --- a/docs/sources/evpn.md +++ b/docs/sources/evpn.md @@ -239,6 +239,37 @@ $ gobgp global rib -a evpn prefix $ gobgp global rib -a evpn del prefix 10.0.0.0/24 172.16.0.1 esi MSTP aa:aa:aa:aa:aa:aa 100 etag 200 label 300 rd 1.1.1.1:65000 ``` +### I-PMSI Route + +```bash +# Add a route +$ gobgp global rib -a evpn add i-pmsi etag <etag> rd <rd> [rt <rt>...] [encap <encap type>] + +# Show routes +$ gobgp global rib -a evpn [i-pmsi] + +# Delete route +$ gobgp global rib -a evpn del i-pmsi etag <etag> rd <rd> +``` + +#### Example - I-PMSI Route + +```bash +# Simple case +$ gobgp global rib -a evpn add i-pmsi etag 100 rd 1.1.1.1:65000 rt 65000:200 +$ gobgp global rib -a evpn i-pmsi + Network Labels Next Hop AS_PATH Age Attrs +*> [type:I-PMSI][rd:1.1.1.1:65000][etag:100][EC:65000:0] 0.0.0.0 00:00:00 [{Origin: ?} +$ gobgp global rib -a evpn del i-pmsi 10.0.0.0/24 etag 100 rd 1.1.1.1:65000 + +# With optionals +$ gobgp global rib -a evpn add i-pmsi etag 100 rd 1.1.1.1:65000 rt 65000:200 encap vxlan pmsi ingress-repl 100 1.1.1.1 +$ gobgp global rib -a evpn i-pmsi + Network Labels Next Hop AS_PATH Age Attrs +*> [type:I-PMSI][rd:1.1.1.1:65000][etag:100][EC:65000:0] 0.0.0.0 00:00:00 [{Origin: ?} {Pmsi: type: ingress-repl, label: 100, tunnel-id: 1.1.1.1} {Extcomms: [65000:200], [VXLAN]}] +$ gobgp global rib -a evpn del i-pmsi etag 200 rd 1.1.1.1:65000 +``` + ## Reference ### Router's MAC Option diff --git a/pkg/packet/bgp/bgp.go b/pkg/packet/bgp/bgp.go index 0aae3559..4d899f24 100644 --- a/pkg/packet/bgp/bgp.go +++ b/pkg/packet/bgp/bgp.go @@ -2933,7 +2933,7 @@ func (er *EVPNIPMSIRoute) DecodeFromBytes(data []byte) error { er.ETag = binary.BigEndian.Uint32(data[0:4]) data = data[4:] - ec, err := ParseExtended(data[0:64]) + ec, err := ParseExtended(data[0:8]) if err != nil { return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Parse extended community interface failed")) } @@ -2967,7 +2967,7 @@ func (er *EVPNIPMSIRoute) String() string { if er.EC != nil { ec = er.EC.String() } - return fmt.Sprintf("[rd:%s][etag:%d][EC]:%s]", er.RD, er.ETag, ec) + return fmt.Sprintf("[type:I-PMSI][rd:%s][etag:%d][EC:%s]", er.RD, er.ETag, ec) } func (er *EVPNIPMSIRoute) MarshalJSON() ([]byte, error) { @@ -10011,6 +10011,9 @@ func ParseExtendedCommunity(subtype ExtendedCommunityAttrSubType, com string) (E return nil, err } localAdmin, _ := strconv.ParseUint(elems[10], 10, 32) + if subtype == EC_SUBTYPE_SOURCE_AS { + localAdmin = 0 + } ip := net.ParseIP(elems[1]) isTransitive := true switch { |