Skip to content

Create your own element

Spencer Stingley edited this page Jul 20, 2023 · 6 revisions

This tutorial will take you through the creation of your own Click element.

Click elements can be found in the elements folder. They are sorted by categories, which correspond more or less to the categories that can be found in the Elements page.

Own developments can take place in the folder elements/local/.

As any C++ classes elements are implemented through a source file (.cc) and a header file (.hh).

A minimal element would be composed of theses two files :

elements/local/myelement.cc

#include <click/config.h>
#include "myelement.hh"
CLICK_DECLS

Packet *
MyElement::simple_action(Packet *p) {
    click_chatter("Received a packet of size %d !", p->length());
    return p;
}

CLICK_ENDDECLS
EXPORT_ELEMENT(MyElement)

elements/local/myelement.hh

#ifndef CLICK_MYELEMENT_HH
#define CLICK_MYELEMENT_HH
#include <click/batchelement.hh>
CLICK_DECLS

class MyElement : public SimpleElement<MyElement> { public:

    MyElement() CLICK_COLD {};

    const char *class_name() const              { return "MyElement"; }
    const char *port_count() const              { return PORTS_1_1; }

    Packet *simple_action(Packet *);
};

CLICK_ENDDECLS
#endif

Let's start with the hh file. The definition of the class MyElement is pretty standard, excepts it inherits a special SimpleElement template that will automatically make the element batch-compatible, and hence very efficient. Old vanilla Click elements would simply inherit Element, while if you want to write different packet and batch processing function you may extend BatchElement.

The class_name() defines the element name, usually identical to the class. And port_count defines the number of I/O ports. See the Element class documentation for more informations about those.

The simple_action function will be called for every packets passing by. The .hh file only contains the declaration, the core of the code is in the .cc file.

Further information on the packet object can be found at: include/click/packet.hh

Multi-threaded element

FastClick's performance also reside in multi-threading. The multi-threading is implicit in Click, multiple threads may follow a path. For instance in a FromDPDKDevice(0) -> MyElementMT -> Discard; configuration the FromDPDKDevice element will use as many threads as possible (one per available cores) to forward packets through MyElementMT. Therefore MyElementMT must be thread-safe.

To write a multi-threaded element, FastClick provides a few helpers. The fastest technique is probably state de-duplication using the per_thread<> macro. Compared to thread-local-storage, per_thread enables aggregation of variables by other threads.

For instance, the following element will count the number of packets and bytes using per-thread counters, and its handler will do the aggregation.

elements/local/myelementmt.hh

// -*- c-basic-offset: 4 -*-
#ifndef CLICK_MYELEMENTMT_HH
#define CLICK_MYELEMENTMT_HH

#include <click/batchelement.hh>
#include <click/sync.hh>

class MyElementMT: public SimpleElement<MyElementMT> { public:

    MyElementMT() CLICK_COLD;
    ~MyElementMT() CLICK_COLD;

    const char *class_name() const override	{ return "MyElementMT"; }
    const char *port_count() const override	{ return PORTS_1_1; }

    void add_handlers() override CLICK_COLD;

    inline Packet* simple_action(Packet *);
    static String read_handler(Element *e, void *thunk);
    void add_handlers();
};

#endif

elements/local/myelementmt.cc

// -*- c-basic-offset: 4 -*-

#include <click/config.h>
#include "myelementmt.hh"

MyElementMT::MyElementMT()
    : _state()
{
}

MyElementMT::~MyElementMT()
{
}

inline Packet*
MyElementMT::simple_action(Packet *p)
{
    state &s = *_state;
    s.accum += p->length();
    s.count++;
    return p;
}

String
MyElementMT::read_handler(Element *e, void *thunk)
{
    MyElementMT*emt= static_cast<MyElementMT*>(e);
    switch ((uintptr_t)thunk) {
      case 0: {
	  PER_THREAD_MEMBER_SUM(uint64_t,count,emt->_state,count);
	  return String(count); }
      case 1: {
	  PER_THREAD_MEMBER_SUM(uint64_t,accum,emt->_state,accum);
	  return String(accum); }
      default:
	  return String("<error>");
    }
}

void
MyElementMT::add_handlers()
{
    add_read_handler("count", read_handler, 0);
    add_read_handler("bytes", read_handler, 1);
}

ELEMENT_REQUIRES(int64)
EXPORT_ELEMENT(MyElementMT)
ELEMENT_MT_SAFE(MyElementMT)
Clone this wiki locally