[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