github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/endpoint/bpf.go (about) 1 // Copyright 2016-2020 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package endpoint 16 17 import ( 18 "bufio" 19 "context" 20 "fmt" 21 "io" 22 "os" 23 "path/filepath" 24 "syscall" 25 "time" 26 27 "github.com/cilium/cilium/api/v1/models" 28 "github.com/cilium/cilium/common" 29 "github.com/cilium/cilium/pkg/bpf" 30 "github.com/cilium/cilium/pkg/completion" 31 "github.com/cilium/cilium/pkg/controller" 32 "github.com/cilium/cilium/pkg/datapath/loader" 33 "github.com/cilium/cilium/pkg/endpoint/regeneration" 34 "github.com/cilium/cilium/pkg/loadinfo" 35 "github.com/cilium/cilium/pkg/logging/logfields" 36 bpfconfig "github.com/cilium/cilium/pkg/maps/configmap" 37 "github.com/cilium/cilium/pkg/maps/ctmap" 38 "github.com/cilium/cilium/pkg/maps/eppolicymap" 39 "github.com/cilium/cilium/pkg/maps/lxcmap" 40 "github.com/cilium/cilium/pkg/maps/policymap" 41 "github.com/cilium/cilium/pkg/option" 42 "github.com/cilium/cilium/pkg/policy" 43 "github.com/cilium/cilium/pkg/policy/trafficdirection" 44 "github.com/cilium/cilium/pkg/revert" 45 "github.com/cilium/cilium/pkg/version" 46 47 "github.com/sirupsen/logrus" 48 ) 49 50 const ( 51 // EndpointGenerationTimeout specifies timeout for proxy completion context 52 EndpointGenerationTimeout = 330 * time.Second 53 ) 54 55 // PolicyMapPathLocked returns the path to the policy map of endpoint. 56 func (e *Endpoint) PolicyMapPathLocked() string { 57 return bpf.LocalMapPath(policymap.MapName, e.ID) 58 } 59 60 // CallsMapPathLocked returns the path to cilium tail calls map of an endpoint. 61 func (e *Endpoint) CallsMapPathLocked() string { 62 return bpf.LocalMapPath(loader.CallsMapName, e.ID) 63 } 64 65 // BPFConfigMapPath returns the path to the BPF config map of endpoint. 66 func (e *Endpoint) BPFConfigMapPath() string { 67 return bpf.LocalMapPath(bpfconfig.MapNamePrefix, e.ID) 68 } 69 70 // BPFIpvlanMapPath returns the path to the ipvlan tail call map of an endpoint. 71 func (e *Endpoint) BPFIpvlanMapPath() string { 72 return bpf.LocalMapPath(IpvlanMapName, e.ID) 73 } 74 75 // writeInformationalComments writes annotations to the specified writer, 76 // including a base64 encoding of the endpoint object, and human-readable 77 // strings describing the configuration of the datapath. 78 // 79 // For configuration of actual datapath behavior, see WriteEndpointConfig(). 80 func (e *Endpoint) writeInformationalComments(w io.Writer) error { 81 fw := bufio.NewWriter(w) 82 83 fmt.Fprint(fw, "/*\n") 84 85 epStr64, err := e.base64() 86 if err == nil { 87 var verBase64 string 88 verBase64, err = version.Base64() 89 if err == nil { 90 fmt.Fprintf(fw, " * %s%s:%s\n * \n", common.CiliumCHeaderPrefix, 91 verBase64, epStr64) 92 } 93 } 94 if err != nil { 95 e.logStatusLocked(BPF, Warning, fmt.Sprintf("Unable to create a base64: %s", err)) 96 } 97 98 if e.ContainerID == "" { 99 fmt.Fprintf(fw, " * Docker Network ID: %s\n", e.DockerNetworkID) 100 fmt.Fprintf(fw, " * Docker Endpoint ID: %s\n", e.DockerEndpointID) 101 } else { 102 fmt.Fprintf(fw, " * Container ID: %s\n", e.ContainerID) 103 } 104 105 fmt.Fprintf(fw, ""+ 106 " * IPv6 address: %s\n"+ 107 " * IPv4 address: %s\n"+ 108 " * Identity: %d\n"+ 109 " * PolicyMap: %s\n"+ 110 " * NodeMAC: %s\n"+ 111 " */\n\n", 112 e.IPv6.String(), e.IPv4.String(), 113 e.GetIdentity(), bpf.LocalMapName(policymap.MapName, e.ID), 114 e.NodeMAC) 115 116 fw.WriteString("/*\n") 117 fw.WriteString(" * Labels:\n") 118 if e.SecurityIdentity != nil { 119 if len(e.SecurityIdentity.Labels) == 0 { 120 fmt.Fprintf(fw, " * - %s\n", "(no labels)") 121 } else { 122 for _, v := range e.SecurityIdentity.Labels { 123 fmt.Fprintf(fw, " * - %s\n", v) 124 } 125 } 126 } 127 fw.WriteString(" */\n\n") 128 129 return fw.Flush() 130 } 131 132 func (e *Endpoint) writeHeaderfile(prefix string) error { 133 headerPath := filepath.Join(prefix, common.CHeaderFileName) 134 e.getLogger().WithFields(logrus.Fields{ 135 logfields.Path: headerPath, 136 }).Debug("writing header file") 137 f, err := os.Create(headerPath) 138 if err != nil { 139 return fmt.Errorf("failed to open file %s for writing: %s", headerPath, err) 140 141 } 142 defer f.Close() 143 144 if err = e.writeInformationalComments(f); err != nil { 145 return err 146 } 147 return e.owner.Datapath().WriteEndpointConfig(f, e) 148 } 149 150 // addNewRedirectsFromMap must be called while holding the endpoint lock for 151 // writing. On success, returns nil; otherwise, returns an error indicating the 152 // problem that occurred while adding an l7 redirect for the specified policy. 153 // Must be called with endpoint.Mutex held. 154 func (e *Endpoint) addNewRedirectsFromMap(m policy.L4PolicyMap, desiredRedirects map[string]bool, proxyWaitGroup *completion.WaitGroup) (error, revert.FinalizeFunc, revert.RevertFunc) { 155 if option.Config.DryMode { 156 return nil, nil, nil 157 } 158 159 var finalizeList revert.FinalizeList 160 var revertStack revert.RevertStack 161 var updatedStats []*models.ProxyStatistics 162 insertedDesiredMapState := make(map[policy.Key]struct{}) 163 updatedDesiredMapState := make(policy.MapState) 164 165 for _, l4 := range m { 166 if l4.IsRedirect() { 167 var redirectPort uint16 168 var err error 169 // Only create a redirect if the proxy is NOT running in a sidecar 170 // container. If running in a sidecar container, just allow traffic 171 // to the port at L4 by setting the proxy port to 0. 172 if !e.hasSidecarProxy || l4.L7Parser != policy.ParserTypeHTTP { 173 var finalizeFunc revert.FinalizeFunc 174 var revertFunc revert.RevertFunc 175 redirectPort, err, finalizeFunc, revertFunc = e.owner.UpdateProxyRedirect(e, l4, proxyWaitGroup) 176 if err != nil { 177 revertStack.Revert() // Ignore errors while reverting. This is best-effort. 178 return err, nil, nil 179 } 180 finalizeList.Append(finalizeFunc) 181 revertStack.Push(revertFunc) 182 183 proxyID := e.ProxyID(l4) 184 if e.realizedRedirects == nil { 185 e.realizedRedirects = make(map[string]uint16) 186 } 187 if _, found := e.realizedRedirects[proxyID]; !found { 188 revertStack.Push(func() error { 189 delete(e.realizedRedirects, proxyID) 190 return nil 191 }) 192 } 193 e.realizedRedirects[proxyID] = redirectPort 194 195 desiredRedirects[proxyID] = true 196 197 // Update the endpoint API model to report that Cilium manages a 198 // redirect for that port. 199 e.proxyStatisticsMutex.Lock() 200 proxyStats := e.getProxyStatisticsLocked(proxyID, string(l4.L7Parser), uint16(l4.Port), l4.Ingress) 201 proxyStats.AllocatedProxyPort = int64(redirectPort) 202 e.proxyStatisticsMutex.Unlock() 203 204 updatedStats = append(updatedStats, proxyStats) 205 } 206 207 // Set the proxy port in the policy map. 208 var direction trafficdirection.TrafficDirection 209 if l4.Ingress { 210 direction = trafficdirection.Ingress 211 } else { 212 direction = trafficdirection.Egress 213 } 214 215 keysFromFilter := l4.ToKeys(direction) 216 217 for _, keyFromFilter := range keysFromFilter { 218 if oldEntry, ok := e.desiredPolicy.PolicyMapState[keyFromFilter]; ok { 219 updatedDesiredMapState[keyFromFilter] = oldEntry 220 } else { 221 insertedDesiredMapState[keyFromFilter] = struct{}{} 222 } 223 224 e.desiredPolicy.PolicyMapState[keyFromFilter] = policy.MapStateEntry{ProxyPort: redirectPort} 225 } 226 227 } 228 } 229 230 revertStack.Push(func() error { 231 // Restore the proxy stats. 232 e.proxyStatisticsMutex.Lock() 233 for _, stats := range updatedStats { 234 stats.AllocatedProxyPort = 0 235 } 236 e.proxyStatisticsMutex.Unlock() 237 238 // Restore the desired policy map state. 239 for key := range insertedDesiredMapState { 240 delete(e.desiredPolicy.PolicyMapState, key) 241 } 242 for key, entry := range updatedDesiredMapState { 243 e.desiredPolicy.PolicyMapState[key] = entry 244 } 245 return nil 246 }) 247 248 return nil, finalizeList.Finalize, revertStack.Revert 249 } 250 251 // addNewRedirects must be called while holding the endpoint lock for writing. 252 // On success, returns nil; otherwise, returns an error indicating the problem 253 // that occurred while adding an l7 redirect for the specified policy. 254 // The returned map contains the exact set of IDs of proxy redirects that is 255 // required to implement the given L4 policy. 256 // Must be called with endpoint.Mutex held. 257 func (e *Endpoint) addNewRedirects(m *policy.L4Policy, proxyWaitGroup *completion.WaitGroup) (desiredRedirects map[string]bool, err error, finalizeFunc revert.FinalizeFunc, revertFunc revert.RevertFunc) { 258 desiredRedirects = make(map[string]bool) 259 var finalizeList revert.FinalizeList 260 var revertStack revert.RevertStack 261 262 var ff revert.FinalizeFunc 263 var rf revert.RevertFunc 264 265 err, ff, rf = e.addNewRedirectsFromMap(m.Ingress, desiredRedirects, proxyWaitGroup) 266 if err != nil { 267 return desiredRedirects, fmt.Errorf("unable to allocate ingress redirects: %s", err), nil, nil 268 } 269 finalizeList.Append(ff) 270 revertStack.Push(rf) 271 272 err, ff, rf = e.addNewRedirectsFromMap(m.Egress, desiredRedirects, proxyWaitGroup) 273 if err != nil { 274 revertStack.Revert() // Ignore errors while reverting. This is best-effort. 275 return desiredRedirects, fmt.Errorf("unable to allocate egress redirects: %s", err), nil, nil 276 } 277 finalizeList.Append(ff) 278 revertStack.Push(rf) 279 280 return desiredRedirects, nil, finalizeList.Finalize, func() error { 281 e.getLogger().Debug("Reverting proxy redirect additions") 282 283 err := revertStack.Revert() 284 285 e.getLogger().Debug("Finished reverting proxy redirect additions") 286 287 return err 288 } 289 } 290 291 // Must be called with endpoint.Mutex held. 292 func (e *Endpoint) removeOldRedirects(desiredRedirects map[string]bool, proxyWaitGroup *completion.WaitGroup) (revert.FinalizeFunc, revert.RevertFunc) { 293 if option.Config.DryMode { 294 return nil, nil 295 } 296 297 var finalizeList revert.FinalizeList 298 var revertStack revert.RevertStack 299 removedRedirects := make(map[string]uint16, len(e.realizedRedirects)) 300 updatedStats := make(map[uint16]*models.ProxyStatistics, len(e.realizedRedirects)) 301 302 for id, redirectPort := range e.realizedRedirects { 303 // Remove only the redirects that are not required. 304 if desiredRedirects[id] { 305 continue 306 } 307 308 err, finalizeFunc, revertFunc := e.owner.RemoveProxyRedirect(e, id, proxyWaitGroup) 309 if err != nil { 310 e.getLogger().WithError(err).WithField(logfields.L4PolicyID, id).Warn("Error while removing proxy redirect") 311 continue 312 } 313 finalizeList.Append(finalizeFunc) 314 revertStack.Push(revertFunc) 315 316 delete(e.realizedRedirects, id) 317 removedRedirects[id] = redirectPort 318 319 // Update the endpoint API model to report that no redirect is 320 // active or known for that port anymore. We never delete stats 321 // until an endpoint is deleted, so we only set the redirect port 322 // to 0. 323 e.proxyStatisticsMutex.Lock() 324 if proxyStats, ok := e.proxyStatistics[id]; ok { 325 updatedStats[redirectPort] = proxyStats 326 proxyStats.AllocatedProxyPort = 0 327 } else { 328 e.getLogger().WithField(logfields.L4PolicyID, id).Warn("Proxy stats not found") 329 } 330 e.proxyStatisticsMutex.Unlock() 331 } 332 333 return finalizeList.Finalize, 334 func() error { 335 e.getLogger().Debug("Reverting proxy redirect removals") 336 337 // Restore the proxy stats. 338 e.proxyStatisticsMutex.Lock() 339 for redirectPort, stats := range updatedStats { 340 stats.AllocatedProxyPort = int64(redirectPort) 341 } 342 e.proxyStatisticsMutex.Unlock() 343 344 for id, redirectPort := range removedRedirects { 345 e.realizedRedirects[id] = redirectPort 346 } 347 348 err := revertStack.Revert() 349 350 e.getLogger().Debug("Finished reverting proxy redirect removals") 351 352 return err 353 } 354 } 355 356 // regenerateBPF rewrites all headers and updates all BPF maps to reflect the 357 // specified endpoint. 358 // ReloadDatapath forces the datapath programs to be reloaded. It does 359 // not guarantee recompilation of the programs. 360 // Must be called with endpoint.Mutex not held and endpoint.buildMutex held. 361 // 362 // Returns the policy revision number when the regeneration has called, 363 // Whether the new state dir is populated with all new BPF state files, and 364 // and an error if something failed. 365 func (e *Endpoint) regenerateBPF(regenContext *regenerationContext) (revnum uint64, stateDirComplete bool, reterr error) { 366 var ( 367 err error 368 compilationExecuted bool 369 headerfileChanged bool 370 ) 371 372 stats := ®enContext.Stats 373 stats.waitingForLock.Start() 374 375 datapathRegenCtxt := regenContext.datapathRegenerationContext 376 377 // Make sure that owner is not compiling base programs while we are 378 // regenerating an endpoint. 379 e.owner.GetCompilationLock().RLock() 380 stats.waitingForLock.End(true) 381 defer e.owner.GetCompilationLock().RUnlock() 382 383 datapathRegenCtxt.prepareForProxyUpdates(regenContext.parentContext) 384 defer datapathRegenCtxt.completionCancel() 385 386 headerfileChanged, err = e.runPreCompilationSteps(regenContext) 387 388 // Keep track of the side-effects of the regeneration that need to be 389 // reverted in case of failure. 390 // Also keep track of the regeneration finalization code that can't be 391 // reverted, and execute it in case of regeneration success. 392 defer func() { 393 // Ignore finalizing of proxy state in dry mode. 394 if !option.Config.DryMode { 395 e.finalizeProxyState(regenContext, reterr) 396 } 397 }() 398 399 if err != nil { 400 return 0, compilationExecuted, err 401 } 402 403 // No need to compile BPF in dry mode. 404 if option.Config.DryMode { 405 return e.nextPolicyRevision, false, nil 406 } 407 408 // Wait for connection tracking cleaning to complete 409 stats.waitingForCTClean.Start() 410 <-datapathRegenCtxt.ctCleaned 411 stats.waitingForCTClean.End(true) 412 413 stats.prepareBuild.End(true) 414 415 compilationExecuted, err = e.realizeBPFState(regenContext) 416 if err != nil { 417 return datapathRegenCtxt.epInfoCache.revision, compilationExecuted, err 418 } 419 420 // Hook the endpoint into the endpoint and endpoint to policy tables then expose it 421 stats.mapSync.Start() 422 epErr := eppolicymap.WriteEndpoint(datapathRegenCtxt.epInfoCache.keys, e.PolicyMap) 423 err = lxcmap.WriteEndpoint(datapathRegenCtxt.epInfoCache) 424 stats.mapSync.End(err == nil) 425 if epErr != nil { 426 e.logStatusLocked(BPF, Warning, fmt.Sprintf("Unable to sync EpToPolicy Map continue with Sockmap support: %s", epErr)) 427 } 428 if err != nil { 429 return 0, compilationExecuted, fmt.Errorf("Exposing new BPF failed: %s", err) 430 } 431 432 // Signal that BPF program has been generated. 433 // The endpoint has at least L3/L4 connectivity at this point. 434 e.CloseBPFProgramChannel() 435 436 // Allow another builder to start while we wait for the proxy 437 if regenContext.DoneFunc != nil { 438 regenContext.DoneFunc() 439 } 440 441 stats.proxyWaitForAck.Start() 442 err = e.WaitForProxyCompletions(datapathRegenCtxt.proxyWaitGroup) 443 stats.proxyWaitForAck.End(err == nil) 444 if err != nil { 445 return 0, compilationExecuted, fmt.Errorf("Error while configuring proxy redirects: %s", err) 446 } 447 448 stats.waitingForLock.Start() 449 err = e.LockAlive() 450 stats.waitingForLock.End(err == nil) 451 if err != nil { 452 return 0, compilationExecuted, err 453 } 454 defer e.Unlock() 455 456 e.ctCleaned = true 457 458 // Synchronously try to update PolicyMap for this endpoint. If any 459 // part of updating the PolicyMap fails, bail out. 460 // Unfortunately, this means that the map will be in an inconsistent 461 // state with the current program (if it exists) for this endpoint. 462 // GH-3897 would fix this by creating a new map to do an atomic swap 463 // with the old one. 464 // 465 // This must be done after allocating the new redirects, to update the 466 // policy map with the new proxy ports. 467 stats.mapSync.Start() 468 err = e.syncPolicyMap() 469 stats.mapSync.End(err == nil) 470 if err != nil { 471 return 0, compilationExecuted, fmt.Errorf("unable to regenerate policy because PolicyMap synchronization failed: %s", err) 472 } 473 474 stateDirComplete = headerfileChanged && compilationExecuted 475 return datapathRegenCtxt.epInfoCache.revision, stateDirComplete, err 476 } 477 478 func (e *Endpoint) realizeBPFState(regenContext *regenerationContext) (compilationExecuted bool, err error) { 479 stats := ®enContext.Stats 480 datapathRegenCtxt := regenContext.datapathRegenerationContext 481 482 e.getLogger().WithField(fieldRegenLevel, datapathRegenCtxt.regenerationLevel).Debug("Preparing to compile BPF") 483 484 if datapathRegenCtxt.regenerationLevel > regeneration.RegenerateWithoutDatapath { 485 if e.Options.IsEnabled(option.Debug) { 486 debugFunc := log.WithFields(logrus.Fields{logfields.EndpointID: e.StringID()}).Debugf 487 ctx, cancel := context.WithCancel(regenContext.parentContext) 488 defer cancel() 489 loadinfo.LogPeriodicSystemLoad(ctx, debugFunc, time.Second) 490 } 491 492 // Compile and install BPF programs for this endpoint 493 if datapathRegenCtxt.regenerationLevel == regeneration.RegenerateWithDatapathRebuild { 494 err = loader.CompileAndLoad(datapathRegenCtxt.completionCtx, datapathRegenCtxt.epInfoCache, &stats.datapathRealization) 495 e.getLogger().WithError(err).Info("Regenerated endpoint BPF program") 496 compilationExecuted = true 497 } else if datapathRegenCtxt.regenerationLevel == regeneration.RegenerateWithDatapathRewrite { 498 err = loader.CompileOrLoad(datapathRegenCtxt.completionCtx, datapathRegenCtxt.epInfoCache, &stats.datapathRealization) 499 if err == nil { 500 e.getLogger().Info("Rewrote endpoint BPF program") 501 } else { 502 e.getLogger().WithError(err).Error("Error while rewriting endpoint BPF program") 503 } 504 compilationExecuted = true 505 } else { // RegenerateWithDatapathLoad 506 err = loader.ReloadDatapath(datapathRegenCtxt.completionCtx, datapathRegenCtxt.epInfoCache, &stats.datapathRealization) 507 if err == nil { 508 e.getLogger().Info("Reloaded endpoint BPF program") 509 } else { 510 e.getLogger().WithError(err).Error("Error while reloading endpoint BPF program") 511 } 512 } 513 514 if err != nil { 515 return compilationExecuted, err 516 } 517 e.bpfHeaderfileHash = datapathRegenCtxt.bpfHeaderfilesHash 518 } else { 519 e.getLogger().WithField(logfields.BPFHeaderfileHash, datapathRegenCtxt.bpfHeaderfilesHash). 520 Debug("BPF header file unchanged, skipping BPF compilation and installation") 521 } 522 523 return compilationExecuted, nil 524 } 525 526 // runPreCompilationSteps runs all of the regeneration steps that are necessary 527 // right before compiling the BPF for the given endpoint. 528 // The endpoint mutex must not be held. 529 // 530 // Returns whether the headerfile changed and/or an error. 531 func (e *Endpoint) runPreCompilationSteps(regenContext *regenerationContext) (headerfileChanged bool, preCompilationError error) { 532 stats := ®enContext.Stats 533 datapathRegenCtxt := regenContext.datapathRegenerationContext 534 535 stats.waitingForLock.Start() 536 err := e.LockAlive() 537 stats.waitingForLock.End(err == nil) 538 if err != nil { 539 return false, err 540 } 541 542 defer e.Unlock() 543 544 currentDir := datapathRegenCtxt.currentDir 545 nextDir := datapathRegenCtxt.nextDir 546 547 // In the first ever regeneration of the endpoint, the conntrack table 548 // is cleaned from the new endpoint IPs as it is guaranteed that any 549 // pre-existing connections using that IP are now invalid. 550 if !e.ctCleaned { 551 go func() { 552 if !option.Config.DryMode { 553 ipv4 := option.Config.EnableIPv4 554 ipv6 := option.Config.EnableIPv6 555 created := ctmap.Exists(nil, ipv4, ipv6) 556 if e.ConntrackLocal() { 557 created = ctmap.Exists(e, ipv4, ipv6) 558 } 559 if created { 560 e.scrubIPsInConntrackTable() 561 } 562 } 563 close(datapathRegenCtxt.ctCleaned) 564 }() 565 } else { 566 close(datapathRegenCtxt.ctCleaned) 567 } 568 569 // If dry mode is enabled, no further changes to BPF maps are performed 570 if option.Config.DryMode { 571 572 // Compute policy for this endpoint. 573 if err = e.regeneratePolicy(); err != nil { 574 return false, fmt.Errorf("Unable to regenerate policy: %s", err) 575 } 576 577 _ = e.updateAndOverrideEndpointOptions(nil) 578 579 // Dry mode needs Network Policy Updates, but the proxy wait group must 580 // not be initialized, as there is no proxy ACKing the changes. 581 if err, _ = e.updateNetworkPolicy(nil); err != nil { 582 return false, err 583 } 584 585 if err = e.writeHeaderfile(nextDir); err != nil { 586 return false, fmt.Errorf("Unable to write header file: %s", err) 587 } 588 589 log.WithField(logfields.EndpointID, e.ID).Debug("Skipping bpf updates due to dry mode") 590 return false, nil 591 } 592 593 if e.PolicyMap == nil { 594 e.PolicyMap, _, err = policymap.OpenOrCreate(e.PolicyMapPathLocked()) 595 if err != nil { 596 return false, err 597 } 598 // Clean up map contents 599 e.getLogger().Debug("flushing old PolicyMap") 600 err = e.PolicyMap.DeleteAll() 601 if err != nil { 602 return false, err 603 } 604 605 // Also reset the in-memory state of the realized state as the 606 // BPF map content is guaranteed to be empty right now. 607 e.realizedPolicy.PolicyMapState = make(policy.MapState) 608 } 609 610 if e.bpfConfigMap == nil { 611 e.bpfConfigMap, _, err = bpfconfig.OpenMapWithName(e.BPFConfigMapPath()) 612 if err != nil { 613 return false, err 614 } 615 // Also reset the in-memory state of the realized state as the 616 // BPF map content is guaranteed to be empty right now. 617 e.realizedBPFConfig = &bpfconfig.EndpointConfig{} 618 } 619 620 // Only generate & populate policy map if a security identity is set up for 621 // this endpoint. 622 if e.SecurityIdentity != nil { 623 stats.policyCalculation.Start() 624 err = e.regeneratePolicy() 625 stats.policyCalculation.End(err == nil) 626 if err != nil { 627 return false, fmt.Errorf("unable to regenerate policy for '%s': %s", e.StringID(), err) 628 } 629 630 _ = e.updateAndOverrideEndpointOptions(nil) 631 632 // Configure the new network policy with the proxies. 633 // Do this before updating the bpf policy maps, so that the proxy listeners have a chance to be 634 // ready when new traffic is redirected to them. 635 stats.proxyPolicyCalculation.Start() 636 err, networkPolicyRevertFunc := e.updateNetworkPolicy(datapathRegenCtxt.proxyWaitGroup) 637 stats.proxyPolicyCalculation.End(err == nil) 638 if err != nil { 639 return false, err 640 } 641 datapathRegenCtxt.revertStack.Push(networkPolicyRevertFunc) 642 643 // Walk the L4Policy to add new redirects and update the desired policy for existing redirects. 644 // Do this before updating the bpf policy maps, so that the proxies are ready when new traffic 645 // is redirected to them. 646 var desiredRedirects map[string]bool 647 var finalizeFunc revert.FinalizeFunc 648 var revertFunc revert.RevertFunc 649 if e.desiredPolicy != nil && e.desiredPolicy.L4Policy != nil && e.desiredPolicy.L4Policy.HasRedirect() { 650 stats.proxyConfiguration.Start() 651 desiredRedirects, err, finalizeFunc, revertFunc = e.addNewRedirects(e.desiredPolicy.L4Policy, datapathRegenCtxt.proxyWaitGroup) 652 stats.proxyConfiguration.End(err == nil) 653 if err != nil { 654 return false, err 655 } 656 datapathRegenCtxt.finalizeList.Append(finalizeFunc) 657 datapathRegenCtxt.revertStack.Push(revertFunc) 658 } 659 660 // realizedBPFConfig may be updated at any point after we figure out 661 // whether ingress/egress policy is enabled. 662 e.desiredBPFConfig = bpfconfig.GetConfig(e) 663 664 // Synchronously try to update PolicyMap for this endpoint. If any 665 // part of updating the PolicyMap fails, bail out and do not generate 666 // BPF. Unfortunately, this means that the map will be in an inconsistent 667 // state with the current program (if it exists) for this endpoint. 668 // GH-3897 would fix this by creating a new map to do an atomic swap 669 // with the old one. 670 stats.mapSync.Start() 671 err = e.syncPolicyMap() 672 stats.mapSync.End(err == nil) 673 if err != nil { 674 return false, fmt.Errorf("unable to regenerate policy because PolicyMap synchronization failed: %s", err) 675 } 676 677 // Synchronously update the BPF ConfigMap for this endpoint. 678 // This is unlikely to fail, but will have the same 679 // inconsistency issues as above if there is a failure. Long 680 // term the solution to this is to templatize this map in the 681 // ELF file, but there's no solution to this just yet. 682 if err = e.bpfConfigMap.Update(e.desiredBPFConfig); err != nil { 683 e.getLogger().WithError(err).Error("unable to update BPF config map") 684 return false, err 685 } 686 687 datapathRegenCtxt.revertStack.Push(func() error { 688 return e.bpfConfigMap.Update(e.realizedBPFConfig) 689 }) 690 691 // At this point, traffic is no longer redirected to the proxy for 692 // now-obsolete redirects, since we synced the updated policy map above. 693 // It's now safe to remove the redirects from the proxy's configuration. 694 stats.proxyConfiguration.Start() 695 finalizeFunc, revertFunc = e.removeOldRedirects(desiredRedirects, datapathRegenCtxt.proxyWaitGroup) 696 datapathRegenCtxt.finalizeList.Append(finalizeFunc) 697 datapathRegenCtxt.revertStack.Push(revertFunc) 698 stats.proxyConfiguration.End(true) 699 } 700 701 stats.prepareBuild.Start() 702 defer func() { 703 stats.prepareBuild.End(preCompilationError == nil) 704 }() 705 706 // Avoid BPF program compilation and installation if the headerfile for the endpoint 707 // or the node have not changed. 708 datapathRegenCtxt.bpfHeaderfilesHash, err = loader.EndpointHash(e) 709 if err != nil { 710 e.getLogger().WithError(err).Warn("Unable to hash header file") 711 datapathRegenCtxt.bpfHeaderfilesHash = "" 712 headerfileChanged = true 713 } else { 714 headerfileChanged = (datapathRegenCtxt.bpfHeaderfilesHash != e.bpfHeaderfileHash) 715 e.getLogger().WithField(logfields.BPFHeaderfileHash, datapathRegenCtxt.bpfHeaderfilesHash). 716 Debugf("BPF header file hashed (was: %q)", e.bpfHeaderfileHash) 717 } 718 if headerfileChanged { 719 datapathRegenCtxt.regenerationLevel = regeneration.RegenerateWithDatapathRewrite 720 if err = e.writeHeaderfile(nextDir); err != nil { 721 return false, fmt.Errorf("unable to write header file: %s", err) 722 } 723 } 724 725 // Cache endpoint information so that we can release the endpoint lock. 726 if datapathRegenCtxt.regenerationLevel >= regeneration.RegenerateWithDatapathRewrite { 727 datapathRegenCtxt.epInfoCache = e.createEpInfoCache(nextDir) 728 } else { 729 datapathRegenCtxt.epInfoCache = e.createEpInfoCache(currentDir) 730 } 731 if datapathRegenCtxt.epInfoCache == nil { 732 return headerfileChanged, fmt.Errorf("Unable to cache endpoint information") 733 } 734 735 return headerfileChanged, nil 736 } 737 738 func (e *Endpoint) finalizeProxyState(regenContext *regenerationContext, err error) { 739 datapathRegenCtx := regenContext.datapathRegenerationContext 740 if err == nil { 741 // Always execute the finalization code, even if the endpoint is 742 // terminating, in order to properly release resources. 743 e.UnconditionalLock() 744 e.getLogger().Debug("Finalizing successful endpoint regeneration") 745 datapathRegenCtx.finalizeList.Finalize() 746 e.Unlock() 747 } else { 748 if err := e.LockAlive(); err != nil { 749 e.getLogger().WithError(err).Debug("Skipping unnecessary reverting of endpoint regeneration changes") 750 return 751 } 752 e.getLogger().Debug("Reverting endpoint changes after BPF regeneration failed") 753 if err := datapathRegenCtx.revertStack.Revert(); err != nil { 754 e.getLogger().WithError(err).Error("Reverting endpoint regeneration changes failed") 755 } 756 e.getLogger().Debug("Finished reverting endpoint changes after BPF regeneration failed") 757 e.Unlock() 758 } 759 } 760 761 // DeleteMapsLocked releases references to all BPF maps associated with this 762 // endpoint. 763 // 764 // For each error that occurs while releasing these references, an error is 765 // added to the resulting error slice which is returned. 766 // 767 // Returns nil on success. 768 func (e *Endpoint) DeleteMapsLocked() []error { 769 var errors []error 770 771 maps := map[string]string{ 772 "config": e.BPFConfigMapPath(), 773 "policy": e.PolicyMapPathLocked(), 774 "calls": e.CallsMapPathLocked(), 775 "egress": e.BPFIpvlanMapPath(), 776 } 777 for name, path := range maps { 778 if err := os.RemoveAll(path); err != nil { 779 errors = append(errors, fmt.Errorf("unable to remove %s map file %s: %s", name, path, err)) 780 } 781 } 782 783 if e.ConntrackLocalLocked() { 784 // Remove local connection tracking maps 785 for _, m := range ctmap.LocalMaps(e, option.Config.EnableIPv4, option.Config.EnableIPv6) { 786 ctPath, err := m.Path() 787 if err == nil { 788 err = os.RemoveAll(ctPath) 789 } 790 if err != nil { 791 errors = append(errors, fmt.Errorf("unable to remove CT map %s: %s", ctPath, err)) 792 } 793 } 794 } 795 796 // Remove handle_policy() tail call entry for EP 797 if err := policymap.RemoveGlobalMapping(uint32(e.ID)); err != nil { 798 errors = append(errors, fmt.Errorf("unable to remove endpoint from global policy map: %s", err)) 799 } 800 801 return errors 802 } 803 804 // DeleteBPFProgramLocked delete the BPF program associated with the endpoint's 805 // veth interface. 806 func (e *Endpoint) DeleteBPFProgramLocked() error { 807 e.getLogger().Debug("deleting bpf program from endpoint") 808 return loader.DeleteDatapath(context.TODO(), e.IfName, "ingress") 809 } 810 811 // garbageCollectConntrack will run the ctmap.GC() on either the endpoint's 812 // local conntrack table or the global conntrack table. 813 // 814 // The endpoint lock must be held 815 func (e *Endpoint) garbageCollectConntrack(filter *ctmap.GCFilter) { 816 var maps []*ctmap.Map 817 818 if e.ConntrackLocalLocked() { 819 maps = ctmap.LocalMaps(e, option.Config.EnableIPv4, option.Config.EnableIPv6) 820 } else { 821 maps = ctmap.GlobalMaps(option.Config.EnableIPv4, option.Config.EnableIPv6) 822 } 823 for _, m := range maps { 824 if err := m.Open(); err != nil { 825 // If the CT table doesn't exist, there's nothing to GC. 826 scopedLog := log.WithError(err).WithField(logfields.EndpointID, e.ID) 827 if os.IsNotExist(err) { 828 scopedLog.WithError(err).Debug("Skipping GC for endpoint") 829 } else { 830 scopedLog.WithError(err).Warn("Unable to open map") 831 } 832 continue 833 } 834 defer m.Close() 835 836 ctmap.GC(m, filter) 837 } 838 } 839 840 func (e *Endpoint) scrubIPsInConntrackTableLocked() { 841 e.garbageCollectConntrack(&ctmap.GCFilter{ 842 MatchIPs: map[string]struct{}{ 843 e.IPv4.String(): {}, 844 e.IPv6.String(): {}, 845 }, 846 }) 847 } 848 849 func (e *Endpoint) scrubIPsInConntrackTable() { 850 e.UnconditionalLock() 851 e.scrubIPsInConntrackTableLocked() 852 e.Unlock() 853 } 854 855 // SkipStateClean can be called on a endpoint before its first build to skip 856 // the cleaning of state such as the conntrack table. This is useful when an 857 // endpoint is being restored from state and the datapath state should not be 858 // claned. 859 // 860 // The endpoint lock must NOT be held. 861 func (e *Endpoint) SkipStateClean() { 862 // Mark conntrack as already cleaned 863 e.UnconditionalLock() 864 e.ctCleaned = true 865 e.Unlock() 866 } 867 868 // GetBPFKeys returns all keys which should represent this endpoint in the BPF 869 // endpoints map 870 func (e *Endpoint) GetBPFKeys() []*lxcmap.EndpointKey { 871 keys := []*lxcmap.EndpointKey{} 872 if e.IPv6.IsSet() { 873 keys = append(keys, lxcmap.NewEndpointKey(e.IPv6.IP())) 874 } 875 876 if e.IPv4.IsSet() { 877 keys = append(keys, lxcmap.NewEndpointKey(e.IPv4.IP())) 878 } 879 880 return keys 881 } 882 883 // GetBPFValue returns the value which should represent this endpoint in the 884 // BPF endpoints map 885 func (e *Endpoint) GetBPFValue() (*lxcmap.EndpointInfo, error) { 886 mac, err := e.LXCMAC.Uint64() 887 if err != nil { 888 return nil, fmt.Errorf("invalid LXC MAC: %v", err) 889 } 890 891 nodeMAC, err := e.NodeMAC.Uint64() 892 if err != nil { 893 return nil, fmt.Errorf("invalid node MAC: %v", err) 894 } 895 896 info := &lxcmap.EndpointInfo{ 897 IfIndex: uint32(e.IfIndex), 898 // Store security identity in network byte order so it can be 899 // written into the packet without an additional byte order 900 // conversion. 901 LxcID: e.ID, 902 MAC: lxcmap.MAC(mac), 903 NodeMAC: lxcmap.MAC(nodeMAC), 904 } 905 906 return info, nil 907 } 908 909 // The bool pointed by hadProxy, if not nil, will be set to 'true' if 910 // the deleted entry had a proxy port assigned to it. *hadProxy is 911 // not otherwise changed (e.g., it is never set to 'false'). 912 func (e *Endpoint) deletePolicyKey(keyToDelete policy.Key, incremental bool, hadProxy *bool) bool { 913 // Convert from policy.Key to policymap.Key 914 policymapKey := policymap.PolicyKey{ 915 Identity: keyToDelete.Identity, 916 DestPort: keyToDelete.DestPort, 917 Nexthdr: keyToDelete.Nexthdr, 918 TrafficDirection: keyToDelete.TrafficDirection, 919 } 920 921 // Do not error out if the map entry was already deleted from the bpf map. 922 // Incremental updates depend on this being OK in cases where identity change 923 // events overlap with full policy computation. 924 // In other cases we only delete entries that exist, but even in that case it 925 // is better to not error out if somebody else has deleted the map entry in the 926 // meanwhile. 927 err, errno := e.PolicyMap.DeleteKeyWithErrno(policymapKey) 928 if err != nil && errno != syscall.ENOENT { 929 e.getLogger().WithError(err).WithField(logfields.BPFMapKey, policymapKey).Error("Failed to delete PolicyMap key") 930 return false 931 } 932 933 if hadProxy != nil { 934 if entry, ok := e.realizedPolicy.PolicyMapState[keyToDelete]; ok && entry.ProxyPort != 0 { 935 *hadProxy = true 936 } 937 } 938 939 // Operation was successful, remove from realized state. 940 delete(e.realizedPolicy.PolicyMapState, keyToDelete) 941 942 // Incremental updates need to update the desired state as well. 943 if incremental && e.desiredPolicy != e.realizedPolicy { 944 delete(e.desiredPolicy.PolicyMapState, keyToDelete) 945 } 946 947 return true 948 } 949 950 func (e *Endpoint) addPolicyKey(keyToAdd policy.Key, entry policy.MapStateEntry, incremental bool) bool { 951 // Convert from policy.Key to policymap.Key 952 policymapKey := policymap.PolicyKey{ 953 Identity: keyToAdd.Identity, 954 DestPort: keyToAdd.DestPort, 955 Nexthdr: keyToAdd.Nexthdr, 956 TrafficDirection: keyToAdd.TrafficDirection, 957 } 958 959 err := e.PolicyMap.AllowKey(policymapKey, entry.ProxyPort) 960 if err != nil { 961 e.getLogger().WithError(err).WithFields(logrus.Fields{ 962 logfields.BPFMapKey: policymapKey, 963 logfields.Port: entry.ProxyPort, 964 }).Error("Failed to add PolicyMap key") 965 return false 966 } 967 968 // Operation was successful, add to realized state. 969 e.realizedPolicy.PolicyMapState[keyToAdd] = entry 970 971 // Incremental updates need to update the desired state as well. 972 if incremental && e.desiredPolicy != e.realizedPolicy { 973 e.desiredPolicy.PolicyMapState[keyToAdd] = entry 974 } 975 976 return true 977 } 978 979 // ApplyPolicyMapChanges updates the Endpoint's PolicyMap with the changes 980 // that have accumulated for the PolicyMap via various outside events (e.g., 981 // identities added / deleted). 982 // 'proxyWaitGroup' may not be nil. 983 func (e *Endpoint) ApplyPolicyMapChanges(proxyWaitGroup *completion.WaitGroup) error { 984 if err := e.LockAlive(); err != nil { 985 return err 986 } 987 defer e.Unlock() 988 989 proxyChanges, err := e.applyPolicyMapChanges() 990 if err != nil { 991 return err 992 } 993 994 if proxyChanges { 995 // Ignoring the revertFunc; keep all successful changes even if some fail. 996 err, _ = e.updateNetworkPolicy(proxyWaitGroup) 997 } else { 998 // Allow caller to wait for the current network policy to be acked 999 e.useCurrentNetworkPolicy(proxyWaitGroup) 1000 } 1001 1002 return err 1003 } 1004 1005 // applyPolicyMapChanges applies any incremental policy map changes 1006 // collected on the desired policy. 1007 func (e *Endpoint) applyPolicyMapChanges() (proxyChanges bool, err error) { 1008 errors := 0 1009 1010 // Note that after successful endpoint regeneration the 1011 // desired and realized policies are the same pointer. During 1012 // the bpf regeneration possible incremental updates are 1013 // collected on the newly computed desired policy, which is 1014 // not fully realized yet. This is why we get the map changes 1015 // from the desired policy here. 1016 adds, deletes := e.desiredPolicy.PolicyMapChanges.ConsumeMapChanges() 1017 1018 // Add policy map entries before deleting to avoid transient drops 1019 for keyToAdd, entry := range adds { 1020 // Keep the existing proxy port, if any 1021 entry.ProxyPort = e.realizedRedirects[policy.ProxyIDFromKey(e.ID, keyToAdd)] 1022 if entry.ProxyPort != 0 { 1023 proxyChanges = true 1024 } 1025 if !e.addPolicyKey(keyToAdd, entry, true) { 1026 errors++ 1027 } 1028 } 1029 1030 for keyToDelete := range deletes { 1031 if !e.deletePolicyKey(keyToDelete, true, &proxyChanges) { 1032 errors++ 1033 } 1034 } 1035 1036 if errors > 0 { 1037 return proxyChanges, fmt.Errorf("updating desired PolicyMap state failed") 1038 } else if len(adds)+len(deletes) > 0 { 1039 e.getLogger().WithFields(logrus.Fields{ 1040 logfields.AddedPolicyID: adds, 1041 logfields.DeletedPolicyID: deletes, 1042 }).Debug("Applied policy map updates due identity changes") 1043 } 1044 1045 return proxyChanges, nil 1046 } 1047 1048 // syncPolicyMap updates the bpf policy map state based on the 1049 // difference between the realized and desired policy state without 1050 // dumping the bpf policy map. 1051 func (e *Endpoint) syncPolicyMap() error { 1052 // Nothing to do if the desired policy is already fully realized. 1053 if e.realizedPolicy != e.desiredPolicy { 1054 errors := 0 1055 1056 // Add policy map entries before deleting to avoid transient drops 1057 err := e.addPolicyMapDelta() 1058 if err != nil { 1059 errors++ 1060 } 1061 1062 // Delete policy keys present in the realized state, but not present in the desired state 1063 for keyToDelete := range e.realizedPolicy.PolicyMapState { 1064 // If key that is in realized state is not in desired state, just remove it. 1065 if _, ok := e.desiredPolicy.PolicyMapState[keyToDelete]; !ok { 1066 if !e.deletePolicyKey(keyToDelete, false, nil) { 1067 errors++ 1068 } 1069 } 1070 } 1071 1072 if errors > 0 { 1073 return fmt.Errorf("syncPolicyMapDelta failed") 1074 } 1075 } 1076 1077 // Still may have changes due to identities added and/or 1078 // deleted after the desired policy was computed. 1079 _, err := e.applyPolicyMapChanges() 1080 return err 1081 } 1082 1083 // addPolicyMapDelta adds new or updates existing bpf policy map state based 1084 // on the difference between the realized and desired policy state without 1085 // dumping the bpf policy map. 1086 func (e *Endpoint) addPolicyMapDelta() error { 1087 // Nothing to do if the desired policy is already fully realized. 1088 if e.realizedPolicy == e.desiredPolicy { 1089 return nil 1090 } 1091 1092 errors := 0 1093 1094 for keyToAdd, entry := range e.desiredPolicy.PolicyMapState { 1095 if oldEntry, ok := e.realizedPolicy.PolicyMapState[keyToAdd]; !ok || oldEntry != entry { 1096 if !e.addPolicyKey(keyToAdd, entry, false) { 1097 errors++ 1098 } 1099 } 1100 } 1101 1102 if errors > 0 { 1103 return fmt.Errorf("updating desired PolicyMap state failed") 1104 } 1105 1106 return nil 1107 } 1108 1109 // syncPolicyMapWithDump attempts to synchronize the PolicyMap for this endpoint to 1110 // contain the set of PolicyKeys represented by the endpoint's desiredMapState. 1111 // It checks the current contents of the endpoint's PolicyMap and deletes any 1112 // PolicyKeys that are not present in the endpoint's desiredMapState. It then 1113 // adds any keys that are not present in the map. When a key from desiredMapState 1114 // is inserted successfully to the endpoint's BPF PolicyMap, it is added to the 1115 // endpoint's realizedMapState field. Returns an error if the endpoint's BPF 1116 // PolicyMap is unable to be dumped, or any update operation to the map fails. 1117 // Must be called with e.Mutex locked. 1118 func (e *Endpoint) syncPolicyMapWithDump() error { 1119 1120 if e.realizedPolicy.PolicyMapState == nil { 1121 e.realizedPolicy.PolicyMapState = make(policy.MapState) 1122 } 1123 1124 if e.desiredPolicy.PolicyMapState == nil { 1125 e.desiredPolicy.PolicyMapState = make(policy.MapState) 1126 } 1127 1128 if e.PolicyMap == nil { 1129 return fmt.Errorf("not syncing PolicyMap state for endpoint because PolicyMap is nil") 1130 } 1131 1132 currentMapContents, err := e.PolicyMap.DumpToSlice() 1133 1134 // If map is unable to be dumped, attempt to close map and open it again. 1135 // See GH-4229. 1136 if err != nil { 1137 e.getLogger().WithError(err).Error("unable to dump PolicyMap when trying to sync desired and realized PolicyMap state") 1138 1139 // Close to avoid leaking of file descriptors, but still continue in case 1140 // Close() does not succeed, because otherwise the map will never be 1141 // opened again unless the agent is restarted. 1142 err := e.PolicyMap.Close() 1143 if err != nil { 1144 e.getLogger().WithError(err).Error("unable to close PolicyMap which was not able to be dumped") 1145 } 1146 1147 e.PolicyMap, _, err = policymap.OpenOrCreate(e.PolicyMapPathLocked()) 1148 if err != nil { 1149 return fmt.Errorf("unable to open PolicyMap for endpoint: %s", err) 1150 } 1151 1152 // Try to dump again, fail if error occurs. 1153 currentMapContents, err = e.PolicyMap.DumpToSlice() 1154 if err != nil { 1155 return err 1156 } 1157 } 1158 1159 errors := 0 1160 1161 for _, entry := range currentMapContents { 1162 // Convert key to host-byte order for lookup in the desiredMapState. 1163 keyHostOrder := entry.Key.ToHost() 1164 1165 // Convert from policymap.Key to policy.Key 1166 keyToDelete := policy.Key{ 1167 Identity: keyHostOrder.Identity, 1168 DestPort: keyHostOrder.DestPort, 1169 Nexthdr: keyHostOrder.Nexthdr, 1170 TrafficDirection: keyHostOrder.TrafficDirection, 1171 } 1172 1173 // If key that is in policy map is not in desired state, just remove it. 1174 if _, ok := e.desiredPolicy.PolicyMapState[keyToDelete]; !ok { 1175 e.getLogger().WithField(logfields.BPFMapKey, entry.Key.String()).Debug("syncPolicyMapWithDump removing a bpf policy entry not in the desired state") 1176 if !e.deletePolicyKey(keyToDelete, false, nil) { 1177 errors++ 1178 } 1179 } 1180 } 1181 1182 err = e.addPolicyMapDelta() 1183 1184 if errors > 0 { 1185 return fmt.Errorf("synchronizing desired PolicyMap state failed") 1186 } 1187 1188 return err 1189 } 1190 1191 func (e *Endpoint) syncPolicyMapController() { 1192 ctrlName := fmt.Sprintf("sync-policymap-%d", e.ID) 1193 e.controllers.UpdateController(ctrlName, 1194 controller.ControllerParams{ 1195 DoFunc: func(ctx context.Context) (reterr error) { 1196 // Failure to lock is not an error, it means 1197 // that the endpoint was disconnected and we 1198 // should exit gracefully. 1199 if err := e.LockAlive(); err != nil { 1200 return controller.NewExitReason("Endpoint disappeared") 1201 } 1202 defer e.Unlock() 1203 return e.syncPolicyMapWithDump() 1204 }, 1205 RunInterval: 1 * time.Minute, 1206 }, 1207 ) 1208 } 1209 1210 // RequireARPPassthrough returns true if the datapath must implement ARP 1211 // passthrough for this endpoint 1212 func (e *Endpoint) RequireARPPassthrough() bool { 1213 return e.DatapathConfiguration.RequireArpPassthrough 1214 } 1215 1216 // RequireEgressProg returns true if the endpoint requires bpf_lxc with esction 1217 // "to-container" to be attached at egress on the host facing veth pair 1218 func (e *Endpoint) RequireEgressProg() bool { 1219 return e.DatapathConfiguration.RequireEgressProg 1220 } 1221 1222 // RequireRouting returns true if the endpoint requires BPF routing to be 1223 // enabled, when disabled, routing is delegated to Linux routing 1224 func (e *Endpoint) RequireRouting() (required bool) { 1225 required = true 1226 if e.DatapathConfiguration.RequireRouting != nil { 1227 required = *e.DatapathConfiguration.RequireRouting 1228 } 1229 return 1230 } 1231 1232 // RequireEndpointRoute returns if the endpoint wants a per endpoint route 1233 func (e *Endpoint) RequireEndpointRoute() bool { 1234 return e.DatapathConfiguration.InstallEndpointRoute 1235 }