Skip to content

Commit 2bb9772

Browse files
committed
actually fix build issues, import mnl
1 parent 137984e commit 2bb9772

3 files changed

Lines changed: 53 additions & 23 deletions

File tree

Cargo.lock

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

odorobo-agent/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ ulid = { workspace = true }
4848
ipnet = { workspace = true }
4949
rtnetlink = "0.20.0"
5050
nftnl = { version = "0.9.1", features = ["nftnl-1-1-3"] }
51+
mnl = "0.3.1"
5152

odorobo-agent/src/networking/actor.rs

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ use crate::networking::messages::{AttachTap, DetachTap};
33
use futures_util::{StreamExt, TryStreamExt};
44

55
use kameo::{message::Context, prelude::*};
6-
use nftnl::{Batch, FinalizedBatch, Hook, MsgType, ProtoFamily, Rule, Table, nft_expr};
6+
use nftnl::{
7+
Batch, FinalizedBatch, Hook, MsgType, ProtoFamily, Rule, Table, expr::InterfaceName, nft_expr,
8+
};
79
use rtnetlink::{Error as NetlinkError, Handle, LinkBridge, LinkUnspec};
810
use stable_eyre::Report;
911
use stable_eyre::eyre::{Context as EyreContext, eyre};
12+
use std::ffi::CString;
1013
use tracing::info;
1114

1215
pub struct DhcpActor {
@@ -93,7 +96,6 @@ pub struct NetworkAgentActor {
9396
// netlink_handle:
9497
netlink_thread: tokio::task::JoinHandle<()>,
9598
netlink_handle: Handle,
96-
nft_socket: mnl::Socket,
9799
}
98100

99101
impl NetworkAgentActor {
@@ -112,19 +114,20 @@ impl NetworkAgentActor {
112114
.wrap_err_with(|| format!("failed to query link {}", link_name))
113115
}
114116

115-
fn send_nft_batch(&mut self, batch: &FinalizedBatch) -> Result<(), Report> {
116-
let portid = self.nft_socket.portid();
117+
fn send_nft_batch(batch: &FinalizedBatch) -> Result<(), Report> {
118+
let socket = mnl::Socket::new(mnl::Bus::Netfilter)
119+
.wrap_err("failed to create netfilter netlink socket")?;
120+
let portid = socket.portid();
117121

118-
self.nft_socket
122+
socket
119123
.send_all(batch)
120124
.wrap_err("failed to send nftables batch to netfilter")?;
121125

122126
let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize];
123127
let mut expected_seqs = batch.sequence_numbers();
124128

125129
while !expected_seqs.is_empty() {
126-
for message in self
127-
.nft_socket
130+
for message in socket
128131
.recv(&mut buffer[..])
129132
.wrap_err("failed to receive nftables netlink acknowledgement")?
130133
{
@@ -141,12 +144,23 @@ impl NetworkAgentActor {
141144
Ok(())
142145
}
143146

144-
fn ensure_nat_rules(
145-
&mut self,
146-
_bridge: &str,
147-
_subnet: &str,
148-
upstream_iface: &str,
149-
) -> Result<(), Report> {
147+
/// Ensures the host-only NAT masquerade rule exists for the configured
148+
/// upstream interface.
149+
///
150+
/// This is intentionally IPv4-only for now.
151+
///
152+
/// The current host-only networking model is built around IPv4 guest
153+
/// addressing and an IPv4 bridge gateway:
154+
/// - guest subnet config uses `Ipv4Net`
155+
/// - bridge gateway config uses `Ipv4Addr`
156+
/// - bridge address assignment is currently IPv4-only
157+
///
158+
/// Although nftables can match by interface in `inet` tables, switching
159+
/// this masquerade rule to dual-stack would implicitly opt us into IPv6
160+
/// NAT/NAT66 policy as well. That is a larger design decision than this
161+
/// hook should make on its own. Until odorobo has an intentional IPv6
162+
/// guest-networking story, we keep host-only NAT scoped to IPv4.
163+
fn ensure_nat_rules(_bridge: &str, _subnet: &str, upstream_iface: &str) -> Result<(), Report> {
150164
let table = Table::new(c"nat", ProtoFamily::Ipv4);
151165

152166
let mut postrouting_chain = nftnl::Chain::new(c"postrouting", &table);
@@ -158,14 +172,18 @@ impl NetworkAgentActor {
158172
batch.add(&postrouting_chain, MsgType::Add);
159173

160174
let mut postrouting_rule = Rule::new(&postrouting_chain);
175+
let upstream_iface = InterfaceName::Exact(
176+
CString::new(upstream_iface)
177+
.wrap_err("upstream interface name contained interior NUL")?,
178+
);
161179
postrouting_rule.add_expr(&nft_expr!(meta oifname));
162-
postrouting_rule.add_expr(&nft_expr!(cmp == upstream_iface));
180+
postrouting_rule.add_expr(&nft_expr!(cmp == &upstream_iface));
163181
postrouting_rule.add_expr(&nft_expr!(masquerade));
164182
batch.add(&postrouting_rule, MsgType::Add);
165183

166184
let finalized = batch.finalize();
167-
self.send_nft_batch(&finalized).wrap_err_with(|| {
168-
format!("failed to apply nftables postrouting masquerade for {upstream_iface}")
185+
Self::send_nft_batch(&finalized).wrap_err_with(|| {
186+
format!("failed to apply nftables postrouting masquerade for {upstream_iface:?}")
169187
})?;
170188

171189
Ok(())
@@ -180,8 +198,6 @@ impl Actor for NetworkAgentActor {
180198

181199
let (connection, handle, _) = rtnetlink::new_connection()?;
182200
let netlink_thread = tokio::spawn(connection);
183-
let nft_socket = mnl::Socket::new(mnl::Bus::Netfilter)
184-
.wrap_err("failed to create netfilter netlink socket")?;
185201

186202
let common = match args.network_mode.clone() {
187203
NetworkMode::HostonlyNat {
@@ -239,15 +255,17 @@ impl Actor for NetworkAgentActor {
239255
None
240256
};
241257

242-
let mut actor = Self {
258+
let actor = Self {
243259
config: args,
244260
common,
245261
dhcp_actor,
246262
netlink_thread,
247263
netlink_handle: handle,
248-
nft_socket,
249264
};
250265

266+
let common_bridge = actor.common.bridge.clone();
267+
let common_subnet = actor.common.subnet.clone();
268+
251269
match actor.config.network_mode.clone() {
252270
NetworkMode::HostonlyNat {
253271
bridge: _,
@@ -272,12 +290,11 @@ impl Actor for NetworkAgentActor {
272290
)
273291
})?;
274292

275-
actor
276-
.ensure_nat_rules(&actor.common.bridge, &actor.common.subnet, &upstream_iface)
293+
Self::ensure_nat_rules(&common_bridge, &common_subnet, &upstream_iface)
277294
.wrap_err_with(|| {
278295
format!(
279296
"failed to ensure nftables NAT rules for bridge {} and upstream {}",
280-
actor.common.bridge, upstream_iface
297+
common_bridge, upstream_iface
281298
)
282299
})?;
283300
}

0 commit comments

Comments
 (0)