Update l2 pod/lb docs#2844
Conversation
✅ Deploy Preview for calico-docs-preview-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview succeeded!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Updates the Local Subnet L2 Reachability documentation for Calico OSS and Calico Enterprise to better describe operational constraints around LoadBalancer VIP ownership and to document additional tuning options for ARP/NDP behavior.
Changes:
- Added new “Limitations” bullets covering
externalTrafficPolicyconstraints and kube-proxy IPVSstrictARPrequirements for LoadBalancer VIPs. - Updated guidance on enabling the feature to state Felix restarts automatically on change.
- Added an optional tuning section for periodic re-announcement via
localSubnetL2ReachabilityRefreshInterval.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| calico/networking/configuring/local-subnet-l2-reachability.mdx | Adds LB/IPVS limitations, updates enablement/restart guidance, and introduces refresh-interval tuning. |
| calico-enterprise/networking/configuring/local-subnet-l2-reachability.mdx | Mirrors the same LB/IPVS limitations, restart guidance, and refresh-interval tuning for Enterprise docs. |
| - **Cloud provider ARP filtering.** Some cloud networks (for example, AWS VPC) filter ARP at the hypervisor. The responder will not work unless the VIP/pod IPs are assigned to the node's cloud interface. This is an infrastructure constraint outside $[prodname]'s control. | ||
| - **Heterogeneous subnets.** Node selection for LoadBalancer VIPs hashes over all nodes without checking whether a node has an interface on the VIP's subnet. This is only a concern when nodes do not share the same subnets. | ||
| - **Only `externalTrafficPolicy: Cluster` is supported for LoadBalancer services.** $[prodname] elects the node that answers for a VIP with a consistent hash over all nodes, independent of where the service's backends run. Under `externalTrafficPolicy: Local` the elected node may have no local backend, so traffic that reaches it is dropped. Leave `externalTrafficPolicy` at its default of `Cluster`. | ||
| - **kube-proxy in IPVS mode requires `strictARP`.** In IPVS mode, kube-proxy binds every LoadBalancer VIP to the `kube-ipvs0` dummy interface on *all* nodes. Because the Linux kernel answers ARP for any IP configured on the host by default, every node replies for the VIP — not just the one $[prodname] elected — so external hosts receive conflicting replies from several MAC addresses. Either run kube-proxy in `iptables` or `nftables` mode, or keep IPVS mode and set `strictARP: true` (the `ipvs.strictARP` field of the kube-proxy `ConfigMap`) so the kernel stops answering ARP for the dummy-interface VIPs and leaves it to $[prodname]'s responder. |
| **Optional: tune periodic re-announcement.** While the feature is enabled, Felix periodically re-announces every IP it proxies with a gratuitous ARP / unsolicited Neighbor Advertisement, keeping upstream neighbor caches and switch forwarding tables warm even when the set of proxied IPs has not changed. `localSubnetL2ReachabilityRefreshInterval` controls how often this happens (default `120s`). Lower it if your upstream devices age out ARP/NDP entries quickly, or set it to `0` to disable periodic re-announcement and keep only the one-shot announcement sent when an IP is first proxied: | ||
|
|
||
| ```bash | ||
| kubectl patch felixconfiguration default --type='merge' \ | ||
| -p '{"spec": {"localSubnetL2ReachabilityRefreshInterval": "30s"}}' |
| - **Cloud provider ARP filtering.** Some cloud networks (for example, AWS VPC) filter ARP at the hypervisor. The responder will not work unless the VIP/pod IPs are assigned to the node's cloud interface. This is an infrastructure constraint outside $[prodname]'s control. | ||
| - **Heterogeneous subnets.** Node selection for LoadBalancer VIPs hashes over all nodes without checking whether a node has an interface on the VIP's subnet. This is only a concern when nodes do not share the same subnets. | ||
| - **Only `externalTrafficPolicy: Cluster` is supported for LoadBalancer services.** $[prodname] elects the node that answers for a VIP with a consistent hash over all nodes, independent of where the service's backends run. Under `externalTrafficPolicy: Local` the elected node may have no local backend, so traffic that reaches it is dropped. Leave `externalTrafficPolicy` at its default of `Cluster`. | ||
| - **kube-proxy in IPVS mode requires `strictARP`.** In IPVS mode, kube-proxy binds every LoadBalancer VIP to the `kube-ipvs0` dummy interface on *all* nodes. Because the Linux kernel answers ARP for any IP configured on the host by default, every node replies for the VIP — not just the one $[prodname] elected — so external hosts receive conflicting replies from several MAC addresses. Either run kube-proxy in `iptables` or `nftables` mode, or keep IPVS mode and set `strictARP: true` (the `ipvs.strictARP` field of the kube-proxy `ConfigMap`) so the kernel stops answering ARP for the dummy-interface VIPs and leaves it to $[prodname]'s responder. |
| **Optional: tune periodic re-announcement.** While the feature is enabled, Felix periodically re-announces every IP it proxies with a gratuitous ARP / unsolicited Neighbor Advertisement, keeping upstream neighbor caches and switch forwarding tables warm even when the set of proxied IPs has not changed. `localSubnetL2ReachabilityRefreshInterval` controls how often this happens (default `120s`). Lower it if your upstream devices age out ARP/NDP entries quickly, or set it to `0` to disable periodic re-announcement and keep only the one-shot announcement sent when an IP is first proxied: | ||
|
|
||
| ```bash | ||
| kubectl patch felixconfiguration default --type='merge' \ | ||
| -p '{"spec": {"localSubnetL2ReachabilityRefreshInterval": "30s"}}' |
ctauchen
left a comment
There was a problem hiding this comment.
I agree with copilot -- will you take a look? Also failing the lint check (dataplane--> data plane). And there's a build failure.
But apart from all that, looks good to me!

Product Version(s):
Issue:
Link to docs preview:
SME review:
DOCS review:
Additional information:
Merge checklist: