Skip to content

Commit

Permalink
Merge pull request #79 from telekom/feature/mgmt-vrf-imports
Browse files Browse the repository at this point in the history
Allow import deny for mgmt VRF
  • Loading branch information
chdxD1 authored Jul 16, 2024
2 parents 1ccefec + f4dfbc2 commit 8e730f4
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 27 deletions.
34 changes: 31 additions & 3 deletions pkg/frr/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,23 @@ type templateConfig struct {
}

func (m *Manager) Configure(in Configuration, nm *nl.Manager) (bool, error) {
config, err := renderSubtemplates(in, nm)
// Remove permit from VRF and only allow deny rules for mgmt VRFs
for i := range in.VRFs {
if in.VRFs[i].Name != m.mgmtVrf {
continue
}
for j := range in.VRFs[i].Import {
for k := range in.VRFs[i].Import[j].Items {
if in.VRFs[i].Import[j].Items[k].Action != "deny" {
return false, fmt.Errorf("only deny rules are allowed in import prefix-lists of mgmt VRFs")
}
// Swap deny to permit, this will be a prefix-list called from a deny route-map
in.VRFs[i].Import[j].Items[k].Action = "permit"
}
}
}

config, err := m.renderSubtemplates(in, nm)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -62,7 +78,15 @@ func (m *Manager) Configure(in Configuration, nm *nl.Manager) (bool, error) {
return false, nil
}

func renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfig, error) {
func (m *Manager) renderRouteMapMgmtIn() ([]byte, error) {
return render(routeMapMgmtInTpl, mgmtImportConfig{
IPv4MgmtRouteMapIn: m.ipv4MgmtRouteMapIn,
IPv6MgmtRouteMapIn: m.ipv6MgmtRouteMapIn,
MgmtVrfName: m.mgmtVrf,
})
}

func (m *Manager) renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfig, error) {
vrfRouterID, err := nlManager.GetUnderlayIP()
if err != nil {
return nil, fmt.Errorf("error getting underlay IP: %w", err)
Expand Down Expand Up @@ -97,6 +121,10 @@ func renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfi
if err != nil {
return nil, err
}
routemapMgmtIn, err := m.renderRouteMapMgmtIn()
if err != nil {
return nil, err
}
asn := in.ASN
if asn == 0 {
asn = vrfAsnConfig
Expand All @@ -118,7 +146,7 @@ func renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfi
NeighborsV6: string(neighborsV6),
BGP: string(bgp),
PrefixLists: string(prefixlists),
RouteMaps: string(routemaps),
RouteMaps: string(routemaps) + "\n" + string(routemapMgmtIn),
UnderlayRouterID: vrfRouterID.String(),
Hostname: hostname,
}, nil
Expand Down
7 changes: 4 additions & 3 deletions pkg/frr/frr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

const (
frrConf = "frr.conf"
mgmtVrf = "mgmt"
)

var (
Expand Down Expand Up @@ -61,20 +62,20 @@ var _ = Describe("frr", func() {
Context("Init() should", func() {
It("return error if cannot read template config", func() {
m := &Manager{}
err := m.Init()
err := m.Init(mgmtVrf)
Expect(err).To(HaveOccurred())
})
It("return error if cannot write template config file", func() {
m := &Manager{ConfigPath: "testdata/" + frrConf}
err := m.Init()
err := m.Init(mgmtVrf)
Expect(err).To(HaveOccurred())
})
It("return no error", func() {
m := &Manager{
ConfigPath: tmpDir + "/" + frrConf,
TemplatePath: tmpDir + "/frr.tpl.conf",
}
err := m.Init()
err := m.Init(mgmtVrf)
Expect(err).ToNot(HaveOccurred())
})
})
Expand Down
29 changes: 24 additions & 5 deletions pkg/frr/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ var (

type Manager struct {
configTemplate *template.Template
ConfigPath string
TemplatePath string
Cli *Cli
dbusToolkit dbus.System

ipv4MgmtRouteMapIn *string
ipv6MgmtRouteMapIn *string
mgmtVrf string

ConfigPath string
TemplatePath string
Cli *Cli
dbusToolkit dbus.System
}

type PrefixList struct {
Expand Down Expand Up @@ -69,7 +74,7 @@ func NewFRRManager() *Manager {
}
}

func (m *Manager) Init() error {
func (m *Manager) Init(mgmtVrf string) error {
if _, err := os.Stat(m.TemplatePath); errors.Is(err, os.ErrNotExist) {
err = generateTemplateConfig(m.TemplatePath, m.ConfigPath)
if err != nil {
Expand All @@ -86,6 +91,20 @@ func (m *Manager) Init() error {
return fmt.Errorf("error creating new FRR config: %w", err)
}
m.configTemplate = tpl

m.mgmtVrf = mgmtVrf
routeMap, err := getRouteMapName(m.ConfigPath, "ipv4", m.mgmtVrf)
if err != nil {
return fmt.Errorf("error getting v4 mgmt route-map from FRR config: %w", err)
}
m.ipv4MgmtRouteMapIn = routeMap

routeMap, err = getRouteMapName(m.ConfigPath, "ipv6", m.mgmtVrf)
if err != nil {
return fmt.Errorf("error getting v6 mgmt route-map from FRR config: %w", err)
}
m.ipv6MgmtRouteMapIn = routeMap

return nil
}

Expand Down
40 changes: 33 additions & 7 deletions pkg/frr/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ var vrfRawTpl string
//go:embed tpl/route-map.tpl
var routeMapRawTpl string

// Template for mgmt vrf in route-map
//
//go:embed tpl/route-map-mgmt-in.tpl
var routeMapMgmtInRawTpl string

// Template for ip prefix-list
//
//go:embed tpl/prefix-list.tpl
Expand All @@ -45,13 +50,14 @@ var neighborV6RawTpl string
var bgpInstanceRawTpl string

var (
vrfTpl = mustParse("vrf", vrfRawTpl)
routeMapTpl = mustParse("route-map", routeMapRawTpl)
prefixListTpl = mustParse("prefix-list", prefixListRawTpl)
neighborTpl = mustParse("neighbor", neighborRawTpl)
neighborV4Tpl = mustParse("neighborv4", neighborV4RawTpl)
neighborV6Tpl = mustParse("neighborv6", neighborV6RawTpl)
bgpInstanceTpl = mustParse("bgpinstance", bgpInstanceRawTpl)
vrfTpl = mustParse("vrf", vrfRawTpl)
routeMapTpl = mustParse("route-map", routeMapRawTpl)
routeMapMgmtInTpl = mustParse("route-map-mgmt-in", routeMapMgmtInRawTpl)
prefixListTpl = mustParse("prefix-list", prefixListRawTpl)
neighborTpl = mustParse("neighbor", neighborRawTpl)
neighborV4Tpl = mustParse("neighborv4", neighborV4RawTpl)
neighborV6Tpl = mustParse("neighborv6", neighborV6RawTpl)
bgpInstanceTpl = mustParse("bgpinstance", bgpInstanceRawTpl)
)

type bgpInstanceConfig struct {
Expand All @@ -60,6 +66,12 @@ type bgpInstanceConfig struct {
ASN int
}

type mgmtImportConfig struct {
IPv4MgmtRouteMapIn *string
IPv6MgmtRouteMapIn *string
MgmtVrfName string
}

func mustParse(name, rawtpl string) *template.Template {
tpl, err := template.New(name).Parse(rawtpl)
if err != nil {
Expand All @@ -77,6 +89,20 @@ func render(tpl *template.Template, vrfs interface{}) ([]byte, error) {
return buf.Bytes(), nil
}

func getRouteMapName(file, addressFamily, mgmtVrfName string) (*string, error) {
fileContent, err := os.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("error reading frr config file %s: %w", file, err)
}
content := string(fileContent)
re := regexp.MustCompile(`(?ms)address-family\s+` + addressFamily + `\s+unicast.*?neighbor\s+def_` + mgmtVrfName + `\s+route-map (\w*)\s+in`)
matches := re.FindStringSubmatch(content)
if len(matches) != len(re.SubexpNames()) {
return nil, nil
}
return &matches[1], nil
}

func generateTemplateConfig(tplFile, original string) error {
fileContent, err := os.ReadFile(original)
if err != nil {
Expand Down
16 changes: 16 additions & 0 deletions pkg/frr/tpl/route-map-mgmt-in.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{ if .IPv4MgmtRouteMapIn }}
route-map {{ .IPv4MgmtRouteMapIn }} permit 5
call rm_{{ .MgmtVrfName }}_import
on-match next
exit
route-map rm_{{ .MgmtVrfName }}_import permit 65535
exit
{{- end }}
{{ if .IPv6MgmtRouteMapIn }}
route-map {{ .IPv6MgmtRouteMapIn }} permit 5
call rm6_{{ .MgmtVrfName }}_import
on-match next
exit
route-map rm6_{{ .MgmtVrfName }}_import permit 65535
exit
{{- end }}
10 changes: 8 additions & 2 deletions pkg/frr/tpl/route-map.tpl
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{{range $vrf := .}}
{{if not $vrf.IsTaaS}}
{{range $i, $pl := $vrf.Import}}
route-map rm_{{$vrf.Name}}_import permit {{$pl.Seq}}
route-map rm_{{$vrf.Name}}_import {{if $vrf.ShouldTemplateVRF}}permit{{else}}deny{{end}} {{$pl.Seq}}
match ip address prefix-list pl_{{$vrf.Name}}_import_{{$i}}
exit
route-map rm6_{{$vrf.Name}}_import permit {{$pl.Seq}}
route-map rm6_{{$vrf.Name}}_import {{if $vrf.ShouldTemplateVRF}}permit{{else}}deny{{end}} {{$pl.Seq}}
match ipv6 address prefix-list pl_{{$vrf.Name}}_import_{{$i}}
exit
{{- end}}
Expand All @@ -31,5 +31,11 @@ route-map rm6_{{$vrf.Name}}_export permit {{$pl.Seq}}
{{- end}}
exit
{{- end -}}
{{if not $vrf.ShouldTemplateVRF}}
route-map rm_{{$vrf.Name}}_import permit 65535
exit
route-map rm6_{{$vrf.Name}}_import permit 65535
exit
{{- end}}
{{- end -}}
{{- end -}}
14 changes: 7 additions & 7 deletions pkg/reconciler/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@ func NewReconciler(clusterClient client.Client, anycastTracker *anycast.Tracker,

reconciler.debouncer = debounce.NewDebouncer(reconciler.reconcileDebounced, defaultDebounceTime, logger)

if val := os.Getenv("FRR_CONFIG_FILE"); val != "" {
reconciler.frrManager.ConfigPath = val
}
if err := reconciler.frrManager.Init(); err != nil {
return nil, fmt.Errorf("error trying to init FRR Manager: %w", err)
}

cfg, err := config.LoadConfig()
if err != nil {
return nil, fmt.Errorf("error loading config: %w", err)
}
reconciler.config = cfg

if val := os.Getenv("FRR_CONFIG_FILE"); val != "" {
reconciler.frrManager.ConfigPath = val
}
if err := reconciler.frrManager.Init(cfg.SkipVRFConfig[0]); err != nil {
return nil, fmt.Errorf("error trying to init FRR Manager: %w", err)
}

nc, err := healthcheck.LoadConfig(healthcheck.NetHealthcheckFile)
if err != nil {
return nil, fmt.Errorf("error loading networking healthcheck config: %w", err)
Expand Down

0 comments on commit 8e730f4

Please sign in to comment.