bridge.c revision 262151
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: stable/10/tools/tools/netmap/bridge.c 262151 2014-02-18 05:01:04Z luigi $
10 */
11
12#include <stdio.h>
13#define NETMAP_WITH_LIBS
14#include <net/netmap_user.h>
15#include <sys/poll.h>
16
17int verbose = 0;
18
19static int do_abort = 0;
20static int zerocopy = 1; /* enable zerocopy if possible */
21
22static void
23sigint_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 */
34int
35pkt_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 */
54static int
55process_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			D("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 > 2048) {
85			D("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		} else {
99			char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx);
100			char *txbuf = NETMAP_BUF(txring, ts->buf_idx);
101			nm_pkt_copy(rxbuf, txbuf, ts->len);
102		}
103		j = nm_ring_next(rxring, j);
104		k = nm_ring_next(txring, k);
105	}
106	rxring->head = rxring->cur = j;
107	txring->head = txring->cur = k;
108	if (verbose && m > 0)
109		D("%s sent %d packets to %p", msg, m, txring);
110
111	return (m);
112}
113
114/* move packts from src to destination */
115static int
116move(struct nm_desc *src, struct nm_desc *dst, u_int limit)
117{
118	struct netmap_ring *txring, *rxring;
119	u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring;
120	const char *msg = (src->req.nr_ringid & NETMAP_SW_RING) ?
121		"host->net" : "net->host";
122
123	while (si <= src->last_rx_ring && di <= dst->last_tx_ring) {
124		rxring = NETMAP_RXRING(src->nifp, si);
125		txring = NETMAP_TXRING(dst->nifp, di);
126		ND("txring %p rxring %p", txring, rxring);
127		if (nm_ring_empty(rxring)) {
128			si++;
129			continue;
130		}
131		if (nm_ring_empty(txring)) {
132			di++;
133			continue;
134		}
135		m += process_rings(rxring, txring, limit, msg);
136	}
137
138	return (m);
139}
140
141
142static void
143usage(void)
144{
145	fprintf(stderr,
146	    "usage: bridge [-v] [-i ifa] [-i ifb] [-b burst] [-w wait_time] [iface]\n");
147	exit(1);
148}
149
150/*
151 * bridge [-v] if1 [if2]
152 *
153 * If only one name, or the two interfaces are the same,
154 * bridges userland and the adapter. Otherwise bridge
155 * two intefaces.
156 */
157int
158main(int argc, char **argv)
159{
160	struct pollfd pollfd[2];
161	int ch;
162	u_int burst = 1024, wait_link = 4;
163	struct nm_desc *pa = NULL, *pb = NULL;
164	char *ifa = NULL, *ifb = NULL;
165	char ifabuf[64] = { 0 };
166
167	fprintf(stderr, "%s built %s %s\n",
168		argv[0], __DATE__, __TIME__);
169
170	while ( (ch = getopt(argc, argv, "b:ci:vw:")) != -1) {
171		switch (ch) {
172		default:
173			D("bad option %c %s", ch, optarg);
174			usage();
175			break;
176		case 'b':	/* burst */
177			burst = atoi(optarg);
178			break;
179		case 'i':	/* interface */
180			if (ifa == NULL)
181				ifa = optarg;
182			else if (ifb == NULL)
183				ifb = optarg;
184			else
185				D("%s ignored, already have 2 interfaces",
186					optarg);
187			break;
188		case 'c':
189			zerocopy = 0; /* do not zerocopy */
190			break;
191		case 'v':
192			verbose++;
193			break;
194		case 'w':
195			wait_link = atoi(optarg);
196			break;
197		}
198
199	}
200
201	argc -= optind;
202	argv += optind;
203
204	if (argc > 1)
205		ifa = argv[1];
206	if (argc > 2)
207		ifb = argv[2];
208	if (argc > 3)
209		burst = atoi(argv[3]);
210	if (!ifb)
211		ifb = ifa;
212	if (!ifa) {
213		D("missing interface");
214		usage();
215	}
216	if (burst < 1 || burst > 8192) {
217		D("invalid burst %d, set to 1024", burst);
218		burst = 1024;
219	}
220	if (wait_link > 100) {
221		D("invalid wait_link %d, set to 4", wait_link);
222		wait_link = 4;
223	}
224	if (!strcmp(ifa, ifb)) {
225		D("same interface, endpoint 0 goes to host");
226		snprintf(ifabuf, sizeof(ifabuf) - 1, "%s^", ifa);
227		ifa = ifabuf;
228	} else {
229		/* two different interfaces. Take all rings on if1 */
230	}
231	pa = nm_open(ifa, NULL, 0, NULL);
232	if (pa == NULL) {
233		D("cannot open %s", ifa);
234		return (1);
235	}
236	// XXX use a single mmap ?
237	pb = nm_open(ifb, NULL, NM_OPEN_NO_MMAP, pa);
238	if (pb == NULL) {
239		D("cannot open %s", ifb);
240		nm_close(pa);
241		return (1);
242	}
243	zerocopy = zerocopy && (pa->mem == pb->mem);
244	D("------- zerocopy %ssupported", zerocopy ? "" : "NOT ");
245
246	/* setup poll(2) variables. */
247	memset(pollfd, 0, sizeof(pollfd));
248	pollfd[0].fd = pa->fd;
249	pollfd[1].fd = pb->fd;
250
251	D("Wait %d secs for link to come up...", wait_link);
252	sleep(wait_link);
253	D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.",
254		pa->req.nr_name, pa->first_rx_ring, pa->req.nr_rx_rings,
255		pb->req.nr_name, pb->first_rx_ring, pb->req.nr_rx_rings);
256
257	/* main loop */
258	signal(SIGINT, sigint_h);
259	while (!do_abort) {
260		int n0, n1, ret;
261		pollfd[0].events = pollfd[1].events = 0;
262		pollfd[0].revents = pollfd[1].revents = 0;
263		n0 = pkt_queued(pa, 0);
264		n1 = pkt_queued(pb, 0);
265		if (n0)
266			pollfd[1].events |= POLLOUT;
267		else
268			pollfd[0].events |= POLLIN;
269		if (n1)
270			pollfd[0].events |= POLLOUT;
271		else
272			pollfd[1].events |= POLLIN;
273		ret = poll(pollfd, 2, 2500);
274		if (ret <= 0 || verbose)
275		    D("poll %s [0] ev %x %x rx %d@%d tx %d,"
276			     " [1] ev %x %x rx %d@%d tx %d",
277				ret <= 0 ? "timeout" : "ok",
278				pollfd[0].events,
279				pollfd[0].revents,
280				pkt_queued(pa, 0),
281				NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->cur,
282				pkt_queued(pa, 1),
283				pollfd[1].events,
284				pollfd[1].revents,
285				pkt_queued(pb, 0),
286				NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->cur,
287				pkt_queued(pb, 1)
288			);
289		if (ret < 0)
290			continue;
291		if (pollfd[0].revents & POLLERR) {
292			struct netmap_ring *rx = NETMAP_RXRING(pa->nifp, pa->cur_rx_ring);
293			D("error on fd0, rx [%d,%d,%d)",
294				rx->head, rx->cur, rx->tail);
295		}
296		if (pollfd[1].revents & POLLERR) {
297			struct netmap_ring *rx = NETMAP_RXRING(pb->nifp, pb->cur_rx_ring);
298			D("error on fd1, rx [%d,%d,%d)",
299				rx->head, rx->cur, rx->tail);
300		}
301		if (pollfd[0].revents & POLLOUT) {
302			move(pb, pa, burst);
303			// XXX we don't need the ioctl */
304			// ioctl(me[0].fd, NIOCTXSYNC, NULL);
305		}
306		if (pollfd[1].revents & POLLOUT) {
307			move(pa, pb, burst);
308			// XXX we don't need the ioctl */
309			// ioctl(me[1].fd, NIOCTXSYNC, NULL);
310		}
311	}
312	D("exiting");
313	nm_close(pb);
314	nm_close(pa);
315
316	return (0);
317}
318