mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-17 15:27:36 +00:00
netmap_user.h:
add separate rx/tx ring indexes add ring specifier in nm_open device name netmap.c, netmap_vale.c more consistent errno numbers netmap_generic.c correctly handle failure in registering interfaces. tools/tools/netmap/ massive cleanup of the example programs (a lot of common code is now in netmap_user.h.) nm_util.[ch] are going away soon. pcap.c will also go when i commit the native netmap support for libpcap.
This commit is contained in:
parent
7855b0bd68
commit
f263752668
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=260700
@ -1052,7 +1052,7 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_adapter **na)
|
||||
* to use generic adapters, we cannot satisfy the request.
|
||||
*/
|
||||
if (!NETMAP_CAPABLE(ifp) && i == NETMAP_ADMODE_NATIVE)
|
||||
return EINVAL;
|
||||
return EOPNOTSUPP;
|
||||
|
||||
/* Otherwise, create a generic adapter and return it,
|
||||
* saving the previously used netmap adapter, if any.
|
||||
@ -1090,22 +1090,19 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_adapter **na)
|
||||
/*
|
||||
* MUST BE CALLED UNDER NMG_LOCK()
|
||||
*
|
||||
* get a refcounted reference to an interface.
|
||||
* Get a refcounted reference to a netmap adapter attached
|
||||
* to the interface specified by nmr.
|
||||
* This is always called in the execution of an ioctl().
|
||||
*
|
||||
* Return ENXIO if the interface does not exist, EINVAL if netmap
|
||||
* is not supported by the interface.
|
||||
* If successful, hold a reference.
|
||||
* Return ENXIO if the interface specified by the request does
|
||||
* not exist, ENOTSUP if netmap is not supported by the interface,
|
||||
* EBUSY if the interface is already attached to a bridge,
|
||||
* EINVAL if parameters are invalid, ENOMEM if needed resources
|
||||
* could not be allocated.
|
||||
* If successful, hold a reference to the netmap adapter.
|
||||
*
|
||||
* When the NIC is attached to a bridge, reference is managed
|
||||
* at na->na_bdg_refcount using ADD/DROP_BDG_REF() as well as
|
||||
* virtual ports. Hence, on the final DROP_BDG_REF(), the NIC
|
||||
* is detached from the bridge, then ifp's refcount is dropped (this
|
||||
* is equivalent to that ifp is destroyed in case of virtual ports.
|
||||
*
|
||||
* This function uses if_rele() when we want to prevent the NIC from
|
||||
* being detached from the bridge in error handling. But once refcount
|
||||
* is acquired by this function, it must be released using nm_if_rele().
|
||||
* No reference is kept on the real interface, which may then
|
||||
* disappear at any time.
|
||||
*/
|
||||
int
|
||||
netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
|
||||
@ -1135,7 +1132,7 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
|
||||
if (ret != NULL) {
|
||||
/* Users cannot use the NIC attached to a bridge directly */
|
||||
if (NETMAP_OWNED_BY_KERN(ret)) {
|
||||
error = EINVAL;
|
||||
error = EBUSY;
|
||||
goto out;
|
||||
}
|
||||
error = 0;
|
||||
|
@ -261,7 +261,7 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
|
||||
/* Prepare to intercept incoming traffic. */
|
||||
error = netmap_catch_rx(na, 1);
|
||||
if (error) {
|
||||
D("netdev_rx_handler_register() failed");
|
||||
D("netdev_rx_handler_register() failed (%d)", error);
|
||||
goto register_handler;
|
||||
}
|
||||
ifp->if_capenable |= IFCAP_NETMAP;
|
||||
@ -283,7 +283,11 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
|
||||
rate_ctx.refcount++;
|
||||
#endif /* RATE */
|
||||
|
||||
} else { /* Disable netmap mode. */
|
||||
} else if (na->tx_rings[0].tx_pool) {
|
||||
/* Disable netmap mode. We enter here only if the previous
|
||||
generic_netmap_register(na, 1) was successfull.
|
||||
If it was not, na->tx_rings[0].tx_pool was set to NULL by the
|
||||
error handling code below. */
|
||||
rtnl_lock();
|
||||
|
||||
ifp->if_capenable &= ~IFCAP_NETMAP;
|
||||
@ -322,7 +326,7 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
|
||||
#ifdef REG_RESET
|
||||
error = ifp->netdev_ops->ndo_open(ifp);
|
||||
if (error) {
|
||||
goto alloc_tx_pool;
|
||||
goto free_tx_pools;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -338,6 +342,11 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
|
||||
if (na->tx_rings[r].tx_pool[i])
|
||||
m_freem(na->tx_rings[r].tx_pool[i]);
|
||||
free(na->tx_rings[r].tx_pool, M_DEVBUF);
|
||||
na->tx_rings[r].tx_pool = NULL;
|
||||
}
|
||||
netmap_mitigation_cleanup(gna);
|
||||
for (r=0; r<na->num_rx_rings; r++) {
|
||||
mbq_safe_destroy(&na->rx_rings[r].rx_queue);
|
||||
}
|
||||
|
||||
return error;
|
||||
|
@ -515,7 +515,7 @@ netmap_get_bdg_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
|
||||
b = nm_find_bridge(name, create);
|
||||
if (b == NULL) {
|
||||
D("no bridges available for '%s'", name);
|
||||
return (ENXIO);
|
||||
return (create ? ENOMEM : ENXIO);
|
||||
}
|
||||
|
||||
/* Now we are sure that name starts with the bridge's name,
|
||||
@ -547,7 +547,7 @@ netmap_get_bdg_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
|
||||
needed = 2; /* in some cases we only need 1 */
|
||||
if (b->bdg_active_ports + needed >= NM_BDG_MAXPORTS) {
|
||||
D("bridge full %d, cannot create new port", b->bdg_active_ports);
|
||||
return EINVAL;
|
||||
return ENOMEM;
|
||||
}
|
||||
/* record the next two ports available, but do not allocate yet */
|
||||
cand = b->bdg_port_index[b->bdg_active_ports];
|
||||
@ -594,7 +594,7 @@ netmap_get_bdg_na(struct nmreq *nmr, struct netmap_adapter **na, int create)
|
||||
if (NETMAP_OWNED_BY_ANY(ret)) {
|
||||
D("NIC %s busy, cannot attach to bridge",
|
||||
NM_IFPNAME(ifp));
|
||||
error = EINVAL;
|
||||
error = EBUSY;
|
||||
goto out;
|
||||
}
|
||||
/* create a fake interface */
|
||||
@ -658,11 +658,13 @@ nm_bdg_attach(struct nmreq *nmr)
|
||||
npriv = malloc(sizeof(*npriv), M_DEVBUF, M_NOWAIT|M_ZERO);
|
||||
if (npriv == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
NMG_LOCK();
|
||||
/* XXX probably netmap_get_bdg_na() */
|
||||
|
||||
error = netmap_get_bdg_na(nmr, &na, 1 /* create if not exists */);
|
||||
if (error) /* no device, or another bridge or user owns the device */
|
||||
goto unlock_exit;
|
||||
|
||||
if (na == NULL) { /* VALE prefix missing */
|
||||
error = EINVAL;
|
||||
goto unlock_exit;
|
||||
@ -707,6 +709,7 @@ nm_bdg_detach(struct nmreq *nmr)
|
||||
if (error) { /* no device, or another bridge or user owns the device */
|
||||
goto unlock_exit;
|
||||
}
|
||||
|
||||
if (na == NULL) { /* VALE prefix missing */
|
||||
error = EINVAL;
|
||||
goto unlock_exit;
|
||||
@ -1945,7 +1948,7 @@ netmap_bwrap_notify(struct netmap_adapter *na, u_int ring_n, enum txrx tx, int f
|
||||
int error = 0;
|
||||
|
||||
if (tx == NR_TX)
|
||||
return ENXIO;
|
||||
return EINVAL;
|
||||
|
||||
kring = &na->rx_rings[ring_n];
|
||||
hw_kring = &hwna->tx_rings[ring_n];
|
||||
@ -1999,7 +2002,7 @@ netmap_bwrap_host_notify(struct netmap_adapter *na, u_int ring_n, enum txrx tx,
|
||||
struct netmap_bwrap_adapter *bna = na->na_private;
|
||||
struct netmap_adapter *port_na = &bna->up.up;
|
||||
if (tx == NR_TX || ring_n != 0)
|
||||
return ENXIO;
|
||||
return EINVAL;
|
||||
return netmap_bwrap_notify(port_na, port_na->num_rx_rings, NR_RX, flags);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,12 @@ NO_MAN=
|
||||
CFLAGS += -Werror -Wall -nostdinc -I/usr/include -I../../../sys
|
||||
CFLAGS += -Wextra
|
||||
|
||||
LDFLAGS += -lpthread -lpcap
|
||||
LDFLAGS += -lpthread
|
||||
.ifdef WITHOUT_PCAP
|
||||
CFLAGS += -DNO_PCAP
|
||||
.else
|
||||
LDFLAGS += -lpcap
|
||||
.endif
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -96,16 +96,16 @@ process_rings(struct netmap_ring *rxring, struct netmap_ring *txring,
|
||||
|
||||
/* move packts from src to destination */
|
||||
static int
|
||||
move(struct my_ring *src, struct my_ring *dst, u_int limit)
|
||||
move(struct nm_desc_t *src, struct nm_desc_t *dst, u_int limit)
|
||||
{
|
||||
struct netmap_ring *txring, *rxring;
|
||||
u_int m = 0, si = src->begin, di = dst->begin;
|
||||
const char *msg = (src->queueid & NETMAP_SW_RING) ?
|
||||
u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring;
|
||||
const char *msg = (src->req.nr_ringid & NETMAP_SW_RING) ?
|
||||
"host->net" : "net->host";
|
||||
|
||||
while (si < src->end && di < dst->end) {
|
||||
rxring = NETMAP_RXRING(src->nifp, si);
|
||||
txring = NETMAP_TXRING(dst->nifp, di);
|
||||
while (si <= src->last_rx_ring && di <= dst->last_tx_ring) {
|
||||
rxring = src->tx + si;
|
||||
txring = dst->tx + di;
|
||||
ND("txring %p rxring %p", txring, rxring);
|
||||
if (nm_ring_empty(rxring)) {
|
||||
si++;
|
||||
@ -121,28 +121,6 @@ move(struct my_ring *src, struct my_ring *dst, u_int limit)
|
||||
return (m);
|
||||
}
|
||||
|
||||
/*
|
||||
* how many packets on this set of queues ?
|
||||
*/
|
||||
static int
|
||||
pkt_queued(struct my_ring *me, int tx)
|
||||
{
|
||||
u_int i, tot = 0;
|
||||
|
||||
ND("me %p begin %d end %d", me, me->begin, me->end);
|
||||
for (i = me->begin; i < me->end; i++) {
|
||||
struct netmap_ring *ring = tx ?
|
||||
NETMAP_TXRING(me->nifp, i) : NETMAP_RXRING(me->nifp, i);
|
||||
tot += nm_ring_space(ring);
|
||||
}
|
||||
if (0 && verbose && tot && !tx)
|
||||
D("ring %s %s %s has %d avail at %d",
|
||||
me->ifname, tx ? "tx": "rx",
|
||||
me->end >= me->nifp->ni_tx_rings ? // XXX who comes first ?
|
||||
"host":"net",
|
||||
tot, NETMAP_TXRING(me->nifp, me->begin)->cur);
|
||||
return tot;
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
@ -165,14 +143,12 @@ main(int argc, char **argv)
|
||||
struct pollfd pollfd[2];
|
||||
int i, ch;
|
||||
u_int burst = 1024, wait_link = 4;
|
||||
struct my_ring me[2];
|
||||
struct nm_desc_t *pa = NULL, *pb = NULL;
|
||||
char *ifa = NULL, *ifb = NULL;
|
||||
|
||||
fprintf(stderr, "%s %s built %s %s\n",
|
||||
argv[0], version, __DATE__, __TIME__);
|
||||
|
||||
bzero(me, sizeof(me));
|
||||
|
||||
while ( (ch = getopt(argc, argv, "b:i:vw:")) != -1) {
|
||||
switch (ch) {
|
||||
default:
|
||||
@ -224,9 +200,6 @@ main(int argc, char **argv)
|
||||
D("invalid wait_link %d, set to 4", wait_link);
|
||||
wait_link = 4;
|
||||
}
|
||||
/* setup netmap interface #1. */
|
||||
me[0].ifname = ifa;
|
||||
me[1].ifname = ifb;
|
||||
if (!strcmp(ifa, ifb)) {
|
||||
D("same interface, endpoint 0 goes to host");
|
||||
i = NETMAP_SW_RING;
|
||||
@ -234,24 +207,26 @@ main(int argc, char **argv)
|
||||
/* two different interfaces. Take all rings on if1 */
|
||||
i = 0; // all hw rings
|
||||
}
|
||||
if (netmap_open(me, i, 1))
|
||||
pa = netmap_open(ifa, i, 1);
|
||||
if (pa == NULL)
|
||||
return (1);
|
||||
me[1].mem = me[0].mem; /* copy the pointer, so only one mmap */
|
||||
if (netmap_open(me+1, 0, 1))
|
||||
// XXX use a single mmap ?
|
||||
pb = netmap_open(ifb, 0, 1);
|
||||
if (pb == NULL) {
|
||||
nm_close(pa);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* setup poll(2) variables. */
|
||||
memset(pollfd, 0, sizeof(pollfd));
|
||||
for (i = 0; i < 2; i++) {
|
||||
pollfd[i].fd = me[i].fd;
|
||||
pollfd[i].events = (POLLIN);
|
||||
}
|
||||
pollfd[0].fd = pa->fd;
|
||||
pollfd[1].fd = pb->fd;
|
||||
|
||||
D("Wait %d secs for link to come up...", wait_link);
|
||||
sleep(wait_link);
|
||||
D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.",
|
||||
me[0].ifname, me[0].queueid, me[0].nifp->ni_rx_rings,
|
||||
me[1].ifname, me[1].queueid, me[1].nifp->ni_rx_rings);
|
||||
pa->req.nr_name, pa->first_rx_ring, pa->req.nr_rx_rings,
|
||||
pb->req.nr_name, pb->first_rx_ring, pb->req.nr_rx_rings);
|
||||
|
||||
/* main loop */
|
||||
signal(SIGINT, sigint_h);
|
||||
@ -259,8 +234,8 @@ main(int argc, char **argv)
|
||||
int n0, n1, ret;
|
||||
pollfd[0].events = pollfd[1].events = 0;
|
||||
pollfd[0].revents = pollfd[1].revents = 0;
|
||||
n0 = pkt_queued(me, 0);
|
||||
n1 = pkt_queued(me + 1, 0);
|
||||
n0 = pkt_queued(pa, 0);
|
||||
n1 = pkt_queued(pb, 0);
|
||||
if (n0)
|
||||
pollfd[1].events |= POLLOUT;
|
||||
else
|
||||
@ -276,39 +251,39 @@ main(int argc, char **argv)
|
||||
ret <= 0 ? "timeout" : "ok",
|
||||
pollfd[0].events,
|
||||
pollfd[0].revents,
|
||||
pkt_queued(me, 0),
|
||||
me[0].rx->cur,
|
||||
pkt_queued(me, 1),
|
||||
pkt_queued(pa, 0),
|
||||
pa->rx->cur,
|
||||
pkt_queued(pa, 1),
|
||||
pollfd[1].events,
|
||||
pollfd[1].revents,
|
||||
pkt_queued(me+1, 0),
|
||||
me[1].rx->cur,
|
||||
pkt_queued(me+1, 1)
|
||||
pkt_queued(pb, 0),
|
||||
pb->rx->cur,
|
||||
pkt_queued(pb, 1)
|
||||
);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
if (pollfd[0].revents & POLLERR) {
|
||||
D("error on fd0, rx [%d,%d)",
|
||||
me[0].rx->cur, me[0].rx->tail);
|
||||
pa->rx->cur, pa->rx->tail);
|
||||
}
|
||||
if (pollfd[1].revents & POLLERR) {
|
||||
D("error on fd1, rx [%d,%d)",
|
||||
me[1].rx->cur, me[1].rx->tail);
|
||||
pb->rx->cur, pb->rx->tail);
|
||||
}
|
||||
if (pollfd[0].revents & POLLOUT) {
|
||||
move(me + 1, me, burst);
|
||||
move(pb, pa, burst);
|
||||
// XXX we don't need the ioctl */
|
||||
// ioctl(me[0].fd, NIOCTXSYNC, NULL);
|
||||
}
|
||||
if (pollfd[1].revents & POLLOUT) {
|
||||
move(me, me + 1, burst);
|
||||
move(pa, pb, burst);
|
||||
// XXX we don't need the ioctl */
|
||||
// ioctl(me[1].fd, NIOCTXSYNC, NULL);
|
||||
}
|
||||
}
|
||||
D("exiting");
|
||||
netmap_close(me + 1);
|
||||
netmap_close(me + 0);
|
||||
nm_close(pb);
|
||||
nm_close(pa);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -37,16 +37,21 @@
|
||||
extern int verbose;
|
||||
|
||||
int
|
||||
nm_do_ioctl(struct my_ring *me, u_long what, int subcmd)
|
||||
nm_do_ioctl(struct nm_desc_t *me, u_long what, int subcmd)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int error;
|
||||
int fd;
|
||||
|
||||
#if defined( __FreeBSD__ ) || defined (__APPLE__)
|
||||
int fd = me->fd;
|
||||
(void)subcmd; // only used on Linux
|
||||
fd = me->fd;
|
||||
#endif
|
||||
|
||||
#ifdef linux
|
||||
struct ethtool_value eval;
|
||||
int fd;
|
||||
|
||||
bzero(&eval, sizeof(eval));
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
printf("Error: cannot get device control socket.\n");
|
||||
@ -54,9 +59,8 @@ nm_do_ioctl(struct my_ring *me, u_long what, int subcmd)
|
||||
}
|
||||
#endif /* linux */
|
||||
|
||||
(void)subcmd; // unused
|
||||
bzero(&ifr, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, me->ifname, sizeof(ifr.ifr_name));
|
||||
strncpy(ifr.ifr_name, me->req.nr_name, sizeof(ifr.ifr_name));
|
||||
switch (what) {
|
||||
case SIOCSIFFLAGS:
|
||||
#ifndef __APPLE__
|
||||
@ -71,6 +75,7 @@ nm_do_ioctl(struct my_ring *me, u_long what, int subcmd)
|
||||
ifr.ifr_curcap = me->if_curcap;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef linux
|
||||
case SIOCETHTOOL:
|
||||
eval.cmd = subcmd;
|
||||
@ -115,108 +120,47 @@ nm_do_ioctl(struct my_ring *me, u_long what, int subcmd)
|
||||
* Returns the file descriptor.
|
||||
* The extra flag checks configures promisc mode.
|
||||
*/
|
||||
int
|
||||
netmap_open(struct my_ring *me, int ringid, int promisc)
|
||||
struct nm_desc_t *
|
||||
netmap_open(const char *name, int ringid, int promisc)
|
||||
{
|
||||
int fd, err, l;
|
||||
struct nmreq req;
|
||||
struct nm_desc_t *d = nm_open(name, NULL, ringid, 0);
|
||||
|
||||
if (d == NULL)
|
||||
return d;
|
||||
|
||||
me->fd = fd = open("/dev/netmap", O_RDWR);
|
||||
if (fd < 0) {
|
||||
D("Unable to open /dev/netmap");
|
||||
return (-1);
|
||||
}
|
||||
bzero(&req, sizeof(req));
|
||||
req.nr_version = NETMAP_API;
|
||||
strncpy(req.nr_name, me->ifname, sizeof(req.nr_name));
|
||||
req.nr_ringid = ringid;
|
||||
err = ioctl(fd, NIOCREGIF, &req);
|
||||
if (err) {
|
||||
D("Unable to register %s", me->ifname);
|
||||
goto error;
|
||||
}
|
||||
me->memsize = l = req.nr_memsize;
|
||||
if (verbose)
|
||||
D("memsize is %d MB", l>>20);
|
||||
|
||||
if (me->mem == NULL) {
|
||||
me->mem = mmap(0, l, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (me->mem == MAP_FAILED) {
|
||||
D("Unable to mmap");
|
||||
me->mem = NULL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
D("memsize is %d MB", d->req.nr_memsize>>20);
|
||||
|
||||
/* Set the operating mode. */
|
||||
if (ringid != NETMAP_SW_RING) {
|
||||
nm_do_ioctl(me, SIOCGIFFLAGS, 0);
|
||||
if ((me[0].if_flags & IFF_UP) == 0) {
|
||||
D("%s is down, bringing up...", me[0].ifname);
|
||||
me[0].if_flags |= IFF_UP;
|
||||
nm_do_ioctl(d, SIOCGIFFLAGS, 0);
|
||||
if ((d->if_flags & IFF_UP) == 0) {
|
||||
D("%s is down, bringing up...", name);
|
||||
d->if_flags |= IFF_UP;
|
||||
}
|
||||
if (promisc) {
|
||||
me[0].if_flags |= IFF_PPROMISC;
|
||||
nm_do_ioctl(me, SIOCSIFFLAGS, 0);
|
||||
d->if_flags |= IFF_PPROMISC;
|
||||
nm_do_ioctl(d, SIOCSIFFLAGS, 0);
|
||||
}
|
||||
|
||||
/* disable GSO, TSO, RXCSUM, TXCSUM...
|
||||
* TODO: set them back when done.
|
||||
*/
|
||||
#ifdef __FreeBSD__
|
||||
/* also disable checksums etc. */
|
||||
nm_do_ioctl(me, SIOCGIFCAP, 0);
|
||||
me[0].if_reqcap = me[0].if_curcap;
|
||||
me[0].if_reqcap &= ~(IFCAP_HWCSUM | IFCAP_TSO | IFCAP_TOE);
|
||||
nm_do_ioctl(me+0, SIOCSIFCAP, 0);
|
||||
nm_do_ioctl(d, SIOCGIFCAP, 0);
|
||||
d->if_reqcap = d->if_curcap;
|
||||
d->if_reqcap &= ~(IFCAP_HWCSUM | IFCAP_TSO | IFCAP_TOE);
|
||||
nm_do_ioctl(d, SIOCSIFCAP, 0);
|
||||
#endif
|
||||
#ifdef linux
|
||||
/* disable:
|
||||
* - generic-segmentation-offload
|
||||
* - tcp-segmentation-offload
|
||||
* - rx-checksumming
|
||||
* - tx-checksumming
|
||||
* XXX check how to set back the caps.
|
||||
*/
|
||||
nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_SGSO);
|
||||
nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_STSO);
|
||||
nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_SRXCSUM);
|
||||
nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_STXCSUM);
|
||||
nm_do_ioctl(d, SIOCETHTOOL, ETHTOOL_SGSO);
|
||||
nm_do_ioctl(d, SIOCETHTOOL, ETHTOOL_STSO);
|
||||
nm_do_ioctl(d, SIOCETHTOOL, ETHTOOL_SRXCSUM);
|
||||
nm_do_ioctl(d, SIOCETHTOOL, ETHTOOL_STXCSUM);
|
||||
#endif /* linux */
|
||||
}
|
||||
|
||||
me->nifp = NETMAP_IF(me->mem, req.nr_offset);
|
||||
me->queueid = ringid;
|
||||
if (ringid & NETMAP_SW_RING) {
|
||||
me->begin = req.nr_rx_rings;
|
||||
me->end = me->begin + 1;
|
||||
me->tx = NETMAP_TXRING(me->nifp, req.nr_tx_rings);
|
||||
me->rx = NETMAP_RXRING(me->nifp, req.nr_rx_rings);
|
||||
} else if (ringid & NETMAP_HW_RING) {
|
||||
D("XXX check multiple threads");
|
||||
me->begin = ringid & NETMAP_RING_MASK;
|
||||
me->end = me->begin + 1;
|
||||
me->tx = NETMAP_TXRING(me->nifp, me->begin);
|
||||
me->rx = NETMAP_RXRING(me->nifp, me->begin);
|
||||
} else {
|
||||
me->begin = 0;
|
||||
me->end = req.nr_rx_rings; // XXX max of the two
|
||||
me->tx = NETMAP_TXRING(me->nifp, 0);
|
||||
me->rx = NETMAP_RXRING(me->nifp, 0);
|
||||
}
|
||||
return (0);
|
||||
error:
|
||||
close(me->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
netmap_close(struct my_ring *me)
|
||||
{
|
||||
D("");
|
||||
if (me->mem)
|
||||
munmap(me->mem, me->memsize);
|
||||
close(me->fd);
|
||||
return (0);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
@ -224,22 +168,18 @@ netmap_close(struct my_ring *me)
|
||||
* how many packets on this set of queues ?
|
||||
*/
|
||||
int
|
||||
pkt_queued(struct my_ring *me, int tx)
|
||||
pkt_queued(struct nm_desc_t *d, int tx)
|
||||
{
|
||||
u_int i, tot = 0;
|
||||
|
||||
ND("me %p begin %d end %d", me, me->begin, me->end);
|
||||
for (i = me->begin; i < me->end; i++) {
|
||||
struct netmap_ring *ring = tx ?
|
||||
NETMAP_TXRING(me->nifp, i) : NETMAP_RXRING(me->nifp, i);
|
||||
tot += nm_ring_space(ring);
|
||||
if (tx) {
|
||||
for (i = d->first_tx_ring; i <= d->last_tx_ring; i++)
|
||||
tot += nm_ring_space(d->tx + i);
|
||||
} else {
|
||||
for (i = d->first_rx_ring; i <= d->last_rx_ring; i++)
|
||||
tot += nm_ring_space(d->rx + i);
|
||||
}
|
||||
if (0 && verbose && tot && !tx)
|
||||
D("ring %s %s %s has %d avail at %d",
|
||||
me->ifname, tx ? "tx": "rx",
|
||||
me->end >= me->nifp->ni_tx_rings ? // XXX who comes first ?
|
||||
"host":"net",
|
||||
tot, NETMAP_TXRING(me->nifp, me->begin)->cur);
|
||||
return tot;
|
||||
}
|
||||
|
||||
@ -258,7 +198,7 @@ Helper routines for multiple readers from the same queue
|
||||
In particular we have a shared head+tail pointers that work
|
||||
together with cur and available
|
||||
ON RETURN FROM THE SYSCALL:
|
||||
shadow->head = ring->cur
|
||||
shadow->cur = ring->cur
|
||||
shadow->tail = ring->tail
|
||||
shadow->link[i] = i for all slots // mark invalid
|
||||
|
||||
@ -267,7 +207,7 @@ Helper routines for multiple readers from the same queue
|
||||
struct nm_q_arg {
|
||||
u_int want; /* Input */
|
||||
u_int have; /* Output, 0 on error */
|
||||
u_int head;
|
||||
u_int cur;
|
||||
u_int tail;
|
||||
struct netmap_ring *ring;
|
||||
};
|
||||
@ -280,24 +220,26 @@ my_grab(struct nm_q_arg q)
|
||||
{
|
||||
const u_int ns = q.ring->num_slots;
|
||||
|
||||
// lock(ring);
|
||||
for (;;) {
|
||||
|
||||
q.head = (volatile u_int)q.ring->head;
|
||||
q.cur = (volatile u_int)q.ring->head;
|
||||
q.have = ns + q.head - (volatile u_int)q.ring->tail;
|
||||
if (q.have >= ns)
|
||||
q.have -= ns;
|
||||
if (q.have == 0) /* no space */
|
||||
if (q.have == 0) /* no space; caller may ioctl/retry */
|
||||
break;
|
||||
if (q.want < q.have)
|
||||
q.have = q.want;
|
||||
q.tail = q.head + q.have;
|
||||
q.tail = q.cur + q.have;
|
||||
if (q.tail >= ns)
|
||||
q.tail -= ns;
|
||||
if (atomic_cmpset_int(&q.ring->head, q.head, q.tail)
|
||||
if (atomic_cmpset_int(&q.ring->cur, q.cur, q.tail)
|
||||
break; /* success */
|
||||
}
|
||||
// unlock(ring);
|
||||
D("returns %d out of %d at %d,%d",
|
||||
q.have, q.want, q.head, q.tail);
|
||||
q.have, q.want, q.cur, q.tail);
|
||||
/* the last one can clear avail ? */
|
||||
return q;
|
||||
}
|
||||
@ -306,16 +248,18 @@ my_grab(struct nm_q_arg q)
|
||||
int
|
||||
my_release(struct nm_q_arg q)
|
||||
{
|
||||
u_int head = q.head, tail = q.tail, i;
|
||||
u_int cur = q.cur, tail = q.tail, i;
|
||||
struct netmap_ring *r = q.ring;
|
||||
|
||||
/* link the block to the next one.
|
||||
* there is no race here because the location is mine.
|
||||
*/
|
||||
r->slot[head].ptr = tail; /* this is mine */
|
||||
r->slot[cur].ptr = tail; /* this is mine */
|
||||
r->slot[cur].flags |= NM_SLOT_PTR; // points to next block
|
||||
// memory barrier
|
||||
if (r->head != head)
|
||||
return; /* not my turn to release */
|
||||
// lock(ring);
|
||||
if (r->head != cur)
|
||||
goto done;
|
||||
for (;;) {
|
||||
// advance head
|
||||
r->head = head = r->slot[head].ptr;
|
||||
@ -327,5 +271,8 @@ my_release(struct nm_q_arg q)
|
||||
* further down.
|
||||
*/
|
||||
// do an ioctl/poll to flush.
|
||||
done:
|
||||
// unlock(ring);
|
||||
return; /* not my turn to release */
|
||||
}
|
||||
#endif /* unused */
|
||||
|
@ -35,60 +35,31 @@
|
||||
|
||||
#define _GNU_SOURCE /* for CPU_SET() */
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h> /* signal */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio.h> /* fprintf */
|
||||
#include <sys/poll.h> /* POLLIN */
|
||||
#include <inttypes.h> /* PRI* macros */
|
||||
#include <string.h> /* strcmp */
|
||||
#include <fcntl.h> /* open */
|
||||
#include <unistd.h> /* close */
|
||||
#include <ifaddrs.h> /* getifaddrs */
|
||||
#include <sys/types.h> /* u_char */
|
||||
|
||||
#include <sys/mman.h> /* PROT_* */
|
||||
#include <sys/ioctl.h> /* ioctl */
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h> /* sockaddr.. */
|
||||
#include <arpa/inet.h> /* ntohs */
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h> /* sysctl */
|
||||
#include <sys/time.h> /* timersub */
|
||||
#include <ifaddrs.h> /* getifaddrs */
|
||||
#include <net/ethernet.h> /* ETHERTYPE_IP */
|
||||
#include <netinet/in.h> /* IPPROTO_* */
|
||||
#include <netinet/ip.h> /* struct ip */
|
||||
#include <netinet/udp.h> /* struct udp */
|
||||
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h> /* ifreq */
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <net/netmap.h>
|
||||
#define NETMAP_WITH_LIBS
|
||||
#include <net/netmap_user.h>
|
||||
|
||||
#ifndef MY_PCAP /* use the system's pcap if available */
|
||||
|
||||
#ifdef NO_PCAP
|
||||
#define PCAP_ERRBUF_SIZE 512
|
||||
typedef void pcap_t;
|
||||
struct pcap_pkthdr;
|
||||
#define pcap_inject(a,b,c) ((void)a, (void)b, (void)c, -1)
|
||||
#define pcap_dispatch(a, b, c, d) (void)c
|
||||
#define pcap_open_live(a, b, c, d, e) ((void)e, NULL)
|
||||
#else /* !NO_PCAP */
|
||||
#include <pcap/pcap.h> // XXX do we need it ?
|
||||
#endif /* !NO_PCAP */
|
||||
|
||||
#endif // XXX hack
|
||||
|
||||
#include <pthread.h> /* pthread_* */
|
||||
|
||||
#ifdef linux
|
||||
|
||||
#define cpuset_t cpu_set_t
|
||||
|
||||
#define ifr_flagshigh ifr_flags
|
||||
#define ifr_curcap ifr_flags
|
||||
#define ifr_reqcap ifr_flags
|
||||
#define IFF_PPROMISC IFF_PROMISC
|
||||
#define ifr_flagshigh ifr_flags /* only the low 16 bits here */
|
||||
#define IFF_PPROMISC IFF_PROMISC /* IFF_PPROMISC does not exist */
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/sockios.h>
|
||||
|
||||
@ -107,6 +78,20 @@ struct pcap_pkthdr;
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#define cpuset_t uint64_t // XXX
|
||||
static inline void CPU_ZERO(cpuset_t *p)
|
||||
{
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
static inline void CPU_SET(uint32_t i, cpuset_t *p)
|
||||
{
|
||||
*p |= 1<< (i & 0x3f);
|
||||
}
|
||||
|
||||
#define pthread_setaffinity_np(a, b, c) ((void)a, 0)
|
||||
|
||||
#define ifr_flagshigh ifr_flags // XXX
|
||||
#define IFF_PPROMISC IFF_PROMISC
|
||||
#include <net/if_dl.h> /* LLADDR */
|
||||
@ -136,54 +121,7 @@ extern int time_second;
|
||||
|
||||
|
||||
|
||||
// XXX does it work on 32-bit machines ?
|
||||
static inline void prefetch (const void *x)
|
||||
{
|
||||
__asm volatile("prefetcht0 %0" :: "m" (*(const unsigned long *)x));
|
||||
}
|
||||
|
||||
// XXX only for multiples of 64 bytes, non overlapped.
|
||||
static inline void
|
||||
pkt_copy(const void *_src, void *_dst, int l)
|
||||
{
|
||||
const uint64_t *src = _src;
|
||||
uint64_t *dst = _dst;
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
if (unlikely(l >= 1024)) {
|
||||
bcopy(src, dst, l);
|
||||
return;
|
||||
}
|
||||
for (; l > 0; l-=64) {
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
*dst++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* info on a ring we handle
|
||||
*/
|
||||
struct my_ring {
|
||||
const char *ifname;
|
||||
int fd;
|
||||
char *mem; /* userspace mmap address */
|
||||
u_int memsize;
|
||||
u_int queueid;
|
||||
u_int begin, end; /* first..last+1 rings to check */
|
||||
struct netmap_if *nifp;
|
||||
struct netmap_ring *tx, *rx; /* shortcuts */
|
||||
|
||||
uint32_t if_flags;
|
||||
uint32_t if_reqcap;
|
||||
uint32_t if_curcap;
|
||||
};
|
||||
int netmap_open(struct my_ring *me, int ringid, int promisc);
|
||||
int netmap_close(struct my_ring *me);
|
||||
int nm_do_ioctl(struct my_ring *me, u_long what, int subcmd);
|
||||
struct nm_desc_t * netmap_open(const char *name, int ringid, int promisc);
|
||||
int nm_do_ioctl(struct nm_desc_t *me, u_long what, int subcmd);
|
||||
int pkt_queued(struct nm_desc_t *d, int tx);
|
||||
#endif /* _NM_UTIL_H */
|
||||
|
@ -65,7 +65,7 @@ struct pcap_stat {
|
||||
#endif /* WIN32 */
|
||||
};
|
||||
|
||||
typedef void pcap_t;
|
||||
typedef struct nm_desc_t pcap_t;
|
||||
typedef enum {
|
||||
PCAP_D_INOUT = 0,
|
||||
PCAP_D_IN,
|
||||
@ -107,41 +107,6 @@ struct eproto {
|
||||
|
||||
char pcap_version[] = "libnetmap version 0.3";
|
||||
|
||||
/*
|
||||
* Our equivalent of pcap_t
|
||||
*/
|
||||
struct pcap_ring {
|
||||
struct my_ring me;
|
||||
#if 0
|
||||
const char *ifname;
|
||||
|
||||
//struct nmreq nmr;
|
||||
|
||||
int fd;
|
||||
char *mem; /* userspace mmap address */
|
||||
u_int memsize;
|
||||
u_int queueid;
|
||||
u_int begin, end; /* first..last+1 rings to check */
|
||||
struct netmap_if *nifp;
|
||||
|
||||
uint32_t if_flags;
|
||||
uint32_t if_reqcap;
|
||||
uint32_t if_curcap;
|
||||
#endif
|
||||
int snaplen;
|
||||
char *errbuf;
|
||||
int promisc;
|
||||
int to_ms;
|
||||
|
||||
struct pcap_pkthdr hdr;
|
||||
|
||||
|
||||
struct pcap_stat st;
|
||||
|
||||
char msg[PCAP_ERRBUF_SIZE];
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* There is a set of functions that tcpdump expects even if probably
|
||||
@ -279,7 +244,7 @@ pcap_can_set_rfmon(pcap_t *p)
|
||||
int
|
||||
pcap_set_snaplen(pcap_t *p, int snaplen)
|
||||
{
|
||||
struct pcap_ring *me = p;
|
||||
struct nm_desc_t *me = p;
|
||||
|
||||
D("len %d", snaplen);
|
||||
me->snaplen = snaplen;
|
||||
@ -289,7 +254,7 @@ pcap_set_snaplen(pcap_t *p, int snaplen)
|
||||
int
|
||||
pcap_snapshot(pcap_t *p)
|
||||
{
|
||||
struct pcap_ring *me = p;
|
||||
struct nm_desc_t *me = p;
|
||||
|
||||
D("len %d", me->snaplen);
|
||||
return me->snaplen;
|
||||
@ -310,17 +275,15 @@ pcap_lookupnet(const char *device, uint32_t *netp,
|
||||
int
|
||||
pcap_set_promisc(pcap_t *p, int promisc)
|
||||
{
|
||||
struct pcap_ring *me = p;
|
||||
|
||||
D("promisc %d", promisc);
|
||||
if (nm_do_ioctl(&me->me, SIOCGIFFLAGS, 0))
|
||||
if (nm_do_ioctl(p, SIOCGIFFLAGS, 0))
|
||||
D("SIOCGIFFLAGS failed");
|
||||
if (promisc) {
|
||||
me->me.if_flags |= IFF_PPROMISC;
|
||||
p->if_flags |= IFF_PPROMISC;
|
||||
} else {
|
||||
me->me.if_flags &= ~IFF_PPROMISC;
|
||||
p->if_flags &= ~IFF_PPROMISC;
|
||||
}
|
||||
if (nm_do_ioctl(&me->me, SIOCSIFFLAGS, 0))
|
||||
if (nm_do_ioctl(p, SIOCSIFFLAGS, 0))
|
||||
D("SIOCSIFFLAGS failed");
|
||||
return 0;
|
||||
}
|
||||
@ -328,10 +291,8 @@ pcap_set_promisc(pcap_t *p, int promisc)
|
||||
int
|
||||
pcap_set_timeout(pcap_t *p, int to_ms)
|
||||
{
|
||||
struct pcap_ring *me = p;
|
||||
|
||||
D("%d ms", to_ms);
|
||||
me->to_ms = to_ms;
|
||||
p->to_ms = to_ms;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -384,31 +345,24 @@ struct pcap_stat;
|
||||
int
|
||||
pcap_stats(pcap_t *p, struct pcap_stat *ps)
|
||||
{
|
||||
struct pcap_ring *me = p;
|
||||
ND("");
|
||||
|
||||
*ps = me->st;
|
||||
*ps = *(struct pcap_stat *)(void *)&(p->st);
|
||||
return 0; /* accumulate from pcap_dispatch() */
|
||||
};
|
||||
|
||||
char *
|
||||
pcap_geterr(pcap_t *p)
|
||||
{
|
||||
struct pcap_ring *me = p;
|
||||
|
||||
D("");
|
||||
return me->msg;
|
||||
return p->msg;
|
||||
}
|
||||
|
||||
pcap_t *
|
||||
pcap_open_live(const char *device, int snaplen,
|
||||
int promisc, int to_ms, char *errbuf)
|
||||
{
|
||||
struct pcap_ring *me;
|
||||
struct nm_desc_t *d;
|
||||
int l;
|
||||
|
||||
(void)snaplen; /* UNUSED */
|
||||
(void)errbuf; /* UNUSED */
|
||||
if (!device) {
|
||||
D("missing device name");
|
||||
return NULL;
|
||||
@ -417,54 +371,40 @@ pcap_open_live(const char *device, int snaplen,
|
||||
l = strlen(device) + 1;
|
||||
D("request to open %s snaplen %d promisc %d timeout %dms",
|
||||
device, snaplen, promisc, to_ms);
|
||||
me = calloc(1, sizeof(*me) + l);
|
||||
if (me == NULL) {
|
||||
D("failed to allocate struct for %s", device);
|
||||
return NULL;
|
||||
}
|
||||
me->me.ifname = (char *)(me + 1);
|
||||
strcpy((char *)me->me.ifname, device);
|
||||
if (netmap_open(&me->me, 0, promisc)) {
|
||||
d = nm_open(device, NULL, 0, 0);
|
||||
if (d == NULL) {
|
||||
D("error opening %s", device);
|
||||
free(me);
|
||||
return NULL;
|
||||
}
|
||||
me->to_ms = to_ms;
|
||||
d->to_ms = to_ms;
|
||||
d->snaplen = snaplen;
|
||||
d->errbuf = errbuf;
|
||||
d->promisc = promisc;
|
||||
|
||||
return (pcap_t *)me;
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
pcap_close(pcap_t *p)
|
||||
{
|
||||
struct my_ring *me = p;
|
||||
|
||||
D("");
|
||||
if (!me)
|
||||
return;
|
||||
if (me->mem)
|
||||
munmap(me->mem, me->memsize);
|
||||
nm_close(p);
|
||||
/* restore original flags ? */
|
||||
close(me->fd);
|
||||
bzero(me, sizeof(*me));
|
||||
free(me);
|
||||
}
|
||||
|
||||
int
|
||||
pcap_fileno(pcap_t *p)
|
||||
{
|
||||
struct my_ring *me = p;
|
||||
D("returns %d", me->fd);
|
||||
return me->fd;
|
||||
struct nm_desc_t *d = p;
|
||||
D("returns %d", d->fd);
|
||||
return d->fd;
|
||||
}
|
||||
|
||||
int
|
||||
pcap_get_selectable_fd(pcap_t *p)
|
||||
{
|
||||
struct my_ring *me = p;
|
||||
struct nm_desc_t *d = p;
|
||||
|
||||
ND("");
|
||||
return me->fd;
|
||||
return d->fd;
|
||||
}
|
||||
|
||||
int
|
||||
@ -488,94 +428,32 @@ pcap_setdirection(pcap_t *p, pcap_direction_t d)
|
||||
int
|
||||
pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
|
||||
{
|
||||
struct pcap_ring *pme = p;
|
||||
struct my_ring *me = &pme->me;
|
||||
int got = 0;
|
||||
u_int si;
|
||||
|
||||
ND("cnt %d", cnt);
|
||||
if (cnt == 0)
|
||||
cnt = -1;
|
||||
/* scan all rings */
|
||||
for (si = me->begin; si < me->end; si++) {
|
||||
struct netmap_ring *ring = NETMAP_RXRING(me->nifp, si);
|
||||
if (nm_ring_empty(ring))
|
||||
continue;
|
||||
pme->hdr.ts = ring->ts;
|
||||
/*
|
||||
* XXX a proper prefetch should be done as
|
||||
* prefetch(i); callback(i-1); ...
|
||||
*/
|
||||
while ((cnt == -1 || cnt != got) && !nm_ring_empty(ring)) {
|
||||
u_int i = ring->cur;
|
||||
u_int idx = ring->slot[i].buf_idx;
|
||||
if (idx < 2) {
|
||||
D("%s bogus RX index %d at offset %d",
|
||||
me->nifp->ni_name, idx, i);
|
||||
sleep(2);
|
||||
}
|
||||
u_char *buf = (u_char *)NETMAP_BUF(ring, idx);
|
||||
prefetch(buf);
|
||||
pme->hdr.len = pme->hdr.caplen = ring->slot[i].len;
|
||||
// D("call %p len %d", p, me->hdr.len);
|
||||
callback(user, &pme->hdr, buf);
|
||||
ring->head = ring->cur = nm_ring_next(ring, i);
|
||||
got++;
|
||||
}
|
||||
}
|
||||
pme->st.ps_recv += got;
|
||||
return got;
|
||||
return nm_dispatch(p, cnt, (void *)callback, user);
|
||||
}
|
||||
|
||||
int
|
||||
pcap_inject(pcap_t *p, const void *buf, size_t size)
|
||||
{
|
||||
struct my_ring *me = p;
|
||||
u_int si;
|
||||
|
||||
ND("cnt %d", cnt);
|
||||
/* scan all rings */
|
||||
for (si = me->begin; si < me->end; si++) {
|
||||
struct netmap_ring *ring = NETMAP_TXRING(me->nifp, si);
|
||||
|
||||
if (nm_ring_empty(ring))
|
||||
continue;
|
||||
u_int i = ring->cur;
|
||||
u_int idx = ring->slot[i].buf_idx;
|
||||
if (idx < 2) {
|
||||
D("%s bogus TX index %d at offset %d",
|
||||
me->nifp->ni_name, idx, i);
|
||||
sleep(2);
|
||||
}
|
||||
u_char *dst = (u_char *)NETMAP_BUF(ring, idx);
|
||||
ring->slot[i].len = size;
|
||||
pkt_copy(buf, dst, size);
|
||||
ring->head = ring->cur = nm_ring_next(ring, i);
|
||||
// if (ring->cur == ring->tail) ioctl(me->fd, NIOCTXSYNC, NULL);
|
||||
return size;
|
||||
}
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
return nm_inject(p, buf, size);
|
||||
}
|
||||
|
||||
int
|
||||
pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
|
||||
{
|
||||
struct pcap_ring *me = p;
|
||||
struct pollfd fds[1];
|
||||
int i;
|
||||
|
||||
ND("cnt %d", cnt);
|
||||
memset(fds, 0, sizeof(fds));
|
||||
fds[0].fd = me->me.fd;
|
||||
fds[0].fd = p->fd;
|
||||
fds[0].events = (POLLIN);
|
||||
|
||||
while (cnt == -1 || cnt > 0) {
|
||||
if (poll(fds, 1, me->to_ms) <= 0) {
|
||||
if (poll(fds, 1, p->to_ms) <= 0) {
|
||||
D("poll error/timeout");
|
||||
continue;
|
||||
}
|
||||
i = pcap_dispatch(p, cnt, callback, user);
|
||||
i = nm_dispatch(p, cnt, (void *)callback, user);
|
||||
if (cnt > 0)
|
||||
cnt -= i;
|
||||
}
|
||||
@ -640,9 +518,9 @@ main(int argc, char **argv)
|
||||
if (ret < 0)
|
||||
continue;
|
||||
if (pollfd[0].revents & POLLIN)
|
||||
pcap_dispatch(p0, burst, do_send, p1);
|
||||
pcap_dispatch(p0, burst, do_send, (void *)p1);
|
||||
if (pollfd[1].revents & POLLIN)
|
||||
pcap_dispatch(p1, burst, do_send, p0);
|
||||
pcap_dispatch(p1, burst, do_send, (void *)p0);
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
@ -37,10 +37,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define MY_PCAP
|
||||
#include "nm_util.h"
|
||||
// #include <net/netmap_user.h>
|
||||
|
||||
#include <ctype.h> // isprint()
|
||||
|
||||
#ifndef NO_PCAP
|
||||
#include <pcap/pcap.h>
|
||||
#endif
|
||||
const char *default_payload="netmap pkt-gen DIRECT payload\n"
|
||||
"http://info.iet.unipi.it/~luigi/netmap/ ";
|
||||
|
||||
@ -105,14 +110,16 @@ struct glob_arg {
|
||||
#define OPT_INDIRECT 32 /* use indirect buffers, tx only */
|
||||
#define OPT_DUMP 64 /* dump rx/tx traffic */
|
||||
int dev_type;
|
||||
#ifndef NO_PCAP
|
||||
pcap_t *p;
|
||||
#endif
|
||||
|
||||
int tx_rate;
|
||||
struct timespec tx_period;
|
||||
|
||||
int affinity;
|
||||
int main_fd;
|
||||
int report_interval;
|
||||
int report_interval; /* milliseconds between prints */
|
||||
void *(*td_body)(void *);
|
||||
void *mmap_addr;
|
||||
int mmap_size;
|
||||
@ -486,17 +493,18 @@ update_addresses(struct pkt *pkt, struct glob_arg *g)
|
||||
struct ip *ip = &pkt->ip;
|
||||
struct udphdr *udp = &pkt->udp;
|
||||
|
||||
do {
|
||||
p = ntohs(udp->uh_sport);
|
||||
if (p < g->src_ip.port1) { /* just inc, no wrap */
|
||||
udp->uh_sport = htons(p + 1);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
udp->uh_sport = htons(g->src_ip.port0);
|
||||
|
||||
a = ntohl(ip->ip_src.s_addr);
|
||||
if (a < g->src_ip.end) { /* just inc, no wrap */
|
||||
ip->ip_src.s_addr = htonl(a + 1);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
ip->ip_src.s_addr = htonl(g->src_ip.start);
|
||||
|
||||
@ -504,17 +512,18 @@ update_addresses(struct pkt *pkt, struct glob_arg *g)
|
||||
p = ntohs(udp->uh_dport);
|
||||
if (p < g->dst_ip.port1) { /* just inc, no wrap */
|
||||
udp->uh_dport = htons(p + 1);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
udp->uh_dport = htons(g->dst_ip.port0);
|
||||
|
||||
a = ntohl(ip->ip_dst.s_addr);
|
||||
if (a < g->dst_ip.end) { /* just inc, no wrap */
|
||||
ip->ip_dst.s_addr = htonl(a + 1);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
ip->ip_dst.s_addr = htonl(g->dst_ip.start);
|
||||
|
||||
} while (0);
|
||||
// update checksum
|
||||
}
|
||||
|
||||
/*
|
||||
@ -531,13 +540,13 @@ initialize_packet(struct targ *targ)
|
||||
uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip);
|
||||
const char *payload = targ->g->options & OPT_INDIRECT ?
|
||||
indirect_payload : default_payload;
|
||||
int i, l, l0 = strlen(payload);
|
||||
int i, l0 = strlen(payload);
|
||||
|
||||
/* create a nice NUL-terminated string */
|
||||
for (i = 0; i < paylen;) {
|
||||
l = min(l0, paylen - i);
|
||||
bcopy(payload, pkt->body + i, l);
|
||||
i += l;
|
||||
for (i = 0; i < paylen; i += l0) {
|
||||
if (l0 > paylen - i)
|
||||
l0 = paylen - i; // last round
|
||||
bcopy(payload, pkt->body + i, l0);
|
||||
}
|
||||
pkt->body[i-1] = '\0';
|
||||
ip = &pkt->ip;
|
||||
@ -593,7 +602,7 @@ send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
|
||||
u_int nfrags)
|
||||
{
|
||||
u_int n, sent, cur = ring->cur;
|
||||
int fcnt;
|
||||
u_int fcnt;
|
||||
|
||||
n = nm_ring_space(ring);
|
||||
if (n < count)
|
||||
@ -608,7 +617,7 @@ send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
|
||||
struct netmap_slot *slot = &ring->slot[cur];
|
||||
char *p = NETMAP_BUF(ring, slot->buf_idx);
|
||||
|
||||
prefetch(p);
|
||||
__builtin_prefetch(p);
|
||||
cur = nm_ring_next(ring, cur);
|
||||
}
|
||||
cur = ring->cur;
|
||||
@ -624,14 +633,14 @@ send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
|
||||
slot->ptr = (uint64_t)frame;
|
||||
} else if (options & OPT_COPY) {
|
||||
pkt_copy(frame, p, size);
|
||||
if (fcnt == 1)
|
||||
if (fcnt == nfrags)
|
||||
update_addresses(pkt, g);
|
||||
} else if (options & OPT_MEMCPY) {
|
||||
memcpy(p, frame, size);
|
||||
if (fcnt == 1)
|
||||
if (fcnt == nfrags)
|
||||
update_addresses(pkt, g);
|
||||
} else if (options & OPT_PREFETCH) {
|
||||
prefetch(p);
|
||||
__builtin_prefetch(p);
|
||||
}
|
||||
if (options & OPT_DUMP)
|
||||
dump_payload(p, size, ring, cur);
|
||||
@ -947,19 +956,7 @@ sender_body(void *data)
|
||||
wait_time(targ->tic);
|
||||
nexttime = targ->tic;
|
||||
}
|
||||
if (targ->g->dev_type == DEV_PCAP) {
|
||||
pcap_t *p = targ->g->p;
|
||||
|
||||
for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
|
||||
if (pcap_inject(p, frame, size) != -1)
|
||||
sent++;
|
||||
update_addresses(pkt, targ->g);
|
||||
if (i > 10000) {
|
||||
targ->count = sent;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
} else if (targ->g->dev_type == DEV_TAP) { /* tap */
|
||||
if (targ->g->dev_type == DEV_TAP) {
|
||||
D("writing to file desc %d", targ->g->main_fd);
|
||||
|
||||
for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
|
||||
@ -971,6 +968,20 @@ sender_body(void *data)
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
#ifndef NO_PCAP
|
||||
} else if (targ->g->dev_type == DEV_PCAP) {
|
||||
pcap_t *p = targ->g->p;
|
||||
|
||||
for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
|
||||
if (pcap_inject(p, frame, size) != -1)
|
||||
sent++;
|
||||
update_addresses(pkt, targ->g);
|
||||
if (i > 10000) {
|
||||
targ->count = sent;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
#endif /* NO_PCAP */
|
||||
} else {
|
||||
int tosend = 0;
|
||||
int frags = targ->g->frags;
|
||||
@ -1016,8 +1027,8 @@ sender_body(void *data)
|
||||
|
||||
m = send_packets(txring, pkt, frame, size, targ->g,
|
||||
limit, options, frags);
|
||||
ND("limit %d avail %d frags %d m %d",
|
||||
limit, txring->avail, frags, m);
|
||||
ND("limit %d tail %d frags %d m %d",
|
||||
limit, txring->tail, frags, m);
|
||||
sent += m;
|
||||
targ->count = sent;
|
||||
if (rate_limit) {
|
||||
@ -1038,7 +1049,7 @@ sender_body(void *data)
|
||||
usleep(1); /* wait 1 tick */
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* end DEV_NETMAP */
|
||||
|
||||
clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
|
||||
targ->completed = 1;
|
||||
@ -1052,6 +1063,7 @@ sender_body(void *data)
|
||||
}
|
||||
|
||||
|
||||
#ifndef NO_PCAP
|
||||
static void
|
||||
receive_pcap(u_char *user, const struct pcap_pkthdr * h,
|
||||
const u_char * bytes)
|
||||
@ -1061,6 +1073,7 @@ receive_pcap(u_char *user, const struct pcap_pkthdr * h,
|
||||
(void)bytes; /* UNUSED */
|
||||
(*count)++;
|
||||
}
|
||||
#endif /* !NO_PCAP */
|
||||
|
||||
static int
|
||||
receive_packets(struct netmap_ring *ring, u_int limit, int dump)
|
||||
@ -1113,12 +1126,7 @@ receiver_body(void *data)
|
||||
|
||||
/* main loop, exit after 1s silence */
|
||||
clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
|
||||
if (targ->g->dev_type == DEV_PCAP) {
|
||||
while (!targ->cancel) {
|
||||
/* XXX should we poll ? */
|
||||
pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
|
||||
}
|
||||
} else if (targ->g->dev_type == DEV_TAP) {
|
||||
if (targ->g->dev_type == DEV_TAP) {
|
||||
D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd);
|
||||
while (!targ->cancel) {
|
||||
char buf[2048];
|
||||
@ -1126,6 +1134,13 @@ receiver_body(void *data)
|
||||
if (read(targ->g->main_fd, buf, sizeof(buf)) > 0)
|
||||
targ->count++;
|
||||
}
|
||||
#ifndef NO_PCAP
|
||||
} else if (targ->g->dev_type == DEV_PCAP) {
|
||||
while (!targ->cancel) {
|
||||
/* XXX should we poll ? */
|
||||
pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
|
||||
}
|
||||
#endif /* !NO_PCAP */
|
||||
} else {
|
||||
int dump = targ->g->options & OPT_DUMP;
|
||||
while (!targ->cancel) {
|
||||
@ -1533,7 +1548,7 @@ main(int arc, char **argv)
|
||||
g.virt_header = 0;
|
||||
|
||||
while ( (ch = getopt(arc, argv,
|
||||
"a:f:F:n:i:It:r:l:d:s:D:S:b:c:o:p:PT:w:WvR:XC:H:h")) != -1) {
|
||||
"a:f:F:n:i:It:r:l:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:h")) != -1) {
|
||||
struct sf *fn;
|
||||
|
||||
switch(ch) {
|
||||
@ -1575,13 +1590,28 @@ main(int arc, char **argv)
|
||||
break;
|
||||
|
||||
case 'i': /* interface */
|
||||
/* a prefix of tap: netmap: or pcap: forces the mode.
|
||||
* otherwise we guess
|
||||
*/
|
||||
D("interface is %s", optarg);
|
||||
g.ifname = optarg;
|
||||
if (!strncmp(optarg, "tap", 3))
|
||||
g.dev_type = DEV_TAP;
|
||||
else
|
||||
if (!strcmp(optarg, "null")) {
|
||||
g.dev_type = DEV_NETMAP;
|
||||
if (!strcmp(g.ifname, "null"))
|
||||
g.dummy_send = 1;
|
||||
} else if (!strncmp(optarg, "tap:", 4)) {
|
||||
g.dev_type = DEV_TAP;
|
||||
g.ifname = optarg + 4;
|
||||
} else if (!strncmp(optarg, "pcap:", 5)) {
|
||||
g.dev_type = DEV_PCAP;
|
||||
g.ifname = optarg + 5;
|
||||
} else if (!strncmp(optarg, "netmap:", 7)) {
|
||||
g.dev_type = DEV_NETMAP;
|
||||
g.ifname = optarg + 7;
|
||||
} else if (!strncmp(optarg, "tap", 3)) {
|
||||
g.dev_type = DEV_TAP;
|
||||
} else {
|
||||
g.dev_type = DEV_NETMAP;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
@ -1634,10 +1664,6 @@ main(int arc, char **argv)
|
||||
g.nthreads = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
g.dev_type = DEV_PCAP;
|
||||
break;
|
||||
|
||||
case 'D': /* destination mac */
|
||||
g.dst_mac.name = optarg;
|
||||
break;
|
||||
@ -1659,8 +1685,10 @@ main(int arc, char **argv)
|
||||
break;
|
||||
case 'H':
|
||||
g.virt_header = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
g.host_ring = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1697,6 +1725,12 @@ main(int arc, char **argv)
|
||||
extract_mac_range(&g.src_mac);
|
||||
extract_mac_range(&g.dst_mac);
|
||||
|
||||
if (g.src_ip.start != g.src_ip.end ||
|
||||
g.src_ip.port0 != g.src_ip.port1 ||
|
||||
g.dst_ip.start != g.dst_ip.end ||
|
||||
g.dst_ip.port0 != g.dst_ip.port1)
|
||||
g.options |= OPT_COPY;
|
||||
|
||||
if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1
|
||||
&& g.virt_header != VIRT_HDR_2) {
|
||||
D("bad virtio-net-header length");
|
||||
@ -1710,7 +1744,8 @@ main(int arc, char **argv)
|
||||
D("cannot open tap %s", g.ifname);
|
||||
usage();
|
||||
}
|
||||
} else if (g.dev_type > DEV_NETMAP) {
|
||||
#ifndef NO_PCAP
|
||||
} else if (g.dev_type == DEV_PCAP) {
|
||||
char pcap_errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
D("using pcap on %s", g.ifname);
|
||||
@ -1720,7 +1755,8 @@ main(int arc, char **argv)
|
||||
D("cannot open pcap on %s", g.ifname);
|
||||
usage();
|
||||
}
|
||||
} else if (g.dummy_send) {
|
||||
#endif /* !NO_PCAP */
|
||||
} else if (g.dummy_send) { /* but DEV_NETMAP */
|
||||
D("using a dummy send routine");
|
||||
} else {
|
||||
bzero(&nmr, sizeof(nmr));
|
||||
@ -1758,9 +1794,6 @@ main(int arc, char **argv)
|
||||
ND("%s: txr %d txd %d rxr %d rxd %d", g.ifname,
|
||||
nmr.nr_tx_rings, nmr.nr_tx_slots,
|
||||
nmr.nr_rx_rings, nmr.nr_rx_slots);
|
||||
if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) {
|
||||
D("Unable to get if info for %s: %s", g.ifname, strerror(errno));
|
||||
}
|
||||
devqueues = nmr.nr_rx_rings;
|
||||
|
||||
/* validate provided nthreads. */
|
||||
@ -1784,7 +1817,21 @@ main(int arc, char **argv)
|
||||
// continue, fail later
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
struct netmap_if *nifp = NETMAP_IF(g.mmap_addr, nmr.nr_offset);
|
||||
|
||||
D("nifp at offset %d, %d tx %d rx rings %s",
|
||||
nmr.nr_offset, nmr.nr_tx_rings, nmr.nr_rx_rings,
|
||||
nmr.nr_ringid & NETMAP_PRIV_MEM ? "PRIVATE" : "common" );
|
||||
for (i = 0; i <= nmr.nr_tx_rings; i++) {
|
||||
D(" TX%d at 0x%lx", i,
|
||||
(char *)NETMAP_TXRING(nifp, i) - (char *)nifp);
|
||||
}
|
||||
for (i = 0; i <= nmr.nr_rx_rings; i++) {
|
||||
D(" RX%d at 0x%lx", i,
|
||||
(char *)NETMAP_RXRING(nifp, i) - (char *)nifp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print some debug information. */
|
||||
fprintf(stdout,
|
||||
@ -1846,16 +1893,6 @@ main(int arc, char **argv)
|
||||
global_nthreads = g.nthreads;
|
||||
signal(SIGINT, sigint_h);
|
||||
|
||||
#if 0 // XXX this is not needed, i believe
|
||||
if (g.dev_type > DEV_NETMAP) {
|
||||
g.p = pcap_open_live(g.ifname, 0, 1, 100, NULL);
|
||||
if (g.p == NULL) {
|
||||
D("cannot open pcap on %s", g.ifname);
|
||||
usage();
|
||||
} else
|
||||
D("using pcap %p on %s", g.p, g.ifname);
|
||||
}
|
||||
#endif // XXX
|
||||
start_threads(&g);
|
||||
main_thread(&g);
|
||||
return 0;
|
||||
|
@ -69,20 +69,22 @@ bdg_ctl(const char *name, int nr_cmd, int nr_arg)
|
||||
nr_arg = 0;
|
||||
nmr.nr_arg1 = nr_arg;
|
||||
error = ioctl(fd, NIOCREGIF, &nmr);
|
||||
if (error == -1)
|
||||
D("Unable to %s %s to the bridge", nr_cmd ==
|
||||
if (error == -1) {
|
||||
ND("Unable to %s %s to the bridge", nr_cmd ==
|
||||
NETMAP_BDG_DETACH?"detach":"attach", name);
|
||||
else
|
||||
D("Success to %s %s to the bridge\n", nr_cmd ==
|
||||
perror(name);
|
||||
} else
|
||||
ND("Success to %s %s to the bridge", nr_cmd ==
|
||||
NETMAP_BDG_DETACH?"detach":"attach", name);
|
||||
break;
|
||||
|
||||
case NETMAP_BDG_LIST:
|
||||
if (strlen(nmr.nr_name)) { /* name to bridge/port info */
|
||||
error = ioctl(fd, NIOCGINFO, &nmr);
|
||||
if (error)
|
||||
D("Unable to obtain info for %s", name);
|
||||
else
|
||||
if (error) {
|
||||
ND("Unable to obtain info for %s", name);
|
||||
perror(name);
|
||||
} else
|
||||
D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
|
||||
nmr.nr_arg2);
|
||||
break;
|
||||
@ -101,9 +103,10 @@ bdg_ctl(const char *name, int nr_cmd, int nr_arg)
|
||||
default: /* GINFO */
|
||||
nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
|
||||
error = ioctl(fd, NIOCGINFO, &nmr);
|
||||
if (error)
|
||||
D("Unable to get if info for %s", name);
|
||||
else
|
||||
if (error) {
|
||||
ND("Unable to get if info for %s", name);
|
||||
perror(name);
|
||||
} else
|
||||
D("%s: %d queues.", name, nmr.nr_rx_rings);
|
||||
break;
|
||||
}
|
||||
@ -164,6 +167,5 @@ main(int argc, char *argv[])
|
||||
}
|
||||
if (argc == 1)
|
||||
nr_cmd = NETMAP_BDG_LIST;
|
||||
bdg_ctl(name, nr_cmd, nr_arg);
|
||||
return 0;
|
||||
return bdg_ctl(name, nr_cmd, nr_arg) ? 1 : 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user