Skip to content
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
61 changes: 61 additions & 0 deletions internal/provider/cisco/iosxr/prefix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0

package iosxr

import (
"fmt"

"github.com/ironcore-dev/network-operator/internal/transport/gnmiext"
)

var _ gnmiext.DataElement = (*PrefixList)(nil)

type PrefixList struct {
Name string `json:"prefix-list-name"`
Sequences struct {
Sequence gnmiext.List[int32, *PrefixEntry] `json:"sequence"`
} `json:"sequences"`
// Is6 indicates whether this is an IPv6 prefix list. This field is not serialized to JSON
// and is only used internally to determine the correct XPath for the prefix list.
Is6 bool `json:"-"`
}

func (*PrefixList) IsListItem() {}

func (p *PrefixList) XPath() string {
if p.Is6 {
return fmt.Sprintf("Cisco-IOS-XR-um-ipv6-prefix-list-cfg:ipv6/prefix-lists/prefix-list[prefix-list-name=%s]", p.Name)
}
return fmt.Sprintf("Cisco-IOS-XR-um-ipv4-prefix-list-cfg:ipv4/prefix-lists/prefix-list[prefix-list-name=%s]", p.Name)
}

type PrefixEntry struct {
SequenceNumber int32 `json:"sequence-number"`
Permission Permission `json:"permission"`
Prefix string `json:"prefix"`
Mask string `json:"mask"`
MatchPrefixLength *MatchPrefixLength `json:"match-prefix-length,omitempty"`
}

func (e *PrefixEntry) Key() int32 { return e.SequenceNumber }

type Permission string

const (
PermissionPermit Permission = "permit"
PermissionDeny Permission = "deny"
)

type PrefixType string

const (
PrefixTypeIPv4 PrefixType = "ipv4"
PrefixTypeIPv6 PrefixType = "ipv6"
)

type MatchPrefixLength struct {
EQ int8 `json:"eq,omitempty"`
GE int8 `json:"ge,omitempty"`
LE int8 `json:"le,omitempty"`
}
52 changes: 52 additions & 0 deletions internal/provider/cisco/iosxr/prefix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0

package iosxr

import (
"github.com/ironcore-dev/network-operator/internal/transport/gnmiext"
)

func init() {
prefixList := new(PrefixList)
prefixList.Name = "PL-IPv4"
prefixList.Is6 = false
prefixList.Sequences.Sequence = gnmiext.List[int32, *PrefixEntry]{}

p1 := &PrefixEntry{
SequenceNumber: 10,
Permission: PermissionPermit,
Prefix: "10.10.0.0",
Mask: "255.255.0.0",
MatchPrefixLength: &MatchPrefixLength{
EQ: 16,
},
}

p2 := &PrefixEntry{
SequenceNumber: 20,
Permission: PermissionPermit,
Prefix: "192.168.1.0",
Mask: "255.255.255.0",
MatchPrefixLength: &MatchPrefixLength{
LE: 30,
},
}

p3 := &PrefixEntry{
SequenceNumber: 30,
Permission: PermissionPermit,
Prefix: "172.16.0.0",
Mask: "255.240.0.0",
MatchPrefixLength: &MatchPrefixLength{
GE: 12,
LE: 24,
},
}

prefixList.Sequences.Sequence.Set(p1)
prefixList.Sequences.Sequence.Set(p2)
prefixList.Sequences.Sequence.Set(p3)

Register("prefix", prefixList)
}
37 changes: 37 additions & 0 deletions internal/provider/cisco/iosxr/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var (
_ provider.VRFProvider = &Provider{}
_ provider.BGPProvider = &Provider{}
_ provider.BGPPeerProvider = &Provider{}
_ provider.PrefixSetProvider = &Provider{}
)

type Provider struct {
Expand Down Expand Up @@ -574,6 +575,42 @@ func (p *Provider) GetPeerStatus(ctx context.Context, req *provider.BGPPeerStatu
return state, nil
}

func (p *Provider) EnsurePrefixSet(ctx context.Context, req *provider.PrefixSetRequest) error {
s := new(PrefixList)
s.Name = req.PrefixSet.Spec.Name
s.Is6 = req.PrefixSet.Is6()
for _, entry := range req.PrefixSet.Spec.Entries {
bits := int8(entry.Prefix.Bits()) // #nosec G115 -- prefix length is max 32 (IPv4) or 128 (IPv6)
pfxEntry := &PrefixEntry{
SequenceNumber: entry.Sequence,
Permission: PermissionPermit,
Prefix: entry.Prefix.Addr().String(),
Mask: net.IP(net.CIDRMask(entry.Prefix.Bits(), entry.Prefix.Addr().BitLen())).String(),
MatchPrefixLength: &MatchPrefixLength{
EQ: bits,
},
}

if entry.MaskLengthRange != nil && (entry.MaskLengthRange.Min != bits || entry.MaskLengthRange.Max != bits) {
pfxEntry.MatchPrefixLength.GE = entry.MaskLengthRange.Min
pfxEntry.MatchPrefixLength.LE = entry.MaskLengthRange.Max
pfxEntry.MatchPrefixLength.EQ = 0
}

s.Sequences.Sequence.Set(pfxEntry)
}

return p.client.Update(ctx, s)
}

func (p *Provider) DeletePrefixSet(ctx context.Context, req *provider.PrefixSetRequest) error {
s := new(PrefixList)
s.Name = req.PrefixSet.Spec.Name
s.Is6 = req.PrefixSet.Is6()

return p.client.Delete(ctx, s)
}

func init() {
provider.Register("cisco-iosxr-gnmi", NewProvider)
}
41 changes: 41 additions & 0 deletions internal/provider/cisco/iosxr/testdata/prefix.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"ipv4": {
"prefix-lists": {
"prefix-list": {
"prefix-list-name": "PL-IPv4",
"sequences": {
"sequence": [
{
"sequence-number": 10,
"permission": "permit",
"prefix": "10.10.0.0",
"mask": "255.255.0.0",
"match-prefix-length": {
"eq": 16
}
},
{
"sequence-number": 20,
"permission": "permit",
"prefix": "192.168.1.0",
"mask": "255.255.255.0",
"match-prefix-length": {
"le": 30
}
},
{
"sequence-number": 30,
"permission": "permit",
"prefix": "172.16.0.0",
"mask": "255.240.0.0",
"match-prefix-length": {
"ge": 12,
"le": 24
}
}
]
}
}
}
}
}
4 changes: 4 additions & 0 deletions internal/provider/cisco/iosxr/testdata/prefix.json.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ipv4 prefix-list PL-IPv4
10 permit 10.10.0.0/16 ge 16
20 permit 192.168.1.0/24 le 30
30 permit 172.16.0.0/12 ge 12 le 24
Loading