zoukankan      html  css  js  c++  java
  • Vector Packet Processing 101: VPP Plugins & Binary API

    In the first part of our new series, we will be building our first VPP platform plug-in, using basic examples. We will start with a first-dive into plugin creation and finish with introducing VAPI into this configuration.

    If you do not know what VPP is, please visit our introductory post regarding VPP and why you should consider using it.

    Table of contents:

    • How to write a new VPP Plugin
      • 1. Preparing your new VPP plugin
      • 2. Building & running your new plugin
    • How to create new API messages
    • How to call the binary API
      • Additional C/C++ Examples

    How to write a new VPP Plugin

    The principle of VPP is, that you can plug in a new graph node, adapt it to your network purposes and run it right off the bat. Including a new plugin does not mean, you need to change your core-code with each new addition. Plugins can be either included in the processing graph, or they can be built outside the source tree and become an individual component in your build.

    Furthermore, this separation of plugins makes crashes a matter of a simple process restart, which does not require your whole build to be restarted because of one plugin failure.

    1. Preparing your new VPP plugin

    The easiest way how to create a new plugin that integrates with VPP is to reuse the sample code at “src/examples/sample-plugin”. The sample code implements a trivial “macswap” algorithm that demonstrates the plugins run-time integration with the VPP graph hierarchy, API and CLI.

    • To create a new plugin based on the sample plugin, copy and rename the sample plugin directory

    cp -r src/examples/sample-plugin/sample src/plugins/newplugin
     
    #replace 'sample' with 'newplugin'. as always, take extra care with sed!
    cd src/plugins/newplugin
    fgrep -il "SAMPLE" * | xargs sed -i.bak 's/SAMPLE/NEWPLUGIN/g'
    fgrep -il "sample" * | xargs sed -i.bak 's/sample/newplugin/g'
    rm *.bak*
    rename 's/sample/newplugin/g' *

    There are the are following files:

      • node.c – implements functionality of this graph node (swap source and destination address) -update according to your requirements.
      • newplugin.api – defines plugin’s API, see below
      • newplugin.c, newplugin_test.c – implements plugin functionality, API handlers, etc.
    • Update CMakeLists.txt in newplugin directory to reflect your requirements:
    add_vpp_plugin(newplugin
    SOURCES
    node.c
    newplugin.c
     
    MULTIARCH_SOURCES
    node.c
     
    API_FILES
    newplugin.api
     
    API_TEST_SOURCES
    newplugin_test.c
     
    COMPONENT vpp-plugin-newplugin
    )
    • Update sample.c to hook your plugin into the VPP graph properly:
    VNET_FEATURE_INIT (newplugin, static) =
    {
    .arc_name = "device-input",
    .node_name = "newplugin",
    .runs_before = VNET_FEATURES ("ethernet-input"),
    };
    • Update newplugin.api to define your API requests/replies. For more details see “API message creation” below.
    • Update node.c to do required actions on input frames, such as handling incoming packets and more

    2. Building & running your new plugin

    • Build vpp and your plugin. New plugins will be built and integrated automatically, based on the CMakeLists.txt
    make rebuild
    • (Optional) Build & install vpp packages for your platform
    make pkg-deb
    cd build-root
    sudo dpkg -i *.deb
    • The binary-api header files you can include later are located in build-root/build-vpp_debug-native/vpp/vpp-api/vapi
      •  If vpp is installed, they are located in /usr/include/vapi
    • Run vpp and check whether your plugin is loaded (newplugin has to be loaded and listed using the show plugin CLI command)
    make run
    ...
    load_one_plugin:189: Loaded plugin: nat_plugin.so (Network Address Translation)
    load_one_plugin:189: Loaded plugin: newplugin_plugin.so (Sample VPP Plugin)
    load_one_plugin:189: Loaded plugin: nsim_plugin.so (network delay simulator plugin)
    ...
    DBGvpp# show plugins
    ...
    Plugin Version Description
    1. ioam_plugin.so 19.01-rc0~144-g0c2319f Inbound OAM
    ...
    x. newplugin_plugin.so 1.0 Sample VPP Plugin
    ...

    How to create new API messages

    API messages are defined in *.api files – see src/vnet/devices/af_packet.api, src/vnet/ip/ip.api, etc. These API files are used to generate corresponding message handlers. There are two types of API messages – non-blocking and blocking. These messages are used to communicate with the VPP Engine to configure and modify data path processing.

    Non-blocking messages use one request and one reply message. Message replies can be auto-generated, or defined manually. Each request contains two mandatory fields – “client-index” and “context“, and each reply message contains mandatory fields – “context” and “retval“.

    • API message with auto-generated reply

    autoreply define ip_table_add_del
    {
    u32 client_index;
    u32 context;
    u32 table_id;
    ...
    };
    • API message with manually defined reply
    define ip_neighbor_add_del
    {
    u32 client_index;
    u32 context;
    u32 sw_if_index;
    ...
    };
    define ip_neighbor_add_del_reply
    {
    u32 context;
    i32 retval;
    u32 stats_index;
    ...
    };

    Blocking messages use one request and series of replies defined in *.api file. Each request contains two mandatory fields – “client-index” and “context“, and each reply message contains mandatory field – “context“.

    • Blocking message is defined using two structs – *-dump and *_details

    define ip_fib_dump
    {
    u32 client_index;
    u32 context;
    ...
    };
    define ip_fib_details
    {
    u32 context;
    ...
    };

    Once you define a message in an API file, you have to define and implement the corresponding handlers for given request/reply message. These handlers are defined in one of component/plugin file and they use predefined naming – vl_api_…_t_handler – for each API message.

    Here is an example for existing API messages (you can check it in src/vnet/ip component):

    #define foreach_ip_api_msg
    _(IP_FIB_DUMP, ip_fib_dump)
    _(IP_NEIGHBOR_ADD_DEL, ip_neighbor_add_del)
    ...
    static void vl_api_ip_neighbor_add_del_t_handler (vl_api_ip_neighbor_add_del_t * mp, vlib_main_t * vm)
    {
    ...
    REPLY_MACRO2 (VL_API_IP_NEIGHBOR_ADD_DEL_REPLY,
    ...
    static void vl_api_ip_fib_dump_t_handler (vl_api_ip_fib_dump_t * mp)
    {
    ...
    send_ip_fib_details (am, reg, fib_table, pfx, api_rpaths, mp->context);
    ...

    Request and reply handlers are usually defined in api_format.c (or in plugin). Request uses a predefined naming – api_… for each API message and you have to also define help for each API message :

    static int api_ip_neighbor_add_del (vat_main_t * vam)
    {
    ...
    /* Construct the API message */
    M (IP_NEIGHBOR_ADD_DEL, mp);
    /* send it... */
    S (mp);
    /* Wait for a reply, return good/bad news */
    W (ret);
    return ret;
    }
    static int api_ip_fib_dump (vat_main_t * vam)
    {
    ...
    M (IP_FIB_DUMP, mp);
    S (mp);
    /* Use a control ping for synchronization */
    MPING (CONTROL_PING, mp_ping);
    S (mp_ping);
    W (ret);
    return ret;
    }
    #define foreach_vpe_api_msg
    ...
    _(ip_neighbor_add_del,
    "(<intfc> | sw_if_index <id>) dst <ip46-address> "
    "[mac <mac-addr>] [vrf <vrf-id>] [is_static] [del]")
    ...
    _(ip_fib_dump, "")
    ...

    Replies can be auto-generated or manually defined.

    • auto-generated reply using define foreach_standard_reply_retval_handler, with predefined naming
    • manually defined reply with details

    How to call the binary API

    In order to call the binary API, we will introduce VAPI to our configuration.

    VAPI is the high-level C/C++ binary API. Please refer to src/vpp-api/vapi/vapi_doc.md for details.

    VAPI’s multitude of advantages include:

    • All headers in a single place – /usr/include/vapi => simplifies code generation
    • Hidden internals – one no longer has to care about message IDs, byte-order conversion
    • Easier binapi calls – passing user provided context between callbacks

    We can use the following C++ code to call our new plugins’s binary API.

    #include <cstdlib>
    #include <iostream>
    #include <cassert>
     
    //necessary includes & macros
    #include <vapi/vapi.hpp>
    #include <vapi/vpe.api.vapi.hpp>
    DEFINE_VAPI_MSG_IDS_VPE_API_JSON
     
    //include the desired modules / plugins
    #include <vapi/newplugin.api.vapi.hpp>
    DEFINE_VAPI_MSG_IDS_NEWPLUGIN_API_JSON
     
    using namespace vapi;
    using namespace std;
     
    //parameters for connecting
    static const char *app_name = "test_client";
    static const char *api_prefix = nullptr;
    static const int max_outstanding_requests = 32;
    static const int response_queue_size = 32;
     
    #define WAIT_FOR_RESPONSE(param, ret)
    do
    {
    ret = con.wait_for_response (param);
    }
    while (ret == VAPI_EAGAIN)
     
    //global connection object
    Connection con;
     
    void die(int exit_code)
    {
    //disconnect & cleanup
    vapi_error_e rv = con.disconnect();
    if (VAPI_OK != rv) {
    fprintf(stderr, "error: (rc:%d)", rv);
    }
     
    exit(exit_code);
    }
     
    int main()
    {
    //connect to VPP
    vapi_error_e rv = con.connect(app_name, api_prefix, max_outstanding_requests, response_queue_size);
     
    if (VAPI_OK != rv) {
    cerr << "error: connecting to vlib";
    return rv;
    }
     
    try
    {
    Newplugin_macswap_enable_disable cl(con);
     
    auto &mp = cl.get_request().get_payload();
     
    mp.enable_disable = true;
    mp.sw_if_index = 5;
     
    auto rv = cl.execute ();
    if (VAPI_OK != rv) {
    throw exception{};
    }
     
    WAIT_FOR_RESPONSE (cl, rv);
    if (VAPI_OK != rv) {
    throw exception{};
    }
     
    //verify the reply
    auto &rp = cl.get_response ().get_payload ();
    if (rp.retval != 0) {
    throw exception{};
    }
    }
    catch (...)
    {
    cerr << "Newplugin_macswap_enable_disable ERROR" << endl;
    die(1);
    }
     
    die(0);
    }

    Additional C/C++ Examples

    Furthermore, you are encouraged to try the minimal VAPI example provided in vapi_minimal.zip. This example creates a loopback interface, assigns it an IPv4 address and then prints the address.
    Follow these steps:

    • Install VPP
    • Extract the archive, build & run examples
    unzip vapi_minimal.zip
    mkdir build; cd build
    cmake ..
    make
     
    #c example
    sudo ./vapi_minimal
    #c++ example
    sudo ./vapi_minimal_cpp

    In conclusion, we have:

    • successfully built and ran our first VPP plugin
    • created and called an API message in VPP

    Our next post will introduce and highlight the key reasons, why you should consider Honeycomb/hc2vpp in your VPP build.


    You can contact us at https://pantheon.tech/

    Explore our Pantheon GitHub. Follow us on Twitter.

    Watch our YouTube Channel.

  • 相关阅读:
    centos6.5+mono+nginx跑asp.net
    YYHS-手机信号
    NOIP2017提高组初赛
    BZOJ-4915-简单的数字题
    BZOJ-5055-膜法师(离散化+树状数组)
    YYHS-Super Big Stupid Cross(二分+扫描线+平衡树)
    BZOJ-1008-[HNOI2008]越狱(快速幂)
    BZOJ-1192-[HNOI2006]鬼谷子的钱袋
    POJ-2417-Discrete Logging(BSGS)
    BZOJ-1010-[HNOI2008]玩具装箱toy(斜率优化)
  • 原文地址:https://www.cnblogs.com/dream397/p/12792356.html
Copyright © 2011-2022 走看看