]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/netmap/bridge.c
MFC r339659:
[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 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 }