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