-
Notifications
You must be signed in to change notification settings - Fork 40
- Introduction
- User-space plugins tutorial
- 2.1 Plugin.cc
- 2.2 sm-example.cc
- 2.3 sm-example.manifest
- 2.4 Makefile
- Kernel-space plugins tutorial
- 3.1 plugin.c
- 3.2 lfa-ps.c
- 3.3 pff-lfa.manifest
- 3.4 Makefile
The purpose of this section is to present some simple but complete examples that illustrate how to write user-space and kernel-space plugins. These tutorials are aligned to the current version of the SDK.
Although it is possible to develop user-space plugins within the rinad/ipcp source code tree, the preferred way of contributing custom plugins to the IPC Process user-space implementation is to develop them outside of the rinad/ipcp source tree. Consequently, this tutorial will focus on this case.
A user-space plugin contains the implementation of one or more policy-sets that will be made available to the IPC Process Daemon at runtime by dynamically linking the shared object containing the plugin. The recommended structure of a plugin consists of the following files, that are usually contained in a directory dedicated to the plugin:
- plugin.cc. The source file containing code that exports the policy-set factories for all the policy-sets contributed by the plugin.
- implementation.cc. A number of .cc and .h files containing the implementation of the policy-sets contributed by the plugin.
- plugin-name.manifest. A text file in JSON format that provides information on the policy-sets contributed by the plugin (see Section 4.3).
- makefiles. A build system to compile, link and install the plugin. For example, it may be a single Makefile, or more files for autoconf/automake ([autoconf],[autoconf]) or CMake.
In order to understand better how all pieces works together, this section will walk through an example of a simple plugin that contributes a single policy-set to the Security-Manager component of the IPCP Daemon. The plugin, which can be found in the plugins/example-smplugin directory of the IRATI repository, is made of the following files:
- plugin.cc
- sm-example.cc
- sm-example.manifest
- Makefile
#include <string>
#include <sstream>
#define IPCP_MODULE "security-manager-example-ps"
#include "ipcp-logging.h"
#include "components.h"
namespace rinad {
extern "C" rina::IPolicySet * createSecurityManagerExamplePs(rina::ApplicationEntity * ctx);
extern "C" void destroySecurityManagerExamplePs(rina::IPolicySet * ps);
extern "C" int get_factories(std::vector<struct rina::PsFactory>& factories)
{
struct rina::PsFactory factory;
factory.info.name = "example";
factory.info.app_entity = rina::ApplicationEntity::SECURITY_MANAGER_AE_NAME;
factory.create = createSecurityManagerExamplePs;
factory.destroy = destroySecurityManagerExamplePs;
factories.push_back(factory);
return 0;
}
} // namespace rinad
This file carries out the following important tasks:
- Inclusion of components.h, which contains the definitions of several IPC Process Daemon base classes and data structures. Among the others, there are the definitions of the base class for the Security Manager policy-sets, and the base class for all IPCP components.
- For each policy-set that the plugin contributes (one in this example), plugin.cc declares one function to create an instance of the policy-set.
- For each policy-set that the plugin contributes (one in this example), plugin.cc declares one function to destroy an instance of the policy-set.
- The definition of a get_factories function to export the policy-set factories. For each factory to be exported (in this case only one), a rina::PsFactory object instance is added to the output array passed as argument. Each entry in the array is built with the policy-set name, the IPC Process component (Application Entity) the policy set is part of (Security Manager in this case), and the references to the factory’s create and destroy functions.
#include <string>
#include <sstream>
#define IPCP_MODULE "security-manager-example-ps"
#include "ipcp-logging.h"
#include "components.h"
namespace rinad {
class SecurityManagerExamplePs: public ISecurityManagerPs {
public:
SecurityManagerExamplePs(IPCPSecurityManager * dm);
bool isAllowedToJoinDIF(const rina::Neighbor& newMember);
bool acceptFlow(const Flow& newFlow);
int set_policy_set_param(const std::string& name, const std::string& value);
virtual ~SecurityManagerExamplePs() {}
private:
// Data model of the security manager component.
IPCPSecurityManager * dm;
int max_retries;
};
// [...]
extern "C" rina::IPolicySet * createSecurityManagerExamplePs(rina::ApplicationEntity * ctx)
{
IPCPSecurityManager * sm = dynamic_cast<IPCPSecurityManager *>(ctx);
if (!sm) {
return NULL;
}
return new SecurityManagerExamplePs(sm);
}
extern "C" void destroySecurityManagerExamplePs(rina::IPolicySet * ps)
{
if (ps) {
delete ps;
}
}
} // namespace rinad
This file carries out the following tasks:
- Definition of a class that inherits from the abstract class ISecurityManagerPS, which is the base class for all the Security Manager policy-sets. Such a class needs to provide an implementation for the isAllowedToJoinDIF and acceptFlow operations. We have omitted those dummy implementations from the listing since it is not important for the purpose of illustrating the SDK usage.
- Definition of the policy factory’s create function, declared in plugin.cc. An IPCP component (or application entity) is passed as a parameter, and is downcasted to the object of type IPCPSecurityManager. Then a new instance of the SecurityManagerExamplePs class is created and returned to the caller.
- Definition of the destroy operation that deletes the instance of the policy set.
{
"PluginName": "sm-example",
"PluginVersion": "1",
"PolicySets" : [ {
"Name": "example",
"Component": "security-manager",
"Version" : "1"
} ]
}
The plugin manifest contains the name and version of the plugin as well as the name, component and version of all the policy-sets contributed by the plugin (just one in this case).
# Set INSTALLDIR environment variable to the directory containing
# the IRATI installation
ifndef INSTALLDIR
$(error The INSTALLDIR environment variable is not set)
endif
PLUGIN_NAME = sm-example
SOURCES = \
plugin.cc \
sm-example.cc
CXX = g++
CXXFLAGS = -Wall -Werror -fPIC
LDFLAGS = -shared -L $(INSTALLDIR)/lib
LIBS = -lrina
CXXFLAGS += -I $(INSTALLDIR)/include/rinad/ipcp
CXXFLAGS += -I $(INSTALLDIR)/include/rinad
CXXFLAGS += -I $(INSTALLDIR)/include
OBJECTS = $(SOURCES:.cc=.o)
all: $(PLUGIN_NAME).so
$(PLUGIN_NAME).so: $(OBJECTS)
$(CXX) $(LDFLAGS) $(OBJECTS) -o $@
.cc.o:
$(CXX) -c $(CXXFLAGS) $< -o $@
clean:
rm -f $(OBJECTS)
install:
install $(PLUGIN_NAME).so $(INSTALLDIR)/lib/rinad/ipcp
install -m 0644 $(PLUGIN_NAME).manifest $(INSTALLDIR)/lib/rinad/ipcp
uninstall:
-rm $(INSTALLDIR)/lib/rinad/ipcp/$(PLUGIN_NAME).so $(INSTALLDIR)/lib/rinad/ipcp/$(PLUGIN_NAME).manifest
When invoking make in the plugin directory, the INSTALLDIR environment variable must be set to the absolute path of the IRATI installation (could be "/", if IRATI is installed systemwide). The PLUGIN_NAME variable contains the name of the plugin to be built, while the SOURCES variable contains the list of the C++ sources that are to be compiled and linked together in order to build the shared object.
Custom plugins for the kernel-space part of the IPC Process are developed as out-of-tree loadable Linux kernel modules. A kernel-space plugin contains the implementation of one or more policy-sets that can be published to the kernel-space SDK at runtime by dynamically loading the associated kernel module.
The recommended structure of a kernel-space plugin contains the following files, that are usually put in a same directory dedicated to the plugin:
- plugin.c. The source file that contains the module init() and exit() functions that are in charge of respectively publishing and un-publishing the policy-set factories for all the policy-sets contributed by the plugin.
- implementation.c. A number of .c and .h files containing the implementation of the policy-sets hooks (methods), and the implementation of the constructor and destructor factory callbacks.
- plugin-name.manifest. A text file in JSON format that provides information about the plugin itself and the list of the policy-sets contributed by the plugin.
- Makefile. A makefile to build the plugin as an out-of-tree kernel module.
Similarly to what presented in Section 2, this section will walk through a concrete example of a simple plugin that contributes a single policy-set to the PDU Forwarding Function (PFF) component of the IPCP Daemon. The plugin, which can be found in the plugins/lfa directory of IRATI repositoru, is made up of the following files:
- plugin.c
- lfa-ps.c
- pff-lfa.manifest
- Makefile
#include <linux/export.h>
#include <linux/module.h>
#include <linux/string.h>
#define RINA_PREFIX "pff-lfa"
#include "logs.h"
#include "rds/rmem.h"
#include "rds/rtimer.h"
#include "pff-ps.h"
#include "debug.h"
#define RINA_PFF_LFA_NAME "lfa"
extern struct ps_factory pff_factory;
static int __init mod_init(void)
{
int ret;
strcpy(pff_factory.name, RINA_PFF_LFA_NAME);
ret = pff_ps_publish(&pff_factory);
if (ret) {
LOG_ERR("Failed to publish policy set factory");
return -1;
}
LOG_INFO("PFF LFA policy set loaded successfully");
return 0;
}
static void __exit mod_exit(void)
{
int ret;
ret = pff_ps_unpublish(RINA_PFF_LFA_NAME);
if (ret) {
LOG_ERR("Failed to unpublish policy set factory");
return;
}
LOG_INFO("PFF LFA policy set unloaded successfully");
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_DESCRIPTION("PFF LFA policy set");
MODULE_LICENSE("GPL");
This file carries out the following tasks:
- Inclusion of pff-ps.h, which contains the declaration of struct pff_ps , that is the base class for all PFF policy-sets.
- For each policy-set that the plugin contributes (one in this example), plugin.c declares the corresponding policy-set factory, which is defined in one of the implementation files, in this case lfa-ps.c.
- In the module loading function, the plugin publishes all the factories to the kernel-space SDK. In this case, there is only one policy-set, for the PFF component, and so the PFFspecific publishing routine is used.
- In the module unloading function, the plugin un-publishes all the factories published to the kernel-space SDK (only one in this case).
#include <linux/export.h>
#include <linux/module.h>
#include <linux/string.h>
#define RINA_PREFIX "pff-lfa"
#include "logs.h"
#include "rds/rmem.h"
#include "rds/rtimer.h"
#include "pff-ps.h"
#include "debug.h"
struct pft_port_entry {
port_id_t port_id;
struct list_head next;
};
struct pft_entry {
address_t destination;
qos_id_t qos_id;
port_id_t nhop;
port_id_t port;
struct list_head alt_ports;
struct list_head next;
};
/* Policy-set-specific data structure to be shared among the
* PFF policies. */
struct pff_ps_priv {
spinlock_t lock;
struct list_head entries;
/* Holds ports that are down */
struct list_head ports_down;
};
/* Policy invoked from the IRATI stack to add an entry to the LFA PFF. */
static int lfa_add(struct pff_ps *ps, struct mod_pff_entry *entry)
{
struct pff_ps_priv *priv;
struct pft_entry *tmp;
struct port_id_altlist *alts;
int i;
priv = (struct pff_ps_priv *) ps->priv;
if (!priv_is_ok(priv))
return -1;
if (!entry) {
LOG_ERR("Bogus output parameters, won't add");
return -1;
}
if (!is_address_ok(entry->fwd_info)) {
LOG_ERR("Bogus destination address passed, cannot add");
return -1;
}
if (!is_qos_id_ok(entry->qos_id)) {
LOG_ERR("Bogus qos-id passed, cannot add");
return -1;
}
spin_lock(&priv->lock);
tmp = pft_find(priv, entry->fwd_info, entry->qos_id);
if (!tmp) {
tmp = pfte_create_ni(entry->fwd_info, entry->qos_id);
if (!tmp) {
spin_unlock(&priv->lock);
return -1;
}
list_add(&tmp->next, &priv->entries);
} else {
LOG_ERR("Entry already exists");
spin_unlock(&priv->lock);
return -1;
}
alts = list_first_entry(&entry->port_id_altlists, typeof(*alts), next);
if (alts->num_ports < 1) {
LOG_INFO("Port id set is empty");
pfte_destroy(tmp);
spin_unlock(&priv->lock);
return -1;
}
tmp->port = alts->ports[0];
tmp->nhop = tmp->port;
for (i = 1; i < alts->num_ports; i++) {
if (pfte_port_add(tmp, alts->ports[i])) {
pfte_destroy(tmp);
spin_unlock(&priv->lock);
return -1;
}
}
spin_unlock(&priv->lock);
return 0;
}
// [...]
/* Constructor for LFA policy-set instances. */
static struct ps_base * pff_ps_lfa_create(struct rina_component *component)
{
struct pff_ps *ps;
struct pff_ps_priv *priv;
struct pff *pff = pff_from_component(component);
/* Allocate memory for the shared data structure. */
priv = rkzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return NULL;
/* Initialized the shared data structure. */
spin_lock_init(&priv->lock);
INIT_LIST_HEAD(&priv->entries);
INIT_LIST_HEAD(&priv->ports_down);
/* Allocate memory for the policy-set instance. */
ps = rkzalloc(sizeof(*ps), GFP_KERNEL);
if (!ps)
return NULL;
/* Initialize references to data model and shared data structure. */
ps->base.set_policy_set_param = NULL; /* default */
ps->dm = pff;
ps->priv = (void *) priv;
/* Fill in the instance's hooks. */
ps->pff_add = lfa_add;
ps->pff_remove = lfa_remove;
ps->pff_port_state_change = lfa_port_state_change;
ps->pff_is_empty = lfa_is_empty;
ps->pff_flush = lfa_flush;
ps->pff_nhop = lfa_nhop;
ps->pff_dump = lfa_dump;
return &ps->base;
}
/* Destructor for LFA policy-set instances. */
static void pff_ps_lfa_destroy(struct ps_base *bps)
{
struct pff_ps *ps = container_of(bps, struct pff_ps, base);
if (bps) {
struct pff_ps_priv *priv;
priv = (struct pff_ps_priv *) ps->priv;
if (!priv_is_ok(priv))
return;
/* Clean up the shared data structure. */
spin_lock(&priv->lock);
__pft_flush(priv);
spin_unlock(&priv->lock);
/* Deallocate the shared data structure and the
* policy-set instance. */
rkfree(priv);
rkfree(ps);
}
}
struct ps_factory pff_factory = {
.owner = THIS_MODULE,
.create = pff_ps_lfa_create,
.destroy = pff_ps_lfa_destroy,
};
This file carries out the following tasks:
- The definition of the LFA-specific private data structure for the LFA policy-set, used to implement shared state among the policies implementation. For the LFA plugin, the private data structure contains a list of forwarding entries (i.e. the forwarding table), the list of ports that are currently down, and a lock for concurrent access.
- Definition of the lfa_add policy, invoked by the IRATI stack to add an entry to the PDU Forwarding Table. As explained above, a pointer to this function is used to initialize the policy-set hooks. The other hooks are not shown in the listing above, since it’s not the purpose of this tutorial to explain the LFA algorithm and data structures.
- The definition of the of the factory’s create function, to be exported to the SDK. The function takes a pointer to the struct rina_component base class, which is actually pointing to an object of derived type struct pff . It allocates memory for either the policy-set instance and the private data structure. The priv and dm fields of the policy-set instance are properly initialized with references to the associated private data structure and data model, and the hooks are filled with pointers to locally defined functions that implement single policies (e.g. lfa_add (2), lfa_remove , etc.). Finally, the address of the policy-set instance just created is returned.
- The definition of the factory’s destroy function, to be exported to the SDK. This function flushes (deallocates) the entries in the forwarding table, deallocates the memory of the policy-set instance and of the private data structure.
- The definition of the LFA factory, as a global variable in the kernel module. The owner field must be set to THIS_MODULE to correctly support reference counting, so that the module cannot be unloaded while LFA policy-set instances are being used. The create and destroy factory callbacks are initialised with the locally defined functions described above.
{
"PluginName": "pff-lfa",
"PluginVersion": "1",
"PolicySets" : [ {
"Name": "lfa",
"Component": "pff",
"Version" : "1"
} ]
}
The plugin manifest contains the name and version of the plugin as well as the name, component and version of the LFA PFF policy-set.
KDIR=../../linux
KREL=`uname -r`
ifneq ($(KERNELRELEASE),)
ccflags-y = -Wtype-limits -Inet/rina
obj-m := pff-lfa.o
pff-lfa-y := lfa-ps.o plugin.o
else
all:
$(MAKE) -C $(KDIR) M=$$PWD
clean:
rm -rf *.o *.ko *.mod.c *.mod.o
install:
$(MAKE) -C $(KDIR) M=$$PWD modules_install
cp pff-lfa.manifest /lib/modules/$(KREL)/extra/
endif
This file carries out the following tasks:
- Defines a new module called pff-lfa ,
- Specifies what compilation units the pff-lfa module is made of.
- Invokes the Linux build system to compile and link the module, while
- Installs the built module, together with the manifest file, in a system directory dedicated to out-of-tree modules.
The Makefile is written to be integrated into the Linux build system.
- Home
- Software Architecture Overview
- IRATI in depth
-
Tutorials
- 1. DIF-over-a-VLAN-(point-to-point-DIF)
- 2. DIF over two VLANs
- 3. Security experiments on small provider net
- 4. Multi-tenant Data Centre Network configured via the NMS-DAF
- 5. Congestion control in multi-tenant DC
- 6. Multi-tenant Data Centre network with Demonstrator
- 7. ISP Security with Demonstrator
- 8. Renumbering in a single DIF
- 9. Application discovery and Distributed Mobility Management over WiFi
- 10. Distributed Mobility Management over multiple providers
- 11. Multi-access: multiple providers, multiple technologies