Skip to content

Commit

Permalink
hook up back end
Browse files Browse the repository at this point in the history
  • Loading branch information
rcgoodfellow committed Sep 3, 2024
1 parent c875f44 commit e068002
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 24 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ omicron-test-utils = { path = "test-utils" }
omicron-workspace-hack = "0.1.0"
omicron-zone-package = "0.11.0"
oxide-client = { path = "clients/oxide-client" }
oxide-vpc = { git = "https://github.com/oxidecomputer/opte", rev = "76878de67229ea113d70503c441eab47ac5dc653", features = [ "api", "std" ] }
oxide-vpc = { git = "https://github.com/oxidecomputer/opte", rev = "4830268f642f766180dee8cbafdca790916bfa09", features = [ "api", "std" ] }
oxlog = { path = "dev-tools/oxlog" }
oxnet = { git = "https://github.com/oxidecomputer/oxnet" }
once_cell = "1.19.0"
Expand All @@ -468,7 +468,7 @@ openapiv3 = "2.0.0"
# must match samael's crate!
openssl = "0.10"
openssl-sys = "0.9"
opte-ioctl = { git = "https://github.com/oxidecomputer/opte", rev = "76878de67229ea113d70503c441eab47ac5dc653" }
opte-ioctl = { git = "https://github.com/oxidecomputer/opte", rev = "4830268f642f766180dee8cbafdca790916bfa09" }
oso = "0.27"
owo-colors = "4.0.0"
oximeter = { path = "oximeter/oximeter" }
Expand Down
2 changes: 1 addition & 1 deletion common/src/api/internal/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ pub struct DhcpConfig {
#[serde(tag = "type", rename_all = "snake_case", content = "value")]
pub enum RouterTarget {
Drop,
InternetGateway,
InternetGateway(IpAddr),
Ip(IpAddr),
VpcSubnet(IpNet),
}
Expand Down
2 changes: 1 addition & 1 deletion illumos-utils/src/opte/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ fn router_target_opte(target: &shared::RouterTarget) -> RouterTarget {
use shared::RouterTarget::*;
match target {
Drop => RouterTarget::Drop,
InternetGateway => RouterTarget::InternetGateway,
InternetGateway(ip) => RouterTarget::InternetGateway((*ip).into()),
Ip(ip) => RouterTarget::Ip((*ip).into()),
VpcSubnet(net) => RouterTarget::VpcSubnet(net_to_cidr(*net)),
}
Expand Down
101 changes: 100 additions & 1 deletion illumos-utils/src/opte/port_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
Expand Down Expand Up @@ -249,7 +250,7 @@ impl PortManager {
};

let vpc_cfg = VpcCfg {
ip_cfg,
ip_cfg: ip_cfg.clone(),
guest_mac: MacAddr::from(nic.mac.into_array()),
gateway_mac: MacAddr::from(gateway.mac.into_array()),
vni,
Expand Down Expand Up @@ -329,6 +330,19 @@ impl PortManager {
// create a record to show that we're interested in receiving
// those routes.
let mut routes = self.inner.routes.lock().unwrap();
let system_routes = match &ip_cfg {
IpCfg::Ipv4(cfg) => {
system_routes_v4(cfg, is_service, &mut routes, &port)
}
IpCfg::Ipv6(cfg) => {
system_routes_v6(cfg, is_service, &mut routes, &port)
}
IpCfg::DualStack { ipv4, ipv6 } => {
system_routes_v4(ipv4, is_service, &mut routes, &port);
system_routes_v6(ipv6, is_service, &mut routes, &port)
}
};
/*
let system_routes =
routes.entry(port.system_router_key()).or_insert_with(|| {
let mut routes = HashSet::new();
Expand All @@ -349,6 +363,7 @@ impl PortManager {
RouteSet { version: None, routes, active_ports: 0 }
});
*/
system_routes.active_ports += 1;
// Clone is needed to get borrowck on our side, sadly.
let system_routes = system_routes.clone();
Expand Down Expand Up @@ -939,3 +954,87 @@ impl Drop for PortTicket {
let _ = self.release_inner();
}
}

fn system_routes_v4<'a>(
cfg: &Ipv4Cfg,
is_service: bool,
routes: &'a mut HashMap<RouterId, RouteSet>,
port: &Port,
) -> &'a mut RouteSet {
routes.entry(port.system_router_key()).or_insert_with(|| {
let mut routes = HashSet::new();
if let Some(ref snat) = cfg.external_ips.snat {
if is_service {
routes.insert(ResolvedVpcRoute {
dest: "0.0.0.0/0".parse().unwrap(),
target: ApiRouterTarget::InternetGateway(IpAddr::V4(
Ipv4Addr::from(snat.external_ip),
)),
});
}
}
if let Some(ref ephemeral) = cfg.external_ips.ephemeral_ip {
if is_service {
routes.insert(ResolvedVpcRoute {
dest: "0.0.0.0/0".parse().unwrap(),
target: ApiRouterTarget::InternetGateway(IpAddr::V4(
Ipv4Addr::from(*ephemeral),
)),
});
}
}
if is_service {
for fip in &cfg.external_ips.floating_ips {
routes.insert(ResolvedVpcRoute {
dest: "0.0.0.0/0".parse().unwrap(),
target: ApiRouterTarget::InternetGateway(IpAddr::V4(
Ipv4Addr::from(*fip),
)),
});
}
}
RouteSet { version: None, routes, active_ports: 0 }
})
}

fn system_routes_v6<'a>(
cfg: &Ipv6Cfg,
is_service: bool,
routes: &'a mut HashMap<RouterId, RouteSet>,
port: &Port,
) -> &'a mut RouteSet {
routes.entry(port.system_router_key()).or_insert_with(|| {
let mut routes = HashSet::new();
if let Some(ref snat) = cfg.external_ips.snat {
if is_service {
routes.insert(ResolvedVpcRoute {
dest: "0.0.0.0/0".parse().unwrap(),
target: ApiRouterTarget::InternetGateway(IpAddr::V6(
Ipv6Addr::from(snat.external_ip),
)),
});
}
}
if let Some(ref ephemeral) = cfg.external_ips.ephemeral_ip {
if is_service {
routes.insert(ResolvedVpcRoute {
dest: "0.0.0.0/0".parse().unwrap(),
target: ApiRouterTarget::InternetGateway(IpAddr::V6(
Ipv6Addr::from(*ephemeral),
)),
});
}
}
if is_service {
for fip in &cfg.external_ips.floating_ips {
routes.insert(ResolvedVpcRoute {
dest: "0.0.0.0/0".parse().unwrap(),
target: ApiRouterTarget::InternetGateway(IpAddr::V6(
Ipv6Addr::from(*fip),
)),
});
}
}
RouteSet { version: None, routes, active_ports: 0 }
})
}
138 changes: 126 additions & 12 deletions nexus/db-queries/src/db/datastore/vpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2413,12 +2413,12 @@ impl DataStore {
RouteDestination::Vpc(_) => (None, None),
};

let (v4_target, v6_target) = match rule.target.0 {
let (v4_target, v6_target) = match &rule.target.0 {
RouteTarget::Ip(ip @ IpAddr::V4(_)) => {
(Some(RouterTarget::Ip(ip)), None)
(Some(RouterTarget::Ip(*ip)), None)
}
RouteTarget::Ip(ip @ IpAddr::V6(_)) => {
(None, Some(RouterTarget::Ip(ip)))
(None, Some(RouterTarget::Ip(*ip)))
}
RouteTarget::Subnet(n) => subnets
.get(&n)
Expand Down Expand Up @@ -2449,15 +2449,9 @@ impl DataStore {
(Some(RouterTarget::Drop), Some(RouterTarget::Drop))
}

// TODO: Internet Gateways.
// The semantic here is 'name match => allow',
// as the other aspect they will control is SNAT
// IP allocation. Today, presence of this rule
// allows upstream regardless of name.
RouteTarget::InternetGateway(_n) => (
Some(RouterTarget::InternetGateway),
Some(RouterTarget::InternetGateway),
),
// There can be multiple targets per internet gateway, so these
// are handled below.
RouteTarget::InternetGateway(_) => (None, None),

// TODO: VPC Peering.
RouteTarget::Vpc(_) => (None, None),
Expand All @@ -2476,6 +2470,126 @@ impl DataStore {
if let (Some(dest), Some(target)) = (v6_dest, v6_target) {
out.insert(dest, target);
}

// The OPTE model for internet gateway routes is that a VPC
// route points at an internet gateway object. That internet
// gateway object contains the source address that is to be
// used for the route.
//
// Therefore, here what we are doing is ...
// 1. Look up the intergnet gateway (igw).
// 2. Fetch the IP pools associated with the igw.
// 3. Look up the external IPs in the vpc.
// 4. For each external IP, see if it belongs to an IP pool
// associated with an internet gateway (yeah, this is
// quadratic, but the number of ip pool associations is
// unlikely to be more than a couple?)
// 5. If the external IP does belong to an IP pool that is
// associated with the gateway target, install the
// destination -> target rule where the target is the
// internet gateway parameterized by the external ip.
if let RouteTarget::InternetGateway(name) = &rule.target.0 {
let conn = self.pool_connection_authorized(opctx).await?;
let igw = db::lookup::LookupPath::new(opctx, self)
.vpc_id(authz_vpc.id())
.internet_gateway_name(&(name.clone().into()))
.fetch()
.await
.ok();

let (.., authz_igw, _db_igw) = match igw {
Some(value) => value,
None => return Ok(out),
};

use db::schema::internet_gateway_ip_pool::dsl as igwp;
let igw_pools = igwp::internet_gateway_ip_pool
.filter(igwp::time_deleted.is_null())
.filter(igwp::internet_gateway_id.eq(authz_igw.id()))
.select(InternetGatewayIpPool::as_select())
.load_async::<InternetGatewayIpPool>(&*conn)
.await
.ok();

let igw_pools = match igw_pools {
Some(value) => value,
None => return Ok(out),
};

for igw_pool in &igw_pools {
use db::schema::ip_pool_range::dsl as ipr;
let prs = match ipr::ip_pool_range
.filter(ipr::time_deleted.is_null())
.filter(ipr::ip_pool_id.eq(igw_pool.ip_pool_id))
.select(IpPoolRange::as_select())
.load_async::<IpPoolRange>(&*conn)
.await
{
Ok(value) => value,
Err(_) => continue,
};

for x in instances.values() {
let ifx = &x.1;
use db::schema::external_ip::dsl as xip;
let ext_ips = xip::external_ip
.filter(xip::time_deleted.is_null())
.filter(xip::parent_id.eq(ifx.instance_id))
.select(ExternalIp::as_select())
.load_async::<ExternalIp>(&*conn)
.await
.ok();

let ext_ips = match ext_ips {
Some(value) => value,
None => continue,
};

for ext_ip in &ext_ips {
match ext_ip.ip.ip() {
IpAddr::V4(v4) => {
if let Some(dest) = v4_dest {
for pr in &prs {
if ext_ip.ip.ip()
>= pr.first_address.ip()
&& ext_ip.ip.ip()
<= pr.last_address.ip()
{
out.insert(
dest,
RouterTarget::InternetGateway(
v4.into(),
),
);
break;
}
}
}
}
IpAddr::V6(v6) => {
if let Some(dest) = v6_dest {
for pr in &prs {
if ext_ip.ip.ip()
>= pr.first_address.ip()
&& ext_ip.ip.ip()
<= pr.last_address.ip()
{
out.insert(
dest,
RouterTarget::InternetGateway(
v6.into(),
),
);
break;
}
}
}
}
};
}
}
}
}
}

Ok(out)
Expand Down
Loading

0 comments on commit e068002

Please sign in to comment.