Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAT RPW #3804

Merged
merged 26 commits into from
Nov 18, 2023
Merged

NAT RPW #3804

Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
315ff35
NAT RPW for instance networking
internet-diglett Jul 31, 2023
e29a612
undo change to nextest config
internet-diglett Oct 17, 2023
eda98c5
update omdb background tasks
internet-diglett Oct 18, 2023
8af0927
convert db tables to follow similar column names as dns rpws
internet-diglett Oct 19, 2023
8fa914e
add missing migration file
internet-diglett Oct 20, 2023
452e4d1
ensure test doesn't rely on table order
internet-diglett Oct 20, 2023
ed63b1c
Merge branch 'main' into nat_rpw
internet-diglett Oct 27, 2023
7fea2ac
Update deleted_by_external_ip to use new table strategy
internet-diglett Oct 31, 2023
38d6c5a
Merge branch 'main' into nat_rpw
internet-diglett Nov 9, 2023
a591710
enforce correct types for Ipv4NatEntry
internet-diglett Nov 13, 2023
9858397
refactor ensure_ipv4_nat_entry to use diesel query
internet-diglett Nov 13, 2023
92df045
refactor sql queries use diesel dsl
internet-diglett Nov 14, 2023
151516f
remove unnecessary internal endpoint
internet-diglett Nov 14, 2023
7f0c0a7
update endpoint to use internal_latencies instrumentation
internet-diglett Nov 14, 2023
18164ee
revert accidental change to non-gimlet/config-rss.toml
internet-diglett Nov 14, 2023
351a0a8
remove unnecessary view struct
internet-diglett Nov 14, 2023
d3e9c60
convert to SqlU32 to i64
internet-diglett Nov 14, 2023
b38ebe5
a wild u32 escaped
internet-diglett Nov 14, 2023
0ffee3f
error on attempt of ipv6 nat configuration
internet-diglett Nov 14, 2023
3ce9d98
remove /rpw prefix from internal endpoint
internet-diglett Nov 14, 2023
e8d4768
remote unnecessary version tracking struct
internet-diglett Nov 14, 2023
049f324
bump schema version
internet-diglett Nov 14, 2023
f547f8a
bump dendrite version
internet-diglett Nov 15, 2023
7bf9e93
fixup dbinit statement ordering for clarity
internet-diglett Nov 16, 2023
45cea66
Merge branch 'main' into nat_rpw
internet-diglett Nov 16, 2023
eff1828
bump dendrite and schema
internet-diglett Nov 18, 2023
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
1 change: 1 addition & 0 deletions common/src/api/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ pub enum ResourceType {
UserBuiltin,
Zpool,
Vmm,
Ipv4NatEntry,
}

// IDENTITY METADATA
Expand Down
19 changes: 17 additions & 2 deletions common/src/nexus_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ pub struct BackgroundTaskConfig {
pub dns_external: DnsTasksConfig,
/// configuration for external endpoint list watcher
pub external_endpoints: ExternalEndpointsConfig,
/// configuration for nat table garbage collector
pub nat_cleanup: NatCleanupConfig,
}

#[serde_as]
Expand Down Expand Up @@ -369,6 +371,14 @@ pub struct ExternalEndpointsConfig {
// allow/disallow wildcard certs, don't serve expired certs, etc.)
}

#[serde_as]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct NatCleanupConfig {
/// period (in seconds) for periodic activations of this background task
#[serde_as(as = "DurationSeconds<u64>")]
pub period_secs: Duration,
}

/// Configuration for a nexus server
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct PackageConfig {
Expand Down Expand Up @@ -478,7 +488,7 @@ mod test {
use crate::nexus_config::{
BackgroundTaskConfig, ConfigDropshotWithTls, Database,
DeploymentConfig, DnsTasksConfig, DpdConfig, ExternalEndpointsConfig,
InternalDns, LoadErrorKind, MgdConfig,
InternalDns, LoadErrorKind, MgdConfig, NatCleanupConfig,
};
use dropshot::ConfigDropshot;
use dropshot::ConfigLogging;
Expand Down Expand Up @@ -626,6 +636,7 @@ mod test {
dns_external.period_secs_propagation = 7
dns_external.max_concurrent_server_updates = 8
external_endpoints.period_secs = 9
nat_cleanup.period_secs = 30
[default_region_allocation_strategy]
type = "random"
seed = 0
Expand Down Expand Up @@ -719,7 +730,10 @@ mod test {
},
external_endpoints: ExternalEndpointsConfig {
period_secs: Duration::from_secs(9),
}
},
nat_cleanup: NatCleanupConfig {
period_secs: Duration::from_secs(30),
},
},
default_region_allocation_strategy:
crate::nexus_config::RegionAllocationStrategy::Random {
Expand Down Expand Up @@ -773,6 +787,7 @@ mod test {
dns_external.period_secs_propagation = 7
dns_external.max_concurrent_server_updates = 8
external_endpoints.period_secs = 9
nat_cleanup.period_secs = 30
[default_region_allocation_strategy]
type = "random"
"##,
Expand Down
1 change: 1 addition & 0 deletions dev-tools/omdb/src/bin/omdb/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ async fn cmd_nexus_background_tasks_show(
"dns_config_external",
"dns_servers_external",
"dns_propagation_external",
"nat_v4_garbage_collector",
] {
if let Some(bgtask) = tasks.remove(name) {
print_task(&bgtask);
Expand Down
15 changes: 15 additions & 0 deletions dev-tools/omdb/tests/env.out
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ task: "external_endpoints"
on each one


task: "nat_v4_garbage_collector"
prunes soft-deleted IPV4 NAT entries from ipv4_nat_entry table based on a
predetermined retention policy


---------------------------------------------
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT
Expand Down Expand Up @@ -113,6 +118,11 @@ task: "external_endpoints"
on each one


task: "nat_v4_garbage_collector"
prunes soft-deleted IPV4 NAT entries from ipv4_nat_entry table based on a
predetermined retention policy


---------------------------------------------
stderr:
note: Nexus URL not specified. Will pick one from DNS.
Expand Down Expand Up @@ -156,6 +166,11 @@ task: "external_endpoints"
on each one


task: "nat_v4_garbage_collector"
prunes soft-deleted IPV4 NAT entries from ipv4_nat_entry table based on a
predetermined retention policy


---------------------------------------------
stderr:
note: Nexus URL not specified. Will pick one from DNS.
Expand Down
12 changes: 12 additions & 0 deletions dev-tools/omdb/tests/successes.out
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ task: "external_endpoints"
on each one


task: "nat_v4_garbage_collector"
prunes soft-deleted IPV4 NAT entries from ipv4_nat_entry table based on a
predetermined retention policy


---------------------------------------------
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
Expand Down Expand Up @@ -295,6 +300,13 @@ task: "dns_propagation_external"
[::1]:REDACTED_PORT success


task: "nat_v4_garbage_collector"
configured period: every 30s
currently executing: no
last completed activation: iter 2, triggered by an explicit signal
started at <REDACTED TIMESTAMP> (<REDACTED DURATION>s ago) and ran for <REDACTED DURATION>ms
warning: unknown background task: "nat_v4_garbage_collector" (don't know how to interpret details: Null)

task: "external_endpoints"
configured period: every 1m
currently executing: no
Expand Down
126 changes: 126 additions & 0 deletions nexus/db-model/src/ipv4_nat_entry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::net::{Ipv4Addr, Ipv6Addr};

use super::MacAddr;
use crate::{
schema::{ipv4_nat_entry, ipv4_nat_version},
SqlU16, SqlU32, Vni,
};
use chrono::{DateTime, Utc};
use omicron_common::api::external;
use schemars::JsonSchema;
use serde::Serialize;
use uuid::Uuid;

// TODO correctness
// If we're not going to store ipv4 and ipv6
// NAT entries in the same table, and we don't
// need any of the special properties of the IpNetwork
// column type, does it make sense to use a different
// column type?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple questions about this:

  • I don't see anything in this struct that appears IPv4 specific -- doesn't ipnetwork::IpNetwork encapsulate both v4 and v6 addresses? Could this entire struct just be IpNatValues (and same elsewhere in this file) or would that be a bad idea?
  • If that genuinely is a bad idea, https://docs.rs/ipnetwork/0.20.0/ipnetwork/struct.Ipv4Network.html exists, and we could use that instead?

Either way, we should resolve this comment before merging!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By keeping the tables for v4 and v6 separate, we maintain a 1:1 mapping with the tables in dendrite, which does make troubleshooting, observability, and state reconciliation a bit more straightforward, so my gut says "keep them separate"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try using Ipv4Network unless someone has an objection :)

/// Database representation of an Ipv4 NAT Entry.
#[derive(Insertable, Debug, Clone)]
#[diesel(table_name = ipv4_nat_entry)]
pub struct Ipv4NatValues {
pub external_address: ipnetwork::IpNetwork,
pub first_port: SqlU16,
pub last_port: SqlU16,
pub sled_address: ipnetwork::IpNetwork,
pub vni: Vni,
pub mac: MacAddr,
}

// TODO correctness
// If we're not going to store ipv4 and ipv6
// NAT entries in the same table, we should probably
// make the types more restrictive to prevent an
// accidental ipv6 entry from being created.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the previous comment

#[derive(Queryable, Debug, Clone, Selectable)]
#[diesel(table_name = ipv4_nat_entry)]
pub struct Ipv4NatEntry {
pub id: Uuid,
pub external_address: ipnetwork::IpNetwork,
pub first_port: SqlU16,
pub last_port: SqlU16,
pub sled_address: ipnetwork::IpNetwork,
pub vni: Vni,
pub mac: MacAddr,
pub version_added: SqlU32,
pub version_removed: Option<SqlU32>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be storing these versions as BigInts, rather than SqlU32s?

If we overflow a u32 (very possible!) these values would start throwing deserialization errors.

pub time_created: DateTime<Utc>,
pub time_deleted: Option<DateTime<Utc>>,
}

impl Ipv4NatEntry {
pub fn first_port(&self) -> u16 {
self.first_port.into()
}

pub fn last_port(&self) -> u16 {
self.last_port.into()
}

pub fn version_added(&self) -> u32 {
self.version_added.into()
}

pub fn version_removed(&self) -> Option<u32> {
self.version_removed.map(|i| i.into())
}
}

#[derive(Queryable, Debug, Clone, Selectable)]
#[diesel(table_name = ipv4_nat_version)]
pub struct Ipv4NatGen {
pub last_value: SqlU32,
pub log_cnt: SqlU32,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about storing this as a u32 vs bigint

pub is_called: bool,
}

/// NAT Record
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub struct Ipv4NatEntryView {
pub external_address: Ipv4Addr,
pub first_port: u16,
pub last_port: u16,
pub sled_address: Ipv6Addr,
pub vni: external::Vni,
pub mac: external::MacAddr,
pub gen: u32,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about u32 vs u64 here

pub deleted: bool,
}

impl From<Ipv4NatEntry> for Ipv4NatEntryView {
fn from(value: Ipv4NatEntry) -> Self {
let external_address = match value.external_address.ip() {
std::net::IpAddr::V4(a) => a,
std::net::IpAddr::V6(_) => unreachable!(),
};

let sled_address = match value.sled_address.ip() {
std::net::IpAddr::V4(_) => unreachable!(),
std::net::IpAddr::V6(a) => a,
};

let (gen, deleted) = match value.version_removed {
Some(gen) => (*gen, true),
None => (*value.version_added, false),
};

Self {
external_address,
first_port: value.first_port(),
last_port: value.last_port(),
sled_address,
vni: value.vni.0,
mac: *value.mac,
gen,
deleted,
}
}
}

/// NAT Generation
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub struct Ipv4NatGenView {
pub gen: u32,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u32 vs u64 again here

}
2 changes: 2 additions & 0 deletions nexus/db-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ mod system_update;
// These actually represent subqueries, not real table.
// However, they must be defined in the same crate as our tables
// for join-based marker trait generation.
mod ipv4_nat_entry;
pub mod queries;
mod rack;
mod region;
Expand Down Expand Up @@ -122,6 +123,7 @@ pub use instance::*;
pub use instance_cpu_count::*;
pub use instance_state::*;
pub use ip_pool::*;
pub use ipv4_nat_entry::*;
pub use ipv4net::*;
pub use ipv6::*;
pub use ipv6net::*;
Expand Down
26 changes: 26 additions & 0 deletions nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,32 @@ table! {
}
}

table! {
ipv4_nat_entry (id) {
id -> Uuid,
external_address -> Inet,
first_port -> Int4,
last_port -> Int4,
sled_address -> Inet,
vni -> Int4,
mac -> Int8,
version_added -> Int8,
version_removed -> Nullable<Int8>,
time_created -> Timestamptz,
time_deleted -> Nullable<Timestamptz>,
}
}

// This is the sequence used for the version number
// in ipv4_nat_entry.
table! {
ipv4_nat_version (last_value) {
last_value -> Int8,
log_cnt -> Int8,
is_called -> Bool,
}
}

table! {
external_ip (id) {
id -> Uuid,
Expand Down
Loading
Loading