diff options
Diffstat (limited to 'docs/gettingstarted')
l--------- | docs/gettingstarted/developers/VPPAPI.md | 1 | ||||
-rw-r--r-- | docs/gettingstarted/developers/add_plugin.rst | 84 | ||||
-rw-r--r-- | docs/gettingstarted/developers/add_plugin_goapi.rst | 83 | ||||
-rw-r--r-- | docs/gettingstarted/developers/index.rst | 2 |
4 files changed, 142 insertions, 28 deletions
diff --git a/docs/gettingstarted/developers/VPPAPI.md b/docs/gettingstarted/developers/VPPAPI.md new file mode 120000 index 00000000000..a5404c116ee --- /dev/null +++ b/docs/gettingstarted/developers/VPPAPI.md @@ -0,0 +1 @@ +../../../src/tools/vppapigen/VPPAPI.md
\ No newline at end of file diff --git a/docs/gettingstarted/developers/add_plugin.rst b/docs/gettingstarted/developers/add_plugin.rst index 16952e8f036..19b935b746a 100644 --- a/docs/gettingstarted/developers/add_plugin.rst +++ b/docs/gettingstarted/developers/add_plugin.rst @@ -7,7 +7,7 @@ Adding a plugin 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>. @@ -21,7 +21,7 @@ Create your new plugin Change directory to **./src/plugins**, and run the plugin generator: .. code-block:: console - + $ cd ./src/plugins $ ../../extras/emacs/make-plugin.sh <snip> @@ -36,7 +36,7 @@ Change directory to **./src/plugins**, and run the plugin generator: 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 @@ -65,9 +65,8 @@ Here are the generated files. We'll go through them in a moment. $ 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 @@ -92,7 +91,7 @@ As a quick sanity check, run vpp and make sure that "myplugin_plugin.so" and "myplugin_test_plugin.so" are loaded: .. code-block:: console - + $ cd <top-of-workspace> $ make run <snip> @@ -122,25 +121,21 @@ the copyright notice: 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 ) @@ -178,13 +173,13 @@ binary API message dispatcher, and to add its messages to vpp's global 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. @@ -193,7 +188,7 @@ You can enable or disable specific vpp plugins from the command 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 () = { @@ -205,14 +200,14 @@ default_disabled in the macro 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 @@ -243,20 +238,53 @@ node.c 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; @@ -264,7 +292,7 @@ specific init function invoke other init functions: 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; @@ -274,7 +302,7 @@ This allows sequencing between plugin init functions. 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"); diff --git a/docs/gettingstarted/developers/add_plugin_goapi.rst b/docs/gettingstarted/developers/add_plugin_goapi.rst new file mode 100644 index 00000000000..dce35b8f0a2 --- /dev/null +++ b/docs/gettingstarted/developers/add_plugin_goapi.rst @@ -0,0 +1,83 @@ +.. _add_plugin_goapi: + +Add a plugin's GO API +===================== + +In order to use your plugin's API with GO, you will need to use +a GO client and GO definitions of the API messages that you defined +in ``myplugin.api`` (go bindings). + +These two things can be found in `govpp <https://github.com/FDio/govpp>`_ + +* The API client lives in `./core` +* The api-generator lives in `./binapigen` +* A sample of its output (the go bindings) for VPP's latest version lives in `./binapi` + +To generate the go bindings for your plugin. Assuming : +* ``/home/vpp`` is a VPP clone with your plugin in it. +* ``/home/controlplane`` is a go controlplane repo + +.. code-block:: console + + $ mkdir /home/controlplane/vpp-go-bindings + $ git clone https://github.com/FDio/govpp> + $ cd govpp + $ BINAPI_DIR=/home/controlplane/vpp-go-bindings VPP_DIR=/home/vpp make gen-binapi-from-code + +This will generate the go-bindings in ``/home/controlplane/vpp-go-bindings`` +For example ``vpp-go-bindings/myplugin/myplugin.ba.go`` will contain : + +.. code-block:: go + + // MypluginEnableDisable defines message 'myplugin_enable_disable'. + type MypluginEnableDisable struct { + EnableDisable bool `binapi:"bool,name=enable_disable" json:"enable_disable,omitempty"` + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + } + + +You can then use the generated go bindings in your go code like this : + +.. code-block:: go + + package main + + import ( + "fmt" + "git.fd.io/govpp.git" + "git.fd.io/govpp.git/binapi/interfaces" + "git.fd.io/govpp.git/binapi/vpe" + + "myplugin.io/controlplane/vpp-go-bindings/myplugin/myplugin" + ) + + func main() { + // Connect to VPP + conn, _ := govpp.Connect("/run/vpp/api.sock") + defer conn.Disconnect() + + // Open channel + ch, _ := conn.NewAPIChannel() + defer ch.Close() + + request := &vpe.MypluginEnableDisable{ + EnableDisable: true, + } + reply := &vpe.MypluginEnableDisableReply{} + + err := ch.SendRequest(request).ReceiveReply(reply) + if err != nil { + fmt.Errorf("SendRequest: %w\n", err) + } + } + +As you will need to import (or ``go get "git.fd.io/govpp.git"``) to leverage the API +client in your code, you might want to use the api-generator directly from the +clone ``go build`` fetches for you. You can do this with : + +.. code-block:: console + + $ export GOVPP_DIR=$(go list -f '{{.Dir}}' -m git.fd.io/govpp.git) + $ cd $GOVPP_DIR && go build -o /some/bin/dir ./cmd/binapi-generator + $ # instead of make gen-binapi-from-code you can rewrite the code to target + $ # your version ./binapi-generator diff --git a/docs/gettingstarted/developers/index.rst b/docs/gettingstarted/developers/index.rst index 378b9fb83c0..04d704319b2 100644 --- a/docs/gettingstarted/developers/index.rst +++ b/docs/gettingstarted/developers/index.rst @@ -22,6 +22,7 @@ The Developers section covers the following areas: running_vpp gdb_examples add_plugin + add_plugin_goapi gitreview softwarearchitecture infrastructure @@ -42,3 +43,4 @@ The Developers section covers the following areas: quic_plugin cross_compile_macos.rst cnat + VPPAPI.md |