Changeset View
Changeset View
Standalone View
Standalone View
tools/tools/netmap/bridge.c
Context not available. | |||||
* | * | ||||
* BSD license | * BSD license | ||||
* | * | ||||
* A netmap application to bridge two network interfaces, | * A netmap client to bridge two network interfaces | ||||
* or one interface and the host stack. | * (or one interface and the host stack). | ||||
* | * | ||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include <libnetmap.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#define NETMAP_WITH_LIBS | |||||
#include <net/netmap_user.h> | |||||
#include <sys/poll.h> | #include <sys/poll.h> | ||||
#include <sys/ioctl.h> | |||||
#include <stdlib.h> | |||||
#include <unistd.h> | |||||
static int verbose = 0; | static int verbose = 0; | ||||
Context not available. | |||||
/* | /* | ||||
* How many slots do we (user application) have on this | * how many packets on this set of queues ? | ||||
* set of queues ? | |||||
*/ | */ | ||||
static int | static int | ||||
rx_slots_avail(struct nmport_d *d) | pkt_queued(struct nm_desc *d, int tx) | ||||
{ | { | ||||
u_int i, tot = 0; | u_int i, tot = 0; | ||||
for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) { | if (tx) { | ||||
tot += nm_ring_space(NETMAP_RXRING(d->nifp, i)); | for (i = d->first_tx_ring; i <= d->last_tx_ring; i++) { | ||||
} | tot += nm_ring_space(NETMAP_TXRING(d->nifp, i)); | ||||
} | |||||
return tot; | } else { | ||||
} | for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) { | ||||
tot += nm_ring_space(NETMAP_RXRING(d->nifp, i)); | |||||
static int | } | ||||
tx_slots_avail(struct nmport_d *d) | |||||
{ | |||||
u_int i, tot = 0; | |||||
for (i = d->first_tx_ring; i <= d->last_tx_ring; i++) { | |||||
tot += nm_ring_space(NETMAP_TXRING(d->nifp, i)); | |||||
} | } | ||||
return tot; | return tot; | ||||
} | } | ||||
/* | /* | ||||
* Move up to 'limit' pkts from rxring to txring, swapping buffers | * move up to 'limit' pkts from rxring to txring swapping buffers. | ||||
* if zerocopy is possible. Otherwise fall back on packet copying. | |||||
*/ | */ | ||||
static int | static int | ||||
rings_move(struct netmap_ring *rxring, struct netmap_ring *txring, | process_rings(struct netmap_ring *rxring, struct netmap_ring *txring, | ||||
u_int limit, const char *msg) | u_int limit, const char *msg) | ||||
{ | { | ||||
u_int j, k, m = 0; | u_int j, k, m = 0; | ||||
Context not available. | |||||
/* print a warning if any of the ring flags is set (e.g. NM_REINIT) */ | /* print a warning if any of the ring flags is set (e.g. NM_REINIT) */ | ||||
if (rxring->flags || txring->flags) | if (rxring->flags || txring->flags) | ||||
D("%s rxflags %x txflags %x", | D("%s rxflags %x txflags %x", | ||||
msg, rxring->flags, txring->flags); | msg, rxring->flags, txring->flags); | ||||
j = rxring->head; /* RX */ | j = rxring->cur; /* RX */ | ||||
k = txring->head; /* TX */ | k = txring->cur; /* TX */ | ||||
m = nm_ring_space(rxring); | m = nm_ring_space(rxring); | ||||
if (m < limit) | if (m < limit) | ||||
limit = m; | limit = m; | ||||
Context not available. | |||||
/* swap packets */ | /* swap packets */ | ||||
if (ts->buf_idx < 2 || rs->buf_idx < 2) { | if (ts->buf_idx < 2 || rs->buf_idx < 2) { | ||||
RD(2, "wrong index rxr[%d] = %d -> txr[%d] = %d", | RD(5, "wrong index rx[%d] = %d -> tx[%d] = %d", | ||||
j, rs->buf_idx, k, ts->buf_idx); | j, rs->buf_idx, k, ts->buf_idx); | ||||
sleep(2); | sleep(2); | ||||
} | } | ||||
/* copy the packet length. */ | /* copy the packet length. */ | ||||
if (rs->len > rxring->nr_buf_size) { | if (rs->len > rxring->nr_buf_size) { | ||||
RD(2, "%s: invalid len %u, rxr[%d] -> txr[%d]", | RD(5, "wrong len %d rx[%d] -> tx[%d]", rs->len, j, k); | ||||
msg, rs->len, j, k); | |||||
rs->len = 0; | rs->len = 0; | ||||
} else if (verbose > 1) { | } else if (verbose > 1) { | ||||
D("%s: fwd len %u, rx[%d] -> tx[%d]", | D("%s send len %d rx[%d] -> tx[%d]", msg, rs->len, j, k); | ||||
msg, rs->len, j, k); | |||||
} | } | ||||
ts->len = rs->len; | ts->len = rs->len; | ||||
if (zerocopy) { | if (zerocopy) { | ||||
Context not available. | |||||
rxring->head = rxring->cur = j; | rxring->head = rxring->cur = j; | ||||
txring->head = txring->cur = k; | txring->head = txring->cur = k; | ||||
if (verbose && m > 0) | if (verbose && m > 0) | ||||
D("%s fwd %d packets: rxring %u --> txring %u", | D("%s sent %d packets to %p", msg, m, txring); | ||||
msg, m, rxring->ringid, txring->ringid); | |||||
return (m); | return (m); | ||||
} | } | ||||
/* Move packets from source port to destination port. */ | /* move packts from src to destination */ | ||||
static int | static int | ||||
ports_move(struct nmport_d *src, struct nmport_d *dst, u_int limit, | move(struct nm_desc *src, struct nm_desc *dst, u_int limit) | ||||
const char *msg) | |||||
{ | { | ||||
struct netmap_ring *txring, *rxring; | struct netmap_ring *txring, *rxring; | ||||
u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring; | u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring; | ||||
const char *msg = (src->req.nr_flags == NR_REG_SW) ? | |||||
"host->net" : "net->host"; | |||||
while (si <= src->last_rx_ring && di <= dst->last_tx_ring) { | while (si <= src->last_rx_ring && di <= dst->last_tx_ring) { | ||||
rxring = NETMAP_RXRING(src->nifp, si); | rxring = NETMAP_RXRING(src->nifp, si); | ||||
txring = NETMAP_TXRING(dst->nifp, di); | txring = NETMAP_TXRING(dst->nifp, di); | ||||
ND("txring %p rxring %p", txring, rxring); | |||||
if (nm_ring_empty(rxring)) { | if (nm_ring_empty(rxring)) { | ||||
si++; | si++; | ||||
continue; | continue; | ||||
Context not available. | |||||
di++; | di++; | ||||
continue; | continue; | ||||
} | } | ||||
m += rings_move(rxring, txring, limit, msg); | m += process_rings(rxring, txring, limit, msg); | ||||
} | } | ||||
return (m); | return (m); | ||||
Context not available. | |||||
{ | { | ||||
fprintf(stderr, | fprintf(stderr, | ||||
"netmap bridge program: forward packets between two " | "netmap bridge program: forward packets between two " | ||||
"netmap ports\n" | "network interfaces\n" | ||||
" usage(1): bridge [-v] [-i ifa] [-i ifb] [-b burst] " | " usage(1): bridge [-v] [-i ifa] [-i ifb] [-b burst] " | ||||
"[-w wait_time] [-L]\n" | "[-w wait_time] [-L]\n" | ||||
" usage(2): bridge [-v] [-w wait_time] [-L] " | " usage(2): bridge [-v] [-w wait_time] [-L] " | ||||
Context not available. | |||||
" is not specified, otherwise loopback traffic on ifa.\n" | " is not specified, otherwise loopback traffic on ifa.\n" | ||||
"\n" | "\n" | ||||
" example: bridge -w 10 -i netmap:eth3 -i netmap:eth1\n" | " example: bridge -w 10 -i netmap:eth3 -i netmap:eth1\n" | ||||
"\n" | |||||
" If ifa and ifb are two interfaces, they must be in\n" | |||||
" promiscuous mode. Otherwise, if bridging with the \n" | |||||
" host stack, the interface must have the offloads \n" | |||||
" disabled.\n" | |||||
); | ); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
Context not available. | |||||
int | int | ||||
main(int argc, char **argv) | main(int argc, char **argv) | ||||
{ | { | ||||
char msg_a2b[128], msg_b2a[128]; | |||||
struct pollfd pollfd[2]; | struct pollfd pollfd[2]; | ||||
int ch; | |||||
u_int burst = 1024, wait_link = 4; | u_int burst = 1024, wait_link = 4; | ||||
struct nmport_d *pa = NULL, *pb = NULL; | struct nm_desc *pa = NULL, *pb = NULL; | ||||
char *ifa = NULL, *ifb = NULL; | char *ifa = NULL, *ifb = NULL; | ||||
char ifabuf[64] = { 0 }; | char ifabuf[64] = { 0 }; | ||||
int pa_sw_rings, pb_sw_rings; | |||||
int loopback = 0; | int loopback = 0; | ||||
int ch; | |||||
fprintf(stderr, "%s built %s %s\n\n", argv[0], __DATE__, __TIME__); | fprintf(stderr, "%s built %s %s\n\n", argv[0], __DATE__, __TIME__); | ||||
Context not available. | |||||
} else { | } else { | ||||
/* two different interfaces. Take all rings on if1 */ | /* two different interfaces. Take all rings on if1 */ | ||||
} | } | ||||
pa = nmport_open(ifa); | pa = nm_open(ifa, NULL, 0, NULL); | ||||
if (pa == NULL) { | if (pa == NULL) { | ||||
D("cannot open %s", ifa); | D("cannot open %s", ifa); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* try to reuse the mmap() of the first interface, if possible */ | /* try to reuse the mmap() of the first interface, if possible */ | ||||
pb = nmport_open(ifb); | pb = nm_open(ifb, NULL, NM_OPEN_NO_MMAP, pa); | ||||
if (pb == NULL) { | if (pb == NULL) { | ||||
D("cannot open %s", ifb); | D("cannot open %s", ifb); | ||||
nmport_close(pa); | nm_close(pa); | ||||
return (1); | return (1); | ||||
} | } | ||||
zerocopy = zerocopy && (pa->mem == pb->mem); | zerocopy = zerocopy && (pa->mem == pb->mem); | ||||
Context not available. | |||||
D("Wait %d secs for link to come up...", wait_link); | D("Wait %d secs for link to come up...", wait_link); | ||||
sleep(wait_link); | sleep(wait_link); | ||||
D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.", | D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.", | ||||
pa->hdr.nr_name, pa->first_rx_ring, pa->reg.nr_rx_rings, | pa->req.nr_name, pa->first_rx_ring, pa->req.nr_rx_rings, | ||||
pb->hdr.nr_name, pb->first_rx_ring, pb->reg.nr_rx_rings); | pb->req.nr_name, pb->first_rx_ring, pb->req.nr_rx_rings); | ||||
pa_sw_rings = (pa->reg.nr_mode == NR_REG_SW || | |||||
pa->reg.nr_mode == NR_REG_ONE_SW); | |||||
pb_sw_rings = (pb->reg.nr_mode == NR_REG_SW || | |||||
pb->reg.nr_mode == NR_REG_ONE_SW); | |||||
snprintf(msg_a2b, sizeof(msg_a2b), "%s:%s --> %s:%s", | |||||
pa->hdr.nr_name, pa_sw_rings ? "host" : "nic", | |||||
pb->hdr.nr_name, pb_sw_rings ? "host" : "nic"); | |||||
snprintf(msg_b2a, sizeof(msg_b2a), "%s:%s --> %s:%s", | |||||
pb->hdr.nr_name, pb_sw_rings ? "host" : "nic", | |||||
pa->hdr.nr_name, pa_sw_rings ? "host" : "nic"); | |||||
/* main loop */ | /* main loop */ | ||||
signal(SIGINT, sigint_h); | signal(SIGINT, sigint_h); | ||||
Context not available. | |||||
int n0, n1, ret; | int n0, n1, ret; | ||||
pollfd[0].events = pollfd[1].events = 0; | pollfd[0].events = pollfd[1].events = 0; | ||||
pollfd[0].revents = pollfd[1].revents = 0; | pollfd[0].revents = pollfd[1].revents = 0; | ||||
n0 = rx_slots_avail(pa); | n0 = pkt_queued(pa, 0); | ||||
n1 = rx_slots_avail(pb); | n1 = pkt_queued(pb, 0); | ||||
#if defined(_WIN32) || defined(BUSYWAIT) | #if defined(_WIN32) || defined(BUSYWAIT) | ||||
if (n0) { | if (n0) { | ||||
ioctl(pollfd[1].fd, NIOCTXSYNC, NULL); | ioctl(pollfd[1].fd, NIOCTXSYNC, NULL); | ||||
Context not available. | |||||
ret <= 0 ? "timeout" : "ok", | ret <= 0 ? "timeout" : "ok", | ||||
pollfd[0].events, | pollfd[0].events, | ||||
pollfd[0].revents, | pollfd[0].revents, | ||||
rx_slots_avail(pa), | pkt_queued(pa, 0), | ||||
NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->head, | NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->cur, | ||||
tx_slots_avail(pa), | pkt_queued(pa, 1), | ||||
pollfd[1].events, | pollfd[1].events, | ||||
pollfd[1].revents, | pollfd[1].revents, | ||||
rx_slots_avail(pb), | pkt_queued(pb, 0), | ||||
NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->head, | NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->cur, | ||||
tx_slots_avail(pb) | pkt_queued(pb, 1) | ||||
); | ); | ||||
if (ret < 0) | if (ret < 0) | ||||
continue; | continue; | ||||
if (pollfd[0].revents & POLLERR) { | if (pollfd[0].revents & POLLERR) { | ||||
struct netmap_ring *rx = NETMAP_RXRING(pa->nifp, pa->cur_rx_ring); | struct netmap_ring *rx = NETMAP_RXRING(pa->nifp, pa->cur_rx_ring); | ||||
D("error on fd0, rx [%d,%d,%d)", | D("error on fd0, rx [%d,%d,%d)", | ||||
rx->head, rx->cur, rx->tail); | rx->head, rx->cur, rx->tail); | ||||
} | } | ||||
if (pollfd[1].revents & POLLERR) { | if (pollfd[1].revents & POLLERR) { | ||||
struct netmap_ring *rx = NETMAP_RXRING(pb->nifp, pb->cur_rx_ring); | struct netmap_ring *rx = NETMAP_RXRING(pb->nifp, pb->cur_rx_ring); | ||||
D("error on fd1, rx [%d,%d,%d)", | D("error on fd1, rx [%d,%d,%d)", | ||||
rx->head, rx->cur, rx->tail); | rx->head, rx->cur, rx->tail); | ||||
} | } | ||||
if (pollfd[0].revents & POLLOUT) | if (pollfd[0].revents & POLLOUT) | ||||
ports_move(pb, pa, burst, msg_b2a); | move(pb, pa, burst); | ||||
if (pollfd[1].revents & POLLOUT) | if (pollfd[1].revents & POLLOUT) | ||||
ports_move(pa, pb, burst, msg_a2b); | move(pa, pb, burst); | ||||
/* | /* We don't need ioctl(NIOCTXSYNC) on the two file descriptors here, | ||||
* We don't need ioctl(NIOCTXSYNC) on the two file descriptors. | * kernel will txsync on next poll(). */ | ||||
* here. The kernel will txsync on next poll(). | |||||
*/ | |||||
} | } | ||||
nmport_close(pb); | nm_close(pb); | ||||
nmport_close(pa); | nm_close(pa); | ||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. |