xref: /freebsd/tools/tools/netmap/bridge.c (revision 10ff414c)
1 /*
2  * (C) 2011-2014 Luigi Rizzo, Matteo Landi
3  *
4  * BSD license
5  *
6  * A netmap application to bridge two network interfaces,
7  * or one interface and the host stack.
8  *
9  * $FreeBSD$
10  */
11 
12 #include <libnetmap.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <sys/poll.h>
16 #include <sys/ioctl.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 
20 #if defined(_WIN32)
21 #define BUSYWAIT
22 #endif
23 
24 static int verbose = 0;
25 
26 static int do_abort = 0;
27 static int zerocopy = 1; /* enable zerocopy if possible */
28 
29 static void
30 sigint_h(int sig)
31 {
32 	(void)sig;	/* UNUSED */
33 	do_abort = 1;
34 	signal(SIGINT, SIG_DFL);
35 }
36 
37 
38 /*
39  * How many slots do we (user application) have on this
40  * set of queues ?
41  */
42 static int
43 rx_slots_avail(struct nmport_d *d)
44 {
45 	u_int i, tot = 0;
46 
47 	for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) {
48 		tot += nm_ring_space(NETMAP_RXRING(d->nifp, i));
49 	}
50 
51 	return tot;
52 }
53 
54 static int
55 tx_slots_avail(struct nmport_d *d)
56 {
57 	u_int i, tot = 0;
58 
59 	for (i = d->first_tx_ring; i <= d->last_tx_ring; i++) {
60 		tot += nm_ring_space(NETMAP_TXRING(d->nifp, i));
61 	}
62 
63 	return tot;
64 }
65 
66 /*
67  * Move up to 'limit' pkts from rxring to txring, swapping buffers
68  * if zerocopy is possible. Otherwise fall back on packet copying.
69  */
70 static int
71 rings_move(struct netmap_ring *rxring, struct netmap_ring *txring,
72 	      u_int limit, const char *msg)
73 {
74 	u_int j, k, m = 0;
75 
76 	/* print a warning if any of the ring flags is set (e.g. NM_REINIT) */
77 	if (rxring->flags || txring->flags)
78 		D("%s rxflags %x txflags %x",
79 		    msg, rxring->flags, txring->flags);
80 	j = rxring->head; /* RX */
81 	k = txring->head; /* TX */
82 	m = nm_ring_space(rxring);
83 	if (m < limit)
84 		limit = m;
85 	m = nm_ring_space(txring);
86 	if (m < limit)
87 		limit = m;
88 	m = limit;
89 	while (limit-- > 0) {
90 		struct netmap_slot *rs = &rxring->slot[j];
91 		struct netmap_slot *ts = &txring->slot[k];
92 
93 		if (ts->buf_idx < 2 || rs->buf_idx < 2) {
94 			RD(2, "wrong index rxr[%d] = %d  -> txr[%d] = %d",
95 			    j, rs->buf_idx, k, ts->buf_idx);
96 			sleep(2);
97 		}
98 		/* Copy the packet length. */
99 		if (rs->len > rxring->nr_buf_size) {
100 			RD(2,  "%s: invalid len %u, rxr[%d] -> txr[%d]",
101 			    msg, rs->len, j, k);
102 			rs->len = 0;
103 		} else if (verbose > 1) {
104 			D("%s: fwd len %u, rx[%d] -> tx[%d]",
105 			    msg, rs->len, j, k);
106 		}
107 		ts->len = rs->len;
108 		if (zerocopy) {
109 			uint32_t pkt = ts->buf_idx;
110 			ts->buf_idx = rs->buf_idx;
111 			rs->buf_idx = pkt;
112 			/* report the buffer change. */
113 			ts->flags |= NS_BUF_CHANGED;
114 			rs->flags |= NS_BUF_CHANGED;
115 		} else {
116 			char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx);
117 			char *txbuf = NETMAP_BUF(txring, ts->buf_idx);
118 			nm_pkt_copy(rxbuf, txbuf, ts->len);
119 		}
120 		/*
121 		 * Copy the NS_MOREFRAG from rs to ts, leaving any
122 		 * other flags unchanged.
123 		 */
124 		ts->flags = (ts->flags & ~NS_MOREFRAG) | (rs->flags & NS_MOREFRAG);
125 		j = nm_ring_next(rxring, j);
126 		k = nm_ring_next(txring, k);
127 	}
128 	rxring->head = rxring->cur = j;
129 	txring->head = txring->cur = k;
130 	if (verbose && m > 0)
131 		D("%s fwd %d packets: rxring %u --> txring %u",
132 		    msg, m, rxring->ringid, txring->ringid);
133 
134 	return (m);
135 }
136 
137 /* Move packets from source port to destination port. */
138 static int
139 ports_move(struct nmport_d *src, struct nmport_d *dst, u_int limit,
140 	const char *msg)
141 {
142 	struct netmap_ring *txring, *rxring;
143 	u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring;
144 
145 	while (si <= src->last_rx_ring && di <= dst->last_tx_ring) {
146 		rxring = NETMAP_RXRING(src->nifp, si);
147 		txring = NETMAP_TXRING(dst->nifp, di);
148 		if (nm_ring_empty(rxring)) {
149 			si++;
150 			continue;
151 		}
152 		if (nm_ring_empty(txring)) {
153 			di++;
154 			continue;
155 		}
156 		m += rings_move(rxring, txring, limit, msg);
157 	}
158 
159 	return (m);
160 }
161 
162 
163 static void
164 usage(void)
165 {
166 	fprintf(stderr,
167 		"netmap bridge program: forward packets between two "
168 			"netmap ports\n"
169 		"    usage(1): bridge [-v] [-i ifa] [-i ifb] [-b burst] "
170 			"[-w wait_time] [-L]\n"
171 		"    usage(2): bridge [-v] [-w wait_time] [-L] "
172 			"[ifa [ifb [burst]]]\n"
173 		"\n"
174 		"    ifa and ifb are specified using the nm_open() syntax.\n"
175 		"    When ifb is missing (or is equal to ifa), bridge will\n"
176 		"    forward between between ifa and the host stack if -L\n"
177 		"    is not specified, otherwise loopback traffic on ifa.\n"
178 		"\n"
179 		"    example: bridge -w 10 -i netmap:eth3 -i netmap:eth1\n"
180 		"\n"
181 		"    If ifa and ifb are two interfaces, they must be in\n"
182 		"    promiscuous mode. Otherwise, if bridging with the \n"
183 		"    host stack, the interface must have the offloads \n"
184 		"    disabled.\n"
185 		);
186 	exit(1);
187 }
188 
189 /*
190  * bridge [-v] if1 [if2]
191  *
192  * If only one name, or the two interfaces are the same,
193  * bridges userland and the adapter. Otherwise bridge
194  * two intefaces.
195  */
196 int
197 main(int argc, char **argv)
198 {
199 	char msg_a2b[256], msg_b2a[256];
200 	struct pollfd pollfd[2];
201 	u_int burst = 1024, wait_link = 4;
202 	struct nmport_d *pa = NULL, *pb = NULL;
203 	char *ifa = NULL, *ifb = NULL;
204 	char ifabuf[64] = { 0 };
205 	int pa_sw_rings, pb_sw_rings;
206 	int loopback = 0;
207 	int ch;
208 
209 	fprintf(stderr, "%s built %s %s\n\n", argv[0], __DATE__, __TIME__);
210 
211 	while ((ch = getopt(argc, argv, "hb:ci:vw:L")) != -1) {
212 		switch (ch) {
213 		default:
214 			D("bad option %c %s", ch, optarg);
215 			/* fallthrough */
216 		case 'h':
217 			usage();
218 			break;
219 		case 'b':	/* burst */
220 			burst = atoi(optarg);
221 			break;
222 		case 'i':	/* interface */
223 			if (ifa == NULL)
224 				ifa = optarg;
225 			else if (ifb == NULL)
226 				ifb = optarg;
227 			else
228 				D("%s ignored, already have 2 interfaces",
229 					optarg);
230 			break;
231 		case 'c':
232 			zerocopy = 0; /* do not zerocopy */
233 			break;
234 		case 'v':
235 			verbose++;
236 			break;
237 		case 'w':
238 			wait_link = atoi(optarg);
239 			break;
240 		case 'L':
241 			loopback = 1;
242 			break;
243 		}
244 
245 	}
246 
247 	argc -= optind;
248 	argv += optind;
249 
250 	if (argc > 0)
251 		ifa = argv[0];
252 	if (argc > 1)
253 		ifb = argv[1];
254 	if (argc > 2)
255 		burst = atoi(argv[2]);
256 	if (!ifb)
257 		ifb = ifa;
258 	if (!ifa) {
259 		D("missing interface");
260 		usage();
261 	}
262 	if (burst < 1 || burst > 8192) {
263 		D("invalid burst %d, set to 1024", burst);
264 		burst = 1024;
265 	}
266 	if (wait_link > 100) {
267 		D("invalid wait_link %d, set to 4", wait_link);
268 		wait_link = 4;
269 	}
270 	if (!strcmp(ifa, ifb)) {
271 		if (!loopback) {
272 			D("same interface, endpoint 0 goes to host");
273 			snprintf(ifabuf, sizeof(ifabuf) - 1, "%s^", ifa);
274 			ifa = ifabuf;
275 		} else {
276 			D("same interface, loopbacking traffic");
277 		}
278 	} else {
279 		/* two different interfaces. Take all rings on if1 */
280 	}
281 	pa = nmport_open(ifa);
282 	if (pa == NULL) {
283 		D("cannot open %s", ifa);
284 		return (1);
285 	}
286 	/* try to reuse the mmap() of the first interface, if possible */
287 	pb = nmport_open(ifb);
288 	if (pb == NULL) {
289 		D("cannot open %s", ifb);
290 		nmport_close(pa);
291 		return (1);
292 	}
293 	zerocopy = zerocopy && (pa->mem == pb->mem);
294 	D("------- zerocopy %ssupported", zerocopy ? "" : "NOT ");
295 
296 	/* setup poll(2) array */
297 	memset(pollfd, 0, sizeof(pollfd));
298 	pollfd[0].fd = pa->fd;
299 	pollfd[1].fd = pb->fd;
300 
301 	D("Wait %d secs for link to come up...", wait_link);
302 	sleep(wait_link);
303 	D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.",
304 		pa->hdr.nr_name, pa->first_rx_ring, pa->reg.nr_rx_rings,
305 		pb->hdr.nr_name, pb->first_rx_ring, pb->reg.nr_rx_rings);
306 
307 	pa_sw_rings = (pa->reg.nr_mode == NR_REG_SW ||
308 	    pa->reg.nr_mode == NR_REG_ONE_SW);
309 	pb_sw_rings = (pb->reg.nr_mode == NR_REG_SW ||
310 	    pb->reg.nr_mode == NR_REG_ONE_SW);
311 
312 	snprintf(msg_a2b, sizeof(msg_a2b), "%s:%s --> %s:%s",
313 			pa->hdr.nr_name, pa_sw_rings ? "host" : "nic",
314 			pb->hdr.nr_name, pb_sw_rings ? "host" : "nic");
315 
316 	snprintf(msg_b2a, sizeof(msg_b2a), "%s:%s --> %s:%s",
317 			pb->hdr.nr_name, pb_sw_rings ? "host" : "nic",
318 			pa->hdr.nr_name, pa_sw_rings ? "host" : "nic");
319 
320 	/* main loop */
321 	signal(SIGINT, sigint_h);
322 	while (!do_abort) {
323 		int n0, n1, ret;
324 		pollfd[0].events = pollfd[1].events = 0;
325 		pollfd[0].revents = pollfd[1].revents = 0;
326 		n0 = rx_slots_avail(pa);
327 		n1 = rx_slots_avail(pb);
328 #ifdef BUSYWAIT
329 		if (n0) {
330 			pollfd[1].revents = POLLOUT;
331 		} else {
332 			ioctl(pollfd[0].fd, NIOCRXSYNC, NULL);
333 		}
334 		if (n1) {
335 			pollfd[0].revents = POLLOUT;
336 		} else {
337 			ioctl(pollfd[1].fd, NIOCRXSYNC, NULL);
338 		}
339 		ret = 1;
340 #else  /* !defined(BUSYWAIT) */
341 		if (n0)
342 			pollfd[1].events |= POLLOUT;
343 		else
344 			pollfd[0].events |= POLLIN;
345 		if (n1)
346 			pollfd[0].events |= POLLOUT;
347 		else
348 			pollfd[1].events |= POLLIN;
349 
350 		/* poll() also cause kernel to txsync/rxsync the NICs */
351 		ret = poll(pollfd, 2, 2500);
352 #endif /* !defined(BUSYWAIT) */
353 		if (ret <= 0 || verbose)
354 		    D("poll %s [0] ev %x %x rx %d@%d tx %d,"
355 			     " [1] ev %x %x rx %d@%d tx %d",
356 				ret <= 0 ? "timeout" : "ok",
357 				pollfd[0].events,
358 				pollfd[0].revents,
359 				rx_slots_avail(pa),
360 				NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->head,
361 				tx_slots_avail(pa),
362 				pollfd[1].events,
363 				pollfd[1].revents,
364 				rx_slots_avail(pb),
365 				NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->head,
366 				tx_slots_avail(pb)
367 			);
368 		if (ret < 0)
369 			continue;
370 		if (pollfd[0].revents & POLLERR) {
371 			struct netmap_ring *rx = NETMAP_RXRING(pa->nifp, pa->cur_rx_ring);
372 			D("error on fd0, rx [%d,%d,%d)",
373 			    rx->head, rx->cur, rx->tail);
374 		}
375 		if (pollfd[1].revents & POLLERR) {
376 			struct netmap_ring *rx = NETMAP_RXRING(pb->nifp, pb->cur_rx_ring);
377 			D("error on fd1, rx [%d,%d,%d)",
378 			    rx->head, rx->cur, rx->tail);
379 		}
380 		if (pollfd[0].revents & POLLOUT) {
381 			ports_move(pb, pa, burst, msg_b2a);
382 #ifdef BUSYWAIT
383 			ioctl(pollfd[0].fd, NIOCTXSYNC, NULL);
384 #endif
385 		}
386 
387 		if (pollfd[1].revents & POLLOUT) {
388 			ports_move(pa, pb, burst, msg_a2b);
389 #ifdef BUSYWAIT
390 			ioctl(pollfd[1].fd, NIOCTXSYNC, NULL);
391 #endif
392 		}
393 
394 		/*
395 		 * We don't need ioctl(NIOCTXSYNC) on the two file descriptors.
396 		 * here. The kernel will txsync on next poll().
397 		 */
398 	}
399 	nmport_close(pb);
400 	nmport_close(pa);
401 
402 	return (0);
403 }
404