diff --git a/elements/etherswitch/etherswitch.cc b/elements/etherswitch/etherswitch.cc index bd34cc3e97..5c44f92c70 100644 --- a/elements/etherswitch/etherswitch.cc +++ b/elements/etherswitch/etherswitch.cc @@ -23,6 +23,8 @@ #include #include #include +#include +#include CLICK_DECLS EtherSwitch::EtherSwitch() @@ -38,24 +40,69 @@ EtherSwitch::~EtherSwitch() int EtherSwitch::configure(Vector &conf, ErrorHandler *errh) { - return Args(conf, this, errh) + if (Args(conf, this, errh) .read("TIMEOUT", SecondsArg(), _timeout) - .complete(); + .complete() < 0) + return -1; + + int n = noutputs(); + _pfrs.resize(n); + for (int i = 0; i < n; i++) + _pfrs[i].configure(i, n); + return 0; } void EtherSwitch::broadcast(int source, Packet *p) { - int n = noutputs(); - assert((unsigned) source < (unsigned) n); - int sent = 0; - for (int i = n - 1; i >=0 ; i--) - if (i != source) { - Packet *pp = (sent < n - 2 ? p->clone() : p); + PortForwardRule &pfr = _pfrs[source]; + int n = pfr.bv.size(); + int w = pfr.w; + assert((unsigned) w <= (unsigned) n); + for (int i = 0; i < n && w > 0; i++) { + if (pfr.bv[i]) { + Packet *pp = (w > 1 ? p->clone() : p); output(i).push(pp); - sent++; + w--; } - assert(sent == n - 1); + } +} + +int +EtherSwitch::remove_port_forwarding(String portmaps, ErrorHandler *errh) +{ + int nn = noutputs(); + Vector new_pfrs(_pfrs); + Vector maps_vec; + cp_argvec(portmaps, maps_vec); + Vector::const_iterator i,n; + for (i = maps_vec.begin(), n = maps_vec.end(); i != n; ++i) { + int source, outport; + Args args = Args(this,errh).push_back_words(*i); + if (args.read_mp("SOURCE", source).consume() < 0) + return -1; + if (source < 0 || source > nn) + return -1; + int new_pfr_n = (new_pfrs[source].bv).size(); + while (!args.empty()) { + if (args.read_p("OUTPORT", outport).consume() < 0) + return -1; + if (outport < 0 || outport > new_pfr_n) + return -1; + (new_pfrs[source].bv)[outport] = false; + } + new_pfrs[source].calculate_weight(); + } + _pfrs = new_pfrs; + return 0; +} + +void +EtherSwitch::reset_port_forwarding() +{ + int n = noutputs(); + for (int i = 0; i < n; i++) + _pfrs[i].configure(i, n); } void @@ -83,10 +130,10 @@ EtherSwitch::push(int source, Packet *p) if (outport < 0) broadcast(source, p); - else if (outport == source) // Don't send back out on same interface - p->kill(); - else // forward - output(outport).push(p); + else if ((_pfrs[source].bv)[outport]) // forward w/ filter + output(outport).push(p); + else + p->kill(); } String @@ -102,17 +149,43 @@ EtherSwitch::reader(Element* f, void *thunk) } case 1: return String(sw->_timeout); + case 2: { + StringAccum sa; + int n = sw->noutputs(); + for (int i = 0; i < n; i++) + sa << "weight: " << (sw->_pfrs[i].w) << "\t" + << i << ": " << (sw->_pfrs[i].bv).unparse() << '\n'; + return sa.take_string(); + } default: return String(); } } int -EtherSwitch::writer(const String &s, Element *e, void *, ErrorHandler *errh) +EtherSwitch::writer(const String &s, Element *e, void *thunk, ErrorHandler *errh) { EtherSwitch *sw = (EtherSwitch *) e; - if (!SecondsArg().parse_saturating(s, sw->_timeout)) - return errh->error("expected timeout (integer)"); + switch((intptr_t) thunk) { + case 0: { + if (!SecondsArg().parse_saturating(s, sw->_timeout)) { + return errh->error("expected timeout (integer)"); + } + break; + } + case 1: { + if (sw->remove_port_forwarding(s, errh) < 0) { + return errh->error("invalid port forwarding"); + } + break; + } + case 2: { + sw->reset_port_forwarding(); + break; + } + default: + return errh->error("bad thunk"); + } return 0; } @@ -121,7 +194,10 @@ EtherSwitch::add_handlers() { add_read_handler("table", reader, 0); add_read_handler("timeout", reader, 1); + add_read_handler("port_forwarding", reader, 2); add_write_handler("timeout", writer, 0); + add_write_handler("remove_port_forwarding", writer, 1); + add_write_handler("reset_port_forwarding", writer, 2); } EXPORT_ELEMENT(EtherSwitch) diff --git a/elements/etherswitch/etherswitch.hh b/elements/etherswitch/etherswitch.hh index 4bf8a12b15..99cbee6bde 100644 --- a/elements/etherswitch/etherswitch.hh +++ b/elements/etherswitch/etherswitch.hh @@ -3,6 +3,8 @@ #include #include #include +#include +#include CLICK_DECLS /* @@ -54,6 +56,23 @@ Returns the current port association table. Returns or sets the TIMEOUT argument. +=e + + from_port0, from_port1 :: FromDevice...; + to_port0, to_port1 :: ToDevice...; + + switch :: EtherSwitch; + + q0 :: Queue -> to_port0; + q1 :: Queue -> to_port1; + + from_port0 -> [0] switch [0] -> q0; + from_port1 -> [1] switch [1] -> q1; + + --- + + echo "0, 1" > /click/switch/remove_port_forwarding + =a ListenEtherSwitch, EtherSpanTree @@ -85,8 +104,26 @@ class EtherSwitch : public Element { public: typedef HashTable Table; Table _table; uint32_t _timeout; + struct PortForwardRule { + Bitvector bv; /* Each bit is a port used in determining forwarding to of packets */ + int w; /* Sum of bv */ + void calculate_weight() { + w = bv.weight(); + } + void configure(int i, int n) { + bv.resize(n); + for (int j = 0; j < n; j++) + bv[j] = true; + assert((unsigned) i < (unsigned) n); + bv[i] = false; + calculate_weight(); + } + }; + Vector _pfrs; void broadcast(int source, Packet*); + int remove_port_forwarding(String portmaps, ErrorHandler *errh); + void reset_port_forwarding(); static String reader(Element *, void *); static int writer(const String &, Element *, void *, ErrorHandler *); diff --git a/elements/test/bitvectortest.cc b/elements/test/bitvectortest.cc index 257d9f9fb5..58d362fb23 100644 --- a/elements/test/bitvectortest.cc +++ b/elements/test/bitvectortest.cc @@ -54,6 +54,46 @@ BitvectorTest::initialize(ErrorHandler *errh) bv.resize(0); CHECK(bv.words()[0] == 0); + CHECK(!bv.parse("3", 0, 5, -3)); + CHECK(!bv.parse("3", 5, 3)); + + CHECK(!bv.parse("-", 0, 5)); + CHECK(!bv.parse("--2", 0, 5)); + CHECK(!bv.parse("2-", 0, 5)); + CHECK(!bv.parse("2--3", -5, 5)); + CHECK(!bv.parse("3", 0, 2)); + CHECK(!bv.parse("1", 3, 5)); + CHECK(!bv.parse("1-4", 3, 5)); + CHECK(!bv.parse("4-6", 3, 5)); + + CHECK(bv.parse("1,3,5", 0, 6)); + CHECK(bv.size() == 7 && !bv[0] && bv[1] && !bv[2] && bv[3] && !bv[4] && bv[5] && !bv[6]); + + CHECK(bv.parse("", 1, 3)); + CHECK(bv.size() == 3 && !bv[0] && !bv[1] && !bv[2]); + + CHECK(bv.parse("-1,1,3", -1, 3)); + CHECK(bv.size() == 5 && bv[0] && !bv[1] && bv[2] && !bv[3] && bv[4]); + + CHECK(bv.parse("-1-0,2-3,5-6", -2, 7)); + CHECK(bv.size() == 10 && !bv[0] && bv[1] && bv[2] && !bv[3] && bv[4] && bv[5] && !bv[6] && bv[7] && bv[8] && !bv[9]); + + CHECK(bv.parse("-7--6,-4--3,-1-0", -7, 0)); + CHECK(bv.size() == 8 && bv[0] && bv[1] && !bv[2] && bv[3] && bv[4] && !bv[5] && bv[6] && bv[7]); + + bv.assign(6, true); + bv[2] = bv[4] = false; + CHECK(bv.unparse() == "0-1,3,5"); + CHECK(bv.unparse(0) == "0-1,3,5"); + CHECK(bv.unparse(-1) == "1-2,4,6"); + CHECK(bv.unparse(1) == "0,2,4"); + CHECK(bv.unparse(8) == ""); + CHECK(bv.unparse(0, 1) == "1-2,4,6"); + CHECK(bv.unparse(0, -1) == "-1-0,2,4"); + CHECK(bv.unparse(1, 1) == "1,3,5"); + bv[4] = true; + CHECK(bv.unparse() == "0-1,3-5"); + errh->message("All tests pass!"); return 0; } diff --git a/include/click/bitvector.hh b/include/click/bitvector.hh index 30e199cb04..f1f906a65d 100644 --- a/include/click/bitvector.hh +++ b/include/click/bitvector.hh @@ -1,6 +1,7 @@ // -*- c-basic-offset: 4; related-file-name: "../../lib/bitvector.cc" -*- #ifndef CLICK_BITVECTOR_HH #define CLICK_BITVECTOR_HH +#include #include CLICK_DECLS @@ -73,6 +74,11 @@ class Bitvector { void swap(Bitvector &x); + inline int weight(); + + bool parse(const String &str, int min_val, int max_val, int offset = 0); + String unparse(int read_offset = 0, int output_offset = 0) const; + /** @cond never */ typedef word_type data_word_type CLICK_DEPRECATED; enum { data_word_bits = wbits }; @@ -80,6 +86,10 @@ class Bitvector { inline const word_type *data_words() const CLICK_DEPRECATED; /** @endcond never */ + void print(); + + static Bitvector from_mask(unsigned long mask); + private: enum { ninline = 2, inlinebits = ninline * wbits }; @@ -375,5 +385,14 @@ inline Bitvector::Bit &Bitvector::Bit::operator-=(bool x) { return *this; } +/** @brief Return the number of true bits */ +inline int Bitvector::weight() { + int w = 0; + for (int i = 0; i < size(); i++) + if ((*this)[i]) + w++; + return w; +} + CLICK_ENDDECLS #endif diff --git a/include/click/confparse.hh b/include/click/confparse.hh index 705a08ddb9..08571c44fb 100644 --- a/include/click/confparse.hh +++ b/include/click/confparse.hh @@ -71,6 +71,16 @@ String cp_shift_spacevec(String &str); String cp_unspacevec(const String *begin, const String *end); inline String cp_unspacevec(const Vector &conf); + +/// @brief Remove and return the first delimiter-separated argument from @a str. +/// @param[in,out] str delimiter-separated string +/// @param[in] delim delimiter +/// +/// All characters up to the first delimiter character are removed and +/// returned. @a str is set to the remaining portion of the string, +/// with up to one delimiter character removed. +String cp_shift_delimiter(String &str, char delim); + //@} /// @name Direct Parsing Functions diff --git a/lib/bitvector.cc b/lib/bitvector.cc index 1fde491866..4980feafb0 100644 --- a/lib/bitvector.cc +++ b/lib/bitvector.cc @@ -19,6 +19,9 @@ */ #include +#include +#include +#include #include CLICK_DECLS @@ -280,4 +283,76 @@ Bitvector::swap(Bitvector &x) x._data = (d == _f ? x._f : d); } +void +Bitvector::print() { + char buf[size()+1]; + int i; + for (i = 0; i < size(); i++) { + buf[i] = ((*this)[i]?'1':'0'); + } + buf[i] = '\0'; + click_chatter("%s",buf); +} + +Bitvector Bitvector::from_mask(unsigned long mask) { + int vsize = sizeof(unsigned long) * 8; + Bitvector v(vsize); + int pow = 0; + while (pow < vsize) { + v[pow] = mask & 1; + mask >>= 1; + ++pow; + } + return v; +} + +bool +Bitvector::parse(const String &str, int min_val, int max_val, int offset) +{ + if (offset < 0 || max_val < min_val) + return false; + + Bitvector result(max_val - min_val + 1 + offset, false); + String work_copy = str; + while (String part = cp_shift_delimiter(work_copy, ',')) { + // initialize val & val_end unnecessarily to avoid uninitialized variable warnings + int val = 0, val_end = 0, dash_offset = part.find_left('-', 1); + if (dash_offset >= 1 && + dash_offset + 1 < part.length() && + BoundedIntArg(min_val, max_val).parse(part.substring(0, dash_offset), val) && + BoundedIntArg(min_val, max_val).parse(part.substring(dash_offset + 1), val_end) && + val <= val_end) { + for (int i = val; i <= val_end; ++i) + result[i - min_val + offset] = true; + } else if (BoundedIntArg(min_val, max_val).parse(part, val)) { + result[val - min_val + offset] = true; + } else { + return false; + } + } + + swap(result); + return true; +} + +String +Bitvector::unparse(int read_offset, int output_offset) const +{ + StringAccum sa; + int start_range = -1; + for (int i = (read_offset < 0 ? 0 : read_offset); i <= size(); ++i) { + if (i < size() && start_range < 0 && (*this)[i]) { + start_range = i; + } else if (start_range >= 0 && (i >= size() || !(*this)[i])) { + if (sa) + sa << ','; + sa << (start_range - read_offset + output_offset); + if (i > start_range + 1) + sa << '-' << (i - 1 - read_offset + output_offset); + start_range = -1; + } + } + return sa.take_string(); +} + CLICK_ENDDECLS diff --git a/lib/confparse.cc b/lib/confparse.cc index f1d739cdd7..eb5d5c68a2 100644 --- a/lib/confparse.cc +++ b/lib/confparse.cc @@ -792,6 +792,21 @@ cp_shift_spacevec(String &str) return answer; } +String +cp_shift_delimiter(String &str, char delim) +{ + int offset = str.find_left(delim); + if (offset >= 0) { + String res = str.substring(0, offset); + str = str.substring(offset + 1); + return res; + } else { + String res = str; + str = String::make_empty(); + return res; + } +} + /// @brief Join the strings of @a conf with commas and return the result. /// /// This function does not quote or otherwise protect the strings in @a conf.