Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F120417817
todevice.u.cc
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Jul 4, 05:55
Size
13 KB
Mime Type
text/x-c
Expires
Sun, Jul 6, 05:55 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
27182103
Attached To
R6591 HyMAB
todevice.u.cc
View Options
/*
* todevice.{cc,hh} -- element writes packets to network via pcap library
* Douglas S. J. De Couto, Eddie Kohler, John Jannotti
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2005-2008 Regents of the University of California
* Copyright (c) 2011 Meraki, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#if HAVE_NET_BPF_H
# include <sys/types.h>
# include <sys/time.h>
# include <net/bpf.h>
# define PCAP_DONT_INCLUDE_PCAP_BPF_H 1
#endif
#include "todevice.hh"
#include <click/error.hh>
#include <click/etheraddress.hh>
#include <click/args.hh>
#include <click/router.hh>
#include <click/standard/scheduleinfo.hh>
#include <click/packet_anno.hh>
#include <click/straccum.hh>
#include <stdio.h>
#include <unistd.h>
#if TODEVICE_ALLOW_DEVBPF
# include <fcntl.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <net/if.h>
#endif
#if TODEVICE_ALLOW_LINUX
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <net/if.h>
//# include <net/if_packet.h>
# include <features.h>
# if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
# include <netpacket/packet.h>
# else
# include <linux/if_packet.h>
# endif
#endif
#if TODEVICE_ALLOW_NETMAP
//# include <sys/mman.h>
#endif
CLICK_DECLS
ToDevice::ToDevice()
: _task(this), _timer(&_task), _q(0), _pulls(0)
{
#if TODEVICE_ALLOW_PCAP
_pcap = 0;
_my_pcap = false;
#endif
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD || TODEVICE_ALLOW_NETMAP
_fd = -1;
_my_fd = false;
#endif
}
ToDevice::~ToDevice()
{
}
int
ToDevice::configure(Vector<String> &conf, ErrorHandler *errh)
{
String method;
_burst = 1;
_debug = false;
_verb_debug = false;
if (Args(conf, this, errh)
.read_mp("DEVNAME", _ifname)
.read("DEBUG", _debug)
.read("VERB_DEBUG", _verb_debug)
.read("METHOD", WordArg(), method)
.read("BURST", _burst)
.complete() < 0)
return -1;
if (!_ifname)
return errh->error("interface not set");
if (_burst <= 0)
return errh->error("bad BURST");
if (method == "") {
#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
}
#if TODEVICE_ALLOW_PCAP
else if (method == "PCAP")
_method = method_pcap;
#endif
#if TODEVICE_ALLOW_LINUX
else if (method == "LINUX")
_method = method_linux;
#endif
#if TODEVICE_ALLOW_DEVBPF
else if (method == "DEVBPF")
_method = method_devbpf;
#endif
#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");
return 0;
}
FromDevice *
ToDevice::find_fromdevice() const
{
Router *r = router();
for (int ei = 0; ei < r->nelements(); ++ei) {
FromDevice *fd = (FromDevice *) r->element(ei)->cast("FromDevice");
if (fd && fd->ifname() == _ifname && fd->fd() >= 0)
return fd;
}
return 0;
}
int
ToDevice::initialize(ErrorHandler *errh)
{
_timer.initialize(this);
FromDevice *fd = find_fromdevice();
if (fd && _method == method_default) {
#if FROMDEVICE_ALLOW_NETMAP && TODEVICE_ALLOW_NETMAP
if (fd->netmap())
_method = method_netmap;
#endif
#if FROMDEVICE_ALLOW_PCAP && TODEVICE_ALLOW_PCAP
if (fd->pcap())
_method = method_pcap;
#endif
#if FROMDEVICE_ALLOW_LINUX && TODEVICE_ALLOW_LINUX
if (fd->linux_fd() >= 0)
_method = method_linux;
#endif
}
#if TODEVICE_ALLOW_NETMAP
// first choice is netmap by default
if (_method == method_default || _method == method_netmap) {
if (fd && fd->netmap()) { // fromdevice already open, reuse
_fd = fd->fd();
_netmap = *fd->netmap();
} else {
_fd = _netmap.open(_ifname, _method == method_netmap, errh);
if (_fd >= 0) {
_my_fd = true;
add_select(_fd, SELECT_READ); // NB NOT writable!
} else if (_method == method_netmap)
return -1; // fail
}
if (_fd >= 0) {
_method = method_netmap;
_netmap.initialize_rings_tx(); // no-op
}
}
#endif
#if TODEVICE_ALLOW_PCAP
if (_method == method_default || _method == method_pcap) {
if (fd && fd->pcap())
_pcap = fd->pcap();
else {
_pcap = FromDevice::open_pcap(_ifname, FromDevice::default_snaplen, false, errh);
if (!_pcap)
return -1;
_my_pcap = true;
}
_fd = pcap_fileno(_pcap);
_method = method_pcap;
}
#endif
#if TODEVICE_ALLOW_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];
sprintf(tmp, "/dev/bpf%d", i);
_fd = open(tmp, 1);
}
if (_fd < 0)
return(errh->error("open /dev/bpf* for write: %s", strerror(errno)));
_my_fd = true;
struct ifreq ifr;
strncpy(ifr.ifr_name, _ifname.c_str(), sizeof(ifr.ifr_name));
ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = 0;
if (ioctl(_fd, BIOCSETIF, (caddr_t)&ifr) < 0)
return errh->error("BIOCSETIF %s failed", ifr.ifr_name);
# ifdef BIOCSHDRCMPLT
int yes = 1;
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_default || _method == method_linux) {
if (fd && fd->linux_fd() >= 0)
_fd = fd->linux_fd();
else {
_fd = FromDevice::open_packet_socket(_ifname, errh);
if (_fd < 0)
return -1;
_my_fd = true;
}
_method = method_linux;
}
#endif
#if TODEVICE_ALLOW_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
// check for duplicate writers
void *&used = router()->force_attachment("device_writer_" + _ifname);
if (used)
return errh->error("duplicate writer for device %<%s%>", _ifname.c_str());
used = this;
ScheduleInfo::join_scheduler(this, &_task, errh);
_signal = Notifier::upstream_empty_signal(this, 0, &_task);
return 0;
}
void
ToDevice::cleanup(CleanupStage)
{
#if TODEVICE_ALLOW_PCAP
if (_pcap && _my_pcap)
pcap_close(_pcap);
_pcap = 0;
#endif
#if TODEVICE_ALLOW_NETMAP
if (_fd >= 0 && _my_fd && _method == method_netmap) {
_netmap.close(_fd); // XXX _fd is not really needed
_fd = -1;
}
#endif
#if TODEVICE_ALLOW_LINUX || TODEVICE_ALLOW_DEVBPF || TODEVICE_ALLOW_PCAPFD || TODEVICE_ALLOW_NETMAP
if (_fd >= 0 && _my_fd)
close(_fd);
_fd = -1;
#endif
}
/*
* Linux select marks datagram fd's as writeable when the socket
* buffer has enough space to do a send (sock_writeable() in
* sock.h). BSD select always marks datagram fd's as writeable
* (bpf_poll() in sys/net/bpf.c) This function should behave
* appropriately under both. It makes use of select if it correctly
* tells us when buffers are available, and it schedules a backoff
* timer if buffers are not available.
* --jbicket
*/
int
ToDevice::send_packet(Packet *p)
{
int r = 0;
errno = 0;
#if TODEVICE_ALLOW_NETMAP
if (_method == method_netmap) {
if (_netmap.send_packet(p, noutputs())) { // fail
errno = ENOBUFS;
r = -1;
} else
r = 0;
}
#endif
#if TODEVICE_ALLOW_PCAP
if (_method == method_pcap) {
# if HAVE_PCAP_INJECT
r = pcap_inject(_pcap, p->data(), p->length());
# else
r = pcap_sendpacket(_pcap, p->data(), p->length());
# endif
}
#endif
#if TODEVICE_ALLOW_LINUX
if (_method == method_linux)
r = send(_fd, p->data(), p->length(), 0);
#endif
#if TODEVICE_ALLOW_DEVBPF
if (_method == method_devbpf)
if (write(_fd, p->data(), p->length()) != (ssize_t) p->length())
r = -1;
#endif
#if TODEVICE_ALLOW_PCAPFD
if (_method == method_pcapfd)
if (write(_fd, p->data(), p->length()) != (ssize_t) p->length())
r = -1;
#endif
if (r >= 0) {
if(_verb_debug) click_chatter("[ToDevice %s:%s] send_packet called, success", name().c_str(), Timestamp::now().unparse().c_str());
return 0;
}
else {
if(_verb_debug) click_chatter("[ToDevice %s:%s] send_packet called, error %d", name().c_str(), Timestamp::now().unparse().c_str(), errno ? -errno : -EINVAL);
return errno ? -errno : -EINVAL;
}
}
bool
ToDevice::run_task(Task *)
{
Packet *p = _q;
_q = 0;
int count = 0, r = 0;
if(_verb_debug) click_chatter("[ToDevice %s:%s] run_task called", name().c_str(), Timestamp::now().unparse().c_str());
Timestamp at_run_task = Timestamp::now();
Timestamp pulls;
Timestamp sends;
String out_str;
do {
if (!p) {
++_pulls;
pulls -= Timestamp::now();
if (!(p = input(0).pull())) {
pulls += Timestamp::now();
out_str=", pull is null";
break;
}
pulls += Timestamp::now();
}
sends -= Timestamp::now();
if ((r = send_packet(p)) >= 0) {
sends += Timestamp::now();
_backoff = 0;
checked_output_push(0, p);
++count;
p = 0;
} else {
out_str=", not sent, errno "+String(-r);
sends += Timestamp::now();
break;
}
} while (count < _burst);
if((_burst > 1 && count > 1 && _debug) || _verb_debug) {
Timestamp now = Timestamp::now();
click_chatter("[ToDevice %s:%s] Sent %d packets in a pull in %s milliseconds (pulls %s ms, sends %s ms)%s.", name().c_str(),
now.unparse().c_str(), count, (1000*(now-at_run_task)).unparse().c_str(),
(1000*pulls).unparse().c_str(), (1000*sends).unparse().c_str(), out_str.c_str());
}
if (r == -ENOBUFS || r == -EAGAIN || r == -EDEADLK) {
assert(!_q);
_q = p;
if (!_backoff) {
if(_verb_debug) click_chatter("[ToDevice %s:%s] backing off for first time", name().c_str(), Timestamp::now().unparse().c_str());
_backoff = 1;
add_select(_fd, SELECT_WRITE);
} else {
_timer.schedule_after(Timestamp::make_usec(_backoff));
if (_backoff < 256)
_backoff *= 2;
if (_verb_debug) {
Timestamp now = Timestamp::now();
click_chatter("%p{element} backing off for %d at %p{timestamp}\n", this, _backoff, &now);
}
}
return count > 0;
} else if (r < 0) {
click_chatter("ToDevice(%s at %s): %s", _ifname.c_str(), Timestamp::now().unparse().c_str(), strerror(-r));
checked_output_push(1, p);
}
if (p || _signal)
_task.fast_reschedule();
return count > 0;
}
void
ToDevice::selected(int, int)
{
if(_verb_debug) click_chatter("[ToDevice %s:%s] selected", name().c_str(), Timestamp::now().unparse().c_str());
_task.reschedule();
remove_select(_fd, SELECT_WRITE);
}
String
ToDevice::read_param(Element *e, void *thunk)
{
ToDevice *td = (ToDevice *)e;
switch((uintptr_t) thunk) {
case h_debug:
return String(td->_debug);
case h_signal:
return String(td->_signal);
case h_pulls:
return String(td->_pulls);
case h_q:
return String((bool) td->_q);
default:
return String();
}
}
int
ToDevice::write_param(const String &in_s, Element *e, void *vparam,
ErrorHandler *errh)
{
ToDevice *td = (ToDevice *)e;
String s = cp_uncomment(in_s);
switch ((intptr_t)vparam) {
case h_debug: {
bool debug;
if (!BoolArg().parse(s, debug))
return errh->error("type mismatch");
td->_debug = debug;
break;
}
case h_verb_debug:
{
bool debug;
if (!BoolArg().parse(s, debug))
return errh->error("type mismatch");
td->_verb_debug = debug;
break;
}
}
return 0;
}
int
ToDevice::set_burst_handler(const String &s, Element *e, void *, ErrorHandler *errh)
{
ToDevice* td = static_cast<ToDevice*>(e);
int arg;
if(!cp_integer(s, &arg))
return errh->error("Burst must be an integer");
td->set_burst(arg);
return 0;
}
void
ToDevice::add_handlers()
{
add_task_handlers(&_task);
add_read_handler("debug", read_param, h_debug, Handler::CHECKBOX);
add_read_handler("pulls", read_param, h_pulls);
add_read_handler("signal", read_param, h_signal);
add_read_handler("q", read_param, h_q);
add_write_handler("debug", write_param, h_debug);
add_write_handler("verb_debug", write_param, h_verb_debug);
add_write_handler("burst", set_burst_handler, 0);
}
CLICK_ENDDECLS
ELEMENT_REQUIRES(FromDevice userlevel)
EXPORT_ELEMENT(ToDevice)
Event Timeline
Log In to Comment