Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns10/dfw/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "10",
"vpcattachment": "10",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns10/iad/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "10",
"vpcattachment": "10",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns10/sjc/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "10",
"vpcattachment": "10",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns20/dfw/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ spec:
"type": "galactic-bgp",
"vpc": "20",
"vpcattachment": "20",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns20/iad/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ spec:
"type": "galactic-bgp",
"vpc": "20",
"vpcattachment": "20",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns20/sjc/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ spec:
"type": "galactic-bgp",
"vpc": "20",
"vpcattachment": "20",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns30/dfw/nad-b.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "30",
"vpcattachment": "31",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns30/dfw/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "30",
"vpcattachment": "30",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns40/iad/nad-b.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "40",
"vpcattachment": "41",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns40/iad/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "40",
"vpcattachment": "40",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns60/dfw/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "60",
"vpcattachment": "60",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns60/iad/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "60",
"vpcattachment": "60",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns60/sjc/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ spec:
"type": "galactic-bgp",
"vpc": "60",
"vpcattachment": "60",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns70/iad/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ spec:
"type": "galactic-bgp",
"vpc": "70",
"vpcattachment": "70",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns70/sjc/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ spec:
"type": "galactic-bgp",
"vpc": "70",
"vpcattachment": "70",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns71/sjc/nad-b.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ spec:
"type": "galactic-bgp",
"vpc": "72",
"vpcattachment": "72",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
3 changes: 2 additions & 1 deletion deploy/containerlab/resources/tenants/ns71/sjc/nad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ spec:
"type": "galactic-bgp",
"vpc": "71",
"vpcattachment": "71",
"namespace": "galactic-system"
"namespace": "galactic-system",
"egress": { "internet": { "mode": "Enabled" } }
}
]
}
22 changes: 19 additions & 3 deletions docs/cni/conflist-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,28 @@ on the same attachment, so cleanup is left to `galactic-router`'s GC controller.

## BGP Publish Fields (`galactic-bgp`)

`galactic-bgp`'s own stanza carries only `vpc`, `vpcattachment`, and
`namespace` — nothing else. It learns which interface kind was created and
what addresses were allocated entirely from `prevResult` (the accumulated
`galactic-bgp`'s own stanza carries `vpc`, `vpcattachment`, `namespace`, and
an optional `egress` declaration. It learns which interface kind was created
and what addresses were allocated entirely from `prevResult` (the accumulated
result of every preceding plugin in the chain), never from its own config or
a kernel call.

### Egress declaration

```json
"egress": { "internet": { "mode": "Enabled" } }
```

The declaration says whether this network reaches the internet. It names no
shard: the node routes toward the shard its own configuration names (see
[`GALACTIC_CNI_EGRESS_SHARD_SIDS`](environment-variables.md#galactic_cni_egress_shard_sids)),
so the declaration decides only whether this VRF gets that route.

`Enabled` installs the VRF's egress routes, and fails the ADD if the node
names no shard or none of its shards resolves. Anything else, including an
absent `egress` key, installs no route and withdraws one that is there, so a
network that declared no egress gets none even on a node that has shards.

### EndpointSlice publish (HTTP ingress backend discovery)

Alongside the `BGPVRFInstance`/`BGPAdvertisement` CRDs, `galactic-bgp`
Expand Down
6 changes: 6 additions & 0 deletions docs/cni/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ field; as of this writing it is still env-only.

## `GALACTIC_CNI_EGRESS_SHARD_SIDS`

> **This list no longer decides which networks get egress.** It names the
> shards a node may route toward. Whether a given VRF gets that route is the
> `egress` declaration in the attachment's own `galactic-bgp` stanza (see the
> [conflist reference](conflist-reference.md#egress-declaration)), so a node
> holding this list installs a route only for a network that declared one.

Comma-separated list of every live `galactic-nat` shard's
`Status.ShardSID` — the fabric-wide membership list
`internal/plumbing/srv6.EgressDefaultRouteAdd` needs to install a tenant
Expand Down
6 changes: 6 additions & 0 deletions docs/nat/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ to an earlier, superseded design this sharded egress tier replaced.

## Shard membership (`galactic-cni` side)

The list below is where a node may route toward. Whether a given network gets
that route is the `egress` declaration in the attachment's own `galactic-bgp`
conflist stanza, rendered from what the network declared; a network that
declares none gets no route however the node is configured. See the
[conflist reference](../cni/conflist-reference.md#egress-declaration).

A tenant's compute node needs to know the fabric-wide list of live shard
SIDs to install its own tenant VRFs' egress routes, and the NAT64 prefix to
install a route toward IPv4 reachability. Both are separate from anything
Expand Down
59 changes: 46 additions & 13 deletions internal/cnibgp/bgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type publishConfig struct {
// ifaceType selects the attachment's egress kind, veth or tap. Inferred from
// prevResult, never a config field.
ifaceType string
egress *Egress
}

// publishResult records what publishBGPState created, so cmdAdd can fold it
Expand Down Expand Up @@ -447,7 +448,7 @@ func publishBGPState(
// Not tracked for rollback: the vrf_table entry is shared by every
// attachment on this VPC and node, like the BGPVRFInstance above.
if _, err := registerEBPFDatapath(
bgp, cfg.vpc, cfg.vpcAttachment, cfg.ifaceType, uint16(vrfID), ebpfPinDir, prefixes,
bgp, cfg.vpc, cfg.vpcAttachment, cfg.ifaceType, uint16(vrfID), ebpfPinDir, prefixes, cfg.egress,
); err != nil {
return fmt.Errorf("register eBPF uSID datapath: %w", err)
}
Expand Down Expand Up @@ -522,6 +523,7 @@ func publishBGPState(
// nothing.
func registerEBPFDatapath(
bgp bgpConfig, vpc, vpcAttachment, ifaceType string, argument uint16, pinDir string, prefixes []string,
egress *Egress,
) (registered bool, err error) {
if bgp.srv6Locator == "" || bgp.nodeID == 0 {
return false, nil
Expand Down Expand Up @@ -551,12 +553,12 @@ func registerEBPFDatapath(
return false, fmt.Errorf("look up VRF table id for eBPF registration: %w", err)
}

// Installs or refreshes this VRF's NAT66 default egress route. The
// optional routing plugin in this chain may be absent from a given
// conflist, and this route must exist wherever a shard is configured, so
// it is written here.
if err := installEgressRoutes(vrfTableID, argument); err != nil {
return false, fmt.Errorf("install NAT66 default egress route: %w", err)
// Installs, refreshes, or withdraws this VRF's egress routes. The optional
// routing plugin in this chain may be absent from a given conflist, and
// these routes must exist wherever this network declares egress, so they
// are written here.
if err := installEgressRoutes(vrfTableID, argument, egress); err != nil {
return false, fmt.Errorf("install egress routes: %w", err)
}

if err := registerLocalEgressRoutes(pinDir, vrfTableID, prefixes); err != nil {
Expand Down Expand Up @@ -761,11 +763,20 @@ func registerLocalEgressRoutes(pinDir string, vrfTableID uint32, prefixes []stri
// may offer NAT64 without NAT66, and then no default route exists for this
// traffic to fall into.
//
// No shard configured is not an error: the shard list parses to an empty slice
// and srv6.EgressDefaultRouteAdd no-ops. A shard SID that is invalid, or that
// has no reachable route yet, fails this attachment's ADD rather than leaving
// the VRF with no egress at all.
func installEgressRoutes(vrfTableID uint32, argument uint16) error {
// egress is this network's own declaration, from its own conflist stanza. A
// network that declares no egress gets no route, and loses one it has, which is
// what makes a declaration of no egress mean anything: the node-wide list on
// its own handed a default route out to every network on the node.
//
// A network that declares egress on a node naming no shard fails this
// attachment's ADD. A node without a shard is an operator error, and failing
// the first instance surfaces it where an attach that succeeded without egress
// would hide it. A shard SID that is invalid, or that has no reachable route
// yet, fails the ADD for the same reason.
func installEgressRoutes(vrfTableID uint32, argument uint16, egress *Egress) error {
if !egress.Enabled() {
return withdrawEgressRoutes(vrfTableID)
}
// cniConfig is nil until InitCNIConfig runs, which several unit tests
// calling registerEBPFDatapath directly never do. Treated as "no shard
// configured" rather than a panic.
Expand All @@ -777,7 +788,8 @@ func installEgressRoutes(vrfTableID uint32, argument uint16) error {
return fmt.Errorf("parse %s: %w", config.EnvCNIEgressShardSIDs, err)
}
if len(shardSIDs) == 0 {
return nil
return fmt.Errorf("this network declares internet egress and this node names no egress shard (%s is empty)",
config.EnvCNIEgressShardSIDs)
}
tenantSIDs, err := shardSIDsForTenant(shardSIDs, argument)
if err != nil {
Expand Down Expand Up @@ -897,3 +909,24 @@ func egressKindForInterfaceType(ifaceType string) (uint32, error) {
return 0, fmt.Errorf("unknown interface type %q", ifaceType)
}
}

// withdrawEgressRoutes removes vrfTableID's egress routes, so a network whose
// declaration withdrew egress, or never declared it, carries none however the
// node is configured. Idempotent, and a no-op on a node whose datapath has not
// loaded, so an attachment there never fails its ADD over a route it never had.
func withdrawEgressRoutes(vrfTableID uint32) error {
if err := srv6.EgressDefaultRouteWithdraw(vrfTableID); err != nil {
return fmt.Errorf("withdraw default egress route: %w", err)
}
if cniConfig == nil || cniConfig.NAT64Prefix == "" {
return nil
}
_, prefix, err := net.ParseCIDR(cniConfig.NAT64Prefix)
if err != nil {
return fmt.Errorf("parse %s %q: %w", config.EnvCNINAT64Prefix, cniConfig.NAT64Prefix, err)
}
if err := srv6.EgressPrefixRouteWithdraw(vrfTableID, prefix); err != nil {
return fmt.Errorf("withdraw NAT64 egress route: %w", err)
}
return nil
}
14 changes: 8 additions & 6 deletions internal/cnibgp/bgp_ebpf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func blockFromLocator(locator string) (uint64, error) {
func TestRegisterEBPFDatapath_NotConfiguredIsNoOp(t *testing.T) {
cfg := bgpConfig{srv6Locator: "", nodeID: 0}
registered, err := registerEBPFDatapath(
cfg, testVPC, testAttachment, ifaceTypeVeth, 42, "/sys/fs/bpf/galactic-does-not-exist", nil)
cfg, testVPC, testAttachment, ifaceTypeVeth, 42, "/sys/fs/bpf/galactic-does-not-exist", nil, enabledEgress())
if err != nil {
t.Errorf("registerEBPFDatapath with unconfigured BGPRouter = %v, want nil (no-op)", err)
}
Expand All @@ -69,7 +69,7 @@ func TestRegisterEBPFDatapath_NotConfiguredIsNoOp(t *testing.T) {
func TestRegisterEBPFDatapath_RejectsOutOfRangeNodeID(t *testing.T) {
cfg := bgpConfig{srv6Locator: "2001:db8:1::/48", nodeID: 0x10001} // wraps to uint16(1) if narrowed unchecked
registered, err := registerEBPFDatapath(
cfg, testVPC, testAttachment, ifaceTypeVeth, 42, "/sys/fs/bpf/galactic-does-not-exist", nil)
cfg, testVPC, testAttachment, ifaceTypeVeth, 42, "/sys/fs/bpf/galactic-does-not-exist", nil, enabledEgress())
if err == nil {
t.Fatal("registerEBPFDatapath with nodeID=0x10001 = nil error, want an out-of-range rejection")
}
Expand Down Expand Up @@ -119,7 +119,8 @@ func TestRegisterEBPFDatapath_RegistersAllThreeTables(t *testing.T) {
t.Cleanup(func() { _ = loaderObjs.Close() })

cfg := bgpConfig{srv6Locator: locator, nodeID: nodeID}
registered, err := registerEBPFDatapath(cfg, vpc, testAttachment, ifaceTypeVeth, uint16(vrfID), pinDir, nil)
registered, err := registerEBPFDatapath(
cfg, vpc, testAttachment, ifaceTypeVeth, uint16(vrfID), pinDir, nil, enabledEgress())
if err != nil {
t.Fatalf("registerEBPFDatapath: %v", err)
}
Expand Down Expand Up @@ -268,14 +269,14 @@ func TestRegisterEBPFDatapath_SecondAttachmentSharesEntry(t *testing.T) {

cfg := bgpConfig{srv6Locator: locator, nodeID: nodeID}
if _, err := registerEBPFDatapath(
cfg, vpc, firstAttachment, ifaceTypeVeth, uint16(vrfID), pinDir, []string{firstPrefix},
cfg, vpc, firstAttachment, ifaceTypeVeth, uint16(vrfID), pinDir, []string{firstPrefix}, enabledEgress(),
); err != nil {
t.Fatalf("first attachment's registerEBPFDatapath: %v", err)
}
// A second attachment on the same VPC/node resolves the same Argument
// (allocateArgument's idempotent lookup) and re-registers the same key.
registered, err := registerEBPFDatapath(
cfg, vpc, secondAttachment, ifaceTypeVeth, uint16(vrfID), pinDir, []string{secondPrefix},
cfg, vpc, secondAttachment, ifaceTypeVeth, uint16(vrfID), pinDir, []string{secondPrefix}, enabledEgress(),
)
if err != nil {
t.Fatalf("second attachment's registerEBPFDatapath: %v", err)
Expand Down Expand Up @@ -384,7 +385,8 @@ func TestRegisterEBPFDatapath_MixedInterfaceTypesKeepOwnEgressKind(t *testing.T)

cfg := bgpConfig{srv6Locator: locator, nodeID: nodeID}
for _, a := range tt.order {
if _, err := registerEBPFDatapath(cfg, vpc, a.name, a.ifaceType, uint16(vrfID), pinDir, nil); err != nil {
if _, err := registerEBPFDatapath(
cfg, vpc, a.name, a.ifaceType, uint16(vrfID), pinDir, nil, enabledEgress()); err != nil {
t.Fatalf("registerEBPFDatapath(%s): %v", a.name, err)
}
}
Expand Down
Loading
Loading