Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement VRF route leaking #160

Merged
merged 7 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ jobs:
if [ "${{ matrix.ip-family }}" == "ipv4" ]; then SKIP="$SKIP\|IPV6\|DUALSTACK"; fi
if [ "${{ matrix.ip-family }}" == "dual" ]; then SKIP="$SKIP\|IPV6"; fi
if [ "${{ matrix.ip-family }}" == "ipv6" ]; then SKIP="$SKIP\|IPV4\|DUALSTACK"; fi
SKIP="$SKIP\|Leaked.*advertising" # TODO fixed by frr 10.0, remove this when https://github.com/metallb/frr-k8s/issues/165 is fixed
GINKGO_ARGS="--skip $SKIP" TEST_ARGS="--report-path=/tmp/kind_logs" make e2etests

- name: Export kind logs
Expand Down
17 changes: 17 additions & 0 deletions API-DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,22 @@ _Appears in:_
| `lastReloadResult` _string_ | LastReloadResult represents the status of the last configuration update operation by FRR, contains "success" or an error. | | |


#### Import



Import represents the possible imported VRFs to a given router.



_Appears in:_
- [Router](#router)

| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `vrf` _string_ | Vrf is the vrf we want to import from | | |


#### LocalPrefPrefixes


Expand Down Expand Up @@ -339,6 +355,7 @@ _Appears in:_
| `vrf` _string_ | VRF is the host vrf used to establish sessions from this router. | | |
| `neighbors` _[Neighbor](#neighbor) array_ | Neighbors is the list of neighbors we want to establish BGP sessions with. | | |
| `prefixes` _string array_ | Prefixes is the list of prefixes we want to advertise from this router instance. | | |
| `imports` _[Import](#import) array_ | Imports is the list of imported VRFs we want for this router / vrf. | | |


#### SecretReference
Expand Down
11 changes: 11 additions & 0 deletions api/v1beta1/frrconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ type Router struct {
// Prefixes is the list of prefixes we want to advertise from this router instance.
// +optional
Prefixes []string `json:"prefixes,omitempty"`

// Imports is the list of imported VRFs we want for this router / vrf.
// +optional
Imports []Import `json:"imports,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not Imports []string?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because frr allows more complex imports than that, and I wanted to leave the door open in case we want to extend. If we stick with []string, we'll have to another field even if it semantically belongs to the imports.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you name for my knowledge which complex imports you have in mind? I am in favor comment the reasoning why not []string

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's the import from l2vpn logic frr provides. I just don't want to prevent that and make the api extensible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

// Import represents the possible imported VRFs to a given router.
type Import struct {
// Vrf is the vrf we want to import from
// +optional
VRF string `json:"vrf,omitempty"`
}

// Neighbor represents a BGP Neighbor we want FRR to connect to.
Expand Down
20 changes: 20 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ spec:
id:
description: ID is the BGP router ID
type: string
imports:
description: Imports is the list of imported VRFs we want
for this router / vrf.
items:
description: Import represents the possible imported VRFs
to a given router.
properties:
vrf:
description: Vrf is the vrf we want to import from
type: string
type: object
type: array
neighbors:
description: Neighbors is the list of neighbors we want
to establish BGP sessions with.
Expand Down
12 changes: 12 additions & 0 deletions config/all-in-one/frr-k8s-prometheus.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ spec:
id:
description: ID is the BGP router ID
type: string
imports:
description: Imports is the list of imported VRFs we want
for this router / vrf.
items:
description: Import represents the possible imported VRFs
to a given router.
properties:
vrf:
description: Vrf is the vrf we want to import from
type: string
type: object
type: array
neighbors:
description: Neighbors is the list of neighbors we want
to establish BGP sessions with.
Expand Down
12 changes: 12 additions & 0 deletions config/all-in-one/frr-k8s.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ spec:
id:
description: ID is the BGP router ID
type: string
imports:
description: Imports is the list of imported VRFs we want
for this router / vrf.
items:
description: Import represents the possible imported VRFs
to a given router.
properties:
vrf:
description: Vrf is the vrf we want to import from
type: string
type: object
type: array
neighbors:
description: Neighbors is the list of neighbors we want
to establish BGP sessions with.
Expand Down
12 changes: 12 additions & 0 deletions config/crd/bases/frrk8s.metallb.io_frrconfigurations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ spec:
id:
description: ID is the BGP router ID
type: string
imports:
description: Imports is the list of imported VRFs we want
for this router / vrf.
items:
description: Import represents the possible imported VRFs
to a given router.
properties:
vrf:
description: Vrf is the vrf we want to import from
type: string
type: object
type: array
neighbors:
description: Neighbors is the list of neighbors we want
to establish BGP sessions with.
Expand Down
32 changes: 32 additions & 0 deletions e2etests/pkg/address/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier:Apache-2.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general the pattern seems to E2E:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what pattern?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I have been missing the comments that I need to reword before submit the code review.
I am talking about the commit message it has E2E Tests: test, the pattern in previous commits is only E2E:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this


package address

import (
"net"

"go.universe.tf/e2etest/pkg/ipfamily"
)

func FilterForFamily(ips []string, family ipfamily.Family) []string {
if family == ipfamily.DualStack {
return ips
}
var res []string
for _, ip := range ips {
parsedIP, _, _ := net.ParseCIDR(ip)
if parsedIP == nil {
panic("invalid ip") // it's a test after all, should never happen
}
isV4 := (parsedIP.To4() != nil)
if family == ipfamily.IPv4 && isV4 {
res = append(res, ip)
continue
}
if family == ipfamily.IPv6 && !isV4 {
res = append(res, ip)
continue
}
}
return res
}
30 changes: 13 additions & 17 deletions e2etests/pkg/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package routes

import (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the commit message:
is there a them for the commit? like : word before : can be E2E or API, or Controller ?
maybe E2E: description

note: I did not understood that flag means the return bool value, is it valid term?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this done

"bytes"
"errors"
"fmt"
"net"

Expand All @@ -18,55 +17,52 @@ import (

// PodHasPrefixFromContainer tells if the given frr-k8s pod has recevied a route for
// the given prefix from the given container.
func PodHasPrefixFromContainer(pod *v1.Pod, frr frrcontainer.FRR, prefix string) bool {
func PodHasPrefixFromContainer(pod *v1.Pod, frr frrcontainer.FRR, vrf, prefix string) bool {
_, cidr, _ := net.ParseCIDR(prefix)
ipFamily := ipfamily.ForCIDR(cidr)
nextHop := frr.Ipv4
if ipFamily == ipfamily.IPv6 {
nextHop = frr.Ipv6
}
vrf := frr.RouterConfig.VRF
return hasPrefix(pod, ipFamily, cidr, nextHop, vrf)
}

// CheckNeighborHasPrefix tells if the given frr container has a route toward the given prefix
// via the set of node passed to this function.
func CheckNeighborHasPrefix(neighbor frrcontainer.FRR, prefix string, nodes []v1.Node) (bool, error) {
func CheckNeighborHasPrefix(neighbor frrcontainer.FRR, vrf, prefix string, nodes []v1.Node) error {
routesV4, routesV6, err := frr.Routes(neighbor)
if err != nil {
return false, err
return err
}

_, cidr, err := net.ParseCIDR(prefix)
if err != nil {
return false, err
return err
}

route, err := routeForCIDR(cidr, routesV4, routesV6)
var notFound RouteNotFoundError
if errors.As(err, &notFound) {
return false, nil
}
if err != nil {
return false, err
return err
}

cidrFamily := ipfamily.ForCIDR(cidr)
err = frr.RoutesMatchNodes(nodes, route, cidrFamily, neighbor.RouterConfig.VRF)
err = frr.RoutesMatchNodes(nodes, route, cidrFamily, vrf)
if err != nil {
return false, nil
return err
}
return true, nil
return nil
}

func cidrsAreEqual(a, b *net.IPNet) bool {
return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
}

type RouteNotFoundError string
type RouteNotFoundError struct {
Route string
}

func (e RouteNotFoundError) Error() string {
return string(e)
return fmt.Sprintf("route not found %s", e.Route)
}

func routeForCIDR(cidr *net.IPNet, routesV4 map[string]frr.Route, routesV6 map[string]frr.Route) (frr.Route, error) {
Expand All @@ -80,7 +76,7 @@ func routeForCIDR(cidr *net.IPNet, routesV4 map[string]frr.Route, routesV6 map[s
return route, nil
}
}
return frr.Route{}, RouteNotFoundError(fmt.Sprintf("route %s not found", cidr))
return frr.Route{}, RouteNotFoundError{Route: cidr.String()}
}

func hasPrefix(pod *v1.Pod, pairingFamily ipfamily.Family, prefix *net.IPNet, nextHop, vrf string) bool {
Expand Down
7 changes: 3 additions & 4 deletions e2etests/tests/graceful_restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,9 @@ var _ = ginkgo.Describe("Establish BGP session with EnableGracefulRestart", func

check := func() error {
for _, p := range peersConfig.Peers() {
found, err := routes.CheckNeighborHasPrefix(p.FRR, prefix, nodes)
Expect(err).NotTo(HaveOccurred())
if !found {
return fmt.Errorf("Neigh %s does not have prefix %s", p.FRR.Name, prefix)
err := routes.CheckNeighborHasPrefix(p.FRR, p.FRR.RouterConfig.VRF, prefix, nodes)
if err != nil {
return fmt.Errorf("Neigh %s does not have prefix %s: %w", p.FRR.Name, prefix, err)
}
}
return nil
Expand Down
Loading
Loading