Skip to content

Commit

Permalink
Add support for BGP large communities
Browse files Browse the repository at this point in the history
This patch only implements large communities but the format is
compatible with a future implementation of extended communities.

Community types are distinguished by using Juniper style syntax, thus
<AS number>:<community value>,<type>:<administrator>:<assigned-number>,
large:<global administrator>:<localdata part 1>:<localdata part 2> for
each of the respective types legacy, extended and large communities.

Also update CRD descriptions for large communities

Signed-off-by: Andreas Karis <ak.karis@gmail.com>
  • Loading branch information
andreaskaris authored and fedepaol committed May 2, 2023
1 parent 1459de5 commit eb58887
Show file tree
Hide file tree
Showing 29 changed files with 1,068 additions and 201 deletions.
5 changes: 3 additions & 2 deletions api/v1beta1/bgpadvertisement_types.go
Expand Up @@ -41,8 +41,9 @@ type BGPAdvertisementSpec struct {
// +optional
LocalPref uint32 `json:"localPref,omitempty"`

// The BGP communities to be associated with the announcement. Each item can be a
// community of the form 1234:1234 or the name of an alias defined in the Community CRD.
// The BGP communities to be associated with the announcement. Each item can be a standard community of the
// form 1234:1234, a large community of the form large:1234:1234:1234 or the name of an alias defined in the
// Community CRD.
// +optional
Communities []string `json:"communities,omitempty"`

Expand Down
3 changes: 2 additions & 1 deletion api/v1beta1/community_types.go
Expand Up @@ -23,7 +23,8 @@ import (
type CommunityAlias struct {
// The name of the alias for the community.
Name string `json:"name,omitempty"`
// The BGP community value corresponding to the given name.
// The BGP community value corresponding to the given name. Can be a standard community of the form 1234:1234
// or a large community of the form large:1234:1234:1234.
Value string `json:"value,omitempty"`
}

Expand Down
5 changes: 3 additions & 2 deletions config/crd/bases/metallb.io_bgpadvertisements.yaml
Expand Up @@ -67,8 +67,9 @@ spec:
type: integer
communities:
description: The BGP communities to be associated with the announcement.
Each item can be a community of the form 1234:1234 or the name of
an alias defined in the Community CRD.
Each item can be a standard community of the form 1234:1234, a large
community of the form large:1234:1234:1234 or the name of an alias
defined in the Community CRD.
items:
type: string
type: array
Expand Down
3 changes: 2 additions & 1 deletion config/crd/bases/metallb.io_communities.yaml
Expand Up @@ -44,7 +44,8 @@ spec:
type: string
value:
description: The BGP community value corresponding to the given
name.
name. Can be a standard community of the form 1234:1234 or
a large community of the form large:1234:1234:1234.
type: string
type: object
type: array
Expand Down
8 changes: 5 additions & 3 deletions config/manifests/metallb-frr-prometheus.yaml
Expand Up @@ -389,8 +389,9 @@ spec:
type: integer
communities:
description: The BGP communities to be associated with the announcement.
Each item can be a community of the form 1234:1234 or the name of
an alias defined in the Community CRD.
Each item can be a standard community of the form 1234:1234, a large
community of the form large:1234:1234:1234 or the name of an alias
defined in the Community CRD.
items:
type: string
type: array
Expand Down Expand Up @@ -883,7 +884,8 @@ spec:
type: string
value:
description: The BGP community value corresponding to the given
name.
name. Can be a standard community of the form 1234:1234 or
a large community of the form large:1234:1234:1234.
type: string
type: object
type: array
Expand Down
8 changes: 5 additions & 3 deletions config/manifests/metallb-frr.yaml
Expand Up @@ -389,8 +389,9 @@ spec:
type: integer
communities:
description: The BGP communities to be associated with the announcement.
Each item can be a community of the form 1234:1234 or the name of
an alias defined in the Community CRD.
Each item can be a standard community of the form 1234:1234, a large
community of the form large:1234:1234:1234 or the name of an alias
defined in the Community CRD.
items:
type: string
type: array
Expand Down Expand Up @@ -883,7 +884,8 @@ spec:
type: string
value:
description: The BGP community value corresponding to the given
name.
name. Can be a standard community of the form 1234:1234 or
a large community of the form large:1234:1234:1234.
type: string
type: object
type: array
Expand Down
8 changes: 5 additions & 3 deletions config/manifests/metallb-native-prometheus.yaml
Expand Up @@ -389,8 +389,9 @@ spec:
type: integer
communities:
description: The BGP communities to be associated with the announcement.
Each item can be a community of the form 1234:1234 or the name of
an alias defined in the Community CRD.
Each item can be a standard community of the form 1234:1234, a large
community of the form large:1234:1234:1234 or the name of an alias
defined in the Community CRD.
items:
type: string
type: array
Expand Down Expand Up @@ -883,7 +884,8 @@ spec:
type: string
value:
description: The BGP community value corresponding to the given
name.
name. Can be a standard community of the form 1234:1234 or
a large community of the form large:1234:1234:1234.
type: string
type: object
type: array
Expand Down
8 changes: 5 additions & 3 deletions config/manifests/metallb-native.yaml
Expand Up @@ -389,8 +389,9 @@ spec:
type: integer
communities:
description: The BGP communities to be associated with the announcement.
Each item can be a community of the form 1234:1234 or the name of
an alias defined in the Community CRD.
Each item can be a standard community of the form 1234:1234, a large
community of the form large:1234:1234:1234 or the name of an alias
defined in the Community CRD.
items:
type: string
type: array
Expand Down Expand Up @@ -883,7 +884,8 @@ spec:
type: string
value:
description: The BGP community value corresponding to the given
name.
name. Can be a standard community of the form 1234:1234 or
a large community of the form large:1234:1234:1234.
type: string
type: object
type: array
Expand Down
29 changes: 28 additions & 1 deletion e2etest/bgptests/bgp.go
Expand Up @@ -846,6 +846,20 @@ var _ = ginkgo.Describe("BGP", func() {
false,
ipfamily.IPv4,
[]metallbv1beta1.Community{}),
ginkgo.Entry("FRR - IPV4 - large community and localpref",
"192.168.10.0/24",
"192.168.16.0/24",
metallbv1beta1.BGPAdvertisement{
ObjectMeta: metav1.ObjectMeta{Name: "advertisement"},
Spec: metallbv1beta1.BGPAdvertisementSpec{
Communities: []string{"large:123:456:7890"},
LocalPref: 50,
IPAddressPools: []string{"bgp-with-advertisement"},
},
},
false,
ipfamily.IPv4,
[]metallbv1beta1.Community{}),
ginkgo.Entry("IPV4 - localpref",
"192.168.10.0/24",
"192.168.16.0/24",
Expand Down Expand Up @@ -1011,8 +1025,21 @@ var _ = ginkgo.Describe("BGP", func() {
},
false,
ipfamily.IPv6,
[]metallbv1beta1.Community{}),
ginkgo.Entry("FRR - IPV6 - large community and localpref",
"fc00:f853:0ccd:e799::0-fc00:f853:0ccd:e799::18",
"fc00:f853:0ccd:e799::19-fc00:f853:0ccd:e799::26",
metallbv1beta1.BGPAdvertisement{
ObjectMeta: metav1.ObjectMeta{Name: "advertisement"},
Spec: metallbv1beta1.BGPAdvertisementSpec{
Communities: []string{"large:123:456:7890"},
LocalPref: 50,
IPAddressPools: []string{"bgp-with-advertisement"},
},
},
false,
ipfamily.IPv6,
[]metallbv1beta1.Community{}))

})

ginkgo.Context("MetalLB FRR rejects", func() {
Expand Down
17 changes: 14 additions & 3 deletions e2etest/pkg/frr/bgp.go
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/pkg/errors"
"go.universe.tf/metallb/e2etest/pkg/executor"

"go.universe.tf/metallb/internal/bgp/community"
bgpfrr "go.universe.tf/metallb/internal/bgp/frr"
"go.universe.tf/metallb/internal/ipfamily"
)
Expand Down Expand Up @@ -97,17 +98,27 @@ func RoutesForFamily(exec executor.Executor, family ipfamily.Family) (map[string
}

// RoutesForCommunity returns informations about routes in the given executor related to the given community.
func RoutesForCommunity(exec executor.Executor, community string, family ipfamily.Family) (map[string]bgpfrr.Route, error) {
func RoutesForCommunity(exec executor.Executor, communityString string, family ipfamily.Family) (map[string]bgpfrr.Route, error) {
// Parse c string to BGP c and determine if this is a legacy or large c.
c, err := community.New(communityString)
if err != nil {
return nil, err
}
communityType := "community"
if community.IsLarge(c) {
communityType = "large-community"
}

families := []string{family.String()}
if family == ipfamily.DualStack {
families = []string{ipfamily.IPv4.String(), ipfamily.IPv6.String()}
}

routes := map[string]bgpfrr.Route{}
for _, f := range families {
res, err := exec.Exec("vtysh", "-c", fmt.Sprintf("show bgp %s community %s json", f, community))
res, err := exec.Exec("vtysh", "-c", fmt.Sprintf("show bgp %s %s %s json", f, communityType, c))
if err != nil {
return nil, errors.Wrapf(err, "Failed to query routes for family %s community %s", f, community)
return nil, errors.Wrapf(err, "Failed to query routes for family %s %s %s", f, communityType, c)
}

r, err := bgpfrr.ParseRoutes(res)
Expand Down
34 changes: 20 additions & 14 deletions e2etest/webhookstests/webhooks.go
Expand Up @@ -224,7 +224,7 @@ var _ = ginkgo.Describe("Webhooks", func() {
})

ginkgo.Context("For Community", func() {
ginkgo.It("Should reject a new invalid Community", func() {
ginkgo.DescribeTable("reject a new invalid Community", func(community, expectedError string) {
ginkgo.By("Creating invalid Community")
resources := metallbconfig.ClusterResources{
Communities: []metallbv1beta1.Community{
Expand All @@ -236,7 +236,7 @@ var _ = ginkgo.Describe("Webhooks", func() {
Communities: []metallbv1beta1.CommunityAlias{
{
Name: "INVALID_COMMUNITY",
Value: "99999999:1",
Value: community,
},
},
},
Expand All @@ -245,10 +245,12 @@ var _ = ginkgo.Describe("Webhooks", func() {
}
err := ConfigUpdater.Update(resources)
framework.ExpectError(err)
Expect(err.Error()).To(ContainSubstring("invalid first section of community"))
})
Expect(err.Error()).To(ContainSubstring(expectedError))
},
ginkgo.Entry("in legacy format", "99999999:1", "invalid community value: invalid section"),
ginkgo.Entry("in large format", "lar:123:9999:123", "expected community to be of format large"))

ginkgo.It("Should reject an update to an invalid Community", func() {
ginkgo.DescribeTable("reject an update to an invalid Community", func(community, expectedError string) {
ginkgo.By("Creating Community")
resources := metallbconfig.ClusterResources{
Communities: []metallbv1beta1.Community{
Expand All @@ -267,16 +269,18 @@ var _ = ginkgo.Describe("Webhooks", func() {
Communities: []metallbv1beta1.CommunityAlias{
{
Name: "INVALID_COMMUNITY",
Value: "99999999:1",
Value: community,
},
},
}
err = ConfigUpdater.Update(resources)
framework.ExpectError(err)
Expect(err.Error()).To(ContainSubstring("invalid first section of community"))
})
Expect(err.Error()).To(ContainSubstring(expectedError))
},
ginkgo.Entry("in legacy format", "99999999:1", "invalid community value: invalid section"),
ginkgo.Entry("in large format", "lar:123:9999:123", "expected community to be of format large"))

ginkgo.It("Should reject Community duplications", func() {
ginkgo.DescribeTable("reject Community duplications", func(community string) {
ginkgo.By("Creating duplicates in the same Community")
resources := metallbconfig.ClusterResources{
Communities: []metallbv1beta1.Community{
Expand All @@ -288,11 +292,11 @@ var _ = ginkgo.Describe("Webhooks", func() {
Communities: []metallbv1beta1.CommunityAlias{
{
Name: "DUP_COMMUNITY",
Value: "1111:2222",
Value: community,
},
{
Name: "DUP_COMMUNITY",
Value: "1111:2222",
Value: community,
},
},
},
Expand All @@ -314,7 +318,7 @@ var _ = ginkgo.Describe("Webhooks", func() {
Communities: []metallbv1beta1.CommunityAlias{
{
Name: "DUP_COMMUNITY",
Value: "1111:2222",
Value: community,
},
},
},
Expand All @@ -327,7 +331,7 @@ var _ = ginkgo.Describe("Webhooks", func() {
Communities: []metallbv1beta1.CommunityAlias{
{
Name: "DUP_COMMUNITY",
Value: "1111:2222",
Value: community,
},
},
},
Expand All @@ -337,7 +341,9 @@ var _ = ginkgo.Describe("Webhooks", func() {
err = ConfigUpdater.Update(resources)
framework.ExpectError(err)
Expect(err.Error()).To(ContainSubstring("duplicate definition of community"))
})
},
ginkgo.Entry("in legacy format", "1111:2222"),
ginkgo.Entry("FRR - in large format", "large:123:9999:123"))
})

ginkgo.Context("For BFDProfile", func() {
Expand Down
3 changes: 2 additions & 1 deletion internal/bgp/bgp.go
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/go-kit/log"
"go.universe.tf/metallb/internal/bgp/community"
"go.universe.tf/metallb/internal/config"
)

Expand All @@ -20,7 +21,7 @@ type Advertisement struct {
// peers (i.e. where the peer ASN matches the local ASN).
LocalPref uint32
// BGP communities to attach to the path.
Communities []uint32
Communities []community.BGPCommunity
// Used to declare the intent of announcing IPs
// only to the BGPPeers in this list.
Peers []string
Expand Down

0 comments on commit eb58887

Please sign in to comment.