[Click] ARP Modules
José María González
chema at cs.berkeley.edu
Fri Feb 25 15:46:31 EST 2005
Hi,
I've been toying with click. As part of my learning, I've written 2
new modules that check and print ARP packets. It's basically a
graft between the corresponding IP modules and tcpdump's print-arp.c.
Thought they could be useful.
-Chema
-------------- next part --------------
diff -Naur click/elements/ethernet/arpprint.cc click.new/elements/ethernet/arpprint.cc
--- click/elements/ethernet/arpprint.cc Wed Dec 31 16:00:00 1969
+++ click.new/elements/ethernet/arpprint.cc Fri Feb 25 12:19:05 2005
@@ -0,0 +1,211 @@
+/*
+ * arpprint.{cc,hh} -- element prints packet contents to system log
+ * Jose Maria Gonzalez
+ *
+ * Shameless graft of ipprint.hh/cc and tcpdump-3.8.3/print-arp.c
+ *
+ * Copyright (c) 2005 Regents of the University of California
+ *
+ * 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>
+#include "arpprint.hh"
+#include <click/glue.hh>
+#include <click/confparse.hh>
+#include <click/error.hh>
+#include <click/straccum.hh>
+#include <click/packet_anno.hh>
+#include <click/router.hh>
+#include <click/nameinfo.hh>
+
+#include <click/etheraddress.hh>
+#include <clicknet/ether.h>
+
+#if CLICK_USERLEVEL
+# include <stdio.h>
+#endif
+
+CLICK_DECLS
+
+
+/*
+ * Address Resolution Protocol.
+ *
+ * See RFC 826 for protocol description. ARP packets are variable
+ * in size; the arphdr structure defines the fixed-length portion.
+ * Protocol type values are the same as those for 10 Mb/s Ethernet.
+ * It is followed by the variable-sized fields ar_sha, arp_spa,
+ * arp_tha and arp_tpa in that order, according to the lengths
+ * specified. Field names used correspond to RFC 826.
+ */
+
+
+
+ARPPrint::ARPPrint()
+ : Element(1, 1)
+{
+#if CLICK_USERLEVEL
+ _outfile = 0;
+#endif
+}
+
+ARPPrint::~ARPPrint()
+{
+}
+
+int
+ARPPrint::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+ _label = "";
+ bool print_time = true;
+ String channel;
+
+ if (cp_va_parse(conf, this, errh,
+ cpOptional,
+ cpString, "label", &_label,
+ cpKeywords,
+ "TIMESTAMP", cpBool, "print packet timestamps?", &print_time,
+#if CLICK_USERLEVEL
+ "OUTFILE", cpFilename, "output filename", &_outfilename,
+#endif
+ cpEnd) < 0)
+ return -1;
+
+ _print_timestamp = print_time;
+ _errh = router()->chatter_channel(channel);
+ return 0;
+}
+
+int
+ARPPrint::initialize(ErrorHandler *errh)
+{
+#if CLICK_USERLEVEL
+ if (_outfilename) {
+ _outfile = fopen(_outfilename.c_str(), "wb");
+ if (!_outfile)
+ return errh->error("%s: %s", _outfilename.c_str(), strerror(errno));
+ }
+#else
+ (void) errh;
+#endif
+ return 0;
+}
+
+void
+ARPPrint::cleanup(CleanupStage)
+{
+#if CLICK_USERLEVEL
+ if (_outfile)
+ fclose(_outfile);
+ _outfile = 0;
+#endif
+}
+
+
+
+Packet *
+ARPPrint::simple_action(Packet *p)
+{
+ const click_ether_arp *ap = p->arp_ether_header();
+ if (!ap)
+ return p;
+
+ StringAccum sa;
+
+ if (_label)
+ sa << _label << ": ";
+ if (_print_timestamp)
+ sa << p->timestamp_anno() << ": ";
+
+ if (p->network_length() < (int) sizeof(click_arp))
+ sa << "truncated-arp (" << p->network_length() << ")";
+ else {
+ uint16_t hrd = ntohs(ap->ea_hdr.ar_hrd);
+ uint16_t pro = ntohs(ap->ea_hdr.ar_pro);
+ uint8_t hln = ap->ea_hdr.ar_hln;
+ uint8_t pln = ap->ea_hdr.ar_pln;
+ uint16_t op = ntohs(ap->ea_hdr.ar_op);
+
+ if ((pro != ETHERTYPE_IP && pro != ETHERTYPE_TRAIL) ||
+ pln != 4 || hln == 0) {
+ sa << "arp-#" << op << " for proto #" << pro << " (" << pln << ") "
+ << "hardware #" << hrd << " (" << hln << ")";
+ return p;
+ }
+ const unsigned char *sha = (const unsigned char *)ap->arp_sha;
+ const unsigned char *spa = (const unsigned char *)ap->arp_spa;
+ const unsigned char *tha = (const unsigned char *)ap->arp_tha;
+ const unsigned char *tpa = (const unsigned char *)ap->arp_tpa;
+
+ if (pro == ETHERTYPE_TRAIL)
+ sa << "trailer-";
+
+ switch (op) {
+ case ARPOP_REQUEST:
+ {
+ const unsigned char ezero[6] = {0,0,0,0,0,0};
+ sa << "arp who-has " << IPAddress(tpa);
+ if ( memcmp (ezero, tha, hln) != 0 )
+ sa << " (" << EtherAddress(tha) << ")";
+ sa << " tell " << IPAddress(spa);
+ break;
+ }
+
+ case ARPOP_REPLY:
+ sa << "arp reply " << IPAddress(spa);
+ sa << " is-at " << EtherAddress(sha);
+ break;
+
+ case ARPOP_REVREQUEST:
+ sa << "rarp who-is " << EtherAddress(tha) << " tell " <<
+ EtherAddress(sha);
+ break;
+
+ case ARPOP_REVREPLY:
+ sa << "rarp reply " << EtherAddress(tha) << " at " << IPAddress(tpa);
+ break;
+
+ case ARPOP_INVREQUEST:
+ sa << "invarp who-is " << EtherAddress(tha) << " tell " <<
+ EtherAddress(sha);
+ break;
+
+ case ARPOP_INVREPLY:
+ sa << "invarp reply " << EtherAddress(tha) << " at " <<
+ IPAddress(tpa);
+ break;
+
+ default:
+ sa << "arp-#" << op;
+ // default_print((const u_char *)ap, caplen);
+ }
+ if (hrd != ARPHRD_ETHER)
+ sa << "hardware #" << hrd;
+ }
+
+#if CLICK_USERLEVEL
+ if (_outfile) {
+ sa << '\n';
+ fwrite(sa.data(), 1, sa.length(), _outfile);
+ } else
+#endif
+ _errh->message("%s", sa.c_str());
+
+ return p;
+}
+
+
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(ARPPrint)
+
diff -Naur click/elements/ethernet/arpprint.hh click.new/elements/ethernet/arpprint.hh
--- click/elements/ethernet/arpprint.hh Wed Dec 31 16:00:00 1969
+++ click.new/elements/ethernet/arpprint.hh Fri Feb 25 12:17:27 2005
@@ -0,0 +1,68 @@
+#ifndef CLICK_ARPPRINT_HH
+#define CLICK_ARPPRINT_HH
+#include <click/element.hh>
+CLICK_DECLS
+
+/*
+=c
+
+ARPPrint([TAG, I<KEYWORDS>])
+
+=s ARP, debugging
+
+pretty-prints ARP packets a la tcpdump
+
+=d
+
+Expects ARP packets as input.
+
+Prints out ARP packets in a human-readable tcpdump-like format, preceded by
+the TAG text.
+
+Keyword arguments are:
+
+=over 2
+
+=item TIMESTAMP
+
+Boolean. Determines whether to print each packet's timestamp in seconds since
+1970. Default is true.
+
+=item OUTFILE
+
+String. Only available at user level. PrintV<> information to the file specified
+by OUTFILE instead of standard error.
+
+=back
+
+=a Print */
+
+class ARPPrint : public Element { public:
+
+ ARPPrint();
+ ~ARPPrint();
+
+ const char *class_name() const { return "ARPPrint"; }
+ const char *processing() const { return AGNOSTIC; }
+
+ int configure(Vector<String> &, ErrorHandler *);
+ int initialize(ErrorHandler *);
+ void cleanup(CleanupStage);
+
+ Packet *simple_action(Packet *);
+
+ private:
+
+ String _label;
+ bool _print_timestamp : 1;
+
+#if CLICK_USERLEVEL
+ String _outfilename;
+ FILE *_outfile;
+#endif
+ ErrorHandler *_errh;
+
+};
+
+CLICK_ENDDECLS
+#endif
diff -Naur click/elements/ethernet/checkarpheader.cc click.new/elements/ethernet/checkarpheader.cc
--- click/elements/ethernet/checkarpheader.cc Wed Dec 31 16:00:00 1969
+++ click.new/elements/ethernet/checkarpheader.cc Fri Feb 25 12:33:08 2005
@@ -0,0 +1,158 @@
+/*
+ * checkarpheader.{cc,hh} -- element checks ARP header for correctness
+ * Jose Maria Gonzalez
+ *
+ * Copyright (c) 1999-2000 Massachusetts Institute of Technology
+ * Copyright (c) 2003 International Computer Science Institute
+ *
+ * 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>
+#include "checkarpheader.hh"
+#include <clicknet/ether.h>
+#include <click/glue.hh>
+#include <click/confparse.hh>
+#include <click/straccum.hh>
+#include <click/error.hh>
+#include <click/standard/alignmentinfo.hh>
+CLICK_DECLS
+
+
+const char * const CheckARPHeader::reason_texts[NREASONS] = {
+ "tiny packet"
+};
+
+void
+CheckARPHeader::static_initialize()
+{
+}
+
+void
+CheckARPHeader::static_cleanup()
+{
+}
+
+CheckARPHeader::CheckARPHeader()
+ : Element(1, 1), _reason_drops(0)
+{
+ _drops = 0;
+}
+
+CheckARPHeader::~CheckARPHeader()
+{
+ delete[] _reason_drops;
+}
+
+void
+CheckARPHeader::notify_noutputs(int n)
+{
+ set_noutputs(n < 2 ? 1 : 2);
+}
+
+int
+CheckARPHeader::configure(Vector<String> &conf, ErrorHandler *errh)
+{
+ _offset = 0;
+ bool verbose = false;
+ bool details = false;
+
+ if (cp_va_parse_remove_keywords(conf, 0, this, errh,
+ "OFFSET", cpUnsigned, "ARP header offset", &_offset,
+ "VERBOSE", cpBool, "be verbose?", &verbose,
+ "DETAILS", cpBool, "keep detailed counts?", &details,
+ cpEnd) < 0)
+ return -1;
+
+ if (conf.size() == 1 && cp_unsigned(conf[0], &_offset))
+ /* nada */;
+ else if (cp_va_parse(conf, this, errh, cpOptional,
+ cpUnsigned, "ARP header offset", &_offset,
+ cpEnd) < 0)
+ return -1;
+
+ _verbose = verbose;
+ if (details)
+ _reason_drops = new atomic_uint32_t[NREASONS];
+
+
+ return 0;
+}
+
+Packet *
+CheckARPHeader::drop(Reason reason, Packet *p)
+{
+ if (_drops == 0 || _verbose)
+ click_chatter("ARP header check failed: %s", reason_texts[reason]);
+ _drops++;
+
+ if (_reason_drops)
+ _reason_drops[reason]++;
+
+ if (noutputs() == 2)
+ output(1).push(p);
+ else
+ p->kill();
+
+ return 0;
+}
+
+Packet *
+CheckARPHeader::simple_action(Packet *p)
+{
+ const click_ether_arp *ap = reinterpret_cast<const click_ether_arp *>(p->data() + _offset);
+ unsigned plen = p->length() - _offset;
+ unsigned hlen;
+
+ // cast to int so very large plen is interpreted as negative
+ if ((int)plen < (int)sizeof(click_ether_arp))
+ return drop(MINISCULE_PACKET, p);
+
+ hlen = 2 + 2 + 1 + 1 + (2 * ap->ea_hdr.ar_hln) + (2 * ap->ea_hdr.ar_pln);
+
+ p->set_arp_header(ap, hlen);
+
+ return(p);
+}
+
+String
+CheckARPHeader::read_handler(Element *e, void *thunk)
+{
+ CheckARPHeader *c = reinterpret_cast<CheckARPHeader *>(e);
+ switch ((intptr_t)thunk) {
+
+ case 0: // drops
+ return String(c->_drops) + "\n";
+
+ case 1: { // drop_details
+ StringAccum sa;
+ for (int i = 0; i < NREASONS; i++)
+ sa << c->_reason_drops[i] << '\t' << reason_texts[i] << '\n';
+ return sa.take_string();
+ }
+
+ default:
+ return String("<error>\n");
+
+ }
+}
+
+void
+CheckARPHeader::add_handlers()
+{
+ add_read_handler("drops", read_handler, (void *)0);
+ if (_reason_drops)
+ add_read_handler("drop_details", read_handler, (void *)1);
+}
+
+
+CLICK_ENDDECLS
+EXPORT_ELEMENT(CheckARPHeader)
diff -Naur click/elements/ethernet/checkarpheader.hh click.new/elements/ethernet/checkarpheader.hh
--- click/elements/ethernet/checkarpheader.hh Wed Dec 31 16:00:00 1969
+++ click.new/elements/ethernet/checkarpheader.hh Fri Feb 25 12:28:26 2005
@@ -0,0 +1,101 @@
+#ifndef CLICK_CHECKARPHEADER_HH
+#define CLICK_CHECKARPHEADER_HH
+#include <click/element.hh>
+#include <click/atomic.hh>
+#include <click/ipaddresslist.hh>
+CLICK_DECLS
+
+/*
+=c
+
+CheckARPHeader([OFFSET, I<keywords> OFFSET, VERBOSE, DETAILS])
+
+=s ARP, checking
+
+checks ARP header
+
+=d
+
+Input packets should have ARP headers starting OFFSET bytes in. Default OFFSET
+is zero. Checks that the packet's length is reasonable.
+
+CheckARPHeader emits valid packets on output 0. Invalid packets are pushed out
+on output 1, unless output 1 was unused; if so, drops invalid packets.
+
+CheckARPHeader prints a message to the console the first time it encounters an
+incorrect ARP packet (but see VERBOSE below).
+
+Keyword arguments are:
+
+=over 5
+
+=item OFFSET
+
+Unsigned integer. Byte position at which the ARP header begins. Default is 0.
+
+=item VERBOSE
+
+Boolean. If it is true, then a message will be printed for every erroneous
+packet, rather than just the first. False by default.
+
+=item DETAILS
+
+Boolean. If it is true, then CheckARPHeader will maintain detailed counts of
+how many packets were dropped for each possible reason, accessible through the
+C<drop_details> handler. False by default.
+
+=back
+
+=n
+
+=h drops read-only
+
+Returns the number of incorrect packets CheckARPHeader has seen.
+
+=h drop_details read-only
+
+Returns a text file showing how many erroneous packets CheckARPHeader has seen,
+subdivided by error. Only available if the DETAILS keyword argument was true.
+
+
+*/
+
+class CheckARPHeader : public Element { public:
+
+ CheckARPHeader();
+ ~CheckARPHeader();
+
+ static void static_initialize();
+ static void static_cleanup();
+
+ const char *class_name() const { return "CheckARPHeader"; }
+ const char *processing() const { return "a/ah"; }
+
+ void notify_noutputs(int);
+ int configure(Vector<String> &, ErrorHandler *);
+ void add_handlers();
+
+ Packet *simple_action(Packet *);
+
+ private:
+
+ unsigned _offset;
+
+ bool _verbose : 1;
+
+ atomic_uint32_t _drops;
+ atomic_uint32_t *_reason_drops;
+
+ enum Reason {
+ MINISCULE_PACKET,
+ NREASONS
+ };
+ static const char * const reason_texts[NREASONS];
+
+ Packet *drop(Reason, Packet *);
+ static String read_handler(Element *, void *);
+
+};
+
+CLICK_ENDDECLS
+#endif
diff -Naur click/include/click/packet.hh click.new/include/click/packet.hh
--- click/include/click/packet.hh Thu Feb 24 09:21:41 2005
+++ click.new/include/click/packet.hh Thu Feb 24 19:53:45 2005
@@ -4,6 +4,7 @@
#include <click/ipaddress.hh>
#include <click/glue.hh>
#include <click/timestamp.hh>
+#include <clicknet/ether.h>
#ifdef CLICK_LINUXMODULE
# include <click/skbmgr.hh>
#endif
@@ -110,6 +111,8 @@
const unsigned char *network_header() const { return _nh.raw; }
const click_ip *ip_header() const { return _nh.iph; }
const click_ip6 *ip6_header() const { return _nh.ip6h; }
+ const click_arp *arp_header() const { return _nh.arph; }
+ const click_ether_arp *arp_ether_header() const { return (const click_ether_arp *)_nh.arph; }
const unsigned char *transport_header() const { return _h.raw; }
const click_icmp *icmp_header() const { return _h.icmph; }
@@ -125,6 +128,7 @@
void set_ip_header(const click_ip *, uint32_t);
void set_ip6_header(const click_ip6 *);
void set_ip6_header(const click_ip6 *, uint32_t);
+ void set_arp_header(const click_ether_arp *, uint32_t);
int mac_header_offset() const;
uint32_t mac_header_length() const;
@@ -308,6 +312,7 @@
unsigned char *raw;
click_ip *iph;
click_ip6 *ip6h;
+ click_arp *arph;
} _nh;
union {
unsigned char *raw;
@@ -751,6 +756,12 @@
Packet::set_ip6_header(const click_ip6 *ip6h)
{
set_ip6_header(ip6h, 40);
+}
+
+inline void
+Packet::set_arp_header(const click_ether_arp *aph, uint32_t len)
+{
+ set_network_header(reinterpret_cast<const unsigned char *>(aph), len);
}
inline int
diff -Naur click/include/clicknet/ether.h click.new/include/clicknet/ether.h
--- click/include/clicknet/ether.h Fri Feb 25 12:12:41 2005
+++ click.new/include/clicknet/ether.h Fri Feb 25 12:12:52 2005
@@ -14,12 +14,12 @@
uint16_t ether_type;
});
+#define ETHERTYPE_TRAIL 0x1000
#define ETHERTYPE_IP 0x0800
#define ETHERTYPE_ARP 0x0806
#define ETHERTYPE_8021Q 0x8100
#define ETHERTYPE_IP6 0x86DD
-
#define ETHERTYPE_GRID 0x7fff /* wvlan_cs driver won't transmit frames with high bit of protocol number set */
struct click_arp {
@@ -31,11 +31,22 @@
};
/* ARP protocol HARDWARE identifiers. */
-#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
+#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
+#define ARPHRD_IEEE802 6 /* token-ring hardware format */
+#define ARPHRD_ARCNET 7 /* arcnet hardware format */
+#define ARPHRD_FRELAY 15 /* frame relay hardware format */
+#define ARPHRD_STRIP 23 /* Ricochet Starmode Radio hardware format */
+#define ARPHRD_IEEE1394 24 /* IEEE 1394 (FireWire) hardware format */
+
/* ARP protocol opcodes. */
-#define ARPOP_REQUEST 1 /* ARP request */
-#define ARPOP_REPLY 2 /* ARP reply */
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+#define ARPOP_REVREQUEST 3 /* request protocol address given hardware */
+#define ARPOP_REVREPLY 4 /* response giving protocol address */
+#define ARPOP_INVREQUEST 8 /* request to identify peer */
+#define ARPOP_INVREPLY 9 /* response identifying peer */
+
struct click_ether_arp {
struct click_arp ea_hdr; /* fixed-size header */
More information about the click
mailing list