Page MenuHomec4science

radiotapdecap.cc
No OneTemporary

File Metadata

Created
Sat, Jul 5, 21:42

radiotapdecap.cc

/*
* radiotapdecap.{cc,hh} -- decapsultates 802.11 packets
* John Bicket
*
* Copyright (c) 2004 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 "radiotapdecap.hh"
#include <click/etheraddress.hh>
#include <click/args.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include <clicknet/wifi.h>
#include <clicknet/radiotap.h>
#include <click/timestamp.hh>
#include <click/packet_anno.hh>
#include <clicknet/llc.h>
CLICK_DECLS
//#define NUM_RADIOTAP_ELEMENTS 20 //Julien: modified from 18 to 19; Sébastien: from 19 to 20
//#define NUM_RADIOTAP_ELEMENTS 19 //Julien: modified from 18 to 19
//static const int radiotap_elem_to_bytes[NUM_RADIOTAP_ELEMENTS] =
//{8, /* IEEE80211_RADIOTAP_TSFT */
// 1, /* IEEE80211_RADIOTAP_FLAGS */
// 1, /* IEEE80211_RADIOTAP_RATE */
// 4, /* IEEE80211_RADIOTAP_CHANNEL */
// 2, /* IEEE80211_RADIOTAP_FHSS */
// 1, /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
// 1, /* IEEE80211_RADIOTAP_DBM_ANTNOISE */
// 2, /* IEEE80211_RADIOTAP_LOCK_QUALITY */
// 2, /* IEEE80211_RADIOTAP_TX_ATTENUATION */
// 2, /* IEEE80211_RADIOTAP_DB_TX_ATTENUATION */
// 1, /* IEEE80211_RADIOTAP_DBM_TX_POWER */
// 1, /* IEEE80211_RADIOTAP_ANTENNA */
// 1, /* IEEE80211_RADIOTAP_DB_ANTSIGNAL */
// 1, /* IEEE80211_RADIOTAP_DB_ANTNOISE */
// 2, /* IEEE80211_RADIOTAP_RX_FLAGS */
// 2, /* IEEE80211_RADIOTAP_TX_FLAGS */
// 1, /* IEEE80211_RADIOTAP_RTS_RETRIES */
// 1, /* IEEE80211_RADIOTAP_DATA_RETRIES */
// 8, /* Sébastien: offset for XChannel field (see Radiotap.org) */
// 3, /* IEEE80211_RADIOTAP_MCS (Julien: I added that) */
//};
//static int rt_el_present(struct ieee80211_radiotap_header *th, u_int32_t element)
//{
// if (element > NUM_RADIOTAP_ELEMENTS)
// return 0;
// return le32_to_cpu(th->it_present) & (1 << element);
//}
//static int rt_check_header(struct ieee80211_radiotap_header *th, int len)
//{
// int bytes = 0;
// int x = 0;
// if (th->it_version != 0) {
// return 0;
// }
// if (le16_to_cpu(th->it_len) < sizeof(struct ieee80211_radiotap_header)) {
// return 0;
// }
// for (x = 0; x < NUM_RADIOTAP_ELEMENTS; x++) {
// if (rt_el_present(th, x))
// bytes += radiotap_elem_to_bytes[x];
// }
// if (le16_to_cpu(th->it_len) < sizeof(struct ieee80211_radiotap_header) + bytes) {
// return 0;
// }
// if (le16_to_cpu(th->it_len) > len) {
// return 0;
// }
// return 1;
//}
//static u_int8_t *rt_el_offset(struct ieee80211_radiotap_header *th, u_int32_t element) {
// unsigned int x = 0;
// u_int8_t *offset = ((u_int8_t *) th) + sizeof(ieee80211_radiotap_header);
// for (x = 0; x < NUM_RADIOTAP_ELEMENTS && x < element; x++) {
// if (rt_el_present(th, x))
// offset += radiotap_elem_to_bytes[x];
// }
// return offset;
//}
//RadiotapDecap::RadiotapDecap()
//{
//}
//RadiotapDecap::~RadiotapDecap()
//{
//}
//int
//RadiotapDecap::configure(Vector<String> &conf, ErrorHandler *errh)
//{
// _debug = false;
// _max_length = 0;
// _nb_packets = 0;
// return Args(conf, this, errh)
// .read("DEBUG", _debug)
// .read("WIFI_ADDR", _eth_addr_1)
// .read("DST_ADDR", _dst_addr)
// .complete();
//}
//Packet *
//RadiotapDecap::simple_action(Packet *p)
//{
// struct ieee80211_radiotap_header *th = (struct ieee80211_radiotap_header *) p->data();
// struct click_wifi_extra *ceh = WIFI_EXTRA_ANNO(p);
// int len0 = p->length();
// int len1=-1;
// if (rt_check_header(th, p->length())) {
// memset((void*)ceh, 0, sizeof(struct click_wifi_extra));
// ceh->magic = WIFI_EXTRA_MAGIC;
// if (rt_el_present(th, IEEE80211_RADIOTAP_FLAGS)) {
// u_int8_t flags = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_FLAGS));
// if (flags & IEEE80211_RADIOTAP_F_DATAPAD) {
// ceh->pad = 1;
// }
// if (flags & IEEE80211_RADIOTAP_F_FCS) {
// p->take(4);
// }
// }
// if (rt_el_present(th, IEEE80211_RADIOTAP_RATE)) {
// ceh->rate = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_RATE));
// }
// len1 = p->length();
// //Julien: bricolage total!
// if (rt_el_present(th, IEEE80211_RADIOTAP_MCS)) {
// ceh->rate1 = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_MCS));
// }
// if (rt_el_present(th, IEEE80211_RADIOTAP_MCS)) {
// ceh->rate2 = *((u_int8_t *) (rt_el_offset(th, IEEE80211_RADIOTAP_MCS)+1));
// }
// if (rt_el_present(th, IEEE80211_RADIOTAP_MCS)) {
// ceh->rate3 = *((u_int8_t *) (rt_el_offset(th, IEEE80211_RADIOTAP_MCS)+2));
// }
// //
// if (rt_el_present(th, IEEE80211_RADIOTAP_DBM_ANTSIGNAL))
// ceh->rssi = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_DBM_ANTSIGNAL));
// //Julien:
// if (rt_el_present(th, IEEE80211_RADIOTAP_CHANNEL))
// ceh->channel = le16_to_cpu(*((u_int16_t *) rt_el_offset(th, IEEE80211_RADIOTAP_CHANNEL)));
// if (rt_el_present(th, IEEE80211_RADIOTAP_DBM_ANTNOISE))
// ceh->silence = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_DBM_ANTNOISE));
// if (rt_el_present(th, IEEE80211_RADIOTAP_DB_ANTSIGNAL))
// ceh->rssi = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_DB_ANTSIGNAL));
// if (rt_el_present(th, IEEE80211_RADIOTAP_DB_ANTNOISE))
// ceh->silence = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_DB_ANTNOISE));
// if (rt_el_present(th, IEEE80211_RADIOTAP_RX_FLAGS)) {
// u_int16_t flags = le16_to_cpu(*((u_int16_t *) rt_el_offset(th, IEEE80211_RADIOTAP_RX_FLAGS)));
// if (flags & IEEE80211_RADIOTAP_F_RX_BADFCS)
// ceh->flags |= WIFI_EXTRA_RX_ERR;
// }
// if (rt_el_present(th, IEEE80211_RADIOTAP_TX_FLAGS)) {
// u_int16_t flags = le16_to_cpu(*((u_int16_t *) rt_el_offset(th, IEEE80211_RADIOTAP_TX_FLAGS)));
// ceh->flags |= WIFI_EXTRA_TX;
// if (flags & IEEE80211_RADIOTAP_F_TX_FAIL)
// ceh->flags |= WIFI_EXTRA_TX_FAIL;
// }
// if (rt_el_present(th, IEEE80211_RADIOTAP_DATA_RETRIES))
// ceh->retries = *((u_int8_t *) rt_el_offset(th, IEEE80211_RADIOTAP_DATA_RETRIES));
// p->pull(le16_to_cpu(th->it_len));
// p->set_mac_header(p->data()); // reset mac-header pointer
// }
#define NUM_RADIOTAP_ELEMENTS 22
static const int radiotap_elem_to_bytes[NUM_RADIOTAP_ELEMENTS] =
{8, /* IEEE80211_RADIOTAP_TSFT */
1, /* IEEE80211_RADIOTAP_FLAGS */
1, /* IEEE80211_RADIOTAP_RATE */
4, /* IEEE80211_RADIOTAP_CHANNEL */
2, /* IEEE80211_RADIOTAP_FHSS */
1, /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
1, /* IEEE80211_RADIOTAP_DBM_ANTNOISE */
2, /* IEEE80211_RADIOTAP_LOCK_QUALITY */
2, /* IEEE80211_RADIOTAP_TX_ATTENUATION */
2, /* IEEE80211_RADIOTAP_DB_TX_ATTENUATION */
1, /* IEEE80211_RADIOTAP_DBM_TX_POWER */
1, /* IEEE80211_RADIOTAP_ANTENNA */
1, /* IEEE80211_RADIOTAP_DB_ANTSIGNAL */
1, /* IEEE80211_RADIOTAP_DB_ANTNOISE */
2, /* IEEE80211_RADIOTAP_RX_FLAGS */
2, /* IEEE80211_RADIOTAP_TX_FLAGS */
1, /* IEEE80211_RADIOTAP_RTS_RETRIES */
1, /* IEEE80211_RADIOTAP_DATA_RETRIES */
8, /* IEEE80211_RADIOTAP_XCHANNEL */
3, /* IEEE80211_RADIOTAP_MCS */
8, /* IEEE80211_RADIOTAP_AMPDU_STATUS */
12, /* IEEE80211_RADIOTAP_VHT */
};
static int rt_el_present(struct ieee80211_radiotap_header *th, u_int32_t element)
{
if (element > NUM_RADIOTAP_ELEMENTS)
return 0;
return le32_to_cpu(th->it_present) & (1 << element);
}
static int rt_check_header(struct ieee80211_radiotap_header *th, int len, u_int8_t *offsets[], u_int8_t additional_it_present_flags)
{
int bytes = additional_it_present_flags * sizeof(u_int32_t);
bytes += bytes % 8;
int x = 0;
u_int8_t *ptr = (u_int8_t *)(th + 1);
if (th->it_version != 0) {
return 0;
}
if (le16_to_cpu(th->it_len) < sizeof(struct ieee80211_radiotap_header)) {
return 0;
}
for (x = 0; x < NUM_RADIOTAP_ELEMENTS; x++) {
if (rt_el_present(th, x)) {
int radiotap_padding_size = radiotap_elem_to_bytes[x];
if(x==IEEE80211_RADIOTAP_CHANNEL) radiotap_padding_size = 2;
if(x==IEEE80211_RADIOTAP_MCS) radiotap_padding_size = 1;
#ifdef __linux__
if(x==IEEE80211_RADIOTAP_AMPDU_STATUS) radiotap_padding_size = 4;
#else
if(x==IEEE80211_RADIOTAP_A_MPDU_STATUS) radiotap_padding_size = 4;
#endif
int pad = bytes % radiotap_padding_size;
if (pad)
bytes += radiotap_padding_size - pad;
offsets[x] = ptr + bytes;
bytes += radiotap_elem_to_bytes[x];
}
}
if (le16_to_cpu(th->it_len) < sizeof(struct ieee80211_radiotap_header) + bytes) {
return 0;
}
if (le16_to_cpu(th->it_len) > len) {
return 0;
}
return 1;
}
RadiotapDecap::RadiotapDecap()
{
}
RadiotapDecap::~RadiotapDecap()
{
}
int
RadiotapDecap::configure(Vector<String> &conf, ErrorHandler *errh)
{
_debug = false;
return Args(conf, this, errh)
.read("DEBUG", _debug)
.read("WIFI_ADDR", _eth_addr_1)
.read("DST_ADDR", _dst_addr)
.complete();
}
Packet *
RadiotapDecap::simple_action(Packet *p)
{
u_int8_t *offsets[NUM_RADIOTAP_ELEMENTS];
struct ieee80211_radiotap_header *th = (struct ieee80211_radiotap_header *) p->data();
u_int8_t additional_it_present_flags = 0;
u_int32_t *itpp = (u_int32_t*) &th->it_present;
while(le32_to_cpu(*itpp) & (1 << IEEE80211_RADIOTAP_EXT)){
additional_it_present_flags++;
itpp += 1;
}
int len0 = p->length();
int len1=-1;
struct click_wifi_extra *ceh = WIFI_EXTRA_ANNO(p);
if (rt_check_header(th, p->length(), offsets, additional_it_present_flags)) {
memset((void*)ceh, 0, sizeof(struct click_wifi_extra));
ceh->magic = WIFI_EXTRA_MAGIC;
if (rt_el_present(th, IEEE80211_RADIOTAP_FLAGS)) {
u_int8_t flags = *offsets[IEEE80211_RADIOTAP_FLAGS];
if (flags & IEEE80211_RADIOTAP_F_DATAPAD) {
ceh->pad = 1;
}
if (flags & IEEE80211_RADIOTAP_F_FCS) {
p->take(4);
}
}
len1 = p->length();
if (rt_el_present(th, IEEE80211_RADIOTAP_RATE)) {
ceh->rate = *offsets[IEEE80211_RADIOTAP_RATE];
}
if (rt_el_present(th, IEEE80211_RADIOTAP_DBM_ANTSIGNAL))
ceh->rssi = *offsets[IEEE80211_RADIOTAP_DBM_ANTSIGNAL];
if (rt_el_present(th, IEEE80211_RADIOTAP_DBM_ANTNOISE))
ceh->silence = *offsets[IEEE80211_RADIOTAP_DBM_ANTNOISE];
if (rt_el_present(th, IEEE80211_RADIOTAP_DB_ANTSIGNAL))
ceh->rssi = *offsets[IEEE80211_RADIOTAP_DB_ANTSIGNAL];
if (rt_el_present(th, IEEE80211_RADIOTAP_DB_ANTNOISE))
ceh->silence = *offsets[IEEE80211_RADIOTAP_DB_ANTNOISE];
if (rt_el_present(th, IEEE80211_RADIOTAP_RX_FLAGS)) {
u_int16_t flags = le16_to_cpu(*((u_int16_t *) offsets[IEEE80211_RADIOTAP_RX_FLAGS]));
if (flags & IEEE80211_RADIOTAP_F_RX_BADFCS)
ceh->flags |= WIFI_EXTRA_RX_ERR;
}
if (rt_el_present(th, IEEE80211_RADIOTAP_TX_FLAGS)) {
u_int16_t flags = le16_to_cpu(*((u_int16_t *) offsets[IEEE80211_RADIOTAP_TX_FLAGS]));
ceh->flags |= WIFI_EXTRA_TX;
if (flags & IEEE80211_RADIOTAP_F_TX_FAIL)
ceh->flags |= WIFI_EXTRA_TX_FAIL;
}
if (rt_el_present(th, IEEE80211_RADIOTAP_DATA_RETRIES))
ceh->retries = *offsets[IEEE80211_RADIOTAP_DATA_RETRIES];
if (rt_el_present(th, IEEE80211_RADIOTAP_MCS)) {
ceh->rate1 = *offsets[IEEE80211_RADIOTAP_MCS];
ceh->rate2 = *(offsets[IEEE80211_RADIOTAP_MCS]+1);
ceh->rate3 = *(offsets[IEEE80211_RADIOTAP_MCS]+2);
}
#ifdef __linux__
if(rt_el_present(th, IEEE80211_RADIOTAP_AMPDU_STATUS)) {
if((le16_to_cpu(*((u_int16_t *) (offsets[IEEE80211_RADIOTAP_AMPDU_STATUS]+4))) & 0x0008) > 0) // is last subframe
memcpy(&ceh->max_tries, offsets[IEEE80211_RADIOTAP_AMPDU_STATUS], 4); // copy first 4 bytes of AMPDU (reference number)
}
#else
if(rt_el_present(th, IEEE80211_RADIOTAP_A_MPDU_STATUS)) {
if((le16_to_cpu(*((u_int16_t *) (offsets[IEEE80211_RADIOTAP_A_MPDU_STATUS]+4))) & 0x0008) > 0) // is last subframe
memcpy(&ceh->max_tries, offsets[IEEE80211_RADIOTAP_A_MPDU_STATUS], 4); // copy first 4 bytes of AMPDU (reference number)
}
#endif
p->pull(le16_to_cpu(th->it_len));
p->set_mac_header(p->data()); // reset mac-header pointer
}
_now.assign_now();
if(_debug) {
uint8_t type;
uint8_t subtype;
uint8_t dir;
uint8_t retry;
EtherAddress src, dst, bssid;
uint8_t bandwidth = 0;
uint8_t GI = 0;
uint8_t mcs_index = 0;
struct click_wifi *w = (struct click_wifi *)p->data();
type = w->i_fc[0] & WIFI_FC0_TYPE_MASK; /* And recover the fields */
subtype = w->i_fc[0] & WIFI_FC0_SUBTYPE_MASK;
dir = w->i_fc[1] & WIFI_FC1_DIR_MASK;
retry = w->i_fc[1] & WIFI_FC1_RETRY; // >0 if this is a retry
bool packet_ok=false;
//we need to declare things before jumping to done:
uint8_t known_field;
/* Is this a data frame? */
if (type != WIFI_FC0_TYPE_DATA) goto done;
/* Is this regular data or QoS data (and their NULL versions)? i.e. 0x00, 0x40, 0x80 or 0xc0 */
if(!(subtype == WIFI_FC0_SUBTYPE_DATA
|| subtype == WIFI_FC0_SUBTYPE_NODATA
|| subtype == WIFI_FC0_SUBTYPE_QOS
|| subtype == WIFI_FC0_SUBTYPE_QOS_NULL)) goto done;
/* Get the source, destination and bssid addresses */
/* Check the direction flags */
switch (dir) {
case WIFI_FC1_DIR_NODS:
dst = EtherAddress(w->i_addr1);
src = EtherAddress(w->i_addr2);
bssid = EtherAddress(w->i_addr3);
/* Most likely ARP requests in ad-hoc mode, ignore and skip */
break;
case WIFI_FC1_DIR_TODS:
bssid = EtherAddress(w->i_addr1);
src = EtherAddress(w->i_addr2);
dst = EtherAddress(w->i_addr3);
break;
case WIFI_FC1_DIR_FROMDS:
dst = EtherAddress(w->i_addr1);
bssid = EtherAddress(w->i_addr2);
src = EtherAddress(w->i_addr3);
break;
case WIFI_FC1_DIR_DSTODS:
/* We should never arrive here */
//assert(false);
break;
default:
/* We should never arrive here */
//assert(false);
break;
}
if (dst.is_broadcast() || dst.is_group()) goto done;
if (src != _eth_addr_1 && dst != _eth_addr_1) goto done; // keep messages from or to local node
if(_dst_addr != EtherAddress() && dst != _dst_addr) goto done;
known_field = ceh->rate1;
bandwidth = ceh->rate2 & 3;
GI = ceh->rate2 & (1 << 2);
mcs_index = ceh->rate3;
packet_ok = true;
if (p->length() > _max_length)
_max_length = p->length();
_nb_packets++;
if((known_field == 0 && (_now - _last_print_0).sec() >= 5) || (known_field != 0 && (_now - _last_print).sec() >= 5)) {
click_chatter("[RadiotapDecap %s] Parsed wifi frame of lengths %d, %d, %d sent by %s to dst %s with known_field=%d, bw=%d, GI=%d, mcs_index=%d. Retry = %d\n",
_now.unparse().c_str(), len0, len1, p->length(), src.unparse().c_str(), dst.unparse().c_str(), known_field, bandwidth, GI, mcs_index, retry);
click_chatter(" Parameters: type=%d, subtype=%d, dir=%d, dst=%s, src=%s, bssid=%s",
type, subtype, dir, dst.unparse().c_str(), src.unparse().c_str(), bssid.unparse().c_str());
click_chatter(" is_present_mcs=%d, present_field=%s, offset=%d, max_length=%d, nb_packets=%d, ampdu_ref=%s",
rt_el_present(th, IEEE80211_RADIOTAP_MCS), String(le32_to_cpu(th->it_present)).c_str(),
(int) (offsets[IEEE80211_RADIOTAP_MCS] - (((u_int8_t *) th) + sizeof(ieee80211_radiotap_header))),
_max_length, _nb_packets, String(*((uint32_t *) &ceh->max_tries)).c_str());
click_chatter(" Test results:");
for (HashTable<uint32_t,int>::iterator it = _test_if.begin(); it; it++) {
click_chatter(" Counter for %s is %d", String(it.key()).c_str(), it.value());
}
if (known_field == 0)
_last_print_0.assign_now();
else
_last_print.assign_now();
_max_length = 0;
_nb_packets = 0;
}
done:
{
if (p->length() > _max_length)
_max_length = p->length();
// is MCS present?
if(packet_ok) {
if (_test_if.get_pointer(le32_to_cpu(th->it_present)) == 0)
_test_if.set(le32_to_cpu(th->it_present),1);
else
_test_if[le32_to_cpu(th->it_present)]++ ;
}
else {
// why does it arrive here?
if (type != WIFI_FC0_TYPE_DATA) {
if (_test_if.get_pointer(0) == 0)
_test_if.set(0,1);
else
_test_if[0]++ ;
}
if(!(subtype == WIFI_FC0_SUBTYPE_DATA
|| subtype == WIFI_FC0_SUBTYPE_NODATA
|| subtype == WIFI_FC0_SUBTYPE_QOS
|| subtype == WIFI_FC0_SUBTYPE_QOS_NULL)) {
if (_test_if.get_pointer(1) == 0)
_test_if.set(1,1);
else
_test_if[1]++ ;
}
if (dst.is_broadcast()) {
if (_test_if.get_pointer(5) == 0)
_test_if.set(5,1);
else
_test_if[5]++ ;
}
if (dst.is_group()) {
if (_test_if.get_pointer(6) == 0)
_test_if.set(6,1);
else
_test_if[6]++ ;
}
if (src != _eth_addr_1) {
if (_test_if.get_pointer(7) == 0)
_test_if.set(7,1);
else {
_test_if[7]++ ;
}
}
if(_dst_addr != EtherAddress() && (dst != _dst_addr || dst != _eth_addr_1)) {
if (_test_if.get_pointer(8) == 0)
_test_if.set(8,1);
else {
_test_if[8]++ ;
}
}
}
_nb_packets++;
_now.assign_now();
if((_now - _last_print_done).sec() >= 5) {
_last_print_done.assign_now();
click_chatter("[RadiotapDecap %s] Test results:", _now.unparse().c_str());
for (HashTable<uint32_t,int>::iterator it = _test_if.begin(); it; it++)
click_chatter(" Counter for %s is %d", String(it.key()).c_str(), it.value());
}
}
}
return p;
}
enum {H_DEBUG};
static String
RadiotapDecap_read_param(Element *e, void *thunk)
{
RadiotapDecap *td = (RadiotapDecap *)e;
switch ((uintptr_t) thunk) {
case H_DEBUG:
return String(td->_debug) + "\n";
default:
return String();
}
}
static int
RadiotapDecap_write_param(const String &in_s, Element *e, void *vparam,
ErrorHandler *errh)
{
RadiotapDecap *f = (RadiotapDecap *)e;
String s = cp_uncomment(in_s);
switch((intptr_t)vparam) {
case H_DEBUG: { //debug
bool debug;
if (!BoolArg().parse(s, debug))
return errh->error("debug parameter must be boolean");
f->_debug = debug;
break;
}
}
return 0;
}
void
RadiotapDecap::add_handlers()
{
add_read_handler("debug", RadiotapDecap_read_param, H_DEBUG);
add_write_handler("debug", RadiotapDecap_write_param, H_DEBUG);
}
CLICK_ENDDECLS
EXPORT_ELEMENT(RadiotapDecap)

Event Timeline