aboutsummaryrefslogtreecommitdiffstats
path: root/src/vpp-api
diff options
context:
space:
mode:
Diffstat (limited to 'src/vpp-api')
-rw-r--r--src/vpp-api/client/client.c2
-rw-r--r--src/vpp-api/client/stat_client.c27
-rw-r--r--src/vpp-api/client/stat_client.h1
-rw-r--r--src/vpp-api/client/test.c2
-rw-r--r--src/vpp-api/python/CMakeLists.txt40
-rw-r--r--src/vpp-api/python/setup.py4
-rw-r--r--src/vpp-api/python/vpp_papi/__init__.py2
-rw-r--r--src/vpp-api/python/vpp_papi/data/memclnt.api.json809
-rw-r--r--src/vpp-api/python/vpp_papi/macaddress.py1
-rw-r--r--src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py6
-rwxr-xr-xsrc/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py28
-rw-r--r--src/vpp-api/python/vpp_papi/vpp_papi.py127
-rw-r--r--src/vpp-api/python/vpp_papi/vpp_serializer.py8
-rwxr-xr-xsrc/vpp-api/python/vpp_papi/vpp_stats.py8
-rw-r--r--src/vpp-api/python/vpp_papi/vpp_transport_socket.py7
-rw-r--r--src/vpp-api/vapi/CMakeLists.txt2
-rw-r--r--src/vpp-api/vapi/fake.api.json2
-rw-r--r--src/vpp-api/vapi/vapi.c946
-rw-r--r--src/vpp-api/vapi/vapi.h71
-rw-r--r--src/vpp-api/vapi/vapi.hpp138
-rwxr-xr-xsrc/vpp-api/vapi/vapi_c_gen.py207
-rw-r--r--src/vpp-api/vapi/vapi_c_test.c118
-rw-r--r--src/vpp-api/vapi/vapi_common.h59
-rwxr-xr-xsrc/vpp-api/vapi/vapi_cpp_gen.py11
-rw-r--r--src/vpp-api/vapi/vapi_cpp_test.cpp76
-rw-r--r--src/vpp-api/vapi/vapi_internal.h14
-rw-r--r--src/vpp-api/vapi/vapi_json_parser.py93
27 files changed, 2420 insertions, 389 deletions
diff --git a/src/vpp-api/client/client.c b/src/vpp-api/client/client.c
index 7a30792402c..d59273ed6cb 100644
--- a/src/vpp-api/client/client.c
+++ b/src/vpp-api/client/client.c
@@ -305,6 +305,8 @@ vac_connect (char * name, char * chroot_prefix, vac_callback_t cb,
}
/* Start read timeout thread */
+ timeout_in_progress = false;
+ timeout_thread_cancelled = false;
rv = pthread_create(&pm->timeout_thread_handle, NULL,
vac_timeout_thread_fn, 0);
if (rv) {
diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c
index 2e8e1fadf8d..359813f8d57 100644
--- a/src/vpp-api/client/stat_client.c
+++ b/src/vpp-api/client/stat_client.c
@@ -225,7 +225,7 @@ stat_vec_combined_init (vlib_counter_t c)
*/
static stat_segment_data_t
copy_data (vlib_stats_entry_t *ep, u32 index2, char *name,
- stat_client_main_t *sm)
+ stat_client_main_t *sm, bool via_symlink)
{
stat_segment_data_t result = { 0 };
int i;
@@ -235,6 +235,7 @@ copy_data (vlib_stats_entry_t *ep, u32 index2, char *name,
assert (sm->shared_header);
result.type = ep->type;
+ result.via_symlink = via_symlink;
result.name = strdup (name ? name : ep->name);
switch (ep->type)
@@ -287,7 +288,9 @@ copy_data (vlib_stats_entry_t *ep, u32 index2, char *name,
{
vlib_stats_entry_t *ep2;
ep2 = vec_elt_at_index (sm->directory_vector, ep->index1);
- return copy_data (ep2, ep->index2, ep->name, sm);
+ /* We do not intend to return the "result", avoid a leak */
+ free (result.name);
+ return copy_data (ep2, ep->index2, ep->name, sm, true);
}
case STAT_DIR_TYPE_EMPTY:
@@ -409,11 +412,20 @@ stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm)
if (stat_segment_access_start (&sa, sm))
return 0;
+ /* preallocate the elements.
+ * This takes care of a special case where
+ * the vec_len(stats) == 0,
+ * such that we return a vector of
+ * length 0, rather than a null pointer
+ * (since null pointer is an error)
+ */
+ vec_alloc (res, vec_len (stats));
+
for (i = 0; i < vec_len (stats); i++)
{
/* Collect counter */
ep = vec_elt_at_index (sm->directory_vector, stats[i]);
- vec_add1 (res, copy_data (ep, ~0, 0, sm));
+ vec_add1 (res, copy_data (ep, ~0, 0, sm, false));
}
if (stat_segment_access_end (&sa, sm))
@@ -421,6 +433,8 @@ stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm)
fprintf (stderr, "Epoch changed while reading, invalid results\n");
// TODO increase counter
+ if (res)
+ stat_segment_data_free (res);
return 0;
}
@@ -472,7 +486,7 @@ stat_segment_dump_entry_r (uint32_t index, stat_client_main_t * sm)
/* Collect counter */
ep = vec_elt_at_index (sm->directory_vector, index);
- vec_add1 (res, copy_data (ep, ~0, 0, sm));
+ vec_add1 (res, copy_data (ep, ~0, 0, sm, false));
if (stat_segment_access_end (&sa, sm))
return res;
@@ -500,6 +514,11 @@ stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm)
return 0;
vec = get_stat_vector_r (sm);
ep = vec_elt_at_index (vec, index);
+ if (ep->type == STAT_DIR_TYPE_EMPTY)
+ {
+ stat_segment_access_end (&sa, sm);
+ return 0;
+ }
if (!stat_segment_access_end (&sa, sm))
return 0;
return strdup (ep->name);
diff --git a/src/vpp-api/client/stat_client.h b/src/vpp-api/client/stat_client.h
index 26752718d90..d9671c69ff2 100644
--- a/src/vpp-api/client/stat_client.h
+++ b/src/vpp-api/client/stat_client.h
@@ -36,6 +36,7 @@ typedef struct
{
char *name;
stat_directory_type_t type;
+ bool via_symlink;
union
{
double scalar_value;
diff --git a/src/vpp-api/client/test.c b/src/vpp-api/client/test.c
index 86d6aef79be..c242e6611a4 100644
--- a/src/vpp-api/client/test.c
+++ b/src/vpp-api/client/test.c
@@ -70,7 +70,6 @@ wrap_vac_callback (unsigned char *data, int len)
static void
test_connect ()
{
- static int i;
int rv = vac_connect("vac_client", NULL, wrap_vac_callback, 32 /* rx queue-length*/);
if (rv != 0) {
printf("Connect failed: %d\n", rv);
@@ -78,7 +77,6 @@ test_connect ()
}
printf(".");
vac_disconnect();
- i++;
}
static void
diff --git a/src/vpp-api/python/CMakeLists.txt b/src/vpp-api/python/CMakeLists.txt
index 789a72233e6..3059619ff21 100644
--- a/src/vpp-api/python/CMakeLists.txt
+++ b/src/vpp-api/python/CMakeLists.txt
@@ -11,27 +11,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-if (CMAKE_VERSION VERSION_LESS 3.12)
- find_package(PythonInterp 2.7)
-else()
- find_package(Python3 COMPONENTS Interpreter)
- set(PYTHONINTERP_FOUND ${Python3_Interpreter_FOUND})
- set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
-endif()
+find_package(Python3 REQUIRED COMPONENTS Interpreter)
+set(PYTHONINTERP_FOUND ${Python3_Interpreter_FOUND})
+set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
-if(PYTHONINTERP_FOUND)
- install(
- CODE "
- execute_process(
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- COMMAND ${PYTHON_EXECUTABLE} ./setup.py
- install
- --root=\$ENV{DESTDIR}/
- --prefix=${CMAKE_INSTALL_PREFIX}
- --single-version-externally-managed
- bdist_egg
- OUTPUT_QUIET
- )"
- COMPONENT vpp-api-python
- )
-endif()
+install(
+ CODE "
+ execute_process(
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMAND ${PYTHON_EXECUTABLE} ./setup.py
+ install
+ --root=\$ENV{DESTDIR}/
+ --prefix=${CMAKE_INSTALL_PREFIX}
+ --single-version-externally-managed
+ bdist_egg
+ OUTPUT_QUIET
+ )"
+ COMPONENT vpp-api-python
+)
diff --git a/src/vpp-api/python/setup.py b/src/vpp-api/python/setup.py
index 18637ba21b7..784013fc606 100644
--- a/src/vpp-api/python/setup.py
+++ b/src/vpp-api/python/setup.py
@@ -11,7 +11,6 @@
# 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.
-import sys
try:
from setuptools import setup, find_packages
@@ -22,7 +21,7 @@ requirements = []
setup(
name="vpp_papi",
- version="2.0.0",
+ version="2.1.0",
description="VPP Python binding",
author="Ole Troan",
author_email="ot@cisco.com",
@@ -31,6 +30,7 @@ setup(
test_suite="vpp_papi.tests",
install_requires=requirements,
packages=find_packages(),
+ package_data={"vpp_papi": ["data/*.json"]},
long_description="""VPP Python language binding.""",
zip_safe=True,
)
diff --git a/src/vpp-api/python/vpp_papi/__init__.py b/src/vpp-api/python/vpp_papi/__init__.py
index f87b6480d4e..dc58c1e18cb 100644
--- a/src/vpp-api/python/vpp_papi/__init__.py
+++ b/src/vpp-api/python/vpp_papi/__init__.py
@@ -14,5 +14,5 @@ import pkg_resources # part of setuptools
try:
__version__ = pkg_resources.get_distribution("vpp_papi").version
-except (pkg_resources.DistributionNotFound):
+except pkg_resources.DistributionNotFound:
"""Can't find vpp_papi via setuptools"""
diff --git a/src/vpp-api/python/vpp_papi/data/memclnt.api.json b/src/vpp-api/python/vpp_papi/data/memclnt.api.json
new file mode 100644
index 00000000000..1734cf12ab0
--- /dev/null
+++ b/src/vpp-api/python/vpp_papi/data/memclnt.api.json
@@ -0,0 +1,809 @@
+{
+ "types": [
+ [
+ "module_version",
+ [
+ "u32",
+ "major"
+ ],
+ [
+ "u32",
+ "minor"
+ ],
+ [
+ "u32",
+ "patch"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ]
+ ],
+ [
+ "message_table_entry",
+ [
+ "u16",
+ "index"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ]
+ ]
+ ],
+ "messages": [
+ [
+ "memclnt_create",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "ctx_quota"
+ ],
+ [
+ "u64",
+ "input_queue"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ [
+ "u32",
+ "api_versions",
+ 8
+ ],
+ {
+ "crc": "0x9c5e1c2f",
+ "options": {
+ "deprecated": null
+ },
+ "comment": "/*\n * Create a client registration\n */"
+ }
+ ],
+ [
+ "memclnt_create_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u64",
+ "message_table"
+ ],
+ {
+ "crc": "0x42ec4560",
+ "options": {
+ "deprecated": null
+ }
+ }
+ ],
+ [
+ "memclnt_delete",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ [
+ "bool",
+ "do_cleanup"
+ ],
+ {
+ "crc": "0x7e1c04e3",
+ "options": {},
+ "comment": "/*\n * Delete a client registration\n */"
+ }
+ ],
+ [
+ "memclnt_delete_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ {
+ "crc": "0x3d3b6312",
+ "options": {}
+ }
+ ],
+ [
+ "rx_thread_exit",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u8",
+ "dummy"
+ ],
+ {
+ "crc": "0xc3a3a452",
+ "options": {},
+ "comment": "/*\n * Client RX thread exit\n */"
+ }
+ ],
+ [
+ "memclnt_rx_thread_suspend",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u8",
+ "dummy"
+ ],
+ {
+ "crc": "0xc3a3a452",
+ "options": {},
+ "comment": "/*\n * Client RX thread suspend\n */"
+ }
+ ],
+ [
+ "memclnt_read_timeout",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u8",
+ "dummy"
+ ],
+ {
+ "crc": "0xc3a3a452",
+ "options": {},
+ "comment": "/*\n * Client read timeout\n */"
+ }
+ ],
+ [
+ "rpc_call",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u64",
+ "function"
+ ],
+ [
+ "u8",
+ "multicast"
+ ],
+ [
+ "u8",
+ "need_barrier_sync"
+ ],
+ [
+ "u8",
+ "send_reply"
+ ],
+ [
+ "u32",
+ "data_len"
+ ],
+ [
+ "u8",
+ "data",
+ 0,
+ "data_len"
+ ],
+ {
+ "crc": "0x7e8a2c95",
+ "options": {},
+ "comment": "/*\n * RPC\n */"
+ }
+ ],
+ [
+ "rpc_call_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804",
+ "options": {}
+ }
+ ],
+ [
+ "get_first_msg_id",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ {
+ "crc": "0xebf79a66",
+ "options": {},
+ "comment": "/*\n * Lookup message-ID base by name\n */"
+ }
+ ],
+ [
+ "get_first_msg_id_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u16",
+ "first_msg_id"
+ ],
+ {
+ "crc": "0x7d337472",
+ "options": {}
+ }
+ ],
+ [
+ "api_versions",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {},
+ "comment": "/*\n * Get API version table (includes built-in and plugins)\n */"
+ }
+ ],
+ [
+ "api_versions_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "count"
+ ],
+ [
+ "vl_api_module_version_t",
+ "api_versions",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x5f0d99d6",
+ "options": {}
+ }
+ ],
+ [
+ "trace_plugin_msg_ids",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "plugin_name",
+ 128
+ ],
+ [
+ "u16",
+ "first_msg_id"
+ ],
+ [
+ "u16",
+ "last_msg_id"
+ ],
+ {
+ "crc": "0xf476d3ce",
+ "options": {},
+ "comment": "/*\n * Trace the plugin message-id allocator\n * so we stand a chance of dealing with different sets of plugins\n * at api trace replay time\n */"
+ }
+ ],
+ [
+ "sockclnt_create",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ {
+ "crc": "0x455fb9c4",
+ "options": {},
+ "comment": "/*\n * Create a socket client registration.\n */"
+ }
+ ],
+ [
+ "sockclnt_create_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u16",
+ "count"
+ ],
+ [
+ "vl_api_message_table_entry_t",
+ "message_table",
+ 0,
+ "count"
+ ],
+ {
+ "crc": "0x35166268",
+ "options": {}
+ }
+ ],
+ [
+ "sockclnt_delete",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ {
+ "crc": "0x8ac76db6",
+ "options": {},
+ "comment": "/*\n * Delete a client registration\n */"
+ }
+ ],
+ [
+ "sockclnt_delete_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ {
+ "crc": "0x8f38b1ee",
+ "options": {}
+ }
+ ],
+ [
+ "sock_init_shm",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "u32",
+ "requested_size"
+ ],
+ [
+ "u8",
+ "nitems"
+ ],
+ [
+ "u64",
+ "configs",
+ 0,
+ "nitems"
+ ],
+ {
+ "crc": "0x51646d92",
+ "options": {},
+ "comment": "/*\n * Initialize shm api over socket api\n */"
+ }
+ ],
+ [
+ "sock_init_shm_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804",
+ "options": {}
+ }
+ ],
+ [
+ "memclnt_keepalive",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {},
+ "comment": "/*\n * Memory client ping / response\n * Only sent on inactive connections\n */"
+ }
+ ],
+ [
+ "memclnt_keepalive_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ {
+ "crc": "0xe8d4e804",
+ "options": {}
+ }
+ ],
+ [
+ "control_ping",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {},
+ "comment": "/** \\brief Control ping from client to api server request\n @param client_index - opaque cookie to identify the sender\n @param context - sender context, to match reply w/ request\n*/"
+ }
+ ],
+ [
+ "control_ping_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "vpe_pid"
+ ],
+ {
+ "crc": "0xf6b0b8ca",
+ "options": {},
+ "comment": "/** \\brief Control ping from the client to the server response\n @param client_index - opaque cookie to identify the sender\n @param context - sender context, to match reply w/ request\n @param retval - return code for the request\n @param vpe_pid - the pid of the vpe, returned by the server\n*/"
+ }
+ ],
+ [
+ "memclnt_create_v2",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "ctx_quota"
+ ],
+ [
+ "u64",
+ "input_queue"
+ ],
+ [
+ "string",
+ "name",
+ 64
+ ],
+ [
+ "u32",
+ "api_versions",
+ 8
+ ],
+ [
+ "bool",
+ "keepalive",
+ {
+ "default": "true"
+ }
+ ],
+ {
+ "crc": "0xc4bd4882",
+ "options": {}
+ }
+ ],
+ [
+ "memclnt_create_v2_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "response"
+ ],
+ [
+ "u64",
+ "handle"
+ ],
+ [
+ "u32",
+ "index"
+ ],
+ [
+ "u64",
+ "message_table"
+ ],
+ {
+ "crc": "0x42ec4560",
+ "options": {}
+ }
+ ],
+ [
+ "get_api_json",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "client_index"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ {
+ "crc": "0x51077d14",
+ "options": {}
+ }
+ ],
+ [
+ "get_api_json_reply",
+ [
+ "u16",
+ "_vl_msg_id"
+ ],
+ [
+ "u32",
+ "context"
+ ],
+ [
+ "i32",
+ "retval"
+ ],
+ [
+ "string",
+ "json",
+ 0
+ ],
+ {
+ "crc": "0xea715b59",
+ "options": {}
+ }
+ ]
+ ],
+ "unions": [],
+ "enums": [],
+ "enumflags": [],
+ "services": {
+ "memclnt_rx_thread_suspend": {
+ "reply": "null"
+ },
+ "memclnt_read_timeout": {
+ "reply": "null"
+ },
+ "rx_thread_exit": {
+ "reply": "null"
+ },
+ "trace_plugin_msg_ids": {
+ "reply": "null"
+ },
+ "memclnt_create": {
+ "reply": "memclnt_create_reply"
+ },
+ "memclnt_delete": {
+ "reply": "memclnt_delete_reply"
+ },
+ "rpc_call": {
+ "reply": "rpc_call_reply"
+ },
+ "get_first_msg_id": {
+ "reply": "get_first_msg_id_reply"
+ },
+ "api_versions": {
+ "reply": "api_versions_reply"
+ },
+ "sockclnt_create": {
+ "reply": "sockclnt_create_reply"
+ },
+ "sockclnt_delete": {
+ "reply": "sockclnt_delete_reply"
+ },
+ "sock_init_shm": {
+ "reply": "sock_init_shm_reply"
+ },
+ "memclnt_keepalive": {
+ "reply": "memclnt_keepalive_reply"
+ },
+ "control_ping": {
+ "reply": "control_ping_reply"
+ },
+ "memclnt_create_v2": {
+ "reply": "memclnt_create_v2_reply"
+ },
+ "get_api_json": {
+ "reply": "get_api_json_reply"
+ }
+ },
+ "options": {
+ "version": "2.1.0"
+ },
+ "aliases": {},
+ "vl_api_version": "0xb197c551",
+ "imports": [],
+ "counters": [],
+ "paths": []
+}
diff --git a/src/vpp-api/python/vpp_papi/macaddress.py b/src/vpp-api/python/vpp_papi/macaddress.py
index 8799bd7be24..66349a3c19a 100644
--- a/src/vpp-api/python/vpp_papi/macaddress.py
+++ b/src/vpp-api/python/vpp_papi/macaddress.py
@@ -53,7 +53,6 @@ class MACAddress:
return "%s(%s)" % (self.__class__.__name__, self.mac_string)
def __eq__(self, other):
-
if not isinstance(other, MACAddress):
try:
# if it looks like a mac address, we'll take it.
diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py
index 2b21c83966a..51c024aa3ab 100644
--- a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py
+++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py
@@ -24,8 +24,7 @@ from vpp_papi import vpp_transport_shmem
class TestVppPapiVPPApiClient(unittest.TestCase):
def test_getcontext(self):
- vpp_papi.VPPApiClient.apidir = "."
- c = vpp_papi.VPPApiClient(testmode=True, use_socket=True)
+ c = vpp_papi.VPPApiClient(apidir=".", testmode=True, use_socket=True)
# reset initialization at module load time.
c.get_context.context = mp.Value(ctypes.c_uint, 0)
@@ -39,8 +38,7 @@ class TestVppPapiVPPApiClientMp(unittest.TestCase):
# run_tests.py (eg. make test TEST_JOBS=10)
def test_get_context_mp(self):
- vpp_papi.VPPApiClient.apidir = "."
- c = vpp_papi.VPPApiClient(testmode=True, use_socket=True)
+ c = vpp_papi.VPPApiClient(apidir=".", testmode=True, use_socket=True)
# reset initialization at module load time.
c.get_context.context = mp.Value(ctypes.c_uint, 0)
diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py
index eee38f00632..f0d2846214a 100755
--- a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py
+++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py
@@ -426,6 +426,32 @@ class TestAddType(unittest.TestCase):
nt, size = s.unpack(b)
self.assertEqual(len(b), size)
+ # Try same with VLA u8
+ byte_array = [b"\0"] * (10)
+ vla_u8 = VPPType("vla_u8", [["u8", "length"], ["u8", "data", 0, "length"]])
+ b = vla_u8.pack({"length": len(byte_array), "data": byte_array})
+ nt, size = vla_u8.unpack(b)
+
+ # VLA Array of fixed length strings
+ fixed_string = VPPType("fixed_string", [["string", "data", 32]])
+ s = VPPType(
+ "string_vla", [["u32", "length"], ["fixed_string", "services", 0, "length"]]
+ )
+
+ string_list = [{"data": "foobar1"}, {"data": "foobar2"}]
+ b = s.pack({"length": 2, "services": string_list})
+ nt, size = s.unpack(b)
+
+ # Try same with u8
+ fixed_u8 = VPPType("fixed_u8", [["u8", "data", 32]])
+ s = VPPType(
+ "u8_vla", [["u32", "length"], ["fixed_string", "services", 0, "length"]]
+ )
+
+ u8_list = [{"data": "foobar1"}, {"data": "foobar2"}]
+ b = s.pack({"length": 2, "services": u8_list})
+ nt, size = s.unpack(b)
+
def test_message(self):
foo = VPPMessage(
"foo",
@@ -442,7 +468,6 @@ class TestAddType(unittest.TestCase):
self.assertEqual(nt.something, 200)
def test_abf(self):
-
fib_mpls_label = VPPType(
"vl_api_fib_mpls_label_t",
[["u8", "is_uniform"], ["u32", "label"], ["u8", "ttl"], ["u8", "exp"]],
@@ -535,7 +560,6 @@ class TestAddType(unittest.TestCase):
)
def test_bier(self):
-
bier_table_id = VPPType(
"vl_api_bier_table_id_t",
[["u8", "bt_set"], ["u8", "bt_sub_domain"], ["u8", "bt_hdr_len_id"]],
diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py
index 1e5d23e59b7..30c00cd8dd3 100644
--- a/src/vpp-api/python/vpp_papi/vpp_papi.py
+++ b/src/vpp-api/python/vpp_papi/vpp_papi.py
@@ -18,7 +18,6 @@ from __future__ import print_function
from __future__ import absolute_import
import ctypes
import ipaddress
-import sys
import multiprocessing as mp
import os
import queue
@@ -30,6 +29,7 @@ import fnmatch
import weakref
import atexit
import time
+import pkg_resources
from .vpp_format import verify_enum_hint
from .vpp_serializer import VPPType, VPPEnumType, VPPEnumFlagType, VPPUnionType
from .vpp_serializer import VPPMessage, vpp_get_type, VPPTypeAlias
@@ -154,7 +154,7 @@ class VPPValueError(ValueError):
class VPPApiJSONFiles:
@classmethod
- def find_api_dir(cls, dirs):
+ def find_api_dir(cls, dirs=[]):
"""Attempt to find the best directory in which API definition
files may reside. If the value VPP_API_DIR exists in the environment
then it is first on the search list. If we're inside a recognized
@@ -170,6 +170,9 @@ class VPPApiJSONFiles:
# in which case, plot a course to likely places in the src tree
import __main__ as main
+ if os.getenv("VPP_API_DIR"):
+ dirs.append(os.getenv("VPP_API_DIR"))
+
if hasattr(main, "__file__"):
# get the path of the calling script
localdir = os.path.dirname(os.path.realpath(main.__file__))
@@ -286,6 +289,18 @@ class VPPApiJSONFiles:
api = json.loads(json_str)
return self._process_json(api)
+ @classmethod
+ def process_json_array_str(self, json_str):
+ services = {}
+ messages = {}
+
+ apis = json.loads(json_str)
+ for a in apis:
+ m, s = self._process_json(a)
+ messages.update(m)
+ services.update(s)
+ return messages, services
+
@staticmethod
def _process_json(api): # -> Tuple[Dict, Dict]
types = {}
@@ -371,12 +386,35 @@ class VPPApiJSONFiles:
try:
messages[m[0]] = VPPMessage(m[0], m[1:])
except VPPNotImplementedError:
- ### OLE FIXME
logger.error("Not implemented error for {}".format(m[0]))
except KeyError:
pass
return messages, services
+ @staticmethod
+ def load_api(apifiles=None, apidir=None):
+ messages = {}
+ services = {}
+ if not apifiles:
+ # Pick up API definitions from default directory
+ try:
+ if isinstance(apidir, list):
+ apifiles = []
+ for d in apidir:
+ apifiles += VPPApiJSONFiles.find_api_files(d)
+ else:
+ apifiles = VPPApiJSONFiles.find_api_files(apidir)
+ except (RuntimeError, VPPApiError):
+ raise VPPRuntimeError
+
+ for file in apifiles:
+ with open(file) as apidef_file:
+ m, s = VPPApiJSONFiles.process_json_file(apidef_file)
+ messages.update(m)
+ services.update(s)
+
+ return apifiles, messages, services
+
class VPPApiClient:
"""VPP interface.
@@ -391,7 +429,6 @@ class VPPApiClient:
these messages in a background thread.
"""
- apidir = None
VPPApiError = VPPApiError
VPPRuntimeError = VPPRuntimeError
VPPValueError = VPPValueError
@@ -402,6 +439,7 @@ class VPPApiClient:
self,
*,
apifiles=None,
+ apidir=None,
testmode=False,
async_thread=True,
logger=None,
@@ -409,6 +447,7 @@ class VPPApiClient:
read_timeout=5,
use_socket=True,
server_address="/run/vpp/api.sock",
+ bootstrapapi=False,
):
"""Create a VPP API object.
@@ -436,6 +475,7 @@ class VPPApiClient:
self.id_msgdef = []
self.header = VPPType("header", [["u16", "msgid"], ["u32", "client_index"]])
self.apifiles = []
+ self.apidir = apidir
self.event_callback = None
self.message_queue = queue.Queue()
self.read_timeout = read_timeout
@@ -445,31 +485,37 @@ class VPPApiClient:
self.server_address = server_address
self._apifiles = apifiles
self.stats = {}
+ self.bootstrapapi = bootstrapapi
- if not apifiles:
- # Pick up API definitions from default directory
+ if not bootstrapapi:
+ if self.apidir is None and hasattr(self.__class__, "apidir"):
+ # Keep supporting the old style of providing apidir.
+ self.apidir = self.__class__.apidir
try:
- apifiles = VPPApiJSONFiles.find_api_files(self.apidir)
- except (RuntimeError, VPPApiError):
- # In test mode we don't care that we can't find the API files
+ self.apifiles, self.messages, self.services = VPPApiJSONFiles.load_api(
+ apifiles, self.apidir
+ )
+ except VPPRuntimeError as e:
if testmode:
- apifiles = []
+ self.apifiles = []
else:
- raise VPPRuntimeError
-
- for file in apifiles:
- with open(file) as apidef_file:
- m, s = VPPApiJSONFiles.process_json_file(apidef_file)
- self.messages.update(m)
- self.services.update(s)
-
- self.apifiles = apifiles
+ raise e
+ else:
+ # Bootstrap the API (memclnt.api bundled with VPP PAPI)
+ resource_path = "/".join(("data", "memclnt.api.json"))
+ file_content = pkg_resources.resource_string(__name__, resource_path)
+ self.messages, self.services = VPPApiJSONFiles.process_json_str(
+ file_content
+ )
# Basic sanity check
if len(self.messages) == 0 and not testmode:
raise VPPValueError(1, "Missing JSON message definitions")
- if not (verify_enum_hint(VppEnum.vl_api_address_family_t)):
- raise VPPRuntimeError("Invalid address family hints. " "Cannot continue.")
+ if not bootstrapapi:
+ if not (verify_enum_hint(VppEnum.vl_api_address_family_t)):
+ raise VPPRuntimeError(
+ "Invalid address family hints. " "Cannot continue."
+ )
self.transport = VppTransport(
self, read_timeout=read_timeout, server_address=server_address
@@ -525,6 +571,13 @@ class VPPApiClient:
return f
+ def make_pack_function(self, msg, i, multipart):
+ def f(**kwargs):
+ return self._call_vpp_pack(i, msg, **kwargs)
+
+ f.msg = msg
+ return f
+
def _register_functions(self, do_async=False):
self.id_names = [None] * (self.vpp_dictionary_maxid + 1)
self.id_msgdef = [None] * (self.vpp_dictionary_maxid + 1)
@@ -539,17 +592,38 @@ class VPPApiClient:
# Create function for client side messages.
if name in self.services:
f = self.make_function(msg, i, self.services[name], do_async)
+ f_pack = self.make_pack_function(msg, i, self.services[name])
setattr(self._api, name, FuncWrapper(f))
+ setattr(self._api, name + "_pack", FuncWrapper(f_pack))
else:
self.logger.debug("No such message type or failed CRC checksum: %s", n)
+ def get_api_definitions(self):
+ """get_api_definition. Bootstrap from the embedded memclnt.api.json file."""
+
+ # Bootstrap so we can call the get_api_json function
+ self._register_functions(do_async=False)
+
+ r = self.api.get_api_json()
+ if r.retval != 0:
+ raise VPPApiError("Failed to load API definitions from VPP")
+
+ # Process JSON
+ m, s = VPPApiJSONFiles.process_json_array_str(r.json)
+ self.messages.update(m)
+ self.services.update(s)
+
def connect_internal(self, name, msg_handler, chroot_prefix, rx_qlen, do_async):
pfx = chroot_prefix.encode("utf-8") if chroot_prefix else None
- rv = self.transport.connect(name, pfx, msg_handler, rx_qlen)
+ rv = self.transport.connect(name, pfx, msg_handler, rx_qlen, do_async)
if rv != 0:
raise VPPIOError(2, "Connect failed")
self.vpp_dictionary_maxid = self.transport.msg_table_max_index()
+
+ # Register functions
+ if self.bootstrapapi:
+ self.get_api_definitions()
self._register_functions(do_async=do_async)
# Initialise control ping
@@ -558,6 +632,7 @@ class VPPApiClient:
("control_ping" + "_" + crc[2:])
)
self.control_ping_msgdef = self.messages["control_ping"]
+
if self.async_thread:
self.event_thread = threading.Thread(target=self.thread_msg_handler)
self.event_thread.daemon = True
@@ -629,6 +704,7 @@ class VPPApiClient:
)
(i, ci, context), size = header.unpack(msg, 0)
+
if self.id_names[i] == "rx_thread_exit":
return
@@ -831,6 +907,13 @@ class VPPApiClient:
self.transport.write(b)
return context
+ def _call_vpp_pack(self, i, msg, **kwargs):
+ """Given a message, return the binary representation."""
+ kwargs["_vl_msg_id"] = i
+ kwargs["client_index"] = 0
+ kwargs["context"] = 0
+ return msg.pack(kwargs)
+
def read_blocking(self, no_type_conversion=False, timeout=None):
"""Get next received message from transport within timeout, decoded.
diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py
index d7da457ea1a..d724cb33ce9 100644
--- a/src/vpp-api/python/vpp_papi/vpp_serializer.py
+++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py
@@ -135,7 +135,7 @@ class String(Packer):
def __init__(self, name, num, options):
self.name = name
self.num = num
- self.size = 1
+ self.size = num if num else 1
self.length_field_packer = BaseTypes("u32")
self.limit = options["limit"] if "limit" in options else num
self.fixed = True if num else False
@@ -304,9 +304,8 @@ class VLAList(Packer):
len(lst), kwargs[self.length_field]
)
)
-
# u8 array
- if self.packer.size == 1:
+ if self.packer.size == 1 and self.field_type == "u8":
if isinstance(lst, list):
return b"".join(lst)
return bytes(lst)
@@ -321,7 +320,7 @@ class VLAList(Packer):
total = 0
# u8 array
- if self.packer.size == 1:
+ if self.packer.size == 1 and self.field_type == "u8":
if result[self.index] == 0:
return b"", 0
p = BaseTypes("u8", result[self.index])
@@ -618,7 +617,6 @@ class VPPType(Packer):
self.packers.append(p)
size += p.size
-
self.size = size
self.tuple = collections.namedtuple(name, self.fields, rename=True)
types[name] = self
diff --git a/src/vpp-api/python/vpp_papi/vpp_stats.py b/src/vpp-api/python/vpp_papi/vpp_stats.py
index 4a342b68a8f..aa9ff85b3c7 100755
--- a/src/vpp-api/python/vpp_papi/vpp_stats.py
+++ b/src/vpp-api/python/vpp_papi/vpp_stats.py
@@ -54,7 +54,7 @@ import re
def recv_fd(sock):
"""Get file descriptor for memory map"""
fds = array.array("i") # Array of ints
- _, ancdata, _, _ = sock.recvmsg(0, socket.CMSG_LEN(4))
+ _, ancdata, _, _ = sock.recvmsg(0, socket.CMSG_SPACE(4))
for cmsg_level, cmsg_type, cmsg_data in ancdata:
if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS:
fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
@@ -136,6 +136,12 @@ class VPPStats:
if self.connected:
return
sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+
+ # Our connect races the corresponding recv_fds call in VPP, if we beat
+ # VPP then we will try (unsuccessfully) to receive file descriptors and
+ # will have gone away before VPP can respond to our connect. A short
+ # timeout here stops this error occurring.
+ sock.settimeout(1)
sock.connect(self.socketname)
mfd = recv_fd(sock)
diff --git a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py
index 3a8c332a00a..174ab74d0b8 100644
--- a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py
+++ b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py
@@ -42,7 +42,7 @@ class VppTransport:
while True:
try:
rlist, _, _ = select.select([self.socket, self.sque._reader], [], [])
- except socket.error:
+ except (socket.error, ValueError):
# Terminate thread
logging.error("select failed")
self.q.put(None)
@@ -65,14 +65,14 @@ class VppTransport:
return
# Put either to local queue or if context == 0
# callback queue
- if self.parent.has_context(msg):
+ if not self.do_async and self.parent.has_context(msg):
self.q.put(msg)
else:
self.parent.msg_handler_async(msg)
else:
raise VppTransportSocketIOError(2, "Unknown response from select")
- def connect(self, name, pfx, msg_handler, rx_qlen):
+ def connect(self, name, pfx, msg_handler, rx_qlen, do_async=False):
# TODO: Reorder the actions and add "roll-backs",
# to restore clean disconnect state when failure happens durng connect.
@@ -125,6 +125,7 @@ class VppTransport:
self.message_table[n] = m.index
self.message_thread.daemon = True
+ self.do_async = do_async
self.message_thread.start()
return 0
diff --git a/src/vpp-api/vapi/CMakeLists.txt b/src/vpp-api/vapi/CMakeLists.txt
index e01692210bb..e53d3e8b238 100644
--- a/src/vpp-api/vapi/CMakeLists.txt
+++ b/src/vpp-api/vapi/CMakeLists.txt
@@ -45,7 +45,7 @@ install(
vapi_json_parser.py
vapi_cpp_gen.py
DESTINATION
- share/vpp
+ ${CMAKE_INSTALL_DATADIR}/vpp
COMPONENT
vpp-dev
)
diff --git a/src/vpp-api/vapi/fake.api.json b/src/vpp-api/vapi/fake.api.json
index 24c9f4dbfa1..f7238c468fa 100644
--- a/src/vpp-api/vapi/fake.api.json
+++ b/src/vpp-api/vapi/fake.api.json
@@ -10,6 +10,8 @@
},
"enums" : [
],
+ "enumflags" : [
+ ],
"unions" : [
],
"types" : [
diff --git a/src/vpp-api/vapi/vapi.c b/src/vpp-api/vapi/vapi.c
index 7808bec8521..022f023aeb0 100644
--- a/src/vpp-api/vapi/vapi.c
+++ b/src/vpp-api/vapi/vapi.c
@@ -63,7 +63,8 @@ typedef struct
u32 context;
vapi_cb_t callback;
void *callback_ctx;
- bool is_dump;
+ vapi_msg_id_t response_id;
+ enum vapi_request_type type;
} vapi_req_t;
static const u32 context_counter_mask = (1 << 31);
@@ -97,8 +98,11 @@ struct vapi_ctx_s
bool connected;
bool handle_keepalives;
pthread_mutex_t requests_mutex;
+ bool use_uds;
svm_queue_t *vl_input_queue;
+ clib_socket_t client_socket;
+ clib_time_t time;
u32 my_client_index;
/** client message index hash table */
uword *msg_index_by_name_and_crc;
@@ -137,15 +141,17 @@ vapi_requests_end (vapi_ctx_t ctx)
}
void
-vapi_store_request (vapi_ctx_t ctx, u32 context, bool is_dump,
- vapi_cb_t callback, void *callback_ctx)
+vapi_store_request (vapi_ctx_t ctx, u32 context, vapi_msg_id_t response_id,
+ enum vapi_request_type request_type, vapi_cb_t callback,
+ void *callback_ctx)
{
assert (!vapi_requests_full (ctx));
/* if the mutex is not held, bad things will happen */
assert (0 != pthread_mutex_trylock (&ctx->requests_mutex));
const int requests_end = vapi_requests_end (ctx);
vapi_req_t *slot = &ctx->requests[requests_end];
- slot->is_dump = is_dump;
+ slot->type = request_type;
+ slot->response_id = response_id;
slot->context = context;
slot->callback = callback;
slot->callback_ctx = callback_ctx;
@@ -227,8 +233,8 @@ vapi_to_be_freed_validate ()
#endif
-void *
-vapi_msg_alloc (vapi_ctx_t ctx, size_t size)
+static void *
+vapi_shm_msg_alloc (vapi_ctx_t ctx, size_t size)
{
if (!ctx->connected)
{
@@ -242,6 +248,23 @@ vapi_msg_alloc (vapi_ctx_t ctx, size_t size)
return rv;
}
+static void *
+vapi_sock_msg_alloc (size_t size)
+{
+ u8 *rv = 0;
+ vec_validate_init_empty (rv, size - 1, 0);
+ return rv;
+}
+
+void *
+vapi_msg_alloc (vapi_ctx_t ctx, size_t size)
+{
+ if (ctx->use_uds)
+ return vapi_sock_msg_alloc (size);
+
+ return vapi_shm_msg_alloc (ctx, size);
+}
+
void
vapi_msg_free (vapi_ctx_t ctx, void *msg)
{
@@ -249,10 +272,19 @@ vapi_msg_free (vapi_ctx_t ctx, void *msg)
{
return;
}
+
#if VAPI_DEBUG_ALLOC
vapi_trace_free (msg);
#endif
- vl_msg_api_free (msg);
+
+ if (ctx->use_uds)
+ {
+ vec_free (msg);
+ }
+ else
+ {
+ vl_msg_api_free (msg);
+ }
}
vapi_msg_id_t
@@ -291,6 +323,7 @@ vapi_ctx_alloc (vapi_ctx_t * result)
}
pthread_mutex_init (&ctx->requests_mutex, NULL);
*result = ctx;
+ clib_time_init (&ctx->time);
return VAPI_OK;
fail:
vapi_ctx_free (ctx);
@@ -340,6 +373,205 @@ vapi_api_name_and_crc_free (vapi_ctx_t ctx)
hash_free (ctx->msg_index_by_name_and_crc);
}
+static vapi_error_e
+vapi_sock_get_errno (int err)
+{
+ switch (err)
+ {
+ case ENOTSOCK:
+ return VAPI_ENOTSOCK;
+ case EACCES:
+ return VAPI_EACCES;
+ case ECONNRESET:
+ return VAPI_ECONNRESET;
+ default:
+ break;
+ }
+ return VAPI_ESOCK_FAILURE;
+}
+
+static vapi_error_e
+vapi_sock_send (vapi_ctx_t ctx, u8 *msg)
+{
+ size_t n;
+ struct msghdr hdr;
+
+ const size_t len = vec_len (msg);
+ const size_t total_len = len + sizeof (msgbuf_t);
+
+ msgbuf_t msgbuf1 = {
+ .q = 0,
+ .gc_mark_timestamp = 0,
+ .data_len = htonl (len),
+ };
+
+ struct iovec bufs[2] = {
+ [0] = { .iov_base = &msgbuf1, .iov_len = sizeof (msgbuf1) },
+ [1] = { .iov_base = msg, .iov_len = len },
+ };
+
+ clib_memset (&hdr, 0, sizeof (hdr));
+ hdr.msg_iov = bufs;
+ hdr.msg_iovlen = 2;
+
+ n = sendmsg (ctx->client_socket.fd, &hdr, 0);
+ if (n < 0)
+ {
+ return vapi_sock_get_errno (errno);
+ }
+
+ if (n < total_len)
+ {
+ return VAPI_EAGAIN;
+ }
+
+ vec_free (msg);
+
+ return VAPI_OK;
+}
+
+static vapi_error_e
+vapi_sock_send2 (vapi_ctx_t ctx, u8 *msg1, u8 *msg2)
+{
+ size_t n;
+ struct msghdr hdr;
+
+ const size_t len1 = vec_len (msg1);
+ const size_t len2 = vec_len (msg2);
+ const size_t total_len = len1 + len2 + 2 * sizeof (msgbuf_t);
+
+ msgbuf_t msgbuf1 = {
+ .q = 0,
+ .gc_mark_timestamp = 0,
+ .data_len = htonl (len1),
+ };
+
+ msgbuf_t msgbuf2 = {
+ .q = 0,
+ .gc_mark_timestamp = 0,
+ .data_len = htonl (len2),
+ };
+
+ struct iovec bufs[4] = {
+ [0] = { .iov_base = &msgbuf1, .iov_len = sizeof (msgbuf1) },
+ [1] = { .iov_base = msg1, .iov_len = len1 },
+ [2] = { .iov_base = &msgbuf2, .iov_len = sizeof (msgbuf2) },
+ [3] = { .iov_base = msg2, .iov_len = len2 },
+ };
+
+ clib_memset (&hdr, 0, sizeof (hdr));
+ hdr.msg_iov = bufs;
+ hdr.msg_iovlen = 4;
+
+ n = sendmsg (ctx->client_socket.fd, &hdr, 0);
+ if (n < 0)
+ {
+ return vapi_sock_get_errno (errno);
+ }
+
+ if (n < total_len)
+ {
+ return VAPI_EAGAIN;
+ }
+
+ vec_free (msg1);
+ vec_free (msg2);
+
+ return VAPI_OK;
+}
+
+static vapi_error_e
+vapi_sock_recv_internal (vapi_ctx_t ctx, u8 **vec_msg, u32 timeout)
+{
+ clib_socket_t *sock = &ctx->client_socket;
+ u32 data_len = 0, msg_size;
+ msgbuf_t *mbp = 0;
+ ssize_t n, current_rx_index;
+ f64 deadline;
+ vapi_error_e rv = VAPI_EAGAIN;
+
+ if (ctx->client_socket.fd == 0)
+ return VAPI_ENOTSOCK;
+
+ deadline = clib_time_now (&ctx->time) + timeout;
+
+ while (1)
+ {
+ current_rx_index = vec_len (sock->rx_buffer);
+ while (current_rx_index < sizeof (*mbp))
+ {
+ vec_validate (sock->rx_buffer, sizeof (*mbp) - 1);
+ n = recv (sock->fd, sock->rx_buffer + current_rx_index,
+ sizeof (*mbp) - current_rx_index, MSG_DONTWAIT);
+ if (n < 0)
+ {
+ if (errno == EAGAIN && clib_time_now (&ctx->time) >= deadline)
+ return VAPI_EAGAIN;
+
+ if (errno == EAGAIN)
+ continue;
+
+ clib_unix_warning ("socket_read");
+ vec_set_len (sock->rx_buffer, current_rx_index);
+ return vapi_sock_get_errno (errno);
+ }
+ current_rx_index += n;
+ }
+ vec_set_len (sock->rx_buffer, current_rx_index);
+
+ mbp = (msgbuf_t *) (sock->rx_buffer);
+ data_len = ntohl (mbp->data_len);
+ current_rx_index = vec_len (sock->rx_buffer);
+ vec_validate (sock->rx_buffer, current_rx_index + data_len);
+ mbp = (msgbuf_t *) (sock->rx_buffer);
+ msg_size = data_len + sizeof (*mbp);
+
+ while (current_rx_index < msg_size)
+ {
+ n = recv (sock->fd, sock->rx_buffer + current_rx_index,
+ msg_size - current_rx_index, MSG_DONTWAIT);
+ if (n < 0)
+ {
+ if (errno == EAGAIN && clib_time_now (&ctx->time) >= deadline)
+ return VAPI_EAGAIN;
+
+ if (errno == EAGAIN)
+ continue;
+
+ clib_unix_warning ("socket_read");
+ vec_set_len (sock->rx_buffer, current_rx_index);
+ return vapi_sock_get_errno (errno);
+ }
+ current_rx_index += n;
+ }
+ vec_set_len (sock->rx_buffer, current_rx_index);
+
+ if (vec_len (sock->rx_buffer) >= data_len + sizeof (*mbp))
+ {
+ if (data_len)
+ {
+ vec_add (*vec_msg, mbp->data, data_len);
+ rv = VAPI_OK;
+ }
+ else
+ {
+ *vec_msg = 0;
+ }
+
+ if (vec_len (sock->rx_buffer) == data_len + sizeof (*mbp))
+ vec_set_len (sock->rx_buffer, 0);
+ else
+ vec_delete (sock->rx_buffer, data_len + sizeof (*mbp), 0);
+ mbp = 0;
+
+ /* Quit if we're out of data, and not expecting a ping reply */
+ if (vec_len (sock->rx_buffer) == 0)
+ break;
+ }
+ }
+ return rv;
+}
+
static void
vapi_memclnt_create_v2_reply_t_handler (vapi_ctx_t ctx,
vl_api_memclnt_create_v2_reply_t *mp)
@@ -374,6 +606,28 @@ vapi_memclnt_create_v2_reply_t_handler (vapi_ctx_t ctx,
}
static void
+vapi_sockclnt_create_reply_t_handler (vapi_ctx_t ctx,
+ vl_api_sockclnt_create_reply_t *mp)
+{
+ int i;
+ u8 *name_and_crc;
+
+ ctx->my_client_index = mp->index;
+
+ /* Clean out any previous hash table (unlikely) */
+ vapi_api_name_and_crc_free (ctx);
+
+ ctx->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword));
+
+ for (i = 0; i < be16toh (mp->count); i++)
+ {
+ name_and_crc = format (0, "%s%c", mp->message_table[i].name, 0);
+ hash_set_mem (ctx->msg_index_by_name_and_crc, name_and_crc,
+ be16toh (mp->message_table[i].index));
+ }
+}
+
+static void
vapi_memclnt_delete_reply_t_handler (vapi_ctx_t ctx,
vl_api_memclnt_delete_reply_t *mp)
{
@@ -386,9 +640,17 @@ vapi_memclnt_delete_reply_t_handler (vapi_ctx_t ctx,
ctx->vl_input_queue = 0;
}
-int
-vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota,
- int input_queue_size, bool keepalive)
+static void
+vapi_sockclnt_delete_reply_t_handler (vapi_ctx_t ctx,
+ vl_api_sockclnt_delete_reply_t *mp)
+{
+ ctx->my_client_index = ~0;
+ ctx->vl_input_queue = 0;
+}
+
+static int
+vapi_shm_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota,
+ int input_queue_size, bool keepalive)
{
vl_api_memclnt_create_v2_t *mp;
vl_api_memclnt_create_v2_reply_t *rp;
@@ -403,7 +665,7 @@ vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota,
if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0)
{
clib_warning ("shmem_hdr / input queue NULL");
- return -1;
+ return VAPI_ECON_FAIL;
}
clib_mem_unpoison (shmem_hdr, sizeof (*shmem_hdr));
@@ -446,7 +708,7 @@ vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota,
ts = tsrem;
}
/* Timeout... */
- return -1;
+ return VAPI_ECON_FAIL;
read_one_msg:
VL_MSG_API_UNPOISON (rp);
@@ -462,6 +724,223 @@ vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota,
return (rv);
}
+static int
+vapi_sock_client_connect (vapi_ctx_t ctx, char *path, const char *name)
+{
+ clib_error_t *error;
+ clib_socket_t *sock;
+ vl_api_sockclnt_create_t *mp;
+ vl_api_sockclnt_create_reply_t *rp;
+ int rv = 0;
+ u8 *msg = 0;
+
+ ctx->my_client_index = ~0;
+
+ if (ctx->client_socket.fd)
+ return VAPI_EINVAL;
+
+ if (name == 0)
+ return VAPI_EINVAL;
+
+ sock = &ctx->client_socket;
+ sock->config = path ? path : API_SOCKET_FILE;
+ sock->flags = CLIB_SOCKET_F_IS_CLIENT;
+
+ if ((error = clib_socket_init (sock)))
+ {
+ clib_error_report (error);
+ return VAPI_ECON_FAIL;
+ }
+
+ mp = vapi_sock_msg_alloc (sizeof (vl_api_sockclnt_create_t));
+ mp->_vl_msg_id = ntohs (VL_API_SOCKCLNT_CREATE);
+ strncpy ((char *) mp->name, name, sizeof (mp->name) - 1);
+
+ if (vapi_sock_send (ctx, (void *) mp) != VAPI_OK)
+ {
+ return VAPI_ECON_FAIL;
+ }
+
+ while (1)
+ {
+ int qstatus;
+ struct timespec ts, tsrem;
+ int i;
+
+ /* Wait up to 10 seconds */
+ for (i = 0; i < 1000; i++)
+ {
+ qstatus = vapi_sock_recv_internal (ctx, &msg, 0);
+
+ if (qstatus == 0)
+ goto read_one_msg;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10000 * 1000; /* 10 ms */
+ while (nanosleep (&ts, &tsrem) < 0)
+ ts = tsrem;
+ }
+ /* Timeout... */
+ return -1;
+
+ read_one_msg:
+ if (vec_len (msg) == 0)
+ continue;
+
+ rp = (void *) msg;
+ if (ntohs (rp->_vl_msg_id) != VL_API_SOCKCLNT_CREATE_REPLY)
+ {
+ clib_warning ("unexpected reply: id %d", ntohs (rp->_vl_msg_id));
+ continue;
+ }
+ rv = clib_net_to_host_u32 (rp->response);
+ vapi_sockclnt_create_reply_t_handler (ctx, rp);
+ break;
+ }
+ return (rv);
+}
+
+static void
+vapi_shm_client_send_disconnect (vapi_ctx_t ctx, u8 do_cleanup)
+{
+ vl_api_memclnt_delete_t *mp;
+ vl_shmem_hdr_t *shmem_hdr;
+ api_main_t *am = vlibapi_get_main ();
+
+ ASSERT (am->vlib_rp);
+ shmem_hdr = am->shmem_hdr;
+ ASSERT (shmem_hdr && shmem_hdr->vl_input_queue);
+
+ mp = vl_msg_api_alloc (sizeof (vl_api_memclnt_delete_t));
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE);
+ mp->index = ctx->my_client_index;
+ mp->do_cleanup = do_cleanup;
+
+ vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp);
+}
+
+static vapi_error_e
+vapi_sock_client_send_disconnect (vapi_ctx_t ctx)
+{
+ vl_api_sockclnt_delete_t *mp;
+
+ mp = vapi_msg_alloc (ctx, sizeof (vl_api_sockclnt_delete_t));
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_SOCKCLNT_DELETE);
+ mp->client_index = ctx->my_client_index;
+
+ return vapi_sock_send (ctx, (void *) mp);
+}
+
+static int
+vapi_shm_client_disconnect (vapi_ctx_t ctx)
+{
+ vl_api_memclnt_delete_reply_t *rp;
+ svm_queue_t *vl_input_queue;
+ time_t begin;
+ msgbuf_t *msgbuf;
+
+ vl_input_queue = ctx->vl_input_queue;
+ vapi_shm_client_send_disconnect (ctx, 0 /* wait for reply */);
+
+ /*
+ * Have to be careful here, in case the client is disconnecting
+ * because e.g. the vlib process died, or is unresponsive.
+ */
+ begin = time (0);
+ while (1)
+ {
+ time_t now;
+
+ now = time (0);
+
+ if (now >= (begin + 2))
+ {
+ clib_warning ("peer unresponsive, give up");
+ ctx->my_client_index = ~0;
+ return VAPI_ENORESP;
+ }
+ if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
+ continue;
+
+ VL_MSG_API_UNPOISON (rp);
+
+ /* drain the queue */
+ if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
+ {
+ clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
+ msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
+ vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
+ continue;
+ }
+ msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
+ vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
+ break;
+ }
+
+ vapi_api_name_and_crc_free (ctx);
+ return 0;
+}
+
+static vapi_error_e
+vapi_sock_client_disconnect (vapi_ctx_t ctx)
+{
+ vl_api_sockclnt_delete_reply_t *rp;
+ u8 *msg = 0;
+ msgbuf_t *msgbuf;
+ int rv;
+ f64 deadline;
+
+ deadline = clib_time_now (&ctx->time) + 2;
+
+ do
+ {
+ rv = vapi_sock_client_send_disconnect (ctx);
+ }
+ while (clib_time_now (&ctx->time) < deadline && rv != VAPI_OK);
+
+ while (1)
+ {
+ if (clib_time_now (&ctx->time) >= deadline)
+ {
+ clib_warning ("peer unresponsive, give up");
+ ctx->my_client_index = ~0;
+ return VAPI_ENORESP;
+ }
+
+ if (vapi_sock_recv_internal (ctx, &msg, 0) != VAPI_OK)
+ continue;
+
+ msgbuf = (void *) msg;
+ rp = (void *) msgbuf->data;
+ /* drain the queue */
+ if (ntohs (rp->_vl_msg_id) != VL_API_SOCKCLNT_DELETE_REPLY)
+ {
+ clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
+ msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
+ vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
+ continue;
+ }
+ msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
+ vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
+ break;
+ }
+
+ clib_socket_close (&ctx->client_socket);
+ vapi_api_name_and_crc_free (ctx);
+ return VAPI_OK;
+}
+
+int
+vapi_client_disconnect (vapi_ctx_t ctx)
+{
+ if (ctx->use_uds)
+ {
+ return vapi_sock_client_disconnect (ctx);
+ }
+ return vapi_shm_client_disconnect (ctx);
+}
+
u32
vapi_api_get_msg_index (vapi_ctx_t ctx, u8 *name_and_crc)
{
@@ -477,9 +956,9 @@ vapi_api_get_msg_index (vapi_ctx_t ctx, u8 *name_and_crc)
}
vapi_error_e
-vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix,
- int max_outstanding_requests, int response_queue_size,
- vapi_mode_e mode, bool handle_keepalives)
+vapi_connect_ex (vapi_ctx_t ctx, const char *name, const char *path,
+ int max_outstanding_requests, int response_queue_size,
+ vapi_mode_e mode, bool handle_keepalives, bool use_uds)
{
int rv;
@@ -487,7 +966,8 @@ vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix,
{
return VAPI_EINVAL;
}
- if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024 * 1024 * 32))
+
+ if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024L * 1024 * 32))
{
return VAPI_ENOMEM;
}
@@ -503,28 +983,39 @@ vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix,
clib_memset (ctx->requests, 0, size);
/* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
ctx->requests_start = ctx->requests_count = 0;
+ ctx->use_uds = use_uds;
- if (chroot_prefix)
+ if (use_uds)
{
- VAPI_DBG ("set memory root path `%s'", chroot_prefix);
- vl_set_memory_root_path ((char *) chroot_prefix);
- }
- static char api_map[] = "/vpe-api";
- VAPI_DBG ("client api map `%s'", api_map);
- if ((rv = vl_map_shmem (api_map, 0 /* is_vlib */)) < 0)
- {
- return VAPI_EMAP_FAIL;
+ if (vapi_sock_client_connect (ctx, (char *) path, name) < 0)
+ {
+ return VAPI_ECON_FAIL;
+ }
}
- VAPI_DBG ("connect client `%s'", name);
- if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size, true) <
- 0)
+ else
{
- vl_client_api_unmap ();
- return VAPI_ECON_FAIL;
- }
+ if (path)
+ {
+ VAPI_DBG ("set memory root path `%s'", path);
+ vl_set_memory_root_path ((char *) path);
+ }
+ static char api_map[] = "/vpe-api";
+ VAPI_DBG ("client api map `%s'", api_map);
+ if ((rv = vl_map_shmem (api_map, 0 /* is_vlib */)) < 0)
+ {
+ return VAPI_EMAP_FAIL;
+ }
+ VAPI_DBG ("connect client `%s'", name);
+ if (vapi_shm_client_connect (ctx, (char *) name, 0, response_queue_size,
+ true) < 0)
+ {
+ vl_client_api_unmap ();
+ return VAPI_ECON_FAIL;
+ }
#if VAPI_DEBUG_CONNECT
VAPI_DBG ("start probing messages");
#endif
+ }
int i;
for (i = 0; i < __vapi_metadata.count; ++i)
@@ -592,11 +1083,20 @@ vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix,
}
return VAPI_OK;
fail:
- vl_client_disconnect ();
+ vapi_client_disconnect (ctx);
vl_client_api_unmap ();
return rv;
}
+vapi_error_e
+vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix,
+ int max_outstanding_requests, int response_queue_size,
+ vapi_mode_e mode, bool handle_keepalives)
+{
+ return vapi_connect_ex (ctx, name, chroot_prefix, max_outstanding_requests,
+ response_queue_size, mode, handle_keepalives, false);
+}
+
/*
* API client running in the same process as VPP
*/
@@ -607,6 +1107,11 @@ vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name,
{
int rv;
+ if (ctx->use_uds)
+ {
+ return VAPI_ENOTSUP;
+ }
+
if (response_queue_size <= 0 || max_outstanding_requests <= 0)
{
return VAPI_EINVAL;
@@ -625,8 +1130,8 @@ vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name,
ctx->requests_start = ctx->requests_count = 0;
VAPI_DBG ("connect client `%s'", name);
- if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size,
- handle_keepalives) < 0)
+ if (vapi_shm_client_connect (ctx, (char *) name, 0, response_queue_size,
+ handle_keepalives) < 0)
{
return VAPI_ECON_FAIL;
}
@@ -689,7 +1194,7 @@ vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name,
}
return VAPI_OK;
fail:
- vl_client_disconnect ();
+ vapi_client_disconnect (ctx);
return rv;
}
@@ -700,11 +1205,17 @@ vapi_disconnect_from_vpp (vapi_ctx_t ctx)
{
return VAPI_EINVAL;
}
+
+ if (ctx->use_uds)
+ {
+ return VAPI_ENOTSUP;
+ }
+
vl_api_memclnt_delete_reply_t *rp;
svm_queue_t *vl_input_queue;
time_t begin;
vl_input_queue = ctx->vl_input_queue;
- vl_client_send_disconnect (0 /* wait for reply */);
+ vapi_shm_client_send_disconnect (ctx, 0 /* wait for reply */);
/*
* Have to be careful here, in case the client is disconnecting
@@ -748,19 +1259,14 @@ fail:
return rv;
}
-vapi_error_e
-vapi_disconnect (vapi_ctx_t ctx)
+static vapi_error_e
+vapi_shm_disconnect (vapi_ctx_t ctx)
{
- if (!ctx->connected)
- {
- return VAPI_EINVAL;
- }
-
vl_api_memclnt_delete_reply_t *rp;
svm_queue_t *vl_input_queue;
time_t begin;
vl_input_queue = ctx->vl_input_queue;
- vl_client_send_disconnect (0 /* wait for reply */);
+ vapi_shm_client_send_disconnect (ctx, 0 /* wait for reply */);
/*
* Have to be careful here, in case the client is disconnecting
@@ -808,90 +1314,162 @@ fail:
return rv;
}
+static vapi_error_e
+vapi_sock_disconnect (vapi_ctx_t ctx)
+{
+ vl_api_sockclnt_delete_reply_t *rp;
+ time_t begin;
+ u8 *msg = 0;
+
+ vapi_sock_client_send_disconnect (ctx);
+
+ begin = time (0);
+ vapi_error_e rv = VAPI_OK;
+ while (1)
+ {
+ time_t now;
+
+ now = time (0);
+
+ if (now >= (begin + 2))
+ {
+ clib_warning ("peer unresponsive, give up");
+ ctx->my_client_index = ~0;
+ rv = VAPI_ENORESP;
+ goto fail;
+ }
+ if (vapi_sock_recv_internal (ctx, &msg, 0) < 0)
+ continue;
+
+ if (vec_len (msg) == 0)
+ continue;
+
+ rp = (void *) msg;
+
+ /* drain the queue */
+ if (ntohs (rp->_vl_msg_id) != VL_API_SOCKCLNT_DELETE_REPLY)
+ {
+ clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
+ continue;
+ }
+ vapi_sockclnt_delete_reply_t_handler (
+ ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/);
+ break;
+ }
+fail:
+ clib_socket_close (&ctx->client_socket);
+ vapi_api_name_and_crc_free (ctx);
+
+ ctx->connected = false;
+ return rv;
+}
+
vapi_error_e
-vapi_get_fd (vapi_ctx_t ctx, int *fd)
+vapi_disconnect (vapi_ctx_t ctx)
{
- return VAPI_ENOTSUP;
+ if (!ctx->connected)
+ {
+ return VAPI_EINVAL;
+ }
+
+ if (ctx->use_uds)
+ {
+ return vapi_sock_disconnect (ctx);
+ }
+ return vapi_shm_disconnect (ctx);
}
vapi_error_e
-vapi_send (vapi_ctx_t ctx, void *msg)
+vapi_get_fd (vapi_ctx_t ctx, int *fd)
{
- vapi_error_e rv = VAPI_OK;
- if (!ctx || !msg || !ctx->connected)
+ if (ctx->use_uds && fd)
{
- rv = VAPI_EINVAL;
- goto out;
+ *fd = ctx->client_socket.fd;
+ return VAPI_OK;
}
- int tmp;
- svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
+ return VAPI_ENOTSUP;
+}
+
#if VAPI_DEBUG
+static void
+vapi_debug_log (vapi_ctx_t ctx, void *msg, const char *fun)
+{
unsigned msgid = be16toh (*(u16 *) msg);
if (msgid <= ctx->vl_msg_id_max)
{
vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
if (id < __vapi_metadata.count)
{
- VAPI_DBG ("send msg@%p:%u[%s]", msg, msgid,
+ VAPI_DBG ("%s msg@%p:%u[%s]", fun, msg, msgid,
__vapi_metadata.msgs[id]->name);
}
else
{
- VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
+ VAPI_DBG ("%s msg@%p:%u[UNKNOWN]", fun, msg, msgid);
}
}
else
{
- VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
+ VAPI_DBG ("%s msg@%p:%u[UNKNOWN]", fun, msg, msgid);
}
+}
+#endif
+
+static vapi_error_e
+vapi_shm_send (vapi_ctx_t ctx, void *msg)
+{
+ int rv = VAPI_OK;
+ int tmp;
+ svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
+#if VAPI_DEBUG
+ vapi_debug_log (ctx, msg, "send");
#endif
- tmp = svm_queue_add (q, (u8 *) & msg,
- VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
+ tmp =
+ svm_queue_add (q, (u8 *) &msg, VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
if (tmp < 0)
{
rv = VAPI_EAGAIN;
}
else
VL_MSG_API_POISON (msg);
-out:
- VAPI_DBG ("vapi_send() rv = %d", rv);
+
return rv;
}
vapi_error_e
-vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
+vapi_send (vapi_ctx_t ctx, void *msg)
{
vapi_error_e rv = VAPI_OK;
- if (!ctx || !msg1 || !msg2 || !ctx->connected)
+ if (!ctx || !msg || !ctx->connected)
{
rv = VAPI_EINVAL;
goto out;
}
- svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
-#if VAPI_DEBUG
- unsigned msgid1 = be16toh (*(u16 *) msg1);
- unsigned msgid2 = be16toh (*(u16 *) msg2);
- const char *name1 = "UNKNOWN";
- const char *name2 = "UNKNOWN";
- if (msgid1 <= ctx->vl_msg_id_max)
+
+ if (ctx->use_uds)
{
- vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid1];
- if (id < __vapi_metadata.count)
- {
- name1 = __vapi_metadata.msgs[id]->name;
- }
+ rv = vapi_sock_send (ctx, msg);
}
- if (msgid2 <= ctx->vl_msg_id_max)
+ else
{
- vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid2];
- if (id < __vapi_metadata.count)
- {
- name2 = __vapi_metadata.msgs[id]->name;
- }
+ rv = vapi_shm_send (ctx, msg);
}
- VAPI_DBG ("send two: %u[%s], %u[%s]", msgid1, name1, msgid2, name2);
+
+out:
+ VAPI_DBG ("vapi_send() rv = %d", rv);
+ return rv;
+}
+
+static vapi_error_e
+vapi_shm_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
+{
+ vapi_error_e rv = VAPI_OK;
+ svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
+#if VAPI_DEBUG
+ vapi_debug_log (ctx, msg1, "send2");
+ vapi_debug_log (ctx, msg2, "send2");
#endif
- int tmp = svm_queue_add2 (q, (u8 *) & msg1, (u8 *) & msg2,
+ int tmp = svm_queue_add2 (q, (u8 *) &msg1, (u8 *) &msg2,
VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
if (tmp < 0)
{
@@ -899,31 +1477,52 @@ vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
}
else
VL_MSG_API_POISON (msg1);
-out:
- VAPI_DBG ("vapi_send() rv = %d", rv);
+
return rv;
}
vapi_error_e
-vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size,
- svm_q_conditional_wait_t cond, u32 time)
+vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
{
- if (!ctx || !ctx->connected || !msg || !msg_size)
+ vapi_error_e rv = VAPI_OK;
+ if (!ctx || !msg1 || !msg2 || !ctx->connected)
{
- return VAPI_EINVAL;
+ rv = VAPI_EINVAL;
+ goto out;
}
+
+ if (ctx->use_uds)
+ {
+ rv = vapi_sock_send2 (ctx, msg1, msg2);
+ }
+ else
+ {
+ rv = vapi_shm_send2 (ctx, msg1, msg2);
+ }
+
+out:
+ VAPI_DBG ("vapi_send() rv = %d", rv);
+ return rv;
+}
+
+static vapi_error_e
+vapi_shm_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size,
+ svm_q_conditional_wait_t cond, u32 time)
+{
vapi_error_e rv = VAPI_OK;
uword data;
svm_queue_t *q = ctx->vl_input_queue;
-again:
VAPI_DBG ("doing shm queue sub");
int tmp = svm_queue_sub (q, (u8 *) & data, cond, time);
- if (tmp == 0)
+ if (tmp != 0)
{
+ return VAPI_EAGAIN;
+ }
+
VL_MSG_API_UNPOISON ((void *) data);
#if VAPI_DEBUG_ALLOC
vapi_add_to_be_freed ((void *) data);
@@ -937,61 +1536,99 @@ again:
}
*msg = (u8 *) data;
*msg_size = ntohl (msgbuf->data_len);
+
#if VAPI_DEBUG
- unsigned msgid = be16toh (*(u16 *) * msg);
- if (msgid <= ctx->vl_msg_id_max)
- {
- vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
- if (id < __vapi_metadata.count)
- {
- VAPI_DBG ("recv msg@%p:%u[%s]", *msg, msgid,
- __vapi_metadata.msgs[id]->name);
- }
- else
- {
- VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
- }
- }
- else
- {
- VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
- }
+ vapi_debug_log (ctx, msg, "recv");
#endif
- if (ctx->handle_keepalives)
+
+ return rv;
+}
+
+static vapi_error_e
+vapi_sock_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size, u32 time)
+{
+ vapi_error_e rv = VAPI_OK;
+ u8 *data = 0;
+ if (time == 0 && ctx->mode == VAPI_MODE_BLOCKING)
+ time = 1;
+
+ rv = vapi_sock_recv_internal (ctx, &data, time);
+
+ if (rv != VAPI_OK)
+ {
+ return rv;
+ }
+
+ *msg = data;
+ *msg_size = vec_len (data);
+
+#if VAPI_DEBUG
+ vapi_debug_log (ctx, msg, "recv");
+#endif
+
+ return rv;
+}
+
+vapi_error_e
+vapi_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size,
+ svm_q_conditional_wait_t cond, u32 time)
+{
+ if (!ctx || !ctx->connected || !msg || !msg_size)
+ {
+ return VAPI_EINVAL;
+ }
+ vapi_error_e rv = VAPI_OK;
+
+again:
+ if (ctx->use_uds)
+ {
+ rv = vapi_sock_recv (ctx, msg, msg_size, time);
+ }
+ else
+ {
+ rv = vapi_shm_recv (ctx, msg, msg_size, cond, time);
+ }
+
+ if (rv != VAPI_OK)
+ return rv;
+
+ if (ctx->handle_keepalives)
+ {
+ unsigned msgid = be16toh (*(u16 *) *msg);
+ if (msgid == vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive))
{
- unsigned msgid = be16toh (*(u16 *) * msg);
- if (msgid ==
- vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive))
+ vapi_msg_memclnt_keepalive_reply *reply = NULL;
+ do
{
- vapi_msg_memclnt_keepalive_reply *reply = NULL;
- do
- {
- reply = vapi_msg_alloc (ctx, sizeof (*reply));
- }
- while (!reply);
- reply->header.context = vapi_get_client_index (ctx);
- reply->header._vl_msg_id =
- vapi_lookup_vl_msg_id (ctx,
- vapi_msg_id_memclnt_keepalive_reply);
- reply->payload.retval = 0;
- vapi_msg_memclnt_keepalive_reply_hton (reply);
- while (VAPI_EAGAIN == vapi_send (ctx, reply));
- vapi_msg_free (ctx, *msg);
- goto again;
+ reply = vapi_msg_alloc (ctx, sizeof (*reply));
}
+ while (!reply);
+ reply->header.context = vapi_get_client_index (ctx);
+ reply->header._vl_msg_id =
+ vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive_reply);
+ reply->payload.retval = 0;
+ vapi_msg_memclnt_keepalive_reply_hton (reply);
+ while (VAPI_EAGAIN == vapi_send (ctx, reply))
+ ;
+ vapi_msg_free (ctx, *msg);
+ goto again;
}
}
- else
- {
- rv = VAPI_EAGAIN;
- }
+
return rv;
}
vapi_error_e
-vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode)
+vapi_wait (vapi_ctx_t ctx)
{
- return VAPI_ENOTSUP;
+ if (ctx->use_uds)
+ return VAPI_ENOTSUP;
+
+ svm_queue_lock (ctx->vl_input_queue);
+ svm_queue_wait (ctx->vl_input_queue);
+ svm_queue_unlock (ctx->vl_input_queue);
+
+ return VAPI_OK;
}
static vapi_error_e
@@ -1042,8 +1679,34 @@ vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id,
int payload_offset = vapi_get_payload_offset (id);
void *payload = ((u8 *) msg) + payload_offset;
bool is_last = true;
- if (ctx->requests[tmp].is_dump)
+ switch (ctx->requests[tmp].type)
{
+ case VAPI_REQUEST_STREAM:
+ if (ctx->requests[tmp].response_id == id)
+ {
+ is_last = false;
+ }
+ else
+ {
+ VAPI_DBG ("Stream response ID doesn't match current ID, move to "
+ "next ID");
+ clib_memset (&ctx->requests[tmp], 0,
+ sizeof (ctx->requests[tmp]));
+ ++ctx->requests_start;
+ --ctx->requests_count;
+ if (ctx->requests_start == ctx->requests_size)
+ {
+ ctx->requests_start = 0;
+ }
+ tmp = ctx->requests_start;
+ if (ctx->requests[tmp].context != context)
+ {
+ VAPI_ERR ("Unexpected context %u, expected context %u!",
+ ctx->requests[tmp].context, context);
+ }
+ }
+ break;
+ case VAPI_REQUEST_DUMP:
if (vapi_msg_id_control_ping_reply == id)
{
payload = NULL;
@@ -1052,6 +1715,9 @@ vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id,
{
is_last = false;
}
+ break;
+ case VAPI_REQUEST_REG:
+ break;
}
if (payload_offset != -1)
{
@@ -1155,13 +1821,13 @@ vapi_dispatch_one (vapi_ctx_t ctx)
return VAPI_EINVAL;
}
const vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[vpp_id];
+ vapi_get_swap_to_host_func (id) (msg);
if (vapi_verify_msg_size (id, msg, size))
{
vapi_msg_free (ctx, msg);
return VAPI_EINVAL;
}
u32 context;
- vapi_get_swap_to_host_func (id) (msg);
if (vapi_msg_is_with_context (id))
{
context = *(u32 *) (((u8 *) msg) + vapi_get_context_offset (id));
@@ -1346,6 +2012,16 @@ vapi_get_msg_name (vapi_msg_id_t id)
return __vapi_metadata.msgs[id]->name;
}
+void
+vapi_stop_rx_thread (vapi_ctx_t ctx)
+{
+ if (!ctx || !ctx->connected || !ctx->vl_input_queue)
+ {
+ return;
+ }
+
+ vl_client_stop_rx_thread (ctx->vl_input_queue);
+}
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vpp-api/vapi/vapi.h b/src/vpp-api/vapi/vapi.h
index 46666293e4b..970c5080667 100644
--- a/src/vpp-api/vapi/vapi.h
+++ b/src/vpp-api/vapi/vapi.h
@@ -108,6 +108,25 @@ vapi_error_e vapi_connect (vapi_ctx_t ctx, const char *name,
bool handle_keepalives);
/**
+ * @brief connect to vpp
+ *
+ * @param ctx opaque vapi context, must be allocated using vapi_ctx_alloc first
+ * @param name application name
+ * @param path shared memory prefix or path to unix socket
+ * @param max_outstanding_requests max number of outstanding requests queued
+ * @param response_queue_size size of the response queue
+ * @param mode mode of operation - blocking or nonblocking
+ * @param handle_keepalives - if true, automatically handle memclnt_keepalive
+ * @param use_uds - if true, use unix domain socket transport
+ *
+ * @return VAPI_OK on success, other error code on error
+ */
+vapi_error_e vapi_connect_ex (vapi_ctx_t ctx, const char *name,
+ const char *path, int max_outstanding_requests,
+ int response_queue_size, vapi_mode_e mode,
+ bool handle_keepalives, bool use_uds);
+
+/**
* @brief connect to vpp from a client in same process
* @remark This MUST be called from a separate thread. If called
* from the main thread, it will deadlock.
@@ -175,7 +194,7 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
*
* @return VAPI_OK on success, other error code on error
*/
- vapi_error_e vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2);
+vapi_error_e vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2);
/**
* @brief low-level api for reading messages from vpp
@@ -191,25 +210,24 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
*
* @return VAPI_OK on success, other error code on error
*/
- vapi_error_e vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size,
- svm_q_conditional_wait_t cond, u32 time);
+vapi_error_e vapi_recv (vapi_ctx_t ctx, void **msg, size_t *msg_size,
+ svm_q_conditional_wait_t cond, u32 time);
/**
- * @brief wait for connection to become readable or writable
+ * @brief wait for connection to become readable
*
* @param ctx opaque vapi context
- * @param mode type of property to wait for - readability, writability or both
*
* @return VAPI_OK on success, other error code on error
*/
- vapi_error_e vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode);
+vapi_error_e vapi_wait (vapi_ctx_t ctx);
/**
* @brief pick next message sent by vpp and call the appropriate callback
*
* @return VAPI_OK on success, other error code on error
*/
- vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx);
+vapi_error_e vapi_dispatch_one (vapi_ctx_t ctx);
/**
* @brief loop vapi_dispatch_one until responses to all currently outstanding
@@ -225,11 +243,11 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
*
* @return VAPI_OK on success, other error code on error
*/
- vapi_error_e vapi_dispatch (vapi_ctx_t ctx);
+vapi_error_e vapi_dispatch (vapi_ctx_t ctx);
/** generic vapi event callback */
- typedef vapi_error_e (*vapi_event_cb) (vapi_ctx_t ctx, void *callback_ctx,
- void *payload);
+typedef vapi_error_e (*vapi_event_cb) (vapi_ctx_t ctx, void *callback_ctx,
+ void *payload);
/**
* @brief set event callback to call when message with given id is dispatched
@@ -239,8 +257,8 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
* @param callback callback
* @param callback_ctx context pointer stored and passed to callback
*/
- void vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
- vapi_event_cb callback, void *callback_ctx);
+void vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
+ vapi_event_cb callback, void *callback_ctx);
/**
* @brief clear event callback for given message id
@@ -248,12 +266,12 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
* @param ctx opaque vapi context
* @param id message id
*/
- void vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id);
+void vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id);
/** generic vapi event callback */
- typedef vapi_error_e (*vapi_generic_event_cb) (vapi_ctx_t ctx,
- void *callback_ctx,
- vapi_msg_id_t id, void *msg);
+typedef vapi_error_e (*vapi_generic_event_cb) (vapi_ctx_t ctx,
+ void *callback_ctx,
+ vapi_msg_id_t id, void *msg);
/**
* @brief set generic event callback
*
@@ -264,16 +282,29 @@ vapi_error_e vapi_send (vapi_ctx_t ctx, void *msg);
* @param callback callback
* @param callback_ctx context pointer stored and passed to callback
*/
- void vapi_set_generic_event_cb (vapi_ctx_t ctx,
- vapi_generic_event_cb callback,
- void *callback_ctx);
+void vapi_set_generic_event_cb (vapi_ctx_t ctx, vapi_generic_event_cb callback,
+ void *callback_ctx);
/**
* @brief clear generic event callback
*
* @param ctx opaque vapi context
*/
- void vapi_clear_generic_event_cb (vapi_ctx_t ctx);
+void vapi_clear_generic_event_cb (vapi_ctx_t ctx);
+
+/**
+ * @brief signal RX thread to exit
+ *
+ * @note This adds a message to the client input queue that indicates that
+ * an RX thread should stop processing incoming messages and exit. If an
+ * application has an RX thread which sleeps while waiting for incoming
+ * messages using vapi_wait(), this call will allow the application to
+ * wake up from the vapi_wait() call and figure out that it should stop
+ * running.
+ *
+ * @param ctx opaque vapi context
+ */
+void vapi_stop_rx_thread (vapi_ctx_t ctx);
#ifdef __cplusplus
}
diff --git a/src/vpp-api/vapi/vapi.hpp b/src/vpp-api/vapi/vapi.hpp
index a1e33a93fd4..34d8f97ad89 100644
--- a/src/vpp-api/vapi/vapi.hpp
+++ b/src/vpp-api/vapi/vapi.hpp
@@ -140,6 +140,10 @@ private:
template <typename Req, typename Resp, typename... Args> friend class Dump;
+ template <typename Req, typename Resp, typename StreamMessage,
+ typename... Args>
+ friend class Stream;
+
template <typename M> friend class Event_registration;
};
@@ -199,13 +203,14 @@ public:
*
* @return VAPI_OK on success, other error code on error
*/
- vapi_error_e connect (const char *name, const char *chroot_prefix,
- int max_outstanding_requests, int response_queue_size,
- bool handle_keepalives = true)
+ vapi_error_e
+ connect (const char *name, const char *chroot_prefix,
+ int max_outstanding_requests, int response_queue_size,
+ bool handle_keepalives = true, bool use_uds = false)
{
- return vapi_connect (vapi_ctx, name, chroot_prefix,
- max_outstanding_requests, response_queue_size,
- VAPI_MODE_BLOCKING, handle_keepalives);
+ return vapi_connect_ex (vapi_ctx, name, chroot_prefix,
+ max_outstanding_requests, response_queue_size,
+ VAPI_MODE_BLOCKING, handle_keepalives, use_uds);
}
/**
@@ -417,7 +422,7 @@ private:
void unregister_request (Common_req *request)
{
std::lock_guard<std::recursive_mutex> lock (requests_mutex);
- std::remove (requests.begin (), requests.end (), request);
+ requests.erase (std::remove (requests.begin (), requests.end (), request));
}
template <typename M> void register_event (Event_registration<M> *event)
@@ -451,6 +456,10 @@ private:
template <typename Req, typename Resp, typename... Args> friend class Dump;
+ template <typename Req, typename Resp, typename StreamMessage,
+ typename... Args>
+ friend class Stream;
+
template <typename M> friend class Result_set;
template <typename M> friend class Event_registration;
@@ -497,6 +506,10 @@ template <typename Req, typename Resp, typename... Args> class Request;
template <typename Req, typename Resp, typename... Args> class Dump;
+template <typename Req, typename Resp, typename StreamMessage,
+ typename... Args>
+class Stream;
+
template <class, class = void> struct vapi_has_payload_trait : std::false_type
{
};
@@ -627,6 +640,10 @@ private:
template <typename Req, typename Resp, typename... Args> friend class Dump;
+ template <typename Req, typename Resp, typename StreamMessage,
+ typename... Args>
+ friend class Stream;
+
template <typename X> friend class Event_registration;
template <typename X> friend class Result_set;
@@ -644,10 +661,11 @@ class Request : public Common_req
{
public:
Request (Connection &con, Args... args,
- std::function<vapi_error_e (Request<Req, Resp, Args...> &)>
- callback = nullptr)
- : Common_req{con}, callback{callback},
- request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr}
+ std::function<vapi_error_e (Request<Req, Resp, Args...> &)>
+ callback = nullptr)
+ : Common_req{ con }, callback{ std::move (callback) },
+ request{ con, vapi_alloc<Req> (con, args...) }, response{ con,
+ nullptr }
{
}
@@ -772,12 +790,96 @@ private:
bool complete;
std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set;
+ template <typename Req, typename Resp, typename StreamMessage,
+ typename... Args>
+ friend class Stream;
+
template <typename Req, typename Resp, typename... Args> friend class Dump;
template <typename X> friend class Event_registration;
};
/**
+ * Class representing a RPC request - zero or more identical responses to a
+ * single request message with a response
+ */
+template <typename Req, typename Resp, typename StreamMessage,
+ typename... Args>
+class Stream : public Common_req
+{
+public:
+ Stream (
+ Connection &con, Args... args,
+ std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
+ cb = nullptr)
+ : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
+ response{ con, nullptr }, result_set{ con }, callback{ std::move (cb) }
+ {
+ }
+
+ Stream (const Stream &) = delete;
+
+ virtual ~Stream () {}
+
+ virtual std::tuple<vapi_error_e, bool>
+ assign_response (vapi_msg_id_t id, void *shm_data)
+ {
+ if (id == response.get_msg_id ())
+ {
+ response.assign_response (id, shm_data);
+ result_set.mark_complete ();
+ set_response_state (RESPONSE_READY);
+ if (nullptr != callback)
+ {
+ return std::make_pair (callback (*this), true);
+ }
+ return std::make_pair (VAPI_OK, true);
+ }
+ else
+ {
+ result_set.assign_response (id, shm_data);
+ }
+ return std::make_pair (VAPI_OK, false);
+ }
+
+ vapi_error_e
+ execute ()
+ {
+ return con.send (this);
+ }
+
+ const Msg<Req> &
+ get_request (void)
+ {
+ return request;
+ }
+
+ const Msg<Resp> &
+ get_response (void)
+ {
+ return response;
+ }
+
+ using resp_type = typename Msg<StreamMessage>::shm_data_type;
+
+ const Result_set<StreamMessage> &
+ get_result_set (void) const
+ {
+ return result_set;
+ }
+
+private:
+ Msg<Req> request;
+ Msg<Resp> response;
+ Result_set<StreamMessage> result_set;
+ std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
+ callback;
+
+ friend class Connection;
+ friend class Result_set<StreamMessage>;
+};
+
+/**
* Class representing a dump request - zero or more identical responses to a
* single request message
*/
@@ -786,10 +888,10 @@ class Dump : public Common_req
{
public:
Dump (Connection &con, Args... args,
- std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
- nullptr)
- : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
- result_set{con}, callback{callback}
+ std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
+ nullptr)
+ : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
+ result_set{ con }, callback{ std::move (callback) }
{
}
@@ -853,9 +955,9 @@ template <typename M> class Event_registration : public Common_req
{
public:
Event_registration (
- Connection &con,
- std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
- : Common_req{con}, result_set{con}, callback{callback}
+ Connection &con,
+ std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
+ : Common_req{ con }, result_set{ con }, callback{ std::move (callback) }
{
if (!con.is_msg_available (M::get_msg_id ()))
{
diff --git a/src/vpp-api/vapi/vapi_c_gen.py b/src/vpp-api/vapi/vapi_c_gen.py
index 2978ebd2077..9d1efb5e438 100755
--- a/src/vpp-api/vapi/vapi_c_gen.py
+++ b/src/vpp-api/vapi/vapi_c_gen.py
@@ -23,7 +23,7 @@ class CField(Field):
return "vapi_type_%s" % self.name
def get_c_def(self):
- if self.type.get_c_name() == "vl_api_string_t":
+ if self.type.get_c_name() == "string":
if self.len:
return "u8 %s[%d];" % (self.name, self.len)
else:
@@ -85,12 +85,15 @@ class CField(Field):
def needs_byte_swap(self):
return self.type.needs_byte_swap()
- def get_vla_field_length_name(self, path):
+ def get_vla_parameter_name(self, path):
return "%s_%s_array_size" % ("_".join(path), self.name)
+ def get_vla_field_name(self, path):
+ return ".".join(path + [self.nelem_field.name])
+
def get_alloc_vla_param_names(self, path):
if self.is_vla():
- result = [self.get_vla_field_length_name(path)]
+ result = [self.get_vla_parameter_name(path)]
else:
result = []
if self.type.has_vla():
@@ -98,20 +101,24 @@ class CField(Field):
result.extend(t)
return result
- def get_vla_calc_size_code(self, prefix, path):
+ def get_vla_calc_size_code(self, prefix, path, is_alloc):
if self.is_vla():
result = [
"sizeof(%s.%s[0]) * %s"
% (
".".join([prefix] + path),
self.name,
- self.get_vla_field_length_name(path),
+ (
+ self.get_vla_parameter_name(path)
+ if is_alloc
+ else "%s.%s" % (prefix, self.get_vla_field_name(path))
+ ),
)
]
else:
result = []
if self.type.has_vla():
- t = self.type.get_vla_calc_size_code(prefix, path + [self.name])
+ t = self.type.get_vla_calc_size_code(prefix, path + [self.name], is_alloc)
result.extend(t)
return result
@@ -123,7 +130,7 @@ class CField(Field):
% (
".".join([prefix] + path),
self.nelem_field.name,
- self.get_vla_field_length_name(path),
+ self.get_vla_parameter_name(path),
)
)
if self.type.has_vla():
@@ -173,17 +180,16 @@ class CStruct(Struct):
for x in f.get_alloc_vla_param_names(path)
]
- def get_vla_calc_size_code(self, prefix, path):
+ def get_vla_calc_size_code(self, prefix, path, is_alloc):
return [
x
for f in self.fields
if f.has_vla()
- for x in f.get_vla_calc_size_code(prefix, path)
+ for x in f.get_vla_calc_size_code(prefix, path, is_alloc)
]
class CSimpleType(SimpleType):
-
swap_to_be_dict = {
"i16": "htobe16",
"u16": "htobe16",
@@ -289,6 +295,8 @@ class CUnion(Union):
class CStructType(StructType, CStruct):
def get_c_name(self):
+ if self.name == "vl_api_string_t":
+ return "vl_api_string_t"
return "vapi_type_%s" % self.name
def get_swap_to_be_func_name(self):
@@ -399,7 +407,9 @@ class CMessage(Message):
" + %s" % x
for f in self.fields
if f.has_vla()
- for x in f.get_vla_calc_size_code("msg->payload", [])
+ for x in f.get_vla_calc_size_code(
+ "msg->payload", [], is_alloc=True
+ )
]
),
),
@@ -443,10 +453,12 @@ class CMessage(Message):
" return sizeof(*msg)%s;"
% "".join(
[
- "+ msg->payload.%s * sizeof(msg->payload.%s[0])"
- % (f.nelem_field.name, f.name)
+ " + %s" % x
for f in self.fields
- if f.nelem_field is not None
+ if f.has_vla()
+ for x in f.get_vla_calc_size_code(
+ "msg->payload", [], is_alloc=False
+ )
]
),
"}",
@@ -478,7 +490,7 @@ class CMessage(Message):
{{
VAPI_ERR("Truncated '{self.name}' msg received, received %lu"
"bytes, expected %lu bytes.", buf_size,
- sizeof({self.get_calc_msg_size_func_name()}));
+ {self.get_calc_msg_size_func_name()}(msg));
return -1;
}}
return 0;
@@ -583,12 +595,16 @@ class CMessage(Message):
' VAPI_DBG("Swapping `%s\'@%%p to big endian", msg);'
% self.get_c_name()
),
- " %s(&msg->header);" % self.header.get_swap_to_be_func_name()
- if self.header is not None
- else "",
- " %s(&msg->payload);" % self.get_swap_payload_to_be_func_name()
- if self.has_payload()
- else "",
+ (
+ " %s(&msg->header);" % self.header.get_swap_to_be_func_name()
+ if self.header is not None
+ else ""
+ ),
+ (
+ " %s(&msg->payload);" % self.get_swap_payload_to_be_func_name()
+ if self.has_payload()
+ else ""
+ ),
"}",
]
)
@@ -602,12 +618,16 @@ class CMessage(Message):
' VAPI_DBG("Swapping `%s\'@%%p to host byte order", msg);'
% self.get_c_name()
),
- " %s(&msg->header);" % self.header.get_swap_to_host_func_name()
- if self.header is not None
- else "",
- " %s(&msg->payload);" % self.get_swap_payload_to_host_func_name()
- if self.has_payload()
- else "",
+ (
+ " %s(&msg->header);" % self.header.get_swap_to_host_func_name()
+ if self.header is not None
+ else ""
+ ),
+ (
+ " %s(&msg->payload);" % self.get_swap_payload_to_host_func_name()
+ if self.has_payload()
+ else ""
+ ),
"}",
]
)
@@ -616,45 +636,66 @@ class CMessage(Message):
return "vapi_%s" % self.name
def get_op_func_decl(self):
- if self.reply.has_payload():
- return "vapi_error_e %s(%s)" % (
- self.get_op_func_name(),
- ",\n ".join(
- [
- "struct vapi_ctx_s *ctx",
- "%s *msg" % self.get_c_name(),
- "vapi_error_e (*callback)(struct vapi_ctx_s *ctx",
- " void *callback_ctx",
- " vapi_error_e rv",
- " bool is_last",
- " %s *reply)"
- % self.reply.get_payload_struct_name(),
- "void *callback_ctx",
- ]
- ),
- )
- else:
- return "vapi_error_e %s(%s)" % (
- self.get_op_func_name(),
- ",\n ".join(
- [
- "struct vapi_ctx_s *ctx",
- "%s *msg" % self.get_c_name(),
- "vapi_error_e (*callback)(struct vapi_ctx_s *ctx",
- " void *callback_ctx",
- " vapi_error_e rv",
- " bool is_last)",
- "void *callback_ctx",
- ]
- ),
- )
+ stream_param_lines = []
+ if self.has_stream_msg:
+ stream_param_lines = [
+ "vapi_error_e (*details_callback)(struct vapi_ctx_s *ctx",
+ " void *callback_ctx",
+ " vapi_error_e rv",
+ " bool is_last",
+ " %s *details)"
+ % self.stream_msg.get_payload_struct_name(),
+ "void *details_callback_ctx",
+ ]
+
+ return "vapi_error_e %s(%s)" % (
+ self.get_op_func_name(),
+ ",\n ".join(
+ [
+ "struct vapi_ctx_s *ctx",
+ "%s *msg" % self.get_c_name(),
+ "vapi_error_e (*reply_callback)(struct vapi_ctx_s *ctx",
+ " void *callback_ctx",
+ " vapi_error_e rv",
+ " bool is_last",
+ " %s *reply)"
+ % self.reply.get_payload_struct_name(),
+ ]
+ + [
+ "void *reply_callback_ctx",
+ ]
+ + stream_param_lines
+ ),
+ )
def get_op_func_def(self):
+ param_check_lines = [" if (!msg || !reply_callback) {"]
+ store_request_lines = [
+ " vapi_store_request(ctx, req_context, %s, %s, "
+ % (
+ self.reply.get_msg_id_name(),
+ "VAPI_REQUEST_DUMP" if self.reply_is_stream else "VAPI_REQUEST_REG",
+ ),
+ " (vapi_cb_t)reply_callback, reply_callback_ctx);",
+ ]
+ if self.has_stream_msg:
+ param_check_lines = [
+ " if (!msg || !reply_callback || !details_callback) {"
+ ]
+ store_request_lines = [
+ f" vapi_store_request(ctx, req_context, {self.stream_msg.get_msg_id_name()}, VAPI_REQUEST_STREAM, ",
+ " (vapi_cb_t)details_callback, details_callback_ctx);",
+ f" vapi_store_request(ctx, req_context, {self.reply.get_msg_id_name()}, VAPI_REQUEST_REG, ",
+ " (vapi_cb_t)reply_callback, reply_callback_ctx);",
+ ]
+
return "\n".join(
[
"%s" % self.get_op_func_decl(),
"{",
- " if (!msg || !callback) {",
+ ]
+ + param_check_lines
+ + [
" return VAPI_EINVAL;",
" }",
" if (vapi_is_nonblocking(ctx) && vapi_requests_full(ctx)) {",
@@ -670,14 +711,12 @@ class CMessage(Message):
(
" if (VAPI_OK == (rv = vapi_send_with_control_ping "
"(ctx, msg, req_context))) {"
- if self.reply_is_stream
+ if (self.reply_is_stream and not self.has_stream_msg)
else " if (VAPI_OK == (rv = vapi_send (ctx, msg))) {"
),
- (
- " vapi_store_request(ctx, req_context, %s, "
- "(vapi_cb_t)callback, callback_ctx);"
- % ("true" if self.reply_is_stream else "false")
- ),
+ ]
+ + store_request_lines
+ + [
" if (VAPI_OK != vapi_producer_unlock (ctx)) {",
" abort (); /* this really shouldn't happen */",
" }",
@@ -762,12 +801,16 @@ class CMessage(Message):
" name_with_crc,",
" sizeof(name_with_crc) - 1,",
" true," if has_context else " false,",
- " offsetof(%s, context)," % self.header.get_c_name()
- if has_context
- else " 0,",
- (" offsetof(%s, payload)," % self.get_c_name())
- if self.has_payload()
- else " VAPI_INVALID_MSG_ID,",
+ (
+ " offsetof(%s, context)," % self.header.get_c_name()
+ if has_context
+ else " 0,"
+ ),
+ (
+ (" offsetof(%s, payload)," % self.get_c_name())
+ if self.has_payload()
+ else " VAPI_INVALID_MSG_ID,"
+ ),
" (verify_msg_size_fn_t)%s," % self.get_verify_msg_size_func_name(),
" (generic_swap_fn_t)%s," % self.get_swap_to_be_func_name(),
" (generic_swap_fn_t)%s," % self.get_swap_to_host_func_name(),
@@ -793,6 +836,8 @@ def emit_definition(parser, json_file, emitted, o):
emit_definition(parser, json_file, emitted, x)
if hasattr(o, "reply"):
emit_definition(parser, json_file, emitted, o.reply)
+ if hasattr(o, "stream_msg"):
+ emit_definition(parser, json_file, emitted, o.stream_msg)
if hasattr(o, "get_c_def"):
if (
o not in parser.enums_by_json[json_file]
@@ -821,14 +866,14 @@ def emit_definition(parser, json_file, emitted, o):
print("%s%s" % (function_attrs, o.get_calc_msg_size_func_def()))
print("")
print("%s%s" % (function_attrs, o.get_verify_msg_size_func_def()))
- if not o.is_reply and not o.is_event:
+ if not o.is_reply and not o.is_event and not o.is_stream:
print("")
print("%s%s" % (function_attrs, o.get_alloc_func_def()))
print("")
print("%s%s" % (function_attrs, o.get_op_func_def()))
print("")
print("%s" % o.get_c_constructor())
- if o.is_reply or o.is_event:
+ if (o.is_reply or o.is_event) and not o.is_stream:
print("")
print("%s%s;" % (function_attrs, o.get_event_cb_func_def()))
elif hasattr(o, "get_swap_to_be_func_def"):
@@ -865,6 +910,20 @@ def gen_json_unified_header(parser, logger, j, io, name):
print("#ifdef __cplusplus")
print('extern "C" {')
print("#endif")
+
+ print("#ifndef __vl_api_string_swap_fns_defined__")
+ print("#define __vl_api_string_swap_fns_defined__")
+ print("")
+ print("#include <vlibapi/api_types.h>")
+ print("")
+ function_attrs = "static inline "
+ o = parser.types["vl_api_string_t"]
+ print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
+ print("")
+ print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
+ print("")
+ print("#endif //__vl_api_string_swap_fns_defined__")
+
if name == "memclnt.api.vapi.h":
print("")
print(
diff --git a/src/vpp-api/vapi/vapi_c_test.c b/src/vpp-api/vapi/vapi_c_test.c
index 99a93fb22fd..7a0e462e40a 100644
--- a/src/vpp-api/vapi/vapi_c_test.c
+++ b/src/vpp-api/vapi/vapi_c_test.c
@@ -28,6 +28,7 @@
#include <vapi/vlib.api.vapi.h>
#include <vapi/vpe.api.vapi.h>
#include <vapi/interface.api.vapi.h>
+#include <vapi/mss_clamp.api.vapi.h>
#include <vapi/l2.api.vapi.h>
#include <fake.api.vapi.h>
@@ -36,11 +37,13 @@
DEFINE_VAPI_MSG_IDS_VPE_API_JSON;
DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON;
+DEFINE_VAPI_MSG_IDS_MSS_CLAMP_API_JSON;
DEFINE_VAPI_MSG_IDS_L2_API_JSON;
DEFINE_VAPI_MSG_IDS_FAKE_API_JSON;
static char *app_name = NULL;
static char *api_prefix = NULL;
+static bool use_uds = false;
static const int max_outstanding_requests = 64;
static const int response_queue_size = 32;
@@ -63,8 +66,9 @@ START_TEST (test_invalid_values)
ck_assert_ptr_eq (NULL, sv);
rv = vapi_send (ctx, sv);
ck_assert_int_eq (VAPI_EINVAL, rv);
- rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests,
- response_queue_size, VAPI_MODE_BLOCKING, true);
+ rv =
+ vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests,
+ response_queue_size, VAPI_MODE_BLOCKING, true, use_uds);
ck_assert_int_eq (VAPI_OK, rv);
rv = vapi_send (ctx, NULL);
ck_assert_int_eq (VAPI_EINVAL, rv);
@@ -365,8 +369,9 @@ START_TEST (test_connect)
vapi_ctx_t ctx;
vapi_error_e rv = vapi_ctx_alloc (&ctx);
ck_assert_int_eq (VAPI_OK, rv);
- rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests,
- response_queue_size, VAPI_MODE_BLOCKING, true);
+ rv =
+ vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests,
+ response_queue_size, VAPI_MODE_BLOCKING, true, use_uds);
ck_assert_int_eq (VAPI_OK, rv);
rv = vapi_disconnect (ctx);
ck_assert_int_eq (VAPI_OK, rv);
@@ -382,8 +387,9 @@ setup_blocking (void)
{
vapi_error_e rv = vapi_ctx_alloc (&ctx);
ck_assert_int_eq (VAPI_OK, rv);
- rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests,
- response_queue_size, VAPI_MODE_BLOCKING, true);
+ rv =
+ vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests,
+ response_queue_size, VAPI_MODE_BLOCKING, true, use_uds);
ck_assert_int_eq (VAPI_OK, rv);
}
@@ -392,8 +398,9 @@ setup_nonblocking (void)
{
vapi_error_e rv = vapi_ctx_alloc (&ctx);
ck_assert_int_eq (VAPI_OK, rv);
- rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests,
- response_queue_size, VAPI_MODE_NONBLOCKING, true);
+ rv = vapi_connect_ex (ctx, app_name, api_prefix, max_outstanding_requests,
+ response_queue_size, VAPI_MODE_NONBLOCKING, true,
+ use_uds);
ck_assert_int_eq (VAPI_OK, rv);
}
@@ -481,6 +488,48 @@ sw_interface_dump_cb (struct vapi_ctx_s *ctx, void *callback_ctx,
return VAPI_OK;
}
+vapi_error_e
+vapi_mss_clamp_enable_disable_reply_cb (
+ struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last,
+ vapi_payload_mss_clamp_enable_disable_reply *reply)
+{
+ bool *x = callback_ctx;
+ *x = true;
+ return VAPI_OK;
+}
+
+vapi_error_e
+vapi_mss_clamp_get_reply_cb (struct vapi_ctx_s *ctx, void *callback_ctx,
+ vapi_error_e rv, bool is_last,
+ vapi_payload_mss_clamp_get_reply *reply)
+{
+ int *counter = callback_ctx;
+ ck_assert_int_gt (*counter, 0); // make sure details were called first
+ ++*counter;
+ ck_assert_int_eq (is_last, true);
+ printf ("Got mss clamp reply error %d\n", rv);
+ ck_assert_int_eq (rv, VAPI_OK);
+ printf ("counter is %d", *counter);
+ return VAPI_OK;
+}
+
+vapi_error_e
+vapi_mss_clamp_get_details_cb (struct vapi_ctx_s *ctx, void *callback_ctx,
+ vapi_error_e rv, bool is_last,
+ vapi_payload_mss_clamp_details *details)
+{
+ int *counter = callback_ctx;
+ ++*counter;
+ if (!is_last)
+ {
+ printf ("Got ipv4 mss clamp to %u for sw_if_index %u\n",
+ details->ipv4_mss, details->sw_if_index);
+ ck_assert_int_eq (details->ipv4_mss, 1000 + details->sw_if_index);
+ }
+ printf ("counter is %d", *counter);
+ return VAPI_OK;
+}
+
START_TEST (test_loopbacks_1)
{
printf ("--- Create/delete loopbacks using blocking API ---\n");
@@ -521,6 +570,37 @@ START_TEST (test_loopbacks_1)
mac_addresses[i][3], mac_addresses[i][4], mac_addresses[i][5],
sw_if_indexes[i]);
}
+
+ { // new context
+ for (int i = 0; i < num_ifs; ++i)
+ {
+ vapi_msg_mss_clamp_enable_disable *mc =
+ vapi_alloc_mss_clamp_enable_disable (ctx);
+ mc->payload.sw_if_index = sw_if_indexes[i];
+ mc->payload.ipv4_mss = 1000 + sw_if_indexes[i];
+ mc->payload.ipv4_direction = MSS_CLAMP_DIR_RX;
+ bool reply_ctx = false;
+ printf ("Set ipv4 mss clamp to %u for sw_if_index %u\n",
+ mc->payload.ipv4_mss, mc->payload.sw_if_index);
+ vapi_error_e rv = vapi_mss_clamp_enable_disable (
+ ctx, mc, vapi_mss_clamp_enable_disable_reply_cb, &reply_ctx);
+ ck_assert_int_eq (VAPI_OK, rv);
+ ck_assert_int_eq (reply_ctx, true);
+ }
+ }
+
+ { // new context
+ int counter = 0;
+ vapi_msg_mss_clamp_get *msg = vapi_alloc_mss_clamp_get (ctx);
+ msg->payload.sw_if_index = ~0;
+ vapi_error_e rv =
+ vapi_mss_clamp_get (ctx, msg, vapi_mss_clamp_get_reply_cb, &counter,
+ vapi_mss_clamp_get_details_cb, &counter);
+ printf ("counter is %d", counter);
+ ck_assert_int_eq (VAPI_OK, rv);
+ ck_assert_int_eq (counter, num_ifs + 1);
+ }
+
bool seen[num_ifs];
sw_interface_dump_ctx dctx = { false, num_ifs, sw_if_indexes, seen, 0 };
vapi_msg_sw_interface_dump *dump;
@@ -530,7 +610,7 @@ START_TEST (test_loopbacks_1)
{
dctx.last_called = false;
clib_memset (&seen, 0, sizeof (seen));
- dump = vapi_alloc_sw_interface_dump (ctx);
+ dump = vapi_alloc_sw_interface_dump (ctx, 0);
while (VAPI_EAGAIN ==
(rv =
vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb,
@@ -559,7 +639,7 @@ START_TEST (test_loopbacks_1)
}
dctx.last_called = false;
clib_memset (&seen, 0, sizeof (seen));
- dump = vapi_alloc_sw_interface_dump (ctx);
+ dump = vapi_alloc_sw_interface_dump (ctx, 0);
while (VAPI_EAGAIN ==
(rv =
vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx)))
@@ -688,7 +768,7 @@ START_TEST (test_loopbacks_2)
bool seen[num_ifs];
clib_memset (&seen, 0, sizeof (seen));
sw_interface_dump_ctx dctx = { false, num_ifs, sw_if_indexes, seen, 0 };
- vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx);
+ vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx, 0);
while (VAPI_EAGAIN ==
(rv =
vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx)))
@@ -728,7 +808,7 @@ START_TEST (test_loopbacks_2)
}
clib_memset (&seen, 0, sizeof (seen));
dctx.last_called = false;
- dump = vapi_alloc_sw_interface_dump (ctx);
+ dump = vapi_alloc_sw_interface_dump (ctx, 0);
while (VAPI_EAGAIN ==
(rv =
vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx)))
@@ -847,7 +927,7 @@ START_TEST (test_no_response_2)
{
printf ("--- Simulate no response to dump message ---\n");
vapi_error_e rv;
- vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx);
+ vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx, 0);
dump->header._vl_msg_id = ~0; /* malformed ID causes vpp to drop the msg */
int no_called = 0;
while (VAPI_EAGAIN ==
@@ -990,13 +1070,23 @@ test_suite (void)
int
main (int argc, char *argv[])
{
- if (3 != argc)
+ if (4 != argc)
{
printf ("Invalid argc==`%d'\n", argc);
return EXIT_FAILURE;
}
app_name = argv[1];
api_prefix = argv[2];
+ if (!strcmp (argv[3], "shm"))
+ use_uds = 0;
+ else if (!strcmp (argv[3], "uds"))
+ use_uds = 1;
+ else
+ {
+ printf ("Unrecognised required argument '%s', expected 'uds' or 'shm'.",
+ argv[3]);
+ return EXIT_FAILURE;
+ }
printf ("App name: `%s', API prefix: `%s'\n", app_name, api_prefix);
int number_failed;
diff --git a/src/vpp-api/vapi/vapi_common.h b/src/vpp-api/vapi/vapi_common.h
index 7157f0a8e0d..69b9b788b51 100644
--- a/src/vpp-api/vapi/vapi_common.h
+++ b/src/vpp-api/vapi/vapi_common.h
@@ -22,37 +22,34 @@
extern "C" {
#endif
-typedef enum
-{
- VAPI_OK = 0, /**< success */
- VAPI_EINVAL, /**< invalid value encountered */
- VAPI_EAGAIN, /**< operation would block */
- VAPI_ENOTSUP, /**< operation not supported */
- VAPI_ENOMEM, /**< out of memory */
- VAPI_ENORESP, /**< no response to request */
- VAPI_EMAP_FAIL, /**< failure while mapping api */
- VAPI_ECON_FAIL, /**< failure while connecting to vpp */
- VAPI_EINCOMPATIBLE, /**< fundamental incompatibility while connecting to vpp
- (control ping/control ping reply mismatch) */
- VAPI_MUTEX_FAILURE, /**< failure manipulating internal mutex(es) */
- VAPI_EUSER, /**< user error used for breaking dispatch,
- never used by VAPI */
-} vapi_error_e;
-
-typedef enum
-{
- VAPI_MODE_BLOCKING = 1, /**< operations block until response received */
- VAPI_MODE_NONBLOCKING = 2, /**< operations never block */
-} vapi_mode_e;
-
-typedef enum
-{
- VAPI_WAIT_FOR_READ, /**< wait until some message is readable */
- VAPI_WAIT_FOR_WRITE, /**< wait until a message can be written */
- VAPI_WAIT_FOR_READ_WRITE, /**< wait until a read or write can be done */
-} vapi_wait_mode_e;
-
-typedef unsigned int vapi_msg_id_t;
+ typedef enum
+ {
+ VAPI_OK = 0, /**< success */
+ VAPI_EINVAL, /**< invalid value encountered */
+ VAPI_EAGAIN, /**< operation would block */
+ VAPI_ENOTSUP, /**< operation not supported */
+ VAPI_ENOMEM, /**< out of memory */
+ VAPI_ENORESP, /**< no response to request */
+ VAPI_EMAP_FAIL, /**< failure while mapping api */
+ VAPI_ECON_FAIL, /**< failure while connecting to vpp */
+ VAPI_EINCOMPATIBLE, /**< fundamental incompatibility while connecting to
+ vpp (control ping/control ping reply mismatch) */
+ VAPI_MUTEX_FAILURE, /**< failure manipulating internal mutex(es) */
+ VAPI_EUSER, /**< user error used for breaking dispatch,
+ never used by VAPI */
+ VAPI_ENOTSOCK, /**< vapi socket doesn't refer to a socket */
+ VAPI_EACCES, /**< no write permission for socket */
+ VAPI_ECONNRESET, /**< connection reset by peer*/
+ VAPI_ESOCK_FAILURE, /**< generic socket failure, check errno */
+ } vapi_error_e;
+
+ typedef enum
+ {
+ VAPI_MODE_BLOCKING = 1, /**< operations block until response received */
+ VAPI_MODE_NONBLOCKING = 2, /**< operations never block */
+ } vapi_mode_e;
+
+ typedef unsigned int vapi_msg_id_t;
#define VAPI_INVALID_MSG_ID ((vapi_msg_id_t)(~0))
diff --git a/src/vpp-api/vapi/vapi_cpp_gen.py b/src/vpp-api/vapi/vapi_cpp_gen.py
index 33744a3d58c..165730ca4b8 100755
--- a/src/vpp-api/vapi/vapi_cpp_gen.py
+++ b/src/vpp-api/vapi/vapi_cpp_gen.py
@@ -96,6 +96,13 @@ class CppMessage(CMessage):
return "%s%s" % (self.name[0].upper(), self.name[1:])
def get_req_template_name(self):
+ if self.has_stream_msg:
+ return "Stream<%s, %s, %s>" % (
+ self.get_c_name(),
+ self.reply.get_c_name(),
+ self.stream_msg.get_c_name(),
+ )
+
if self.reply_is_stream:
template = "Dump"
else:
@@ -196,7 +203,7 @@ def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments):
print("/* m.get_cpp_constructor() */")
print("%s" % m.get_cpp_constructor())
print("")
- if not m.is_reply and not m.is_event:
+ if not m.is_reply and not m.is_event and not m.is_stream:
if add_debug_comments:
print("/* m.get_alloc_template_instantiation() */")
print("%s" % m.get_alloc_template_instantiation())
@@ -210,6 +217,8 @@ def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments):
print("/* m.get_reply_type_alias() */")
print("%s" % m.get_reply_type_alias())
continue
+ if m.is_stream:
+ continue
if add_debug_comments:
print("/* m.get_req_template_instantiation() */")
print("%s" % m.get_req_template_instantiation())
diff --git a/src/vpp-api/vapi/vapi_cpp_test.cpp b/src/vpp-api/vapi/vapi_cpp_test.cpp
index c0e0cdc3ab8..918c7590b60 100644
--- a/src/vpp-api/vapi/vapi_cpp_test.cpp
+++ b/src/vpp-api/vapi/vapi_cpp_test.cpp
@@ -25,14 +25,17 @@
#include <vapi/vapi.hpp>
#include <vapi/vpe.api.vapi.hpp>
#include <vapi/interface.api.vapi.hpp>
+#include <vapi/mss_clamp.api.vapi.hpp>
#include <fake.api.vapi.hpp>
DEFINE_VAPI_MSG_IDS_VPE_API_JSON;
DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON;
+DEFINE_VAPI_MSG_IDS_MSS_CLAMP_API_JSON;
DEFINE_VAPI_MSG_IDS_FAKE_API_JSON;
static char *app_name = nullptr;
static char *api_prefix = nullptr;
+static bool use_uds = false;
static const int max_outstanding_requests = 32;
static const int response_queue_size = 32;
@@ -58,8 +61,9 @@ Connection con;
void setup (void)
{
- vapi_error_e rv = con.connect (
- app_name, api_prefix, max_outstanding_requests, response_queue_size);
+ vapi_error_e rv =
+ con.connect (app_name, api_prefix, max_outstanding_requests,
+ response_queue_size, true, use_uds);
ck_assert_int_eq (VAPI_OK, rv);
}
@@ -145,8 +149,53 @@ START_TEST (test_loopbacks_1)
}
{ // new context
+ for (int i = 0; i < num_ifs; ++i)
+ {
+ Mss_clamp_enable_disable d (con);
+ auto &req = d.get_request ().get_payload ();
+ req.sw_if_index = sw_if_indexes[i];
+ req.ipv4_mss = 1420;
+ req.ipv4_direction = vapi_enum_mss_clamp_dir::MSS_CLAMP_DIR_RX;
+ auto rv = d.execute ();
+ ck_assert_int_eq (VAPI_OK, rv);
+ WAIT_FOR_RESPONSE (d, rv);
+ ck_assert_int_eq (VAPI_OK, rv);
+ }
+ }
+
+ { // new context
+ bool seen[num_ifs] = { 0 };
+ Mss_clamp_get d (con);
+ d.get_request ().get_payload ().sw_if_index = ~0;
+ auto rv = d.execute ();
+ ck_assert_int_eq (VAPI_OK, rv);
+ WAIT_FOR_RESPONSE (d, rv);
+ ck_assert_int_eq (VAPI_OK, rv);
+ auto &rs = d.get_result_set ();
+ for (auto &r : rs)
+ {
+ auto &p = r.get_payload ();
+ ck_assert_int_eq (p.ipv4_mss, 1420);
+ printf ("tcp-clamp: sw_if_idx %u ip4-mss %d dir %d\n", p.sw_if_index,
+ p.ipv4_mss, p.ipv4_direction);
+ for (int i = 0; i < num_ifs; ++i)
+ {
+ if (sw_if_indexes[i] == p.sw_if_index)
+ {
+ ck_assert_int_eq (0, seen[i]);
+ seen[i] = true;
+ }
+ }
+ }
+ for (int i = 0; i < num_ifs; ++i)
+ {
+ ck_assert_int_eq (1, seen[i]);
+ }
+ }
+
+ { // new context
bool seen[num_ifs] = {0};
- Sw_interface_dump d (con);
+ Sw_interface_dump d (con, 0);
auto rv = d.execute ();
ck_assert_int_eq (VAPI_OK, rv);
WAIT_FOR_RESPONSE (d, rv);
@@ -185,7 +234,7 @@ START_TEST (test_loopbacks_1)
}
{ // new context
- Sw_interface_dump d (con);
+ Sw_interface_dump d (con, 0);
auto rv = d.execute ();
ck_assert_int_eq (VAPI_OK, rv);
WAIT_FOR_RESPONSE (d, rv);
@@ -300,7 +349,7 @@ START_TEST (test_loopbacks_2)
}
Sw_interface_dump_cb<num_ifs> swdcb (ccbs);
- Sw_interface_dump d (con, std::ref (swdcb));
+ Sw_interface_dump d (con, 0, std::ref (swdcb));
auto rv = d.execute ();
ck_assert_int_eq (VAPI_OK, rv);
WAIT_FOR_RESPONSE (d, rv);
@@ -326,7 +375,7 @@ START_TEST (test_loopbacks_2)
}
{ // new context
- Sw_interface_dump d (con);
+ Sw_interface_dump d (con, 0);
auto rv = d.execute ();
ck_assert_int_eq (VAPI_OK, rv);
WAIT_FOR_RESPONSE (d, rv);
@@ -405,14 +454,25 @@ Suite *test_suite (void)
int main (int argc, char *argv[])
{
- if (3 != argc)
+ if (4 != argc)
{
printf ("Invalid argc==`%d'\n", argc);
return EXIT_FAILURE;
}
app_name = argv[1];
api_prefix = argv[2];
- printf ("App name: `%s', API prefix: `%s'\n", app_name, api_prefix);
+ if (!strcmp (argv[3], "shm"))
+ use_uds = 0;
+ else if (!strcmp (argv[3], "uds"))
+ use_uds = 1;
+ else
+ {
+ printf ("Unrecognised required argument '%s', expected 'uds' or 'shm'.",
+ argv[3]);
+ return EXIT_FAILURE;
+ }
+ printf ("App name: `%s', API prefix: `%s', use unix sockets %d\n", app_name,
+ api_prefix, use_uds);
int number_failed;
Suite *s;
diff --git a/src/vpp-api/vapi/vapi_internal.h b/src/vpp-api/vapi/vapi_internal.h
index 49c041769d0..ca47dd10459 100644
--- a/src/vpp-api/vapi/vapi_internal.h
+++ b/src/vpp-api/vapi/vapi_internal.h
@@ -118,8 +118,18 @@ bool vapi_requests_full (vapi_ctx_t ctx);
size_t vapi_get_request_count (vapi_ctx_t ctx);
size_t vapi_get_max_request_count (vapi_ctx_t ctx);
u32 vapi_gen_req_context (vapi_ctx_t ctx);
-void vapi_store_request (vapi_ctx_t ctx, u32 context, bool is_dump,
- vapi_cb_t callback, void *callback_ctx);
+
+enum vapi_request_type
+{
+ VAPI_REQUEST_REG = 0,
+ VAPI_REQUEST_DUMP = 1,
+ VAPI_REQUEST_STREAM = 2,
+};
+
+void vapi_store_request (vapi_ctx_t ctx, u32 context,
+ vapi_msg_id_t response_id,
+ enum vapi_request_type type, vapi_cb_t callback,
+ void *callback_ctx);
int vapi_get_payload_offset (vapi_msg_id_t id);
void (*vapi_get_swap_to_host_func (vapi_msg_id_t id)) (void *payload);
void (*vapi_get_swap_to_be_func (vapi_msg_id_t id)) (void *payload);
diff --git a/src/vpp-api/vapi/vapi_json_parser.py b/src/vpp-api/vapi/vapi_json_parser.py
index a323f15e7b6..c06cb8cf77b 100644
--- a/src/vpp-api/vapi/vapi_json_parser.py
+++ b/src/vpp-api/vapi/vapi_json_parser.py
@@ -158,6 +158,7 @@ class Message(object):
self.header = None
self.is_reply = json_parser.is_reply(self.name)
self.is_event = json_parser.is_event(self.name)
+ self.is_stream = json_parser.is_stream(self.name)
fields = []
for header in get_msg_header_defs(
struct_type_class, field_class, json_parser, logger
@@ -196,12 +197,18 @@ class Message(object):
"array `%s' doesn't have reference to member "
"containing the actual length" % (name, field[1])
)
- if field[0] == "string" and field[2] > 0:
- field_type = json_parser.lookup_type_like_id("u8")
+ if field[0] == "string" and field[2] == 0:
+ field_type = json_parser.lookup_type_like_id("vl_api_string_t")
+ p = field_class(field_name=field[1], field_type=field_type)
+ else:
+ if field[0] == "string" and field[2] > 0:
+ field_type = json_parser.lookup_type_like_id("u8")
- p = field_class(
- field_name=field[1], field_type=field_type, array_len=field[2]
- )
+ p = field_class(
+ field_name=field[1],
+ field_type=field_type,
+ array_len=field[2],
+ )
elif l == 4:
nelem_field = None
for f in fields:
@@ -254,13 +261,31 @@ class StructType(Type, Struct):
p = field_class(field_name=field[1], field_type=field_type)
elif len(field) == 3:
if field[2] == 0:
- raise ParseError(
- "While parsing type `%s': array `%s' has "
- "variable length" % (name, field[1])
+ if name == "vl_api_string_t":
+ p = None
+ for f in fields:
+ if f.name == "length":
+ nelem_field = f
+ p = field_class(
+ field_name=field[1],
+ field_type=field_type,
+ array_len=field[2],
+ nelem_field=nelem_field,
+ )
+ break
+ if p is None:
+ raise ParseError(
+ "While parsing type `%s': missing `length'" % name
+ )
+ else:
+ raise ParseError(
+ "While parsing type `%s': array `%s' has "
+ "variable length" % (name, field[1])
+ )
+ else:
+ p = field_class(
+ field_name=field[1], field_type=field_type, array_len=field[2]
)
- p = field_class(
- field_name=field[1], field_type=field_type, array_len=field[2]
- )
elif len(field) == 4:
nelem_field = None
for f in fields:
@@ -324,6 +349,7 @@ class JsonParser(object):
self.services = {}
self.messages = {}
self.enums = {}
+ self.enumflags = {}
self.unions = {}
self.aliases = {}
self.types = {
@@ -342,9 +368,16 @@ class JsonParser(object):
]
}
- self.types["string"] = simple_type_class("vl_api_string_t")
+ self.types["string"] = simple_type_class("u8")
+ self.types["vl_api_string_t"] = struct_type_class(
+ ["vl_api_string_t", ["u32", "length"], ["u8", "buf", 0]],
+ self,
+ field_class,
+ logger,
+ )
self.replies = set()
self.events = set()
+ self.streams = set()
self.simple_type_class = simple_type_class
self.enum_class = enum_class
self.union_class = union_class
@@ -383,6 +416,8 @@ class JsonParser(object):
if "events" in self.services[k]:
for x in self.services[k]["events"]:
self.events.add(x)
+ if "stream_msg" in self.services[k]:
+ self.streams.add(self.services[k]["stream_msg"])
for e in j["enums"]:
name = e[0]
value_pairs = e[1:-1]
@@ -391,6 +426,14 @@ class JsonParser(object):
self.enums[enum.name] = enum
self.logger.debug("Parsed enum: %s" % enum)
self.enums_by_json[path].append(enum)
+ for e in j["enumflags"]:
+ name = e[0]
+ value_pairs = e[1:-1]
+ enumtype = self.types[e[-1]["enumtype"]]
+ enum = self.enum_class(name, value_pairs, enumtype)
+ self.enums[enum.name] = enum
+ self.logger.debug("Parsed enumflag: %s" % enum)
+ self.enums_by_json[path].append(enum)
exceptions = []
progress = 0
last_progress = 0
@@ -485,6 +528,8 @@ class JsonParser(object):
return self.types[name]
elif name in self.enums:
return self.enums[name]
+ elif name in self.enumflags:
+ return self.enumflags[name]
elif name in self.unions:
return self.unions[name]
elif name in self.aliases:
@@ -493,6 +538,8 @@ class JsonParser(object):
return self.types[mundane_name]
elif mundane_name in self.enums:
return self.enums[mundane_name]
+ elif mundane_name in self.enumflags:
+ return self.enumflags[mundane_name]
elif mundane_name in self.unions:
return self.unions[mundane_name]
elif mundane_name in self.aliases:
@@ -508,6 +555,20 @@ class JsonParser(object):
def is_event(self, message):
return message in self.events
+ def is_stream(self, message):
+ return message in self.streams
+
+ def has_stream_msg(self, message):
+ return (
+ message.name in self.services
+ and "stream_msg" in self.services[message.name]
+ )
+
+ def get_stream_msg(self, message):
+ if not self.has_stream_msg(message):
+ return None
+ return self.messages[self.services[message.name]["stream_msg"]]
+
def get_reply(self, message):
return self.messages[self.services[message]["reply"]]
@@ -519,13 +580,15 @@ class JsonParser(object):
remove = []
for n, m in j.items():
try:
- if not m.is_reply and not m.is_event:
+ if not m.is_reply and not m.is_event and not m.is_stream:
try:
m.reply = self.get_reply(n)
+ m.reply_is_stream = False
+ m.has_stream_msg = self.has_stream_msg(m)
if "stream" in self.services[m.name]:
m.reply_is_stream = self.services[m.name]["stream"]
- else:
- m.reply_is_stream = False
+ if m.has_stream_msg:
+ m.stream_msg = self.get_stream_msg(m)
m.reply.request = m
except:
raise ParseError("Cannot find reply to message `%s'" % n)