diff --git a/AUTHORS b/AUTHORS
index 59e7cca30d0f88264af27a3dc8499f6d4ed4874c..ef2d3d1552df2828da1836be7db023fc1282bbd7 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,6 +3,10 @@ benjie@lcs.mit.edu
polling extensions, Linux kernel patches, stride scheduling, Linux kernel
thread, device driver updates
+Douglas S. J. DeCouto
+decouto@lcs.mit.edu
+user level elements
+
Thomer Gil
thomer@lcs.mit.edu
dynamic IP routing table
diff --git a/doc/element2man.pl b/doc/element2man.pl
index bcebd5d064d730fe4ea2ef3f3855b8c65d13fef9..4c546a7cd042bd42beca8b6c128b48916819339d 100755
--- a/doc/element2man.pl
+++ b/doc/element2man.pl
@@ -12,10 +12,10 @@
# version. For more information, see the `COPYRIGHT' file in the source
# distribution.
-my(%section_is_array) = ( 'h' => 1, 'a' => 1 );
+my(%section_is_array) = ( 'h' => 1, 'a' => 1, 'page' => 1 );
my $directory;
my $section = 'n';
-my(@all_created, %class_name, %processing);
+my(@all_outnames, %all_outsections, %class_name, %processing);
my(%processing_constants) =
( 'AGNOSTIC' => 'a/a', 'PUSH' => 'h/h', 'PULL' => 'l/l',
@@ -42,24 +42,19 @@ my $prologue = <<'EOD;';
EOD;
chomp $prologue;
-sub nroffize ($;$$$) {
- my($t, $embolden, $srelated, $related_source) = @_;
+sub nroffize ($;$$) {
+ my($t, $related, $related_source) = @_;
my($i);
# embolden & manpageize
- if (defined $embolden) {
- foreach $i (sort { length($b) <=> length($a) } @$embolden) {
- $t =~ s{(^|[^\w@/])$i($|[^\w@/])}{$1<B>$i</B>$2}gs;
- }
- }
- if (defined $srelated) {
- foreach $i (@$srelated) {
- $t =~ s{(^|[^\w@/])$i($|[^\w@/(])}{$1<\#>$i</\#>$2}gs;
+ if (defined $related) {
+ foreach $i (@$related) {
+ $t =~ s{(^|[^\w@/>])$i($|[^\w@/])}{$1<\#>$i</\#>$2}gs;
}
}
# remove emboldening & manpaging on examples
- 1 while ($t =~ s{^= (.*)</?[B\#]>}{= $1}gm);
+ 1 while ($t =~ s{^= (.*)</?[\#]>}{= $1}gm);
$t =~ s/\\/\\\\/g;
$t =~ s/^(= )?\./$1\\&./gm;
@@ -68,7 +63,14 @@ sub nroffize ($;$$$) {
$t =~ s/<i>(.*?)<\/i>/\\fI$1\\fP/ig;
$t =~ s/<b>(.*?)<\/b>/\\fB$1\\fP/ig;
$t =~ s/<tt>(.*?)<\/tt>/\\f(CW$1\\fP/ig;
- $t =~ s/<\#>(.*?)<\/\#>(\S*)\s*/\n.M $1 $related_source->{$1} $2\n/g;
+ $t =~ s{<\#>(.*?)<\/\#>(\S*)(\s*)}{
+ if ($related_source->{$1}) {
+ "\n.M $1 \"$related_source->{$1}\" $2\n";
+ } else {
+ "\\fB$1\\fP$2$3";
+ }
+ }eg;
+ $t =~ s{\n\.M (\S+) \"(\S+)\" \(\2\)\n}{\n.M $1 "$2"\n}g;
1 while ($t =~ s/^\.PP\n\.PP\n/.PP\n/gm);
$t =~ s/^= (.*\n)/.nf\n$1.fi\n/mg;
$t =~ s/^\.fi\n\.nf\n//mg;
@@ -100,7 +102,8 @@ sub process_comment ($$) {
$x{$1} .= "$2\n";
}
}
-
+
+ # snarf class names
my(@classes, %classes);
while ($x{'c'} =~ /^\s*(\w+)\(/mg) { # configuration arguments section
push @classes, $1 if !exists $classes{$1};
@@ -114,36 +117,76 @@ sub process_comment ($$) {
print STDERR "$filename: no class definitions\n (did you forget `()' in the =c section?)\n";
return;
}
- my($classes_plural) = (@classes == 1 ? '' : 's');
- my($classes) = join(', ', @classes);
+
+ # output filenames might be specified in 'page' section
+ my(@outfiles) = @classes;
+ my(@outsections) = ($section) x @classes;
+ if ($x{'page'}) {
+ @outfiles = ();
+ @outsections = ();
+ foreach $i (map(split(/\s+/), @{$x{'page'}})) {
+ if ($i =~ /^(.*)\((.*)\)$/) {
+ push @outfiles, $1;
+ push @outsections, $2;
+ } else {
+ push @outfiles, $i;
+ push @outsections, $section;
+ }
+ }
+ }
# open new output file if necessary
+ my($main_outname);
if ($directory) {
- if (!open(OUT, ">$directory/$classes[0].$section")) {
- print STDERR "$directory/$classes[0].$section: $!\n";
+ $main_outname = "$directory/$outfiles[0].$outsections[0]";
+ if (!open(OUT, ">$main_outname")) {
+ print STDERR "$main_outname: $!\n";
return;
}
}
- push @all_created, $classes[0];
-
+ push @all_outfiles, $outfiles[0];
+ $all_outsections{$outfiles[0]} = $outsections[0];
+
+ # front matter
+ my($classes_text) = join(', ', @classes);
+ my($oneliner) = ($x{'desc'} ? $x{'desc'} : (@classes == 1 ? "Click element" : "Click elements"));
+ my($outfiles_text) = join(', ', @outfiles);
+
print OUT <<"EOD;";
.\\" -*- mode: nroff -*-
.\\" Generated by \`element2man.pl' from \`$filename'
$prologue
-.TH "\U$classes\E" $section "$today" "Click"
+.TH "\U$outfiles_text\E" $outsections[0] "$today" "Click"
.SH "NAME"
-$classes \- Click element$classes_plural
+$classes_text \- $oneliner
EOD;
+ # prepare related
+ my(@related, @srelated, %related_source, %srelated_source);
+ if ($x{'a'}) {
+ foreach $i (map(split(/\s+/), @{$x{'a'}})) {
+ if ($i =~ /^(.*)\((.*)\)$/) {
+ push @related, $1;
+ $related_source{$1} = $2;
+ } else {
+ push @related, $i;
+ $related_source{$i} = 'n';
+ }
+ }
+ }
+ @srelated = sort { length($b) <=> length($a) } (@related, @classes);
+ %srelated_source = %related_source;
+ map(delete $srelated_source{$_}, @classes);
+
if ($x{'c'}) {
print OUT ".SH \"SYNOPSIS\"\n";
while ($x{'c'} =~ /^\s*(\S.*)$/mg) {
- print OUT nroffize($1, \@classes), "\n.br\n";
+ print OUT nroffize($1, \@srelated), "\n.br\n";
}
}
- if (@classes == 1 && $processing{$classes}) {
- my $p = process_processing($processing{$classes});
+ if (@classes == 1 && $processing{$classes[0]}) {
+ my $p = process_processing($processing{$classes[0]});
if ($p) {
print OUT ".SH \"PROCESSING TYPE\"\n";
print OUT nroffize($p), "\n";
@@ -155,33 +198,19 @@ EOD;
print OUT nroffize($x{'io'});
}
- my(@related, @srelated, %related_source);
- if ($x{'a'}) {
- foreach $i (map(split(/\s+/), @{$x{'a'}})) {
- if ($i =~ /^(.*)\((.*)\)$/) {
- push @related, $1;
- $related_source{$1} = $2;
- } else {
- push @related, $i;
- $related_source{$i} = 'n';
- }
- }
- @srelated = sort { length($b) <=> length($a) } @related;
- }
-
if ($x{'d'}) {
print OUT ".SH \"DESCRIPTION\"\n";
- print OUT nroffize($x{'d'}, \@classes, \@srelated, \%related_source);
+ print OUT nroffize($x{'d'}, \@srelated, \%srelated_source);
}
if ($x{'n'}) {
print OUT ".SH \"NOTES\"\n";
- print OUT nroffize($x{'n'}, \@classes, \@srelated, \%related_source);
+ print OUT nroffize($x{'n'}, \@srelated, \%srelated_source);
}
if ($x{'e'}) {
print OUT ".SH \"EXAMPLES\"\n";
- print OUT nroffize($x{'e'}, \@classes, \@srelated, \%related_source);
+ print OUT nroffize($x{'e'}, \@srelated, \%srelated_source);
}
if ($x{'h'} && @{$x{'h'}}) {
@@ -206,12 +235,14 @@ EOD;
# close output file & make links if appropriate
if ($directory) {
close OUT;
- foreach $i (@classes[1..$#classes]) {
- unlink("$directory/$i.$section");
- if (link "$directory/$classes[0].$section", "$directory/$i.$section") {
- push @all_created, $i;
+ for ($i = 1; $i < @outfiles; $i++) {
+ my($outname) = "$directory/$outfiles[$i].$outsections[$i]";
+ unlink($outname);
+ if (link $main_outname, $outname) {
+ push @all_outfiles, $outfiles[$i];
+ $all_outsections{$outfiles[$i]} = $outsections[$i];
} else {
- print STDERR "$directory/$i.$section: $!\n";
+ print STDERR "$outname: $!\n";
}
}
}
@@ -323,13 +354,13 @@ This page lists all Click element classes that have manual page documentation.
.SH "SEE ALSO"
.nh
EOD;
- @all_created = sort @all_created;
- my($last) = pop @all_created;
- print OUT map(".M $_ n ,\n", @all_created);
- print OUT ".M $last n\n.hy\n";
+ @all_outfiles = sort @all_outfiles;
+ my($last) = pop @all_outfiles;
+ print OUT map(".M $_ $all_outsections{$_} ,\n", @all_outfiles);
+ print OUT ".M $last $all_outsections{$last}\n.hy\n";
close OUT if $directory;
}
-if ($elementlist && @all_created) {
+if ($elementlist && @all_outfiles) {
make_elementlist();
}
diff --git a/elements/ip/ipprint.cc b/elements/ip/ipprint.cc
index 3a098cefa4e9ab49a6bd14dbab99ec01329921c4..a5410f776c0d58967c56ec16fd0951f30e95ab81 100644
--- a/elements/ip/ipprint.cc
+++ b/elements/ip/ipprint.cc
@@ -48,7 +48,7 @@ IPPrint::configure(const Vector<String> &conf, ErrorHandler* errh)
if (cp_va_parse(conf, this, errh,
cpString, "label", &_label,
cpOptional,
- cpBool, "print packet contents in hex", &_hex,
+ cpBool, "print packet contents in hex?", &_hex,
cpInteger, "number of bytes to dump", &_bytes,
cpEnd) < 0)
return -1;
diff --git a/elements/linuxmodule/fromdevice.hh b/elements/linuxmodule/fromdevice.hh
index b4b0522e5afffe5ed0e09d6254f28fc5b66a6ca4..3f82fb4fdc2beecc7b12377ba70e384f52d02e16 100644
--- a/elements/linuxmodule/fromdevice.hh
+++ b/elements/linuxmodule/fromdevice.hh
@@ -5,6 +5,11 @@
* =c
* FromDevice(DEVNAME)
* =d
+ *
+ * This manual page describes the Linux kernel module version of the
+ * FromDevice element. For the user-level element, read the FromDevice.u
+ * manual page.
+ *
* Intercepts all packets received by the Linux network interface
* named DEVNAME and pushes them out output 0.
* The packets include the link-level header.
@@ -17,12 +22,11 @@
* This is bad for performance. If you care about performance and have a
* polling-capable device, use PollDevice instead.
*
- * This element is only available inside the kernel module.
- *
* =a PollDevice
* =a ToDevice
* =a FromLinux
- * =a ToLinux */
+ * =a ToLinux
+ * =a FromDevice.u */
#include "anydevice.hh"
diff --git a/elements/linuxmodule/todevice.hh b/elements/linuxmodule/todevice.hh
index 10fad5a1682800e1cc1556b186130f9d6a948247..1091f76b1e5da3e52903a7ac6682501f2cd22c76 100644
--- a/elements/linuxmodule/todevice.hh
+++ b/elements/linuxmodule/todevice.hh
@@ -5,6 +5,10 @@
* =c
* ToDevice(DEVNAME)
* =d
+ *
+ * This manual page describes the Linux kernel module version of the ToDevice
+ * element. For the user-level element, read the ToDevice.u(n) manual page.
+ *
* Sends packets out the Linux network interface named DEVNAME.
*
* Packets must have a link header. For ethernet, ToDevice
@@ -21,13 +25,11 @@
* we depend on the net driver's send operation for synchronization (e.g.
* tulip send operation uses a bit lock).
*
- * This element is only available inside the kernel module.
- *
* =a FromDevice
* =a PollDevice
* =a FromLinux
* =a ToLinux
- */
+ * =a ToDevice.u */
#include "anydevice.hh"
diff --git a/elements/userlevel/fromdevice.cc b/elements/userlevel/fromdevice.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1d6510e3a2cfd066c422fdb768dfb724791a0b5e
--- /dev/null
+++ b/elements/userlevel/fromdevice.cc
@@ -0,0 +1,281 @@
+/*
+ * fromdevice.{cc,hh} -- element reads packets live from network via pcap
+ * Douglas S. J. DeCouto, Eddie Kohler, John Jannotti
+ *
+ * Copyright (c) 1999-2000 Massachusetts Institute of Technology.
+ *
+ * This software is being provided by the copyright holders under the GNU
+ * General Public License, either version 2 or, at your discretion, any later
+ * version. For more information, see the `COPYRIGHT' file in the source
+ * distribution.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "fromdevice.hh"
+#include "todevice.hh"
+#include "error.hh"
+#include "packet.hh"
+#include "confparse.hh"
+#include "elements/standard/scheduleinfo.hh"
+#include "glue.hh"
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#if FROMDEVICE_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>
+# include <net/ethernet.h>
+# else
+# include <linux/if_packet.h>
+# include <linux/if_ether.h>
+# endif
+#endif
+
+FromDevice::FromDevice()
+ : _promisc(0), _packetbuf_size(0)
+{
+ add_output();
+#if FROMDEVICE_PCAP
+ _pcap = 0;
+#endif
+#if FROMDEVICE_LINUX
+ _fd = -1;
+ _packetbuf = 0;
+#endif
+}
+
+FromDevice::~FromDevice()
+{
+ uninitialize();
+}
+
+FromDevice *
+FromDevice::clone() const
+{
+ return new FromDevice;
+}
+
+int
+FromDevice::configure(const Vector<String> &conf, ErrorHandler *errh)
+{
+ bool promisc = false;
+ _packetbuf_size = 2048;
+ if (cp_va_parse(conf, this, errh,
+ cpString, "interface name", &_ifname,
+ cpOptional,
+ cpBool, "be promiscuous?", &promisc,
+#if FROMDEVICE_LINUX
+ cpUnsigned, "maximum packet length", &_packetbuf_size,
+#endif
+ cpEnd) < 0)
+ return -1;
+ if (_packetbuf_size > 8192 || _packetbuf_size < 128)
+ return errh->error("maximum packet length out of range");
+ _promisc = promisc;
+ return 0;
+}
+
+#if FROMDEVICE_LINUX
+int
+FromDevice::open_packet_socket(String ifname, ErrorHandler *errh)
+{
+ int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (fd == -1)
+ return errh->error("%s: socket: %s", ifname.cc(), strerror(errno));
+
+ // get interface index
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname.cc(), sizeof(ifr.ifr_name));
+ int res = ioctl(fd, SIOCGIFINDEX, &ifr);
+ if (res != 0) {
+ close(fd);
+ return errh->error("%s: SIOCGIFINDEX: %s", ifname.cc(), strerror(errno));
+ }
+ int ifindex = ifr.ifr_ifindex;
+
+ // bind to the specified interface. from packet man page, only
+ // sll_protocol and sll_ifindex fields are used; also have to set
+ // sll_family
+ sockaddr_ll sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sll_family = AF_PACKET;
+ sa.sll_protocol = htons(ETH_P_ALL);
+ sa.sll_ifindex = ifindex;
+ res = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (res != 0) {
+ close(fd);
+ return errh->error("%s: bind: %s", ifname.cc(), strerror(errno));
+ }
+
+ // nonblocking I/O on the packet socket so we can poll
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ return fd;
+}
+
+int
+FromDevice::set_promiscuous(int fd, String ifname, bool promisc)
+{
+ // get interface flags
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname.cc(), sizeof(ifr.ifr_name));
+ int res = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ if (res != 0)
+ return -2;
+ int was_promisc = (ifr.ifr_flags & IFF_PROMISC ? 1 : 0);
+
+ // set or reset promiscuous flag
+ if (was_promisc != promisc) {
+ ifr.ifr_flags = (promisc ? ifr.ifr_flags | IFF_PROMISC : ifr.ifr_flags & ~IFF_PROMISC);
+ res = ioctl(fd, SIOCSIFFLAGS, &ifr);
+ if (res != 0)
+ return -3;
+ }
+
+ return was_promisc;
+}
+#endif
+
+int
+FromDevice::initialize(ErrorHandler *errh)
+{
+ if (!_ifname)
+ return errh->error("interface not set");
+
+ /*
+ * Later versions of pcap distributed with linux (e.g. the redhat
+ * linux pcap-0.4-16) want to have a filter installed before they
+ * will pick up any packets.
+ */
+
+#if FROMDEVICE_PCAP
+
+ assert(!_pcap);
+ char *ifname = _ifname.mutable_c_str();
+ char ebuf[PCAP_ERRBUF_SIZE];
+ _pcap = pcap_open_live(ifname,
+ 12000, /* XXX snaplen */
+ _promisc,
+ 1, /* timeout: don't wait for packets */
+ ebuf);
+ if (!_pcap)
+ return errh->error("%s: %s", ifname, ebuf);
+
+ // nonblocking I/O on the packet socket so we can poll
+ int fd = pcap_fileno(_pcap);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ bpf_u_int32 netmask;
+ bpf_u_int32 localnet;
+ if (pcap_lookupnet(ifname, &localnet, &netmask, ebuf) < 0) {
+ errh->warning("%s: %s", ifname, ebuf);
+ }
+
+ struct bpf_program fcode;
+ /*
+ * assume we can use 0 pointer for program string and get ``empty''
+ * filter program.
+ */
+ if (pcap_compile(_pcap, &fcode, 0, 0, netmask) < 0) {
+ return errh->error("%s: %s", ifname, pcap_geterr(_pcap));
+ }
+
+ if (pcap_setfilter(_pcap, &fcode) < 0) {
+ return errh->error("%s: %s", ifname, pcap_geterr(_pcap));
+ }
+
+#elif FROMDEVICE_LINUX
+
+ _fd = open_packet_socket(_ifname, errh);
+ if (_fd < 0) return -1;
+
+ int promisc_ok = set_promiscuous(_fd, _ifname, _promisc);
+ if (promisc_ok < 0) {
+ if (_promisc)
+ errh->warning("cannot set promiscuous mode");
+ _was_promisc = -1;
+ } else
+ _was_promisc = promisc_ok;
+
+ // create packet buffer
+ _packetbuf = new unsigned char[_packetbuf_size];
+ if (!_packetbuf) {
+ close(_fd);
+ return errh->error("out of memory");
+ }
+
+#else
+
+ return errh->error("FromDevice is not supported on this platform");
+
+#endif
+
+ ScheduleInfo::join_scheduler(this, errh);
+ return 0;
+}
+
+void
+FromDevice::uninitialize()
+{
+#if FROMDEVICE_PCAP
+ if (_pcap)
+ pcap_close(_pcap);
+ _pcap = 0;
+#endif
+#if FROMDEVICE_LINUX
+ if (_was_promisc >= 0)
+ set_promiscuous(_fd, _ifname, _was_promisc);
+ if (_fd >= 0)
+ close(_fd);
+ _fd = -1;
+ delete[] _packetbuf;
+ _packetbuf = 0;
+#endif
+}
+
+#if FROMDEVICE_PCAP
+void
+FromDevice::get_packet(u_char* clientdata,
+ const struct pcap_pkthdr* pkthdr,
+ const u_char* data)
+{
+ FromDevice *fd = (FromDevice *) clientdata;
+ int length = pkthdr->caplen;
+ Packet* p = Packet::make(data, length);
+ fd->output(0).push(p);
+}
+#endif
+
+void
+FromDevice::run_scheduled()
+{
+#if FROMDEVICE_PCAP
+ // Read and push() at most one packet.
+ pcap_dispatch(_pcap, 1, FromDevice::get_packet, (u_char *) this);
+#endif
+#if FROMDEVICE_LINUX
+ struct sockaddr_ll sa;
+ //memset(&sa, 0, sizeof(sa));
+ socklen_t fromlen = sizeof(sa);
+ int len = recvfrom(_fd, _packetbuf, _packetbuf_size, 0,
+ (sockaddr *)&sa, &fromlen);
+ if (len > 0) {
+ if (sa.sll_pkttype != PACKET_OUTGOING)
+ output(0).push(Packet::make(_packetbuf, len));
+ } else if (errno != EAGAIN)
+ click_chatter("FromDevice(%s): recvfrom: %s", _ifname.cc(), strerror(errno));
+#endif
+ reschedule();
+}
+
+EXPORT_ELEMENT(FromDevice)
diff --git a/elements/userlevel/fromdevice.hh b/elements/userlevel/fromdevice.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e5c5ef72535232ed9bf3bef93dda22c3a337c41e
--- /dev/null
+++ b/elements/userlevel/fromdevice.hh
@@ -0,0 +1,100 @@
+#ifndef FROMDEVICE_HH
+#define FROMDEVICE_HH
+
+/*
+ * =page FromDevice.u
+ * =c
+ * FromDevice(DEVNAME [, PROMISC? [, MAXPACKETSIZE]])
+ * =d
+ *
+ * This manual page describes the user-level version of the FromDevice
+ * element. For the Linux kernel module element, read the FromDevice(n) manual
+ * page.
+ *
+ * Reads packets from the kernel that were received on the network controller
+ * named DEVNAME. Puts the device in promiscuous mode if PROMISC? (a Boolean)
+ * is true. PROMISC? defaults to false. On some systems, packets larger than
+ * MAXPACKETSIZE will be truncated; default MAXPACKETSIZE is 2048 bytes.
+ *
+ * The kernel networking code sees all of the packets that FromDevice
+ * produces; be careful that at most one of Click and the kernel forwards each
+ * packet.
+ *
+ * Under Linux, a FromDevice element will not receive packets sent by a
+ * ToDevice element for the same device. Under other operating systems, your
+ * mileage may vary.
+ *
+ * =e
+ * = FromDevice(eth0, 0) -> ...
+ *
+ * =a ToDevice.u
+ * =a FromDump
+ * =a ToDump
+ * =a FromDevice */
+
+#include "element.hh"
+
+#ifdef __linux__
+# define FROMDEVICE_LINUX 1
+#elif defined(HAVE_PCAP)
+# define FROMDEVICE_PCAP 1
+extern "C" {
+# include <pcap.h>
+}
+#endif
+
+class FromDevice : public Element {
+
+ String _ifname;
+ bool _promisc : 1;
+ int _was_promisc : 2;
+ int _packetbuf_size;
+
+#if FROMDEVICE_LINUX
+ int _fd;
+ unsigned char *_packetbuf;
+#endif
+#if FROMDEVICE_PCAP
+ pcap_t* _pcap;
+ static void get_packet(u_char *, const struct pcap_pkthdr *,
+ const u_char *);
+ int do_select(int waitms);
+#endif
+
+ public:
+
+ enum ConfigurePhase {
+ CONFIGURE_PHASE_FROMDEVICE = CONFIGURE_PHASE_DEFAULT,
+ CONFIGURE_PHASE_TODEVICE = CONFIGURE_PHASE_FROMDEVICE + 1
+ };
+
+ FromDevice();
+ ~FromDevice();
+
+ const char *class_name() const { return "FromDevice"; }
+ const char *processing() const { return PUSH; }
+
+ FromDevice *clone() const;
+ int configure_phase() const { return CONFIGURE_PHASE_FROMDEVICE; }
+ int configure(const Vector<String> &, ErrorHandler *);
+ int initialize(ErrorHandler *);
+ void uninitialize();
+
+ String ifname() const { return _ifname; }
+#if FROMDEVICE_PCAP
+ pcap_t *pcap() const { return _pcap; }
+#endif
+#if FROMDEVICE_LINUX
+ int fd() const { return _fd; }
+#endif
+
+ void run_scheduled();
+
+#if FROMDEVICE_LINUX
+ static int open_packet_socket(String, ErrorHandler *);
+ static int set_promiscuous(int, String, bool);
+#endif
+
+};
+
+#endif
diff --git a/elements/userlevel/todevice.cc b/elements/userlevel/todevice.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d80d75c0283170f3df62482966a62008f295fad4
--- /dev/null
+++ b/elements/userlevel/todevice.cc
@@ -0,0 +1,182 @@
+/*
+ * todevice.{cc,hh} -- element writes packets to network via pcap library
+ * Douglas S. J. DeCouto, Eddie Kohler, John Jannotti
+ *
+ * Copyright (c) 1999-2000 Massachusetts Institute of Technology.
+ *
+ * This software is being provided by the copyright holders under the GNU
+ * General Public License, either version 2 or, at your discretion, any later
+ * version. For more information, see the `COPYRIGHT' file in the source
+ * distribution.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "todevice.hh"
+#include "error.hh"
+#include "etheraddress.hh"
+#include "confparse.hh"
+#include "router.hh"
+#include "elements/standard/scheduleinfo.hh"
+
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if TODEVICE_BSD_DEV_BPF
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/ioctl.h>
+# include <net/if.h>
+#elif TODEVICE_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
+
+ToDevice::ToDevice()
+ : Element(1, 0), _fd(-1), _my_fd(false)
+{
+#if TODEVICE_BSD_DEV_BPF
+ _pcap = 0;
+#endif
+}
+
+ToDevice::~ToDevice()
+{
+ uninitialize();
+}
+
+ToDevice *
+ToDevice::clone() const
+{
+ return new ToDevice;
+}
+
+int
+ToDevice::configure(const Vector<String> &conf, ErrorHandler *errh)
+{
+ if (cp_va_parse(conf, this, errh,
+ cpString, "interface name", &_ifname,
+ 0) < 0)
+ return -1;
+ if (!_ifname)
+ return errh->error("interface not set");
+ return 0;
+}
+
+int
+ToDevice::initialize(ErrorHandler *errh)
+{
+ _fd = -1;
+
+#if TODEVICE_BSD_DEV_BPF
+
+ /* 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("can't open a bpf"));
+
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, _ifname, 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);
+
+#elif TODEVICE_LINUX
+
+ // find a FromDevice and reuse its socket if possible
+ for (int ei = 0; ei < router()->nelements() && _fd < 0; ei++) {
+ Element *e = router()->element(ei);
+ ToDevice *td = (ToDevice *)e->cast("FromDevice");
+ if (td && td->ifname() == _ifname && td->fd() >= 0) {
+ _fd = td->fd();
+ _my_fd = false;
+ }
+ }
+
+ if (_fd < 0) {
+ _fd = FromDevice::open_packet_socket(_ifname, errh);
+ _my_fd = true;
+ }
+
+ if (_fd < 0) return -1;
+
+#else
+
+ return errh->error("ToDevice is not supported on this platform");
+
+#endif
+
+ if (input_is_pull(0))
+ ScheduleInfo::join_scheduler(this, errh);
+ return 0;
+}
+
+void
+ToDevice::uninitialize()
+{
+#if TODEVICE_BSD_DEV_BPF
+ if (_pcap) pcap_close(_pcap);
+ _pcap = 0;
+#endif
+#if TODEVICE_LINUX
+ if (_fd >= 0 && _my_fd) close(_fd);
+ _fd = -1;
+#endif
+ unschedule();
+}
+
+void
+ToDevice::send_packet(Packet *p)
+{
+ int retval;
+ const char *syscall;
+
+#if TODEVICE_WRITE
+ retval = (write(_fd, p->data(), p->length()) > 0 ? 0 : -1);
+ syscall = "write";
+#elif TODEVICE_SEND
+ retval = send(_fd, p->data(), p->length(), 0);
+ syscall = "send";
+#else
+ retval = 0;
+#endif
+
+ if (retval < 0)
+ click_chatter("ToDevice(%d) %s: %s", _ifname.cc(), syscall, strerror(errno));
+ p->kill();
+}
+
+void
+ToDevice::push(int, Packet *p)
+{
+ assert(p->length() >= 14);
+ send_packet(p);
+}
+
+void
+ToDevice::run_scheduled()
+{
+ // XXX reduce tickets when idle
+ if (Packet *p = input(0).pull())
+ send_packet(p);
+ reschedule();
+}
+
+ELEMENT_REQUIRES(FromDevice)
+EXPORT_ELEMENT(ToDevice)
diff --git a/elements/userlevel/todevice.hh b/elements/userlevel/todevice.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3f0b6a8806a6da7f7a27f25aaa7e9487ce7b1da3
--- /dev/null
+++ b/elements/userlevel/todevice.hh
@@ -0,0 +1,93 @@
+#ifndef TODEVICE_HH
+#define TODEVICE_HH
+#include "element.hh"
+#include "string.hh"
+#include "elements/userlevel/fromdevice.hh"
+
+/*
+ * =page ToDevice.u
+ * =c
+ * ToDevice(DEVNAME)
+ * =d
+ *
+ * This manual page describes the user-level version of the ToDevice element.
+ * For the Linux kernel module element, read the ToDevice(n) manual page.
+ *
+ * Pulls packets and sends them out the named device using
+ * Berkeley Packet Filters (or Linux equivalent).
+ *
+ * Packets sent via ToDevice should already have a link-level
+ * header prepended. This means that ARP processing,
+ * for example, must already have been done.
+ *
+ * Under Linux, a FromDevice element will not receive packets sent by a
+ * ToDevice element for the same device. Under other operating systems, your
+ * mileage may vary.
+ *
+ * This element is only available at user level.
+ *
+ * =a FromDevice.u
+ * =a FromDump
+ * =a ToDump
+ * =a ToDevice */
+
+#ifdef HAVE_PCAP
+extern "C" {
+# include <pcap.h>
+}
+#else
+# include "fakepcap.h"
+#endif
+
+#ifdef HAVE_PCAP
+# if defined(__FreeBSD__) || defined(__OpenBSD__)
+# define TODEVICE_BSD_DEV_BPF 1
+# define TODEVICE_WRITE 1
+# endif
+#endif
+#if defined(__linux__)
+# define TODEVICE_LINUX 1
+# define TODEVICE_SEND 1
+#endif
+
+/*
+ * Write packets to the ethernet via the bpf.
+ * Expects packets that already have an ether header.
+ * Can push or pull.
+ */
+
+class ToDevice : public Element {
+
+ String _ifname;
+ int _fd;
+ bool _my_fd;
+
+#if TODEVICE_BSD_DEV_BPF
+ pcap_t *_pcap;
+#endif
+
+ void send_packet(Packet *);
+
+ public:
+
+ ToDevice();
+ ~ToDevice();
+
+ const char *class_name() const { return "ToDevice"; }
+ const char *processing() const { return AGNOSTIC; }
+
+ ToDevice *clone() const;
+ int configure_phase() const { return FromDevice::CONFIGURE_PHASE_TODEVICE; }
+ int configure(const Vector<String> &, ErrorHandler *);
+ int initialize(ErrorHandler *);
+ void uninitialize();
+
+ String ifname() const { return _ifname; }
+ int fd() const { return _fd; }
+
+ void push(int port, Packet *);
+ void run_scheduled();
+
+};
+
+#endif
diff --git a/lib/router.cc b/lib/router.cc
index ad7398e3878c4dffb9de5c5dba4e3a91b5155fab..44396040e277ac446aa5b850252e344b283088ef 100644
--- a/lib/router.cc
+++ b/lib/router.cc
@@ -813,7 +813,7 @@ Router::initialize(ErrorHandler *errh)
// If there were errors, uninitialize any elements that we initialized
// successfully and return -1 (error). Otherwise, we're all set!
if (!all_ok) {
- errh->error("router could not be initialized");
+ errh->error("Router could not be initialized!");
for (int i = 0; i < _elements.size(); i++)
if (element_ok[i])
_elements[i]->uninitialize();