Click
Loading...
Searching...
No Matches
ctxelement.hh
1#ifndef CLICK_CTXELEMENT_HH
2#define CLICK_CTXELEMENT_HH
3#include <click/config.h>
4#include <click/element.hh>
5#include <click/router.hh>
6#include <click/flowbuffer.hh>
7#include <click/flow/common.hh>
9#ifdef HAVE_AVX2
10#include <immintrin.h>
11#endif
12#include <algorithm>
13#include <click/flow/flowelement.hh>
14#include <type_traits>
15#include <string>
16
17CLICK_DECLS
18
19
20struct StackReleaseChain {
21 SubFlowRealeaseFnt previous_fnt;
22 void* previous_thunk;
23};
24
25/*
26=c
27
28CTXElement()
29
30=s middlebox
31
32base class for the stack of the middlebox
33
34=d
35
36This element provides a common abstract base for the elements of the stack of the middlebox.
37It provides useful methods and the mechanism of function stack. This element is not meant to be used
38directly in a click configuration. Instead, use elements that inherit from it.
39
40To use the function stack, simply call one of the methods using this mechanism and the method
41will be called automatically on upstream elements until an IPIn element is reached. For instance,
42to remove bytes in a packet, elements can simply call removeBytes, giving it the right parameters
43and the method will be called on upstream elements that will handle the request and act
44consequently.
45
46Elements that inherit from this class can override the processPacket method to define their own
47behaviour.
48
49Implementation:
50
51initialize() and solve_initialize() are not accessible, instead you're forced to use the initialization
52dependency system. For instance, at the end of your configure() add the following pattern :
53
54 //Set the initialization to run after all stack objects passed
55 allStackInitialized.post(new Router::FctFuture([this](ErrorHandler* errh) {
56 //Do your initialization here
57 }));
58 return 0;;
59
60*/
61
62class CTXElement : public VirtualFlowSpaceElement
63{
64public:
65 friend class PathMerger;
66 friend class FlowBuffer;
67 friend class FlowBufferContentIter;
68
73 CTXElement() CLICK_COLD;
74
78 ~CTXElement() CLICK_COLD;
79
80 // Click related methods
81 const char *class_name() const { return "CTXElement"; }
82 const char *port_count() const { return PORTS_1_1; }
83 const char *processing() const { return "h/hh"; }
84 virtual const size_t flow_data_size() const { return 0; };
85 void* cast(const char*) override;
86
87 bool _visited;
88 static CounterInitFuture allStackInitialized;
89 Router::FctChildFuture _on_fcb_built_future;
90
91 // Custom methods
92
99 virtual void addCTXElementInList(CTXElement* element, int port);
100
106 static bool isCTXElement(Element* element);
107
112 virtual int maxModificationLevel(Element* stop);
113
114
115protected:
116
122 void setAnnotationLastUseful(Packet* packet, bool value) const;
123
129 bool getAnnotationLastUseful(Packet* packet) const;
130
139 inline char* searchInContent(char *content, const StringRef &pattern, uint32_t length);
140
147 void setInitialAck(Packet *packet, uint32_t initialAck) const;
148
155 uint32_t getInitialAck(Packet *packet) const;
156
162 void buildFunctionStack();
163
164 // Methods using the function stack mechanism
165
166 const int MODIFICATION_NONE = 0;
167 const int MODIFICATION_WRITABLE = 1; //May write to the packet without resizing
168 const int MODIFICATION_STALL = 2; //May stall packets
169 const int MODIFICATION_REPLACE = 4; //May replace some content
170 const int MODIFICATION_RESIZE = 8; //May resize the packet
171
179 virtual void removeBytes(WritablePacket* packet, uint32_t position,
180 uint32_t length);
181
192 virtual WritablePacket* insertBytes(WritablePacket* packet, uint32_t position,
193 uint32_t length) CLICK_WARN_UNUSED_RESULT;
194
203 virtual void requestMorePackets(Packet *packet, bool force = false);
204
211 virtual void closeConnection(Packet *packet, bool graceful);
212
216 virtual bool registerConnectionClose(StackReleaseChain* fcb_chain, SubFlowRealeaseFnt fnt, void* thunk);
217
224 virtual bool isEstablished();
225
233 virtual bool isLastUsefulPacket(Packet *packet);
234
242 virtual unsigned int determineFlowDirection();
243
244private:
252 void setAnnotationBit(Packet* packet, int bit, bool value) const;
253
261 bool getAnnotationBit(Packet* packet, int bit) const;
262
263 CTXElement *previousCTXElement; // Previous stack element in the configuration path
264 // and therefore next element in the function stack.
265
266 // Constants
267 // Up to 8 booleans can be stored in the corresponding annotation (see setAnnotationBit)
268 const int OFFSET_ANNOTATION_LASTUSEFUL = 0; // Indicates if a packet is the last useful one
269
270};
271
272template<typename T>
273class CTXSpaceElement : public CTXElement {
274public :
275 CTXSpaceElement();
276
277 ~CTXSpaceElement();
278
279 virtual const size_t flow_data_size() const { return sizeof(T); }
280
284 inline T* fcb_data_for(FlowControlBlock* fcb) {
285 T* flowdata = static_cast<T*>((void*)&fcb->data[_flow_data_offset]);
286 return flowdata;
287 }
288
292 inline T* fcb_data() {
293 return fcb_data_for(fcb_stack);
294 }
295
296 void push_batch(int port,PacketBatch* head) final {
297 //click_chatter("Pushing packet batch %p with fcb %p in %p{element}",head,fcb_data(),this);
298 push_flow(port, fcb_data(), head);
299 }
300
301 virtual void push_flow(int port, T* flowdata, PacketBatch* head) = 0;
302
303 };
304
305
313{
314public:
318 StackVisitor(CTXElement* startElement)
319 {
320 this->startElement = startElement;
321 }
322
327 {
328
329 }
330
336 bool visit(Element *e, bool, int port, Element*, int, int);
337
338private:
339 CTXElement* startElement; // Element that started the visit
340};
341
342template<typename T>
343CTXSpaceElement<T>::CTXSpaceElement() : CTXElement() {
344
345}
346
347template<typename T>
348CTXSpaceElement<T>::~CTXSpaceElement() {
349
350}
351
352template <typename T>
353struct BufferData {
354 T userdata;
355 FlowBuffer flowBuffer;
356};
357
358
359
372template<class Derived, typename T> class CTXStateElement : public CTXElement {
373 struct AT : public StackReleaseChain {
374 T v;
375 bool seen;
376 };
377public :
378
379
380 CTXStateElement() CLICK_COLD {};
381 virtual const size_t flow_data_size() const { return sizeof(AT); }
382
386 inline bool new_flow(T*, Packet*) {
387 return true;
388 }
389
393 inline T* fcb_data_for(FlowControlBlock* fcb) {
394 AT* flowdata = static_cast<AT*>((void*)&fcb->data[_flow_data_offset]);
395 return &flowdata->v;
396 }
397
401 inline T* fcb_data() {
402 return fcb_data_for(fcb_stack);
403 }
404
405 static void release_fnt(FlowControlBlock* fcb, void* thunk ) {
406 Derived* derived = static_cast<Derived*>(thunk);
407 AT* my_fcb = reinterpret_cast<AT*>(&fcb->data[derived->_flow_data_offset]);
408 derived->release_flow(&my_fcb->v);
409 if (my_fcb->previous_fnt)
410 my_fcb->previous_fnt(fcb, my_fcb->previous_thunk);
411 }
412
413 void push_batch(int port,PacketBatch* head) final {
414 auto my_fcb = my_fcb_data();
415 if (!my_fcb->seen) {
416 if (static_cast<Derived*>(this)->new_flow(&my_fcb->v, head->first())) {
417 my_fcb->seen = true;
418 if (!this->registerConnectionClose(my_fcb, &release_fnt, (void*)this)) {
419 click_chatter("ERROR in %p{element}: No element handles the connection",this);
420 abort();
421 }
422 } else {
423 head->fast_kill();
424 }
425 }
426 static_cast<Derived*>(this)->push_flow(port, &my_fcb->v, head);
427 };
428
429
430private:
431 inline AT* my_fcb_data() {
432 return static_cast<AT*>((void*)&fcb_stack->data[_flow_data_offset]);
433 }
434
435};
436
437template <class Derived, typename T>
438class StackBufferElement : public CTXStateElement<Derived, BufferData<T>>
439{
440 public:
441
442 const char *processing() const final { return Element::PUSH; }
443
444 void push_flow(int port, BufferData<T>* fcb_data, PacketBatch* flow)
445 {
446 auto it = fcb_data->flowBuffer.enqueueAllIter(flow);
447 int action = static_cast<Derived*>(this)->process_data(&fcb_data->userdata,it);
448 if (action < 0) {
449 this->closeConnection((it.current() ? it.current() : flow->first()), true);
450 release_flow(fcb_data);
451 return;
452 } else if (action > 0) {
453 this->closeConnection((it.current() ? it.current() : flow->first()), true);
454 this->checked_output_push_batch(action,fcb_data->flowBuffer.dequeueAll());
455 return;
456 }
457
458 PacketBatch* passed = it.flush();
459// click_chatter("Passed %d",passed);
460 if (it.current()) {
461// click_chatter("Pending %p",it.current());
462 this->requestMorePackets(it.current(), false);
463 }
464 if (passed)
465 this->checked_output_push_batch(action,passed);
466 }
467
468 void release_flow(BufferData<T>* fcb) {
469 PacketBatch* batch = fcb->flowBuffer.dequeueAll();
470 if (batch) {
471 batch->fast_kill();
472 }
473 }
474};
475
476template <class Derived, typename T>
477class StackChunkBufferElement : public CTXStateElement<Derived, BufferData<T>>
478{
479 public:
480
481 const char *processing() const final { return Element::PUSH; }
482
483
487 inline void release_stream(T*) {
488 return;
489 }
490
491
492 void push_flow(int port, BufferData<T>* fcb_data, PacketBatch* flow)
493 {
494 auto it = fcb_data->flowBuffer.enqueueAllChunkIter(flow);
495 int action = static_cast<Derived*>(this)->process_data(&fcb_data->userdata,it);
496 if (action < 0) {
497 this->closeConnection((it.current() ? it.current() : flow->first()), true);
498 release_flow(fcb_data);
499 return;
500 } else if (action > 0) {
501 this->closeConnection((it.current() ? it.current() : flow->first()), true);
502 this->checked_output_push_batch(action,fcb_data->flowBuffer.dequeueAll());
503 return;
504 }
505
506 PacketBatch* passed = it.flush();
507// click_chatter("Passed %d",passed);
508 if (it.current()) {
509// click_chatter("Pending %p",it.current());
510 this->requestMorePackets(it.current(), false);
511 }
512 if (passed)
513 this->checked_output_push_batch(action,passed);
514 }
515
516 void release_flow(BufferData<T>* fcb) {
517 static_cast<Derived*>(this)->release_stream(&fcb->userdata);
518 PacketBatch* batch = fcb->flowBuffer.dequeueAll();
519 if (batch) {
520 batch->fast_kill();
521 }
522 }
523};
524
525/*
526Copyright (c) 2008-2016, Wojciech Muła
527All rights reserved.
528
529Redistribution and use in source and binary forms, with or without
530modification, are permitted provided that the following conditions are
531met:
532
5331. Redistributions of source code must retain the above copyright
534 notice, this list of conditions and the following disclaimer.
535
5362. Redistributions in binary form must reproduce the above copyright
537 notice, this list of conditions and the following disclaimer in the
538 documentation and/or other materials provided with the distribution.
539
540THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
541IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
542TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
543PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
544HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
545SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
546TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
547PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
548LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
549NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
550SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
551namespace bits {
552
553 template <typename T>
554 inline T clear_leftmost_set(const T value) {
555
556 assert(value != 0);
557
558 return value & (value - 1);
559 }
560
561
562 template <typename T>
563 inline unsigned get_first_bit_set(const T value) {
564
565 assert(value != 0);
566
567 return __builtin_ctz(value);
568 }
569
570
571 template <>
572 inline unsigned get_first_bit_set<uint64_t>(const uint64_t value) {
573
574 assert(value != 0);
575
576 return __builtin_ctzl(value);
577 }
578
579} // namespace bits
580
581// Code from https://github.com/WojciechMula/sse4-strstr/
582#ifdef HAVE_AVX2
583inline size_t avx2_strstr_anysize(const char* s, size_t n, const char* needle, size_t k) {
584
585 const __m256i first = _mm256_set1_epi8(needle[0]);
586 const __m256i last = _mm256_set1_epi8(needle[k - 1]);
587
588 for (size_t i = 0; i < n; i += 32) {
589
590 const __m256i block_first = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(s + i));
591 const __m256i block_last = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(s + i + k - 1));
592
593 const __m256i eq_first = _mm256_cmpeq_epi8(first, block_first);
594 const __m256i eq_last = _mm256_cmpeq_epi8(last, block_last);
595
596 uint32_t mask = _mm256_movemask_epi8(_mm256_and_si256(eq_first, eq_last));
597
598 while (mask != 0) {
599
600 const auto bitpos = bits::get_first_bit_set(mask);
601
602 if (memcmp(s + i + bitpos + 1, needle + 1, k - 2) == 0) {
603 return i + bitpos;
604 }
605
606 mask = bits::clear_leftmost_set(mask);
607 }
608 }
609
610 return std::string::npos;
611}
612#elif HAVE_SSE42
613inline size_t sse42_strstr_anysize(const char* s, size_t n, const char* needle, size_t k) {
614 const __m128i N = _mm_loadu_si128((__m128i*)needle);
615
616 for (size_t i = 0; i < n; i += 16) {
617
618 const int mode = _SIDD_UBYTE_OPS
619 | _SIDD_CMP_EQUAL_ORDERED
620 | _SIDD_BIT_MASK;
621
622 const __m128i D = _mm_loadu_si128((__m128i*)(s + i));
623 const __m128i res = _mm_cmpestrm(N, k, D, n - i, mode);
624 uint64_t mask = _mm_cvtsi128_si64(res);
625
626 while (mask != 0) {
627
628 const auto bitpos = bits::get_first_bit_set(mask);
629
630 // we know that at least the first character of needle matches
631 if (memcmp(s + i + bitpos + 1, needle + 1, k - 1) == 0) {
632 return i + bitpos;
633 }
634
635 mask = bits::clear_leftmost_set(mask);
636 }
637 }
638
639 return std::string::npos;
640}
641#elif HAVE_SSE2
642inline size_t sse2_strstr_anysize(const char* s, size_t n, const char* needle, size_t k) {
643
644 assert(k > 0);
645 assert(n > 0);
646
647 const __m128i first = _mm_set1_epi8(needle[0]);
648 const __m128i last = _mm_set1_epi8(needle[k - 1]);
649
650 for (size_t i = 0; i < n; i += 16) {
651
652 const __m128i block_first = _mm_loadu_si128(reinterpret_cast<const __m128i*>(s + i));
653 const __m128i block_last = _mm_loadu_si128(reinterpret_cast<const __m128i*>(s + i + k - 1));
654
655 const __m128i eq_first = _mm_cmpeq_epi8(first, block_first);
656 const __m128i eq_last = _mm_cmpeq_epi8(last, block_last);
657
658 uint16_t mask = _mm_movemask_epi8(_mm_and_si128(eq_first, eq_last));
659
660 while (mask != 0) {
661
662 const auto bitpos = bits::get_first_bit_set(mask);
663
664 if (memcmp(s + i + bitpos + 1, needle + 1, k - 2) == 0) {
665 return i + bitpos;
666 }
667
668 mask = bits::clear_leftmost_set(mask);
669 }
670 }
671
672 return std::string::npos;
673}
674#elif __aarch64__
675#include <arm_neon.h>
676inline size_t aarch64_strstr_anysize(const char* s, size_t n, const char* needle, size_t k) {
677
678 assert(k > 0);
679 assert(n > 0);
680
681 const uint8x16_t first = vdupq_n_u8(needle[0]);
682 const uint8x16_t last = vdupq_n_u8(needle[k - 1]);
683
684 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(s);
685
686 for (size_t i = 0; i < n; i += 16) {
687
688 const uint8x16_t block_first = vld1q_u8(ptr + i);
689 const uint8x16_t block_last = vld1q_u8(ptr + i + k - 1);
690
691 const uint8x16_t eq_first = vceqq_u8(first, block_first);
692 const uint8x16_t eq_last = vceqq_u8(last, block_last);
693 const uint8x16_t pred_16 = vandq_u8(eq_first, eq_last);
694
695 uint64_t mask;
696
697 mask = vgetq_lane_u64(vreinterpretq_u64_u8(pred_16), 0);
698 if (mask) {
699 for (int j=0; j < 8; j++) {
700 if ((mask & 0xff) && (memcmp(s + i + j + 1, needle + 1, k - 2) == 0)) {
701 return i + j;
702 }
703
704 mask >>= 8;
705 }
706 }
707
708 mask = vgetq_lane_u64(vreinterpretq_u64_u8(pred_16), 1);
709 if (mask) {
710 for (int j=0; j < 8; j++) {
711 if ((mask & 0xff) && (memcmp(s + i + j + 8 + 1, needle + 1, k - 2) == 0)) {
712 return i + j + 8;
713 }
714
715 mask >>= 8;
716 }
717 }
718 }
719
720 return std::string::npos;
721}
722#endif
723
724//Inline functions
725inline char* CTXElement::searchInContent(char *content, const StringRef &pattern, uint32_t length) {
726#ifdef HAVE_AVX2
727 size_t pos = avx2_strstr_anysize(content, length, pattern.data(), pattern.length());
728#elif HAVE_SSE42
729 size_t pos = sse42_strstr_anysize(content, length, pattern.data(), pattern.length());
730#elif HAVE_SSE2
731 size_t pos = sse2_strstr_anysize(content, length, pattern.data(), pattern.length());
732#elif __aarch64__
733 size_t pos = aarch64_strstr_anysize(content, length, pattern.data(), pattern.length());
734#endif
735 if (pos == std::string::npos)
736 return 0;
737 else return content + pos;
738}
739
740#if HAVE_CTX
741extern CounterInitFuture _ctx_builded_init_future;
742#endif
743
744CLICK_ENDDECLS
745
746#endif
Definition ctxelement.hh:372
T * fcb_data_for(FlowControlBlock *fcb)
Definition ctxelement.hh:393
T * fcb_data()
Definition ctxelement.hh:401
bool new_flow(T *, Packet *)
Definition ctxelement.hh:386
Base class for Click elements.
Definition element.hh:42
This iterator allows to iterate the content in the buffer seamlessly accross the packets.
Definition flowbuffer.hh:216
This buffer allows to search, replace or remove data in the packets buffered as in a contiguous flow....
Definition flowbuffer.hh:40
Definition packetbatch.hh:415
A network packet.
Definition packet.hh:68
Base class for router configuration visitors.
Definition routervisitor.hh:17
This class defines a RouterVisitor that will be used to build the function stack Each element starts ...
Definition ctxelement.hh:313
~StackVisitor()
Destruct a StackVisitor.
Definition ctxelement.hh:326
StackVisitor(CTXElement *startElement)
Construct a StackVisitor.
Definition ctxelement.hh:318
bool visit(Element *e, bool, int port, Element *, int, int)
Visit the path of elements until we find a stack element. We will indicate to this element that we ar...
Definition ctxelement.cc:11
A network packet believed not to be shared.
Definition packet.hh:1004
Click's Element class.
The Router class implementing a router configuration.
Router configuration visitors for finding elements and ports.