Skip to content

Commit

Permalink
Support neighbor PrefixesWithCommunity
Browse files Browse the repository at this point in the history
Signed-off-by: Ori Braunshtein <[email protected]>
  • Loading branch information
oribon committed Jul 17, 2023
1 parent 0837ace commit 8e79d61
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 11 deletions.
54 changes: 49 additions & 5 deletions internal/controller/api_to_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ package controller

import (
"fmt"
"sort"

v1beta1 "github.com/metallb/frrk8s/api/v1beta1"
"github.com/metallb/frrk8s/internal/frr"
"github.com/metallb/frrk8s/internal/ipfamily"
"k8s.io/apimachinery/pkg/util/sets"
)

func apiToFRR(fromK8s v1beta1.FRRConfiguration) (*frr.Config, error) {
Expand Down Expand Up @@ -75,17 +77,16 @@ func neighborToFRR(n v1beta1.Neighbor, ipv4Prefixes, ipv6Prefixes []string) (*fr
EBGPMultiHop: n.EBGPMultiHop,
}

advs := map[string]*frr.AdvertisementConfig{}
if n.ToAdvertise.Allowed.Mode == v1beta1.AllowAll {
for _, p := range ipv4Prefixes {
res.Advertisements = append(res.Advertisements, &frr.AdvertisementConfig{Prefix: p, IPFamily: ipfamily.IPv4})
advs[p] = &frr.AdvertisementConfig{Prefix: p, IPFamily: ipfamily.IPv4}
res.HasV4Advertisements = true
}
for _, p := range ipv6Prefixes {
res.Advertisements = append(res.Advertisements, &frr.AdvertisementConfig{Prefix: p, IPFamily: ipfamily.IPv6})
advs[p] = &frr.AdvertisementConfig{Prefix: p, IPFamily: ipfamily.IPv6}
res.HasV6Advertisements = true
}

return res, nil
}

for _, p := range n.ToAdvertise.Allowed.Prefixes {
Expand All @@ -96,12 +97,55 @@ func neighborToFRR(n v1beta1.Neighbor, ipv4Prefixes, ipv6Prefixes []string) (*fr
case ipfamily.IPv6:
res.HasV6Advertisements = true
}
res.Advertisements = append(res.Advertisements, &frr.AdvertisementConfig{Prefix: p, IPFamily: family})

// TODO: check that the prefix matches the passed IPv4/IPv6 prefixes
advs[p] = &frr.AdvertisementConfig{Prefix: p, IPFamily: family}
}

communitiesForPrefix := map[string]sets.Set[string]{}
for _, pfxs := range n.ToAdvertise.PrefixesWithCommunity {
// TODO: add community format verification
for _, p := range pfxs.Prefixes {
_, ok := advs[p]
if !ok {
return nil, fmt.Errorf("prefix %s with community %s not in allowed list for neighbor %s", p, pfxs.Community, n.Address)
}
_, ok = communitiesForPrefix[p]
if !ok {
communitiesForPrefix[p] = sets.New(pfxs.Community)
continue
}

communitiesForPrefix[p].Insert(pfxs.Community)
}
}

for p, c := range communitiesForPrefix {
adv, ok := advs[p]
if !ok { // shouldn't happen as we check in previous loop, just in case
return nil, fmt.Errorf("unexpected err - no community prefix matching %s", p)
}
adv.Communities = sets.List(c)
}

res.Advertisements = sortMap(advs)

return res, nil
}

func neighborName(ASN uint32, peerAddr string) string {
return fmt.Sprintf("%d@%s", ASN, peerAddr)
}

func sortMap[T any](toSort map[string]T) []T {
keys := make([]string, 0)
for k := range toSort {
keys = append(keys, k)
}
sort.Strings(keys)
res := make([]T, 0)
for _, k := range keys {
res = append(res, toSort[k])
}
return res
}
175 changes: 175 additions & 0 deletions internal/controller/api_to_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package controller

import (
"fmt"
"testing"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -420,6 +421,180 @@ func TestConversion(t *testing.T) {
},
err: nil,
},
{
name: "Two Neighbor with ToAdvertise, one advertise all, both with communities",
fromK8s: []v1beta1.FRRConfiguration{
{
Spec: v1beta1.FRRConfigurationSpec{
BGP: v1beta1.BGPConfig{
Routers: []v1beta1.Router{
{
ASN: 65040,
ID: "192.0.2.20",
Neighbors: []v1beta1.Neighbor{
{
ASN: 65041,
Address: "192.0.2.21",
Port: 179,
ToAdvertise: v1beta1.Advertise{
Allowed: v1beta1.AllowedPrefixes{
Prefixes: []string{"192.0.2.0/24", "192.0.4.0/24"},
Mode: v1beta1.AllowRestricted,
},
PrefixesWithCommunity: []v1beta1.CommunityPrefixes{
{
Prefixes: []string{"192.0.2.0/24", "192.0.4.0/24"},
Community: "10:100",
},
{
Prefixes: []string{"192.0.2.0/24"},
Community: "10:102",
}, {
Prefixes: []string{"192.0.4.0/24"},
Community: "10:104",
},
},
},
},
{
ASN: 65041,
Address: "192.0.2.22",
Port: 179,
ToAdvertise: v1beta1.Advertise{
Allowed: v1beta1.AllowedPrefixes{
Mode: v1beta1.AllowAll,
},
PrefixesWithCommunity: []v1beta1.CommunityPrefixes{
{
Prefixes: []string{"192.0.2.0/24", "192.0.4.0/24"},
Community: "10:100",
},
{
Prefixes: []string{"192.0.2.0/24"},
Community: "10:102",
},
{
Prefixes: []string{"192.0.2.0/24", "2001:db8::/64"},
Community: "10:108",
},
},
},
},
},
Prefixes: []string{"192.0.2.0/24", "192.0.3.0/24", "192.0.4.0/24", "2001:db8::/64"},
},
},
},
},
},
},
expected: &frr.Config{
Routers: []*frr.RouterConfig{
{
MyASN: 65040,
RouterID: "192.0.2.20",
Neighbors: []*frr.NeighborConfig{
{
IPFamily: ipfamily.IPv4,
Name: "[email protected]",
ASN: 65041,
Addr: "192.0.2.21",
Port: 179,
Advertisements: []*frr.AdvertisementConfig{
{
IPFamily: ipfamily.IPv4,
Prefix: "192.0.2.0/24",
Communities: []string{"10:100", "10:102"},
},
{
IPFamily: ipfamily.IPv4,
Prefix: "192.0.4.0/24",
Communities: []string{"10:100", "10:104"},
},
},
HasV4Advertisements: true,
},
{
IPFamily: ipfamily.IPv4,
Name: "[email protected]",
ASN: 65041,
Addr: "192.0.2.22",
Port: 179,
Advertisements: []*frr.AdvertisementConfig{
{
IPFamily: ipfamily.IPv4,
Prefix: "192.0.2.0/24",
Communities: []string{"10:100", "10:102", "10:108"},
},
{
IPFamily: ipfamily.IPv4,
Prefix: "192.0.3.0/24",
},
{
IPFamily: ipfamily.IPv4,
Prefix: "192.0.4.0/24",
Communities: []string{"10:100"},
},
{
IPFamily: ipfamily.IPv6,
Prefix: "2001:db8::/64",
Communities: []string{"10:108"},
},
},
HasV4Advertisements: true,
HasV6Advertisements: true,
},
},
IPV4Prefixes: []string{"192.0.2.0/24", "192.0.3.0/24", "192.0.4.0/24"},
IPV6Prefixes: []string{"2001:db8::/64"},
},
},
},
err: nil,
},
{
name: "One neighbor, trying to set community on an unallowed prefix",
fromK8s: []v1beta1.FRRConfiguration{
{
Spec: v1beta1.FRRConfigurationSpec{
BGP: v1beta1.BGPConfig{
Routers: []v1beta1.Router{
{
ASN: 65040,
ID: "192.0.2.20",
Neighbors: []v1beta1.Neighbor{
{
ASN: 65041,
Address: "192.0.2.21",
Port: 179,
ToAdvertise: v1beta1.Advertise{
Allowed: v1beta1.AllowedPrefixes{
Prefixes: []string{"192.0.2.0/24", "192.0.4.0/24"},
Mode: v1beta1.AllowRestricted,
},
PrefixesWithCommunity: []v1beta1.CommunityPrefixes{
{
Prefixes: []string{"192.0.2.0/24", "192.0.4.0/24"},
Community: "10:100",
},
{
Prefixes: []string{"192.0.10.10/32"}, // not allowed
Community: "10:100",
},
},
},
},
},
Prefixes: []string{"192.0.2.0/24", "192.0.3.0/24", "192.0.4.0/24", "2001:db8::/64"},
},
},
},
},
},
},
expected: nil,
err: fmt.Errorf("prefix %s with community %s not in allowed list for neighbor %s", "192.0.10.10/32", "10:100", "192.0.2.21"),
},
}

for _, test := range tests {
Expand Down
10 changes: 6 additions & 4 deletions internal/frr/frr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,14 @@ func TestTwoRoutersTwoNeighbors(t *testing.T) {
Port: 4567,
Advertisements: []*AdvertisementConfig{
{
IPFamily: ipfamily.IPv4,
Prefix: "192.169.1.0/24",
IPFamily: ipfamily.IPv4,
Prefix: "192.169.1.0/24",
Communities: []string{"10:169", "10:170"},
},
{
IPFamily: ipfamily.IPv4,
Prefix: "192.170.1.0/22",
IPFamily: ipfamily.IPv4,
Prefix: "192.170.1.0/22",
Communities: []string{"10:170"},
},
},
},
Expand Down
19 changes: 17 additions & 2 deletions internal/frr/testdata/TestTwoRoutersTwoNeighbors.golden
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,30 @@ ip nht resolve-via-default
ipv6 nht resolve-via-default
route-map 192.168.1.2-in deny 20

ip prefix-list 192.168.1.2-10:169-ipv4-community-prefixes permit 192.169.1.0/24
route-map 192.168.1.2-out permit 1
match ip address prefix-list 192.168.1.2-10:169-ipv4-community-prefixes
set community 10:169 additive
on-match next
ip prefix-list 192.168.1.2-10:170-ipv4-community-prefixes permit 192.169.1.0/24
route-map 192.168.1.2-out permit 2
match ip address prefix-list 192.168.1.2-10:170-ipv4-community-prefixes
set community 10:170 additive
on-match next

ip prefix-list 192.168.1.2-pl-ipv4 permit 192.169.1.0/24

ip prefix-list 192.168.1.2-10:170-ipv4-community-prefixes permit 192.170.1.0/22
route-map 192.168.1.2-out permit 3
match ip address prefix-list 192.168.1.2-10:170-ipv4-community-prefixes
set community 10:170 additive
on-match next

ip prefix-list 192.168.1.2-pl-ipv4 permit 192.170.1.0/22

route-map 192.168.1.2-out permit 1
route-map 192.168.1.2-out permit 4
match ip address prefix-list 192.168.1.2-pl-ipv4
route-map 192.168.1.2-out permit 2
route-map 192.168.1.2-out permit 5
match ipv6 address prefix-list 192.168.1.2-pl-ipv4


Expand Down

0 comments on commit 8e79d61

Please sign in to comment.