Overview
________
-
+
This section shows how a VPP developer can create a new plugin, and
add it to VPP. We assume that we are starting from the VPP <top-of-workspace>.
Change directory to **./src/plugins**, and run the plugin generator:
.. code-block:: console
-
+
$ cd ./src/plugins
$ ../../extras/emacs/make-plugin.sh
<snip>
Plugin name: myplugin
Dispatch type [dual or qs]: dual
(Shell command succeeded with no output)
-
+
OK...
The plugin generator script asks two questions: the name of the
$ cd ./myplugin
$ ls
- CMakeLists.txt myplugin.c myplugin_periodic.c setup.pg
- myplugin_all_api_h.h myplugin.h myplugin_test.c
- myplugin.api myplugin_msg_enum.h node.c
+ CMakeLists.txt myplugin.api myplugin.c myplugin.h
+ myplugin_periodic.c myplugin_test.c node.c setup.pg
Due to recent build system improvements, you **don't** need to touch
any other files to integrate your new plugin into the vpp build. Simply
"myplugin_plugin.so" and "myplugin_test_plugin.so" are loaded:
.. code-block:: console
-
+
$ cd <top-of-workspace>
$ make run
<snip>
The rest of the build recipe is pretty simple:
-.. code-block:: console
+.. code-block:: CMake
add_vpp_plugin (myplugin
SOURCES
- myplugin.c
- node.c
+ myplugin.c
+ node.c
myplugin_periodic.c
myplugin.h
-
+
MULTIARCH_SOURCES
- node.c
-
+ node.c
+
API_FILES
myplugin.api
-
- INSTALL_HEADERS
- myplugin_all_api_h.h
- myplugin_msg_enum.h
-
+
API_TEST_SOURCES
myplugin_test.c
)
Vpp itself uses dlsym(...) to track down the vlib_plugin_registration_t
generated by the VLIB_PLUGIN_REGISTER macro:
-.. code-block:: console
+.. code-block:: C
- VLIB_PLUGIN_REGISTER () =
+ VLIB_PLUGIN_REGISTER () =
{
.version = VPP_BUILD_VER,
.description = "myplugin plugin description goes here",
- };
+ };
Vpp only loads .so files from the plugin directory which contain an
instance of this data structure.
line. By default, plugins are loaded. To change that behavior, set
default_disabled in the macro VLIB_PLUGIN_REGISTER:
-.. code-block:: console
+.. code-block:: C
VLIB_PLUGIN_REGISTER () =
{
The boilerplate generator places the graph node dispatch function
onto the "device-input" feature arc. This may or may not be useful.
-.. code-block:: console
+.. code-block:: C
VNET_FEATURE_INIT (myplugin, static) =
{
.arc_name = "device-input",
.node_name = "myplugin",
.runs_before = VNET_FEATURES ("ethernet-input"),
- };
+ };
As given by the plugin generator, myplugin.c contains the binary API
message handler for a generic "please enable my feature on such and
This is the generated graph node dispatch function. You'll need to
rewrite it to solve the problem at hand. It will save considerable
time and aggravation to retain the **structure** of the node dispatch
-function.
+function.
Even for an expert, it's a waste of time to reinvent the *loop
structure*, enqueue patterns, and so forth. Simply tear out and
replace the specimen 1x, 2x, 4x packet processing code with code
relevant to the problem you're trying to solve.
+myplugin.api
+------------
+
+This contains the API message definition. Here we only have defined
+a single one named ``myplugin_enable_disable`` and an implicit
+``myplugin_enable_disable_reply`` containing only a return value due
+to the ``autoreply`` keyword.
+
+The syntax reference for ``.api`` files can be found at VPP API Language
+
+Addressing the binary API with this message will run the handler defined
+in ``myplugin.c`` as ``vl_api_myplugin_enable_disable_t_handler``.
+It will receive a message pointer ``*mp`` which is the struct defined
+in ``myplugin.api`` and should return another message pointer ``*rmp``,
+of the reply type. That's what ``REPLY_MACRO`` does.
+
+To be noted, all API messages are in net-endian and vpp is host-endian,
+so you will need to use :
+
+* ``u32 value = ntohl(mp->value);``
+* ``rmp->value = htonl(value);``
+
+You can now use this API with :ref:`GoLang bindings <add_plugin_goapi>`
+
+myplugin_periodic.c
+-------------------
+
+This defines a VPP process, a routine that will run indefinitely and
+be woken up intermittently, here to process plugin events.
+
+To be noted, vlib_processes aren't thread-safe, and data structures
+should be locked when shared between workers.
+
Plugin "Friends with Benefits"
------------------------------
In vpp VLIB_INIT_FUNCTION functions, It's reasonably common to see a
specific init function invoke other init functions:
-.. code-block:: console
+.. code-block:: C
if ((error = vlib_call_init_function (vm, some_other_init_function))
return error;
In the case where one plugin needs to call a init function in another
plugin, use the vlib_call_plugin_init_function macro:
-.. code-block:: console
+.. code-block:: C
if ((error = vlib_call_plugin_init_function (vm, "otherpluginname", some_init_function))
return error;
If you wish to obtain a pointer to a symbol in another plugin, use the
vlib_plugin_get_symbol(...) API:
-.. code-block:: console
+.. code-block:: C
void *p = vlib_get_plugin_symbol ("plugin_name", "symbol");