1 //! 6lowpan benchmark exmaple
2 //!
3 //! This example runs a simple TCP throughput benchmark using the 6lowpan implementation in smoltcp
4 //! It is designed to run using the Linux ieee802154/6lowpan support,
5 //! using mac802154_hwsim.
6 //!
7 //! mac802154_hwsim allows you to create multiple "virtual" radios and specify
8 //! which is in range with which. This is very useful for testing without
9 //! needing real hardware. By default it creates two interfaces `wpan0` and
10 //! `wpan1` that are in range with each other. You can customize this with
11 //! the `wpan-hwsim` tool.
12 //!
13 //! We'll configure Linux to speak 6lowpan on `wpan0`, and leave `wpan1`
14 //! unconfigured so smoltcp can use it with a raw socket.
15 //!
16 //!
17 //!
18 //!
19 //!
20 //! # Setup
21 //!
22 //! modprobe mac802154_hwsim
23 //!
24 //! ip link set wpan0 down
25 //! ip link set wpan1 down
26 //! iwpan dev wpan0 set pan_id 0xbeef
27 //! iwpan dev wpan1 set pan_id 0xbeef
28 //! ip link add link wpan0 name lowpan0 type lowpan
29 //! ip link set wpan0 up
30 //! ip link set wpan1 up
31 //! ip link set lowpan0 up
32 //!
33 //!
34 //! # Running
35 //!
36 //! Compile with `cargo build --release --example sixlowpan_benchmark`
37 //! Run it with `sudo ./target/release/examples/sixlowpan_benchmark [reader|writer]`.
38 //!
39 //! # Teardown
40 //!
41 //! rmmod mac802154_hwsim
42 //!
43
44 mod utils;
45
46 use std::os::unix::io::AsRawFd;
47 use std::str;
48
49 use smoltcp::iface::{Config, Interface, SocketSet};
50 use smoltcp::phy::{wait as phy_wait, Medium, RawSocket};
51 use smoltcp::socket::tcp;
52 use smoltcp::wire::{Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr};
53
54 //For benchmark
55 use smoltcp::time::{Duration, Instant};
56 use std::cmp;
57 use std::io::{Read, Write};
58 use std::net::SocketAddrV6;
59 use std::net::TcpStream;
60 use std::sync::atomic::{AtomicBool, Ordering};
61 use std::thread;
62
63 use std::fs;
64
if_nametoindex(ifname: &str) -> u3265 fn if_nametoindex(ifname: &str) -> u32 {
66 let contents = fs::read_to_string(format!("/sys/devices/virtual/net/{ifname}/ifindex"))
67 .expect("couldn't read interface from \"/sys/devices/virtual/net\"")
68 .replace('\n', "");
69 contents.parse::<u32>().unwrap()
70 }
71
72 const AMOUNT: usize = 100_000_000;
73
74 enum Client {
75 Reader,
76 Writer,
77 }
78
client(kind: Client)79 fn client(kind: Client) {
80 let port: u16 = match kind {
81 Client::Reader => 1234,
82 Client::Writer => 1235,
83 };
84
85 let scope_id = if_nametoindex("lowpan0");
86
87 let socket_addr = SocketAddrV6::new(
88 "fe80:0:0:0:180b:4242:4242:4242".parse().unwrap(),
89 port,
90 0,
91 scope_id,
92 );
93
94 let mut stream = TcpStream::connect(socket_addr).expect("failed to connect TLKAGMKA");
95 let mut buffer = vec![0; 1_000_000];
96
97 let start = Instant::now();
98
99 let mut processed = 0;
100 while processed < AMOUNT {
101 let length = cmp::min(buffer.len(), AMOUNT - processed);
102 let result = match kind {
103 Client::Reader => stream.read(&mut buffer[..length]),
104 Client::Writer => stream.write(&buffer[..length]),
105 };
106 match result {
107 Ok(0) => break,
108 Ok(result) => {
109 // print!("(P:{})", result);
110 processed += result
111 }
112 Err(err) => panic!("cannot process: {err}"),
113 }
114 }
115
116 let end = Instant::now();
117
118 let elapsed = (end - start).total_millis() as f64 / 1000.0;
119
120 println!("throughput: {:.3} Gbps", AMOUNT as f64 / elapsed / 0.125e9);
121
122 CLIENT_DONE.store(true, Ordering::SeqCst);
123 }
124
125 static CLIENT_DONE: AtomicBool = AtomicBool::new(false);
126
main()127 fn main() {
128 #[cfg(feature = "log")]
129 utils::setup_logging("info");
130
131 let (mut opts, mut free) = utils::create_options();
132 utils::add_middleware_options(&mut opts, &mut free);
133 free.push("MODE");
134
135 let mut matches = utils::parse_options(&opts, free);
136
137 let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap();
138
139 let fd = device.as_raw_fd();
140 let mut device =
141 utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
142
143 let mode = match matches.free[0].as_ref() {
144 "reader" => Client::Reader,
145 "writer" => Client::Writer,
146 _ => panic!("invalid mode"),
147 };
148
149 // Create interface
150 let mut config = Config::new();
151 config.random_seed = rand::random();
152 config.hardware_addr =
153 Some(Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into());
154 config.pan_id = Some(Ieee802154Pan(0xbeef));
155
156 let mut iface = Interface::new(config, &mut device);
157 iface.update_ip_addrs(|ip_addrs| {
158 ip_addrs
159 .push(IpCidr::new(
160 IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242),
161 64,
162 ))
163 .unwrap();
164 });
165
166 let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
167 let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
168 let tcp1_socket = tcp::Socket::new(tcp1_rx_buffer, tcp1_tx_buffer);
169
170 let tcp2_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
171 let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
172 let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer);
173
174 let mut sockets = SocketSet::new(vec![]);
175 let tcp1_handle = sockets.add(tcp1_socket);
176 let tcp2_handle = sockets.add(tcp2_socket);
177
178 let default_timeout = Some(Duration::from_millis(1000));
179
180 thread::spawn(move || client(mode));
181 let mut processed = 0;
182
183 while !CLIENT_DONE.load(Ordering::SeqCst) {
184 let timestamp = Instant::now();
185 iface.poll(timestamp, &mut device, &mut sockets);
186
187 // tcp:1234: emit data
188 let socket = sockets.get_mut::<tcp::Socket>(tcp1_handle);
189 if !socket.is_open() {
190 socket.listen(1234).unwrap();
191 }
192
193 if socket.can_send() && processed < AMOUNT {
194 let length = socket
195 .send(|buffer| {
196 let length = cmp::min(buffer.len(), AMOUNT - processed);
197 (length, length)
198 })
199 .unwrap();
200 processed += length;
201 }
202
203 // tcp:1235: sink data
204 let socket = sockets.get_mut::<tcp::Socket>(tcp2_handle);
205 if !socket.is_open() {
206 socket.listen(1235).unwrap();
207 }
208
209 if socket.can_recv() && processed < AMOUNT {
210 let length = socket
211 .recv(|buffer| {
212 let length = cmp::min(buffer.len(), AMOUNT - processed);
213 (length, length)
214 })
215 .unwrap();
216 processed += length;
217 }
218
219 match iface.poll_at(timestamp, &sockets) {
220 Some(poll_at) if timestamp < poll_at => {
221 phy_wait(fd, Some(poll_at - timestamp)).expect("wait error");
222 }
223 Some(_) => (),
224 None => {
225 phy_wait(fd, default_timeout).expect("wait error");
226 }
227 }
228 }
229 }
230