]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - tools/tools/netmap/bridge.c
MFC: import netmap core files into RELENG_9.
[FreeBSD/stable/9.git] / tools / tools / netmap / bridge.c
1 /*
2  * (C) 2011 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 <errno.h>
13 #include <signal.h> /* signal */
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h> /* strcmp */
17 #include <fcntl.h> /* open */
18 #include <unistd.h> /* close */
19
20 #include <sys/endian.h> /* le64toh */
21 #include <sys/mman.h> /* PROT_* */
22 #include <sys/ioctl.h> /* ioctl */
23 #include <machine/param.h>
24 #include <sys/poll.h>
25 #include <sys/socket.h> /* sockaddr.. */
26 #include <arpa/inet.h> /* ntohs */
27
28 #include <net/if.h>     /* ifreq */
29 #include <net/ethernet.h>
30 #include <net/netmap.h>
31 #include <net/netmap_user.h>
32
33 #include <netinet/in.h> /* sockaddr_in */
34
35 #define MIN(a, b) ((a) < (b) ? (a) : (b))
36
37 int verbose = 0;
38
39 /* debug support */
40 #define ND(format, ...) {}
41 #define D(format, ...) do {                                     \
42         if (!verbose) break;                                    \
43         struct timeval _xxts;                                   \
44         gettimeofday(&_xxts, NULL);                             \
45         fprintf(stderr, "%03d.%06d %s [%d] " format "\n",       \
46         (int)_xxts.tv_sec %1000, (int)_xxts.tv_usec,            \
47         __FUNCTION__, __LINE__, ##__VA_ARGS__);                 \
48         } while (0)
49
50
51 char *version = "$Id: bridge.c 9642 2011-11-07 21:39:47Z luigi $";
52
53 static int do_abort = 0;
54
55 /*
56  * info on a ring we handle
57  */
58 struct my_ring {
59         const char *ifname;
60         int fd;
61         char *mem;                      /* userspace mmap address */
62         u_int memsize;
63         u_int queueid;
64         u_int begin, end;               /* first..last+1 rings to check */
65         struct netmap_if *nifp;
66         struct netmap_ring *tx, *rx;    /* shortcuts */
67
68         uint32_t if_flags;
69         uint32_t if_reqcap;
70         uint32_t if_curcap;
71 };
72
73 static void
74 sigint_h(__unused int sig)
75 {
76         do_abort = 1;
77         signal(SIGINT, SIG_DFL);
78 }
79
80
81 static int
82 do_ioctl(struct my_ring *me, int what)
83 {
84         struct ifreq ifr;
85         int error;
86
87         bzero(&ifr, sizeof(ifr));
88         strncpy(ifr.ifr_name, me->ifname, sizeof(ifr.ifr_name));
89         switch (what) {
90         case SIOCSIFFLAGS:
91                 ifr.ifr_flagshigh = me->if_flags >> 16;
92                 ifr.ifr_flags = me->if_flags & 0xffff;
93                 break;
94         case SIOCSIFCAP:
95                 ifr.ifr_reqcap = me->if_reqcap;
96                 ifr.ifr_curcap = me->if_curcap;
97                 break;
98         }
99         error = ioctl(me->fd, what, &ifr);
100         if (error) {
101                 D("ioctl error %d", what);
102                 return error;
103         }
104         switch (what) {
105         case SIOCGIFFLAGS:
106                 me->if_flags = (ifr.ifr_flagshigh << 16) |
107                         (0xffff & ifr.ifr_flags);
108                 if (verbose)
109                         D("flags are 0x%x", me->if_flags);
110                 break;
111
112         case SIOCGIFCAP:
113                 me->if_reqcap = ifr.ifr_reqcap;
114                 me->if_curcap = ifr.ifr_curcap;
115                 if (verbose)
116                         D("curcap are 0x%x", me->if_curcap);
117                 break;
118         }
119         return 0;
120 }
121
122 /*
123  * open a device. if me->mem is null then do an mmap.
124  */
125 static int
126 netmap_open(struct my_ring *me, int ringid)
127 {
128         int fd, err, l;
129         struct nmreq req;
130
131         me->fd = fd = open("/dev/netmap", O_RDWR);
132         if (fd < 0) {
133                 D("Unable to open /dev/netmap");
134                 return (-1);
135         }
136         bzero(&req, sizeof(req));
137         strncpy(req.nr_name, me->ifname, sizeof(req.nr_name));
138         req.nr_ringid = ringid;
139         err = ioctl(fd, NIOCGINFO, &req);
140         if (err) {
141                 D("cannot get info on %s", me->ifname);
142                 goto error;
143         }
144         me->memsize = l = req.nr_memsize;
145         if (verbose)
146                 D("memsize is %d MB", l>>20);
147         err = ioctl(fd, NIOCREGIF, &req);
148         if (err) {
149                 D("Unable to register %s", me->ifname);
150                 goto error;
151         }
152
153         if (me->mem == NULL) {
154                 me->mem = mmap(0, l, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
155                 if (me->mem == MAP_FAILED) {
156                         D("Unable to mmap");
157                         me->mem = NULL;
158                         goto error;
159                 }
160         }
161
162         me->nifp = NETMAP_IF(me->mem, req.nr_offset);
163         me->queueid = ringid;
164         if (ringid & NETMAP_SW_RING) {
165                 me->begin = req.nr_numrings;
166                 me->end = me->begin + 1;
167         } else if (ringid & NETMAP_HW_RING) {
168                 me->begin = ringid & NETMAP_RING_MASK;
169                 me->end = me->begin + 1;
170         } else {
171                 me->begin = 0;
172                 me->end = req.nr_numrings;
173         }
174         me->tx = NETMAP_TXRING(me->nifp, me->begin);
175         me->rx = NETMAP_RXRING(me->nifp, me->begin);
176         return (0);
177 error:
178         close(me->fd);
179         return -1;
180 }
181
182
183 static int
184 netmap_close(struct my_ring *me)
185 {
186         D("");
187         if (me->mem)
188                 munmap(me->mem, me->memsize);
189         ioctl(me->fd, NIOCUNREGIF, NULL);
190         close(me->fd);
191         return (0);
192 }
193
194
195 /*
196  * move up to 'limit' pkts from rxring to txring swapping buffers.
197  */
198 static int
199 process_rings(struct netmap_ring *rxring, struct netmap_ring *txring,
200               u_int limit, const char *msg)
201 {
202         u_int j, k, m = 0;
203
204         /* print a warning if any of the ring flags is set (e.g. NM_REINIT) */
205         if (rxring->flags || txring->flags)
206                 D("%s rxflags %x txflags %x",
207                         msg, rxring->flags, txring->flags);
208         j = rxring->cur; /* RX */
209         k = txring->cur; /* TX */
210         if (rxring->avail < limit)
211                 limit = rxring->avail;
212         if (txring->avail < limit)
213                 limit = txring->avail;
214         m = limit;
215         while (limit-- > 0) {
216                 struct netmap_slot *rs = &rxring->slot[j];
217                 struct netmap_slot *ts = &txring->slot[k];
218                 uint32_t pkt;
219
220                 /* swap packets */
221                 if (ts->buf_idx < 2 || rs->buf_idx < 2) {
222                         D("wrong index rx[%d] = %d  -> tx[%d] = %d",
223                                 j, rs->buf_idx, k, ts->buf_idx);
224                         sleep(2);
225                 }
226                 pkt = ts->buf_idx;
227                 ts->buf_idx = rs->buf_idx;
228                 rs->buf_idx = pkt;
229
230                 /* copy the packet length. */
231                 if (rs->len < 14 || rs->len > 2048)
232                         D("wrong len %d rx[%d] -> tx[%d]", rs->len, j, k);
233                 else if (verbose > 1)
234                         D("send len %d rx[%d] -> tx[%d]", rs->len, j, k);
235                 ts->len = rs->len;
236
237                 /* report the buffer change. */
238                 ts->flags |= NS_BUF_CHANGED;
239                 rs->flags |= NS_BUF_CHANGED;
240                 j = NETMAP_RING_NEXT(rxring, j);
241                 k = NETMAP_RING_NEXT(txring, k);
242         }
243         rxring->avail -= m;
244         txring->avail -= m;
245         rxring->cur = j;
246         txring->cur = k;
247         if (verbose && m > 0)
248                 D("sent %d packets to %p", m, txring);
249
250         return (m);
251 }
252
253 /* move packts from src to destination */
254 static int
255 move(struct my_ring *src, struct my_ring *dst, u_int limit)
256 {
257         struct netmap_ring *txring, *rxring;
258         u_int m = 0, si = src->begin, di = dst->begin;
259         const char *msg = (src->queueid & NETMAP_SW_RING) ?
260                 "host->net" : "net->host";
261
262         while (si < src->end && di < dst->end) {
263                 rxring = NETMAP_RXRING(src->nifp, si);
264                 txring = NETMAP_TXRING(dst->nifp, di);
265                 ND("txring %p rxring %p", txring, rxring);
266                 if (rxring->avail == 0) {
267                         si++;
268                         continue;
269                 }
270                 if (txring->avail == 0) {
271                         di++;
272                         continue;
273                 }
274                 m += process_rings(rxring, txring, limit, msg);
275         }
276
277         return (m);
278 }
279
280 /*
281  * how many packets on this set of queues ?
282  */
283 static int
284 howmany(struct my_ring *me, int tx)
285 {
286         u_int i, tot = 0;
287
288         ND("me %p begin %d end %d", me, me->begin, me->end);
289         for (i = me->begin; i < me->end; i++) {
290                 struct netmap_ring *ring = tx ?
291                         NETMAP_TXRING(me->nifp, i) : NETMAP_RXRING(me->nifp, i);
292                 tot += ring->avail;
293         }
294         if (0 && verbose && tot && !tx)
295                 D("ring %s %s %s has %d avail at %d",
296                         me->ifname, tx ? "tx": "rx",
297                         me->end > me->nifp->ni_num_queues ?
298                                 "host":"net",
299                         tot, NETMAP_TXRING(me->nifp, me->begin)->cur);
300         return tot;
301 }
302
303 /*
304  * bridge [-v] if1 [if2]
305  *
306  * If only one name, or the two interfaces are the same,
307  * bridges userland and the adapter. Otherwise bridge
308  * two intefaces.
309  */
310 int
311 main(int argc, char **argv)
312 {
313         struct pollfd pollfd[2];
314         int i;
315         u_int burst = 1024;
316         struct my_ring me[2];
317
318         fprintf(stderr, "%s %s built %s %s\n",
319                 argv[0], version, __DATE__, __TIME__);
320
321         bzero(me, sizeof(me));
322
323         while (argc > 1 && !strcmp(argv[1], "-v")) {
324                 verbose++;
325                 argv++;
326                 argc--;
327         }
328
329         if (argc < 2 || argc > 4) {
330                 D("Usage: %s IFNAME1 [IFNAME2 [BURST]]", argv[0]);
331                 return (1);
332         }
333
334         /* setup netmap interface #1. */
335         me[0].ifname = argv[1];
336         if (argc == 2 || !strcmp(argv[1], argv[2])) {
337                 D("same interface, endpoint 0 goes to host");
338                 i = NETMAP_SW_RING;
339                 me[1].ifname = argv[1];
340         } else {
341                 /* two different interfaces. Take all rings on if1 */
342                 i = 0;  // all hw rings
343                 me[1].ifname = argv[2];
344         }
345         if (netmap_open(me, i))
346                 return (1);
347         me[1].mem = me[0].mem; /* copy the pointer, so only one mmap */
348         if (netmap_open(me+1, 0))
349                 return (1);
350
351         /* if bridging two interfaces, set promisc mode */
352         if (i != NETMAP_SW_RING) {
353                 do_ioctl(me, SIOCGIFFLAGS);
354                 if ((me[0].if_flags & IFF_UP) == 0) {
355                         D("%s is down, bringing up...", me[0].ifname);
356                         me[0].if_flags |= IFF_UP;
357                 }
358                 me[0].if_flags |= IFF_PPROMISC;
359                 do_ioctl(me, SIOCSIFFLAGS);
360
361                 do_ioctl(me+1, SIOCGIFFLAGS);
362                 me[1].if_flags |= IFF_PPROMISC;
363                 do_ioctl(me+1, SIOCSIFFLAGS);
364
365                 /* also disable checksums etc. */
366                 do_ioctl(me, SIOCGIFCAP);
367                 me[0].if_reqcap = me[0].if_curcap;
368                 me[0].if_reqcap &= ~(IFCAP_HWCSUM | IFCAP_TSO | IFCAP_TOE);
369                 do_ioctl(me+0, SIOCSIFCAP);
370         }
371         do_ioctl(me+1, SIOCGIFFLAGS);
372         if ((me[1].if_flags & IFF_UP) == 0) {
373                 D("%s is down, bringing up...", me[1].ifname);
374                 me[1].if_flags |= IFF_UP;
375         }
376         do_ioctl(me+1, SIOCSIFFLAGS);
377
378         do_ioctl(me+1, SIOCGIFCAP);
379         me[1].if_reqcap = me[1].if_curcap;
380         me[1].if_reqcap &= ~(IFCAP_HWCSUM | IFCAP_TSO | IFCAP_TOE);
381         do_ioctl(me+1, SIOCSIFCAP);
382         if (argc > 3)
383                 burst = atoi(argv[3]);  /* packets burst size. */
384
385         /* setup poll(2) variables. */
386         memset(pollfd, 0, sizeof(pollfd));
387         for (i = 0; i < 2; i++) {
388                 pollfd[i].fd = me[i].fd;
389                 pollfd[i].events = (POLLIN);
390         }
391
392         D("Wait 2 secs for link to come up...");
393         sleep(2);
394         D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.",
395                 me[0].ifname, me[0].queueid, me[0].nifp->ni_num_queues,
396                 me[1].ifname, me[1].queueid, me[1].nifp->ni_num_queues);
397
398         /* main loop */
399         signal(SIGINT, sigint_h);
400         while (!do_abort) {
401                 int n0, n1, ret;
402                 pollfd[0].events = pollfd[1].events = 0;
403                 pollfd[0].revents = pollfd[1].revents = 0;
404                 n0 = howmany(me, 0);
405                 n1 = howmany(me + 1, 0);
406                 if (n0)
407                         pollfd[1].events |= POLLOUT;
408                 else
409                         pollfd[0].events |= POLLIN;
410                 if (n1)
411                         pollfd[0].events |= POLLOUT;
412                 else
413                         pollfd[1].events |= POLLIN;
414                 ret = poll(pollfd, 2, 2500);
415                 if (ret <= 0 || verbose)
416                     D("poll %s [0] ev %x %x rx %d@%d tx %d,"
417                              " [1] ev %x %x rx %d@%d tx %d",
418                                 ret <= 0 ? "timeout" : "ok",
419                                 pollfd[0].events,
420                                 pollfd[0].revents,
421                                 howmany(me, 0),
422                                 me[0].rx->cur,
423                                 howmany(me, 1),
424                                 pollfd[1].events,
425                                 pollfd[1].revents,
426                                 howmany(me+1, 0),
427                                 me[1].rx->cur,
428                                 howmany(me+1, 1)
429                         );
430                 if (ret < 0)
431                         continue;
432                 if (pollfd[0].revents & POLLERR) {
433                         D("error on fd0, rxcur %d@%d",
434                                 me[0].rx->avail, me[0].rx->cur);
435                 }
436                 if (pollfd[1].revents & POLLERR) {
437                         D("error on fd1, rxcur %d@%d",
438                                 me[1].rx->avail, me[1].rx->cur);
439                 }
440                 if (pollfd[0].revents & POLLOUT) {
441                         move(me + 1, me, burst);
442                         // XXX we don't need the ioctl */
443                         // ioctl(me[0].fd, NIOCTXSYNC, NULL);
444                 }
445                 if (pollfd[1].revents & POLLOUT) {
446                         move(me, me + 1, burst);
447                         // XXX we don't need the ioctl */
448                         // ioctl(me[1].fd, NIOCTXSYNC, NULL);
449                 }
450         }
451         D("exiting");
452         netmap_close(me + 1);
453         netmap_close(me + 0);
454
455         return (0);
456 }