github.com/cilium/cilium@v1.16.2/pkg/bgpv1/api/conversions.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package api 5 6 import ( 7 "encoding/base64" 8 "errors" 9 "fmt" 10 "net/netip" 11 12 "github.com/osrg/gobgp/v3/pkg/packet/bgp" 13 14 "github.com/cilium/cilium/api/v1/models" 15 restapi "github.com/cilium/cilium/api/v1/server/restapi/bgp" 16 "github.com/cilium/cilium/pkg/bgpv1/types" 17 ) 18 19 const ( 20 routePolicyTypeExport = "export" 21 routePolicyTypeImport = "import" 22 23 routePolicyActionNone = "none" 24 routePolicyActionAccept = "accept" 25 routePolicyActionReject = "reject" 26 ) 27 28 func ToAgentGetRoutesRequest(params restapi.GetBgpRoutesParams) (*types.GetRoutesRequest, error) { 29 ret := &types.GetRoutesRequest{} 30 31 if ret.TableType = types.ParseTableType(params.TableType); ret.TableType == types.TableTypeUnknown { 32 return nil, fmt.Errorf("unknown table type %s", params.TableType) 33 } 34 35 if ret.Family.Afi = types.ParseAfi(params.Afi); ret.Family.Afi == types.AfiUnknown { 36 return nil, fmt.Errorf("unknown AFI %s", params.Afi) 37 } 38 39 if ret.Family.Safi = types.ParseSafi(params.Safi); ret.Family.Safi == types.SafiUnknown { 40 return nil, fmt.Errorf("unknown SAFI %s", params.Safi) 41 } 42 43 if params.Neighbor != nil { 44 if ret.TableType == types.TableTypeLocRIB { 45 return nil, fmt.Errorf("neighbor is unnecessary for loc-rib table type") 46 } 47 addr, err := netip.ParseAddr(*params.Neighbor) 48 if err != nil { 49 return nil, fmt.Errorf("invalid neighbor address: %w", err) 50 } 51 ret.Neighbor = addr 52 } else { 53 if ret.TableType == types.TableTypeAdjRIBIn || ret.TableType == types.TableTypeAdjRIBOut { 54 return nil, fmt.Errorf("table %s requires neighbor parameter", params.TableType) 55 } 56 } 57 58 return ret, nil 59 } 60 61 func ToAPIFamily(f *types.Family) (*models.BgpFamily, error) { 62 return &models.BgpFamily{ 63 Afi: f.Afi.String(), 64 Safi: f.Safi.String(), 65 }, nil 66 } 67 68 func ToAgentFamily(m *models.BgpFamily) (*types.Family, error) { 69 f := &types.Family{} 70 71 if f.Afi = types.ParseAfi(m.Afi); f.Afi == types.AfiUnknown { 72 return nil, fmt.Errorf("unknown afi %s", m.Afi) 73 } 74 75 if f.Safi = types.ParseSafi(m.Safi); f.Safi == types.SafiUnknown { 76 return nil, fmt.Errorf("unknown safi %s", m.Safi) 77 } 78 79 return f, nil 80 } 81 82 func ToAPIPath(p *types.Path) (*models.BgpPath, error) { 83 ret := &models.BgpPath{} 84 85 ret.AgeNanoseconds = p.AgeNanoseconds 86 ret.Best = p.Best 87 88 // We need this Base64 encoding because OpenAPI 2.0 spec doesn't support Union 89 // type and we don't have any way to express the API response field which can 90 // be a multiple types. This is especially inconvenient for NLRI and Path 91 // Attributes. The workaround here is serialize NLRI or Path Attribute into 92 // BGP UPDATE messsage format and encode it with base64 to put them into text 93 // based protocol. So that we can still stick to the standard (theoretically 94 // people can use standard BGP decoder to decode this base64 field). 95 bin, err := p.NLRI.Serialize() 96 if err != nil { 97 return nil, fmt.Errorf("failed to serialize NLRI: %w", err) 98 } 99 ret.Nlri = &models.BgpNlri{Base64: base64.StdEncoding.EncodeToString(bin)} 100 101 if ret.Family, err = ToAPIFamily(&types.Family{ 102 Afi: types.Afi(p.NLRI.AFI()), 103 Safi: types.Safi(p.NLRI.SAFI()), 104 }); err != nil { 105 return nil, fmt.Errorf("failed to serialize address family: %w", err) 106 } 107 108 for _, pattr := range p.PathAttributes { 109 bin, err := pattr.Serialize() 110 if err != nil { 111 return nil, fmt.Errorf("failed to serialize Path Attribute: %w", err) 112 } 113 114 ret.PathAttributes = append(ret.PathAttributes, &models.BgpPathAttribute{ 115 Base64: base64.StdEncoding.EncodeToString(bin), 116 }) 117 } 118 119 return ret, nil 120 } 121 122 func ToAgentPath(m *models.BgpPath) (*types.Path, error) { 123 p := &types.Path{} 124 125 p.AgeNanoseconds = m.AgeNanoseconds 126 p.Best = m.Best 127 128 afi := types.ParseAfi(m.Family.Afi) 129 safi := types.ParseSafi(m.Family.Safi) 130 131 // Create empty NLRI structure. The underlying type will be set correctly by providing AFI/SAFI 132 nlri, err := bgp.NewPrefixFromRouteFamily(uint16(afi), uint8(safi)) 133 if err != nil { 134 return nil, fmt.Errorf("failed to create native NLRI struct from AFI/SAFI: %w", err) 135 } 136 137 // Decode serialized NLRI 138 bin, err := base64.StdEncoding.DecodeString(m.Nlri.Base64) 139 if err != nil { 140 return nil, fmt.Errorf("failed to decode base64-encoded NLRI: %w", err) 141 } 142 143 if err := nlri.DecodeFromBytes(bin); err != nil { 144 return nil, fmt.Errorf("failed to decode NLRI: %w", err) 145 } 146 147 p.NLRI = nlri 148 149 // Decode path attributes 150 for _, pattr := range m.PathAttributes { 151 bin, err := base64.StdEncoding.DecodeString(pattr.Base64) 152 if err != nil { 153 return nil, fmt.Errorf("failed to decode base64-encoded Path Attribute: %w", err) 154 } 155 156 attr, err := bgp.GetPathAttribute(bin) 157 if err != nil { 158 return nil, fmt.Errorf("failed to retrieve serialized Path Attribute: %w", err) 159 } 160 err = attr.DecodeFromBytes(bin) 161 if err != nil { 162 return nil, fmt.Errorf("failed to decode serialized Path Attribute: %w", err) 163 } 164 165 p.PathAttributes = append(p.PathAttributes, attr) 166 } 167 168 return p, nil 169 } 170 171 func ToAPIPaths(ps []*types.Path) ([]*models.BgpPath, error) { 172 errs := []error{} 173 ret := []*models.BgpPath{} 174 175 for _, p := range ps { 176 path, err := ToAPIPath(p) 177 if err != nil { 178 errs = append(errs, err) 179 continue 180 } 181 ret = append(ret, path) 182 } 183 184 if len(errs) != 0 { 185 return nil, errors.Join(errs...) 186 } 187 188 return ret, nil 189 } 190 191 func ToAgentPaths(ms []*models.BgpPath) ([]*types.Path, error) { 192 errs := []error{} 193 ret := []*types.Path{} 194 195 for _, m := range ms { 196 path, err := ToAgentPath(m) 197 if err != nil { 198 errs = append(errs, err) 199 continue 200 } 201 ret = append(ret, path) 202 } 203 204 if len(errs) != 0 { 205 return nil, errors.Join(errs...) 206 } 207 208 return ret, nil 209 } 210 211 func ToAPIRoute(r *types.Route, routerASN int64, neighbor string) (*models.BgpRoute, error) { 212 paths, err := ToAPIPaths(r.Paths) 213 if err != nil { 214 return nil, fmt.Errorf("failed to serialize Paths: %w", err) 215 } 216 return &models.BgpRoute{ 217 RouterAsn: routerASN, 218 Neighbor: neighbor, 219 Prefix: r.Prefix, 220 Paths: paths, 221 }, nil 222 } 223 224 func ToAgentRoute(m *models.BgpRoute) (*types.Route, error) { 225 paths, err := ToAgentPaths(m.Paths) 226 if err != nil { 227 return nil, fmt.Errorf("failed to deserialize Paths: %w", err) 228 } 229 return &types.Route{ 230 Prefix: m.Prefix, 231 Paths: paths, 232 }, nil 233 } 234 235 func ToAPIRoutes(rs []*types.Route, routerASN int64, neighbor string) ([]*models.BgpRoute, error) { 236 errs := []error{} 237 ret := []*models.BgpRoute{} 238 239 for _, r := range rs { 240 route, err := ToAPIRoute(r, routerASN, neighbor) 241 if err != nil { 242 errs = append(errs, err) 243 continue 244 } 245 ret = append(ret, route) 246 } 247 248 if len(errs) != 0 { 249 return nil, errors.Join(errs...) 250 } 251 252 return ret, nil 253 } 254 255 func ToAgentRoutes(ms []*models.BgpRoute) ([]*types.Route, error) { 256 errs := []error{} 257 ret := []*types.Route{} 258 259 for _, m := range ms { 260 route, err := ToAgentRoute(m) 261 if err != nil { 262 errs = append(errs, err) 263 continue 264 } 265 ret = append(ret, route) 266 } 267 268 if len(errs) != 0 { 269 return nil, errors.Join(errs...) 270 } 271 272 return ret, nil 273 } 274 275 func ToAPIRoutePolicies(policies []*types.RoutePolicy, routerASN int64) []*models.BgpRoutePolicy { 276 ret := make([]*models.BgpRoutePolicy, 0, len(policies)) 277 278 for _, p := range policies { 279 policy := ToAPIRoutePolicy(p, routerASN) 280 ret = append(ret, policy) 281 } 282 return ret 283 } 284 285 func ToAgentRoutePolicies(policies []*models.BgpRoutePolicy) ([]*types.RoutePolicy, error) { 286 var retErr error 287 ret := make([]*types.RoutePolicy, 0, len(policies)) 288 289 for _, p := range policies { 290 policy, err := ToAgentRoutePolicy(p) 291 if err != nil { 292 retErr = errors.Join(retErr, err) 293 continue 294 } 295 ret = append(ret, policy) 296 } 297 return ret, retErr 298 } 299 300 func ToAPIRoutePolicy(policy *types.RoutePolicy, routerASN int64) *models.BgpRoutePolicy { 301 return &models.BgpRoutePolicy{ 302 RouterAsn: routerASN, 303 Name: policy.Name, 304 Type: ToApiRoutePolicyType(policy.Type), 305 Statements: ToAPIRoutePolicyStatements(policy.Statements), 306 } 307 } 308 309 func ToAgentRoutePolicy(policy *models.BgpRoutePolicy) (*types.RoutePolicy, error) { 310 stmts, err := ToAgentRoutePolicyStatements(policy.Statements) 311 if err != nil { 312 return nil, err 313 } 314 return &types.RoutePolicy{ 315 Name: policy.Name, 316 Type: ToAgentRoutePolicyType(policy.Type), 317 Statements: stmts, 318 }, nil 319 } 320 321 func ToAPIRoutePolicyStatements(statements []*types.RoutePolicyStatement) []*models.BgpRoutePolicyStatement { 322 ret := make([]*models.BgpRoutePolicyStatement, 0, len(statements)) 323 324 for _, s := range statements { 325 ret = append(ret, ToAPIRoutePolicyStatement(s)) 326 } 327 return ret 328 } 329 330 func ToAgentRoutePolicyStatements(statements []*models.BgpRoutePolicyStatement) ([]*types.RoutePolicyStatement, error) { 331 var retErr error 332 ret := make([]*types.RoutePolicyStatement, 0, len(statements)) 333 334 for _, s := range statements { 335 stmt, err := ToAgentRoutePolicyStatement(s) 336 if err != nil { 337 retErr = errors.Join(retErr, err) 338 continue 339 } 340 ret = append(ret, stmt) 341 } 342 return ret, retErr 343 } 344 345 func ToAPIRoutePolicyStatement(s *types.RoutePolicyStatement) *models.BgpRoutePolicyStatement { 346 localPref := int64(-1) 347 if s.Actions.SetLocalPreference != nil { 348 localPref = *s.Actions.SetLocalPreference 349 } 350 ret := &models.BgpRoutePolicyStatement{ 351 MatchNeighbors: s.Conditions.MatchNeighbors, 352 MatchPrefixes: ToApiMatchPrefixes(s.Conditions.MatchPrefixes), 353 RouteAction: ToApiRoutePolicyAction(s.Actions.RouteAction), 354 AddCommunities: s.Actions.AddCommunities, 355 AddLargeCommunities: s.Actions.AddLargeCommunities, 356 SetLocalPreference: localPref, 357 } 358 return ret 359 } 360 361 func ToAgentRoutePolicyStatement(s *models.BgpRoutePolicyStatement) (*types.RoutePolicyStatement, error) { 362 var localPref *int64 363 if s.SetLocalPreference >= 0 { 364 localPref = &s.SetLocalPreference 365 } 366 prefixes, err := ToAgentMatchPrefixes(s.MatchPrefixes) 367 if err != nil { 368 return nil, err 369 } 370 ret := &types.RoutePolicyStatement{ 371 Conditions: types.RoutePolicyConditions{ 372 MatchNeighbors: s.MatchNeighbors, 373 MatchPrefixes: prefixes, 374 }, 375 Actions: types.RoutePolicyActions{ 376 RouteAction: ToAgentRoutePolicyAction(s.RouteAction), 377 AddCommunities: s.AddCommunities, 378 AddLargeCommunities: s.AddLargeCommunities, 379 SetLocalPreference: localPref, 380 }, 381 } 382 return ret, nil 383 } 384 385 func ToApiMatchPrefixes(prefixes []*types.RoutePolicyPrefixMatch) []*models.BgpRoutePolicyPrefixMatch { 386 ret := make([]*models.BgpRoutePolicyPrefixMatch, 0, len(prefixes)) 387 388 for _, p := range prefixes { 389 ret = append(ret, &models.BgpRoutePolicyPrefixMatch{ 390 Cidr: p.CIDR.String(), 391 PrefixLenMin: int64(p.PrefixLenMin), 392 PrefixLenMax: int64(p.PrefixLenMax), 393 }) 394 } 395 return ret 396 } 397 398 func ToAgentMatchPrefixes(prefixes []*models.BgpRoutePolicyPrefixMatch) ([]*types.RoutePolicyPrefixMatch, error) { 399 var retErr error 400 ret := make([]*types.RoutePolicyPrefixMatch, 0, len(prefixes)) 401 402 for _, p := range prefixes { 403 cidr, err := netip.ParsePrefix(p.Cidr) 404 if err != nil { 405 retErr = errors.Join(retErr, err) 406 continue 407 } 408 ret = append(ret, &types.RoutePolicyPrefixMatch{ 409 CIDR: cidr, 410 PrefixLenMin: int(p.PrefixLenMin), 411 PrefixLenMax: int(p.PrefixLenMax), 412 }) 413 } 414 return ret, retErr 415 } 416 417 func ToApiRoutePolicyType(t types.RoutePolicyType) string { 418 if t == types.RoutePolicyTypeExport { 419 return routePolicyTypeExport 420 } 421 return routePolicyTypeImport 422 } 423 424 func ToAgentRoutePolicyType(t string) types.RoutePolicyType { 425 if t == routePolicyTypeExport { 426 return types.RoutePolicyTypeExport 427 } 428 return types.RoutePolicyTypeImport 429 } 430 431 func ToApiRoutePolicyAction(a types.RoutePolicyAction) string { 432 switch a { 433 case types.RoutePolicyActionNone: 434 return routePolicyActionNone 435 case types.RoutePolicyActionAccept: 436 return routePolicyActionAccept 437 case types.RoutePolicyActionReject: 438 return routePolicyActionReject 439 } 440 return routePolicyActionNone 441 } 442 443 func ToAgentRoutePolicyAction(a string) types.RoutePolicyAction { 444 switch a { 445 case routePolicyActionNone: 446 return types.RoutePolicyActionNone 447 case routePolicyActionAccept: 448 return types.RoutePolicyActionAccept 449 case routePolicyActionReject: 450 return types.RoutePolicyActionReject 451 } 452 return types.RoutePolicyActionNone 453 }