#ifndef CLICK_DSDVROUTETABLE_HH
#define CLICK_DSDVROUTETABLE_HH
#include <click/bighashmap.hh>
#include <click/etheraddress.hh>
#include <click/ipaddress.hh>
#include <clicknet/ether.h>
#include <elements/grid/grid.hh>
#include <elements/grid/gridgenericrt.hh>
#include <click/timer.hh>
#include <elements/grid/gridgenericlogger.hh>
#include <click/dequeue.hh>
#include <elements/grid/gridgenericmetric.hh>
CLICK_DECLS
/*
* =c
* DSDVRouteTable(TIMEOUT, PERIOD, JITTER, MIN_TRIGGER_PERIOD, ETH, IP [, I<KEYWORDS>])
*
* =s Grid
* Run DSDV local routing protocol
*
* =d
*
* This is meant to be an `official' implementation of DSDV. It is
* intended to exactly replicate the behavior of DSDV as described in
* the original Perkins paper, the CMU ad-hoc bakeoff paper, and the
* CMU ns implementation. I make no guarantees that any of the above
* are actually achieved.
*
* DSDVRouteTable expects the Paint annotation to be set on Grid
* packets arriving from the network.
*
* There must only be one of DSDVRouteTable or GridRouteTable in a
* grid configuration.
*
* Regular arguments are:
*
* =over 8
*
* =item TIMEOUT
*
* Unsigned integer. Milliseconds after which to expire route entries.
*
* =item PERIOD
*
* Unsigned integer. Milliseconds between full dumps, plus/minus some jitter (see below).
*
* =item JITTER
*
* Unsigned integer. Maximum milliseconds by which to randomly jitter full dumps.
*
* =item MIN_TRIGGER_PERIOD
*
* Unsigned integer. Minimum milliseconds between triggered updates.
*
* =item ETH
*
* This node's ethernet hardware address.
*
* =item IP
*
* This node's IP address.
*
* =back
*
* Keywords arguments are:
*
* =over 8
*
* =item GW
*
* GridGatewayInfo element. Determines whether or not this node is a
* gateway. If this argument is not provided, the node is not a
* gateway.
*
* =item MAX_HOPS
*
* Unsigned integer. The maximum number of hops for which a route
* should propagate. The default number of hops is 3.
*
* =item METRIC
*
* GridGenericMetric element. The metric element that should be used
* to compare two routes. If not specified, minimum hop-count is
* used.
*
* =item USE_SEQ_METRIC
*
* Boolean. Iff true, use the `dsdv_seqs' metric, and ignore the
* METRIC keyword. Defaults to false.
*
* =item IGNORE_INVALID_ROUTES
*
* Boolean. Iff true, don't advertise or use routes with invalid
* route metrics. Defaults to false.
*
* =item LOG
*
* GridGenericLogger element. If provided, Grid events are logged to
* this element.
*
* =item WST0 (zero, not `oh')
*
* Unsigned integer. Initial weighted settling time in milliseconds.
* Defaults to 6,000 (6 seconds).
*
* =item ALPHA
*
* Unsigned integer. DSDV settling time weighting parameter, in
* percent, between 0 and 100 inclusive. Defaults to 88%.
*
* =item SEQ0 (zero, not `oh')
*
* Unsigned integer. Initial sequence number used in advertisements.
* Defaults to 0. Must be even.
*
* =item MTU
*
* Unsigned integer. Maximum packet size (`mtu') allowed by
* interface; route broadcasts will not exceed this size. Defaults to
* 2000.
*
* =item VERBOSE
*
* Boolean. Be verbose about warning and status messages? Defaults
* to true.
*
* =back
*
* =h nbrs read-only
* Print 1-hop routes. `nbrs_v' is more verbose.
* =h rtes read-only
* Print route table. `rtes_v' is more verbose.
* =h links read-only
* Print this node's link table.
* =h ip read-only
* Print this node's IP address.
* =h eth read-only
* Print this node's Ethernet address.
* =h seqno read/write
* Get/set this node's current sequence number (unsigned int, must be even).
*
* =h paused read/write
*
* Get/set this node's pause status (boolean). After the node is
* paused, subsequent data packets are routed using a snapshot of the
* route table taken at the pause time; however, the real route table
* continues to be updated and route ads continue to be sent. When
* the node becomes unpaused, packets are routed using the real route
* table. The node is initially unpaused. This functionality may be
* disabled at compile-time.
*
* =h use_old_route read/write
*
* Unsigned integer 0-2. If 0 (default), immediately use routes with
* newer sequence numbers. If 1, only use new routes after the
* settling time has passed and they can be advertised. If 2,
* immediately use routes with newer sequence numbers only if their
* metric is better than the last route; otherwise wait until the
* settling time has passed before using the new route. This
* functionality may be disabled at compile-time.
*
* =a
* SendGridHello, FixSrcLoc, SetGridChecksum, LookupLocalGridRoute, LookupGeographicGridRoute
* GridGatewayInfo, HopcountMetric, GridLogger, Paint */
// if 1, enable `paused' handler
#define ENABLE_PAUSE 1
// if 1, don't use routes until it's also okay to advertise them
// (enable `use_old_route handler' functionality).
#define USE_OLD_SEQ 1
// if 1, use new routes even if it's not okay to advertise them, as
// long as they have a better metric. This is a modification of the
// USE_OLD_SEQ functionality.
#define USE_GOOD_NEW_ROUTES 1
// if 1, enable `dsdv_seq' metric, which only uses routes from nodes
// who deliver OLD_BCASTS_NEEDED out of every MAX_BCAST_HISTORY route
// ads.
#define SEQ_METRIC 1
#define MAX_BCAST_HISTORY 3
#define OLD_BCASTS_NEEDED 2
// if 1, enable `seen' link handshaking, described in Chin, Judge,
// William, and Kermode 2002.
#define ENABLE_SEEN 1
class GridGatewayInfo;
class DSDVRouteTable : public GridGenericRouteTable {
public:
// generic rt methods
bool current_gateway(RouteEntry &entry);
bool get_one_entry(const IPAddress &dest_ip, RouteEntry &entry);
void get_all_entries(Vector<RouteEntry> &vec);
unsigned get_number_direct_neigbors();
DSDVRouteTable();
~DSDVRouteTable();
const char *class_name() const { return "DSDVRouteTable"; }
void *cast(const char *);
const char *port_count() const { return PORTS_1_1; }
const char *processing() const { return "h/h"; }
const char *flow_code() const { return "x/y"; }
int configure(Vector<String> &, ErrorHandler *);
int initialize(ErrorHandler *);
virtual bool can_live_reconfigure() const { return false; }
Packet *simple_action(Packet *);
void add_handlers();
private:
#if SEQ_METRIC
bool _use_seq_metric; // use the `dsdv_seqs' metric
HashMap<IPAddress, DEQueue<unsigned> > _seq_history;
#endif
typedef GridGenericMetric::metric_t metric_t;
/*
* route table entry
*/
class RTEntry : public RouteEntry {
private:
bool _init;
public:
class EtherAddress dest_eth; // hardware address of destination;
// may be all 0s if we don't hear any ads...
bool is_gateway;
unsigned int ttl; // msecs
unsigned int last_updated_jiffies; // last time this entry was updated
// metrics are invalid until updated to incorporate the last hop's
// link, i.e. by calling initialize_metric or update_metric.
metric_t metric;
// DSDV book-keeping
unsigned int wst; // weighted settling time (msecs)
metric_t last_adv_metric; // last metric we advertised
unsigned int last_seq_jiffies; // last time the seq_no changed
unsigned int advertise_ok_jiffies; // when it is ok to advertise route
bool need_seq_ad;
bool need_metric_ad;
unsigned int last_expired_jiffies; // when the route was expired (if broken)
#if ENABLE_SEEN
unsigned int last_seen_jiffies; // last time this dest said it `saw' us (advertised a route to us)
#endif
bool broken() const { check(); return num_hops() == 0; }
bool good() const { check(); return num_hops() != 0; }
String dump() const;
void check() const {
assert(_init);
assert((num_hops() > 0) != (seq_no() & 1));
assert((num_hops() != 1) || (dest_ip == next_hop_ip && dest_eth == next_hop_eth));
// only check if last_seq_jiff has been set
assert(last_seq_jiffies ? last_updated_jiffies >= last_seq_jiffies : true);
}
void invalidate(unsigned int jiff) {
check();
assert(num_hops() > 0);
_num_hops = 0;
_seq_no++;
last_expired_jiffies = jiff;
check();
}
RTEntry() :
_init(false), is_gateway(false), ttl(0), last_updated_jiffies(0), wst(0),
last_seq_jiffies(0), advertise_ok_jiffies(0), need_seq_ad(false),
need_metric_ad(false), last_expired_jiffies(0)
{ }
/* constructor for 1-hop route entry, converting from net byte order */
RTEntry(IPAddress ip, EtherAddress eth, grid_hdr *gh, grid_hello *hlo,
unsigned char interface, unsigned int jiff) :
RouteEntry(ip, gh->loc_good, gh->loc_err, gh->loc, eth, ip, interface, hlo->seq_no, 1),
_init(true), dest_eth(eth), is_gateway(hlo->is_gateway), ttl(hlo->ttl), last_updated_jiffies(jiff),
wst(0), last_seq_jiffies(jiff), advertise_ok_jiffies(0), need_seq_ad(false),
need_metric_ad(false), last_expired_jiffies(0)
{
loc_err = ntohs(loc_err);
_seq_no = ntohl(_seq_no);
ttl = ntohl(ttl);
check();
}
/* constructor from grid_nbr_entry, converting from net byte order */
RTEntry(IPAddress ip, EtherAddress eth, grid_nbr_entry *nbr,
unsigned char interface, unsigned int jiff) :
RouteEntry(nbr->ip, nbr->loc_good, nbr->loc_err, nbr->loc,
eth, ip, interface,
nbr->seq_no, nbr->num_hops > 0 ? nbr->num_hops + 1 : 0),
_init(true), is_gateway(nbr->is_gateway), ttl(nbr->ttl), last_updated_jiffies(jiff),
wst(0), last_seq_jiffies(0),
advertise_ok_jiffies(0), need_seq_ad(false), need_metric_ad(false),
last_expired_jiffies(nbr->num_hops > 0 ? 0 : jiff)
{
loc_err = ntohs(loc_err);
_seq_no = ntohl(_seq_no);
ttl = ntohl(ttl);
metric = metric_t(htonl(nbr->metric), nbr->metric_valid);
check();
}
/* copy data from this into nb, converting to net byte order */
void fill_in(grid_nbr_entry *nb) const;
};
friend class RTEntry;
typedef HashMap<IPAddress, RTEntry> RTable;
typedef RTable::const_iterator RTIter;
/* the route table */
// Invariants:
// 1. every route in the table that is not expired (num_hops > 0) is
// valid: i.e. its ttl has not run out, nor has it been in the table
// past its timeout. There is an expire timer for this route in
// _expire_timers.
// 2. no route in the table that *is* expired (num_hops == 0) has an
// entry in _expire_timers.
// 3. expired routes *are* allowed in the table, since that's what
// the DSDV description does.
RTable _rtes;
#if USE_OLD_SEQ
RTable _old_rtes;
bool use_old_route(const IPAddress &dst, unsigned jiff);
#endif
// returns true if route entry was inserted into route table, else return false
bool handle_update(RTEntry, const bool was_sender, const unsigned int jiff);
void insert_route(const RTEntry &, const GridGenericLogger::reason_t why);
void schedule_triggered_update(const IPAddress &ip, unsigned int when); // when is in jiffies
bool lookup_route(const IPAddress &dest_ip, RTEntry &entry);
typedef HashMap<IPAddress, Timer *> TMap;
typedef TMap::iterator TMIter;
struct HookPair {
DSDVRouteTable *obj;
unsigned int ip;
HookPair(DSDVRouteTable *o, unsigned int _ip) : obj(o), ip(_ip) { }
private:
HookPair() { }
};
typedef HashMap<IPAddress, HookPair *> HMap;
typedef HMap::iterator HMIter;
// Expire timer map invariants: every good route (r.good() is true)
// has a running timer in _expire_timers. No broken routes have a
// timer. Every entry in _expire_timers has a corresponding
// TimerHook pointer stored in _expire_hooks.
TMap _expire_timers;
HMap _expire_hooks;
// Trigger timer invariants: any route may have a timer in this
// table. All timers in the table must be running. Every entry in
// _trigger_timers has a corresponding TimerHook pointer stored in
// _trigger_hooks. Note: a route entry r may have a trigger timer
// even if r.need_seq_ad and r.need_metric_ad flags are false. We
// might have sent a full update before r's triggered update timer
// expired, but after r.advertise_ok_jiffies: the triggered update
// was delayed past r.advertise_ok_jiffies to enforce the minimum
// triggered update period. In that case there may be other
// destinations whose triggered updates were cancelled when r's
// trigger was delayed, and which should be advertised when r's
// triggered update timer finally fires, even if r needn't be.
TMap _trigger_timers;
HMap _trigger_hooks;
// check table, timer, and trigger hook invariants
void check_invariants(const IPAddress *ignore = 0) const;
/* max time to keep an entry in RT */
unsigned int _timeout; // msecs
/* route broadcast timing parameters */
unsigned int _period; // msecs
unsigned int _jitter; // msecs
unsigned int _min_triggered_update_period; // msecs
GridGatewayInfo *_gw_info;
GridGenericMetric *_metric;
/* binary logging */
GridGenericLogger *_log;
static const unsigned int _log_dump_period = 5 * 1000; // msecs
/* this node's addresses */
IPAddress _ip;
EtherAddress _eth;
unsigned int _seq_no; // latest sequence number for this node's route entry
unsigned int _mtu; // maximum total bytes per packet, including ethernet headers
unsigned int _bcast_count; // incremented on every broadcast
/* local DSDV radius */
unsigned int _max_hops;
/* settling time constants */
unsigned int _alpha; // percent, 0-100
unsigned int _wst0; // msecs
/* track route ads */
unsigned int _last_periodic_update; // jiffies
unsigned int _last_triggered_update; // jiffies
/* Keep and propagate route with invalid metrics? */
bool _ignore_invalid_routes;
Timer _hello_timer;
static void static_hello_hook(Timer *, void *e) { ((DSDVRouteTable *) e)->hello_hook(); }
void hello_hook();
Timer _log_dump_timer;
static void static_log_dump_hook(Timer *, void *e) { ((DSDVRouteTable *) e)->log_dump_hook(true); }
void log_dump_hook(bool reschedule);
static void static_expire_hook(Timer *, void *v)
{ ((HookPair *) v)->obj->expire_hook(((HookPair *) v)->ip); }
void expire_hook(const IPAddress &);
static void static_trigger_hook(Timer *, void *v)
{ ((HookPair *) v)->obj->trigger_hook(((HookPair *) v)->ip); }
void trigger_hook(const IPAddress &);
void send_full_update();
void send_triggered_update(const IPAddress &);
/* send a route advertisement containing the specified entries */
void build_and_tx_ad(Vector<RTEntry> &);
int max_rtes_per_ad() const {
int hdr_sz = sizeof(click_ether) + sizeof(grid_hdr) + sizeof(grid_hello);
return ((_mtu - hdr_sz) / sizeof(grid_nbr_entry));
}
public:
static unsigned int decr_ttl(unsigned int ttl, unsigned int decr)
{ return (ttl > decr ? ttl - decr : 0); }
static unsigned int jiff_to_msec(unsigned int j)
{ return (j * 1000) / CLICK_HZ; }
static unsigned int msec_to_jiff(unsigned int m)
{ return (CLICK_HZ * m) / 1000; }
private:
class RouteEntry make_generic_rte(const RTEntry &rte) { return rte; }
/* update route metric with the last hop from the advertising node */
void update_metric(RTEntry &);
/* initialize the metric for a 1-hop neighbor */
void init_metric(RTEntry &);
/* update weight settling time */
void update_wst(RTEntry *old_r, RTEntry &new_r, const unsigned int jiff);
// True iff first route's metric is preferable to second route's
// metric -- note that this is a strict comparison, if the metrics
// are equivalent, then the function returns false. This is
// necessary to get stability, e.g. when using the hopcount metric.
bool metric_preferable(const RTEntry &, const RTEntry &);
// True iff the metrics are different, either in validity or value.
bool metrics_differ(const metric_t &, const metric_t &);
// True iff first metric is strictly `less' than second metric.
// Requires both metrics to be valid.
bool metric_val_lt(const metric_t &, const metric_t &);
static String print_rtes(Element *e, void *);
static String print_rtes_v(Element *e, void *);
static String print_nbrs(Element *e, void *);
static String print_nbrs_v(Element *e, void *);
static String print_ip(Element *e, void *);
static String print_eth(Element *e, void *);
static unsigned int jiff_diff_as_msec(unsigned int j1, unsigned int j2, bool &neg); // j1 - j2, in msecs
static String jiff_diff_string(unsigned int j1, unsigned int j2);
static String print_seqno(Element *e, void *);
static int write_seqno(const String &, Element *, void *, ErrorHandler *);
static String print_paused(Element *e, void *);
static int write_paused(const String &, Element *, void *, ErrorHandler *);
static String print_dump(Element *e, void *);
const metric_t _bad_metric; // default value is ``bad''
#if ENABLE_PAUSE
bool _paused;
unsigned _snapshot_jiffies;
RTable _snapshot_rtes;
#if USE_OLD_SEQ
RTable _snapshot_old_rtes;
#endif
#endif
#if USE_OLD_SEQ
bool _use_old_route;
static String print_use_old_route(Element *e, void *);
static int write_use_old_route(const String &, Element *, void *, ErrorHandler *);
#if USE_GOOD_NEW_ROUTES
bool _use_good_new_route;
#endif
#endif
#if ENABLE_SEEN
bool _use_seen;
static const unsigned _metric_seen = 999999;
#endif
// be verbose about warnings and status messages?
bool _verbose;
void dsdv_assert_(const char *, int, const char *) const;
};
inline unsigned
dsdv_jiffies()
{
static unsigned last_click_jiffies = 0;
unsigned j = click_jiffies();
assert(j >= last_click_jiffies);
last_click_jiffies = j;
return j;
}
CLICK_ENDDECLS
#endif
syntax highlighted by Code2HTML, v. 0.9.1