Skip to content

Commit 20e592c

Browse files
authored
feat: automatically set address during creation (#28)
Close: #27
1 parent 06d3e2e commit 20e592c

File tree

5 files changed

+119
-50
lines changed

5 files changed

+119
-50
lines changed

ci/scripts/verify.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ ping_verify() {
104104
--uplink-delay ${delay}ms \
105105
--downlink-delay ${delay}ms \
106106
--downlink-loss $loss \
107-
"$WORKDIR/ping.sh $PING_ITERS 192.168.12.1" \
107+
$WORKDIR/ping.sh $PING_ITERS 192.168.12.1 \
108108
>"rattan_${suffix}_ping.log" 2>&1
109109
calc_ping_stats $delay $loss $suffix
110110
}
@@ -154,7 +154,7 @@ iperf_verify() {
154154
--downlink-loss $loss \
155155
--uplink-bandwidth $bandwidth \
156156
--downlink-bandwidth $bandwidth \
157-
"$WORKDIR/iperf3.sh $IPERF_ITERS 192.168.12.1 $cc" \
157+
$WORKDIR/iperf3.sh $IPERF_ITERS 192.168.12.1 $cc \
158158
>"rattan_$suffix.log" 2>&1
159159
calc_iperf_stats $(expr 12 \* $bw_mul) $loss $suffix
160160
}

rattan-cli/src/main.rs

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ use rattan::metal::netns::NetNsGuard;
2020
use rattan::netem_trace::{Bandwidth, Delay};
2121
use std::io::BufRead;
2222
use std::process::Stdio;
23-
use std::thread::sleep;
24-
use std::time::Duration;
25-
use tracing::{error, info, span, Instrument, Level};
23+
use tracing::{debug, error, info, span, Instrument, Level};
2624
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
2725

2826
// mod docker;
2927

28+
const CONFIG_PORT_BASE: u16 = 8086;
29+
3030
#[derive(Debug, Parser, Clone)]
3131
pub struct CommandArgs {
3232
/// Verbose debug output
@@ -159,17 +159,15 @@ fn mahimahi_file_to_pattern(filename: &str) -> Vec<u64> {
159159
})
160160
.unwrap();
161161
let line = line.trim();
162-
163-
line
164-
.parse::<u64>()
162+
line.parse::<u64>()
165163
.map_err(|e| {
166164
error!("Failed to parse line {} in {}: {}", i, &filename, e);
167165
e
168166
})
169167
.unwrap()
170168
})
171169
.collect();
172-
info!("Trace pattern: {:?}", trace_pattern);
170+
debug!("Trace pattern: {:?}", trace_pattern);
173171
trace_pattern
174172
}
175173

@@ -187,12 +185,13 @@ fn main() {
187185
// }
188186
info!("{:?}", opts);
189187

190-
let _std_env = get_std_env(StdNetEnvConfig {
188+
let std_env = get_std_env(StdNetEnvConfig {
191189
mode: rattan::env::StdNetEnvMode::Compatible,
192190
})
193191
.unwrap();
194-
let left_ns = _std_env.left_ns.clone();
195-
let _right_ns = _std_env.right_ns.clone();
192+
let left_ns = std_env.left_ns.clone();
193+
let _right_ns = std_env.right_ns.clone();
194+
let rattan_base = std_env.right_pair.right.ip_addr.0;
196195

197196
let mut machine = RattanMachine::<StdPacket>::new();
198197
let cancel_token = machine.cancel_token();
@@ -201,9 +200,9 @@ fn main() {
201200
let rattan_thread_span = span!(Level::DEBUG, "rattan_thread").or_current();
202201
let rattan_thread = std::thread::spawn(move || {
203202
let _entered = rattan_thread_span.entered();
204-
let original_ns = _std_env.rattan_ns.enter().unwrap();
205-
let _left_pair_guard = _std_env.left_pair.clone();
206-
let _right_pair_guard = _std_env.right_pair.clone();
203+
let original_ns = std_env.rattan_ns.enter().unwrap();
204+
let _left_pair_guard = std_env.left_pair.clone();
205+
let _right_pair_guard = std_env.right_pair.clone();
207206
let runtime = tokio::runtime::Builder::new_current_thread()
208207
.enable_io()
209208
.enable_time()
@@ -215,10 +214,10 @@ fn main() {
215214
let rng = StdRng::seed_from_u64(42);
216215

217216
let left_device = VirtualEthernet::<StdPacket, AfPacketDriver>::new(
218-
_std_env.left_pair.right.clone(),
217+
std_env.left_pair.right.clone(),
219218
);
220219
let right_device = VirtualEthernet::<StdPacket, AfPacketDriver>::new(
221-
_std_env.right_pair.left.clone(),
220+
std_env.right_pair.left.clone(),
222221
);
223222

224223
let (left_device_rx, left_device_tx) = machine.add_device(left_device);
@@ -449,39 +448,48 @@ fn main() {
449448
machine.link_device(right_fd[i * 2], right_fd[i * 2 + 1]);
450449
}
451450

452-
let config = RattanMachineConfig {
453-
original_ns,
454-
port: 8086,
455-
};
451+
// get the last byte of rattan_base as the port number
452+
let port = CONFIG_PORT_BASE - 1
453+
+ match rattan_base {
454+
std::net::IpAddr::V4(ip) => ip.octets()[3],
455+
std::net::IpAddr::V6(ip) => ip.octets()[15],
456+
} as u16;
457+
458+
let config = RattanMachineConfig { original_ns, port };
456459
machine.core_loop(config).await
457460
}
458461
.in_current_span(),
459462
);
460463
});
461464

462465
// Test connectivity before starting
463-
let res = {
464-
let _span = span!(Level::INFO, "ping_test").entered();
465-
info!("ping testing...");
466+
let res = if opts.commands.is_empty() {
467+
info!("ping {} testing...", rattan_base);
466468
let _left_ns_guard = NetNsGuard::new(left_ns.clone()).unwrap();
467469
let handle = std::process::Command::new("ping")
468-
.args(["192.168.12.1", "-c", "5", "-i", "0.2"])
470+
.args([&rattan_base.to_string(), "-c", "3", "-i", "0.2"])
469471
.stdout(std::process::Stdio::piped())
470472
.spawn()
471473
.unwrap();
472474
let output = handle.wait_with_output().unwrap();
473475
let stdout = String::from_utf8(output.stdout).unwrap();
474476
stdout.contains("time=")
477+
} else {
478+
// skip ping test if commands are provided
479+
true
475480
};
476481
match res {
477482
true => {
478-
info!("ping test passed");
479483
left_ns.enter().unwrap();
480-
sleep(Duration::from_secs(1));
481-
let mut client_handle = std::process::Command::new("/bin/bash");
482-
if !opts.commands.is_empty() {
483-
client_handle.arg("-c").args(opts.commands);
484+
std::thread::sleep(std::time::Duration::from_secs(1));
485+
let mut client_handle = std::process::Command::new("/usr/bin/env");
486+
client_handle.env("RATTAN_BASE", rattan_base.to_string());
487+
if opts.commands.is_empty() {
488+
client_handle.arg("bash");
489+
} else {
490+
client_handle.args(opts.commands);
484491
}
492+
info!("Running {:?}", client_handle);
485493
let mut client_handle = client_handle
486494
.stdin(Stdio::inherit())
487495
.stdout(Stdio::inherit())

rattan/src/core.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,27 +120,28 @@ where
120120
#[cfg(feature = "control")]
121121
let token_dup = self.token.clone();
122122
#[cfg(feature = "control")]
123-
let control_thread_span = span!(Level::DEBUG, "control_thread").or_current();
123+
let control_thread_span = span!(Level::INFO, "control_thread").or_current();
124124

125125
#[cfg(feature = "control")]
126126
let control_thread = std::thread::spawn(move || {
127127
let _entered = control_thread_span.entered();
128128
config.original_ns.enter().unwrap();
129129
info!("control thread started");
130130

131+
#[cfg(feature = "http")]
131132
let rt = tokio::runtime::Builder::new_current_thread()
132133
.enable_all()
133134
.build()
134135
.unwrap();
136+
#[cfg(feature = "http")]
135137
rt.block_on(async move {
136-
#[cfg(feature = "http")]
137138
let server =
138139
axum::Server::bind(&format!("127.0.0.1:{}", config.port).parse().unwrap())
139140
.serve(router_clone.into_make_service())
140141
.with_graceful_shutdown(async {
141142
token_dup.cancelled().await;
142143
});
143-
#[cfg(feature = "http")]
144+
info!("Listening on http://127.0.0.1:{}", config.port);
144145
match server.await {
145146
Ok(_) => {}
146147
Err(e) => {

rattan/src/devices/bandwidth/mod.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::fmt::Debug;
1010
use std::sync::Arc;
1111
use tokio::sync::mpsc;
1212
use tokio::time::{Duration, Instant};
13-
use tracing::{debug, info, warn};
13+
use tracing::{debug, info, trace, warn};
1414

1515
use super::{ControlInterface, Egress, Ingress};
1616

@@ -341,12 +341,12 @@ where
341341
Q: PacketQueue<P>,
342342
{
343343
fn change_bandwidth(&mut self, bandwidth: Bandwidth, change_time: Instant) {
344-
debug!(
344+
trace!(
345345
"Changing bandwidth to {:?} (should at {:?} ago)",
346346
bandwidth,
347347
change_time.elapsed()
348348
);
349-
debug!(
349+
trace!(
350350
"Previous next_available distance: {:?}",
351351
self.next_available - change_time
352352
);
@@ -357,12 +357,12 @@ where
357357
+ (self.next_available - change_time)
358358
.mul_f64(self.current_bandwidth.as_bps() as f64 / bandwidth.as_bps() as f64)
359359
};
360-
debug!(
360+
trace!(
361361
before = ?self.current_bandwidth,
362362
after = ?bandwidth,
363363
"Set inner bandwidth:"
364364
);
365-
debug!(
365+
trace!(
366366
"Now next_available distance: {:?}",
367367
self.next_available - change_time
368368
);
@@ -371,12 +371,13 @@ where
371371

372372
fn set_config(&mut self, config: BwReplayDeviceConfig<P, Q>) {
373373
if let Some(trace_config) = config.trace_config {
374+
debug!("Set inner trace config");
374375
self.trace = trace_config.into_model();
375376
match self.trace.next_bw() {
376377
Some((bandwidth, duration)) => {
377378
self.change_bandwidth(bandwidth, Instant::now());
378379
self.next_change = Instant::now() + duration;
379-
debug!(
380+
trace!(
380381
"Bandwidth change to {:?}, next change after {:?}",
381382
bandwidth,
382383
self.next_change - Instant::now()
@@ -401,7 +402,7 @@ where
401402
let next_bw = self.trace.next_bw();
402403
next_bw.map(|(bandwidth, duration)| {
403404
self.change_bandwidth(bandwidth, change_time);
404-
debug!(
405+
trace!(
405406
"Bandwidth changed to {:?}, next change after {:?}",
406407
bandwidth,
407408
change_time + duration - Instant::now()

rattan/src/env.rs

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,42 @@ use std::{
1616
net::{IpAddr, Ipv4Addr},
1717
str::FromStr,
1818
};
19-
use tracing::{debug, info, instrument, span, trace, Level};
19+
use tracing::{debug, error, info, instrument, span, trace, Level};
2020

2121
// ns-client ns-rattan ns-server
2222
// +-----------+ veth pair +--------------------+ veth pair +-----------+
2323
// | rc-left| <-------------> |rc-right [P] rs-left| <-------------> |rs-right |
24-
// | .11.1/24| |.11.2/24 .12.2/24| |.12.1/24 |
24+
// | .11.x/32| |.11.2/32 .12.2/32| |.12.x/32 |
2525
// +-----------+ +--------------------+ +-----------+
26+
//
27+
// Use /32 to avoid route conflict between multiple rattan instances
28+
//
29+
30+
fn get_addresses_in_use() -> Vec<IpAddr> {
31+
debug!("Get addresses in use");
32+
let rt = tokio::runtime::Builder::new_current_thread()
33+
.enable_all()
34+
.build()
35+
.unwrap();
36+
let _guard = rt.enter();
37+
let (conn, rtnl_handle, _) = rtnetlink::new_connection().unwrap();
38+
rt.spawn(conn);
39+
40+
let mut addresses = vec![];
41+
rt.block_on(async {
42+
let mut links = rtnl_handle.address().get().execute();
43+
while let Ok(Some(address_msg)) = links.try_next().await {
44+
for address_attr in address_msg.attributes {
45+
if let AddressAttribute::Address(address) = address_attr {
46+
debug!(?address, ?address_msg.header.prefix_len, "Get address");
47+
addresses.push(address);
48+
}
49+
}
50+
}
51+
});
52+
debug!(?addresses, "Addresses in use");
53+
addresses
54+
}
2655

2756
lazy_static::lazy_static! {
2857
static ref STD_ENV_LOCK: Arc<parking_lot::Mutex<()>> = Arc::new(parking_lot::Mutex::new(()));
@@ -59,6 +88,7 @@ pub struct StdNetEnv {
5988
#[instrument(skip_all, level = "debug")]
6089
pub fn get_std_env(config: StdNetEnvConfig) -> anyhow::Result<StdNetEnv> {
6190
trace!(?config);
91+
get_addresses_in_use();
6292
let _guard = STD_ENV_LOCK.lock();
6393
let rand_string: String = thread_rng()
6494
.sample_iter(&Alphanumeric)
@@ -79,19 +109,45 @@ pub fn get_std_env(config: StdNetEnvConfig) -> anyhow::Result<StdNetEnv> {
79109
let rattan_netns = NetNs::new(&rattan_netns_name)?;
80110
trace!(?rattan_netns, "Rattan netns {} created", rattan_netns_name);
81111

112+
// Get server veth address
113+
let veth_addr_suffix = match config.mode {
114+
StdNetEnvMode::Compatible => {
115+
let addresses_in_use = get_addresses_in_use();
116+
let mut addr_suffix = 1;
117+
while addresses_in_use.contains(&IpAddr::V4(Ipv4Addr::new(192, 168, 12, addr_suffix)))
118+
|| addresses_in_use.contains(&IpAddr::V4(Ipv4Addr::new(192, 168, 11, addr_suffix)))
119+
{
120+
addr_suffix += 1;
121+
if addr_suffix == 2 {
122+
addr_suffix += 1;
123+
}
124+
if addr_suffix == 255 {
125+
error!("No available address suffix for server veth");
126+
return Err(anyhow::anyhow!(
127+
"No available address suffix for server veth"
128+
));
129+
}
130+
}
131+
addr_suffix
132+
}
133+
_ => 1,
134+
};
82135
let veth_pair_client = VethPairBuilder::new()
83136
.name(
84137
format!("rc-left-{}", rand_string),
85138
format!("rc-right-{}", rand_string),
86139
)
87140
.namespace(Some(client_netns.clone()), Some(rattan_netns.clone()))
88141
.mac_addr(
89-
[0x38, 0x7e, 0x58, 0xe7, 0x87, 0x2a].into(),
90-
[0x38, 0x7e, 0x58, 0xe7, 0x87, 0x2b].into(),
142+
[0x38, 0x7e, 0x58, 0xe7, 11, veth_addr_suffix].into(),
143+
[0x38, 0x7e, 0x58, 0xe7, 11, 2].into(),
91144
)
92145
.ip_addr(
93-
(IpAddr::V4(Ipv4Addr::new(192, 168, 11, 1)), 24),
94-
(IpAddr::V4(Ipv4Addr::new(192, 168, 11, 2)), 24),
146+
(
147+
IpAddr::V4(Ipv4Addr::new(192, 168, 11, veth_addr_suffix)),
148+
32,
149+
),
150+
(IpAddr::V4(Ipv4Addr::new(192, 168, 11, 2)), 32),
95151
)
96152
.build()?;
97153

@@ -102,12 +158,15 @@ pub fn get_std_env(config: StdNetEnvConfig) -> anyhow::Result<StdNetEnv> {
102158
)
103159
.namespace(Some(rattan_netns.clone()), Some(server_netns.clone()))
104160
.mac_addr(
105-
[0x38, 0x7e, 0x58, 0xe7, 0x87, 0x2c].into(),
106-
[0x38, 0x7e, 0x58, 0xe7, 0x87, 0x2d].into(),
161+
[0x38, 0x7e, 0x58, 0xe7, 12, 2].into(),
162+
[0x38, 0x7e, 0x58, 0xe7, 12, veth_addr_suffix].into(),
107163
)
108164
.ip_addr(
109-
(IpAddr::V4(Ipv4Addr::new(192, 168, 12, 2)), 24),
110-
(IpAddr::V4(Ipv4Addr::new(192, 168, 12, 1)), 24),
165+
(IpAddr::V4(Ipv4Addr::new(192, 168, 12, 2)), 32),
166+
(
167+
IpAddr::V4(Ipv4Addr::new(192, 168, 12, veth_addr_suffix)),
168+
32,
169+
),
111170
)
112171
.build()?;
113172

0 commit comments

Comments
 (0)