Emacs-lisp scripts to generate complete vpp plugins

Change-Id: Id71147a8d5e30aadfb90dc10ea9468cf36ef23a8
Signed-off-by: Dave Barach <dave@barachs.net>
This commit is contained in:
Dave Barach
2016-01-04 15:27:42 -05:00
parent 7d08f5635d
commit b852bfa18b
12 changed files with 1271 additions and 4 deletions

View File

@ -0,0 +1,86 @@
How to construct a complete plugin using the emacs skeletons
0. Install open-vpp, including the development package.
1. Load emacs skeletons
M-x find-file all-skel.el
M-x eval-buffer
2. Pick a single-word, lower-case name for your plugin. For example: macswap.
Hereafter, we'll refer to the selected name as <plugin-name>.
3. Generate the entire plugin:
M-x make-plugin
Plugin-name: <plugin-name>
Or, generate each file individually:
3. Create the required directories, e.g. under .../vpp
$ mkdir -p <plugin-name>-plugin/<plugin-name>
4. Create <plugin-name>-plugin/{configure.ac,Makefile.am}
M-x find-file <plugin-name>-plugin/configure.ac
M-x plugin-configure-skel
M-x find-file <plugin-name>-plugin/Makefile.am
M-x plugin-makefile.skel
5. Create the api skeleton
M-x find-file <plugin-name>-plugin/<plugin-name>/<plugin-name>.api
M-x plugin-api-skel
6. Create the api message enumeration header file
M-x find-file <plugin-name>-plugin/<plugin-name>/<plugin-name>_msg_enum.h
M-x plugin-msg-enum-skel
7. Create the "all-api" header file
M-x find-file <plugin-name>-plugin/<plugin-name>/<plugin-name>_all_api_h.h
M-x plugin-all-apih-skel
8. Create the main data structure definition header file
M-x find-file <plugin-name>-plugin/<plugin-name>/<plugin-name>.h
M-x plugin-h-skel
9. Create the plugin main C file
M-x find-file <plugin-name>-plugin/<plugin-name>/<plugin-name>.c
M-x plugin-main-skel
10. Create the vpp-api-test plugin main C file
M-x find-file <plugin-name>-plugin/<plugin-name>/<plugin-name>_test.c
M-x plugin-test-skel
11. Create the data plane packet processing node
M-x find-file <plugin-name>-plugin/<plugin-name>/node.c
M-x plugin-node-skel
12. Process autotools input files
$ cd <plugin-name>-plugin
$ autoreconf -i -f
13. Build the plugin skeleton
$ mkdir build
$ cd build
$ ../configure
$ make
$ sudo make install

View File

@ -1,11 +1,37 @@
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;; plugin all-in-1 program
(load-file "./plugin.el")
;; list of clib / vlib / vnet / vpp skeleton files
(load-file "./cli-cmd-skel.el")
(load-file "./pipe-skel.el")
(load-file "./config-skel.el")
(load-file "./dual-loop-skel.el")
(load-file "./periodic-skel.el")
(load-file "./config-skel.el")
(load-file "./pipe-skel.el")
(load-file "./plugin-all-apih-skel.el")
(load-file "./plugin-api-skel.el")
(load-file "./plugin-configure-skel.el")
(load-file "./plugin-h-skel.el")
(load-file "./plugin-main-skel.el")
(load-file "./plugin-makefile-skel.el")
(load-file "./plugin-msg-enum-skel.el")
(load-file "./plugin-node-skel.el")
(load-file "./plugin-test-skel.el")
(load-file "./tunnel-c-skel.el")
(load-file "./tunnel-h-skel.el")
(load-file "./tunnel-encap-skel.el")
(load-file "./tunnel-decap-skel.el")
(load-file "./tunnel-encap-skel.el")
(load-file "./tunnel-h-skel.el")

View File

@ -0,0 +1,43 @@
;;; plugin-all-apih-skel.el - vpp engine plug-in "all-apih.c" skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-all-apih-skel
"Insert a plug-in 'all_api_h.h' skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
/*
* " plugin-name "_all_api_h.h - skeleton vpp engine plug-in api #include file
*
* Copyright (c) <current-year> <your-organization>
* Licensed under the Apache License, Version 2.0 (the \"License\");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an \"AS IS\" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Include the generated file, see BUILT_SOURCES in Makefile.am */
#include <" plugin-name "/" plugin-name ".api.h>
")

View File

@ -0,0 +1,48 @@
;;; plugin-api-skel.el - vpp engine plug-in "all-apih.c" skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-api-skel
"Insert a plug-in '<name>.api' skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
/* Define a simple enable-disable binary API to control the feature */
define " plugin-name "_enable_disable {
/* Client identifier, set from api_main.my_client_index */
u32 client_index;
/* Arbitrary context, so client can match reply to request */
u32 context;
/* Enable / disable the feature */
u8 enable_disable;
/* Interface handle */
u32 sw_if_index;
};
define " plugin-name "_enable_disable_reply {
/* From the request */
u32 context;
/* Return value, zero means all OK */
i32 retval;
};
")

View File

@ -0,0 +1,42 @@
;;; plugin-configure-skel.el - vpp engine plug-in "main.c" skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-configure-skel
"Insert a plug-in 'configure.ac' skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
AC_INIT(" plugin-name "_plugin, 1.0)
AM_INIT_AUTOMAKE
AC_PROG_LIBTOOL
AM_PROG_AS
AC_PROG_CC
AM_PROG_CC_C_O
AC_ARG_WITH(plugin-toolkit,
AC_HELP_STRING([--with-plugin-toolkit],
[build using the vpp toolkit]),
[with_plugin_toolkit=${prefix}/include],
[with_plugin_toolkit=.])
AC_SUBST(TOOLKIT_INCLUDE,[${with_plugin_toolkit}])
AM_CONDITIONAL(WITH_PLUGIN_TOOLKIT, test \"$with_plugin_toolkit\" != \".\")
AC_OUTPUT([Makefile])
")

View File

@ -0,0 +1,66 @@
;;; plugin-h-skel.el - vpp engine plug-in "main.c" skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-h-skel
"Insert a plug-in 'main.c' skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
/*
* " plugin-name ".h - skeleton vpp engine plug-in header file
*
* Copyright (c) <current-year> <your-organization>
* Licensed under the Apache License, Version 2.0 (the \"License\");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an \"AS IS\" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __included_" plugin-name "_h__
#define __included_" plugin-name "_h__
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vnet/ethernet/ethernet.h>
#include <vppinfra/hash.h>
#include <vppinfra/error.h>
typedef struct {
/* API message ID base */
u16 msg_id_base;
/* convenience */
vlib_main_t * vlib_main;
vnet_main_t * vnet_main;
ethernet_main_t * ethernet_main;
} " plugin-name "_main_t;
" plugin-name "_main_t " plugin-name "_main;
vlib_node_registration_t " plugin-name "_node;
#endif /* __included_" plugin-name "_h__ */
")

View File

@ -0,0 +1,262 @@
;;; plugin-main-skel.el - vpp engine plug-in "main.c" skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-main-skel
"Insert a plug-in 'main.c' skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
/*
* " plugin-name ".c - skeleton vpp engine plug-in
*
* Copyright (c) <current-year> <your-organization>
* Licensed under the Apache License, Version 2.0 (the \"License\");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an \"AS IS\" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <" plugin-name "/" plugin-name ".h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vlibsocket/api.h>
/* define message IDs */
#include <" plugin-name "/" plugin-name "_msg_enum.h>
/* define message structures */
#define vl_typedefs
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_typedefs
/* define generated endian-swappers */
#define vl_endianfun
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_endianfun
/* instantiate all the print functions we know about */
#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
#define vl_printfun
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_printfun
/* Get the API version number */
#define vl_api_version(n,v) static u32 api_version=(v);
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_api_version
/*
* A handy macro to set up a message reply.
* Assumes that the following variables are available:
* mp - pointer to request message
* rmp - pointer to reply message type
* rv - return value
*/
#define REPLY_MACRO(t) \\
do { \\
unix_shared_memory_queue_t * q = \\
vl_api_client_index_to_input_queue (mp->client_index); \\
if (!q) \\
return; \\
\\
rmp = vl_msg_api_alloc (sizeof (*rmp)); \\
rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base); \\
rmp->context = mp->context; \\
rmp->retval = ntohl(rv); \\
\\
vl_msg_api_send_shmem (q, (u8 *)&rmp); \\
} while(0);
/* List of message types that this plugin understands */
#define foreach_" plugin-name "_plugin_api_msg \\
_(" PLUGIN-NAME "_ENABLE_DISABLE, " plugin-name "_enable_disable)
/*
* This routine exists to convince the vlib plugin framework that
* we haven't accidentally copied a random .dll into the plugin directory.
*
* Also collects global variable pointers passed from the vpp engine
*/
clib_error_t *
vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
int from_early_init)
{
" plugin-name "_main_t * sm = &" plugin-name "_main;
clib_error_t * error = 0;
sm->vlib_main = vm;
sm->vnet_main = h->vnet_main;
sm->ethernet_main = h->ethernet_main;
return error;
}
/* Action function shared between message handler and debug CLI */
int " plugin-name "_enable_disable (" plugin-name "_main_t * sm, u32 sw_if_index,
int enable_disable)
{
vnet_sw_interface_t * sw;
int rv;
u32 node_index = enable_disable ? " plugin-name "_node.index : ~0;
/* Utterly wrong? */
if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
sw_if_index))
return VNET_API_ERROR_INVALID_SW_IF_INDEX;
/* Not a physical port? */
sw = vnet_get_sw_interface (sm->vnet_main, sw_if_index);
if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
return VNET_API_ERROR_INVALID_SW_IF_INDEX;
/*
* Redirect pkts from the driver to the macswap node.
* Returns VNET_API_ERROR_UNIMPLEMENTED if the h/w driver
* doesn't implement the API.
*
* Node_index = ~0 => shut off redirection
*/
rv = vnet_hw_interface_rx_redirect_to_node (sm->vnet_main, sw_if_index,
node_index);
return rv;
}
static clib_error_t *
" plugin-name "_enable_disable_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
" plugin-name "_main_t * sm = &" plugin-name "_main;
u32 sw_if_index = ~0;
int enable_disable = 1;
int rv;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
if (unformat (input, \"disable\"))
enable_disable = 0;
else if (unformat (input, \"%U\", unformat_vnet_sw_interface,
sm->vnet_main, &sw_if_index))
;
else
break;
}
if (sw_if_index == ~0)
return clib_error_return (0, \"Please specify an interface...\");
rv = " plugin-name "_enable_disable (sm, sw_if_index, enable_disable);
switch(rv) {
case 0:
break;
case VNET_API_ERROR_INVALID_SW_IF_INDEX:
return clib_error_return
(0, \"Invalid interface, only works on physical ports\");
break;
case VNET_API_ERROR_UNIMPLEMENTED:
return clib_error_return (0, \"Device driver doesn't support redirection\");
break;
default:
return clib_error_return (0, \"" plugin-name "_enable_disable returned %d\",
rv);
}
return 0;
}
VLIB_CLI_COMMAND (" plugin-name "_enable_disable_command, static) = {
.path = \"" plugin-name " enable-disable\",
.short_help =
\"" plugin-name " enable-disable <interface-name> [disable]\",
.function = " plugin-name "_enable_disable_command_fn,
};
/* API message handler */
static void vl_api_" plugin-name "_enable_disable_t_handler
(vl_api_" plugin-name "_enable_disable_t * mp)
{
vl_api_" plugin-name "_enable_disable_reply_t * rmp;
" plugin-name "_main_t * sm = &" plugin-name "_main;
int rv;
rv = " plugin-name "_enable_disable (sm, ntohl(mp->sw_if_index),
(int) (mp->enable_disable));
REPLY_MACRO(VL_API_" PLUGIN-NAME "_ENABLE_DISABLE_REPLY);
}
/* Set up the API message handling tables */
static clib_error_t *
" plugin-name "_plugin_api_hookup (vlib_main_t *vm)
{
" plugin-name "_main_t * sm = &" plugin-name "_main;
#define _(N,n) \\
vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \\
#n, \\
vl_api_##n##_t_handler, \\
vl_noop_handler, \\
vl_api_##n##_t_endian, \\
vl_api_##n##_t_print, \\
sizeof(vl_api_##n##_t), 1);
foreach_" plugin-name "_plugin_api_msg;
#undef _
return 0;
}
static clib_error_t * " plugin-name "_init (vlib_main_t * vm)
{
" plugin-name "_main_t * sm = &" plugin-name "_main;
clib_error_t * error = 0;
u8 * name;
name = format (0, \"" plugin-name "_%08x%c\", api_version, 0);
/* Ask for a correctly-sized block of API message decode slots */
sm->msg_id_base = vl_msg_api_get_msg_ids
((char *) name, VL_MSG_FIRST_AVAILABLE);
error = " plugin-name "_plugin_api_hookup (vm);
vec_free(name);
return error;
}
VLIB_INIT_FUNCTION (" plugin-name "_init);
")

View File

@ -0,0 +1,74 @@
;;; plugin-makefile-skel.el - vpp engine plug-in "main.c" skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-makefile-skel
"Insert a plug-in 'Makefile.am' skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
# Copyright (c) <current-year> <your-organization>
# Licensed under the Apache License, Version 2.0 (the \"License\");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an \"AS IS\" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
AUTOMAKE_OPTIONS = foreign subdir-objects
AM_CFLAGS = -Wall -I@TOOLKIT_INCLUDE@
lib_LTLIBRARIES = " plugin-name "_plugin.la " plugin-name "_test_plugin.la
" plugin-name "_plugin_la_SOURCES = " plugin-name "/" plugin-name ".c \\
" plugin-name "/node.c \\
" plugin-name "/" plugin-name "_plugin.api.h
" plugin-name "_plugin_la_LDFLAGS = -module
BUILT_SOURCES = " plugin-name "/" plugin-name ".api.h
SUFFIXES = .api.h .api
%.api.h: %.api
mkdir -p `dirname $@` ; \\
$(CC) $(CPPFLAGS) -E -P -C -x c $^ \\
| vppapigen --input - --output $@ --show-name $@
nobase_include_HEADERS = \\
" plugin-name "/" plugin-name "_all_api_h.h \\
" plugin-name "/" plugin-name "_msg_enum.h \\
" plugin-name "/" plugin-name ".api.h
" plugin-name "_test_plugin_la_SOURCES = \\
" plugin-name "/" plugin-name "_test.c " plugin-name "/" plugin-name "_plugin.api.h
" plugin-name "_test_plugin_la_LDFLAGS = -module
if WITH_PLUGIN_TOOLKIT
install-data-hook:
mkdir /usr/lib/vpp_plugins || true
mkdir /usr/lib/vpp_api_test_plugins || true
cp $(prefix)/lib/" plugin-name "_plugin.so.*.*.* /usr/lib/vpp_plugins
cp $(prefix)/lib/" plugin-name "_test_plugin.so.*.*.* \\
/usr/lib/vpp_api_test_plugins
endif
")

View File

@ -0,0 +1,55 @@
;;; plugin-msg-enum-skel.el - vpp engine plug-in message enum skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-msg-enum-skel
"Insert a plug-in message enumeration skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
/*
* " plugin-name "_msg_enum.h - skeleton vpp engine plug-in message enumeration
*
* Copyright (c) <current-year> <your-organization>
* Licensed under the Apache License, Version 2.0 (the \"License\");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an \"AS IS\" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef included_" plugin-name "_msg_enum_h
#define included_" plugin-name "_msg_enum_h
#include <vppinfra/byte_order.h>
#define vl_msg_id(n,h) n,
typedef enum {
#include <" plugin-name "/" plugin-name "_all_api_h.h>
/* We'll want to know how many messages IDs we need... */
VL_MSG_FIRST_AVAILABLE,
} vl_msg_id_t;
#undef vl_msg_id
#endif /* included_" plugin-name "_msg_enum_h */
")

View File

@ -0,0 +1,295 @@
;;; plugin-node-skel.el - vpp engine plug-in "node.c" skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-node-skel
"Insert a plug-in 'node.c' skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
/*
* node.c - skeleton vpp engine plug-in dual-loop node skeleton
*
* Copyright (c) <current-year> <your-organization>
* Licensed under the Apache License, Version 2.0 (the \"License\");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an \"AS IS\" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/pg/pg.h>
#include <vppinfra/error.h>
#include <" plugin-name "/" plugin-name ".h>
typedef struct {
u32 next_index;
u32 sw_if_index;
} " plugin-name "_trace_t;
/* packet trace format function */
static u8 * format_" plugin-name "_trace (u8 * s, va_list * args)
{
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
" plugin-name "_trace_t * t = va_arg (*args, " plugin-name "_trace_t *);
s = format (s, \"" PLUGIN-NAME ": sw_if_index %d, next index %d\",
t->sw_if_index, t->next_index);
return s;
}
vlib_node_registration_t " plugin-name "_node;
#define foreach_" plugin-name "_error \\
_(SWAPPED, \"Mac swap packets processed\")
typedef enum {
#define _(sym,str) " PLUGIN-NAME "_ERROR_##sym,
foreach_" plugin-name "_error
#undef _
" PLUGIN-NAME "_N_ERROR,
} " plugin-name "_error_t;
static char * " plugin-name "_error_strings[] = {
#define _(sym,string) string,
foreach_" plugin-name "_error
#undef _
};
typedef enum {
" PLUGIN-NAME "_NEXT_INTERFACE_OUTPUT,
" PLUGIN-NAME "_N_NEXT,
} " plugin-name "_next_t;
#define foreach_mac_address_offset \\
_(0) \\
_(1) \\
_(2) \\
_(3) \\
_(4) \\
_(5)
static uword
" plugin-name "_node_fn (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
u32 n_left_from, * from, * to_next;
" plugin-name "_next_t next_index;
u32 pkts_swapped = 0;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
while (n_left_from > 0)
{
u32 n_left_to_next;
vlib_get_next_frame (vm, node, next_index,
to_next, n_left_to_next);
while (n_left_from >= 4 && n_left_to_next >= 2)
{
u32 next0 = " PLUGIN-NAME "_NEXT_INTERFACE_OUTPUT;
u32 next1 = " PLUGIN-NAME "_NEXT_INTERFACE_OUTPUT;
u32 sw_if_index0, sw_if_index1;
u8 tmp0[6], tmp1[6];
ethernet_header_t *en0, *en1;
u32 bi0, bi1;
vlib_buffer_t * b0, * b1;
/* Prefetch next iteration. */
{
vlib_buffer_t * p2, * p3;
p2 = vlib_get_buffer (vm, from[2]);
p3 = vlib_get_buffer (vm, from[3]);
vlib_prefetch_buffer_header (p2, LOAD);
vlib_prefetch_buffer_header (p3, LOAD);
CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
}
/* speculatively enqueue b0 and b1 to the current next frame */
to_next[0] = bi0 = from[0];
to_next[1] = bi1 = from[1];
from += 2;
to_next += 2;
n_left_from -= 2;
n_left_to_next -= 2;
b0 = vlib_get_buffer (vm, bi0);
b1 = vlib_get_buffer (vm, bi1);
ASSERT (b0->current_data == 0);
ASSERT (b1->current_data == 0);
en0 = vlib_buffer_get_current (b0);
en1 = vlib_buffer_get_current (b1);
/* This is not the fastest way to swap src + dst mac addresses */
#define _(a) tmp0[a] = en0->src_address[a];
foreach_mac_address_offset;
#undef _
#define _(a) en0->src_address[a] = en0->dst_address[a];
foreach_mac_address_offset;
#undef _
#define _(a) en0->dst_address[a] = tmp0[a];
foreach_mac_address_offset;
#undef _
#define _(a) tmp1[a] = en1->src_address[a];
foreach_mac_address_offset;
#undef _
#define _(a) en1->src_address[a] = en1->dst_address[a];
foreach_mac_address_offset;
#undef _
#define _(a) en1->dst_address[a] = tmp1[a];
foreach_mac_address_offset;
#undef _
sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
/* Send pkt back out the RX interface */
vnet_buffer(b0)->sw_if_index[VLIB_TX] = sw_if_index0;
vnet_buffer(b1)->sw_if_index[VLIB_TX] = sw_if_index1;
pkts_swapped += 2;
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
{
if (b0->flags & VLIB_BUFFER_IS_TRACED)
{
" plugin-name "_trace_t *t =
vlib_add_trace (vm, node, b0, sizeof (*t));
t->sw_if_index = sw_if_index0;
t->next_index = next0;
}
if (b1->flags & VLIB_BUFFER_IS_TRACED)
{
" plugin-name "_trace_t *t =
vlib_add_trace (vm, node, b1, sizeof (*t));
t->sw_if_index = sw_if_index1;
t->next_index = next1;
}
}
/* verify speculative enqueues, maybe switch current next frame */
vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
to_next, n_left_to_next,
bi0, bi1, next0, next1);
}
while (n_left_from > 0 && n_left_to_next > 0)
{
u32 bi0;
vlib_buffer_t * b0;
u32 next0 = " PLUGIN-NAME "_NEXT_INTERFACE_OUTPUT;
u32 sw_if_index0;
u8 tmp0[6];
ethernet_header_t *en0;
/* speculatively enqueue b0 to the current next frame */
bi0 = from[0];
to_next[0] = bi0;
from += 1;
to_next += 1;
n_left_from -= 1;
n_left_to_next -= 1;
b0 = vlib_get_buffer (vm, bi0);
/*
* Direct from the driver, we should be at offset 0
* aka at &b0->data[0]
*/
ASSERT (b0->current_data == 0);
en0 = vlib_buffer_get_current (b0);
/* This is not the fastest way to swap src + dst mac addresses */
#define _(a) tmp0[a] = en0->src_address[a];
foreach_mac_address_offset;
#undef _
#define _(a) en0->src_address[a] = en0->dst_address[a];
foreach_mac_address_offset;
#undef _
#define _(a) en0->dst_address[a] = tmp0[a];
foreach_mac_address_offset;
#undef _
sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
/* Send pkt back out the RX interface */
vnet_buffer(b0)->sw_if_index[VLIB_TX] = sw_if_index0;
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
&& (b0->flags & VLIB_BUFFER_IS_TRACED))) {
" plugin-name "_trace_t *t =
vlib_add_trace (vm, node, b0, sizeof (*t));
t->sw_if_index = sw_if_index0;
t->next_index = next0;
}
pkts_swapped += 1;
/* verify speculative enqueue, maybe switch current next frame */
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next,
bi0, next0);
}
vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}
vlib_node_increment_counter (vm, " plugin-name "_node.index,
" PLUGIN-NAME "_ERROR_SWAPPED, pkts_swapped);
return frame->n_vectors;
}
VLIB_REGISTER_NODE (" plugin-name "_node) = {
.function = " plugin-name "_node_fn,
.name = \"" plugin-name "\",
.vector_size = sizeof (u32),
.format_trace = format_" plugin-name "_trace,
.type = VLIB_NODE_TYPE_INTERNAL,
.n_errors = ARRAY_LEN(" plugin-name "_error_strings),
.error_strings = " plugin-name "_error_strings,
.n_next_nodes = " PLUGIN-NAME "_N_NEXT,
/* edit / add dispositions here */
.next_nodes = {
[" PLUGIN-NAME "_NEXT_INTERFACE_OUTPUT] = \"interface-output\",
},
};
")

View File

@ -0,0 +1,235 @@
;;; plugin-test-skel.el - vpp-api-test plug-in skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(require 'skeleton)
(define-skeleton plugin-test-skel
"Insert a plug-in vpp-api-test skeleton "
nil
'(if (not (boundp 'plugin-name))
(setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
"
/*
* " plugin-name ".c - skeleton vpp-api-test plug-in
*
* Copyright (c) <current-year> <your-organization>
* Licensed under the Apache License, Version 2.0 (the \"License\");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an \"AS IS\" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vlibsocket/api.h>
#include <vppinfra/error.h>
uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
/* Declare message IDs */
#include <" plugin-name "/" plugin-name "_msg_enum.h>
/* define message structures */
#define vl_typedefs
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_typedefs
/* declare message handlers for each api */
#define vl_endianfun /* define message structures */
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_endianfun
/* instantiate all the print functions we know about */
#define vl_print(handle, ...)
#define vl_printfun
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_printfun
/* Get the API version number. */
#define vl_api_version(n,v) static u32 api_version=(v);
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_api_version
typedef struct {
/* API message ID base */
u16 msg_id_base;
vat_main_t *vat_main;
} " plugin-name "_test_main_t;
" plugin-name "_test_main_t " plugin-name "_test_main;
#define foreach_standard_reply_retval_handler \\
_(" plugin-name "_enable_disable_reply)
#define _(n) \\
static void vl_api_##n##_t_handler \\
(vl_api_##n##_t * mp) \\
{ \\
vat_main_t * vam = " plugin-name "_test_main.vat_main; \\
i32 retval = ntohl(mp->retval); \\
if (vam->async_mode) { \\
vam->async_errors += (retval < 0); \\
} else { \\
vam->retval = retval; \\
vam->result_ready = 1; \\
} \\
}
foreach_standard_reply_retval_handler;
#undef _
/*
* Table of message reply handlers, must include boilerplate handlers
* we just generated
*/
#define foreach_vpe_api_reply_msg \\
_(" PLUGIN-NAME "_ENABLE_DISABLE_REPLY, " plugin-name "_enable_disable_reply)
/* M: construct, but don't yet send a message */
#define M(T,t) \\
do { \\
vam->result_ready = 0; \\
mp = vl_msg_api_alloc(sizeof(*mp)); \\
memset (mp, 0, sizeof (*mp)); \\
mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \\
mp->client_index = vam->my_client_index; \\
} while(0);
#define M2(T,t,n) \\
do { \\
vam->result_ready = 0; \\
mp = vl_msg_api_alloc(sizeof(*mp)+(n)); \\
memset (mp, 0, sizeof (*mp)); \\
mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \\
mp->client_index = vam->my_client_index; \\
} while(0);
/* S: send a message */
#define S (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp))
/* W: wait for results, with timeout */
#define W \\
do { \\
timeout = vat_time_now (vam) + 1.0; \\
\\
while (vat_time_now (vam) < timeout) { \\
if (vam->result_ready == 1) { \\
return (vam->retval); \\
} \\
} \\
return -99; \\
} while(0);
static int api_" plugin-name "_enable_disable (vat_main_t * vam)
{
" plugin-name "_test_main_t * sm = &" plugin-name "_test_main;
unformat_input_t * i = vam->input;
f64 timeout;
int enable_disable = 1;
u32 sw_if_index = ~0;
vl_api_" plugin-name "_enable_disable_t * mp;
/* Parse args required to build the message */
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) {
if (unformat (i, \"%U\", unformat_sw_if_index, vam, &sw_if_index))
;
else if (unformat (i, \"sw_if_index %d\", &sw_if_index))
;
else if (unformat (i, \"disable\"))
enable_disable = 0;
else
break;
}
if (sw_if_index == ~0) {
errmsg (\"missing interface name / explicit sw_if_index number \\n\");
return -99;
}
/* Construct the API message */
M(" PLUGIN-NAME "_ENABLE_DISABLE, " plugin-name "_enable_disable);
mp->sw_if_index = ntohl (sw_if_index);
mp->enable_disable = enable_disable;
/* send it... */
S;
/* Wait for a reply... */
W;
}
/*
* List of messages that the api test plugin sends,
* and that the data plane plugin processes
*/
#define foreach_vpe_api_msg \\
_(" plugin-name "_enable_disable, \"<intfc> [disable]\")
void vat_api_hookup (vat_main_t *vam)
{
" plugin-name "_test_main_t * sm = &" plugin-name "_test_main;
/* Hook up handlers for replies from the data plane plug-in */
#define _(N,n) \\
vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \\
#n, \\
vl_api_##n##_t_handler, \\
vl_noop_handler, \\
vl_api_##n##_t_endian, \\
vl_api_##n##_t_print, \\
sizeof(vl_api_##n##_t), 1);
foreach_vpe_api_reply_msg;
#undef _
/* API messages we can send */
#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
foreach_vpe_api_msg;
#undef _
/* Help strings */
#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
foreach_vpe_api_msg;
#undef _
}
clib_error_t * vat_plugin_register (vat_main_t *vam)
{
" plugin-name "_test_main_t * sm = &" plugin-name "_test_main;
u8 * name;
sm->vat_main = vam;
/* Ask the vpp engine for the first assigned message-id */
name = format (0, \"" plugin-name "_%08x%c\", api_version, 0);
sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
if (sm->msg_id_base != (u16) ~0)
vat_api_hookup (vam);
vec_free(name);
return 0;
}
")

View File

@ -0,0 +1,35 @@
(defun make-plugin ()
"Create a plugin"
(interactive)
(save-excursion
(let (cd-args cmd-args start-dir)
(setq start-dir default-directory)
(makunbound 'plugin-name)
(makunbound 'PLUGIN-NAME)
(setq plugin-name (read-string "Plugin name: "))
(setq PLUGIN-NAME (upcase plugin-name))
(setq cmd-args (concat "mkdir -p " plugin-name "-plugin/" plugin-name))
(shell-command cmd-args)
(setq cd-args (concat start-dir plugin-name "-plugin"))
(setq default-directory cd-args)
(find-file "Makefile.am")
(plugin-makefile-skel)
(find-file "configure.ac")
(plugin-configure-skel)
(setq default-directory (concat cd-args "/" plugin-name))
(find-file (concat plugin-name ".api"))
(plugin-api-skel)
(find-file (concat plugin-name "_all_api_h.h"))
(plugin-all-apih-skel)
(find-file (concat plugin-name ".h"))
(plugin-h-skel)
(find-file (concat plugin-name ".c"))
(plugin-main-skel)
(find-file (concat plugin-name "_msg_enum.h"))
(plugin-msg-enum-skel)
(find-file "node.c")
(plugin-node-skel)
(find-file (concat plugin-name "_test.c"))
(plugin-test-skel)
(cd start-dir))))