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