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