github.com/cilium/cilium@v1.16.2/pkg/maps/lbmap/lbmap.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package lbmap 5 6 import ( 7 "errors" 8 "fmt" 9 "net" 10 "strconv" 11 12 "github.com/sirupsen/logrus" 13 "golang.org/x/sys/unix" 14 15 "github.com/cilium/cilium/pkg/bpf" 16 "github.com/cilium/cilium/pkg/cidr" 17 cmtypes "github.com/cilium/cilium/pkg/clustermesh/types" 18 datapathTypes "github.com/cilium/cilium/pkg/datapath/types" 19 "github.com/cilium/cilium/pkg/ip" 20 "github.com/cilium/cilium/pkg/loadbalancer" 21 "github.com/cilium/cilium/pkg/logging" 22 "github.com/cilium/cilium/pkg/logging/logfields" 23 "github.com/cilium/cilium/pkg/maglev" 24 "github.com/cilium/cilium/pkg/option" 25 "github.com/cilium/cilium/pkg/u8proto" 26 ) 27 28 const DefaultMaxEntries = 65536 29 30 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-lb") 31 32 var ( 33 // MaxEntries contains the maximum number of entries that are allowed 34 // in Cilium LB service, backend and affinity maps. 35 ServiceMapMaxEntries = DefaultMaxEntries 36 ServiceBackEndMapMaxEntries = DefaultMaxEntries 37 RevNatMapMaxEntries = DefaultMaxEntries 38 AffinityMapMaxEntries = DefaultMaxEntries 39 SourceRangeMapMaxEntries = DefaultMaxEntries 40 MaglevMapMaxEntries = DefaultMaxEntries 41 ) 42 43 // LBBPFMap is an implementation of the LBMap interface. 44 type LBBPFMap struct { 45 // Buffer used to avoid excessive allocations to temporarily store backend 46 // IDs. Concurrent access is protected by the 47 // pkg/service.go:(Service).UpsertService() lock. 48 maglevBackendIDsBuffer []loadbalancer.BackendID 49 maglevTableSize uint64 50 } 51 52 func New() *LBBPFMap { 53 maglev := option.Config.NodePortAlg == option.NodePortAlgMaglev 54 maglevTableSize := option.Config.MaglevTableSize 55 56 m := &LBBPFMap{} 57 58 if maglev { 59 m.maglevBackendIDsBuffer = make([]loadbalancer.BackendID, maglevTableSize) 60 m.maglevTableSize = uint64(maglevTableSize) 61 } 62 63 return m 64 } 65 66 func (lbmap *LBBPFMap) upsertServiceProto(p *datapathTypes.UpsertServiceParams, ipv6 bool) error { 67 var svcKey ServiceKey 68 var svcVal ServiceValue 69 70 // Backends should be added to the backend maps for the case when: 71 // - Plain IPv6 (to IPv6) or IPv4 (to IPv4) service. 72 // - IPv4 to IPv6 will only have a dummy IPv4 service entry (0 backends) 73 // as it will recicle the packet into the IPv6 path. 74 // - IPv6 to IPv4 will add its IPv4 backends as IPv4-in-IPv6 backends 75 // to the IPv6 backend map. 76 backendsOk := ipv6 || !ipv6 && p.NatPolicy != loadbalancer.SVCNatPolicyNat46 77 78 if ipv6 { 79 svcKey = NewService6Key(p.IP, p.Port, u8proto.ANY, p.Scope, 0) 80 svcVal = &Service6Value{} 81 } else { 82 svcKey = NewService4Key(p.IP, p.Port, u8proto.ANY, p.Scope, 0) 83 svcVal = &Service4Value{} 84 } 85 86 slot := 1 87 88 // start off with #backends = 0 for updateMasterService() 89 backends := make(map[string]*loadbalancer.Backend) 90 if backendsOk { 91 backends = p.ActiveBackends 92 if len(p.PreferredBackends) > 0 { 93 backends = p.PreferredBackends 94 } 95 if p.UseMaglev && len(backends) != 0 { 96 if err := lbmap.UpsertMaglevLookupTable(p.ID, backends, ipv6); err != nil { 97 return err 98 } 99 } 100 backendIDs := p.GetOrderedBackends() 101 for _, backendID := range backendIDs { 102 if backendID == 0 { 103 return fmt.Errorf("Invalid backend ID 0") 104 } 105 svcVal.SetBackendID(loadbalancer.BackendID(backendID)) 106 svcVal.SetRevNat(int(p.ID)) 107 svcKey.SetBackendSlot(slot) 108 if err := updateServiceEndpoint(svcKey, svcVal); err != nil { 109 if errors.Is(err, unix.E2BIG) { 110 return fmt.Errorf("Unable to update service entry %+v => %+v: "+ 111 "Unable to update element for LB bpf map: "+ 112 "You can resize it with the flag \"--%s\". "+ 113 "The resizing might break existing connections to services", 114 svcKey, svcVal, option.LBMapEntriesName) 115 } 116 117 return fmt.Errorf("Unable to update service entry %+v => %+v: %w", svcKey, svcVal, err) 118 } 119 slot++ 120 } 121 } 122 123 zeroValue := svcVal.New().(ServiceValue) 124 zeroValue.SetRevNat(int(p.ID)) // TODO change to uint16 125 revNATKey := zeroValue.RevNatKey() 126 revNATValue := svcKey.RevNatValue() 127 if err := updateRevNatLocked(revNATKey, revNATValue); err != nil { 128 return fmt.Errorf("Unable to update reverse NAT %+v => %+v: %w", revNATKey, revNATValue, err) 129 } 130 131 if err := updateMasterService(svcKey, svcVal.New().(ServiceValue), len(backends), int(p.ID), p.Type, p.ExtLocal, p.IntLocal, p.NatPolicy, 132 p.SessionAffinity, p.SessionAffinityTimeoutSec, p.CheckSourceRange, p.L7LBProxyPort, p.LoopbackHostport); err != nil { 133 deleteRevNatLocked(revNATKey) 134 return fmt.Errorf("Unable to update service %+v: %w", svcKey, err) 135 } 136 137 if backendsOk { 138 for i := slot; i <= p.PrevBackendsCount; i++ { 139 svcKey.SetBackendSlot(i) 140 if err := deleteServiceLocked(svcKey); err != nil { 141 log.WithFields(logrus.Fields{ 142 logfields.ServiceKey: svcKey, 143 logfields.BackendSlot: svcKey.GetBackendSlot(), 144 }).WithError(err).Warn("Unable to delete service entry from BPF map") 145 } 146 } 147 } 148 149 return nil 150 } 151 152 // UpsertService inserts or updates the given service in a BPF map. 153 // 154 // The corresponding backend entries (identified with the given backendIDs) 155 // have to exist before calling the function. 156 // 157 // The service's prevActiveBackendCount denotes the count of previously active 158 // backend entries that were added to the BPF map so that the function can remove 159 // obsolete ones. 160 // 161 // The service's non-active backends are appended to the active backends list, 162 // and skipped from the service backends count set in the master key so that the 163 // non-active backends will not be considered for load-balancing traffic. The 164 // backends count is used in the datapath to determine if a service has any backends. 165 // The non-active backends are, however, populated in the service map so that they 166 // can be restored upon agent restart along with their state. 167 func (lbmap *LBBPFMap) UpsertService(p *datapathTypes.UpsertServiceParams) error { 168 if p.ID == 0 { 169 return fmt.Errorf("Invalid svc ID 0") 170 } 171 if err := lbmap.upsertServiceProto(p, 172 p.IPv6 || p.NatPolicy == loadbalancer.SVCNatPolicyNat46); err != nil { 173 return err 174 } 175 if p.NatPolicy == loadbalancer.SVCNatPolicyNat46 { 176 if err := lbmap.upsertServiceProto(p, false); err != nil { 177 return err 178 } 179 } 180 return nil 181 } 182 183 // UpsertMaglevLookupTable calculates Maglev lookup table for given backends, and 184 // inserts into the Maglev BPF map. 185 func (lbmap *LBBPFMap) UpsertMaglevLookupTable(svcID uint16, backends map[string]*loadbalancer.Backend, ipv6 bool) error { 186 table := maglev.GetLookupTable(backends, lbmap.maglevTableSize) 187 for i, id := range table { 188 lbmap.maglevBackendIDsBuffer[i] = loadbalancer.BackendID(id) 189 } 190 if err := updateMaglevTable(ipv6, svcID, lbmap.maglevBackendIDsBuffer); err != nil { 191 return err 192 } 193 194 return nil 195 } 196 197 func deleteServiceProto(svc loadbalancer.L3n4AddrID, backendCount int, useMaglev, ipv6 bool) error { 198 var ( 199 svcKey ServiceKey 200 revNATKey RevNatKey 201 ) 202 203 if ipv6 { 204 svcKey = NewService6Key(svc.AddrCluster.AsNetIP(), svc.Port, u8proto.ANY, svc.Scope, 0) 205 revNATKey = NewRevNat6Key(uint16(svc.ID)) 206 } else { 207 svcKey = NewService4Key(svc.AddrCluster.AsNetIP(), svc.Port, u8proto.ANY, svc.Scope, 0) 208 revNATKey = NewRevNat4Key(uint16(svc.ID)) 209 } 210 211 for slot := 0; slot <= backendCount; slot++ { 212 svcKey.SetBackendSlot(slot) 213 if err := svcKey.MapDelete(); err != nil { 214 return fmt.Errorf("Unable to delete service entry %+v: %w", svcKey, err) 215 } 216 } 217 218 if useMaglev { 219 if err := deleteMaglevTable(ipv6, uint16(svc.ID)); err != nil { 220 return fmt.Errorf("Unable to delete maglev lookup table %d: %w", svc.ID, err) 221 } 222 } 223 224 if err := deleteRevNatLocked(revNATKey); err != nil { 225 return fmt.Errorf("Unable to delete revNAT entry %+v: %w", revNATKey, err) 226 } 227 228 return nil 229 } 230 231 // DeleteService removes given service from a BPF map. 232 func (*LBBPFMap) DeleteService(svc loadbalancer.L3n4AddrID, backendCount int, useMaglev bool, 233 natPolicy loadbalancer.SVCNatPolicy) error { 234 if svc.ID == 0 { 235 return fmt.Errorf("Invalid svc ID 0") 236 } 237 if err := deleteServiceProto(svc, backendCount, useMaglev, 238 svc.IsIPv6() || natPolicy == loadbalancer.SVCNatPolicyNat46); err != nil { 239 return err 240 } 241 if natPolicy == loadbalancer.SVCNatPolicyNat46 { 242 if err := deleteServiceProto(svc, 0, false, false); err != nil { 243 return err 244 } 245 } 246 return nil 247 } 248 249 // AddBackend adds a backend into a BPF map. ipv6 indicates if the backend needs 250 // to be added in the v4 or v6 backend map. 251 func (*LBBPFMap) AddBackend(b *loadbalancer.Backend, ipv6 bool) error { 252 var ( 253 backend Backend 254 err error 255 ) 256 257 if backend, err = getBackend(b, ipv6); err != nil { 258 return err 259 } 260 if err := updateBackend(backend); err != nil { 261 return fmt.Errorf("unable to add backend %+v: %w", backend, err) 262 } 263 264 return nil 265 } 266 267 // UpdateBackendWithState updates the state for the given backend. 268 // 269 // This function should only be called to update backend's state. 270 func (*LBBPFMap) UpdateBackendWithState(b *loadbalancer.Backend) error { 271 var ( 272 backend Backend 273 err error 274 ) 275 276 if backend, err = getBackend(b, b.L3n4Addr.IsIPv6()); err != nil { 277 return err 278 } 279 if err := updateBackend(backend); err != nil { 280 return fmt.Errorf("unable to update backend state %+v: %w", b, err) 281 } 282 283 return nil 284 } 285 286 func deleteBackendByIDFamily(id loadbalancer.BackendID, ipv6 bool) error { 287 var key BackendKey 288 289 if ipv6 { 290 key = NewBackend6KeyV3(loadbalancer.BackendID(id)) 291 } else { 292 key = NewBackend4KeyV3(loadbalancer.BackendID(id)) 293 } 294 295 if err := deleteBackendLocked(key); err != nil { 296 return fmt.Errorf("Unable to delete backend %d (%t): %w", id, ipv6, err) 297 } 298 299 return nil 300 } 301 302 // DeleteBackendByID removes a backend identified with the given ID from a BPF map. 303 func (*LBBPFMap) DeleteBackendByID(id loadbalancer.BackendID) error { 304 if id == 0 { 305 return fmt.Errorf("Invalid backend ID 0") 306 } 307 308 // The backend could be a backend for a NAT64 service, therefore 309 // attempt to remove from both backend maps. 310 if option.Config.EnableIPv6 { 311 deleteBackendByIDFamily(id, true) 312 } 313 if option.Config.EnableIPv4 { 314 deleteBackendByIDFamily(id, false) 315 } 316 return nil 317 } 318 319 // DeleteAffinityMatch removes the affinity match for the given svc and backend ID 320 // tuple from the BPF map 321 func (*LBBPFMap) DeleteAffinityMatch(revNATID uint16, backendID loadbalancer.BackendID) error { 322 return AffinityMatchMap.Delete( 323 NewAffinityMatchKey(revNATID, backendID).ToNetwork()) 324 } 325 326 // AddAffinityMatch adds the given affinity match to the BPF map. 327 func (*LBBPFMap) AddAffinityMatch(revNATID uint16, backendID loadbalancer.BackendID) error { 328 return AffinityMatchMap.Update( 329 NewAffinityMatchKey(revNATID, backendID).ToNetwork(), 330 &AffinityMatchValue{}) 331 } 332 333 // DumpAffinityMatches returns the affinity match map represented as a nested 334 // map which first key is svc ID and the second - backend ID. 335 func (*LBBPFMap) DumpAffinityMatches() (datapathTypes.BackendIDByServiceIDSet, error) { 336 matches := datapathTypes.BackendIDByServiceIDSet{} 337 338 parse := func(key bpf.MapKey, value bpf.MapValue) { 339 matchKey := key.(*AffinityMatchKey).ToHost() 340 svcID := matchKey.RevNATID 341 backendID := matchKey.BackendID 342 343 if _, ok := matches[svcID]; !ok { 344 matches[svcID] = map[loadbalancer.BackendID]struct{}{} 345 } 346 matches[svcID][backendID] = struct{}{} 347 } 348 349 err := AffinityMatchMap.DumpWithCallback(parse) 350 if err != nil { 351 return nil, err 352 } 353 354 return matches, nil 355 } 356 357 func (*LBBPFMap) DumpSourceRanges(ipv6 bool) (datapathTypes.SourceRangeSetByServiceID, error) { 358 ret := datapathTypes.SourceRangeSetByServiceID{} 359 parser := func(key bpf.MapKey, value bpf.MapValue) { 360 k := key.(SourceRangeKey).ToHost() 361 revNATID := k.GetRevNATID() 362 if _, found := ret[revNATID]; !found { 363 ret[revNATID] = []*cidr.CIDR{} 364 } 365 ret[revNATID] = append(ret[revNATID], k.GetCIDR()) 366 } 367 368 m := SourceRange4Map 369 if ipv6 { 370 m = SourceRange6Map 371 } 372 if err := m.DumpWithCallback(parser); err != nil { 373 return nil, err 374 } 375 376 return ret, nil 377 } 378 379 func updateRevNatLocked(key RevNatKey, value RevNatValue) error { 380 if key.GetKey() == 0 { 381 return fmt.Errorf("invalid RevNat ID (0)") 382 } 383 if err := key.Map().OpenOrCreate(); err != nil { 384 return err 385 } 386 387 return key.Map().Update(key.ToNetwork(), value.ToNetwork()) 388 } 389 390 func deleteRevNatLocked(key RevNatKey) error { 391 return key.Map().Delete(key.ToNetwork()) 392 } 393 394 func (*LBBPFMap) UpdateSourceRanges(revNATID uint16, prevSourceRanges []*cidr.CIDR, 395 sourceRanges []*cidr.CIDR, ipv6 bool) error { 396 397 m := SourceRange4Map 398 if ipv6 { 399 m = SourceRange6Map 400 } 401 402 srcRangeMap := map[string]*cidr.CIDR{} 403 for _, cidr := range sourceRanges { 404 // k8s api server does not catch the IP family mismatch, so we need to catch it here 405 if ip.IsIPv6(cidr.IP) == !ipv6 { 406 log.WithFields(logrus.Fields{ 407 logfields.ServiceID: revNATID, 408 logfields.CIDR: cidr, 409 }).Warn("Source range's IP family does not match with the LB's. Ignoring the source range CIDR") 410 continue 411 } 412 srcRangeMap[cidr.String()] = cidr 413 } 414 415 for _, prevCIDR := range prevSourceRanges { 416 if _, found := srcRangeMap[prevCIDR.String()]; !found { 417 if err := m.Delete(srcRangeKey(prevCIDR, revNATID, ipv6)); err != nil { 418 return err 419 } 420 } else { 421 delete(srcRangeMap, prevCIDR.String()) 422 } 423 } 424 425 for _, cidr := range srcRangeMap { 426 if err := m.Update(srcRangeKey(cidr, revNATID, ipv6), &SourceRangeValue{}); err != nil { 427 return err 428 } 429 } 430 431 return nil 432 } 433 434 // DumpServiceMaps dumps the services from the BPF maps. 435 func (*LBBPFMap) DumpServiceMaps() ([]*loadbalancer.SVC, []error) { 436 newSVCMap := svcMap{} 437 errors := []error{} 438 flagsCache := map[string]loadbalancer.ServiceFlags{} 439 backendValueMap := map[loadbalancer.BackendID]BackendValue{} 440 441 parseBackendEntries := func(key bpf.MapKey, value bpf.MapValue) { 442 backendKey := key.(BackendKey) 443 backendValue := value.(BackendValue).ToHost() 444 backendValueMap[backendKey.GetID()] = backendValue 445 } 446 447 parseSVCEntries := func(key bpf.MapKey, value bpf.MapValue) { 448 svcKey := key.(ServiceKey).ToHost() 449 svcValue := value.(ServiceValue).ToHost() 450 451 fe := svcFrontend(svcKey, svcValue) 452 453 // Create master entry in case there are no backends. 454 if svcKey.GetBackendSlot() == 0 { 455 // Build a cache of flags stored in the value of the master key to 456 // map it later. 457 // FIXME proto is being ignored everywhere in the datapath. 458 addrStr := svcKey.GetAddress().String() 459 portStr := strconv.Itoa(int(svcKey.GetPort())) 460 flagsCache[net.JoinHostPort(addrStr, portStr)] = loadbalancer.ServiceFlags(svcValue.GetFlags()) 461 462 newSVCMap.addFE(fe) 463 return 464 } 465 466 backendID := svcValue.GetBackendID() 467 backendValue, found := backendValueMap[backendID] 468 if !found { 469 errors = append(errors, fmt.Errorf("backend %d not found", backendID)) 470 return 471 } 472 473 be := svcBackend(backendID, backendValue) 474 newSVCMap.addFEnBE(fe, be, svcKey.GetBackendSlot()) 475 } 476 477 if option.Config.EnableIPv4 { 478 // TODO(brb) optimization: instead of dumping the backend map, we can 479 // pass its content to the function. 480 err := Backend4MapV3.DumpWithCallback(parseBackendEntries) 481 if err != nil { 482 errors = append(errors, err) 483 } 484 err = Service4MapV2.DumpWithCallback(parseSVCEntries) 485 if err != nil { 486 errors = append(errors, err) 487 } 488 } 489 490 if option.Config.EnableIPv6 { 491 // TODO(brb) same ^^ optimization applies here as well. 492 err := Backend6MapV3.DumpWithCallback(parseBackendEntries) 493 if err != nil { 494 errors = append(errors, err) 495 } 496 err = Service6MapV2.DumpWithCallback(parseSVCEntries) 497 if err != nil { 498 errors = append(errors, err) 499 } 500 } 501 502 newSVCList := make([]*loadbalancer.SVC, 0, len(newSVCMap)) 503 for hash := range newSVCMap { 504 svc := newSVCMap[hash] 505 addrStr := svc.Frontend.AddrCluster.String() 506 portStr := strconv.Itoa(int(svc.Frontend.Port)) 507 host := net.JoinHostPort(addrStr, portStr) 508 svc.Type = flagsCache[host].SVCType() 509 svc.ExtTrafficPolicy = flagsCache[host].SVCExtTrafficPolicy() 510 svc.IntTrafficPolicy = flagsCache[host].SVCIntTrafficPolicy() 511 svc.NatPolicy = flagsCache[host].SVCNatPolicy(svc.Frontend.L3n4Addr) 512 newSVCList = append(newSVCList, &svc) 513 } 514 515 return newSVCList, errors 516 } 517 518 // DumpBackendMaps dumps the backend entries from the BPF maps. 519 func (*LBBPFMap) DumpBackendMaps() ([]*loadbalancer.Backend, error) { 520 backendValueMap := map[loadbalancer.BackendID]BackendValue{} 521 lbBackends := []*loadbalancer.Backend{} 522 523 parseBackendEntries := func(key bpf.MapKey, value bpf.MapValue) { 524 // No need to deep copy the key because we are using the ID which 525 // is a value. 526 backendKey := key.(BackendKey) 527 backendValue := value.(BackendValue).ToHost() 528 backendValueMap[backendKey.GetID()] = backendValue 529 } 530 531 if option.Config.EnableIPv4 { 532 err := Backend4MapV3.DumpWithCallback(parseBackendEntries) 533 if err != nil { 534 return nil, fmt.Errorf("Unable to dump lb4 backends map: %w", err) 535 } 536 } 537 538 if option.Config.EnableIPv6 { 539 err := Backend6MapV3.DumpWithCallback(parseBackendEntries) 540 if err != nil { 541 return nil, fmt.Errorf("Unable to dump lb6 backends map: %w", err) 542 } 543 } 544 545 for backendID, backendVal := range backendValueMap { 546 ip := backendVal.GetAddress() 547 addrCluster := cmtypes.MustAddrClusterFromIP(ip) 548 port := backendVal.GetPort() 549 proto := loadbalancer.NONE 550 state := loadbalancer.GetBackendStateFromFlags(backendVal.GetFlags()) 551 zone := backendVal.GetZone() 552 lbBackend := loadbalancer.NewBackendWithState(backendID, proto, addrCluster, port, zone, state) 553 lbBackends = append(lbBackends, lbBackend) 554 } 555 556 return lbBackends, nil 557 } 558 559 // IsMaglevLookupTableRecreated returns true if the maglev lookup BPF map 560 // was recreated due to the changed M param. 561 func (*LBBPFMap) IsMaglevLookupTableRecreated(ipv6 bool) bool { 562 if ipv6 { 563 return maglevRecreatedIPv6 564 } 565 return maglevRecreatedIPv4 566 } 567 568 func updateMasterService(fe ServiceKey, v ServiceValue, activeBackends int, revNATID int, svcType loadbalancer.SVCType, 569 svcExtLocal, svcIntLocal bool, svcNatPolicy loadbalancer.SVCNatPolicy, sessionAffinity bool, 570 sessionAffinityTimeoutSec uint32, checkSourceRange bool, l7lbProxyPort uint16, loopbackHostport bool) error { 571 572 // isRoutable denotes whether this service can be accessed from outside the cluster. 573 isRoutable := !fe.IsSurrogate() && 574 (svcType != loadbalancer.SVCTypeClusterIP || option.Config.ExternalClusterIP) 575 576 fe.SetBackendSlot(0) 577 v.SetCount(activeBackends) 578 v.SetRevNat(revNATID) 579 flag := loadbalancer.NewSvcFlag(&loadbalancer.SvcFlagParam{ 580 SvcType: svcType, 581 SvcExtLocal: svcExtLocal, 582 SvcIntLocal: svcIntLocal, 583 SvcNatPolicy: svcNatPolicy, 584 SessionAffinity: sessionAffinity, 585 IsRoutable: isRoutable, 586 CheckSourceRange: checkSourceRange, 587 L7LoadBalancer: l7lbProxyPort != 0, 588 LoopbackHostport: loopbackHostport, 589 }) 590 v.SetFlags(flag.UInt16()) 591 if sessionAffinity { 592 v.SetSessionAffinityTimeoutSec(sessionAffinityTimeoutSec) 593 } 594 if l7lbProxyPort != 0 { 595 v.SetL7LBProxyPort(l7lbProxyPort) 596 } 597 598 return updateServiceEndpoint(fe, v) 599 } 600 601 func deleteServiceLocked(key ServiceKey) error { 602 return key.Map().Delete(key.ToNetwork()) 603 } 604 605 func getBackend(backend *loadbalancer.Backend, ipv6 bool) (Backend, error) { 606 var ( 607 lbBackend Backend 608 err error 609 ) 610 611 if backend.ID == 0 { 612 return lbBackend, fmt.Errorf("invalid backend ID 0") 613 } 614 615 if ipv6 { 616 lbBackend, err = NewBackend6V3(backend.ID, backend.AddrCluster, backend.Port, u8proto.ANY, 617 backend.State, backend.ZoneID) 618 } else { 619 lbBackend, err = NewBackend4V3(backend.ID, backend.AddrCluster, backend.Port, u8proto.ANY, 620 backend.State, backend.ZoneID) 621 } 622 if err != nil { 623 return lbBackend, fmt.Errorf("unable to create lbBackend (%d, %s, %d, %t): %w", 624 backend.ID, backend.AddrCluster.String(), backend.Port, ipv6, err) 625 } 626 627 return lbBackend, nil 628 } 629 630 func updateBackend(backend Backend) error { 631 if err := backend.Map().OpenOrCreate(); err != nil { 632 return err 633 } 634 635 return backend.Map().Update(backend.GetKey(), backend.GetValue().ToNetwork()) 636 } 637 638 func deleteBackendLocked(key BackendKey) error { 639 _, err := key.Map().SilentDelete(key) 640 return err 641 } 642 643 func updateServiceEndpoint(key ServiceKey, value ServiceValue) error { 644 if key.GetBackendSlot() != 0 && value.RevNatKey().GetKey() == 0 { 645 return fmt.Errorf("invalid RevNat ID (0) in the Service Value") 646 } 647 if err := key.Map().OpenOrCreate(); err != nil { 648 return err 649 } 650 651 if err := key.Map().Update(key.ToNetwork(), value.ToNetwork()); err != nil { 652 return err 653 } 654 655 log.WithFields(logrus.Fields{ 656 logfields.ServiceKey: key, 657 logfields.ServiceValue: value, 658 logfields.BackendSlot: key.GetBackendSlot(), 659 }).Debug("Upserted service entry") 660 661 return nil 662 } 663 664 type svcMap map[string]loadbalancer.SVC 665 666 // addFE adds the give 'fe' to the svcMap without any backends. If it does not 667 // yet exist, an entry is created. Otherwise, the existing entry is left 668 // unchanged. 669 func (svcs svcMap) addFE(fe *loadbalancer.L3n4AddrID) *loadbalancer.SVC { 670 hash := fe.Hash() 671 lbsvc, ok := svcs[hash] 672 if !ok { 673 lbsvc = loadbalancer.SVC{Frontend: *fe} 674 svcs[hash] = lbsvc 675 } 676 return &lbsvc 677 } 678 679 // addFEnBE adds the given 'fe' and 'be' to the svcMap. If 'fe' exists and beIndex is 0, 680 // the new 'be' will be appended to the list of existing backends. If beIndex is bigger 681 // than the size of existing backends slice, it will be created a new array with size of 682 // beIndex and the new 'be' will be inserted on index beIndex-1 of that new array. All 683 // remaining be elements will be kept on the same index and, in case the new array is 684 // larger than the number of backends, some elements will be empty. 685 func (svcs svcMap) addFEnBE(fe *loadbalancer.L3n4AddrID, be *loadbalancer.Backend, beIndex int) *loadbalancer.SVC { 686 hash := fe.Hash() 687 lbsvc, ok := svcs[hash] 688 if !ok { 689 var bes []*loadbalancer.Backend 690 if beIndex == 0 { 691 bes = make([]*loadbalancer.Backend, 1) 692 bes[0] = be 693 } else { 694 bes = make([]*loadbalancer.Backend, beIndex) 695 bes[beIndex-1] = be 696 } 697 lbsvc = loadbalancer.SVC{ 698 Frontend: *fe, 699 Backends: bes, 700 } 701 } else { 702 var bes []*loadbalancer.Backend 703 if len(lbsvc.Backends) < beIndex { 704 bes = make([]*loadbalancer.Backend, beIndex) 705 copy(bes, lbsvc.Backends) 706 lbsvc.Backends = bes 707 } 708 if beIndex == 0 { 709 lbsvc.Backends = append(lbsvc.Backends, be) 710 } else { 711 lbsvc.Backends[beIndex-1] = be 712 } 713 } 714 715 svcs[hash] = lbsvc 716 return &lbsvc 717 } 718 719 // Init updates the map info defaults for sock rev nat {4,6} and LB maps and 720 // then initializes all LB-related maps. 721 func Init(params InitParams) { 722 if params.MaxSockRevNatMapEntries != 0 { 723 MaxSockRevNat4MapEntries = params.MaxSockRevNatMapEntries 724 MaxSockRevNat6MapEntries = params.MaxSockRevNatMapEntries 725 } 726 727 MaglevMapMaxEntries = params.MaglevMapMaxEntries 728 729 initSVC(params) 730 initAffinity(params) 731 initSourceRange(params) 732 } 733 734 // ExistsSockRevNat checks if the passed entry exists in the sock rev nat map. 735 func (*LBBPFMap) ExistsSockRevNat(cookie uint64, addr net.IP, port uint16) bool { 736 if addr.To4() != nil { 737 key := NewSockRevNat4Key(cookie, addr, port) 738 if _, err := key.Map().Lookup(key); err == nil { 739 return true 740 } 741 } else { 742 key := NewSockRevNat6Key(cookie, addr, port) 743 if _, err := key.Map().Lookup(key); err == nil { 744 return true 745 } 746 } 747 748 return false 749 } 750 751 // InitParams represents the parameters to be passed to Init(). 752 type InitParams struct { 753 IPv4, IPv6 bool 754 755 MaxSockRevNatMapEntries int 756 ServiceMapMaxEntries, BackEndMapMaxEntries, RevNatMapMaxEntries int 757 AffinityMapMaxEntries int 758 SourceRangeMapMaxEntries int 759 MaglevMapMaxEntries int 760 }