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