Click
Loading...
Searching...
No Matches
flowelement.hh
1// -*- c-basic-offset: 4 -*-
2#ifndef CLICK_FLOWELEMENT_HH
3#define CLICK_FLOWELEMENT_HH
4#include <click/glue.hh>
5#include <click/vector.hh>
6#include <click/string.hh>
7#include <click/batchelement.hh>
9#include <click/pair.hh>
10#include "flow.hh"
11
12
13CLICK_DECLS
14
15#ifdef HAVE_FLOW
16# if HAVE_CTX
17class CTXManager;
18# endif
19class VirtualFlowManager;
20
21enum FlowType {
22 FLOW_NONE = 0,
23 FLOW_ETHER,
24 FLOW_ARP,
25 FLOW_IP,
26 FLOW_TCP,
27 FLOW_UDP,
28 FLOW_ICMP,
29 FLOW_HTTP
30};
31
32class FlowElement : public BatchElement {
33public:
34 FlowElement();
35 ~FlowElement();
36
37 //Those should actually be in some kind of base CTXElement
38# if HAVE_CTX
39 virtual FlowNode* get_table(int iport, Vector<FlowElement*> contextStack);
40
41 virtual FlowNode* resolveContext(FlowType, Vector<FlowElement*> stack);
42# endif
43 virtual FlowType getContext(int port);
44
45 virtual bool stopClassifier() { return false; };
46
47#if HAVE_FLOW_DYNAMIC
48 inline void fcb_acquire(int count = 1) {
49 fcb_stack->acquire(count);
50 }
51 inline void fcb_update(int count) {
52 if (count > 0)
53 fcb_stack->acquire(count);
54 else if (count < 0)
55 fcb_stack->release(-count);
56 }
57
58 inline void fcb_release(int count = 1) {
59 fcb_stack->release(count);
60 }
61#else
62 inline void fcb_acquire(int count = 1) {
63 (void)count;
64 }
65 inline void fcb_update(int) {}
66 inline void fcb_release(int count = 1) {
67 (void)count;
68 }
69#endif
70
71};
72
76class VirtualFlowSpaceElement : public FlowElement {
77public:
78 VirtualFlowSpaceElement();
79
80 virtual const size_t flow_data_size() const = 0;
81 virtual const int flow_data_index() const {
82 return -1;
83 }
84 virtual const int flow_announce_manager(VirtualFlowManager* manager, ErrorHandler* errh) const {
85 return 0;
86 }
87 inline void set_flow_data_offset(int offset) {_flow_data_offset = offset; }
88 inline int flow_data_offset() {return _flow_data_offset; }
89
90 int configure_phase() const { return CONFIGURE_PHASE_DEFAULT + 5; }
91
92 void *cast(const char *name) override;
93
94
95#if HAVE_CTX_GLOBAL_TIMEOUT
96 inline void ctx_acquire_timeout(int nmsec) {
97 //Do not set a smaller timeout
98 if ((fcb_stack->flags & FLOW_TIMEOUT) && (nmsec <= (int)(fcb_stack->flags >> FLOW_TIMEOUT_SHIFT))) {
99#if DEBUG_CLASSIFIER_TIMEOUT > 1
100 click_chatter("Acquiring timeout of %p, not changing it, flag %d",this,fcb_stack->flags);
101#endif
102 return;
103 }
104#if DEBUG_CLASSIFIER_TIMEOUT > 1
105 click_chatter("Acquiring timeout of %p to %d, flag %d",this,nmsec,fcb_stack->flags);
106#endif
107 fcb_stack->flags = (nmsec << FLOW_TIMEOUT_SHIFT) | FLOW_TIMEOUT | ((fcb_stack->flags & FLOW_TIMEOUT_INLIST) ? FLOW_TIMEOUT_INLIST : 0);
108 }
109
110 inline void ctx_release_timeout() {
111#if DEBUG_CLASSIFIER_TIMEOUT > 1
112 click_chatter("Releasing timeout of %p",this);
113#endif
114 //If the timeout is in list, we must not put it back in the pool
115 if (fcb_stack->flags & FLOW_TIMEOUT_INLIST)
116 assert(fcb_stack->flags & FLOW_TIMEOUT);
117 if ((fcb_stack->flags & FLOW_TIMEOUT) && (fcb_stack->flags & FLOW_TIMEOUT_INLIST))
118 fcb_stack->flags = 0 | FLOW_TIMEOUT | FLOW_TIMEOUT_INLIST;
119 else
120 fcb_stack->flags = 0;
121 }
122#else
123 inline void ctx_acquire_timeout(int nmsec) {
124 //TODO : use a local timer
125 fcb_acquire();
126 }
127
128 inline void ctx_release_timeout() {
129 fcb_release();
130 }
131#endif
132
133#if HAVE_FLOW_DYNAMIC
134 inline void fcb_set_release_fnt(struct FlowReleaseChain* fcb_chain, SubFlowRealeaseFnt fnt) {
135 fcb_chain->previous_fnt = fcb_stack->release_fnt;
136 fcb_chain->previous_thunk = fcb_stack->thunk;
137 fcb_stack->release_fnt = fnt;
138 fcb_stack->thunk = this;
139#if DEBUG_CLASSIFIER_RELEASE
140 click_chatter("Release fnt set to %p, was %p",fcb_stack->release_fnt,fcb_chain->previous_fnt);
141#endif
142 }
143 inline void fcb_remove_release_fnt(struct FlowReleaseChain* fcb_chain, SubFlowRealeaseFnt fnt) {
144 debug_flow("Release fnt remove %p",fnt);
145 if (likely(fcb_stack->release_fnt == fnt)) { //Normally it will call the chain in the same order
146 fcb_stack->release_fnt = fcb_chain->previous_fnt;
147 fcb_stack->thunk = fcb_chain->previous_thunk;
148 debug_flow("Release removed is now to %p",fcb_stack->release_fnt);
149 } else {
150 SubFlowRealeaseFnt chain_fnt = fcb_stack->release_fnt;
151 VirtualFlowSpaceElement* fe = static_cast<VirtualFlowSpaceElement*>(fcb_stack->thunk);
152 FlowReleaseChain* frc;
153 do {
154 if (fe == 0) {
155 click_chatter("BAD ERROR : Trying to remove a timeout flow function that is not set...");
156 return;
157 }
158
159 frc = reinterpret_cast<FlowReleaseChain*>(&fcb_stack->data[fe->_flow_data_offset]);
160 chain_fnt = frc->previous_fnt;
161 if (chain_fnt == 0) {
162 click_chatter("ERROR : Trying to remove a timeout flow function that is not set...");
163 return;
164 }
165 fe = static_cast<VirtualFlowSpaceElement*>(frc->previous_thunk);
166 } while (chain_fnt != fnt);
167 frc->previous_fnt = fcb_chain->previous_fnt;
168 frc->previous_thunk = fcb_chain->previous_thunk;
169 }
170 }
171#else
172 inline void fcb_set_release_fnt(struct FlowReleaseChain*, SubFlowRealeaseFnt) {
173 click_chatter("ERROR: YOU MUST HAVE DYNAMIC FLOW RELEASE FNT fct setted !");
174 assert(false);
175 }
176#endif
177
178
179 virtual PacketBatch* pull_batch(int port, unsigned max) override final {
180 click_chatter("ERROR : Flow Elements do not support pull");
181 return 0;
182 }
183
184 int initialize(ErrorHandler *errh) override CLICK_COLD {
185 //The element itself is automatically posted by build_fcb via fcb_builded_init_future
186 return 0;
187 }
188protected:
189
190 int _flow_data_offset;
191 friend class FlowBufferVisitor;
192 friend class VirtualFlowManager;
193};
194
195# if HAVE_FLOW_DYNAMIC
196class UnstackVisitor : public RouterVisitor {
197public:
198 bool visit(Element *e, bool isoutput, int port,
199 Element *from_e, int from_port, int distance);
200
201};
202# endif
203
204
205
212class CounterInitFuture : public Router::ChildrenFuture { public:
213 CounterInitFuture(String name, std::function<int(ErrorHandler*)> on_reached);
214 CounterInitFuture(String name, std::function<void(void)> on_reached);
215
216 virtual void notifyParent(InitFuture* future) override;
217
218 virtual int solve_initialize(ErrorHandler* errh) override;
219
220 virtual int completed(ErrorHandler* errh) override;
221private:
222 int _n;
223 String _name;
224 std::function<int(ErrorHandler*)> _on_reached;
225};
226
230class VirtualFlowManager : public FlowElement { public:
231 VirtualFlowManager();
232
233 static CounterInitFuture* fcb_builded_init_future() {
234 return &_fcb_builded_init_future;
235 }
236
237 static CounterInitFuture _fcb_builded_init_future;
238
239protected:
240 int _reserve;
241
242 typedef Pair<Element*,int> EDPair;
243 Vector<EDPair> _reachable_list;
244
245 static Vector<VirtualFlowManager*> _entries;
246
247
248 void find_children(int verbose = 0);
249
250 static void _build_fcb(int verbose, bool ordered);
251 static void build_fcb();
252 virtual void fcb_built() {
253
254 }
255
256 bool stopClassifier() { return true; };
257
258 friend class CTXElement;
259};
260
261
262template<typename T> class FlowSpaceElement : public VirtualFlowSpaceElement {
263
264public :
265
266 FlowSpaceElement() CLICK_COLD;
267
268 void fcb_set_init_data(FlowControlBlock* fcb, const T data) CLICK_COLD;
269
270 virtual const size_t flow_data_size() const override { return sizeof(T); }
271
272
276 inline T* fcb_data_for(FlowControlBlock* fcb) {
277 T* flowdata = static_cast<T*>((void*)&fcb->data[_flow_data_offset]);
278 return flowdata;
279 }
280
284 inline T* fcb_data() {
285 return fcb_data_for(fcb_stack);
286 }
287
288 void push_batch(int port, PacketBatch* head) final {
289 push_flow(port, fcb_data(), head);
290 };
291
292 virtual void push_flow(int port, T* flowdata, PacketBatch* head) = 0;
293};
294
295class DefaultChecker { public:
296 struct str {
297 bool seen;
298 };
299 static inline bool seen(void*, str* s) {
300 return s->seen;;
301 }
302 static inline void mark_seen(void*, str* s) {
303 s->seen = true;
304 }
305 static inline void release(void*, str* s) {
306 s->seen = false;
307 }
308};
309
321template<class Derived, typename T, typename Checker = DefaultChecker> class FlowStateElement : public VirtualFlowSpaceElement {
322 struct AT : public FlowReleaseChain {
323 T v;
324 typename Checker::str str;
325 };
326public :
327
328 typedef FlowStateElement<Derived, T, Checker> derived;
329
330 FlowStateElement() CLICK_COLD;
331
332 virtual const size_t flow_data_size() const { return sizeof(AT); }
333 virtual const int flow_announce_manager(Element* manager, ErrorHandler* errh) const {
334 if (Derived::timeout > 0) {
335 if (manager->cast("CTXManager") == 0) {
336 errh->warning("The timeout of %dms of %p{element} is ignored, only the flow manager %p{element} timeout is prevalent.", Derived::timeout, this, manager);
337 }
338 }
339 return 0;
340 }
341
345 inline bool new_flow(T*, Packet*) {
346 return true;
347 }
348
349 inline FlowControlBlock* stack_from_flow(void* ptr) {
350 return (FlowControlBlock*)(((uint8_t*)ptr) - _flow_data_offset - sizeof(FlowControlBlock));
351 }
352
356 inline T* fcb_data_for(FlowControlBlock* fcb) {
357 AT* flowdata = static_cast<AT*>((void*)&fcb->data[_flow_data_offset]);
358 return &flowdata->v;
359 }
360
364 inline T* fcb_data() {
365 return fcb_data_for(fcb_stack);
366 }
367
368 static void release_fnt(FlowControlBlock* fcb, void* thunk ) {
369 Derived* derived = static_cast<Derived*>(thunk);
370 AT* my_fcb = reinterpret_cast<AT*>(&fcb->data[derived->_flow_data_offset]);
371 derived->release_flow(&my_fcb->v);
372 if (my_fcb->previous_fnt)
373 my_fcb->previous_fnt(fcb, my_fcb->previous_thunk);
374 }
375
376 void push_batch(int port, PacketBatch* head) {
377 auto my_fcb = my_fcb_data();
378 if (!Checker::seen(&my_fcb->v, &my_fcb->str)) {
379 if (static_cast<Derived*>(this)->new_flow(&my_fcb->v, head->first())) {
380 Checker::mark_seen(&my_fcb->v, &my_fcb->str);
381 if (Derived::timeout > 0)
382 this->ctx_acquire_timeout(Derived::timeout);
383#if HAVE_FLOW_DYNAMIC
384 this->fcb_set_release_fnt(my_fcb, &release_fnt);
385#endif
386 } else { //TODO set early drop?
387 head->fast_kill();
388 return;
389 }
390 }
391 static_cast<Derived*>(this)->push_flow(port, &my_fcb->v, head);
392 };
393
394 void close_flow() {
395 if (Derived::timeout > 0) {
396 this->ctx_release_timeout();
397 }
398#if HAVE_FLOW_DYNAMIC
399 this->fcb_remove_release_fnt(my_fcb_data(), &release_fnt);
400#endif
401 Checker::release(&my_fcb_data()->v, &my_fcb_data()->str);
402 }
403
404private:
405 inline AT* my_fcb_data() {
406 return static_cast<AT*>((void*)&fcb_stack->data[_flow_data_offset]);
407 }
408
409};
410
411
412template<typename T, int index> class FlowSharedBufferElement : public FlowSpaceElement<T> {
413
414public :
415
416 FlowSharedBufferElement() : FlowSpaceElement<T>() {
417
418 }
419
420 const size_t flow_data_size() const final { return sizeof(T); }
421 const int flow_data_index() const final { return index; }
422};
423
424
425
426#define DefineFlowSharedBuffer(name,type,index) class FlowSharedBuffer ## name ## Element : public FlowSharedBufferElement<type,index>{ };
427
428DefineFlowSharedBuffer(Paint,int,0);
429#define NR_SHARED_FLOW 1
430
431class FlowElementVisitor : public RouterVisitor {
432public:
433 Element* origin;
434 FlowElementVisitor(Element* e) : origin(e) {
435
436 }
437
438 struct inputref {
439 FlowElement* elem;
440 int iport;
441 };
442 Vector<struct inputref> dispatchers;
443
444 bool visit(Element *e, bool isoutput, int port,
445 Element *from_e, int from_port, int distance) {
446 (void)from_e;
447 (void)from_port;
448 (void)distance;
449 FlowElement* dispatcher = dynamic_cast<FlowElement*>(e);
450 if (dispatcher != NULL) {
451 if (dispatcher == origin)
452 return false;
453 struct inputref ref = {.elem = dispatcher, .iport = port};
454 dispatchers.push_back(ref);
455 return false;
456 } else {
457
458 }
459 /*if (v.dispatchers[i] == (FlowElement*)e) {
460 click_chatter("Classification loops are unsupported, place another CTXManager before reinjection of the packets.");
461 e->router()->please_stop_driver();
462 return 0;
463 }*/
464 return true;
465 }
466
467 static FlowNode* get_downward_table(Element* e, int output, Vector<FlowElement*> context, bool resolve_context = false);
468};
469
474template<typename T>
475FlowSpaceElement<T>::FlowSpaceElement() : VirtualFlowSpaceElement() {
476}
477
478# if HAVE_CTX
479template<typename T>
480void FlowSpaceElement<T>::fcb_set_init_data(FlowControlBlock* fcb, const T data) {
481 for (int i = 0; i < sizeof(T); i++) {
482 if (fcb->data[FCBPool::init_data_size() + _flow_data_offset + i] && fcb->data[_flow_data_offset + i] != ((uint8_t*)&data)[i]) {
483 click_chatter("In %p{element} for offset %d :",this, _flow_data_offset+i);
484 click_chatter("Index [%d] : Cannot set data to %d, as it is already %d",i,*((T*)(&fcb->data[_flow_data_offset])),data);
485 click_chatter("Is marked as set : %d", fcb->data[FCBPool::init_data_size() + _flow_data_offset + i]);
486 fcb->reverse_print();
487
488 click_chatter("It generally means your graph is messed up");
489 assert(false);
490 }
491 fcb->data[FCBPool::init_data_size() + _flow_data_offset + i] = 0xff;
492 }
493 *((T*)(&fcb->data[_flow_data_offset])) = data;
494}
495# endif
496
501template<class Derived, typename T, typename Checker>
502FlowStateElement<Derived, T, Checker>::FlowStateElement() : VirtualFlowSpaceElement() {
503}
504
510# if HAVE_CTX
511
512#define DEFAULT_4TUPLE "12/0/ffffffff:HASH-3 16/0/ffffffff:HASH-3 20/0/ffffffff:HASH-3"
513
514//Those should not be used anymore, as the FLOW_CONTEXT is a much better alternative that assuming the top session is IP...
515#define TCP_SESSION "9/06! 12/0/ffffffff:HASH-3 16/0/ffffffff:HASH-3 20/0/ffffffff:HASH-3"
516#define UDP_SESSION "9/11! 12/0/ffffffff:HASH-3 16/0/ffffffff:HASH-3 20/0/ffffffff:HASH-3"
517
518//Use only one of the 3 following macros
519
523#define FLOW_ELEMENT_DEFINE_CONTEXT(ft) \
524FlowNode* get_table(int iport, Vector<FlowElement*> context) override CLICK_COLD {\
525 context.push_back(this);\
526 return FlowElement::get_table(iport, context);\
527}\
528virtual FlowType getContext(int) override {\
529 return ft;\
530}\
531
535#define FLOW_ELEMENT_DEFINE_SESSION_CONTEXT(rule,ft) \
536FlowNode* get_table(int iport, Vector<FlowElement*> contextStack) override CLICK_COLD {\
537 if (ft)\
538 contextStack.push_back(this);\
539 FlowNode* down = FlowElement::get_table(iport,contextStack); \
540 FlowNode* my = FlowClassificationTable::parse(this,rule).root;\
541 return my->combine(down, true, true, true, this);\
542}\
543virtual FlowType getContext(int) override {\
544 return ft;\
545}\
546
550#define FLOW_ELEMENT_DEFINE_SESSION(rule) \
551 FLOW_ELEMENT_DEFINE_SESSION_CONTEXT(rule,FLOW_NONE)
552
556#define FLOW_ELEMENT_DEFINE_SESSION_DUAL(ruleA,ruleB) \
557FlowNode* get_table(int iport, Vector<FlowElement*> context) override CLICK_COLD {\
558 return FlowClassificationTable::parse(this,ruleA).root->combine(FlowClassificationTable::parse(this,ruleB).root,false,false,true,this)->combine(FlowElement::get_table(iport,context), true, true, true, this);\
559}
560
564#define FLOW_ELEMENT_DEFINE_PORT_SESSION_CONTEXT(port_num,rule,ft) \
565FlowNode* get_table(int iport, Vector<FlowElement*> contextStack) override {\
566 if (iport == port_num) {\
567 return FlowClassificationTable::parse(this,rule).root->combine(FlowElement::get_table(iport,contextStack), true, true, true, this);\
568 }\
569 if (ft)\
570 contextStack.push_back(this);\
571 return FlowElement::get_table(iport,contextStack);\
572}\
573virtual FlowType getContext(int) override {\
574 return ft;\
575}
576
577#endif
578#else //Not even HAVE_FLOW
579typedef BatchElement FlowElement;
580#endif
581#if !defined(HAVE_CTX)
582#define FLOW_ELEMENT_DEFINE_SESSION(rule,context)
583#define FLOW_ELEMENT_DEFINE_PORT_CONTEXT(port,rule,context)
584#define FLOW_ELEMENT_DEFINE_SESSION_DUAL(ruleA,ruleB)
585#define FLOW_ELEMENT_DEFINE_SESSION_CONTEXT(rule,context)
586#define FLOW_ELEMENT_DEFINE_PORT_SESSION_CONTEXT(port,rule,context)
587#endif
588
589CLICK_ENDDECLS
590
591#endif
Base class for Click elements.
Definition element.hh:42
virtual void * cast(const char *name)
Attempt to cast the element to a named type.
Definition element.cc:515
Error reporting class.
Definition error.hh:90
int warning(const char *fmt,...)
Print a warning message (level el_warning).
Definition error.cc:815
Definition packetbatch.hh:415
A network packet.
Definition packet.hh:68
Base class for router configuration visitors.
Definition routervisitor.hh:17
virtual bool visit(Element *e, bool isoutput, int port, Element *from_e, int from_port, int distance)
Visit an element.
Definition routervisitor.cc:29
A string of characters.
Definition string.hh:22
Vector template.
Definition vector.hh:111
void push_back(value_argument_type v)
Append element v.
Definition vector.hh:399
Router configuration visitors for finding elements and ports.
Click's String class.
Click's vector container template.