PageRenderTime 1118ms CodeModel.GetById 30ms RepoModel.GetById 7ms app.codeStats 0ms

/tools/tools/netmap/bridge.c

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