// -*- c-basic-offset: 4 -*-
/*
* arpresponder.{cc,hh} -- element that responds to ARP queries
* Robert Morris
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
*
* 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 "arpresponder.hh"
#include <clicknet/ether.h>
#include <click/etheraddress.hh>
#include <click/ipaddress.hh>
#include <click/confparse.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include <click/straccum.hh>
CLICK_DECLS
ARPResponder::ARPResponder()
{
}
ARPResponder::~ARPResponder()
{
}
void
ARPResponder::add_map(IPAddress ipa, IPAddress mask, EtherAddress ena)
{
struct Entry e;
e.dst = ipa & mask;
e.mask = mask;
e.ena = ena;
_v.push_back(e);
}
int
ARPResponder::configure(Vector<String> &conf, ErrorHandler *errh)
{
_v.clear();
int before = errh->nerrors();
for (int i = 0; i < conf.size(); i++) {
IPAddress ipa, mask;
EtherAddress ena;
bool have_ena = false;
int first = _v.size();
Vector<String> words;
cp_spacevec(conf[i], words);
for (int j = 0; j < words.size(); j++)
if (cp_ip_address(words[j], &ipa, this))
add_map(ipa, IPAddress(0xFFFFFFFFU), EtherAddress());
else if (cp_ip_prefix(words[j], &ipa, &mask, this))
add_map(ipa, mask, EtherAddress());
else if (cp_ethernet_address(words[j], &ena, this)) {
if (have_ena)
errh->error("argument %d has more than one Ethernet address", i);
have_ena = true;
} else {
errh->error("argument %d should be `IP/MASK ETHADDR'", i);
j = words.size();
}
// check for an argument that is both IP address and Ethernet address
for (int j = 0; !have_ena && j < words.size(); j++)
if (cp_ethernet_address(words[j], &ena, this))
have_ena = true;
if (first == _v.size())
errh->error("argument %d had no IP address and masks", i);
if (!have_ena)
errh->error("argument %d had no Ethernet addresses", i);
for (int j = first; j < _v.size(); j++)
_v[j].ena = ena;
}
return (before == errh->nerrors() ? 0 : -1);
}
int
ARPResponder::live_reconfigure(Vector<String> &conf, ErrorHandler *errh)
{
// Copy the old mappings to a temporary vector
Vector<Entry> old_v = _v;
// if the configuration fails copy the old mapping vector back
if (configure(conf, errh) < 0) {
_v = old_v;
return -1;
} else
return 0;
}
Packet *
ARPResponder::make_response(u_char tha[6], /* him */
u_char tpa[4],
u_char sha[6], /* me */
u_char spa[4],
Packet *p /* only used for annotations */)
{
WritablePacket *q = Packet::make(sizeof(click_ether) + sizeof(click_ether_arp));
if (q == 0) {
click_chatter("in arp responder: cannot make packet!");
return 0;
}
// in case of FromLinux, set the device annotation: want to make it seem
// that ARP response came from the device that the query arrived on
q->set_device_anno(p->device_anno());
memset(q->data(), '\0', q->length());
click_ether *e = (click_ether *) q->data();
q->set_ether_header(e);
memcpy(e->ether_dhost, tha, 6);
memcpy(e->ether_shost, sha, 6);
e->ether_type = htons(ETHERTYPE_ARP);
click_ether_arp *ea = (click_ether_arp *) (e + 1);
ea->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
ea->ea_hdr.ar_pro = htons(ETHERTYPE_IP);
ea->ea_hdr.ar_hln = 6;
ea->ea_hdr.ar_pln = 4;
ea->ea_hdr.ar_op = htons(ARPOP_REPLY);
memcpy(ea->arp_tha, tha, 6);
memcpy(ea->arp_tpa, tpa, 4);
memcpy(ea->arp_sha, sha, 6);
memcpy(ea->arp_spa, spa, 4);
return q;
}
bool
ARPResponder::lookup(IPAddress a, EtherAddress &ena) const
{
int best = -1;
for (int i = 0; i < _v.size(); i++)
if (a.matches_prefix(_v[i].dst, _v[i].mask)) {
if (best < 0 || _v[i].mask.mask_as_specific(_v[best].mask))
best = i;
}
if (best < 0)
return false;
else {
ena = _v[best].ena;
return true;
}
}
Packet *
ARPResponder::simple_action(Packet *p)
{
click_ether *e = (click_ether *) p->data();
click_ether_arp *ea = (click_ether_arp *) (e + 1);
unsigned int tpa;
memcpy(&tpa, ea->arp_tpa, 4);
IPAddress ipa = IPAddress(tpa);
Packet *q = 0;
if (p->length() >= sizeof(*e) + sizeof(click_ether_arp) &&
ntohs(e->ether_type) == ETHERTYPE_ARP &&
ntohs(ea->ea_hdr.ar_hrd) == ARPHRD_ETHER &&
ntohs(ea->ea_hdr.ar_pro) == ETHERTYPE_IP &&
ntohs(ea->ea_hdr.ar_op) == ARPOP_REQUEST) {
EtherAddress ena;
if (lookup(ipa, ena)) {
q = make_response(ea->arp_sha, ea->arp_spa, ena.data(), ea->arp_tpa, p);
}
} else {
struct in_addr ina;
memcpy(&ina, &ea->arp_tpa, 4);
}
p->kill();
return(q);
}
String
ARPResponder::read_handler(Element *e, void *thunk)
{
ARPResponder *ar = static_cast<ARPResponder *>(e);
switch ((intptr_t)thunk) {
case 0: { // table
StringAccum sa;
for (int i = 0; i < ar->_v.size(); i++)
sa << ar->_v[i].dst.unparse_with_mask(ar->_v[i].mask) << ' ' << ar->_v[i].ena << '\n';
return sa.take_string();
}
default:
return "<error>\n";
}
}
void
ARPResponder::add_handlers()
{
add_read_handler("table", read_handler, (void *)0);
}
EXPORT_ELEMENT(ARPResponder)
// generate Vector template instance
#include <click/vector.cc>
#if EXPLICIT_TEMPLATE_INSTANCES
template class Vector<ARPResponder::Entry>;
#endif
CLICK_ENDDECLS
syntax highlighted by Code2HTML, v. 0.9.1