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 static routing support #30

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions api/v1alpha1/vrfrouteconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ type VRFRouteConfigurationSpec struct {
// Aggregate Routes that should be announced
Aggregate []string `json:"aggregate,omitempty"`

StaticRoutes []string `json:"staticRoutes,omitempty"`

// +kubebuilder:validation:Required
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65534
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/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 @@ -113,6 +113,10 @@ spec:
maximum: 65534
minimum: 1
type: integer
staticRoutes:
items:
type: string
type: array
vrf:
description: VRF this configuration refers to
maxLength: 12
Expand Down
5 changes: 5 additions & 0 deletions pkg/frr/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ type VRFConfiguration struct {
AggregateIPv6 []string
Import []PrefixList
Export []PrefixList

StaticRoutesIPv4 []string
StaticRoutesIPv6 []string
StaticRouteInterface string
StaticRouteIPv6NextHop string
}

type FRRConfiguration struct {
Expand Down
6 changes: 6 additions & 0 deletions pkg/frr/tpl/vrf.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ vrf vr.{{$vrf.Name}}
vni {{$vrf.VNI}}
exit-vrf
{{end}}
{{range $item := $vrf.StaticRoutesIPv4}}
ip route {{$item}} 169.254.0.1 {{$vrf.StaticRouteInterface}}
{{- end }}
{{range $item := $vrf.StaticRoutesIPv6}}
ipv6 route {{$item}} {{$vrf.StaticRouteIPv6NextHop}} {{$vrf.StaticRouteInterface}}
{{- end }}
{{- end -}}
51 changes: 51 additions & 0 deletions pkg/nl/layer3.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package nl

import (
"fmt"
"net"
"sort"
"strings"

"github.com/telekom/das-schiff-network-operator/pkg/bpf"
"github.com/vishvananda/netlink"
)

type VRFInformation struct {
Expand Down Expand Up @@ -139,3 +142,51 @@ func (n *NetlinkManager) GetL3ByName(name string) (*VRFInformation, error) {
}
return nil, fmt.Errorf("no VRF with name %s", name)
}

func (n *NetlinkManager) getVethFromVRF(links []netlink.Link, vrf *netlink.Vrf) (*netlink.Veth, error) {
for _, link := range links {
if link.Attrs().MasterIndex == vrf.Attrs().Index && link.Type() == "veth" && strings.HasPrefix(link.Attrs().Name, VRF_TO_DEFAULT_PREFIX) {
return link.(*netlink.Veth), nil
}
}
return nil, fmt.Errorf("no veth in vrf %s found", vrf.Attrs().Name)
}

func (n *NetlinkManager) GetInterfaceAndNexthop(vrf string) (string, net.IP, error) {
links, err := netlink.LinkList()
if err != nil {
return "", nil, err
}
for _, link := range links {
if link.Type() != "vrf" {
continue
}
if link.Attrs().Name != fmt.Sprintf("Vrf_%s", vrf) && link.Attrs().Name != fmt.Sprintf("%s%s", VRF_PREFIX, vrf) {
continue
}
veth, err := n.getVethFromVRF(links, link.(*netlink.Vrf))
if err != nil {
return "", nil, err
}

vethPeerIdx, err := netlink.VethPeerIndex(veth)
if err != nil {
return "", nil, err
}

vethPeer, err := netlink.LinkByIndex(vethPeerIdx)
if err != nil {
return "", nil, err
}

addresses, err := netlink.AddrList(veth, netlink.FAMILY_V4)
if err != nil {
return "", nil, err
}
if len(addresses) != 1 {
return "", nil, fmt.Errorf("veth in vrf %s has more than one IPv6 address", vrf)
}
return vethPeer.Attrs().Name, addresses[0].IP, nil
}
return "", nil, fmt.Errorf("no vrf with name %s found", vrf)
}
20 changes: 20 additions & 0 deletions pkg/reconciler/layer3.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ func (r *reconcile) reconcileLayer3(l3vnis []networkv1alpha1.VRFRouteConfigurati
config.AggregateIPv4 = append(config.AggregateIPv4, aggregate)
}
}
for _, staticRoute := range spec.StaticRoutes {
_, network, err := net.ParseCIDR(staticRoute)
if err != nil {
return err
}
if network.IP.To4() == nil {
config.StaticRoutesIPv6 = append(config.StaticRoutesIPv6, staticRoute)
} else {
config.StaticRoutesIPv4 = append(config.StaticRoutesIPv4, staticRoute)
}
}
vrfConfigMap[spec.VRF] = config
}

Expand All @@ -105,6 +116,15 @@ func (r *reconcile) reconcileLayer3(l3vnis []networkv1alpha1.VRFRouteConfigurati
// We wait here for two seconds to let FRR settle after updating netlink devices
time.Sleep(2 * time.Second)

for _, vrf := range vrfConfigs {
routeInterface, routeNextHop, err := r.netlinkManager.GetInterfaceAndNexthop(vrf.Name)
if err != nil {
return err
}
vrf.StaticRouteInterface = routeInterface
vrf.StaticRouteIPv6NextHop = routeNextHop.String()
}

changed, err := r.frrManager.Configure(frr.FRRConfiguration{
VRFs: vrfConfigs,
ASN: r.config.ServerASN,
Expand Down