Skip to content

Commit 8c22ce5

Browse files
committed
Add support for external subnets
1 parent 2bbb2b5 commit 8c22ce5

File tree

38 files changed

+11608
-183
lines changed

38 files changed

+11608
-183
lines changed

.github/buildomat/common.sh

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
#!/bin/bash
22

3+
# The tofino2 has 20 stages, and the current sidecar.p4 needs all 20 of them.
4+
# Specifying the number of stages isn't strictly necessary, but it allows us to
5+
# track when we exceed the current ceiling. The underlying intention is to grow
6+
# deliberately and thoughtfully, given the limited space on the ASIC.
7+
#
8+
# Note: this now seems silly since we have maxed out the number of stages, but
9+
# we want to leave this check and note in place should we ever find a way to
10+
# reduce our footprint below 20 stages.
11+
TOFINO_STAGES=20
12+
313
# These describe which version of the SDE to download and where to find it
4-
SDE_COMMIT=eb08b0b0e55792bbba206202603da0fd791ea555
5-
SDE_PKG_SHA256=fbaea9ccd7ece70cdc20207e2f9b4750243468c6fe95c6a1fae9734201307db7
6-
SDE_DEB_SHA256=14050fd9a08b8b0b2ec7c827fa06a51516369a81bcf616d16699302b58d8ea9a
14+
SDE_COMMIT=e61fe02c3c1c384b2e212c90177fcea76a31fd4e
15+
SDE_PKG_SHA256=8a87a9b0bed3c5440a173a7a41361bdeb5e7a848882da6b4aa48c8fb0043f3bd
16+
SDE_DEB_SHA256=a292e2dd5311647c4852bb41f2532dd1fdf30325b6d04cccb7e85b873e521d5f
717

818
[ `uname -s` == "SunOS" ] && SERIES=illumos
919
[ `uname -s` == "SunOS" ] || SERIES=linux

.github/buildomat/jobs/image.sh

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,7 @@ pfexec chown "$UID" /out
101101

102102
banner "P4 Codegen"
103103
# Add gcc-12 so the p4 compiler can find cpp
104-
# The tofino2 has 20 stages, but the current sidecar.p4 will fit into 19. We
105-
# add the "--stages 19" here to detect if/when the program grows beyond that
106-
# limit. It's not necessarily a problem if we grow, but given the limited space
107-
# on the ASIC, we want to grow deliberatately and thoughtfully.
108-
PATH=/opt/gcc-12/bin:$PATH cargo xtask codegen --stages 19
104+
PATH=/opt/gcc-12/bin:$PATH cargo xtask codegen --stages $TOFINO_STAGES
109105

110106
# Preserve all the diagnostics spit out by the compiler
111107
mkdir -p /out/p4c-diags

.github/buildomat/jobs/packet-test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export SDE=/opt/oxide/tofino_sde
7777

7878
banner "Build"
7979
cargo build --features=tofino_asic --bin dpd --bin swadm
80-
cargo xtask codegen --stages 19
80+
cargo xtask codegen --stages $TOFINO_STAGES
8181

8282
banner "Test"
8383
sudo -E ./tools/veth_setup.sh

asic/src/softnpu/table.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/
44
//
5-
// Copyright 2025 Oxide Computer Company
5+
// Copyright 2026 Oxide Computer Company
66

77
use slog::{error, trace};
88
use softnpu_lib::{ManagementRequest, TableAdd, TableRemove};
@@ -31,6 +31,8 @@ const LOCAL_V6: &str = "ingress.local.local_v6";
3131
const LOCAL_V4: &str = "ingress.local.local_v4";
3232
const NAT_V4: &str = "ingress.nat.nat_v4";
3333
const NAT_V6: &str = "ingress.nat.nat_v6";
34+
const ATTACHED_SUBNET_V4: &str = "ingress.attached.attached_subnet_v4";
35+
const ATTACHED_SUBNET_V6: &str = "ingress.attached.attached_subnet_v6";
3436
const _NAT_ICMP_V6: &str = "ingress.nat.nat_icmp_v6";
3537
const _NAT_ICMP_V4: &str = "ingress.nat.nat_icmp_v4";
3638
const RESOLVER_V4: &str = "ingress.resolver.resolver_v4";
@@ -54,12 +56,16 @@ const ARP: &str = "pipe.Ingress.l3_router.Router4.Arp.tbl";
5456
const DPD_MAC_REWRITE: &str = "pipe.Ingress.mac_rewrite.mac_rewrite";
5557
const NAT_INGRESS4: &str = "pipe.Ingress.nat_ingress.ingress_ipv4";
5658
const NAT_INGRESS6: &str = "pipe.Ingress.nat_ingress.ingress_ipv6";
59+
const ATTACHED_SUBNET_INGRESS4: &str =
60+
"pipe.Ingress.attached_subnet_ingress.attached_subnets_v4";
61+
const ATTACHED_SUBNET_INGRESS6: &str =
62+
"pipe.Ingress.attached_subnet_ingress.attached_subnets_v6";
5763

5864
// All tables are defined to be 1024 entries deep
5965
const TABLE_SIZE: usize = 4096;
6066

6167
impl TableOps<Handle> for Table {
62-
fn new(_hdl: &Handle, name: &str) -> AsicResult<Table> {
68+
fn new(hdl: &Handle, name: &str) -> AsicResult<Table> {
6369
// TODO just mapping sidecar.p4 things onto simplified sidecar-lite.p4
6470
// things to get started.
6571
let (id, dpd_id) = match name {
@@ -84,8 +90,16 @@ impl TableOps<Handle> for Table {
8490
}
8591
NAT_INGRESS4 => (Some(NAT_V4.into()), Some(NAT_INGRESS4.into())),
8692
NAT_INGRESS6 => (Some(NAT_V6.into()), Some(NAT_INGRESS6.into())),
93+
ATTACHED_SUBNET_INGRESS4 => (
94+
Some(ATTACHED_SUBNET_V4.into()),
95+
Some(ATTACHED_SUBNET_INGRESS4.into()),
96+
),
97+
ATTACHED_SUBNET_INGRESS6 => (
98+
Some(ATTACHED_SUBNET_V6.into()),
99+
Some(ATTACHED_SUBNET_INGRESS6.into()),
100+
),
87101
x => {
88-
println!("TABLE NOT HANDLED {x}");
102+
error!(hdl.log, "TABLE NOT HANDLED {x}");
89103
(None, None)
90104
}
91105
};
@@ -382,7 +396,9 @@ impl TableOps<Handle> for Table {
382396
("rewrite", params)
383397
}
384398
(NAT_INGRESS4, "forward_ipv4_to")
385-
| (NAT_INGRESS6, "forward_ipv6_to") => {
399+
| (NAT_INGRESS6, "forward_ipv6_to")
400+
| (ATTACHED_SUBNET_INGRESS4, "forward_to_v4")
401+
| (ATTACHED_SUBNET_INGRESS6, "forward_to_v6") => {
386402
let mut target = Vec::new();
387403
let mut vni = Vec::new();
388404
let mut mac = Vec::new();
@@ -463,7 +479,7 @@ impl TableOps<Handle> for Table {
463479
("forward_to_sled", params)
464480
}
465481
(tbl, x) => {
466-
println!("ACTION NOT HANDLED {tbl} {x}");
482+
error!(hdl.log, "ACTION NOT HANDLED {tbl} {x}");
467483
return Ok(());
468484
}
469485
};
@@ -610,7 +626,7 @@ fn keyset_data(match_data: Vec<MatchEntryField>, table: &str) -> Vec<u8> {
610626
MatchEntryValue::Lpm(x) => {
611627
let mut data: Vec<u8> = Vec::new();
612628
match table {
613-
ROUTER_V4_IDX => {
629+
ROUTER_V4_IDX | ATTACHED_SUBNET_V4 => {
614630
// prefix for longest prefix match operation
615631
// "dst_addr" => hdr.ipv4.dst: lpm => bit<32>
616632
serialize_value_type_be(&x.prefix, &mut data);

common/src/attached_subnet.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/
4+
//
5+
// Copyright 2026 Oxide Computer Company
6+
7+
use oxnet::IpNet;
8+
use std::fmt;
9+
10+
use schemars::JsonSchema;
11+
use serde::{Deserialize, Serialize};
12+
13+
use crate::network::InstanceTarget;
14+
15+
/** represents an external subnet mapping */
16+
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
17+
pub struct AttachedSubnetEntry {
18+
pub subnet: IpNet,
19+
pub tgt: InstanceTarget,
20+
}
21+
22+
impl fmt::Display for AttachedSubnetEntry {
23+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24+
write!(f, "{}->{}", self.subnet, self.tgt)
25+
}
26+
}

common/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/
44
//
5-
// Copyright 2025 Oxide Computer Company
5+
// Copyright 2026 Oxide Computer Company
66

77
use std::collections::BTreeSet;
88
use std::convert::TryFrom;
@@ -11,6 +11,7 @@ use std::sync::Once;
1111
use std::time::Duration;
1212
use std::time::Instant;
1313

14+
pub mod attached_subnet;
1415
pub mod counters;
1516
pub mod logging;
1617
pub mod nat;

common/src/nat.rs

Lines changed: 3 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,97 +2,14 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/
44
//
5-
// Copyright 2025 Oxide Computer Company
5+
// Copyright 2026 Oxide Computer Company
66

7-
use std::fmt;
87
use std::net::{Ipv4Addr, Ipv6Addr};
98

109
use schemars::JsonSchema;
1110
use serde::{Deserialize, Serialize};
1211

13-
use crate::network::MacAddr;
14-
15-
/// A Geneve Virtual Network Identifier.
16-
///
17-
/// A Geneve VNI is a 24-bit value used to identify virtual networks
18-
/// encapsulated using the Generic Network Virtualization Encapsulation (Geneve)
19-
/// protocol (RFC 8926).
20-
#[derive(
21-
Clone,
22-
Copy,
23-
Debug,
24-
Deserialize,
25-
Eq,
26-
Hash,
27-
JsonSchema,
28-
PartialEq,
29-
PartialOrd,
30-
Ord,
31-
Serialize,
32-
)]
33-
#[serde(try_from = "u32")]
34-
pub struct Vni(u32);
35-
36-
impl Vni {
37-
const MAX_VNI: u32 = 0x00FF_FFFF;
38-
const ERR_MSG: &'static str = "VNI out of 24-bit range";
39-
40-
/// Construct a new VNI, validating that it's a valid 24-bit value.
41-
pub const fn new(vni: u32) -> Option<Self> {
42-
// bool.then_some is not const, unforunately
43-
if vni <= Self::MAX_VNI {
44-
Some(Self(vni))
45-
} else {
46-
None
47-
}
48-
}
49-
50-
/// Return the VNI as a u32.
51-
pub const fn as_u32(&self) -> u32 {
52-
self.0
53-
}
54-
}
55-
56-
impl core::convert::TryFrom<u32> for Vni {
57-
type Error = &'static str;
58-
59-
fn try_from(vni: u32) -> Result<Self, Self::Error> {
60-
Self::new(vni).ok_or(Self::ERR_MSG)
61-
}
62-
}
63-
64-
impl core::str::FromStr for Vni {
65-
type Err = &'static str;
66-
67-
fn from_str(s: &str) -> Result<Self, Self::Err> {
68-
match s.parse::<u32>().map(Vni::new) {
69-
Err(_) | Ok(None) => Err(Self::ERR_MSG),
70-
Ok(Some(vni)) => Ok(vni),
71-
}
72-
}
73-
}
74-
75-
impl fmt::Display for Vni {
76-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77-
write!(f, "{}", self.0)
78-
}
79-
}
80-
81-
/** represents an internal NAT target */
82-
#[derive(
83-
Debug, Copy, Clone, Deserialize, Serialize, JsonSchema, Eq, PartialEq,
84-
)]
85-
pub struct NatTarget {
86-
pub internal_ip: Ipv6Addr,
87-
pub inner_mac: MacAddr,
88-
pub vni: Vni,
89-
}
90-
91-
impl fmt::Display for NatTarget {
92-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93-
write!(f, "{}/{}/{}", self.internal_ip, self.inner_mac, self.vni)
94-
}
95-
}
12+
use crate::network::NatTarget;
9613

9714
/** represents an IPv6 NAT reservation */
9815
#[derive(Debug, Copy, Clone, Deserialize, Serialize, JsonSchema)]
@@ -130,7 +47,7 @@ impl PartialEq for Ipv4Nat {
13047

13148
#[cfg(test)]
13249
mod tests {
133-
use super::Vni;
50+
use crate::network::Vni;
13451

13552
#[test]
13653
fn test_vni() {

0 commit comments

Comments
 (0)