github.com/cilium/cilium@v1.16.2/pkg/bgpv1/gobgp/conversions.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package gobgp 5 6 import ( 7 "errors" 8 "fmt" 9 "net/netip" 10 11 gobgp "github.com/osrg/gobgp/v3/api" 12 "github.com/osrg/gobgp/v3/pkg/apiutil" 13 "github.com/osrg/gobgp/v3/pkg/packet/bgp" 14 "google.golang.org/protobuf/types/known/timestamppb" 15 16 "github.com/cilium/cilium/pkg/bgpv1/types" 17 "github.com/cilium/cilium/pkg/time" 18 ) 19 20 // ToGoBGPPath converts the Agent Path type to the GoBGP Path type 21 func ToGoBGPPath(p *types.Path) (*gobgp.Path, error) { 22 nlri, err := apiutil.MarshalNLRI(p.NLRI) 23 if err != nil { 24 return nil, fmt.Errorf("failed to convert NLRI: %w", err) 25 } 26 27 pattrs, err := apiutil.MarshalPathAttributes(p.PathAttributes) 28 if err != nil { 29 return nil, fmt.Errorf("failed to convert PathAttribute: %w", err) 30 } 31 32 // ageTimestamp is Path's creation time stamp. 33 // It is calculated by subtraction of the AgeNanoseconds from the current timestamp. 34 ageTimestamp := timestamppb.New(time.Now().Add(time.Duration(-1 * p.AgeNanoseconds))) 35 36 family := toGoBGPFamily(p.Family) 37 38 // infer family from NLRI if not provided 39 if family.Afi == gobgp.Family_AFI_UNKNOWN { 40 family = &gobgp.Family{ 41 Afi: gobgp.Family_Afi(p.NLRI.AFI()), 42 Safi: gobgp.Family_Safi(p.NLRI.SAFI()), 43 } 44 } 45 46 return &gobgp.Path{ 47 Nlri: nlri, 48 Pattrs: pattrs, 49 Age: ageTimestamp, 50 Best: p.Best, 51 Family: family, 52 Uuid: p.UUID, 53 }, nil 54 } 55 56 // ToAgentPath converts the GoBGP Path type to the Agent Path type 57 func ToAgentPath(p *gobgp.Path) (*types.Path, error) { 58 family := bgp.AfiSafiToRouteFamily(uint16(p.Family.Afi), uint8(p.Family.Safi)) 59 60 nlri, err := apiutil.UnmarshalNLRI(family, p.Nlri) 61 if err != nil { 62 return nil, fmt.Errorf("failed to convert Nlri: %w", err) 63 } 64 65 pattrs, err := apiutil.UnmarshalPathAttributes(p.Pattrs) 66 if err != nil { 67 return nil, fmt.Errorf("failed to convert Pattrs: %w", err) 68 } 69 70 // ageNano is time since the Path was created in nanoseconds. 71 // It is calculated by difference in time from age timestamp till now. 72 ageNano := int64(time.Since(p.Age.AsTime())) 73 74 return &types.Path{ 75 NLRI: nlri, 76 Family: toAgentFamily(p.Family), 77 PathAttributes: pattrs, 78 AgeNanoseconds: ageNano, 79 Best: p.Best, 80 UUID: p.Uuid, 81 }, nil 82 } 83 84 // ToAgentPaths converts slice of the GoBGP Path type to slice of the Agent Path type 85 func ToAgentPaths(paths []*gobgp.Path) ([]*types.Path, error) { 86 errs := []error{} 87 ps := []*types.Path{} 88 89 for _, path := range paths { 90 p, err := ToAgentPath(path) 91 if err != nil { 92 errs = append(errs, err) 93 continue 94 } 95 ps = append(ps, p) 96 } 97 98 if len(errs) != 0 { 99 return nil, errors.Join(errs...) 100 } 101 102 return ps, nil 103 } 104 105 func toGoBGPFamily(family types.Family) *gobgp.Family { 106 return &gobgp.Family{ 107 Afi: toGoBGPAfi(family.Afi), 108 Safi: toGoBGPSafi(family.Safi), 109 } 110 } 111 112 func toGoBGPAfi(a types.Afi) gobgp.Family_Afi { 113 switch a { 114 case types.AfiUnknown: 115 return gobgp.Family_AFI_UNKNOWN 116 case types.AfiIPv4: 117 return gobgp.Family_AFI_IP 118 case types.AfiIPv6: 119 return gobgp.Family_AFI_IP6 120 case types.AfiL2VPN: 121 return gobgp.Family_AFI_L2VPN 122 case types.AfiLS: 123 return gobgp.Family_AFI_LS 124 case types.AfiOpaque: 125 return gobgp.Family_AFI_OPAQUE 126 default: 127 return gobgp.Family_AFI_UNKNOWN 128 } 129 } 130 131 func toGoBGPSafi(s types.Safi) gobgp.Family_Safi { 132 switch s { 133 case types.SafiUnknown: 134 return gobgp.Family_SAFI_UNKNOWN 135 case types.SafiUnicast: 136 return gobgp.Family_SAFI_UNICAST 137 case types.SafiMulticast: 138 return gobgp.Family_SAFI_MULTICAST 139 case types.SafiMplsLabel: 140 return gobgp.Family_SAFI_MPLS_LABEL 141 case types.SafiEncapsulation: 142 return gobgp.Family_SAFI_ENCAPSULATION 143 case types.SafiVpls: 144 return gobgp.Family_SAFI_VPLS 145 case types.SafiEvpn: 146 return gobgp.Family_SAFI_EVPN 147 case types.SafiLs: 148 return gobgp.Family_SAFI_LS 149 case types.SafiSrPolicy: 150 return gobgp.Family_SAFI_SR_POLICY 151 case types.SafiMup: 152 return gobgp.Family_SAFI_MUP 153 case types.SafiMplsVpn: 154 return gobgp.Family_SAFI_MPLS_VPN 155 case types.SafiMplsVpnMulticast: 156 return gobgp.Family_SAFI_MPLS_VPN_MULTICAST 157 case types.SafiRouteTargetConstraints: 158 return gobgp.Family_SAFI_ROUTE_TARGET_CONSTRAINTS 159 case types.SafiFlowSpecUnicast: 160 return gobgp.Family_SAFI_FLOW_SPEC_UNICAST 161 case types.SafiFlowSpecVpn: 162 return gobgp.Family_SAFI_FLOW_SPEC_VPN 163 case types.SafiKeyValue: 164 return gobgp.Family_SAFI_KEY_VALUE 165 default: 166 return gobgp.Family_SAFI_UNKNOWN 167 } 168 } 169 170 func toGoBGPPolicy(apiPolicy *types.RoutePolicy) (*gobgp.Policy, []*gobgp.DefinedSet) { 171 var definedSets []*gobgp.DefinedSet 172 173 policy := &gobgp.Policy{ 174 Name: apiPolicy.Name, 175 } 176 for i, stmt := range apiPolicy.Statements { 177 statement, dSets := toGoBGPPolicyStatement(stmt, policyStatementName(apiPolicy.Name, i)) 178 policy.Statements = append(policy.Statements, statement) 179 definedSets = append(definedSets, dSets...) 180 } 181 182 return policy, definedSets 183 } 184 185 func toAgentPolicy(p *gobgp.Policy, definedSets map[string]*gobgp.DefinedSet, assignment *gobgp.PolicyAssignment) *types.RoutePolicy { 186 policy := &types.RoutePolicy{ 187 Name: p.Name, 188 Type: toAgentPolicyType(assignment.Direction), 189 } 190 for _, s := range p.Statements { 191 policy.Statements = append(policy.Statements, toAgentPolicyStatement(s, definedSets)) 192 } 193 return policy 194 } 195 196 func toGoBGPPolicyStatement(apiStatement *types.RoutePolicyStatement, name string) (*gobgp.Statement, []*gobgp.DefinedSet) { 197 var definedSets []*gobgp.DefinedSet 198 199 s := &gobgp.Statement{ 200 Name: name, 201 Conditions: &gobgp.Conditions{}, 202 Actions: &gobgp.Actions{ 203 RouteAction: toGoBGPRouteAction(apiStatement.Actions.RouteAction), 204 }, 205 } 206 207 // defined set to match neighbor 208 if len(apiStatement.Conditions.MatchNeighbors) > 0 { 209 ds := &gobgp.DefinedSet{ 210 DefinedType: gobgp.DefinedType_NEIGHBOR, 211 Name: policyNeighborDefinedSetName(name), 212 List: apiStatement.Conditions.MatchNeighbors, 213 } 214 s.Conditions.NeighborSet = &gobgp.MatchSet{ 215 Type: gobgp.MatchSet_ANY, // any of the configured neighbors 216 Name: ds.Name, 217 } 218 definedSets = append(definedSets, ds) 219 } 220 221 // defined set to match prefixes 222 if len(apiStatement.Conditions.MatchPrefixes) > 0 { 223 ds := &gobgp.DefinedSet{ 224 DefinedType: gobgp.DefinedType_PREFIX, 225 Name: policyPrefixDefinedSetName(name), 226 } 227 for _, prefix := range apiStatement.Conditions.MatchPrefixes { 228 p := &gobgp.Prefix{ 229 IpPrefix: prefix.CIDR.String(), 230 MaskLengthMin: uint32(prefix.PrefixLenMin), 231 MaskLengthMax: uint32(prefix.PrefixLenMax), 232 } 233 ds.Prefixes = append(ds.Prefixes, p) 234 } 235 s.Conditions.PrefixSet = &gobgp.MatchSet{ 236 Type: gobgp.MatchSet_ANY, // any of the configured prefixes 237 Name: ds.Name, 238 } 239 definedSets = append(definedSets, ds) 240 } 241 242 // community actions 243 if len(apiStatement.Actions.AddCommunities) > 0 { 244 s.Actions.Community = &gobgp.CommunityAction{ 245 Type: gobgp.CommunityAction_ADD, 246 Communities: apiStatement.Actions.AddCommunities, 247 } 248 } 249 if len(apiStatement.Actions.AddLargeCommunities) > 0 { 250 s.Actions.LargeCommunity = &gobgp.CommunityAction{ 251 Type: gobgp.CommunityAction_ADD, 252 Communities: apiStatement.Actions.AddLargeCommunities, 253 } 254 } 255 256 // local preference actions 257 if apiStatement.Actions.SetLocalPreference != nil { 258 // Local preference only makes sense for iBGP sessions. However, it can be applied 259 // unconditionally here - it would have no effect on eBGP peers matching this policy. 260 s.Actions.LocalPref = &gobgp.LocalPrefAction{ 261 Value: uint32(*apiStatement.Actions.SetLocalPreference), 262 } 263 } 264 return s, definedSets 265 } 266 267 func toAgentPolicyStatement(s *gobgp.Statement, definedSets map[string]*gobgp.DefinedSet) *types.RoutePolicyStatement { 268 stmt := &types.RoutePolicyStatement{} 269 270 if s.Conditions != nil { 271 if s.Conditions.NeighborSet != nil && definedSets[s.Conditions.NeighborSet.Name] != nil { 272 stmt.Conditions.MatchNeighbors = definedSets[s.Conditions.NeighborSet.Name].List 273 } 274 if s.Conditions.PrefixSet != nil && definedSets[s.Conditions.PrefixSet.Name] != nil { 275 for _, pfx := range definedSets[s.Conditions.PrefixSet.Name].Prefixes { 276 cidr, err := netip.ParsePrefix(pfx.IpPrefix) 277 if err == nil { 278 stmt.Conditions.MatchPrefixes = append(stmt.Conditions.MatchPrefixes, &types.RoutePolicyPrefixMatch{ 279 CIDR: cidr, 280 PrefixLenMin: int(pfx.MaskLengthMin), 281 PrefixLenMax: int(pfx.MaskLengthMax), 282 }) 283 } 284 } 285 } 286 } 287 if s.Actions != nil { 288 stmt.Actions.RouteAction = toAgentRouteAction(s.Actions.RouteAction) 289 if s.Actions.Community != nil { 290 stmt.Actions.AddCommunities = s.Actions.Community.Communities 291 } 292 if s.Actions.LargeCommunity != nil { 293 stmt.Actions.AddLargeCommunities = s.Actions.LargeCommunity.Communities 294 } 295 if s.Actions.LocalPref != nil { 296 localPref := int64(s.Actions.LocalPref.Value) 297 stmt.Actions.SetLocalPreference = &localPref 298 } 299 } 300 return stmt 301 } 302 303 func policyStatementName(policyName string, cnt int) string { 304 return fmt.Sprintf("%s-%d", policyName, cnt) 305 } 306 307 func policyNeighborDefinedSetName(policyStatementName string) string { 308 return fmt.Sprintf(policyStatementName + "-neighbor") 309 } 310 311 func policyPrefixDefinedSetName(policyStatementName string) string { 312 return fmt.Sprintf(policyStatementName + "-prefix") 313 } 314 315 func toGoBGPRouteAction(a types.RoutePolicyAction) gobgp.RouteAction { 316 switch a { 317 case types.RoutePolicyActionAccept: 318 return gobgp.RouteAction_ACCEPT 319 case types.RoutePolicyActionReject: 320 return gobgp.RouteAction_REJECT 321 } 322 return gobgp.RouteAction_NONE 323 } 324 325 func toAgentRouteAction(a gobgp.RouteAction) types.RoutePolicyAction { 326 switch a { 327 case gobgp.RouteAction_ACCEPT: 328 return types.RoutePolicyActionAccept 329 case gobgp.RouteAction_REJECT: 330 return types.RoutePolicyActionReject 331 } 332 return types.RoutePolicyActionNone 333 } 334 335 func toGoBGPPolicyDirection(policyType types.RoutePolicyType) gobgp.PolicyDirection { 336 switch policyType { 337 case types.RoutePolicyTypeExport: 338 return gobgp.PolicyDirection_EXPORT 339 case types.RoutePolicyTypeImport: 340 return gobgp.PolicyDirection_IMPORT 341 } 342 return gobgp.PolicyDirection_UNKNOWN 343 } 344 345 func toAgentPolicyType(d gobgp.PolicyDirection) types.RoutePolicyType { 346 if d == gobgp.PolicyDirection_IMPORT { 347 return types.RoutePolicyTypeImport 348 } 349 return types.RoutePolicyTypeExport 350 } 351 352 func toGoBGPSoftResetDirection(direction types.SoftResetDirection) gobgp.ResetPeerRequest_SoftResetDirection { 353 switch direction { 354 case types.SoftResetDirectionIn: 355 return gobgp.ResetPeerRequest_IN 356 case types.SoftResetDirectionOut: 357 return gobgp.ResetPeerRequest_OUT 358 } 359 return gobgp.ResetPeerRequest_BOTH 360 } 361 362 // toAgentSessionState translates gobgp session state to cilium bgp session state. 363 func toAgentSessionState(s gobgp.PeerState_SessionState) types.SessionState { 364 switch s { 365 case gobgp.PeerState_UNKNOWN: 366 return types.SessionUnknown 367 case gobgp.PeerState_IDLE: 368 return types.SessionIdle 369 case gobgp.PeerState_CONNECT: 370 return types.SessionConnect 371 case gobgp.PeerState_ACTIVE: 372 return types.SessionActive 373 case gobgp.PeerState_OPENSENT: 374 return types.SessionOpenSent 375 case gobgp.PeerState_OPENCONFIRM: 376 return types.SessionOpenConfirm 377 case gobgp.PeerState_ESTABLISHED: 378 return types.SessionEstablished 379 default: 380 return types.SessionUnknown 381 } 382 } 383 384 func toAgentFamily(family *gobgp.Family) types.Family { 385 return types.Family{ 386 Afi: toAgentAfi(family.Afi), 387 Safi: toAgentSafi(family.Safi), 388 } 389 } 390 391 // toAgentAfi translates gobgp AFI to cilium bgp AFI. 392 func toAgentAfi(a gobgp.Family_Afi) types.Afi { 393 switch a { 394 case gobgp.Family_AFI_UNKNOWN: 395 return types.AfiUnknown 396 case gobgp.Family_AFI_IP: 397 return types.AfiIPv4 398 case gobgp.Family_AFI_IP6: 399 return types.AfiIPv6 400 case gobgp.Family_AFI_L2VPN: 401 return types.AfiL2VPN 402 case gobgp.Family_AFI_LS: 403 return types.AfiLS 404 case gobgp.Family_AFI_OPAQUE: 405 return types.AfiOpaque 406 default: 407 return types.AfiUnknown 408 } 409 } 410 411 func toAgentSafi(s gobgp.Family_Safi) types.Safi { 412 switch s { 413 case gobgp.Family_SAFI_UNKNOWN: 414 return types.SafiUnknown 415 case gobgp.Family_SAFI_UNICAST: 416 return types.SafiUnicast 417 case gobgp.Family_SAFI_MULTICAST: 418 return types.SafiMulticast 419 case gobgp.Family_SAFI_MPLS_LABEL: 420 return types.SafiMplsLabel 421 case gobgp.Family_SAFI_ENCAPSULATION: 422 return types.SafiEncapsulation 423 case gobgp.Family_SAFI_VPLS: 424 return types.SafiVpls 425 case gobgp.Family_SAFI_EVPN: 426 return types.SafiEvpn 427 case gobgp.Family_SAFI_LS: 428 return types.SafiLs 429 case gobgp.Family_SAFI_SR_POLICY: 430 return types.SafiSrPolicy 431 case gobgp.Family_SAFI_MUP: 432 return types.SafiMup 433 case gobgp.Family_SAFI_MPLS_VPN: 434 return types.SafiMplsVpn 435 case gobgp.Family_SAFI_MPLS_VPN_MULTICAST: 436 return types.SafiMplsVpnMulticast 437 case gobgp.Family_SAFI_ROUTE_TARGET_CONSTRAINTS: 438 return types.SafiRouteTargetConstraints 439 case gobgp.Family_SAFI_FLOW_SPEC_UNICAST: 440 return types.SafiFlowSpecUnicast 441 case gobgp.Family_SAFI_FLOW_SPEC_VPN: 442 return types.SafiFlowSpecVpn 443 case gobgp.Family_SAFI_KEY_VALUE: 444 return types.SafiKeyValue 445 default: 446 return types.SafiUnknown 447 } 448 } 449 450 func toGoBGPTableType(t types.TableType) (gobgp.TableType, error) { 451 switch t { 452 case types.TableTypeLocRIB: 453 return gobgp.TableType_LOCAL, nil 454 case types.TableTypeAdjRIBIn: 455 return gobgp.TableType_ADJ_IN, nil 456 case types.TableTypeAdjRIBOut: 457 return gobgp.TableType_ADJ_OUT, nil 458 default: 459 return gobgp.TableType_LOCAL, fmt.Errorf("unknown table type %d", t) 460 } 461 }