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