Skip to content

Commit f3002b3

Browse files
Support for omicron internet gateway model (#588)
The Omicron internet gateway model associates IP pools, and subsequently addresses from those IP pools, with internet gateways. When packets are directed at an internet gateway through a VPC route, they should take on a source address from the IP pool that is tied to the target internet gateway. This trivially happens when there is only one internet gateway and an instance only has an address from one IP pool. But when there are multiple IP pools in play, particularly with floating IPs from different pools, a decision needs to be made about which one to use. That question is answered by the IP-pool/internet-gateway association in combination with VPC routes that direct packets at a particular internet gateway. --------- Co-authored-by: Kyle Simpson <[email protected]>
1 parent d72e46a commit f3002b3

File tree

14 files changed

+786
-155
lines changed

14 files changed

+786
-155
lines changed

.github/buildomat/jobs/opteadm.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#: "=/work/release/opteadm.release.sha256",
1212
#: ]
1313
#:
14+
#: [[publish]]
15+
#: series = "release"
16+
#: name = "opteadm"
17+
#: from_output = "/work/release/opteadm"
1418

1519
set -o errexit
1620
set -o pipefail

.github/buildomat/jobs/xde.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#: series = "module"
2222
#: name = "xde"
2323
#: from_output = "/work/release/xde"
24-
#
24+
#:
2525
#: [[publish]]
2626
#: series = "module"
2727
#: name = "xde.sha256"

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ syn = "2"
7272
tabwriter = { version = "1", features = ["ansi_formatting"] }
7373
thiserror = "1.0"
7474
toml = "0.8"
75+
uuid = { version = "1.0", default-features = false, features = ["serde"]}
7576
usdt = "0.5"
7677
version_check = "0.9"
7778
zerocopy = { version = "0.7", features = ["derive"] }

bench/src/packet.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ impl BenchPacketInstance for UlpProcessInstance {
294294
router::add_entry(
295295
&g1.port,
296296
IpCidr::Ip4("0.0.0.0/0".parse().unwrap()),
297-
RouterTarget::InternetGateway,
297+
RouterTarget::InternetGateway(None),
298298
RouterClass::System,
299299
)
300300
.unwrap();
@@ -303,7 +303,7 @@ impl BenchPacketInstance for UlpProcessInstance {
303303
router::add_entry(
304304
&g1.port,
305305
IpCidr::Ip6("::/0".parse().unwrap()),
306-
RouterTarget::InternetGateway,
306+
RouterTarget::InternetGateway(None),
307307
RouterClass::System,
308308
)
309309
.unwrap();

bin/opteadm/src/bin/opteadm.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,10 @@ fn main() -> anyhow::Result<()> {
782782
port_name: port,
783783
external_ips_v4: Some(cfg),
784784
external_ips_v6: None,
785+
// TODO: It'd be nice to have this user-specifiable via
786+
// opteadm, but for now it's a purely control-plane
787+
// concept.
788+
inet_gw_map: None,
785789
};
786790
hdl.set_external_ips(&req)?;
787791
} else if let Ok(cfg) =
@@ -791,6 +795,8 @@ fn main() -> anyhow::Result<()> {
791795
port_name: port,
792796
external_ips_v6: Some(cfg),
793797
external_ips_v4: None,
798+
// TODO: As above.
799+
inet_gw_map: None,
794800
};
795801
hdl.set_external_ips(&req)?;
796802
} else {

lib/opte-test-utils/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ pub const BS_IP_ADDR: Ipv6Addr =
102102
const UFT_LIMIT: Option<NonZeroU32> = NonZeroU32::new(16);
103103
const TCP_LIMIT: Option<NonZeroU32> = NonZeroU32::new(16);
104104

105+
pub const EXT_IP4: &str = "10.77.77.13";
106+
pub const EXT_IP6: &str = "fd00:100::1";
107+
105108
pub fn ox_vpc_mac(id: [u8; 3]) -> MacAddr {
106109
MacAddr::from([0xA8, 0x40, 0x25, 0xF0 | id[0], id[1], id[2]])
107110
}
@@ -132,7 +135,7 @@ pub fn g1_cfg() -> VpcCfg {
132135
gateway_ip: "172.30.0.1".parse().unwrap(),
133136
external_ips: ExternalIpCfg {
134137
snat: Some(SNat4Cfg {
135-
external_ip: "10.77.77.13".parse().unwrap(),
138+
external_ip: EXT_IP4.parse().unwrap(),
136139
ports: 1025..=4096,
137140
}),
138141
ephemeral_ip: None,
@@ -372,7 +375,8 @@ pub fn oxide_net_setup2(
372375
"set:firewall.rules.out=0",
373376
// * Outbound IPv4 SNAT
374377
// * Outbound IPv6 SNAT
375-
"set:nat.rules.out=2",
378+
// * Drop uncaught InetGw packets.
379+
"set:nat.rules.out=3",
376380
];
377381

378382
[

lib/oxide-vpc/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ usdt = ["opte/usdt"]
3333
illumos-sys-hdrs.workspace = true
3434
opte.workspace = true
3535

36+
cfg-if.workspace = true
37+
poptrie.workspace = true
3638
serde.workspace = true
3739
smoltcp.workspace = true
3840
tabwriter = { workspace = true, optional = true }
41+
uuid.workspace = true
3942
zerocopy.workspace = true
40-
poptrie.workspace = true
41-
cfg-if.workspace = true
4243

4344
[dev-dependencies]
4445
ctor.workspace = true

lib/oxide-vpc/src/api.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
// Copyright 2024 Oxide Computer Company
66

7+
use alloc::collections::BTreeMap;
78
use alloc::collections::BTreeSet;
89
use alloc::string::String;
910
use alloc::string::ToString;
@@ -16,6 +17,7 @@ use illumos_sys_hdrs::datalink_id_t;
1617
pub use opte::api::*;
1718
use serde::Deserialize;
1819
use serde::Serialize;
20+
use uuid::Uuid;
1921

2022
/// This is the MAC address that OPTE uses to act as the virtual gateway.
2123
pub const GW_MAC_ADDR: MacAddr =
@@ -347,7 +349,8 @@ impl From<PhysNet> for GuestPhysAddr {
347349
/// * InternetGateway: Packets matching this entry are forwarded to
348350
/// the internet. In the case of the Oxide Network the IG is not an
349351
/// actual destination, but rather a configuration that determines how
350-
/// we should NAT the flow.
352+
/// we should NAT the flow. The address in the gateway is the source
353+
/// address that is to be used.
351354
///
352355
/// * Ip: Packets matching this entry are forwarded to the specified IP.
353356
///
@@ -362,7 +365,7 @@ impl From<PhysNet> for GuestPhysAddr {
362365
#[derive(Clone, Debug, Copy, Deserialize, Serialize)]
363366
pub enum RouterTarget {
364367
Drop,
365-
InternetGateway,
368+
InternetGateway(Option<Uuid>),
366369
Ip(IpAddr),
367370
VpcSubnet(IpCidr),
368371
}
@@ -374,7 +377,7 @@ impl FromStr for RouterTarget {
374377
fn from_str(s: &str) -> Result<Self, Self::Err> {
375378
match s.to_ascii_lowercase().as_str() {
376379
"drop" => Ok(Self::Drop),
377-
"ig" => Ok(Self::InternetGateway),
380+
"ig" => Ok(Self::InternetGateway(None)),
378381
lower => match lower.split_once('=') {
379382
Some(("ip4", ip4s)) => {
380383
let ip4 = ip4s
@@ -396,6 +399,10 @@ impl FromStr for RouterTarget {
396399
cidr6s.parse().map(|x| Self::VpcSubnet(IpCidr::Ip6(x)))
397400
}
398401

402+
Some(("ig", uuid)) => Ok(Self::InternetGateway(Some(
403+
uuid.parse::<Uuid>().map_err(|e| e.to_string())?,
404+
))),
405+
399406
_ => Err(format!("malformed router target: {}", lower)),
400407
},
401408
}
@@ -406,7 +413,8 @@ impl Display for RouterTarget {
406413
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407414
match self {
408415
Self::Drop => write!(f, "Drop"),
409-
Self::InternetGateway => write!(f, "IG"),
416+
Self::InternetGateway(None) => write!(f, "ig"),
417+
Self::InternetGateway(Some(id)) => write!(f, "ig={}", id),
410418
Self::Ip(IpAddr::Ip4(ip4)) => write!(f, "ip4={}", ip4),
411419
Self::Ip(IpAddr::Ip6(ip6)) => write!(f, "ip6={}", ip6),
412420
Self::VpcSubnet(IpCidr::Ip4(sub4)) => write!(f, "sub4={}", sub4),
@@ -612,6 +620,7 @@ pub struct SetExternalIpsReq {
612620
pub port_name: String,
613621
pub external_ips_v4: Option<ExternalIpCfg<Ipv4Addr>>,
614622
pub external_ips_v6: Option<ExternalIpCfg<Ipv6Addr>>,
623+
pub inet_gw_map: Option<BTreeMap<IpAddr, BTreeSet<Uuid>>>,
615624
}
616625

617626
#[derive(Debug, Deserialize, Serialize)]

0 commit comments

Comments
 (0)