Page MenuHomec4science

neighbors.cc
No OneTemporary

File Metadata

Created
Fri, Jul 4, 16:12

neighbors.cc

#include <click/config.h>
/*
#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>
#include <cstring>
#include <math.h>
#include <cstdlib>
*/
#include "neighbors.hh"
using namespace std;
CLICK_DECLS
#define DEBUG_CHATTER(arg, ...) do { if (_debug) { click_chatter(arg, ## __VA_ARGS__);} } while (0)
#define VERB_DEBUG_CHATTER(arg, ...) do { if (_verb_debug) { click_chatter(arg, ## __VA_ARGS__);} } while (0)
Neighbors::Neighbors() : _notifier(ActiveNotifier::SEARCH_CONTINUE_WAKE), _hello_timer(this), _frame_timer(this), _capacity_estimation_timer(this) {}
Neighbors::~Neighbors(){}
void * Neighbors::cast(const char *n)
{
if (strcmp(n, ActiveNotifier::EMPTY_NOTIFIER) == 0){
return &_notifier;
}
else
return Element::cast(n);
}
int Neighbors::configure(Vector<String> &conf, ErrorHandler *errh){
_notifier.initialize(ActiveNotifier::EMPTY_NOTIFIER, router());
set_mcs_to_rate();
//set_plc_mac();
_static = false;
_debug = false;
_verb_debug = false;
_nr_interfaces = 0;
_eth_addr1_present = false;
_eth_addr2_present = false;
_period_hello = 10;
_period_frame_ms = 1000;
_timeout_duration = 600; //remove links after _timeout_duration seconds
_period_capacity_estimation = 20; //update capacity every _period_capacity_estimation secs
_load_estimation = 5000;
_size_estimation_packet = 1300;
_print_stat_freq = 0;
_last_time_print = Timestamp();
_now = Timestamp();
_csv_print = false;
_current_int2_index = 1;
_current_int1_index = 1;
_last_seq_number = 0;
_capacity_tonemap_moni = false;
_pass_int2 = false;
_pass_int1 = false;
_alpha_ewma_plc = 200; // for PLC, estimation already averaged by int6krate, so greater
_alpha_ewma_wifi = 100;
_ewma = false;
_begin_duration = BEGIN_DURATION;
_send_empty_frames = true;
EtherAddress eth_addr_wifi2;
EtherAddress eth_addr_plc;
_active = true;
_is_reliable_int1 = false;
_is_reliable_int2 = false;
#ifdef CLICK_USERLEVEL
if(_static)
_ff = FromFile();
#endif
if (Args(conf, this, errh)
.read("WIFI_ADDR", _eth_addr1)
.read("WIFI2_ADDR", eth_addr_wifi2)
.read("PLC_ADDR", eth_addr_plc)
.read("STATIC", _static)
#ifdef CLICK_USERLEVEL
.read("FILENAME", FilenameArg(), _ff.filename())
#endif
.read("DEBUG", _debug)
.read("VERB_DEBUG", _verb_debug)
.read("HELLO_PERIOD_SEC", _period_hello)
.read("FRAME_PERIOD_MSEC", _period_frame_ms)
.read("LOAD_ESTIMATION_PER_LINK", _load_estimation) // load per link in bits/s
.read("SIZE_ESTIMATION_PACKET", _size_estimation_packet) // in B
.read("TIMEOUT", _timeout_duration)
.read("PRINT_STAT", _print_stat_freq)
.read("CSV_PRINT", _csv_print)
.read("PRINT_UPDATES", _print_updates)
.read("MONI_WIFI", _capacity_tonemap_moni)
.read("PERIOD_CAPA_EST_SEC", _period_capacity_estimation)
.read("BEGIN_DURATION", _begin_duration)
.read("EWMA", _ewma) // if rates averaged through EWMA; otherwise, average on 1000/_alpha last measurements
.read("ALPHA_EWMA_PLC", _alpha_ewma_plc) // alpha for EWMA for rates in per 1000; if no EWMA, gives number of measurements to average on by 1000/_alpha
.read("ALPHA_EWMA_WIFI", _alpha_ewma_wifi)
.complete() < 0)
return -1;
if(eth_addr_wifi2!=_empty_eth && eth_addr_plc!=_empty_eth)
return errh->error("[Neighbors] WIFI2_ADDR and PLC_ADDR cannot be both given.");
if(eth_addr_wifi2!=_empty_eth) {
_eth_addr2 = eth_addr_wifi2;
_addr2_is_plc = false;
_alpha_ewma_int2=_alpha_ewma_wifi;
}
if(eth_addr_plc!=_empty_eth) {
_eth_addr2 = eth_addr_plc;
_addr2_is_plc = true;
_alpha_ewma_int2=_alpha_ewma_plc;
}
if(_verb_debug)
_debug = true;
if(_debug)
click_chatter("[Neighbors] enters configure.\n");
if(_eth_addr1 != _empty_eth) {
_alpha_ewma_int1 = _alpha_ewma_wifi;
_eth_addr1_present = true;
_nr_interfaces++;
//store last bytes for quick mapping later:
const unsigned char *data_addr = _eth_addr1.data();
LastAddrBytes lastbytes;
for(uint8_t b=0; b<NB_BYTES_HOP; ++b) {
lastbytes._lastbytes[b] = data_addr[6-NB_BYTES_HOP+b];
}
lastbytes.set_hash();
_lastbytes2neighbor[lastbytes] = _eth_addr1;
set_wifi_addr(_eth_addr1);
}
else
_is_reliable_int1 = true;
if(_eth_addr2 != _empty_eth) {
_eth_addr2_present = true;
_nr_interfaces++;
//store last bytes for quick mapping later:
const unsigned char *data_addr = _eth_addr2.data();
LastAddrBytes lastbytes;
for(uint8_t b=0; b<NB_BYTES_HOP; ++b) {
lastbytes._lastbytes[b] = data_addr[6-NB_BYTES_HOP+b];
}
lastbytes.set_hash();
_lastbytes2neighbor[lastbytes] = _eth_addr2;
if(_addr2_is_plc)
set_plc_addr(_eth_addr2);
else
set_wifi_addr(_eth_addr2);
}
else
_is_reliable_int2 = true;
if(_nr_interfaces == 0 and !_static){
return errh->error("[Neighbors] !!! ERROR: Have 0 interface, should have at least 1 !!!\n");
}
#ifdef CLICK_USERLEVEL
if(_static){
if(parse_file(errh)){
return errh->error("[Neighbors] Error parsing file.");
}
}
#endif
if(_debug){
click_chatter("[Neighbors] configure done.\n");
click_chatter("[Neighbors] Configure done. I have %d interfaces.\n", _nr_interfaces);
}
return 0;
}
int Neighbors::initialize(ErrorHandler *errh){
if(_debug)
click_chatter("[Neighbors] Initializing...\n");
Element::initialize(errh);
if(_eth_addr2_present && _addr2_is_plc)
set_plc_neighbor_groups();
_hello_timer.initialize(this);
_hello_timer.schedule_after_sec(1);
_frame_timer.initialize(this);
int wait_time_empty_frame = 60; // begin to send frames only after 60 seconds (to get all hellos)
_frame_timer.schedule_after_msec(click_random(wait_time_empty_frame*1000,15*wait_time_empty_frame*100)); //between 1* and 1.5* of wait_time_empty_frame
_capacity_estimation_timer.initialize(this);
_capacity_estimation_timer.schedule_after_msec(2*wait_time_empty_frame);
if(!_capacity_tonemap_moni)
capacity_estimation_wifi();
_begin.assign_now();
// if(!_static) {
// _hello_timer.schedule_after_sec(5);
// }
// else {
// _hello_timer.schedule_after_sec(1);
// }
if(_debug)
click_chatter("[Neighbors] Done!\n");
if(_eth_addr2 != _empty_eth && _addr2_is_plc)
send_mm_plc(); // to get chip address
click_chatter("Size of acne header is %d", sizeof(acne_header));
return 0;
}
#ifdef CLICK_USERLEVEL
int Neighbors::parse_file(ErrorHandler *errh){
if(_debug) click_chatter("[Neighbors] Reading text file...");
if (_ff.initialize(errh) < 0) return errh->error("Could not open file");
String line;
int nb_line = 0;
while(1){
nb_line++;
if (_ff.read_line(line, errh, true) <= 0) {
break;
}
if (line.starts_with("LINK")) {
click_chatter("[Neighbors] Parsing link...");
Vector<String> link_elements = parse_string(line.substring(4), " ");
if (link_elements.size() != 4){
click_chatter("[Neighbors] Illegal link declaration!");
return 1;
}
EtherAddress interfaces[2];
for(int i=0; i<=1; i++){
if(!cp_ethernet_address(link_elements[i], &interfaces[i])){
click_chatter("[Neighbors] Illegal link declaration!");
return 1;
}
}
//check if this is an outgoing link for me
//bool concerns_me = false;
if(interfaces[0] == _eth_addr1 || interfaces[0] == _eth_addr2){
;
} else {
continue;
}
//I'm part of this link.
double capa;
if (!cp_double(link_elements[2], &capa)) {
click_chatter("[Neighbors] Error in file at line %d: invalid capa", nb_line);
return 1;
}
int inter;
if (!cp_integer(link_elements[3], &inter)) {
click_chatter("[Neighbors] Error in file at line %d: invalid interface", nb_line);
return 1;
}
//build neighbor
NeighborLink link = NeighborLink(interfaces[1], inter, 0);
link.capa = (uint32_t)capa;
link.capa_th = (uint32_t)capa;
_neighbors[interfaces[1]] = link;
}
}
if(_debug){
click_chatter("Neighbors configured from file. Neighbors:\n");
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
NeighborLink link = it.value();
if(link.interface == INTERFACE2)
click_chatter("[Neighbors]\t (%s): interface=PLC, capa=%d, success proba = %d", link.eth_addr.unparse().c_str(), link.capa, link.success_proba);
if(link.interface == INTERFACE1)
click_chatter("[Neighbors]\t (%s): interface=WIFI, capa=%d, success proba = %d.", link.eth_addr.unparse().c_str(), link.capa, link.success_proba);
}
}
return 0;
}
#endif
void Neighbors::push(int port, Packet* p) {
if(port < _nr_interfaces){
//Receives a layer 2.5 frame representing a HELLO message.
parse_hello(p, port);
} else if ((_nr_interfaces == 2 && port == 2) || (_nr_interfaces == 1 && _eth_addr1_present && port == 1) ||
(!_addr2_is_plc && _nr_interfaces == 2 && port == 3) ) {
if(_capacity_tonemap_moni)
parse_wifi_frame(p);
if(p)
p->kill();
} else if(_addr2_is_plc && ((_nr_interfaces == 2 && port == 3) || (_nr_interfaces == 1 && _eth_addr2_present && port == 1))){
click_ether *e = (click_ether *) p->data();
if(e->ether_type == htons(ETHERTYPE_HP_AV)) { // should always be true
//check if this is a reply for a tone map request
click_hp_av_tone_map_rep *hpavh = (click_hp_av_tone_map_rep *) (e + 1);
/*//TODO: replace this with a Classifier in the Click script!
if(hpavh->MMType == htons(TONE_MAP_REP) && _capacity_tonemap_moni) {
int plc_rate = processToneMapRep(hpavh);
if(plc_rate == -1 || plc_rate == -2){
if(is_in_hash_table(_neighbors, _current_plc_addr)) {
if(_debug)
click_chatter("[Neighbors] PLC Rate : Not Connected (unknown MAC or Slot %d). current neighbor = %s",
plc_rate,_current_plc_addr.unparse().c_str());
//We consider a neighbor unkown to Fifa as an instance of a zero-rate measure:
_neighbors.get_pointer(_current_plc_addr)->update_plc(0);
}
}
else {
if(is_in_hash_table(_neighbors, _current_plc_addr)) {
if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Neighbor %s has PLC Rate : %d Kbps", _now.unparse().c_str(), _current_plc_addr.unparse().c_str(), plc_rate);
}
NeighborLink *link = _neighbors.get_pointer(_current_plc_addr);
Timestamp now;
now.assign_now();
if((now - link->last_update).sec() >= 1 && (_now - _begin).sec() >= UPDATE_AFTER_SEC) { // update only every second at most and after 10 seconds
uint32_t old_rate = link->capa;
link->update_plc(plc_rate);
if(_print_updates) {
click_chatter("[RoutingLinks %s]Updating capacity of link %s: rateD=%d, new capa %d, old capa %d, capa_th %d, proba %d",
now.unparse().c_str(), link->eth_addr.unparse().c_str(), plc_rate,
link->capa, old_rate, link->capa_th, link->success_proba);
}
}
}
}
p->kill();
return;
}
else */
if (hpavh->MMType == htons(NW_STATS_REP)) {
rec_mm_plc(p);
p->kill();
return;
}
else {
output(4).push(p);
return;
}
}
if(_nr_interfaces == 2) output(4).push(p);
else output(2).push(p);
}
}
/*
* Interprete PLC MMType messages:
*/
uint32_t
Neighbors::processToneMapRep(click_hp_av_tone_map_rep *tm_rep){
if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Processing ToneMap Rep...", _now.unparse().c_str());
}
// returns the expected Tone Map
int i;
struct modulation_stats stats = {0,0,0,0,0,0,0,0,0};
uint16_t max_carriers;
uint32_t plc_rate;
switch (tm_rep->mstatus) {
case 0x00:
// click_chatter("TONEMAP REQ Status: Success\n");
break;
case 0x01:
// click_chatter("TONEMAP REQ Status: Unknown MAC address\n");
return -1; // Unknown MAC Address Flag
break;
case 0x02:
// click_chatter("TONEMAP REQ Status: unknown ToneMap slot\n");
return -2; // Unknown Slot Flag
break;
}
//click_chatter("TONEMAP REQ Tone map slot: %02hhd\n", tm_rep->tmslot);
//click_chatter("TONEMAP REQ Number of tone map: %02hhd\n", tm_rep->num_tms);
//click_chatter("TONEMAP REQ Tone map number of active carriers: %d\n", tm_rep->tm_num_act_carrier);
max_carriers = tm_rep->tm_num_act_carrier / 2;
if (tm_rep->tm_num_act_carrier & 1)
max_carriers += 1;
uint8_t mod_list[1156];
memset(mod_list,0,1156);
for (i = 0; i < max_carriers*2; i=i+2) {
mod_list[i] = get_carrier_modulation(tm_rep->carriers[i/2].mod_carrier_lo, &stats);
mod_list[i+1] = get_carrier_modulation(tm_rep->carriers[i/2].mod_carrier_hi, &stats);
}
// Christina: corrected Forward Error Correction Code (16/21) and Guard Interval (5.56 \mu s)
uint32_t sum_symbols = (1*stats.bpsk + 2*stats.qpsk + 3*stats.qam8 + 4*stats.qam16 + 6*stats.qam64 +
8*stats.qam256 + 10*stats.qam1024 + 12*stats.unknown);
uint64_t operand1 = (uint64_t)(16*100*1024) * ((uint64_t) sum_symbols);
uint32_t operand2 = (uint32_t)(21*(4096+556));
plc_rate = (uint32_t)(int_divide(operand1, operand2));
if(_debug && stats.unknown != 0)
click_chatter("Number of carriers with Unknown/unused modulation: %d \n", stats.unknown);
return plc_rate;
}
uint8_t Neighbors::get_carrier_modulation(short unsigned int modulation, struct modulation_stats *stats)
{
switch(modulation) {
case NO:
stats->no++;
return 0;
case BPSK:
stats->bpsk++;
return 1;
case QPSK:
stats->qpsk++;
return 2;
case QAM_8:
stats->qam8++;
return 3;
case QAM_16:
stats->qam16++;
return 4;
case QAM_64:
stats->qam64++;
return 6;
case QAM_256:
stats->qam256++;
return 8;
case QAM_1024:
stats->qam1024++;
return 10;
default:
stats->unknown++;
return 0;
}
}
void Neighbors::parse_hello(Packet *pkt, int port){
// Parse HELLO; create new NeighborLink if does not exist; else, update success proba
acne_header *hdr_acne = (acne_header *) pkt->data();
hello_pkt *h_pkt = (hello_pkt *)(pkt->data()+sizeof(acne_header));
EtherAddress pkt_src = h_pkt->src_addr;
EtherAddress pkt_plc_chip_addr = h_pkt->plc_chip_addr;
_now.assign_now();
if(_neighbors.get_pointer(pkt_src) != 0){
_neighbors[pkt_src].last_time_heard = _now;
//_neighbors[pkt_src].nr_hello_rcvd++;
String chip_str;
if(pkt_plc_chip_addr!=EtherAddress() && !is_in_hash_table(_plc_mac, pkt_plc_chip_addr)) {
_plc_mac.set(pkt_plc_chip_addr, pkt_src);
StringAccum s;
s << " (plc_chip address set to " << pkt_plc_chip_addr.unparse() << ")";
chip_str = s.take_string();
}
if(_debug || chip_str.length() > 0)
click_chatter("[Neighbors %s] Received a hello from %s%s",
_now.unparse().c_str(),pkt_src.unparse().c_str(), chip_str.c_str());
// if(hdr_acne->_type == HELLO_INT2_IP_UNICAST || hdr_acne->_type == HELLO_INT1_IP_UNICAST) { // update success proba based on unicast transmission
// if(_now >= _neighbors[pkt_src].last_update) // means that UPDATE_AFTER_SEC seconds have elapsed
// _neighbors[pkt_src].update_success_proba(h_pkt->seq_num, _debug);
// else
// _neighbors[pkt_src].last_seq_number = h_pkt->seq_num;
// }
// //store last bytes for quick mapping later:
// const unsigned char *data_addr = pkt_src.data();
// LastAddrBytes lastbytes;
// for(uint8_t b=0; b<NB_BYTES_HOP; ++b) {
// lastbytes._lastbytes[b] = data_addr[6-NB_BYTES_HOP+b];
// }
// lastbytes.set_hash();
// _lastbytes2neighbor[lastbytes] = pkt_src;
} else {
//Create a new NeighborLink
NeighborLink link;
if(_eth_addr1_present && _eth_addr2_present) link = NeighborLink(pkt_src, port, port == INTERFACE1 ? (!_ewma)*_alpha_ewma_int1 : (!_ewma)*_alpha_ewma_int2 );
else if(_eth_addr1_present) link = NeighborLink(pkt_src, INTERFACE1, _alpha_ewma_int1);
else if(_eth_addr2_present) link = NeighborLink(pkt_src, INTERFACE2, _alpha_ewma_int2);
link.last_update = _now+Timestamp(UPDATE_AFTER_SEC);
_neighbors[pkt_src] = link;
String chip_str;
if(pkt_plc_chip_addr!=EtherAddress()) {
_plc_mac.set(pkt_plc_chip_addr, pkt_src);
StringAccum s;
s << " (plc_chip address set to " << pkt_plc_chip_addr.unparse() << ")";
chip_str = s.take_string();
}
if(_debug)
click_chatter("[Neighbors %s] Received first hello from %s, add as a neighbor%s",
_now.unparse().c_str(),pkt_src.unparse().c_str(), chip_str.c_str());
//store last bytes for quick mapping later:
const unsigned char *data_addr = pkt_src.data();
LastAddrBytes lastbytes;
for(uint8_t b=0; b<NB_BYTES_HOP; ++b) {
lastbytes._lastbytes[b] = data_addr[6-NB_BYTES_HOP+b];
}
lastbytes.set_hash();
_lastbytes2neighbor[lastbytes] = pkt_src;
notifyListeners();
}
pkt->kill();
}
void Neighbors::parse_wifi_frame(const Packet *p){
if(_verb_debug)
click_chatter("[Neighbors]: parsing Wifi frame...");
uint8_t type;
uint8_t subtype;
uint8_t dir;
uint8_t retry;
EtherAddress src, dst, bssid;
struct click_wifi_extra *cwe = WIFI_EXTRA_ANNO(p); //radiotap header
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
//we need to declare things before jumping to done:
uint32_t rateD;
int8_t rssi;
uint8_t known_field;
//Timestamp now;
//int old_rate;
//float a = 1;
uint8_t bandwidth = 0;
uint8_t GI = 0;
uint8_t mcs_index = 0;
/* 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_addr1 || (_eth_addr2_present && !_addr2_is_plc && src == _eth_addr2)) ) goto done;
/*
* At this point we know this is a data packet sent by us.
* Check if we recognize the dst in our neighbors
* and update capacity
*/
rssi = cwe->rssi;
known_field = cwe->rate1;
rateD = 0;
//mcs_index present is 2nd bit of mcs_known field
//mcs_bandwidth present is the 1st bit of mcs_known field
//mcs_guard_interval present is the 3rd bit of mcs_known field
if(known_field & (1 << 1) && known_field & 1 && known_field & (1 << 2)){
bandwidth = cwe->rate2 & 3;
GI = cwe->rate2 & (1 << 2);
mcs_index = cwe->rate3;
if(mcs_index < NR_MCS){ //check that we don't try to fetch too large a MCS
if(bandwidth == 0 && GI == 0){
rateD = _mcs_to_rate_20_LG[mcs_index];
_current_mcs_array.set(dst, _mcs_to_rate_20_LG);
}
if(bandwidth == 0 && GI > 0){
rateD = _mcs_to_rate_20_SG[mcs_index];
_current_mcs_array.set(dst, _mcs_to_rate_20_SG);
}
if(bandwidth == 1 && GI == 0){
rateD = _mcs_to_rate_40_LG[mcs_index];
_current_mcs_array.set(dst, _mcs_to_rate_40_LG);
}
else if(bandwidth == 1 && GI > 0){
rateD = _mcs_to_rate_40_SG[mcs_index];
_current_mcs_array.set(dst, _mcs_to_rate_40_SG);
}
}
}
if (retry>0) {
_nb_retry[dst]++;
}
_nb_packets[dst]++;
if(_verb_debug) {
click_chatter("[Neighbors] Parsed wifi frame sent by me (%s) to dst %s at PHY rate %d (MCS %u) and RSSI %d. Retry = %d\n", src.unparse().c_str(),
dst.unparse().c_str(), rateD, mcs_index, rssi, 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());
}
if(rateD > 0) {
_nb_packets_rate[dst]++;
VERB_DEBUG_CHATTER("[Neighbors] Check hashtable");
if(_last_packets.get(dst).size() == 0) {
VERB_DEBUG_CHATTER("[Neighbors] Create first hashtable for dst %s", dst.unparse().c_str());
_last_packets.set(dst, Vector<int>(NR_MCS,0));
VERB_DEBUG_CHATTER("[Neighbors] Create second hashtable");
_retries_packets.set(dst, Vector<int>(NR_MCS,0));
VERB_DEBUG_CHATTER("[Neighbors] Hashtables created");
}
VERB_DEBUG_CHATTER("[Neighbors] Increment last_packets for dst %s", dst.unparse().c_str());
_last_packets[dst][mcs_index]++;
VERB_DEBUG_CHATTER("[Neighbors] Increment done, is now %d", _last_packets[dst][mcs_index]);
_total_packets[dst]++;
if(retry > 0) {
_retries_packets[dst][mcs_index]++;
_total_packets[dst]++;
}
}
done:
VERB_DEBUG_CHATTER("[Neighbors] Parsing done");
return;
}
void Neighbors::run_timer(Timer *timer){
uint32_t tailroom = 0;
uint32_t headroom = 100;
if(timer == &_hello_timer) {
if(_static && _now==Timestamp()) {
if(_debug)
click_chatter("[Neighbors] Notifying Neighbors for first time");
notifyListeners();
}
_now.assign_now();
//build a broadcast HELLO msg
uint32_t datalength = sizeof(acne_header) + sizeof(hello_pkt);
if(datalength < 70)
datalength = 70;
//send one HELLO on each interface
for(int i=0; i<_nr_interfaces; i++){
WritablePacket *pkt = Packet::make(headroom, 0, datalength, tailroom);
memset(pkt->data(), 0, pkt->length());
if(_debug)
click_chatter("[Neighbors] Create HELLO packet for interface %d", i);
//set header:
acne_header *header = (acne_header *)pkt->data();
hello_pkt *payload = (hello_pkt *)(pkt->data() + sizeof(acne_header));
if((i == 0 && _nr_interfaces == 2) || (_nr_interfaces == 1 && _eth_addr1_present)){
header->_type = HELLO_INT1;
memcpy(&(payload->src_addr), &_eth_addr1, sizeof(EtherAddress));
memset(&(payload->plc_chip_addr), 0, sizeof(EtherAddress));
if(_debug)
click_chatter("[Neighbors %s]: broadcasting HELLO_INT1...", _now.unparse().c_str());
if(_nr_interfaces == 2) output(1).push(pkt);
else output(0).push(pkt);
}
else if((i == 1 && _nr_interfaces == 2) || (_nr_interfaces == 1 && _eth_addr2_present)){
header->_type = HELLO_INT2;
memcpy(&(payload->src_addr), &_eth_addr2, sizeof(EtherAddress));
memcpy(&(payload->plc_chip_addr), &_plc_chip_addr, sizeof(EtherAddress));
if(_debug)
click_chatter("[Neighbors %s]: broadcasting HELLO_INT2...", _now.unparse().c_str());
output(0).push(pkt);
}
}
_last_seq_number = (_last_seq_number + 1) % MAX_SEQ_NUMBER_HELLO;
//print all neighbors:
if((_print_stat_freq > 0 && (_now - _last_time_print).sec() >= _print_stat_freq)){
_last_time_print = _now;
//TODO: change this with a Click Timestamp
//time_t now = time(0);
//tm *ltm = localtime(&now);
if(_csv_print) {
StringAccum s;
/*char buffer[2];
s << (ltm->tm_year+1900);
sprintf(buffer, "%02d", (ltm->tm_mon+1));
s << buffer;
sprintf(buffer, "%02d", ltm->tm_mday);
s << buffer;
sprintf(buffer, "%02d", ltm->tm_hour);
s << buffer;
sprintf(buffer, "%02d", ltm->tm_min);
s << buffer;
sprintf(buffer, "%02d", ltm->tm_sec);
s << buffer;*/
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
NeighborLink link = it.value();
s << ",";
s << link.eth_addr.unparse();
s << ",";
s << link.capa;
}
click_chatter(s.c_str());
}
else {
click_chatter(print_info().c_str());
}
}
if(_active) {
//Finally, check all neighbors for potential removal and update success probablity:
Vector<EtherAddress> toremove = Vector<EtherAddress>();
_now.assign_now();
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
if((_now - it.value().last_time_heard).sec() > _timeout_duration){
if(_debug) click_chatter("[Neighbors]: Neighbor (%s) has been silent for more than %d seconds. adding to remove list...", it.key().unparse().c_str(), _timeout_duration);
toremove.push_back(it.key());
}
// //update success proba on each link:
// _neighbors.get_pointer(it.key())->update_success_proba();
}
//if(_debug) click_chatter("[Neighbors]: I have %d neighbors to remove.", toremove.size());
for(int i=0; i<toremove.size(); ++i){
if(_debug) click_chatter("[Neighbors]: Removing neighbor (%s)", toremove[i].unparse().c_str());
_neighbors.erase(toremove[i]);
}
if(toremove.size() > 0){
notifyListeners();
}
}
//re-schedule timer:
_hello_timer.schedule_after_sec(_period_hello);
}
else if (timer == &_frame_timer) {
_now.assign_now();
if(!_active || !_send_empty_frames) {
_frame_timer.schedule_after_sec(10);
return;
}
// PLC cannot estimate more than 7 stations at a time; do by chunks
if(_eth_addr2_present && _addr2_is_plc && ((_now - _last_chunk_change).sec() > 2*TIME_PLC_CAPA || _last_chunk_change == Timestamp())) {
// if less than 6 PLC nodes in network, can do without chunks
if(_plc_addresses_static.size() <= 6) {
_all_neighbors_plc = true;
_begin_duration = 120;
}
else
_all_neighbors_plc = false;
if(!_all_neighbors_plc) {
switch (_plc_estimation_chunk_index) {
case 0:
// estimate own group
switch (_group_index) {
case 1:
_vector_current_plc_neighbors = _group_plc_neighbors[0];
break;
case 2:
_vector_current_plc_neighbors = _group_plc_neighbors[1];
break;
case 3:
_vector_current_plc_neighbors = _group_plc_neighbors[2];
break;
case 4:
_vector_current_plc_neighbors = _group_plc_neighbors[3];
break;
default:
click_chatter("[Neighbors %s] WARNING: unknown group index %d", _now.unparse().c_str(), _group_index);
break;
}
break;
case 1:
// estimate other group
switch (_group_index) {
case 1:
_vector_current_plc_neighbors = _group_plc_neighbors[1];
break;
case 2:
_vector_current_plc_neighbors = _group_plc_neighbors[0];
break;
case 3:
_vector_current_plc_neighbors = _group_plc_neighbors[3];
break;
case 4:
_vector_current_plc_neighbors = _group_plc_neighbors[2];
break;
default:
click_chatter("[Neighbors %s] WARNING: unknown group index %d", _now.unparse().c_str(), _group_index);
break;
}
break;
default:
click_chatter("[Neighbors %s] WARNING: unknown chunk index %d", _now.unparse().c_str(), _plc_estimation_chunk_index);
break;
}
if(_debug)
click_chatter("[Neighbors %s] Changing the PLC stations to estimate (%d in total), index is now %d (vector %s)", _now.unparse().c_str(),
_nb_plc_neighbors,_plc_estimation_chunk_index, print_vector(_vector_current_plc_neighbors).c_str());
_plc_estimation_chunk_index = (_plc_estimation_chunk_index+1) % 2;
}
_last_chunk_change = _now;
_estimated_plc_end_chunk = false;
// Vector<OrderedEtherAddress> plc_neighbors;
// _nb_plc_neighbors = 0;
// for(HashTable<EtherAddress, bool>::iterator it = _plc_addresses_static.begin(); it!=_plc_addresses_static.end(); ++it){
// insert_in_sorted_descent_vector(plc_neighbors, OrderedEtherAddress(it.key()));
// _nb_plc_neighbors++;
// }
// for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
// if(it.value().interface == INTERFACE2){
// _nb_plc_neighbors++;
// }
// }
// int nb_chunks = 1+_nb_plc_neighbors/7;
// _vector_current_plc_neighbors.clear();
// for (int i=(_plc_estimation_chunk_index*(1+(_nb_plc_neighbors/nb_chunks)));
// i < mymin((1+_plc_estimation_chunk_index)*(1+(_nb_plc_neighbors/nb_chunks)), _nb_plc_neighbors) ;i++) {
// _vector_current_plc_neighbors.push_back(plc_neighbors[i].eth);
// }
// click_chatter("[Neighbors %s] Changing the PLC stations to estimate (%d in total), index is now %d/%d (vector %s)", _now.unparse().c_str(),
// _nb_plc_neighbors,_plc_estimation_chunk_index, nb_chunks, print_vector(_vector_current_plc_neighbors).c_str());
// _plc_estimation_chunk_index = (_plc_estimation_chunk_index+1) % nb_chunks;
// //
// if(_nb_plc_neighbors > 14) {
// // TOFIX
// click_chatter("[Neighbors] WARNING: does not support capacity estimation with more than 14 neighbors for now (have %d)",
// _nb_plc_neighbors);
// }
// if (_nb_plc_neighbors > 7) {
// if(_plc_estimation_chunk_index == 1) {
// _vector_current_plc_neighbors.clear();
// for(int i=0;i<_nb_plc_neighbors/2;i++) {
// _vector_current_plc_neighbors.push_back(plc_neighbors[i].eth);
// }
// _plc_estimation_chunk_index = 0;
// }
// else {
// _vector_current_plc_neighbors.clear();
// for(int i=_nb_plc_neighbors/2;i<_nb_plc_neighbors;i++) {
// _vector_current_plc_neighbors.push_back(plc_neighbors[i].eth);
// }
// _plc_estimation_chunk_index = 1;
// }
// click_chatter("[Neighbors] Changing the PLC stations to estimate, index is now %d (vector %s)",
// _plc_estimation_chunk_index, print_vector(_vector_current_plc_neighbors).c_str());
// }
// else {
// _vector_current_plc_neighbors.clear();
// for(int i=0;i<_nb_plc_neighbors;i++)
// _vector_current_plc_neighbors.push_back(plc_neighbors[i].eth);
// }
// _last_chunk_change = _now;
}
if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Scheduling frame timer", _now.unparse().c_str());
}
bool old_pass_int2 = _pass_int2;
bool old_pass_int1= _pass_int1;
for(int k=0;k<=1;k++) {
uint8_t interface;
EtherAddress local_address;
uint32_t current_index;
if(k==0) {
if(!_eth_addr1_present) continue;
if(old_pass_int2 && !old_pass_int1) continue; // PLC has been passed at last timer but not WiFi, continue
interface = INTERFACE1;
local_address = _eth_addr1;
current_index = _current_int1_index;
_current_int1_index = (_current_int1_index % (INT_MAX-1)) + 1;
}
if(k==1) {
_now.assign_now();
if(!_eth_addr2_present) continue;
if(_addr2_is_plc && ((_now - _last_chunk_change).sec() > TIME_PLC_CAPA) && !_all_neighbors_plc) {
if (!_estimated_plc_end_chunk) {
send_mm_plc();
_estimated_plc_end_chunk = true;
}
continue;
}
if(!old_pass_int2 && old_pass_int1) continue; // WiFi has been passed at last timer but not PLC, continue
interface = INTERFACE2;
local_address = _eth_addr2;
current_index = _current_int2_index;
_current_int2_index = (_current_int2_index % (INT_MAX-1)) + 1;
}
int nb_neighbors = 0;
if(k==0 || !_addr2_is_plc || _all_neighbors_plc) {
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
if(it.value().interface == interface){
nb_neighbors++;
}
}
}
else
nb_neighbors = _vector_current_plc_neighbors.size();
if (nb_neighbors==0) {
DEBUG_CHATTER("[Neighbors] No neighbors, passing");
continue;
}
unsigned int i = 0;
bool done_empty_frame = false;
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
if(it.value().interface == interface){
bool pass = false;
// Send empty frames to one neighbor (number depends on specified load)
bool index_ok;
if(interface == INTERFACE1 || !_addr2_is_plc || _all_neighbors_plc)
index_ok = (i == (current_index % nb_neighbors));
else
index_ok = (it.key() == _vector_current_plc_neighbors[current_index % nb_neighbors]);
if (index_ok) {
_now.assign_now();
int div_packets = 1;
// if very low capa, send less packets, less often (sending packets is much more costly whereas link is not very useful)
if(it.value().capa > 0 && it.value().capa < 5000 || (it.value().capa==0 && (_now - _begin).sec() > _begin_duration)) {
if(click_random(0,4) != 0)
pass=true;
else
div_packets = 10;
}
else if(it.value().capa >= 5000 && it.value().capa <= 10000) {
if(click_random(0,2) != 0)
pass=true;
else
div_packets = 5;
}
else if(it.value().capa >= 10000 && it.value().capa <= 20000) {
div_packets = 2;
}
// compute number of packets to reach specified load per link _load_estimation
uint32_t nb_packets = _load_estimation*nb_neighbors*_period_frame_ms/(1000*8*_size_estimation_packet);
_now.assign_now();
if(k==1 && _addr2_is_plc) {
nb_packets*=3; // more for PLC because some are missed (stations that are not neighbors)
// need more packets for very good links (because they are aggregated)
if(it.value().capa >= 30000)
nb_packets*=3;
if(it.value().capa >= 50000)
nb_packets*=2;
}
if((_now - _begin).sec() <= _begin_duration) // during the first minutes, send more packets to get good estimation;
nb_packets = mymax(nb_packets,200u);
else
nb_packets/=div_packets;
nb_packets = mymax(10u,nb_packets);
if(!pass) {
DEBUG_CHATTER("[Neighbors %s]: for a load of %d bits/s, should send %d packets", _now.unparse().c_str(),
_load_estimation, nb_packets);
_neighbors[it.key()].traffic_sent = true;
if((_now - it.value().last_time_sent).sec() >= 1) { // send at most every second to a same neighbor
it.value().last_time_sent = _now;
uint32_t datalength = _size_estimation_packet; // Ethernet and IP
WritablePacket *pkt = Packet::make(Packet::default_headroom, 0, datalength, tailroom);
memset(pkt->data(), 0, pkt->length());
pkt->set_timestamp_anno(_now);
click_ether *eth_hdr = (click_ether *) pkt->data();
click_ip *ip = (click_ip *) (pkt->data() + sizeof(click_ether));
click_udp *udp = (click_udp *) (pkt->data() + sizeof(click_ether) + sizeof(click_ip));
// set Ethernet header
memcpy(eth_hdr->ether_dhost, it.key().data(), 6);
memcpy(eth_hdr->ether_shost, local_address.data(), 6);
eth_hdr->ether_type = htons(0x0800); // IP
pkt->set_mac_header((unsigned char *) eth_hdr, sizeof(click_ether));
memset(ip, 0, sizeof(click_ip)+sizeof(click_udp));
// IP header
ip->ip_v = 4;
ip->ip_hl = sizeof(click_ip) >> 2;
ip->ip_len = htons(pkt->length()-sizeof(click_ether));
ip->ip_p = IP_PROTO_UDP;
ip->ip_dst = _rl_elmt->get_ip(it.key()).in_addr();
ip->ip_src = _rl_elmt->get_ip(local_address).in_addr();
ip->ip_tos = 0;
ip->ip_off = 0;
ip->ip_ttl = 250;
#if HAVE_FAST_CHECKSUM
ip->ip_sum = ip_fast_csum((unsigned char *) ip, sizeof(click_ip) >> 2);
#else
ip->ip_sum = click_in_cksum((unsigned char *) ip, sizeof(click_ip));
#endif
pkt->set_ip_header(ip, sizeof(click_ip));
// set up UDP header
udp->uh_sport = htons(12345);
udp->uh_dport = htons(12345);
uint16_t len = pkt->length() - sizeof(click_ip) - sizeof(click_ether);
udp->uh_ulen = htons(len);
udp->uh_sum = 0;
unsigned csum = click_in_cksum((unsigned char *)udp, len);
udp->uh_sum = click_in_cksum_pseudohdr(csum, ip, len);
if(_debug) click_chatter("[Neighbors %s]: sending %d empty frames to %s (index %d, current index is %d)...",
_now.unparse().c_str(), nb_packets, it.key().unparse().c_str(), i, current_index);
for(int j=0;j<nb_packets;j++) {
Packet *p_send;
if(j==(nb_packets-1))
p_send = pkt;
else
p_send = pkt->clone();
if(k==0) {
if(_nr_interfaces == 2) output(2).push(p_send);
else output(1).push(p_send);
}
else if (k==1) {
if(_nr_interfaces == 2) output(3).push(p_send);
else output(1).push(p_send);
}
}
if(interface == INTERFACE1)
_pass_int1=false;
else
_pass_int2=false;
done_empty_frame = true;
}
}
else { // pass
if(interface == INTERFACE1)
_pass_int1=true;
else
_pass_int2=true;
DEBUG_CHATTER("[Neighbors %s] Passing frame sending for neighbor %s with capacity %d",
_now.unparse().c_str(), it.key().unparse().c_str(), it.value().capa);
done_empty_frame = true;
}
}
if (done_empty_frame)
break;
i++;
}
}
}
if(!_pass_int2 && !_pass_int1) {
if((_now - _begin).sec() <= _begin_duration)
_frame_timer.reschedule_after_msec(click_random(2*_period_frame_ms,3*_period_frame_ms)); // more traffic sent at beginning, so wait more
else
_frame_timer.reschedule_after_msec(click_random(_period_frame_ms,2*_period_frame_ms));
}
else //_pass_int2 || _pass_int1
_frame_timer.schedule_now(); // reschedule because no frame has been sent
if(_debug) {
_now.assign_now();
// if(_pass_int2 || _pass_int1)
// click_chatter("[Neighbors %s] Rescheduling now (passed), expire at %s", _now.unparse().c_str(),
// _frame_timer.expiry().unparse().c_str());
// else
click_chatter("[Neighbors %s] Rescheduling after %d ms, expire at %s", _now.unparse().c_str(),
_period_frame_ms, _frame_timer.expiry().unparse().c_str());
}
}
if(timer == &_capacity_estimation_timer) {
// if(!_active) {
// _capacity_estimation_timer.schedule_after_sec(30);
// return;
// }
if(_eth_addr2_present && _addr2_is_plc) {
//_number_estimations++;
//if(_number_estimations >= 5) { // update PLC less often than WiFi (all links are estimated)
//_number_estimations = 0;
send_mm_plc();
//}
}
if(_eth_addr1_present || (_eth_addr2_present && !_addr2_is_plc)) {
/*
int nb_wifi_neighbors = 0;
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
if(it.value().interface == INTERFACE1){
nb_wifi_neighbors++;
}
}
unsigned int i = 0;
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
if(it.value().interface == INTERFACE1){
// Estimate capacity for one neighbor
if (i == (_current_int1_index_estimation % nb_wifi_neighbors)){
it.value().update_wifi(capacity_estimation_wifi(it.key()));
break;
}
i++;
}
}
_current_int1_index_estimation = (_current_int1_index_estimation+1)%INT_MAX;
*/
capacity_estimation_wifi();
}
_capacity_estimation_timer.reschedule_after_sec(_period_capacity_estimation);
}
}
void Neighbors::request_tone_maps(EtherAddress dst){
//create new MMType 0x6048 NW_STATS_REQ
const unsigned char dst_msg[6] = {0x00, 0xB0, 0x52, 0x00, 0x00, 0x01};
const unsigned char oui[3] ={0x00, 0xB0, 0x52};
//static_assert(Packet::default_headroom >= sizeof(click_ether));
WritablePacket *q = Packet::make(100, NULL, sizeof(click_ether) + sizeof(click_hp_av_tone_map_req), 0);
if (!q) {
click_chatter("[Neighbors] cannot create MMType packet.");
return;
}
click_ether *e = (click_ether *) q->data();
q->set_ether_header(e);
memset(e->ether_shost, 0x00, 6);
memcpy(e->ether_dhost, dst_msg,6);
e->ether_type = htons(ETHERTYPE_HP_AV);
click_hp_av_tone_map_req* tm_req = (click_hp_av_tone_map_req *) (e + 1);
tm_req->version = 0;
tm_req->MMType = htons(TONE_MAP_REQ);
// Copy Request Body
memcpy(tm_req->macaddr,&dst,sizeof(EtherAddress));
tm_req->tmslot = 1;
memcpy(tm_req->oui,oui,3);
q->timestamp_anno().assign_now();
if(_debug)
click_chatter("[Neighbors] Sent TONE MAP Request for neighbor %s", dst.unparse().c_str());
if(_nr_interfaces == 2) output(3).push(q);
else output(1).push(q);
}
EtherAddress
Neighbors::get_eth_addr_from_last_bytes(unsigned char *data){
//build key to find neighbor. this should go as fast as possible
LastAddrBytes lastbytes;
//if(_debug)
// click_chatter("[Neighbors] rcvd in argument: 1st byte = %02X, 2nd byte = %02X", data[0], data[1]);
//fairly dangerous, data needs to really have NB_BYTES_HOP bytes.
for(uint8_t iter=0; iter<NB_BYTES_HOP; ++iter) {
lastbytes._lastbytes[iter] = data[iter];
}
lastbytes.set_hash();
EtherAddress *addr = _lastbytes2neighbor.get_pointer(lastbytes);
if(addr != 0) {
return *addr;
}
else {
if(_debug)
click_chatter("[Neighbors] No address found for %s", lastbytes.unparse().c_str());
return _empty_eth;
}
}
/* for the PLC interface, we sent a management message to the interface in order to get capacity estimation
*/
void
Neighbors::send_mm_plc()
{
const unsigned char dst[6] = {0x00, 0xB0, 0x52, 0x00, 0x00, 0x01};
//static_assert(Packet::default_headroom >= sizeof(click_ether));
WritablePacket *q = Packet::make(Packet::default_headroom, NULL, sizeof(click_ether) + sizeof(click_hp_av_header), 0);
if (!q) {
click_chatter("[Neighbors] Cannot make packet for MM.");
return;
}
if(_debug)
_now.assign_now();
DEBUG_CHATTER("[Neighbors %s] Sending PLC request for stats.", _now.unparse().c_str());
click_ether *e = (click_ether *) q->data();
q->set_ether_header(e);
memset(e->ether_shost, 0x00, 6);
memcpy(e->ether_dhost, dst,6);
e->ether_type = htons(ETHERTYPE_HP_AV);
click_hp_av_header *hpavh = (click_hp_av_header *) (e + 1);
hpavh->version = 1;
hpavh->MMType = htons(NW_STATS_REQ);
hpavh->fmi = 0;
//q->timestamp_anno().assign_now();
if(_nr_interfaces == 2)
output(3).push(q);
else
output(1).push(q);
}
// We received a reply from the PLC interface
void
Neighbors::rec_mm_plc(Packet *p)
{
// process the incoming HP_AV Management message
click_ether *ethh = (click_ether *) p->data();
EtherAddress src;
EtherAddress dst;
memcpy(dst.data(), ethh->ether_dhost, 6);
memcpy(src.data(), ethh->ether_shost, 6);
click_hp_av_header *hpavh = (click_hp_av_header *) (ethh + 1);
//click_chatter("%u type ",hpavh->MMType);
click_hp_av_nw_stats_conf *nwstats = (click_hp_av_nw_stats_conf *)(hpavh + 1);
int txstats;
uint32_t capacity = 0;
int num_STAs = (int) nwstats->sta.NumSTAs;
_now.assign_now();
DEBUG_CHATTER("[Neighbors %s] Num of stas %d (eth header: src %s, dst %s)", _now.unparse().c_str(), num_STAs, src.unparse().c_str(), dst.unparse().c_str());
_plc_chip_addr = src;
_is_reliable_int2 = true;
int count_stations=0;
for (int i = 0; i < num_STAs ; i++){
EtherAddress station = EtherAddress(nwstats->sta.infos[i].DA);
txstats = nwstats->sta.infos[i].AvgPHYDR_TX;
capacity = (uint32_t) (1024*txstats);
DEBUG_CHATTER("[Neighbors] MAC address: %s, Avg rate from STA to DA: %d", station.unparse().c_str(), txstats);
NeighborLink *link = _neighbors.get_pointer(_plc_mac.get(station));
if(link != 0 && txstats != 0 && txstats != 9) { // 9 is default capacity
if((_now - _begin).sec() <= _begin_duration) // at start, no EWMA
link->reset_capa();
link->update_plc(capacity,_alpha_ewma_plc);
count_stations++;
}
if (link!=0) {
if(!link->traffic_sent)
_is_reliable_int2 = false;
}
//_availt->update_measurements(_plc_mac.get(station), capacity);
}
if(_debug) {
Timestamp now = Timestamp::now();
click_chatter("[Neighbors %s] Capacity estimated for %d stations (is reliable %d) in %s seconds.", now.unparse().c_str(),
count_stations, _is_reliable_int2, (now-_now).unparse().c_str());
}
}
void
Neighbors::capacity_estimation_wifi() {
_is_reliable_int1 = true;
if(_eth_addr2_present && !_addr2_is_plc)
_is_reliable_int2 = true;
_now.assign_now();
DEBUG_CHATTER("[Neighbors %s] Estimating Wifi capacity for %d stations.", _now.unparse().c_str(), _last_packets.size());
if(_capacity_tonemap_moni) {
int count_stations=0;
VERB_DEBUG_CHATTER("[Neighbors] Hashtable");
for(HashTable<EtherAddress, Vector<int> >::const_iterator it = _last_packets.begin(); it!=_last_packets.end(); ++it) {
VERB_DEBUG_CHATTER("[Neighbors] Link");
VERB_DEBUG_CHATTER("[Neighbors] Link %s", it.key().unparse().c_str());
NeighborLink *link = _neighbors.get_pointer(it.key());
VERB_DEBUG_CHATTER("[Neighbors] NeighborLink");
VERB_DEBUG_CHATTER("[Neighbors] NeighborLink %p", link);
if(link!=0 && _total_packets[it.key()] > 10 && _retries_packets[it.key()].size() > 0 &&
_current_mcs_array[it.key()].size() > 0) {
uint32_t final_throughput = 0;
VERB_DEBUG_CHATTER("[Neighbors] All MCS");
for(int i=0;i<NR_MCS;i++) {
if((it.value()[i]+_retries_packets[it.key()][i]) > 0) {
// to avoid overflow (might happen when sending high rate traffic), compute in uint64
uint64_t nb_packets64 = (uint64_t) it.value()[i];
// int_divide(uint64,uint32) = uint64 (see integers.hh)
final_throughput += (uint32_t) int_divide((nb_packets64+((uint64_t)_retries_packets[it.key()][i]))*
int_divide(nb_packets64*((uint64_t)_current_mcs_array[it.key()][i]),it.value()[i]+_retries_packets[it.key()][i]),_total_packets[it.key()]);
}
}
if(_ewma && (_now - _begin).sec() <= _begin_duration) { // at start, no EWMA
VERB_DEBUG_CHATTER("[Neighbors] Reset capa");
link->reset_capa();
}
VERB_DEBUG_CHATTER("[Neighbors] Update capa with %u", final_throughput);
link->update_wifi(final_throughput, _alpha_ewma_wifi);
if(_debug) {
click_chatter("[Neighbors] Capacity for neighbor %s estimated at %d with %d packets (packets: %s, retries %s).",
it.key().unparse().c_str(), final_throughput, _total_packets[it.key()], print_sparse_vector(it.value()).c_str(),
print_sparse_vector(_retries_packets[it.key()]).c_str());
_total_packets[it.key()] = 0;
_last_packets[it.key()] = Vector<int>(NR_MCS,0);
_retries_packets[it.key()] = Vector<int>(NR_MCS,0);
count_stations++;
}
}
else if(_debug) {
click_chatter("[Neighbors] Capacity for neighbor %s has not been updated (only %d packets)",
it.key().unparse().c_str(), _total_packets[it.key()]);
}
if (link!=0) {
if(!link->traffic_sent) {
if(link->interface == INTERFACE1)
_is_reliable_int1 = false;
else if(link->interface == INTERFACE1)
_is_reliable_int2 = false;
}
}
}
if(_debug) {
Timestamp now = Timestamp::now();
click_chatter("[Neighbors %s] Capacity estimated for %d stations (is reliable %d) in %s seconds.", now.unparse().c_str(),
count_stations, _is_reliable_int1, (now-_now).unparse().c_str());
}
}
else {
Timestamp begin;
if(_debug)
begin.assign_now();
Vector<uint32_t> throughput_mcs = Vector<uint32_t>(NR_MCS,0);
Vector<int> packets_mcs = Vector<int>(NR_MCS,0);
Vector<int> last_packets_mcs = Vector<int>(NR_MCS,0);
String out_command = exec("tail -n 20 /sys/kernel/debug/ieee80211/phy0/netdev:wlan0/stations/*/rc_stats");
Timestamp end_exec;
if(_debug)
end_exec.assign_now();
EtherAddress eth;
int mcs_idx = -1;
int count_idx = 1000;
int total_packets = 0;
int ewma;
int ind_end_line;
while((ind_end_line = out_command.find_left('\n')) >= 0) {
String line = out_command.substring(0,ind_end_line);
VERB_DEBUG_CHATTER("[Neighbors] Parsing line %s", line.c_str());
out_command = out_command.substring(ind_end_line+1);
if(line.find_left("==>") >= 0) { // new ethernet address
if(eth!=EtherAddress() && is_in_hash_table(_neighbors, eth)) {
uint32_t final_throughput = 0;
if(total_packets > 10) {
for(int i=0;i<NR_MCS;i++) {
final_throughput += int_divide(packets_mcs[i]*throughput_mcs[i],total_packets); // kBits/s
_last_packets[eth][i] = last_packets_mcs[i];
}
_neighbors[eth].update_wifi(final_throughput, _alpha_ewma_wifi);
if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Capacity for neighbor %s estimated at %d with %d packets (took %s sec to compute, %s for exec)",
_now.unparse().c_str(), eth.unparse().c_str(), final_throughput,
total_packets, (_now-begin).unparse().c_str(), (end_exec - begin).unparse().c_str());
}
}
else if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Capacity for neighbor %s has not been updated (only %d packets) (took %s sec to compute, %s for exec)",
_now.unparse().c_str(), eth.unparse().c_str(), total_packets, (_now-begin).unparse().c_str(), (end_exec - begin).unparse().c_str());
}
}
int rc_ind = line.find_left("rc_stats");
if(rc_ind == -1)
click_chatter("[Neighbors] Could not get rc_stats index for string %s", line.c_str());
else {
if(!cp_ethernet_address(line.substring(rc_ind-18,17).c_str(), &eth)) {
click_chatter("[Neighbors] Could not get EtherAddress from string %s", line.substring(rc_ind-18,17).c_str());
eth = EtherAddress();
}
else {
if(!is_in_hash_table(_last_packets, eth))
_last_packets.set(eth, Vector<int>(NR_MCS, -1));
}
}
throughput_mcs = Vector<uint32_t>(NR_MCS,0);
packets_mcs = Vector<int>(NR_MCS,0);
last_packets_mcs = Vector<int>(NR_MCS,0);
mcs_idx = -1;
total_packets = 0;
continue;
}
if (line.find_left("HT20/LGI") == -1)
continue;
ewma = -1;
mcs_idx++;
int found = line.find_left("MCS");
// parse line
if (found == -1)
continue;
count_idx = 0;
int ind_space;
int ind_t;
ind_space = line.find_left(' ', found);
ind_t = line.find_left('\t', found);
if(ind_space == -1 || (ind_t!=-1 && ind_t<ind_space))
ind_space = ind_t;
if(ind_space == -1)
continue;
line = line.substring(ind_space);
while(line.length() > 0) {
String sub;
if(line[0] == ' ' || line[0] == '\t') {
line = line.substring(1);
continue;
}
else {
ind_space = line.find_left(' ');
ind_t = line.find_left('\t');
if(ind_space == -1 || (ind_t!=-1 && ind_t<ind_space))
ind_space = ind_t;
if(ind_space == -1) { // no space or alinea, sub is all line
sub = line;
line = "";
}
else {
sub = line.substring(0,ind_space);
line = line.substring(ind_space);
}
count_idx++;
VERB_DEBUG_CHATTER("[Neighbors] New word %s, index %d, line (mcs %d) is now %s", sub.c_str(), count_idx, mcs_idx, line.c_str());
}
if (count_idx == 2) {
//emwa prob
if(mcs_idx < NR_MCS) {
sub = sub.substring(0,sub.find_left('.'));
if(!cp_integer(sub, &ewma)) {
click_chatter("[Neighbors] WARNING: could not parse ewma %s", sub.c_str());
break;
}
throughput_mcs[mcs_idx] = (_mcs_to_rate_20_LG[mcs_idx]*ewma)/100;
VERB_DEBUG_CHATTER("[Neighbors] Ewma is %d, throughput for MCS %d is %d", ewma, mcs_idx, throughput_mcs[mcs_idx]);
}
//cout << "MCS" << mcs_idx-1 << " "<< throughput_mcs[mcs_idx-1]<< " ";
//cout << "string: " << sub << endl;;
continue;
}
else if (count_idx == 8 && mcs_idx < NR_MCS) {
if(eth==EtherAddress()) {
click_chatter("[Neighbors] Has no EtherAddress, continue.");
continue;
}
int nb_packets;
if(!cp_integer(sub, &nb_packets)) {
click_chatter("[Neighbors] WARNING: could not parse number of packets %s", sub.c_str());
break;
}
if(_last_packets[eth][mcs_idx] != -1) {
if(nb_packets >= _last_packets[eth][mcs_idx]) {
packets_mcs[mcs_idx] = nb_packets - _last_packets[eth][mcs_idx];
total_packets += packets_mcs[mcs_idx];
last_packets_mcs[mcs_idx] = nb_packets;
VERB_DEBUG_CHATTER("[Neighbors] For eth %s, MCS %d, has %d packets (was %d)", eth.unparse().c_str(), mcs_idx, nb_packets, _last_packets[eth][mcs_idx]);
}
else {
_now.assign_now();
uint32_t capa = 0;
if(is_in_hash_table(_neighbors, eth))
capa = _neighbors[eth].capa;
click_chatter("[Neighbors %s] WARNING: for %s (capa %d), mcs index %d, nb_packets=%d, last_packets=%d", _now.unparse().c_str(),
eth.unparse().c_str(), capa, mcs_idx, nb_packets, _last_packets[eth][mcs_idx]);
}
}
else {
DEBUG_CHATTER("[Neighbors] Initialization of last_packets for %s, mcs %d, is now %d", eth.unparse().c_str(),
mcs_idx, nb_packets);
_last_packets[eth][mcs_idx] = nb_packets;
last_packets_mcs[mcs_idx] = nb_packets;
}
/*
DEBUG_CHATTER("[Neighbors] Throughput for %s, MCS %d is %f (ewma %f) with %d packets (%d, was %d)",
eth.unparse().c_str(), mcs_idx, throughput_mcs[mcs_idx], ewma,
packets_mcs[mcs_idx], nb_packets, _last_packets[eth][mcs_idx]);
*/
break;
}
}
}
// final neighbor
if(eth!=EtherAddress()) {
uint32_t final_throughput = 0;
if(total_packets > 10) {
for(int i=0;i<NR_MCS;i++) {
final_throughput += int_divide(packets_mcs[i]*throughput_mcs[i],total_packets); // kBits/s
_last_packets[eth][i] = last_packets_mcs[i];
}
if(is_in_hash_table(_neighbors, eth)) {
_neighbors[eth].update_wifi(final_throughput, _alpha_ewma_wifi);
}
if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Capacity for neighbor %s estimated at %d with %d packets (took %s sec to compute, %s for exec)",
_now.unparse().c_str(), eth.unparse().c_str(), final_throughput,
total_packets, (_now-begin).unparse().c_str(), (end_exec - begin).unparse().c_str());
}
}
else if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Capacity for neighbor %s has not been updated (only %d packets) (took %s sec to compute, %s for exec)",
_now.unparse().c_str(), eth.unparse().c_str(), total_packets, (_now-begin).unparse().c_str(), (end_exec - begin).unparse().c_str());
}
}
}
}
/*
// Estimate capacity for WIFI
uint32_t Neighbors::capacity_estimation_wifi(EtherAddress eth)
{
if(!is_in_hash_table(_last_packets, eth))
_last_packets.set(eth, Vector<int>(NR_MCS, -1));
Vector<double> throughput_mcs = Vector<double>(NR_MCS,0);
Vector<int> packets_mcs = Vector<int>(NR_MCS,0);
Timestamp begin;
if(_debug)
begin.assign_now();
// MAC address
std::string bss1 = eth.unparse().c_str();
std::transform(bss1.begin(), bss1.end(), bss1.begin(), ::tolower);
int count = 0;
std::string cmd_name = "cat /sys/kernel/debug/ieee80211/phy0/netdev:wlan0/stations/";
String out_command;
std::string Table;
// read rc_stats file
int mcs_idx = -1;
int count_idx = 1000;
double final_throughput = 0;
int total_packets = 0;
while (count < bss1.length()) {
if(count%3==2) {
bss1.replace(bss1.begin()+count,bss1.begin()+count+1,":");
}
count++;
}
cmd_name.append(bss1);
cmd_name.append("/rc_stats");
char *cstr = &cmd_name[0u];
// exec commad
Timestamp begin_exec;
if(_debug)
begin_exec.assign_now();
out_command = exec(cstr);
Timestamp end_exec;
if(_debug)
end_exec.assign_now();
Table.assign(out_command.c_str());
std::istringstream iss(Table);
double ewma;
while(iss) {
std::string sub;
iss >> sub;
size_t found = sub.find("MCS");
if (sub.compare("HT20/LGI") == 0) {
//cout << "MCS "<< mcs_idx << " ";
ewma = -1;
mcs_idx++;
continue;
}
else {
if (found != std::string::npos) {
count_idx = 0;
continue;
}
else {
count_idx++;
}
if (count_idx == 2) {
//emwa prob
if(mcs_idx < NR_MCS) {
ewma = atof(sub.c_str());
throughput_mcs[mcs_idx] = _mcs_to_rate_20_LG[mcs_idx]*(ewma/double(100));
}
//cout << "MCS" << mcs_idx-1 << " "<< throughput_mcs[mcs_idx-1]<< " ";
//cout << "string: " << sub << endl;;
continue;
}
else if (count_idx == 8 && mcs_idx < NR_MCS) {
int nb_packets = atof(sub.c_str());
if(_last_packets[eth][mcs_idx] != -1 && nb_packets >= _last_packets[eth][mcs_idx]) {
packets_mcs[mcs_idx] = nb_packets - _last_packets[eth][mcs_idx];
total_packets += packets_mcs[mcs_idx];
}
DEBUG_CHATTER("[Neighbors] Throughput for %s, MCS %d is %f (ewma %f) with %d packets (%d, was %d)",
eth.unparse().c_str(), mcs_idx, throughput_mcs[mcs_idx], ewma,
packets_mcs[mcs_idx], nb_packets, _last_packets[eth][mcs_idx]);
_last_packets[eth][mcs_idx] = nb_packets;
}
}
}
// compute capacity
if(total_packets > 0) {
for(int i=0;i<NR_MCS;i++) {
double weight = double(packets_mcs[i])/total_packets;
final_throughput += weight*throughput_mcs[i]; // kBits/s
}
}
if(_debug) {
_now.assign_now();
click_chatter("[Neighbors %s] Capacity for neighbor %s estimated at %d with %d packets (took %s sec to compute, %s for exec)",
_now.unparse().c_str(), eth.unparse().c_str(), (int) final_throughput,
total_packets, (_now-begin).unparse().c_str(), (end_exec - begin_exec).unparse().c_str());
}
return (uint32_t) final_throughput;
}
*/
IPAddress Neighbors::my_ip() {
if(_rl_elmt)
return _rl_elmt->my_ip();
return IPAddress();
}
IPAddress Neighbors::get_ip(unsigned char *addr) {
if(_rl_elmt)
return _rl_elmt->get_ip(addr);
return IPAddress();
}
IPAddress Neighbors::get_ip(EtherAddress addr) {
if(_rl_elmt)
return _rl_elmt->get_ip(addr);
return IPAddress();
}
////////// DEFINE EXTERNAL FUNCTIONS DECLARED IN UTIL.H ///////////////
HashTable<EtherAddress, bool> _wifi_addresses_static = HashTable<EtherAddress,bool>();
void set_wifi_addr(EtherAddress addr) {
if(!is_in_hash_table(_wifi_addresses_static, addr)) {
_wifi_addresses_static.set(addr, true);
click_chatter("Adding %s as WIFI address (have now %d addresses)", addr.unparse().c_str(),
_wifi_addresses_static.size());
}
}
bool is_wifi_addr(EtherAddress addr) {
return is_in_hash_table(_wifi_addresses_static, addr);
}
HashTable<EtherAddress, bool> _plc_addresses_static = HashTable<EtherAddress,bool>();
void set_plc_addr(EtherAddress addr) {
if(!is_in_hash_table(_plc_addresses_static, addr)) {
_plc_addresses_static.set(addr, true);
click_chatter("Adding %s as PLC address (have now %d addresses)", addr.unparse().c_str(),
_plc_addresses_static.size());
}
}
bool is_plc_addr(EtherAddress addr) {
return is_in_hash_table(_plc_addresses_static, addr);
}
////////////////////////////////////////////////////////////////////
int Neighbors::get_interface_to_neighbor(EtherAddress neighbor){
NeighborLink *link = _neighbors.get_pointer(neighbor);
if(link != 0){
return (int)(link->interface);
}
return -1;
}
String Neighbors::print_info() {
StringAccum s;
_now.assign_now();
s << "[Neighbors " << _now.unparse() << "] I have " << _neighbors.size() << " neighbor(s):" << "\n";
for(LinksTable::iterator it = _neighbors.begin(); it!=_neighbors.end(); ++it){
NeighborLink link = it.value();
if(is_plc_addr(link.eth_addr))
s << "[Neighbors]\t (" << link.eth_addr.unparse() << "): duration=" << (_now - link.last_time_heard).sec() <<
", interface=PLC, capa=" << link.capa << "\n";
else if(is_wifi_addr(link.eth_addr)) {
// if (!is_in_hash_table(_nb_retry, link.eth_addr)) {
// _nb_retry.set(link.eth_addr, 0);
// _nb_packets.set(link.eth_addr, 0);
// }
s << "[Neighbors]\t (" << link.eth_addr.unparse() << "): duration=" << (_now - link.last_time_heard).sec() <<
", interface=WIFI, capa=" << link.capa;
s << " (" << _nb_retry[link.eth_addr] << " retries out of " << _nb_packets[link.eth_addr] << " packets, ";
s << _nb_packets_rate[link.eth_addr] <<" with rate > 0)\n";
}
}
return s.take_string();
}
static String
print_handler(Element *e, void *param) {
Neighbors *nei = (Neighbors *)e;
return nei->print_info();
}
int
Neighbors::debug_handler(const String &s, Element *e, void *a,
ErrorHandler *errh) {
Neighbors *elmt = (Neighbors *)e;
int debug;
if(!cp_integer(s, &debug))
return errh->error("Debug must be 0 or 1");
if (!(debug == 0 || debug == 1))
return errh->error("Debug must be 0 or 1");
if(((intptr_t) a) == 0)
elmt->set_debug(debug==1);
else if(((intptr_t) a) == 1)
elmt->set_verb_debug(debug==1);
return 0;
}
int
Neighbors::active_handler(const String &s, Element *e, void *a,
ErrorHandler *errh) {
Neighbors *elmt = (Neighbors *)e;
int arg;
if(!cp_integer(s, &arg))
return errh->error("Arg must be 0 or 1");
if (!(arg == 0 || arg == 1))
return errh->error("Arg must be 0 or 1");
if(((intptr_t) a) == 0)
elmt->set_active(arg==1);
else if(((intptr_t) a) == 1)
elmt->set_send_empty_frames(arg==1);
return 0;
}
int
Neighbors::int_handler(const String &s, Element *e, void *a,
ErrorHandler *errh) {
Neighbors *elmt = (Neighbors *)e;
int arg;
if(!cp_integer(s, &arg))
return errh->error("Must be an integer");
if(arg < 0)
return errh->error("Must be positive");
if(((intptr_t) a) == 0)
elmt->set_load((uint32_t)arg);
if(((intptr_t) a) == 1)
elmt->set_alpha_ewma_plc((int)arg);
if(((intptr_t) a) == 2)
elmt->set_alpha_ewma_wifi((int)arg);
return 0;
}
int
Neighbors::reset_handler(const String &, Element *e, void *, ErrorHandler *) {
Neighbors *elmt = (Neighbors *)e;
elmt->reinitialize_counters();
return 0;
}
int
Neighbors::print_updates_handler(const String &s, Element *e, void *,
ErrorHandler *errh) {
Neighbors *elmt = (Neighbors *)e;
int arg;
if(!cp_integer(s, &arg))
return errh->error("Print_updates must be 0 or 1");
if (!(arg == 0 || arg == 1))
return errh->error("Print_updates must be 0 or 1");
elmt->set_print_updates(arg==1);
return 0;
}
void Neighbors::add_handlers() {
add_write_handler("debug", debug_handler, 0);
add_write_handler("verb_debug", debug_handler, 1);
add_write_handler("active", active_handler, 0);
add_write_handler("send_empty_frames", active_handler, 1);
add_write_handler("reset", reset_handler, 0);
add_write_handler("load_estimation_per_link", int_handler, 0);
add_write_handler("alpha_ewma_plc", int_handler, 1);
add_write_handler("alpha_ewma_wifi", int_handler, 2);
add_write_handler("print_updates", print_updates_handler, 0);
add_read_handler("print_info", print_handler,0);
}
CLICK_ENDDECLS
EXPORT_ELEMENT(Neighbors)
ELEMENT_PROVIDES(Neighbors)

Event Timeline