Skip to content

Commit a8600a2

Browse files
sven-rosenzweigfelix-kaestner
authored andcommitted
[IOS-XR] Support VRF in BGP Spec
Signed-off-by: Sven Rosenzweig <sven.rosenzweig@sap.com> Signed-off-by: Felix Kästner <felix.kaestner@sap.com>
1 parent dba9058 commit a8600a2

7 files changed

Lines changed: 695 additions & 0 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package iosxr
5+
6+
import (
7+
"github.com/ironcore-dev/network-operator/internal/transport/gnmiext"
8+
)
9+
10+
const (
11+
BGPDefaultInstance = "default"
12+
)
13+
14+
var _ gnmiext.DataElement = (*BGP)(nil)
15+
16+
type BGP struct {
17+
InstanceName string `json:"instance-name"`
18+
AS []ASList `json:"as"`
19+
}
20+
21+
type ASList struct {
22+
ASNumber string `json:"as-number"`
23+
}
24+
25+
func (p *BGP) XPath() string {
26+
return "Cisco-IOS-XR-um-router-bgp-cfg:router/bgp/instances/instance[instance-name=default]"
27+
}
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package iosxr
5+
6+
import (
7+
"encoding/json"
8+
"errors"
9+
"fmt"
10+
"math"
11+
"net/netip"
12+
"strconv"
13+
"strings"
14+
15+
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
16+
"github.com/ironcore-dev/network-operator/internal/transport/gnmiext"
17+
)
18+
19+
var (
20+
_ gnmiext.DataElement = (*BGPPeer)(nil)
21+
_ gnmiext.DataElement = (*BGPPeerOperStatus)(nil)
22+
23+
_ json.Marshaler = BGPPeerOperStatus{}
24+
_ json.Unmarshaler = (*BGPPeerOperStatus)(nil)
25+
)
26+
27+
type AfName string
28+
29+
const (
30+
AfNameIpv4Unicast AfName = "ipv4-unicast"
31+
AfNameIpv4Multicast AfName = "ipv4-multicast"
32+
AfNameIpv6Unicast AfName = "ipv6-unicast"
33+
AfNameIpv6Multicast AfName = "ipv6-multicast"
34+
)
35+
36+
type BGPPeerOperSt string
37+
38+
const (
39+
BGPPeerOperStIdle BGPPeerOperSt = "bgp-st-idle"
40+
BGPPeerOperStConnect BGPPeerOperSt = "bgp-st-connect"
41+
BGPPeerOperStActive BGPPeerOperSt = "bgp-st-active"
42+
BGPPeerOperStOpenSent BGPPeerOperSt = "bgp-st-opensent"
43+
BGPPeerOperStOpenConf BGPPeerOperSt = "bgp-st-openconfirm"
44+
BGPPeerOperStEstablished BGPPeerOperSt = "bgp-st-established"
45+
BGGPeerOperStClosing BGPPeerOperSt = "bgp-st-closing"
46+
BGPPeerOperStClosingSyng BGPPeerOperSt = "bgp-st-closing-synrcvd"
47+
)
48+
49+
func (s BGPPeerOperSt) ToSessionState() v1alpha1.BGPPeerSessionState {
50+
switch s {
51+
case BGPPeerOperStIdle:
52+
return v1alpha1.BGPPeerSessionStateIdle
53+
case BGPPeerOperStConnect:
54+
return v1alpha1.BGPPeerSessionStateConnect
55+
case BGPPeerOperStActive:
56+
return v1alpha1.BGPPeerSessionStateActive
57+
case BGPPeerOperStOpenSent:
58+
return v1alpha1.BGPPeerSessionStateOpenSent
59+
case BGPPeerOperStOpenConf:
60+
return v1alpha1.BGPPeerSessionStateOpenConfirm
61+
case BGPPeerOperStEstablished:
62+
return v1alpha1.BGPPeerSessionStateEstablished
63+
default:
64+
return v1alpha1.BGPPeerSessionStateUnknown
65+
}
66+
}
67+
68+
type BGPPeer struct {
69+
RouterID string `json:"-"`
70+
AF ActivatedAddressFamilies `json:"address-families"`
71+
Name string `json:"vrf-name"`
72+
RD RouteDistinguisher `json:"rd,omitzero"`
73+
Neighbors NeighborList `json:"neighbors,omitzero"`
74+
}
75+
76+
// ActivatedAddressFamilies is required for IOS XR to activate the Address-Family under the BGP process
77+
type ActivatedAddressFamilies struct {
78+
AF []ActivatedAddressFamily `json:"address-family"`
79+
}
80+
81+
type ActivatedAddressFamily struct {
82+
AFName string `json:"af-name"`
83+
Redistribute Redistribute `json:"redistribute,omitempty"`
84+
}
85+
86+
type Redistribute struct {
87+
Static Static `json:"static"`
88+
}
89+
90+
type Static struct{}
91+
92+
type RouteDistinguisher struct {
93+
TwoByteAS TwoByteAS `json:"two-byte-as,omitzero"`
94+
FourByteAS FourByteAS `json:"four-byte-as,omitzero"`
95+
IPAddress IPAddressAS `json:"ip-address,omitzero"`
96+
}
97+
98+
type IPAddressAS struct {
99+
Address string `json:"address"`
100+
Index int64 `json:"ipv4address-index"`
101+
}
102+
103+
type TwoByteAS struct {
104+
ASNumber int64 `json:"two-byte-as-number"`
105+
Index int64 `json:"asn2-index"`
106+
}
107+
108+
type FourByteAS struct {
109+
ASNumber int64 `json:"four-byte-as-number"`
110+
Index int64 `json:"asn4-index"`
111+
}
112+
113+
type NeighborList struct {
114+
Neighbors []Neighbor `json:"neighbor"`
115+
}
116+
117+
type Neighbor struct {
118+
NeighborAddress string `json:"address"`
119+
AF NeighborAddressFamilies `json:"address-families"`
120+
LocalAS LocalAS `json:"local-as"`
121+
RemoteAS int32 `json:"remote-as"`
122+
SessionConfig SessionConfig `json:"use"`
123+
}
124+
125+
// Inherit address-family independent config from a session-group
126+
type SessionConfig struct {
127+
SessionGroup string `json:"session-group,omitzero"`
128+
}
129+
130+
type LocalAS struct {
131+
AS AS `json:"as"`
132+
}
133+
134+
type AS struct {
135+
ASNumber int32 `json:"as-number"`
136+
NoPrepend PrependAS `json:"no-prepend"`
137+
}
138+
139+
type PrependAS struct {
140+
ReplaceAS ReplaceAS `json:"replace-as"`
141+
}
142+
143+
type ReplaceAS struct{}
144+
145+
type NeighborAddressFamilies struct {
146+
AF []NeighborAddressFamily `json:"address-family"`
147+
}
148+
149+
type NeighborAddressFamily struct {
150+
AfName string `json:"af-name"`
151+
MaximumPrefix MaximumPrefix `json:"maximum-prefix,omitempty"`
152+
RoutePolicy PeeringRPL `json:"route-policy"`
153+
SendCommunityEbgp SendCommunityEbgp `json:"send-community-ebgp,omitempty"`
154+
SendCommunityGShutEbgp SendCommunityGShutEbgp `json:"send-community-gshut-ebgp,omitempty"`
155+
SendExtendedCommunityEbgp SendExtendedCommunityEbgp `json:"send-extended-community-ebgp,omitempty"`
156+
SoftReconfiguration SoftReconfiguration `json:"soft-reconfiguration,omitempty"`
157+
}
158+
159+
type SoftReconfiguration struct {
160+
Inbound Inbound `json:"inbound"`
161+
}
162+
163+
type Inbound struct {
164+
Always gnmiext.Empty `json:"always"`
165+
}
166+
167+
type SendCommunityEbgp struct{}
168+
169+
type SendCommunityGShutEbgp struct{}
170+
171+
type SendExtendedCommunityEbgp struct{}
172+
173+
type MaximumPrefix struct {
174+
PrefixLimit int `json:"maximum-prefix-number"`
175+
Restart int `json:"restart"`
176+
Threshold int `json:"threshold-value"`
177+
}
178+
179+
type PeeringRPL struct {
180+
In string `json:"in"`
181+
Out string `json:"out"`
182+
}
183+
184+
type BGPPeerOperStatus struct {
185+
Name string `json:"-"`
186+
State BGPPeerOperSt `json:"connection-state"`
187+
ConnectionUpTime uint32 `json:"connection-established-time"`
188+
}
189+
190+
func (p *BGPPeerOperStatus) XPath() string {
191+
return "Cisco-IOS-XR-ipv4-bgp-oper:bgp/instances/instance[instance-name=" + BGPDefaultInstance + "]/instance-active/vrfs/vrf[vrf-name=" + p.Name + "]/sessions/session/connection-state"
192+
}
193+
194+
func (p BGPPeerOperStatus) MarshalJSON() ([]byte, error) {
195+
return json.Marshal(p.State)
196+
}
197+
198+
func (p *BGPPeerOperStatus) UnmarshalJSON(data []byte) error {
199+
var t string
200+
if err := json.Unmarshal(data, &t); err != nil {
201+
return err
202+
}
203+
p.State = BGPPeerOperSt(t)
204+
return nil
205+
}
206+
207+
func (p *BGPPeer) XPath() string {
208+
return "Cisco-IOS-XR-um-router-bgp-cfg:router/bgp/instances/instance[instance-name=" + BGPDefaultInstance + "]/as[as-number=" + p.RouterID + "]/vrfs/vrf[vrf-name=" + p.Name + "]"
209+
}
210+
211+
func NewRouteDistinguisher(rd string) (RouteDistinguisher, error) {
212+
rdObj := RouteDistinguisher{}
213+
214+
rds := strings.Split(rd, ":")
215+
if len(rds) != 2 {
216+
return rdObj, fmt.Errorf("invalid route distinguisher format: %s", rd)
217+
}
218+
219+
index, err := strconv.ParseInt(rds[1], 10, 64)
220+
if err != nil {
221+
return rdObj, fmt.Errorf("invalid route distinguisher index: %s", rds[1])
222+
}
223+
224+
// Type 1: IPv4:Number(0-65535)
225+
if _, err := netip.ParseAddr(rds[0]); err == nil {
226+
if index > math.MaxUint16 {
227+
return rdObj, errors.New("type-1 'Assigned Number' is out of range (0–65535)")
228+
}
229+
rdObj.IPAddress = IPAddressAS{
230+
Address: rds[0],
231+
Index: index,
232+
}
233+
return rdObj, nil
234+
}
235+
236+
asNumber, err := strconv.ParseUint(rds[0], 10, 64)
237+
if err != nil {
238+
return rdObj, fmt.Errorf("invalid route distinguisher ASN: %s", rds[0])
239+
}
240+
241+
// Type 0: ASN(0-65535):Number(0-4294967295)
242+
if asNumber <= math.MaxUint16 && index <= math.MaxUint32 {
243+
rdObj.TwoByteAS = TwoByteAS{
244+
ASNumber: int64(asNumber),
245+
Index: index,
246+
}
247+
return rdObj, nil
248+
}
249+
250+
// Type 2: ASN(65536-4294967295):Number(0-65535)
251+
if asNumber <= math.MaxUint32 && index <= math.MaxUint16 {
252+
rdObj.FourByteAS = FourByteAS{
253+
ASNumber: int64(asNumber),
254+
Index: index,
255+
}
256+
return rdObj, nil
257+
}
258+
259+
return rdObj, errors.New("not a valid type-0, type-1, or type-2 RD")
260+
}

0 commit comments

Comments
 (0)