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