Skip to content

Commit f57a46d

Browse files
author
sdarayan
committed
click/etherswitch: Forward traffic to a set of ports
The default behavior of etherswitch is to forward traffic to all ports except the source when broadcast or to the destination port found in lookup of etherswitch's mac table. This commit expands the functionality of etherswitch to send packet(s) out port(s) iff the port to forward traffic is enabled. Thus, etherswitch can selectively forward traffic to particular ports, a feature proving useful with broadcast traffic so as to avoid traffic loops between click and other external hardware (e.g. switch). Ports can selectively be removed and the port forwarding map can be reset via write handlers "remove_port_forwarding" and "reset_port_forwarding". The arguments to "remove_port_forwarding" write handler is a list of numbers with the first one being a source port and the others being ports to remove. For example, "1 2 3 4, 2 3 4" means port 1 will not forward traffic to ports 2, 3, and 4 -- in addition port 2 will not forward traffic to ports 3 and 4. No arguments are necessary for "reset_port_forwarding" write handler. Also, the port forwarding map can be viewed via read handler "port_forwarding".
1 parent 23da763 commit f57a46d

File tree

2 files changed

+130
-17
lines changed

2 files changed

+130
-17
lines changed

elements/etherswitch/etherswitch.cc

Lines changed: 93 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <click/args.hh>
2424
#include <click/straccum.hh>
2525
#include <click/error.hh>
26+
#include <click/confparse.hh>
27+
#include <click/string.hh>
2628
CLICK_DECLS
2729

2830
EtherSwitch::EtherSwitch()
@@ -38,24 +40,69 @@ EtherSwitch::~EtherSwitch()
3840
int
3941
EtherSwitch::configure(Vector<String> &conf, ErrorHandler *errh)
4042
{
41-
return Args(conf, this, errh)
43+
if (Args(conf, this, errh)
4244
.read("TIMEOUT", SecondsArg(), _timeout)
43-
.complete();
45+
.complete() < 0)
46+
return -1;
47+
48+
int n = noutputs();
49+
_pfrs.resize(n);
50+
for (int i = 0; i < n; i++)
51+
_pfrs[i].configure(i, n);
52+
return 0;
4453
}
4554

4655
void
4756
EtherSwitch::broadcast(int source, Packet *p)
4857
{
49-
int n = noutputs();
50-
assert((unsigned) source < (unsigned) n);
51-
int sent = 0;
52-
for (int i = n - 1; i >=0 ; i--)
53-
if (i != source) {
54-
Packet *pp = (sent < n - 2 ? p->clone() : p);
58+
PortForwardRule &pfr = _pfrs[source];
59+
int n = pfr.bv.size();
60+
int w = pfr.w;
61+
assert((unsigned) w <= (unsigned) n);
62+
for (int i = 0; i < n && w > 0; i++) {
63+
if (pfr.bv[i]) {
64+
Packet *pp = (w > 1 ? p->clone() : p);
5565
output(i).push(pp);
56-
sent++;
66+
w--;
5767
}
58-
assert(sent == n - 1);
68+
}
69+
}
70+
71+
int
72+
EtherSwitch::remove_port_forwarding(String portmaps, ErrorHandler *errh)
73+
{
74+
int nn = noutputs();
75+
Vector<PortForwardRule> new_pfrs(_pfrs);
76+
Vector<String> maps_vec;
77+
cp_argvec(portmaps, maps_vec);
78+
Vector<String>::const_iterator i,n;
79+
for (i = maps_vec.begin(), n = maps_vec.end(); i != n; ++i) {
80+
int source, outport;
81+
Args args = Args(this,errh).push_back_words(*i);
82+
if (args.read_mp("SOURCE", source).consume() < 0)
83+
return -1;
84+
if (source < 0 || source > nn)
85+
return -1;
86+
int new_pfr_n = (new_pfrs[source].bv).size();
87+
while (!args.empty()) {
88+
if (args.read_p("OUTPORT", outport).consume() < 0)
89+
return -1;
90+
if (outport < 0 || outport > new_pfr_n)
91+
return -1;
92+
(new_pfrs[source].bv)[outport] = false;
93+
}
94+
new_pfrs[source].calculate_weight();
95+
}
96+
_pfrs = new_pfrs;
97+
return 0;
98+
}
99+
100+
void
101+
EtherSwitch::reset_port_forwarding()
102+
{
103+
int n = noutputs();
104+
for (int i = 0; i < n; i++)
105+
_pfrs[i].configure(i, n);
59106
}
60107

61108
void
@@ -83,10 +130,10 @@ EtherSwitch::push(int source, Packet *p)
83130

84131
if (outport < 0)
85132
broadcast(source, p);
86-
else if (outport == source) // Don't send back out on same interface
87-
p->kill();
88-
else // forward
89-
output(outport).push(p);
133+
else if ((_pfrs[source].bv)[outport]) // forward w/ filter
134+
output(outport).push(p);
135+
else
136+
p->kill();
90137
}
91138

92139
String
@@ -102,17 +149,43 @@ EtherSwitch::reader(Element* f, void *thunk)
102149
}
103150
case 1:
104151
return String(sw->_timeout);
152+
case 2: {
153+
StringAccum sa;
154+
int n = sw->noutputs();
155+
for (int i = 0; i < n; i++)
156+
sa << "weight: " << (sw->_pfrs[i].w) << "\t"
157+
<< i << ": " << (sw->_pfrs[i].bv).unparse() << '\n';
158+
return sa.take_string();
159+
}
105160
default:
106161
return String();
107162
}
108163
}
109164

110165
int
111-
EtherSwitch::writer(const String &s, Element *e, void *, ErrorHandler *errh)
166+
EtherSwitch::writer(const String &s, Element *e, void *thunk, ErrorHandler *errh)
112167
{
113168
EtherSwitch *sw = (EtherSwitch *) e;
114-
if (!SecondsArg().parse_saturating(s, sw->_timeout))
115-
return errh->error("expected timeout (integer)");
169+
switch((intptr_t) thunk) {
170+
case 0: {
171+
if (!SecondsArg().parse_saturating(s, sw->_timeout)) {
172+
return errh->error("expected timeout (integer)");
173+
}
174+
break;
175+
}
176+
case 1: {
177+
if (sw->remove_port_forwarding(s, errh) < 0) {
178+
return errh->error("invalid port forwarding");
179+
}
180+
break;
181+
}
182+
case 2: {
183+
sw->reset_port_forwarding();
184+
break;
185+
}
186+
default:
187+
return errh->error("bad thunk");
188+
}
116189
return 0;
117190
}
118191

@@ -121,7 +194,10 @@ EtherSwitch::add_handlers()
121194
{
122195
add_read_handler("table", reader, 0);
123196
add_read_handler("timeout", reader, 1);
197+
add_read_handler("port_forwarding", reader, 2);
124198
add_write_handler("timeout", writer, 0);
199+
add_write_handler("remove_port_forwarding", writer, 1);
200+
add_write_handler("reset_port_forwarding", writer, 2);
125201
}
126202

127203
EXPORT_ELEMENT(EtherSwitch)

elements/etherswitch/etherswitch.hh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <click/element.hh>
44
#include <click/etheraddress.hh>
55
#include <click/hashtable.hh>
6+
#include <click/bitvector.hh>
7+
#include <click/vector.hh>
68
CLICK_DECLS
79

810
/*
@@ -54,6 +56,23 @@ Returns the current port association table.
5456
5557
Returns or sets the TIMEOUT argument.
5658
59+
=e
60+
61+
from_port0, from_port1 :: FromDevice...;
62+
to_port0, to_port1 :: ToDevice...;
63+
64+
switch :: EtherSwitch;
65+
66+
q0 :: Queue -> to_port0;
67+
q1 :: Queue -> to_port1;
68+
69+
from_port0 -> [0] switch [0] -> q0;
70+
from_port1 -> [1] switch [1] -> q1;
71+
72+
---
73+
74+
echo "0, 1" > /click/switch/remove_port_forwarding
75+
5776
=a
5877
5978
ListenEtherSwitch, EtherSpanTree
@@ -85,8 +104,26 @@ class EtherSwitch : public Element { public:
85104
typedef HashTable<EtherAddress, AddrInfo> Table;
86105
Table _table;
87106
uint32_t _timeout;
107+
struct PortForwardRule {
108+
Bitvector bv; /* Each bit is a port used in determining forwarding to of packets */
109+
int w; /* Sum of bv */
110+
void calculate_weight() {
111+
w = bv.weight();
112+
}
113+
void configure(int i, int n) {
114+
bv.resize(n);
115+
for (int j = 0; j < n; j++)
116+
bv[j] = true;
117+
assert((unsigned) i < (unsigned) n);
118+
bv[i] = false;
119+
calculate_weight();
120+
}
121+
};
122+
Vector<PortForwardRule> _pfrs;
88123

89124
void broadcast(int source, Packet*);
125+
int remove_port_forwarding(String portmaps, ErrorHandler *errh);
126+
void reset_port_forwarding();
90127

91128
static String reader(Element *, void *);
92129
static int writer(const String &, Element *, void *, ErrorHandler *);

0 commit comments

Comments
 (0)