Commit 3d46d1b5 authored by Eddie Kohler's avatar Eddie Kohler

User-level Click: Support netmap.

With Luigi Rizzo. We look for the netmap header, and if provided, support
"METHOD NETMAP". Netmap is an efficient way to move packets between the
kernel and user level using shared memory packet rings. This checkin
provides basic netmap support for FromDevice and ToDevice.
Signed-off-by: default avatarEddie Kohler <ekohler@gmail.com>
parent 61d61451
......@@ -113,6 +113,9 @@
/* Define if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define if you have the <net/netmap.h> header file. */
#undef HAVE_NET_NETMAP_H
/* Define if you have the <netpacket/packet.h> header file. */
#undef HAVE_NETPACKET_PACKET_H
......
......@@ -10161,7 +10161,61 @@ done
LDFLAGS="$saveflags"
fi
if test "$HAVE_PCAP" != yes -a "$ac_cv_under_linux" != yes; then
HAVE_NETMAP=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for net/netmap.h" >&5
$as_echo_n "checking for net/netmap.h... " >&6; }
if ${ac_cv_net_netmap_header_path+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <net/netmap.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
ac_cv_net_netmap_header_path="found"
else
ac_cv_net_netmap_header_path="not found"
fi
rm -f conftest.err conftest.i conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_net_netmap_header_path" >&5
$as_echo "$ac_cv_net_netmap_header_path" >&6; }
if test "$ac_cv_net_netmap_header_path" = "found"; then
HAVE_NETMAP=yes
fi
if test "$HAVE_NETMAP" = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether net/netmap.h works" >&5
$as_echo_n "checking whether net/netmap.h works... " >&6; }
if ${ac_cv_working_net_netmap_h+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <net/netmap.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
ac_cv_working_net_netmap_h=yes
else
ac_cv_working_net_netmap_h=no
fi
rm -f conftest.err conftest.i conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_net_netmap_h" >&5
$as_echo "$ac_cv_working_net_netmap_h" >&6; }
test "$ac_cv_working_net_netmap_h" != yes && HAVE_NETMAP=
fi
if test "$HAVE_NETMAP" = yes; then
$as_echo "#define HAVE_NET_NETMAP_H 1" >>confdefs.h
fi
if test "$HAVE_PCAP" != yes -a "$HAVE_NETMAP" != yes -a "$ac_cv_under_linux" != yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
=========================================
......
......@@ -1058,7 +1058,8 @@ dnl note: no longer need pcap under Linux
if test "$enable_userlevel" = yes; then
CLICK_CHECK_LIBPCAP
if test "$HAVE_PCAP" != yes -a "$ac_cv_under_linux" != yes; then
CLICK_CHECK_NETMAP
if test "$HAVE_PCAP" != yes -a "$HAVE_NETMAP" != yes -a "$ac_cv_under_linux" != yes; then
AC_MSG_WARN([
=========================================
......
This diff is collapsed.
......@@ -2,12 +2,13 @@
#define CLICK_FROMDEVICE_USERLEVEL_HH
#include <click/element.hh>
#include "elements/userlevel/kernelfilter.hh"
#ifdef __linux__
# define FROMDEVICE_LINUX 1
# define FROMDEVICE_ALLOW_LINUX 1
#endif
#if HAVE_PCAP
# define FROMDEVICE_PCAP 1
# include <click/task.hh>
# define FROMDEVICE_ALLOW_PCAP 1
extern "C" {
# include <pcap.h>
/* Prototype pcap_setnonblock if we have it, but not the prototype. */
......@@ -17,6 +18,18 @@ int pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf);
void FromDevice_get_packet(u_char*, const struct pcap_pkthdr*, const u_char*);
}
#endif
#if HAVE_NET_NETMAP_H
# define FROMDEVICE_ALLOW_NETMAP 1
# include <net/if.h>
# include <net/netmap.h>
# include <net/netmap_user.h>
#endif
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
# include <click/task.hh>
#endif
CLICK_DECLS
/*
......@@ -163,32 +176,55 @@ class FromDevice : public Element { public:
inline int fd() const { return _fd; }
void selected(int fd, int mask);
#if FROMDEVICE_PCAP
#if FROMDEVICE_ALLOW_PCAP
pcap_t *pcap() const { return _pcap; }
bool run_task(Task *);
static const char *pcap_error(pcap_t *pcap, const char *ebuf);
static pcap_t *open_pcap(String ifname, int snaplen, bool promisc, ErrorHandler *errh);
#endif
#if FROMDEVICE_LINUX
int linux_fd() const { return _capture == CAPTURE_LINUX ? _fd : -1; }
#if FROMDEVICE_ALLOW_LINUX
int linux_fd() const { return _method == method_linux ? _fd : -1; }
static int open_packet_socket(String, ErrorHandler *);
static int set_promiscuous(int, String, bool);
#endif
#if FROMDEVICE_ALLOW_NETMAP
struct netmap_type {
char *mem;
size_t memsize;
unsigned ring_begin;
unsigned ring_end;
struct netmap_if *nifp;
int open(const String &ifname, bool always_error,
ErrorHandler *errh);
};
const netmap_type *netmap() const { return _method == method_netmap ? &_netmap : 0; }
#endif
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
bool run_task(Task *task);
#endif
void kernel_drops(bool& known, int& max_drops) const;
private:
#if FROMDEVICE_LINUX || FROMDEVICE_PCAP
#if FROMDEVICE_ALLOW_LINUX || FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
int _fd;
#endif
#if FROMDEVICE_LINUX
#if FROMDEVICE_ALLOW_NETMAP || FROMDEVICE_ALLOW_PCAP
Task _task;
#endif
#if FROMDEVICE_ALLOW_LINUX
unsigned char *_linux_packetbuf;
#endif
#if FROMDEVICE_PCAP
#if FROMDEVICE_ALLOW_PCAP || FROMDEVICE_ALLOW_NETMAP
void emit_packet_data(const unsigned char *buf, int len, int fulllen,
const Timestamp &ts);
#endif
#if FROMDEVICE_ALLOW_PCAP
pcap_t *_pcap;
Task _pcap_task;
int _pcap_complaints;
friend void FromDevice_get_packet(u_char*, const struct pcap_pkthdr*,
const u_char*);
......@@ -196,6 +232,11 @@ class FromDevice : public Element { public:
return pcap_error(_pcap, ebuf);
}
#endif
#if FROMDEVICE_ALLOW_NETMAP
netmap_type _netmap;
int netmap_dispatch();
#endif
bool _force_ip;
int _burst;
int _datalink;
......@@ -214,9 +255,9 @@ class FromDevice : public Element { public:
int _was_promisc : 2;
int _snaplen;
unsigned _headroom;
enum { CAPTURE_PCAP, CAPTURE_LINUX };
int _capture;
#if FROMDEVICE_PCAP
enum { method_default, method_netmap, method_pcap, method_linux };
int _method;
#if FROMDEVICE_ALLOW_PCAP
String _bpf_filter;
#endif
......
......@@ -54,6 +54,9 @@
# include <linux/if_packet.h>
# endif
#endif
#if TODEVICE_ALLOW_NETMAP
# include <sys/mman.h>
#endif
CLICK_DECLS
......@@ -64,7 +67,7 @@ ToDevice::ToDevice()
_pcap = 0;
_my_pcap = false;
#endif
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD || TODEVICE_ALLOW_NETMAP
_fd = -1;
_my_fd = false;
#endif
......@@ -92,19 +95,8 @@ ToDevice::configure(Vector<String> &conf, ErrorHandler *errh)
return errh->error("bad BURST");
if (method == "") {
#if TODEVICE_ALLOW_PCAP && TODEVICE_ALLOW_LINUX
_method = method_pcap;
if (FromDevice *fd = find_fromdevice())
if (fd->linux_fd())
_method = method_linux;
#elif TODEVICE_ALLOW_PCAP
_method = method_pcap;
#elif TODEVICE_ALLOW_LINUX
_method = method_linux;
#elif TODEVICE_ALLOW_DEVBPF
_method = method_devbpf;
#elif TODEVICE_ALLOW_PCAPFD
_method = method_pcapfd;
#if TODEVICE_ALLOW_PCAP || TODEVICE_ALLOW_PCAPFD || TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_NETMAP
_method = method_default;
#else
return errh->error("cannot send packets on this platform");
#endif
......@@ -124,6 +116,10 @@ ToDevice::configure(Vector<String> &conf, ErrorHandler *errh)
#if TODEVICE_ALLOW_PCAPFD
else if (method == "PCAPFD")
_method = method_pcapfd;
#endif
#if TODEVICE_ALLOW_NETMAP
else if (method == "NETMAP")
_method = method_netmap;
#endif
else
return errh->error("bad METHOD");
......@@ -148,9 +144,41 @@ ToDevice::initialize(ErrorHandler *errh)
{
_timer.initialize(this);
FromDevice *fd = find_fromdevice();
if (_method == method_default) {
#if FROMDEVICE_ALLOW_NETMAP
if (fd->netmap())
_method = method_netmap;
#endif
#if FROMDEVICE_ALLOW_PCAP
if (fd->pcap())
_method = method_pcap;
#endif
#if FROMDEVICE_ALLOW_LINUX
if (fd->linux_fd() >= 0)
_method = method_linux;
#endif
}
#if TODEVICE_ALLOW_NETMAP
if (_method == method_default || _method == method_netmap) {
if (fd && fd->netmap()) {
_fd = fd->fd();
_netmap = *fd->netmap();
} else {
_fd = _netmap.open(_ifname, _method == method_netmap, errh);
if (_fd >= 0)
_my_fd = true;
else if (_method == method_netmap)
return -1;
}
if (_fd >= 0)
_method = method_netmap;
}
#endif
#if TODEVICE_ALLOW_PCAP
if (_method == method_pcap) {
FromDevice *fd = find_fromdevice();
if (_method == method_default || _method == method_pcap) {
if (fd && fd->pcap())
_pcap = fd->pcap();
else {
......@@ -160,12 +188,12 @@ ToDevice::initialize(ErrorHandler *errh)
_my_pcap = true;
}
_fd = pcap_fileno(_pcap);
/* _my_fd = false by default */
_method = method_pcap;
}
#endif
#if TODEVICE_ALLOW_DEVBPF
if (_method == method_devbpf) {
if (_method == method_default || _method == method_devbpf) {
/* pcap_open_live() doesn't open for writing. */
for (int i = 0; i < 16 && _fd < 0; i++) {
char tmp[64];
......@@ -186,12 +214,12 @@ ToDevice::initialize(ErrorHandler *errh)
if (ioctl(_fd, BIOCSHDRCMPLT, (caddr_t)&yes) < 0)
errh->warning("BIOCSHDRCMPLT %s failed", ifr.ifr_name);
# endif
_method = method_devbpf;
}
#endif
#if TODEVICE_ALLOW_LINUX
if (_method == method_linux) {
FromDevice *fd = find_fromdevice();
if (_method == method_default || _method == method_linux) {
if (fd && fd->linux_fd() >= 0)
_fd = fd->linux_fd();
else {
......@@ -200,16 +228,18 @@ ToDevice::initialize(ErrorHandler *errh)
return -1;
_my_fd = true;
}
_method = method_linux;
}
#endif
#if TODEVICE_ALLOW_PCAPFD
if (_method == method_pcapfd) {
if (_method == method_default || _method == method_pcapfd) {
FromDevice *fd = find_fromdevice();
if (fd && fd->pcap())
_fd = fd->fd();
else
return errh->error("initialized FromDevice required on this platform");
_method = method_pcapfd;
}
#endif
......@@ -232,7 +262,14 @@ ToDevice::cleanup(CleanupStage)
pcap_close(_pcap);
_pcap = 0;
#endif
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD
#if TODEVICE_ALLOW_NETMAP
if (_fd >= 0 && _my_fd && _method == method_netmap) {
munmap(_netmap.mem, _netmap.memsize);
ioctl(_fd, NIOCUNREGIF, (struct nmreq *) 0);
_netmap.mem = 0;
}
#endif
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD || TODEVICE_ALLOW_NETMAP
if (_fd >= 0 && _my_fd)
close(_fd);
_fd = -1;
......@@ -240,6 +277,31 @@ ToDevice::cleanup(CleanupStage)
}
#if TODEVICE_ALLOW_NETMAP
int
ToDevice::netmap_send_packet(Packet *p)
{
for (unsigned ri = _netmap.ring_begin; ri != _netmap.ring_end; ++ri) {
struct netmap_ring *ring = NETMAP_TXRING(_netmap.nifp, ri);
if (ring->avail == 0)
continue;
unsigned i = ring->cur;
unsigned buf_idx = ring->slot[i].buf_idx;
if (buf_idx < 2)
continue;
unsigned char *buf = (unsigned char *) NETMAP_BUF(ring, buf_idx);
memcpy(buf, p->data(), p->length());
ring->slot[i].len = p->length();
__asm__ volatile("" : : : "memory");
ring->cur = NETMAP_RING_NEXT(ring, i);
ring->avail--;
return 0;
}
errno = ENOBUFS;
return -1;
}
#endif
/*
* Linux select marks datagram fd's as writeable when the socket
* buffer has enough space to do a send (sock_writeable() in
......@@ -256,6 +318,11 @@ ToDevice::send_packet(Packet *p)
int r = 0;
errno = 0;
#if TODEVICE_ALLOW_NETMAP
if (_method == method_netmap)
r = netmap_send_packet(p);
#endif
#if TODEVICE_ALLOW_PCAP
if (_method == method_pcap) {
# if HAVE_PCAP_INJECT
......
......@@ -77,10 +77,16 @@ extern "C" {
#elif defined(__sun)
# define TODEVICE_ALLOW_PCAPFD 1
#endif
class FromDevice;
#if FROMDEVICE_ALLOW_NETMAP
# define TODEVICE_ALLOW_NETMAP 1
#endif
class ToDevice : public Element { public:
#if TODEVICE_ALLOW_NETMAP
typedef struct FromDevice::netmap_type netmap_type;
#endif
ToDevice();
~ToDevice();
......@@ -110,10 +116,14 @@ class ToDevice : public Element { public:
#if TODEVICE_ALLOW_PCAP
pcap_t *_pcap;
#endif
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD || TODEVICE_ALLOW_NETMAP
int _fd;
#endif
enum { method_linux, method_pcap, method_devbpf, method_pcapfd };
#if TODEVICE_ALLOW_NETMAP
netmap_type _netmap;
int netmap_send_packet(Packet *p);
#endif
enum { method_default, method_netmap, method_linux, method_pcap, method_devbpf, method_pcapfd };
int _method;
NotifierSignal _signal;
......@@ -124,7 +134,7 @@ class ToDevice : public Element { public:
#if TODEVICE_ALLOW_PCAP
bool _my_pcap;
#endif
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD || TODEVICE_ALLOW_NETMAP
bool _my_fd;
#endif
int _backoff;
......
......@@ -380,6 +380,36 @@ AC_DEFUN([CLICK_CHECK_LIBPCAP], [
])
dnl
dnl CLICK_CHECK_NETMAP
dnl Finds header files for netmap.
dnl
AC_DEFUN([CLICK_CHECK_NETMAP], [
HAVE_NETMAP=no
AC_CACHE_CHECK([for net/netmap.h], [ac_cv_net_netmap_header_path], [
AC_PREPROC_IFELSE([AC_LANG_SOURCE([[#include <net/netmap.h>]])],
[ac_cv_net_netmap_header_path="found"],
[ac_cv_net_netmap_header_path="not found"])])
if test "$ac_cv_net_netmap_header_path" = "found"; then
HAVE_NETMAP=yes
fi
if test "$HAVE_NETMAP" = yes; then
AC_CACHE_CHECK([whether net/netmap.h works],
[ac_cv_working_net_netmap_h], [
AC_PREPROC_IFELSE([AC_LANG_SOURCE([[#include <net/netmap.h>]])],
[ac_cv_working_net_netmap_h=yes],
[ac_cv_working_net_netmap_h=no])])
test "$ac_cv_working_net_netmap_h" != yes && HAVE_NETMAP=
fi
if test "$HAVE_NETMAP" = yes; then
AC_DEFINE([HAVE_NET_NETMAP_H], [1], [Define if you have the <net/netmap.h> header file.])
fi
])
dnl
dnl CLICK_PROG_INSTALL
dnl Substitute both INSTALL and INSTALL_IF_CHANGED.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment