Skip to content

Commit

Permalink
Adding the option to import IPv4/6 routes from VRF tables to the Glob…
Browse files Browse the repository at this point in the history
…al table as EVPN Type 5

The current behavior is:

For neighbors attached to VRFs, IPv4/6 routes are imported as VPNv4/6 to the
Global RIB. For example:

```
root@9652c769b373:/go# gobgp vrf blue neighbor
Peer          AS  Up/Down State       |#Received  Accepted
172.17.0.5 65001 00:00:48 Establ      |        1         1
172.17.0.6 65001 00:00:48 Establ      |        1         1

root@9652c769b373:/go# gobgp vrf blue neighbor
Peer          AS  Up/Down State       |#Received  Accepted
172.17.0.5 65001 00:00:48 Establ      |        1         1
172.17.0.6 65001 00:00:48 Establ      |        1         1

root@9652c769b373:/go# gobgp vrf blue rib
   Network              Next Hop             AS_PATH              Age        Attrs
*  10.0.0.0/24          172.17.0.5                                00:01:50   [{Origin: ?} {LocalPref: 100}]

root@9652c769b373:/go# gobgp vrf red rib
   Network              Next Hop             AS_PATH              Age        Attrs
*  10.0.0.0/24          172.17.0.2                                00:01:50   [{Origin: ?} {LocalPref: 100}]

root@9652c769b373:/go# gobgp global rib -a vpnv4
   Network              Labels     Next Hop             AS_PATH              Age        Attrs
*> 10:10:10.0.0.0/24    [0]        172.17.0.2                                00:02:43   [{Origin: ?} {LocalPref: 100} {Extcomms: [10:10]}]
*> 20:20:10.0.0.0/24    [0]        172.17.0.5                                00:02:43   [{Origin: ?} {LocalPref: 100} {Extcomms: [20:20]}]
```

After this change, we can configure the VRF to convert to EVPN Type 5 (IP Prefix)
instead of VPNv4/v6:

```
config file:
...
...
[[vrfs]]
    [vrfs.config]
        name = "blue"
        rd = "20:20"
        both-rt-list = ["65000:200"]
        import-as-evpn-ipprefix = true
        routers-mac = "ca:fe:00:00:be:ef"
        ethernet-tag = 100

root@dc00a25785dd:/go# gobgp vrf
  Name                 RD                   Import RT            Export RT            Router's MAC         ID    Import as EVPN       Ethernet Tag
  blue                 20:20                20:20                20:20                ca:fe:00:00:fe:ed    0     true                 200
  red                  10:10                10:10                10:10                ca:fe:00:00:be:ef    0     true                 100

root@dc00a25785dd:/go# gobgp global rib -a evpn
*> [type:Prefix][rd:20:20][etag:200][prefix:10.0.0.0/24] [0]        172.17.0.5                                00:00:41   [{Origin: ?} {LocalPref: 100} {Extcomms: [20:20], [router's mac: ca:fe:00:00:fe:ed]} [ESI: single-homed] [GW: 0.0.0.0]]
*> [type:Prefix][rd:10:10][etag:100][prefix:10.0.0.0/24] [0]        172.17.0.2                                00:00:28   [{Origin: ?} {LocalPref: 100} {Extcomms: [10:10], [router's mac: ca:fe:00:00:be:ef]} [ESI: single-homed] [GW: 0.0.0.0]]

```
  • Loading branch information
Lucas Lima committed Nov 15, 2024
1 parent 3b819cb commit 051d4b9
Show file tree
Hide file tree
Showing 23 changed files with 950 additions and 456 deletions.
2 changes: 1 addition & 1 deletion api/attribute.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/capability.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

786 changes: 409 additions & 377 deletions api/gobgp.pb.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions api/gobgp.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,9 @@ message Vrf {
// or FourOctetAsSpecificExtended.
repeated google.protobuf.Any export_rt = 4;
uint32 id = 5;
bool import_as_evpn_ipprefix = 6;
string routers_mac = 7;
uint32 ethernet_tag = 8;
}

message DefaultRouteDistance {
Expand Down
11 changes: 8 additions & 3 deletions cmd/gobgp/neighbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ var (
columnWidthTEID = 10
columnWidthQFI = 10
columnWidthEndpoint = 20
columnWidthAttrs = 20
)

func updateColumnWidth(nlri, nexthop, aspath, label, teid, qfi, endpoint string) {
func updateColumnWidth(nlri, nexthop, aspath, label, teid, qfi, endpoint, attrs string) {
if prefixLen := len(nlri); columnWidthPrefix < prefixLen {
columnWidthPrefix = prefixLen
}
Expand All @@ -69,6 +70,10 @@ func updateColumnWidth(nlri, nexthop, aspath, label, teid, qfi, endpoint string)
if columnWidthEndpoint < len(endpoint) {
columnWidthEndpoint = len(endpoint)
}
if columnWidthAttrs < len(attrs) {
columnWidthAttrs = len(attrs)
}

}

func getNeighbors(address string, enableAdv bool) ([]*api.Peer, error) {
Expand Down Expand Up @@ -660,7 +665,7 @@ func makeShowRouteArgs(p *api.Path, idx int, now time.Time, showAge, showBest, s
}
}

updateColumnWidth(nlri.String(), nexthop, aspathstr, label, teid, qfi, endpoint)
updateColumnWidth(nlri.String(), nexthop, aspathstr, label, teid, qfi, endpoint, pattrstr)

return args
}
Expand Down Expand Up @@ -699,7 +704,7 @@ func showRoute(dsts []*api.Destination, showAge, showBest, showLabel, showMUP, s
format += "%-10s "
}
headers = append(headers, "Attrs")
format += "%-s"
format += fmt.Sprintf("%%-%ds ", columnWidthAttrs)

if showSendMaxFiltered {
headers = append(headers, "Filtered")
Expand Down
57 changes: 43 additions & 14 deletions cmd/gobgp/vrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -53,7 +54,7 @@ func getVrfs() ([]*api.Vrf, error) {
}

func showVrfs() error {
maxLens := []int{20, 20, 20, 20, 5}
maxLens := []int{20, 20, 20, 20, 20, 5, 20, 5}
vrfs, err := getVrfs()
if err != nil {
return err
Expand Down Expand Up @@ -98,19 +99,19 @@ func showVrfs() error {
if err != nil {
return err
}
lines = append(lines, []string{name, rdStr, importRts, exportRts, fmt.Sprintf("%d", v.Id)})
lines = append(lines, []string{name, rdStr, importRts, exportRts, v.RoutersMac, fmt.Sprintf("%d", v.Id), fmt.Sprintf("%v", v.ImportAsEvpnIpprefix), fmt.Sprintf("%d", v.EthernetTag)})

for i, v := range []int{len(name), len(rdStr), len(importRts), len(exportRts)} {
for i, v := range []int{len(name), len(rdStr), len(importRts), len(exportRts), len(v.RoutersMac)} {
if v > maxLens[i] {
maxLens[i] = v + 4
}
}

}
format := fmt.Sprintf(" %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds\n", maxLens[0], maxLens[1], maxLens[2], maxLens[3], maxLens[4])
fmt.Printf(format, "Name", "RD", "Import RT", "Export RT", "ID")
format := fmt.Sprintf(" %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds\n", maxLens[0], maxLens[1], maxLens[2], maxLens[3], maxLens[4], maxLens[5], maxLens[6], maxLens[7])
fmt.Printf(format, "Name", "RD", "Import RT", "Export RT", "Router's MAC", "ID", "Import as EVPN", "Ethernet Tag")
for _, l := range lines {
fmt.Printf(format, l[0], l[1], l[2], l[3], l[4])
fmt.Printf(format, l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7])
}
return nil
}
Expand All @@ -123,9 +124,13 @@ func modVrf(typ string, args []string) error {
switch typ {
case cmdAdd:
a, err := extractReserved(args, map[string]int{
"rd": paramSingle,
"rt": paramList,
"id": paramSingle})
"rd": paramSingle,
"rt": paramList,
"id": paramSingle,
"import-as-evpn": paramFlag,
"routers-mac": paramSingle,
"ethernet-tag": paramSingle,
})
if err != nil || len(a[""]) != 1 || len(a["rd"]) != 1 || len(a["rt"]) < 2 {
//lint:ignore ST1005 cli example
return fmt.Errorf("usage: gobgp vrf add <vrf name> [ id <id> ] rd <rd> rt { import | export | both } <rt>...")
Expand Down Expand Up @@ -172,13 +177,37 @@ func modVrf(typ string, args []string) error {
irt, _ := apiutil.MarshalRTs(importRt)
ert, _ := apiutil.MarshalRTs(exportRt)

var etag uint64
if len(a["ethernet-tag"]) > 0 {
etag, err = strconv.ParseUint(a["ethernet-tag"][0], 10, 32)
if err != nil {
return err
}
}

importAsEVPN := false
if _, ok := a["import-as-evpn"]; ok {
importAsEVPN = true
}

routersMac := ""
if mac, ok := a["routers-mac"]; ok {
if _, err := net.ParseMAC(mac[0]); err != nil {
return fmt.Errorf("invalid router's mac: %q", mac[0])
}
routersMac = mac[0]
}

_, err = client.AddVrf(ctx, &api.AddVrfRequest{
Vrf: &api.Vrf{
Name: name,
Rd: v,
ImportRt: irt,
ExportRt: ert,
Id: uint32(id),
Name: name,
Rd: v,
ImportRt: irt,
ExportRt: ert,
Id: uint32(id),
ImportAsEvpnIpprefix: importAsEVPN,
RoutersMac: routersMac,
EthernetTag: uint32(etag),
},
})
return err
Expand Down
9 changes: 9 additions & 0 deletions docs/sources/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@
# are preferred than both-rt-list.
both-rt-list = ["65000:100"]

[[vrfs]]
[vrfs.config]
name = "vrf2"
rd = "65000:200"
both-rt-list = ["65000:200"]
import-as-evpn-ipprefix = true
routers-mac = "ca:fe:00:00:be:ef"
ethernet-tag = 100

[[mrt-dump]]
[mrt-dump.config]
dump-type = "updates"
Expand Down
64 changes: 55 additions & 9 deletions internal/pkg/table/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -1115,11 +1115,18 @@ func (lhs *Path) Compare(rhs *Path) int {

func (v *Vrf) ToGlobalPath(path *Path) error {
nlri := path.GetNlri()
addRoutersMacAttr := false
switch rf := path.GetRouteFamily(); rf {
case bgp.RF_IPv4_UC:
n := nlri.(*bgp.IPAddrPrefix)
pathIdentifier := path.GetNlri().PathIdentifier()
path.OriginInfo().nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(v.MplsLabel), v.Rd)
if v.ImportToGlobalAsEvpnType5 {
esi, _ := bgp.ParseEthernetSegmentIdentifier([]string{"single-homed"})
path.OriginInfo().nlri = bgp.NewEVPNIPPrefixRoute(v.Rd, esi, v.EthernetTag, n.Length, n.Prefix.String(), "0.0.0.0", 0)
addRoutersMacAttr = true
} else {
path.OriginInfo().nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(v.MplsLabel), v.Rd)
}
path.GetNlri().SetPathIdentifier(pathIdentifier)
case bgp.RF_FS_IPv4_UC:
n := nlri.(*bgp.FlowSpecIPv4Unicast)
Expand All @@ -1129,7 +1136,13 @@ func (v *Vrf) ToGlobalPath(path *Path) error {
case bgp.RF_IPv6_UC:
n := nlri.(*bgp.IPv6AddrPrefix)
pathIdentifier := path.GetNlri().PathIdentifier()
path.OriginInfo().nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(v.MplsLabel), v.Rd)
if v.ImportToGlobalAsEvpnType5 {
esi, _ := bgp.ParseEthernetSegmentIdentifier([]string{"single-homed"})
path.OriginInfo().nlri = bgp.NewEVPNIPPrefixRoute(v.Rd, esi, v.EthernetTag, n.Length, n.Prefix.String(), "0.0.0.0", 0)
addRoutersMacAttr = true
} else {
path.OriginInfo().nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(v.MplsLabel), v.Rd)
}
path.GetNlri().SetPathIdentifier(pathIdentifier)
case bgp.RF_FS_IPv6_UC:
n := nlri.(*bgp.FlowSpecIPv6Unicast)
Expand Down Expand Up @@ -1159,23 +1172,41 @@ func (v *Vrf) ToGlobalPath(path *Path) error {
default:
return fmt.Errorf("unsupported route family for vrf: %s", rf)
}
path.SetExtCommunities(v.ExportRt, false)
extCommunity := v.ExportRt
if addRoutersMacAttr {
extCommunity = append(extCommunity, bgp.NewRoutersMacExtended(v.RoutersMac))
}
path.SetExtCommunities(extCommunity, false)

return nil
}

func (p *Path) ToGlobal(vrf *Vrf) *Path {
nlri := p.GetNlri()
nh := p.GetNexthop()
pathId := nlri.PathIdentifier()
addRoutersMacAttr := false
switch rf := p.GetRouteFamily(); rf {
case bgp.RF_IPv4_UC:
n := nlri.(*bgp.IPAddrPrefix)
nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(vrf.MplsLabel), vrf.Rd)
nlri.SetPathIdentifier(pathId)
if vrf.ImportToGlobalAsEvpnType5 {
esi, _ := bgp.ParseEthernetSegmentIdentifier([]string{"single-homed"})
nlri = bgp.NewEVPNIPPrefixRoute(vrf.Rd, esi, vrf.EthernetTag, n.Length, n.Prefix.String(), "0.0.0.0", 0)
addRoutersMacAttr = true
} else {
nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(vrf.MplsLabel), vrf.Rd)
nlri.SetPathIdentifier(pathId)
}
case bgp.RF_IPv6_UC:
n := nlri.(*bgp.IPv6AddrPrefix)
nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(vrf.MplsLabel), vrf.Rd)
nlri.SetPathIdentifier(pathId)
if vrf.ImportToGlobalAsEvpnType5 {
esi, _ := bgp.ParseEthernetSegmentIdentifier([]string{"single-homed"})
nlri = bgp.NewEVPNIPPrefixRoute(vrf.Rd, esi, vrf.EthernetTag, n.Length, n.Prefix.String(), "0.0.0.0", 0)
addRoutersMacAttr = true
} else {
nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(vrf.MplsLabel), vrf.Rd)
nlri.SetPathIdentifier(pathId)
}
case bgp.RF_EVPN:
n := nlri.(*bgp.EVPNNLRI)
switch n.RouteType {
Expand Down Expand Up @@ -1222,9 +1253,15 @@ func (p *Path) ToGlobal(vrf *Vrf) *Path {
return p
}
path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false)
path.SetExtCommunities(vrf.ExportRt, false)

path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nh.String(), []bgp.AddrPrefixInterface{nlri}))

extCommunities := vrf.ExportRt
if addRoutersMacAttr {
extCommunities = append(extCommunities, bgp.NewRoutersMacExtended(vrf.RoutersMac))
}
path.SetExtCommunities(extCommunities, false)
return path
}

Expand Down Expand Up @@ -1258,12 +1295,21 @@ func (p *Path) ToLocal() *Path {
nlri = bgp.NewFlowSpecIPv6Unicast(n.FlowSpecNLRI.Value)
nlri.SetPathLocalIdentifier(localPathId)
nlri.SetPathIdentifier(pathId)
case bgp.RF_EVPN:
n := nlri.(*bgp.EVPNNLRI)
v, ok := n.RouteTypeData.(*bgp.EVPNIPPrefixRoute)
if !ok {
return p
}
nlri = bgp.NewIPAddrPrefix(v.IPPrefixLength, v.IPPrefix.String())
nlri.SetPathLocalIdentifier(localPathId)
nlri.SetPathIdentifier(pathId)
default:
return p
}
path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false)
switch f {
case bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN:
case bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN, bgp.RF_EVPN:
path.delPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
case bgp.RF_FS_IPv4_VPN, bgp.RF_FS_IPv6_VPN:
extcomms := path.GetExtCommunities()
Expand Down
Loading

0 comments on commit 051d4b9

Please sign in to comment.