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