]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/netmap/bridge.c
zfs: merge openzfs/zfs@c629f0bf6
[FreeBSD/FreeBSD.git] / tools / tools / netmap / bridge.c
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 }