@@ -0,0 +1,68 @@
1#!/usr/bin/env python3
2#
3# the host side of xv6's nettest. QEMU's user-mode network (slirp)
4# shows the host to xv6 as 10.0.2.2, and forwards the host's UDP
5# port FWDPORT (see the Makefile) to xv6's port 2000.
6#
7# python3 nettest.py echo PORT echo every UDP packet that
8# arrives at PORT back to its sender;
9# then run "nettest PORT" in xv6.
10# python3 nettest.py ping FWDPORT N first run "nettest server N" in
11# xv6; then send N packets to it,
12# one at a time, and time each
13# round trip.
14#
15
16import socket
17import sys
18import time
19
20
21def echo(port, idle=60):
22 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
23 s.bind(("127.0.0.1", port))
24 s.settimeout(idle)
25 n = 0
26 print(f"nettest.py: echoing on port {port}", flush=True) 27 try:
28 while True:
31 n += 1
32 except socket.timeout:
33 pass
34 print(f"nettest.py: echoed {n} packets, then {idle} s without one", flush=True) 35
36
37def ping(port, n):
38 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
39 s.settimeout(5)
40 rtts = []
41 for i in range(n):
42 msg = b"ping %d" % i
43 t0 = time.perf_counter()
44 s.sendto(msg, ("127.0.0.1", port))
45 try:
46 data, _ = s.recvfrom(4096) 47 except socket.timeout:
48 print(f"nettest.py: ping {i}: no reply in 5 s: FAIL") 50 rtts.append(time.perf_counter() - t0)
52 print(f"nettest.py: ping {i}: wrong reply {data!r}: FAIL") 54 rtts.sort()
55 us = lambda x: round(x * 1e6)
56 print(f"nettest.py: {n} round trips: OK") 57 print(f"nettest.py: round trip in microseconds: min {us(rtts[0])}, " 58 f"median {us(rtts[n // 2])}, 99th percentile {us(rtts[n * 99 // 100])}, "
59 f"max {us(rtts[-1])}")
60
61
65 ping(int(sys.argv[2]), int(sys.argv[3])) 66else:
67 print("usage: nettest.py echo PORT [IDLE] | nettest.py ping FWDPORT N") @@ -0,0 +1,295 @@
1//
2// test the e1000 driver, the network stack and UDP sockets
3// against nettest.py on the host. see the comment there.
4//
5// host: python3 nettest.py echo PORT then xv6: nettest PORT
6// xv6: nettest server N then host: python3 nettest.py ping FWDPORT N
7//
8
9#include "kernel/types.h"
10#include "kernel/stat.h"
11#include "user/user.h"
12
13#define HOST MAKE_IP(10, 0, 2, 2) // the host, as slirp shows it to xv6
14#define DNS MAKE_IP(10, 0, 2, 3) // slirp's DNS server
15#define MAKE_IP(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
16
17int hostport;
18
19// the bytes of round seq of process id.
20void
21fill(char *buf, int id, int seq, int len) 22{
23 for (int i = 0; i < len; i++) 24 buf[i] = 'a' + (id * 7 + seq * 3 + i) % 26; 25}
26
27// one round trip through the host's echo server from local
28// port lport. returns 0 if the echo is exactly what was sent.
29int
30roundtrip(int lport, int id, int seq, int len) 31{
32 static char out[1500], in[1500];
33 uint32 src;
34 uint16 sport;
35
36 fill(out, id, seq, len); 37 if (send(HOST, hostport, lport, out, len) < 0) { 38 printf("nettest: send failed\n"); 39 return -1;
40 }
41 int n = recv(lport, &src, &sport, in, sizeof(in));
42 if (n != len || src != HOST || sport != hostport || memcmp(in, out, len)) { 43 printf("nettest: process %d round %d: bad echo (%d bytes, from 0x%x:%d)\n", 44 id, seq, n, src, sport);
45 return -1;
46 }
47 return 0;
48}
49
50// n round trips from lport with lengths from 1 to 1472 bytes.
51int
52roundtrips(int lport, int id, int n)
53{
54 unbind(lport); // in case a failed earlier run left it bound
55 if (bind(lport) < 0) {
56 printf("nettest: bind %d failed\n", lport); 57 return -1;
58 }
59 for (int seq = 0; seq < n; seq++) {
60 if (roundtrip(lport, id, seq, 1 + (seq * 97 + id * 31) % 1472) < 0)
61 return -1;
62 }
63 unbind(lport);
64 return 0;
65}
66
67// run f(arg) in a child; kill it after timeout ticks.
68// returns the child's exit status, or -1 if it was killed.
69int
70run(int (*f)(int), int arg, int timeout) 71{
74 exit(f(arg) == 0 ? 0 : 1); 76 if (dog == 0) {
80 }
81 // wait for both; the order in which they exit varies.
82 int xs = -1, st, w;
83 while ((w = wait(&st)) >= 0) { 85 xs = st;
87 }
88 }
89 return xs;
90}
91
92int
93one(int arg)
94{
95 return roundtrips(2001, 0, 1);
96}
97
98int
99many(int arg)
100{
101 return roundtrips(2002, 0, 100);
102}
103
104int
105pingpong(int n)
106{
107 unbind(2003);
108 if (bind(2003) < 0)
109 return -1;
110 for (int seq = 0; seq < n; seq++) {
111 if (roundtrip(2003, 0, seq, 64) < 0)
112 return -1;
113 }
114 unbind(2003);
115 return 0;
116}
117
118// three processes, one per hart, each with its own port.
119int
120parallel(int npkt)
121{
122 for (int i = 0; i < 3; i++) {
124 exit(roundtrips(2010 + i, i + 1, npkt) == 0 ? 0 : 1); 125 }
126 int ok = 1;
127 for (int i = 0; i < 3; i++) {
128 int xs;
130 if (xs != 0)
131 ok = 0;
132 }
133 return ok ? 0 : -1;
134}
135
136// recv() on a port nobody sends to sleeps; kill() must end it.
137int
138killrecv(int arg)
139{
141 uint32 src;
142 uint16 sport;
143
144 unbind(2020);
145 if (bind(2020) < 0)
146 return -1;
149 recv(2020, &src, &sport, buf, sizeof(buf)); 150 exit(0); // not reached if recv() returns because of the kill 151 }
154 int xs;
156 unbind(2020);
157 return xs == -1 ? 0 : -1;
158}
159
160// ask slirp's DNS server for pdos.csail.mit.edu's address.
161int
162dns(int arg)
163{
164 static char q[64], buf[512]; 165 uchar *r = (uchar *)buf; 166 char *name = "\4pdos\5csail\3mit\3edu"; 167 uint32 src;
168 uint16 sport;
169
170 // header: id 0x1234, recursion desired, one question.
172 q[0] = 0x12;
173 q[1] = 0x34;
174 q[2] = 0x01;
175 q[5] = 1;
176 int n = 12;
179 q[n + 1] = 1; // type A
180 q[n + 3] = 1; // class IN
181 n += 4;
182
183 unbind(2030);
184 if (bind(2030) < 0)
185 return -1;
186 if (send(DNS, 53, 2030, q, n) < 0)
187 return -1;
188 int len = recv(2030, &src, &sport, buf, sizeof(buf)); 189 unbind(2030);
190 if (len < 12 || src != DNS || sport != 53 || r[0] != 0x12 || r[1] != 0x34 || 191 (r[3] & 0xf) != 0) {
192 printf("nettest: dns: bad reply (%d bytes)\n", len); 193 return -1;
194 }
195
196 // skip the question (the same name, type and class), then look
197 // for an A record among the answers.
198 int nans = (r[6] << 8) | r[7];
199 int i = n;
200 for (int a = 0; a < nans && i + 12 <= len; a++) { 201 if ((r[i] & 0xc0) == 0xc0) {
202 i += 2; // the name, compressed
203 } else {
204 while (i < len && r[i] != 0) 205 i += r[i] + 1;
206 i += 1;
207 }
208 int type = (r[i] << 8) | r[i + 1]; 209 int rdlen = (r[i + 8] << 8) | r[i + 9];
210 i += 10;
211 if (type == 1 && rdlen == 4 && i + 4 <= len) { 212 printf("nettest: dns: pdos.csail.mit.edu is %d.%d.%d.%d\n", r[i], 213 r[i + 1], r[i + 2], r[i + 3]);
214 return 0;
215 }
216 i += rdlen;
217 }
218 printf("nettest: dns: no A record in the reply\n"); 219 return -1;
220}
221
222void
223check(char *what, int xs, int *ok)
224{
225 if (xs == 0) {
226 printf("nettest: %s: OK\n", what); 227 } else {
228 printf("nettest: %s: FAIL%s\n", what, xs == -1 ? " (timed out)" : ""); 229 *ok = 0;
230 }
231}
232
233// echo n packets that arrive at port 2000 back to their sender.
234void
235server(int n)
236{
237 static char buf[1500]; 238 uint32 src;
239 uint16 sport;
240
241 unbind(2000);
242 if (bind(2000) < 0) {
243 printf("nettest server: bind 2000 failed\n"); 245 }
246 printf("nettest server: echoing %d packets on port 2000\n", n); 247 for (int i = 0; i < n; i++) {
248 int len = recv(2000, &src, &sport, buf, sizeof(buf)); 249 if (len < 0 || send(src, sport, 2000, buf, len) < 0) { 250 printf("nettest server: failed\n"); 252 }
253 }
254 unbind(2000);
255 printf("nettest server: done\n"); 257}
258
259int
261{
262 int ok = 1;
263
264 if (argc == 3 && strcmp(argv[1], "server") == 0) 266 if (argc != 2) {
267 printf("usage: nettest hostport | nettest server n\n"); 269 }
271
272 check("one round trip", run(one, 0, 50), &ok); 273 check("100 round trips", run(many, 0, 100), &ok); 274 check("3 processes x 300 round trips", run(parallel, 300, 600), &ok); 275 check("kill wakes recv", run(killrecv, 0, 50), &ok); 276 int dnsok = 1;
277 check("dns query to 10.0.2.3", run(dns, 0, 50), &dnsok); 278
279 // latency, in clock ticks (about 0.1 s each).
281 int xs = run(pingpong, 1000, 600); 283 if (xs == 0)
284 printf("nettest: 1000 round trips of 64 bytes took %d ticks\n", t1 - t0); 285 else
286 check("1000 round trips of 64 bytes", xs, &ok);
287
288 if (ok && dnsok)
289 printf("nettest: ALL OK\n"); 290 else if (ok)
291 printf("nettest: ALL OK except dns\n"); 292 else
293 printf("nettest: SOME TESTS FAILED\n"); 295}