📄 spantree.cc
字号:
/* * spantree.{cc,hh} -- element implementing IEEE 802.1d protocol for * Ethernet bridges * John Jannotti * * 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 "spantree.hh"#include <click/confparse.hh>#include "elements/standard/suppressor.hh"#include <click/error.hh>CLICK_DECLSEtherSpanTree::EtherSpanTree() : _input_sup(0), _output_sup(0), _topology_change(0), _bridge_priority(0xdead), // Make it very unlikely to be root _long_cache_timeout(5*60), _hello_timer(hello_hook, this){}EtherSpanTree::~EtherSpanTree(){}voidEtherSpanTree::notify_ninputs(int n) { set_ninputs(n); set_noutputs(n); // the rest is redundant with notify_noutputs below _port.resize(n);}intEtherSpanTree::configure(Vector<String> &conf, ErrorHandler *errh){ Element* in; Element* out; Element* sw; if (cp_va_parse(conf, this, errh, cpEthernetAddress, "bridge address", _addr, cpElement, "Suppressor to control input", &in, cpElement, "Suppressor to control output", &out, cpElement, "EtherSwitch", &sw, cpEnd) < 0) return -1; if (!in->cast("Suppressor")) return errh->error("EtherSpanTree needs an input Suppressor"); if (!out->cast("Suppressor")) return errh->error("EtherSpanTree needs an output Suppressor"); if (!sw->cast("EtherSwitch")) return errh->error("EtherSpanTree needs an EtherSwitch"); _input_sup = (Suppressor*)in; _output_sup = (Suppressor*)out; _switch = (EtherSwitch*)sw; memcpy(&_bridge_id, _addr, 6); return 0;}intEtherSpanTree::initialize(ErrorHandler *){ for (int i = 0; i < _port.size(); i++) { set_state(i, FORWARD); } _best.reset(((uint64_t)_bridge_priority << 48) | _bridge_id); _hello_timer.initialize(this); _hello_timer.schedule_after_ms(_best._hello_time * 1000); return 0;}StringEtherSpanTree::read_msgs(Element* f, void *) { EtherSpanTree* sw = (EtherSpanTree*)f; String s; for (int i = 0; i < sw->_port.size(); i++) { s += sw->_port[i].msg.s() + "\n"; } s += sw->_best.s() + "\n"; return s;}voidEtherSpanTree::add_handlers(){ add_read_handler("msgs", read_msgs, 0);}voidEtherSpanTree::periodic() { // Push LISTEN and LEARN ports forward. Timestamp cutoff = Timestamp::now(); cutoff._sec -= _best._forward_delay; for (int i = 0; i < _port.size(); i++) { if (_port[i].state == LISTEN || _port[i].state == LEARN) if (_port[i].since < cutoff) { set_state(i, FORWARD); } } expire(); find_tree();}boolEtherSpanTree::expire() { Timestamp t = Timestamp::now(); t._sec -= _best._max_age; bool expired = false; for (int i = 0; i < _port.size(); i++) { if (_port[i].msg.expire(t)) { expired = true; click_chatter("Expiring message on port %d", i); } } return expired;}voidEtherSpanTree::find_tree() { // First, determine _best, which will either be the bridge's own // message or the best message received on one of its ports. int root_port = -1; uint64_t my_id = ((uint64_t)_bridge_priority << 48) | _bridge_id; _best.reset(my_id); for (int i = 0; i < _port.size(); i++) { // Temporarily inc cost _port[i].msg._cost++; // Make configurable JJJ if (_best.compare(&_port[i].msg) < 0) { _best = _port[i].msg; root_port = i; } _port[i].msg._cost--; // Make configurable JJJ } for (int i = 0; i < _port.size(); i++) { // The tree includes the port on which _best came in, and ports on // which we are the designated bridge. if (i == root_port) { if (_port[i].state == BLOCK) set_state(i, FORWARD); continue; } // We are the designated bridge on a given port if we could spit // out a message on that port that is better than the one we have // received. int cmp = _best.compare(&_port[i].msg, my_id, i); if (cmp < 0) // (_best.compare(&_port[i].msg, i) < 0) set_state(i, BLOCK); else if (_port[i].state == BLOCK) set_state(i, FORWARD); }}boolEtherSpanTree::set_state(int i, PortState state){ assert(state != LISTEN); assert(state != LEARN); if (_port[i].state == state) return false; if (state == FORWARD) { if (_port[i].state == BLOCK) { click_chatter("Setting send_tc_msg: BLOCK -> FORWARD on %d", i); _send_tc_msg = true; } // Can't go there directly, just increment state = (PortState)(_port[i].state+1); } else { // Since we already checked that _port[i].state != state, this // must be a topology change. click_chatter("Setting send_tc_msg: FORWARD -> BLOCK on %d", i); _send_tc_msg = true; } click_chatter("Changing port %d from %d to %d", i, _port[i].state, state); _port[i].state = state; _port[i].since = Timestamp::now(); switch (state) { case BLOCK: case LISTEN: // LISTEN == BLOCK except it timeouts to LEARN _input_sup->suppress(i); _output_sup->suppress(i); break; case LEARN: // Port is "read-only" until timeout to FORWARD _input_sup->allow(i); _output_sup->suppress(i); break; case FORWARD: _input_sup->allow(i); _output_sup->allow(i); break; } return true;}voidEtherSpanTree::push(int source, Packet* p) { const BridgeMessage::wire* msg = reinterpret_cast<const BridgeMessage::wire*>(p->data()); // Accept a message if it is better *or equal* to current message. // (if it is equal, we need it so that its timestamp is updated) int cmp = _port[source].msg.compare(msg); if (cmp <= 0) { _port[source].msg.from_wire(msg); _send_tc_msg &= !msg->tca; // Stop sending tc if this is an ack. } find_tree(); p->kill();}voidEtherSpanTree::hello_hook(Timer *, void *thunk){ EtherSpanTree *e = (EtherSpanTree *)thunk; e->periodic(); for (int i = 0; i < e->noutputs(); i++) { Packet* p = e->generate_packet(i); if (p) e->output(i).push(p); } e->_hello_timer.schedule_after_ms(e->_best._hello_time * 1000);}Packet*EtherSpanTree::generate_packet(int output){ // Return without doing anything unless we are the "designated // bridge" for the lan on this port. This is only if we can send // out a better advertisement than we have received. int cmp = _best.compare(&_port[output].msg); if (cmp < 0) { // This is a quite rare case. It occurs when two nearly identical // messages on received on two different ports. Then, the // increase caused by incrementing cost can cause my best message // to be worse than the other message which was very close to the // one I based _best on. return 0; } if (cmp == 0 && !_send_tc_msg) { return 0; } // _best is better (or we need send topology change) WritablePacket* p = Packet::make(sizeof(BridgeMessage::wire)); BridgeMessage::wire* msg = reinterpret_cast<BridgeMessage::wire*>(p->data()); if (cmp == 0) { // Root port, send topology change message BridgeMessage::fill_tcm(msg); } else { // We are designated bridge for this port, send _best. _best.to_wire(msg); msg->bridge_id = htonq(((uint64_t)_bridge_priority << 48) | _bridge_id); msg->port_id = htons(output); if (_topology_change) { Timestamp cutoff = Timestamp::now(); cutoff._sec -= _best._forward_delay + _best._max_age; if (*_topology_change < cutoff) { delete _topology_change; _topology_change = 0; } else { msg->tc = 1; } } if (_port[output].needs_tca) { _port[output].needs_tca = false; msg->tca = 1; } } memcpy(msg->src, _addr, 6); return p;}EXPORT_ELEMENT(EtherSpanTree)ELEMENT_REQUIRES(Suppressor EtherSwitchBridgeMessage)// generate Vector template instance#include <click/vector.cc>#if EXPLICIT_TEMPLATE_INSTANCEStemplate class Vector<EtherSpanTree::PortInfo>;#endifCLICK_ENDDECLS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -