summaryrefslogtreecommitdiffstats
path: root/src/plugins/cdp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cdp')
-rw-r--r--src/plugins/cdp/cdp.api36
-rw-r--r--src/plugins/cdp/cdp.c222
-rw-r--r--src/plugins/cdp/cdp.h154
-rw-r--r--src/plugins/cdp/cdp.pg7
-rw-r--r--src/plugins/cdp/cdp_all_api_h.h19
-rw-r--r--src/plugins/cdp/cdp_input.c504
-rw-r--r--src/plugins/cdp/cdp_msg_enum.h31
-rw-r--r--src/plugins/cdp/cdp_node.c215
-rw-r--r--src/plugins/cdp/cdp_periodic.c515
-rw-r--r--src/plugins/cdp/cdp_protocol.h186
-rw-r--r--src/plugins/cdp/cdp_test.c179
11 files changed, 2068 insertions, 0 deletions
diff --git a/src/plugins/cdp/cdp.api b/src/plugins/cdp/cdp.api
new file mode 100644
index 00000000000..34507025f42
--- /dev/null
+++ b/src/plugins/cdp/cdp.api
@@ -0,0 +1,36 @@
+/*
+ * Simple enable/disable API for the cdp protocol
+ *
+ * Copyright (c) 2011-2018 by Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+option version = "1.0.0";
+
+autoreply define cdp_enable_disable
+{
+ /* Client identifier, set from api_main.my_client_index */
+ u32 client_index;
+
+ /* Arbitrary context, so client can match reply to request */
+ u32 context;
+
+ /* Enable / disable the feature */
+ u8 enable_disable;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cdp/cdp.c b/src/plugins/cdp/cdp.c
new file mode 100644
index 00000000000..a88a06bc9c5
--- /dev/null
+++ b/src/plugins/cdp/cdp.c
@@ -0,0 +1,222 @@
+/*
+ * cdp.c - cdp protocol plug-in
+ *
+ * Copyright (c) 2011-2018 by Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <cdp/cdp.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vpp/app/version.h>
+
+/* define message IDs */
+#include <cdp/cdp_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <cdp/cdp_all_api_h.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <cdp/cdp_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <cdp/cdp_all_api_h.h>
+#undef vl_printfun
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <cdp/cdp_all_api_h.h>
+#undef vl_api_version
+
+/*
+ * A handy macro to set up a message reply.
+ * Assumes that the following variables are available:
+ * mp - pointer to request message
+ * rmp - pointer to reply message type
+ * rv - return value
+ */
+
+#define REPLY_MACRO(t) \
+do { \
+ unix_shared_memory_queue_t * q = \
+ vl_api_client_index_to_input_queue (mp->client_index); \
+ if (!q) \
+ return; \
+ \
+ rmp = vl_msg_api_alloc (sizeof (*rmp)); \
+ rmp->_vl_msg_id = ntohs((t)+cm->msg_id_base); \
+ rmp->context = mp->context; \
+ rmp->retval = ntohl(rv); \
+ \
+ vl_msg_api_send_shmem (q, (u8 *)&rmp); \
+} while(0);
+
+
+/* List of message types that this plugin understands */
+
+#define foreach_cdp_plugin_api_msg \
+_(CDP_ENABLE_DISABLE, cdp_enable_disable)
+
+/* Action function shared between message handler and debug CLI */
+
+int
+cdp_enable_disable (cdp_main_t * cm, int enable_disable)
+{
+ int rv = 0;
+
+ if (enable_disable)
+ vlib_process_signal_event (cm->vlib_main, cdp_process_node.index,
+ CDP_EVENT_ENABLE, 0);
+ else
+ vlib_process_signal_event (cm->vlib_main, cdp_process_node.index,
+ CDP_EVENT_DISABLE, 0);
+ cm->enabled = enable_disable;
+
+ return rv;
+}
+
+static clib_error_t *
+cdp_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ cdp_main_t *cm = &cdp_main;
+ int enable_disable = 1;
+
+ int rv;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "disable"))
+ enable_disable = 0;
+ else if (unformat (input, "enable"))
+ enable_disable = 1;
+ else
+ break;
+ }
+
+ rv = cdp_enable_disable (cm, enable_disable);
+
+ switch (rv)
+ {
+ case 0:
+ break;
+
+ default:
+ return clib_error_return (0, "cdp_enable_disable returned %d", rv);
+ }
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (cdp_command, static) =
+{
+ .path = "cdp",
+ .short_help = "cdp enable | disable",
+ .function = cdp_command_fn,
+};
+/* *INDENT-ON* */
+
+/* API message handler */
+static void vl_api_cdp_enable_disable_t_handler
+ (vl_api_cdp_enable_disable_t * mp)
+{
+ vl_api_cdp_enable_disable_reply_t *rmp;
+ cdp_main_t *cm = &cdp_main;
+ int rv;
+
+ rv = cdp_enable_disable (cm, (int) (mp->enable_disable));
+
+ REPLY_MACRO (VL_API_CDP_ENABLE_DISABLE_REPLY);
+}
+
+/* Set up the API message handling tables */
+static clib_error_t *
+cdp_plugin_api_hookup (vlib_main_t * vm)
+{
+ cdp_main_t *cm = &cdp_main;
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + cm->msg_id_base), \
+ #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_cdp_plugin_api_msg;
+#undef _
+
+ return 0;
+}
+
+#define vl_msg_name_crc_list
+#include <cdp/cdp_all_api_h.h>
+#undef vl_msg_name_crc_list
+
+static void
+setup_message_id_table (cdp_main_t * cm, api_main_t * am)
+{
+#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n #crc, id + cm->msg_id_base);
+ foreach_vl_msg_name_crc_cdp;
+#undef _
+}
+
+static clib_error_t *
+cdp_init (vlib_main_t * vm)
+{
+ cdp_main_t *cm = &cdp_main;
+ clib_error_t *error = 0;
+ u8 *name;
+
+ cm->vlib_main = vm;
+
+ name = format (0, "cdp_%08x%c", api_version, 0);
+
+ /* Ask for a correctly-sized block of API message decode slots */
+ cm->msg_id_base = vl_msg_api_get_msg_ids
+ ((char *) name, VL_MSG_FIRST_AVAILABLE);
+
+ error = cdp_plugin_api_hookup (vm);
+
+ /* Add our API messages to the global name_crc hash table */
+ setup_message_id_table (cm, &api_main);
+
+ vec_free (name);
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (cdp_init);
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () =
+{
+ .version = VPP_BUILD_VER,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cdp/cdp.h b/src/plugins/cdp/cdp.h
new file mode 100644
index 00000000000..32a07825ecb
--- /dev/null
+++ b/src/plugins/cdp/cdp.h
@@ -0,0 +1,154 @@
+
+/*
+ * cdp.h - cdp protocol plug-in
+ *
+ * Copyright (c) 2011-2018 by Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_cdp_h__
+#define __included_cdp_h__
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+
+#include <vnet/snap/snap.h>
+#include <vnet/hdlc/hdlc.h>
+#include <vnet/hdlc/packet.h>
+
+#include <vppinfra/format.h>
+#include <vppinfra/hash.h>
+
+#include "cdp_protocol.h"
+
+typedef enum
+{
+ CDP_PACKET_TEMPLATE_ETHERNET,
+ CDP_PACKET_TEMPLATE_HDLC,
+ CDP_PACKET_TEMPLATE_SRP,
+ CDP_N_PACKET_TEMPLATES,
+} cdp_packet_template_id_t;
+
+typedef struct
+{
+ /* neighbor's vlib software interface index */
+ u32 sw_if_index;
+
+ /* Timers */
+ f64 last_heard;
+ f64 last_sent;
+
+ /* Neighbor time-to-live (usually 180s) */
+ u8 ttl_in_seconds;
+
+ /* "no cdp run" or similar */
+ u8 disabled;
+
+ /* tx packet template id for this neighbor */
+ u8 packet_template_index;
+
+ /* Jenkins hash optimization: avoid tlv scan, send short keepalive msg */
+ u8 last_packet_signature_valid;
+ uword last_packet_signature;
+
+ /* Info we actually keep about each neighbor */
+ u8 *device_name;
+ u8 *version;
+ u8 *port_id;
+ u8 *platform;
+
+ /* last received packet, for the J-hash optimization */
+ u8 *last_rx_pkt;
+} cdp_neighbor_t;
+
+#define foreach_neighbor_string_field \
+_(device_name) \
+_(version) \
+_(port_id) \
+_(platform)
+
+typedef struct
+{
+ /* pool of cdp neighbors */
+ cdp_neighbor_t *neighbors;
+
+ /* plugin message id base */
+ u16 msg_id_base;
+
+ /* tx pcap debug enable */
+ u8 tx_pcap_debug;
+
+ /* rapidly find a neighbor by vlib software interface index */
+ uword *neighbor_by_sw_if_index;
+
+ /* Background process node index */
+ u32 cdp_process_node_index;
+
+ /* top-level state */
+ int enabled;
+
+ /* Packet templates for different encap types */
+ vlib_packet_template_t packet_templates[CDP_N_PACKET_TEMPLATES];
+
+ /* convenience variables */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+} cdp_main_t;
+
+extern cdp_main_t cdp_main;
+extern vlib_node_registration_t cdp_process_node;
+
+/* Packet counters */
+#define foreach_cdp_error \
+_ (NONE, "good cdp packets (processed)") \
+_ (CACHE_HIT, "good cdp packets (cache hit)") \
+_ (BAD_TLV, "cdp packets with bad TLVs") \
+_ (PROTOCOL_VERSION, "cdp packets with bad protocol versions") \
+_ (CHECKSUM, "cdp packets with bad checksums") \
+_ (DISABLED, "cdp packets received on disabled interfaces")
+
+typedef enum
+{
+#define _(sym,str) CDP_ERROR_##sym,
+ foreach_cdp_error
+#undef _
+ CDP_N_ERROR,
+} cdp_error_t;
+
+/* cdp packet trace capture */
+typedef struct
+{
+ u32 len;
+ u8 data[400];
+} cdp_input_trace_t;
+
+typedef enum
+{
+ CDP_EVENT_ENABLE,
+ CDP_EVENT_DISABLE,
+} cdp_process_event_t;
+
+cdp_error_t cdp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0);
+void cdp_periodic (vlib_main_t * vm);
+void cdp_keepalive (cdp_main_t * cm, cdp_neighbor_t * n);
+u16 cdp_checksum (void *p, int count);
+u8 *cdp_input_format_trace (u8 * s, va_list * args);
+
+#endif /* __included_cdp_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cdp/cdp.pg b/src/plugins/cdp/cdp.pg
new file mode 100644
index 00000000000..b6ba18656c2
--- /dev/null
+++ b/src/plugins/cdp/cdp.pg
@@ -0,0 +1,7 @@
+packet-generator new {
+ name cdp
+ limit 1
+ node cdp-input
+ size 374-374
+ data { hex 0x02b46b96000100096978676265000500bf436973636f20494f5320536f6674776172652c2043333735304520536f66747761726520284333373530452d554e4956455253414c2d4d292c2056657273696f6e2031322e32283335295345352c2052454c4541534520534f4654574152452028666331290a436f707972696768742028632920313938362d3230303720627920436973636f2053797374656d732c20496e632e0a436f6d70696c6564205468752031392d4a756c2d30372031363a3137206279206e616368656e00060018636973636f2057532d4333373530452d3234544400020011000000010101cc0004000000000003001b54656e4769676162697445746865726e6574312f302f3100040008000000280008002400000c011200000000ffffffff010221ff000000000000001e7a50f000ff000000090004000a00060001000b0005010012000500001300050000160011000000010101cc000400000000001a00100000000100000000ffffffff }
+}
diff --git a/src/plugins/cdp/cdp_all_api_h.h b/src/plugins/cdp/cdp_all_api_h.h
new file mode 100644
index 00000000000..cc0047e6399
--- /dev/null
+++ b/src/plugins/cdp/cdp_all_api_h.h
@@ -0,0 +1,19 @@
+
+/*
+ * cdp_all_api_h.h - cdp plug-in api #include file
+ *
+ * Copyright (c) 2011-2018 by Cisco and/or its affiliates
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Include the generated file, see BUILT_SOURCES in Makefile.am */
+#include <cdp/cdp.api.h>
diff --git a/src/plugins/cdp/cdp_input.c b/src/plugins/cdp/cdp_input.c
new file mode 100644
index 00000000000..fa993b84def
--- /dev/null
+++ b/src/plugins/cdp/cdp_input.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2011-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cdp/cdp.h>
+
+cdp_main_t cdp_main;
+
+#define DEBUG_TLV_DUMP 0 /* 1=> dump TLV's to stdout while processing them */
+
+/*
+ * ported from an unspecified Cisco cdp implementation.
+ * Compute / return in HOST byte order. 0 => good checksum.
+ */
+u16
+cdp_checksum (void *p, int count)
+{
+ u32 sum;
+ u16 i, *data;
+
+ data = p;
+ sum = 0;
+ while (count > 1)
+ {
+ sum += ntohs (*data);
+ data++;
+ count -= 2;
+ }
+
+ if (count > 0)
+ sum += *(char *) data;
+
+ while (sum >> 16)
+ {
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ }
+
+ i = (i16) sum;
+ return (~i);
+}
+
+/* TLV handler table */
+typedef struct
+{
+ char *name;
+ u32 tlv_id;
+ void *format;
+ void *process;
+} tlv_handler_t;
+
+static tlv_handler_t tlv_handlers[];
+
+/* Display a generic TLV as a set of hex bytes */
+static u8 *
+format_generic_tlv (u8 * s, va_list * va)
+{
+ cdp_tlv_t *t = va_arg (*va, cdp_tlv_t *);
+ tlv_handler_t *h = &tlv_handlers[t->t];
+
+ s = format (s, "%s(%d): %U\n", h->name,
+ t->t, format_hex_bytes, t->v, t->l - sizeof (*t));
+ return s;
+}
+
+/* Ignore / skip a TLV we don't support */
+static cdp_error_t
+process_generic_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t)
+{
+#if DEBUG_TLV_DUMP > 0
+ fformat (stdout, "%U", format_generic_tlv, t);
+#endif
+
+ return CDP_ERROR_NONE;
+}
+
+/* print a text tlv */
+static u8 *
+format_text_tlv (u8 * s, va_list * va)
+{
+ cdp_tlv_t *t = va_arg (*va, cdp_tlv_t *);
+ tlv_handler_t *h = &tlv_handlers[t->t];
+ int i;
+
+ s = format (s, "%s(%d): ", h->name, t->t);
+
+ for (i = 0; i < (t->l - sizeof (*t)); i++)
+ vec_add1 (s, t->v[i]);
+
+ vec_add1 (s, '\n');
+ return s;
+}
+
+#if DEBUG_TLV_DUMP == 0
+/* gcc warning be gone */
+CLIB_UNUSED (static cdp_error_t
+ process_text_tlv (cdp_main_t * cm, cdp_neighbor_t * n,
+ cdp_tlv_t * t));
+#endif
+
+/* process / skip a generic text TLV that we don't support */
+static cdp_error_t
+process_text_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t)
+{
+#if DEBUG_TLV_DUMP > 0
+ fformat (stdout, "%U\n", format_text_tlv, t);
+#endif
+
+ return CDP_ERROR_NONE;
+}
+
+/* per-TLV format function definitions */
+#define format_unused_tlv format_generic_tlv
+#define format_device_name_tlv format_text_tlv
+#define format_address_tlv format_generic_tlv
+#define format_port_id_tlv format_text_tlv
+#define format_capabilities_tlv format_generic_tlv
+#define format_version_tlv format_text_tlv
+#define format_platform_tlv format_text_tlv
+#define format_ipprefix_tlv format_generic_tlv
+#define format_hello_tlv format_generic_tlv
+#define format_vtp_domain_tlv format_generic_tlv
+#define format_native_vlan_tlv format_generic_tlv
+#define format_duplex_tlv format_generic_tlv
+#define format_appl_vlan_tlv format_generic_tlv
+#define format_trigger_tlv format_generic_tlv
+#define format_power_tlv format_generic_tlv
+#define format_mtu_tlv format_generic_tlv
+#define format_trust_tlv format_generic_tlv
+#define format_cos_tlv format_generic_tlv
+#define format_sysname_tlv format_generic_tlv
+#define format_sysobject_tlv format_generic_tlv
+#define format_mgmt_addr_tlv format_generic_tlv
+#define format_physical_loc_tlv format_generic_tlv
+#define format_mgmt_addr2_tlv format_generic_tlv
+#define format_power_requested_tlv format_generic_tlv
+#define format_power_available_tlv format_generic_tlv
+#define format_port_unidirectional_tlv format_generic_tlv
+#define format_unknown_28_tlv format_generic_tlv
+#define format_energywise_tlv format_generic_tlv
+#define format_unknown_30_tlv format_generic_tlv
+#define format_spare_poe_tlv format_generic_tlv
+
+/* tlv ID=0 is a mistake */
+static cdp_error_t
+process_unused_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t)
+{
+ return CDP_ERROR_BAD_TLV;
+}
+
+/* list of text TLV's that we snapshoot */
+#define foreach_text_to_struct_tlv \
+_(device_name,DEBUG_TLV_DUMP) \
+_(version,DEBUG_TLV_DUMP) \
+_(platform,DEBUG_TLV_DUMP) \
+_(port_id,DEBUG_TLV_DUMP)
+
+#define _(z,dbg) \
+static \
+cdp_error_t process_##z##_tlv (cdp_main_t *cm, cdp_neighbor_t *n, \
+ cdp_tlv_t *t) \
+{ \
+ int i; \
+ if (dbg) \
+ fformat(stdout, "%U\n", format_text_tlv, t); \
+ \
+ if (n->z) \
+ _vec_len(n->z) = 0; \
+ \
+ for (i = 0; i < (t->l - sizeof (*t)); i++) \
+ vec_add1(n->z, t->v[i]); \
+ \
+ vec_add1(n->z, 0); \
+ \
+ return CDP_ERROR_NONE; \
+}
+
+foreach_text_to_struct_tlv
+#undef _
+#define process_address_tlv process_generic_tlv
+#define process_capabilities_tlv process_generic_tlv
+#define process_ipprefix_tlv process_generic_tlv
+#define process_hello_tlv process_generic_tlv
+#define process_vtp_domain_tlv process_generic_tlv
+#define process_native_vlan_tlv process_generic_tlv
+#define process_duplex_tlv process_generic_tlv
+#define process_appl_vlan_tlv process_generic_tlv
+#define process_trigger_tlv process_generic_tlv
+#define process_power_tlv process_generic_tlv
+#define process_mtu_tlv process_generic_tlv
+#define process_trust_tlv process_generic_tlv
+#define process_cos_tlv process_generic_tlv
+#define process_sysname_tlv process_generic_tlv
+#define process_sysobject_tlv process_generic_tlv
+#define process_mgmt_addr_tlv process_generic_tlv
+#define process_physical_loc_tlv process_generic_tlv
+#define process_mgmt_addr2_tlv process_generic_tlv
+#define process_power_requested_tlv process_generic_tlv
+#define process_power_available_tlv process_generic_tlv
+#define process_port_unidirectional_tlv process_generic_tlv
+#define process_unknown_28_tlv process_generic_tlv
+#define process_energywise_tlv process_generic_tlv
+#define process_unknown_30_tlv process_generic_tlv
+#define process_spare_poe_tlv process_generic_tlv
+static tlv_handler_t tlv_handlers[] = {
+#define _(a) {#a, CDP_TLV_##a, format_##a##_tlv, process_##a##_tlv},
+ foreach_cdp_tlv_type
+#undef _
+};
+
+#if DEBUG_TLV_DUMP == 0
+CLIB_UNUSED (static u8 * format_cdp_hdr (u8 * s, va_list * va));
+#endif
+
+static u8 *
+format_cdp_hdr (u8 * s, va_list * va)
+{
+ cdp_hdr_t *h = va_arg (*va, cdp_hdr_t *);
+
+ s = format (s, "version %d, ttl %d(secs), cksum 0x%04x\n",
+ h->version, h->ttl, h->checksum);
+ return s;
+}
+
+static cdp_error_t
+process_cdp_hdr (cdp_main_t * cm, cdp_neighbor_t * n, cdp_hdr_t * h)
+{
+#if DEBUG_TLV_DUMP > 0
+ fformat (stdout, "%U", format_cdp_hdr, h);
+#endif
+
+ if (h->version != 1 && h->version != 2)
+ return CDP_ERROR_PROTOCOL_VERSION;
+
+ n->ttl_in_seconds = h->ttl;
+
+ return CDP_ERROR_NONE;
+}
+
+/* scan a cdp packet; header, then tlv's */
+static int
+cdp_packet_scan (cdp_main_t * cm, cdp_neighbor_t * n)
+{
+ u8 *cur = n->last_rx_pkt;
+ cdp_hdr_t *h;
+ cdp_tlv_t *tlv;
+ cdp_error_t e = CDP_ERROR_NONE;
+ tlv_handler_t *handler;
+ cdp_error_t (*fp) (cdp_main_t *, cdp_neighbor_t *, cdp_tlv_t *);
+ u16 computed_checksum;
+
+ computed_checksum = cdp_checksum (cur, vec_len (cur));
+
+ if (computed_checksum)
+ return CDP_ERROR_CHECKSUM;
+
+ h = (cdp_hdr_t *) cur;
+
+ e = process_cdp_hdr (cm, n, h);
+ if (e)
+ return e;
+
+ cur = (u8 *) (h + 1);
+
+ while (cur < n->last_rx_pkt + vec_len (n->last_rx_pkt) - 1)
+ {
+ tlv = (cdp_tlv_t *) cur;
+ tlv->t = ntohs (tlv->t);
+ tlv->l = ntohs (tlv->l);
+ if (tlv->t >= ARRAY_LEN (tlv_handlers))
+ return CDP_ERROR_BAD_TLV;
+ handler = &tlv_handlers[tlv->t];
+ fp = handler->process;
+ e = (*fp) (cm, n, tlv);
+ if (e)
+ return e;
+ /* tlv length includes (t, l) */
+ cur += tlv->l;
+ }
+
+ return CDP_ERROR_NONE;
+}
+
+/*
+ * cdp input routine
+ */
+cdp_error_t
+cdp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0)
+{
+ cdp_main_t *cm = &cdp_main;
+ cdp_neighbor_t *n;
+ uword *p, nbytes;
+ cdp_error_t e;
+ uword last_packet_signature;
+
+ /* find or create a neighbor pool entry for the (sw) interface
+ upon which we received this pkt */
+ p = hash_get (cm->neighbor_by_sw_if_index,
+ vnet_buffer (b0)->sw_if_index[VLIB_RX]);
+
+ if (p == 0)
+ {
+ pool_get (cm->neighbors, n);
+ memset (n, 0, sizeof (*n));
+ n->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ n->packet_template_index = (u8) ~ 0;
+ hash_set (cm->neighbor_by_sw_if_index, n->sw_if_index,
+ n - cm->neighbors);
+ }
+ else
+ {
+ n = pool_elt_at_index (cm->neighbors, p[0]);
+ }
+
+ /*
+ * typical clib idiom. Don't repeatedly allocate and free
+ * the per-neighbor rx buffer. Reset its apparent length to zero
+ * and reuse it.
+ */
+
+ if (n->last_rx_pkt)
+ _vec_len (n->last_rx_pkt) = 0;
+
+ /* cdp disabled on this interface, we're done */
+ if (n->disabled)
+ return CDP_ERROR_DISABLED;
+
+ /*
+ * Make sure the per-neighbor rx buffer is big enough to hold
+ * the data we're about to copy
+ */
+ vec_validate (n->last_rx_pkt, vlib_buffer_length_in_chain (vm, b0) - 1);
+
+ /*
+ * Coalesce / copy e the buffer chain into the per-neighbor
+ * rx buffer
+ */
+ nbytes = vlib_buffer_contents (vm, bi0, n->last_rx_pkt);
+ ASSERT (nbytes <= vec_len (n->last_rx_pkt));
+
+ /*
+ * Compute Jenkins hash of the new packet, decide if we need to
+ * actually parse through the TLV's. CDP packets are all identical,
+ * so unless we time out the peer, we don't need to process the packet.
+ */
+ last_packet_signature =
+ hash_memory (n->last_rx_pkt, vec_len (n->last_rx_pkt), 0xd00b);
+
+ if (n->last_packet_signature_valid &&
+ n->last_packet_signature == last_packet_signature)
+ {
+ e = CDP_ERROR_CACHE_HIT;
+ }
+ else
+ {
+ /* Actually scan the packet */
+ e = cdp_packet_scan (cm, n);
+ n->last_packet_signature_valid = 1;
+ n->last_packet_signature = last_packet_signature;
+ }
+
+ if (e == CDP_ERROR_NONE)
+ {
+ n->last_heard = vlib_time_now (vm);
+ }
+
+ return e;
+}
+
+/*
+ * setup neighbor hash table
+ */
+static clib_error_t *
+cdp_input_init (vlib_main_t * vm)
+{
+ clib_error_t *error;
+ cdp_main_t *cm = &cdp_main;
+ void vnet_cdp_node_reference (void);
+
+ vnet_cdp_node_reference ();
+
+ if ((error = vlib_call_init_function (vm, cdp_periodic_init)))
+ return error;
+
+ cm->vlib_main = vm;
+ cm->vnet_main = vnet_get_main ();
+ cm->neighbor_by_sw_if_index = hash_create (0, sizeof (uword));
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (cdp_input_init);
+
+
+static u8 *
+format_cdp_neighbors (u8 * s, va_list * va)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
+ cdp_main_t *cm = va_arg (*va, cdp_main_t *);
+ vnet_main_t *vnm = &vnet_main;
+ cdp_neighbor_t *n;
+ vnet_hw_interface_t *hw;
+
+ s = format (s,
+ "%=25s %=15s %=25s %=10s\n",
+ "Our Port", "Peer System", "Peer Port", "Last Heard");
+
+ /* *INDENT-OFF* */
+ pool_foreach (n, cm->neighbors,
+ ({
+ hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
+
+ if (n->disabled == 0)
+ s = format (s, "%=25s %=15s %=25s %=10.1f\n",
+ hw->name, n->device_name, n->port_id,
+ n->last_heard);
+ }));
+ /* *INDENT-ON* */
+ return s;
+}
+
+static clib_error_t *
+show_cdp (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ cdp_main_t *cm = &cdp_main;
+
+ if (cm->enabled == 0)
+ vlib_cli_output (vm, "CDP is not enabled...");
+ else
+ vlib_cli_output (vm, "%U\n", format_cdp_neighbors, vm, cm);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_cdp_command, static) = {
+ .path = "show cdp",
+ .short_help = "Show cdp command",
+ .function = show_cdp,
+};
+/* *INDENT-ON* */
+
+
+/*
+ * packet trace format function, very similar to
+ * cdp_packet_scan except that we call the per TLV format
+ * functions instead of the per TLV processing functions
+ */
+u8 *
+cdp_input_format_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ cdp_input_trace_t *t = va_arg (*args, cdp_input_trace_t *);
+ u8 *cur;
+ cdp_hdr_t *h;
+ cdp_tlv_t *tlv;
+ tlv_handler_t *handler;
+ u8 *(*fp) (cdp_tlv_t *);
+
+ cur = t->data;
+
+ h = (cdp_hdr_t *) cur;
+ s = format (s, "%U", format_cdp_hdr, h);
+
+ cur = (u8 *) (h + 1);
+
+ while (cur < t->data + t->len)
+ {
+ tlv = (cdp_tlv_t *) cur;
+ tlv->t = ntohs (tlv->t);
+ tlv->l = ntohs (tlv->l);
+ if (tlv->t >= ARRAY_LEN (tlv_handlers))
+ {
+ s = format (s, "BAD_TLV\n");
+ break;
+ }
+ handler = &tlv_handlers[tlv->t];
+ fp = handler->format;
+ s = format (s, " %U", fp, tlv);
+ /* tlv length includes (t, l) */
+ cur += tlv->l;
+ }
+
+ return s;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cdp/cdp_msg_enum.h b/src/plugins/cdp/cdp_msg_enum.h
new file mode 100644
index 00000000000..0a7bf063b67
--- /dev/null
+++ b/src/plugins/cdp/cdp_msg_enum.h
@@ -0,0 +1,31 @@
+
+/*
+ * cdp_msg_enum.h - cdp plug-in message enumeration
+ *
+ * Copyright (c) 2011-2018 by Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef included_cdp_msg_enum_h
+#define included_cdp_msg_enum_h
+
+#include <vppinfra/byte_order.h>
+
+#define vl_msg_id(n,h) n,
+typedef enum {
+#include <cdp/cdp_all_api_h.h>
+ /* We'll want to know how many messages IDs we need... */
+ VL_MSG_FIRST_AVAILABLE,
+} vl_msg_id_t;
+#undef vl_msg_id
+
+#endif /* included_cdp_msg_enum_h */
diff --git a/src/plugins/cdp/cdp_node.c b/src/plugins/cdp/cdp_node.c
new file mode 100644
index 00000000000..1336c567287
--- /dev/null
+++ b/src/plugins/cdp/cdp_node.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2011-2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cdp/cdp.h>
+#include <vnet/ethernet/packet.h>
+
+/** \file
+
+ 2 x CDP graph nodes: an "interior" node to process
+ incoming announcements, and a "process" node to periodically
+ send announcements.
+
+ The interior node is neither pipelined nor dual-looped, because
+ it would be very unusual to see more than one CDP packet in
+ a given input frame. So, it's a very simple / straighforward
+ example.
+*/
+
+/*
+ * packet counter strings
+ * Dump these counters via the "show error" CLI command
+ */
+static char *cdp_error_strings[] = {
+#define _(sym,string) string,
+ foreach_cdp_error
+#undef _
+};
+
+/*
+ * We actually send all cdp pkts to the "error" node after scanning
+ * them, so the graph node has only one next-index. The "error-drop"
+ * node automatically bumps our per-node packet counters for us.
+ */
+typedef enum
+{
+ CDP_INPUT_NEXT_NORMAL,
+ CDP_INPUT_N_NEXT,
+} cdp_next_t;
+
+/*
+ * Process a frame of cdp packets
+ * Expect 1 packet / frame
+ */
+static uword
+cdp_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, *from;
+ cdp_input_trace_t *t0;
+
+ from = vlib_frame_vector_args (frame); /* array of buffer indices */
+ n_left_from = frame->n_vectors; /* number of buffer indices */
+
+ while (n_left_from > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u32 next0, error0;
+
+ bi0 = from[0];
+ b0 = vlib_get_buffer (vm, bi0);
+
+ next0 = CDP_INPUT_NEXT_NORMAL;
+
+ /* scan this cdp pkt. error0 is the counter index to bump */
+ error0 = cdp_input (vm, b0, bi0);
+ b0->error = node->errors[error0];
+
+ /* If this pkt is traced, snapshoot the data */
+ if (b0->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ int len;
+ t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ len = (b0->current_length < sizeof (t0->data))
+ ? b0->current_length : sizeof (t0->data);
+ t0->len = len;
+ clib_memcpy (t0->data, vlib_buffer_get_current (b0), len);
+ }
+ /* push this pkt to the next graph node, always error-drop */
+ vlib_set_next_frame_buffer (vm, node, next0, bi0);
+
+ from += 1;
+ n_left_from -= 1;
+ }
+
+ return frame->n_vectors;
+}
+
+/*
+ * cdp input graph node declaration
+ */
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (cdp_input_node, static) = {
+ .function = cdp_node_fn,
+ .name = "cdp-input",
+ .vector_size = sizeof (u32),
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = CDP_N_ERROR,
+ .error_strings = cdp_error_strings,
+
+ .format_trace = cdp_input_format_trace,
+
+ .n_next_nodes = CDP_INPUT_N_NEXT,
+ .next_nodes = {
+ [CDP_INPUT_NEXT_NORMAL] = "error-drop",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * cdp periodic function
+ */
+static uword
+cdp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ cdp_main_t *cm = &cdp_main;
+ f64 poll_time_remaining;
+ uword event_type, *event_data = 0;
+
+ /* So we can send events to the cdp process */
+ cm->cdp_process_node_index = cdp_process_node.index;
+
+ /* Dynamically register the cdp input node with the snap classifier */
+ snap_register_input_protocol (vm, "cdp-input", 0xC /* ieee_oui, Cisco */ ,
+ 0x2000 /* protocol CDP */ ,
+ cdp_input_node.index);
+
+ snap_register_input_protocol (vm, "cdp-input", 0xC /* ieee_oui, Cisco */ ,
+ 0x2004 /* protocol CDP */ ,
+ cdp_input_node.index);
+
+#if 0 /* retain for reference */
+ /* with the hdlc classifier */
+ hdlc_register_input_protocol (vm, HDLC_PROTOCOL_cdp, cdp_input_node.index);
+#endif
+
+ /* with ethernet input (for SRP) */
+ ethernet_register_input_type (vm, ETHERNET_TYPE_CDP /* CDP */ ,
+ cdp_input_node.index);
+
+ /* Start w/ cdp effectively disabled */
+ poll_time_remaining = 86400.0;
+
+ while (1)
+ {
+ /* sleep until next poll time, or msg serialize event occurs */
+ poll_time_remaining =
+ vlib_process_wait_for_event_or_clock (vm, poll_time_remaining);
+
+ event_type = vlib_process_get_events (vm, &event_data);
+ switch (event_type)
+ {
+ case ~0: /* no events => timeout */
+ break;
+
+ case CDP_EVENT_ENABLE:
+ poll_time_remaining = 10.0;
+ break;
+
+ case CDP_EVENT_DISABLE:
+ poll_time_remaining = 86400.0;
+ break;
+
+ default:
+ clib_warning ("BUG: event type 0x%wx", event_type);
+ break;
+ }
+ vec_reset_length (event_data);
+
+ /* peer timeout scan, send announcements */
+ if (vlib_process_suspend_time_is_zero (poll_time_remaining))
+ {
+ cdp_periodic (vm);
+ poll_time_remaining = 10.0;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * cdp periodic node declaration
+ */
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (cdp_process_node) = {
+ .function = cdp_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "cdp-process",
+};
+/* *INDENT-ON* */
+
+void
+vnet_cdp_node_reference (void)
+{
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cdp/cdp_periodic.c b/src/plugins/cdp/cdp_periodic.c
new file mode 100644
index 00000000000..c67af00c7a5
--- /dev/null
+++ b/src/plugins/cdp/cdp_periodic.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (c) 2011-2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cdp/cdp.h>
+#include <vppinfra/hash.h>
+#include <vnet/unix/pcap.h>
+#include <vnet/srp/srp.h>
+#include <vnet/ppp/ppp.h>
+#include <vnet/hdlc/hdlc.h>
+#include <vnet/srp/packet.h>
+
+/*
+ * Generate a set of specific CDP TLVs.
+ *
+ * $$$ eventually these need to fish better data from
+ * other data structures; e.g. the hostname, software version info
+ * etc.
+ */
+
+static void
+add_device_name_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
+{
+ cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
+
+ t->t = htons (CDP_TLV_device_name);
+ t->l = htons (3 + sizeof (*t));
+ clib_memcpy (&t->v, "VPP", 3);
+
+ *t0p += ntohs (t->l);
+}
+
+static void
+add_port_id_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
+{
+ cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
+
+ t->t = htons (CDP_TLV_port_id);
+ t->l = htons (vec_len (hw->name) + sizeof (*t));
+ clib_memcpy (&t->v, hw->name, vec_len (hw->name));
+ *t0p += ntohs (t->l);
+}
+
+static void
+add_version_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
+{
+ cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
+
+ t->t = htons (CDP_TLV_version);
+ t->l = htons (12 + sizeof (*t));
+ clib_memcpy (&t->v, "VPP Software", 12);
+ *t0p += ntohs (t->l);
+}
+
+static void
+add_platform_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
+{
+ cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
+
+ t->t = htons (CDP_TLV_platform);
+ t->l = htons (2 + sizeof (*t));
+ clib_memcpy (&t->v, "SW", 2);
+ *t0p += ntohs (t->l);
+}
+
+static void
+add_capability_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
+{
+ cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
+ u32 capabilities;
+
+ t->t = htons (CDP_TLV_capabilities);
+ t->l = htons (4 + sizeof (*t));
+ capabilities = CDP_ROUTER_DEVICE;
+ capabilities = htonl (capabilities);
+ clib_memcpy (&t->v, &capabilities, sizeof (capabilities));
+ *t0p += ntohs (t->l);
+}
+
+static void
+add_tlvs (cdp_main_t * cm, vnet_hw_interface_t * hw, u8 ** t0p)
+{
+ add_device_name_tlv (hw, t0p);
+ add_port_id_tlv (hw, t0p);
+ add_version_tlv (hw, t0p);
+ add_platform_tlv (hw, t0p);
+ add_capability_tlv (hw, t0p);
+}
+
+/*
+ * send a cdp pkt on an ethernet interface
+ */
+static void
+send_ethernet_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
+{
+ u32 *to_next;
+ ethernet_llc_snap_and_cdp_header_t *h0;
+ vnet_hw_interface_t *hw;
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u8 *t0;
+ u16 checksum;
+ int nbytes_to_checksum;
+ int i;
+ vlib_frame_t *f;
+ vlib_main_t *vm = cm->vlib_main;
+ vnet_main_t *vnm = cm->vnet_main;
+
+ for (i = 0; i < count; i++)
+ {
+ /*
+ * see cdp_periodic_init() to understand what's already painted
+ * into the buffer by the packet template mechanism
+ */
+ h0 = vlib_packet_template_get_packet
+ (vm, &cm->packet_templates[n->packet_template_index], &bi0);
+
+ if (!h0)
+ break;
+
+ /* Add the interface's ethernet source address */
+ hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
+
+ clib_memcpy (h0->ethernet.src_address, hw->hw_address,
+ vec_len (hw->hw_address));
+
+ t0 = (u8 *) & h0->cdp.data;
+
+ /* add TLVs */
+ add_tlvs (cm, hw, &t0);
+
+ /* add the cdp packet checksum */
+ nbytes_to_checksum = t0 - (u8 *) & h0->cdp;
+ checksum = cdp_checksum (&h0->cdp, nbytes_to_checksum);
+ h0->cdp.checksum = htons (checksum);
+
+ /* Set the outbound packet length */
+ b0 = vlib_get_buffer (vm, bi0);
+ b0->current_length = nbytes_to_checksum + sizeof (*h0)
+ - sizeof (cdp_hdr_t);
+
+ /* And the outbound interface */
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
+
+ /* Set the 802.3 ethernet length */
+ h0->ethernet.len = htons (b0->current_length
+ - sizeof (ethernet_802_3_header_t));
+
+ /* And output the packet on the correct interface */
+ f = vlib_get_frame_to_node (vm, hw->output_node_index);
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+
+ vlib_put_frame_to_node (vm, hw->output_node_index, f);
+ n->last_sent = vlib_time_now (vm);
+ }
+}
+
+/*
+ * send a cdp pkt on an hdlc interface
+ */
+static void
+send_hdlc_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
+{
+ u32 *to_next;
+ hdlc_and_cdp_header_t *h0;
+ vnet_hw_interface_t *hw;
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u8 *t0;
+ u16 checksum;
+ int nbytes_to_checksum;
+ int i;
+ vlib_frame_t *f;
+ vlib_main_t *vm = cm->vlib_main;
+ vnet_main_t *vnm = cm->vnet_main;
+
+ for (i = 0; i < count; i++)
+ {
+ /*
+ * see cdp_periodic_init() to understand what's already painted
+ * into the buffer by the packet template mechanism
+ */
+ h0 = vlib_packet_template_get_packet
+ (vm, &cm->packet_templates[n->packet_template_index], &bi0);
+
+ hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
+
+ t0 = (u8 *) & h0->cdp.data;
+
+ /* add TLVs */
+ add_tlvs (cm, hw, &t0);
+
+ /* add the cdp packet checksum */
+ nbytes_to_checksum = t0 - (u8 *) & h0->cdp;
+ checksum = cdp_checksum (&h0->cdp, nbytes_to_checksum);
+ h0->cdp.checksum = htons (checksum);
+
+ /* Set the outbound packet length */
+ b0 = vlib_get_buffer (vm, bi0);
+ b0->current_length = nbytes_to_checksum + sizeof (*h0)
+ - sizeof (cdp_hdr_t);
+
+ /* And output the packet on the correct interface */
+ f = vlib_get_frame_to_node (vm, hw->output_node_index);
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+
+ vlib_put_frame_to_node (vm, hw->output_node_index, f);
+ n->last_sent = vlib_time_now (vm);
+ }
+}
+
+/*
+ * send a cdp pkt on an srp interface
+ */
+static void
+send_srp_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
+{
+ u32 *to_next;
+ srp_and_cdp_header_t *h0;
+ vnet_hw_interface_t *hw;
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u8 *t0;
+ u16 checksum;
+ int nbytes_to_checksum;
+ int i;
+ vlib_frame_t *f;
+ vlib_main_t *vm = cm->vlib_main;
+ vnet_main_t *vnm = cm->vnet_main;
+
+ for (i = 0; i < count; i++)
+ {
+ /*
+ * see cdp_periodic_init() to understand what's already painted
+ * into the buffer by the packet template mechanism
+ */
+ h0 = vlib_packet_template_get_packet
+ (vm, &cm->packet_templates[n->packet_template_index], &bi0);
+
+ hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
+
+ t0 = (u8 *) & h0->cdp.data;
+
+ /* add TLVs */
+ add_tlvs (cm, hw, &t0);
+
+ /* Add the interface's ethernet source address */
+ clib_memcpy (h0->ethernet.src_address, hw->hw_address,
+ vec_len (hw->hw_address));
+
+ /* add the cdp packet checksum */
+ nbytes_to_checksum = t0 - (u8 *) & h0->cdp;
+ checksum = cdp_checksum (&h0->cdp, nbytes_to_checksum);
+ h0->cdp.checksum = htons (checksum);
+
+ /* Set the outbound packet length */
+ b0 = vlib_get_buffer (vm, bi0);
+ b0->current_length = nbytes_to_checksum + sizeof (*h0)
+ - sizeof (cdp_hdr_t);
+
+ /* And output the packet on the correct interface */
+ f = vlib_get_frame_to_node (vm, hw->output_node_index);
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+
+ vlib_put_frame_to_node (vm, hw->output_node_index, f);
+ n->last_sent = vlib_time_now (vm);
+ }
+}
+
+/*
+ * Decide which cdp packet template to use
+ */
+static int
+pick_packet_template (cdp_main_t * cm, cdp_neighbor_t * n)
+{
+ n->packet_template_index = CDP_PACKET_TEMPLATE_ETHERNET;
+
+ return 0;
+}
+
+/* Send a cdp neighbor announcement */
+static void
+send_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
+{
+ if (n->packet_template_index == (u8) ~ 0)
+ {
+ /* If we don't know how to talk to this peer, don't try again */
+ if (pick_packet_template (cm, n))
+ {
+ n->last_sent = 1e70;
+ return;
+ }
+ }
+
+ switch (n->packet_template_index)
+ {
+ case CDP_PACKET_TEMPLATE_ETHERNET:
+ send_ethernet_hello (cm, n, count);
+ break;
+
+ case CDP_PACKET_TEMPLATE_HDLC:
+ send_hdlc_hello (cm, n, count);
+ break;
+
+ case CDP_PACKET_TEMPLATE_SRP:
+ send_srp_hello (cm, n, count);
+ break;
+
+ default:
+ ASSERT (0);
+ }
+ n->last_sent = vlib_time_now (cm->vlib_main);
+}
+
+static void
+delete_neighbor (cdp_main_t * cm, cdp_neighbor_t * n, int want_broadcast)
+{
+ hash_unset (cm->neighbor_by_sw_if_index, n->sw_if_index);
+ vec_free (n->device_name);
+ vec_free (n->version);
+ vec_free (n->port_id);
+ vec_free (n->platform);
+ vec_free (n->last_rx_pkt);
+ pool_put (cm->neighbors, n);
+}
+
+void
+cdp_periodic (vlib_main_t * vm)
+{
+ cdp_main_t *cm = &cdp_main;
+ cdp_neighbor_t *n;
+ f64 now = vlib_time_now (vm);
+ vnet_sw_interface_t *sw;
+ static u32 *delete_list = 0;
+ int i;
+ static cdp_neighbor_t **n_list = 0;
+
+ /* *INDENT-OFF* */
+ pool_foreach (n, cm->neighbors,
+ ({
+ vec_add1 (n_list, n);
+ }));
+ /* *INDENT-ON* */
+
+ /* Across all cdp neighbors known to the system */
+ for (i = 0; i < vec_len (n_list); i++)
+ {
+ n = n_list[i];
+
+ /* "no cdp run" provisioned on the interface? */
+ if (n->disabled == 1)
+ continue;
+
+ sw = vnet_get_sw_interface (cm->vnet_main, n->sw_if_index);
+
+ /* Interface shutdown or rx timeout? */
+ if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+ || (now > (n->last_heard + (f64) n->ttl_in_seconds)))
+ /* add to list of neighbors to delete */
+ vec_add1 (delete_list, n - cm->neighbors);
+ else if (n->last_sent == 0.0)
+ /* First time, send 3 hellos */
+ send_hello (cm, n, 3 /* three to begin with */ );
+ else if (now > (n->last_sent + (((f64) n->ttl_in_seconds) / 6.0)))
+ /* Normal keepalive, send one */
+ send_hello (cm, n, 1 /* one as a keepalive */ );
+ }
+
+ for (i = 0; i < vec_len (delete_list); i++)
+ {
+ n = vec_elt_at_index (cm->neighbors, delete_list[i]);
+ delete_neighbor (cm, n, 1);
+ }
+ if (delete_list)
+ _vec_len (delete_list) = 0;
+ if (n_list)
+ _vec_len (n_list) = 0;
+}
+
+static clib_error_t *
+cdp_periodic_init (vlib_main_t * vm)
+{
+ cdp_main_t *cm = &cdp_main;
+
+ /* Create the ethernet cdp hello packet template */
+ {
+ ethernet_llc_snap_and_cdp_header_t h;
+
+ memset (&h, 0, sizeof (h));
+
+ /* Send to 01:00:0c:cc:cc */
+ h.ethernet.dst_address[0] = 0x01;
+ /* h.ethernet.dst_address[1] = 0x00; (memset) */
+ h.ethernet.dst_address[2] = 0x0C;
+ h.ethernet.dst_address[3] = 0xCC;
+ h.ethernet.dst_address[4] = 0xCC;
+ h.ethernet.dst_address[5] = 0xCC;
+
+ /* leave src address blank (fill in at send time) */
+
+ /* leave length blank (fill in at send time) */
+
+ /* LLC */
+ h.llc.dst_sap = h.llc.src_sap = 0xAA; /* SNAP */
+ h.llc.control = 0x03; /* UI (no extended control bytes) */
+
+ /* SNAP */
+ /* h.snap.oui[0] = 0x00; (memset) */
+ /* h.snap.oui[1] = 0x00; (memset) */
+ h.snap.oui[2] = 0x0C; /* Cisco = 0x00000C */
+ h.snap.protocol = htons (0x2000); /* CDP = 0x2000 */
+
+ /* CDP */
+ h.cdp.version = 2;
+ h.cdp.ttl = 180;
+
+ vlib_packet_template_init
+ (vm, &cm->packet_templates[CDP_PACKET_TEMPLATE_ETHERNET],
+ /* data */ &h,
+ sizeof (h),
+ /* alloc chunk size */ 8,
+ "cdp-ethernet");
+ }
+
+#if 0 /* retain for reference */
+
+ /* Create the hdlc cdp hello packet template */
+ {
+ hdlc_and_cdp_header_t h;
+
+ memset (&h, 0, sizeof (h));
+
+ h.hdlc.address = 0x0f;
+ /* h.hdlc.control = 0; (memset) */
+ h.hdlc.protocol = htons (0x2000); /* CDP = 0x2000 */
+
+ /* CDP */
+ h.cdp.version = 2;
+ h.cdp.ttl = 180;
+
+ vlib_packet_template_init
+ (vm, &cm->packet_templates[CDP_PACKET_TEMPLATE_HDLC],
+ /* data */ &h,
+ sizeof (h),
+ /* alloc chunk size */ 8,
+ "cdp-hdlc");
+ }
+
+ /* Create the srp cdp hello packet template */
+ {
+ srp_and_cdp_header_t h;
+
+ memset (&h, 0, sizeof (h));
+
+ /* Send to 01:00:0c:cc:cc */
+ h.ethernet.dst_address[0] = 0x01;
+ /* h.ethernet.dst_address[1] = 0x00; (memset) */
+ h.ethernet.dst_address[2] = 0x0C;
+ h.ethernet.dst_address[3] = 0xCC;
+ h.ethernet.dst_address[4] = 0xCC;
+ h.ethernet.dst_address[5] = 0xCC;
+
+ /* leave src address blank (fill in at send time) */
+
+ /* The srp header is filled in at xmt */
+ h.srp.ttl = 1;
+ h.srp.priority = 7;
+ h.srp.mode = SRP_MODE_data;
+ srp_header_compute_parity (&h.srp);
+
+ /* Inner ring and parity will be set at send time */
+
+ h.ethernet.type = htons (0x2000); /* CDP = 0x2000 */
+
+ /* CDP */
+ h.cdp.version = 2;
+ h.cdp.ttl = 180;
+
+ vlib_packet_template_init
+ (vm, &cm->packet_templates[CDP_PACKET_TEMPLATE_SRP],
+ /* data */ &h,
+ sizeof (h),
+ /* alloc chunk size */ 8,
+ "cdp-srp");
+ }
+#endif
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (cdp_periodic_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cdp/cdp_protocol.h b/src/plugins/cdp/cdp_protocol.h
new file mode 100644
index 00000000000..46a5603aa70
--- /dev/null
+++ b/src/plugins/cdp/cdp_protocol.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2011-2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_cdp_protocol_h__
+#define __included_cdp_protocol_h__
+
+#include <vnet/ethernet/ethernet.h> /* for ethernet_header_t */
+#include <vnet/llc/llc.h>
+#include <vnet/snap/snap.h>
+#include <vnet/srp/packet.h>
+
+typedef CLIB_PACKED (struct
+ {
+ u8 version;
+ u8 ttl;
+ u16 checksum; /* 1's complement of the 1's complement sum */
+ u8 data[0];
+ }) cdp_hdr_t;
+
+typedef struct
+{
+ u8 dst_address[6];
+ u8 src_address[6];
+ u16 len;
+} ethernet_802_3_header_t;
+
+typedef CLIB_PACKED (struct
+ {
+ ethernet_802_3_header_t ethernet;
+ llc_header_t llc; snap_header_t snap; cdp_hdr_t cdp;
+ }) ethernet_llc_snap_and_cdp_header_t;
+
+typedef CLIB_PACKED (struct
+ {
+ hdlc_header_t hdlc; cdp_hdr_t cdp;
+ }) hdlc_and_cdp_header_t;
+
+typedef CLIB_PACKED (struct
+ {
+ srp_header_t srp;
+ ethernet_header_t ethernet; cdp_hdr_t cdp;
+ }) srp_and_cdp_header_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u16 t;
+ u16 l;
+ u8 v[0];
+ }) cdp_tlv_t;
+
+/*
+ * TLV codes.
+ */
+#define foreach_cdp_tlv_type \
+_(unused) \
+_(device_name) /* uniquely identifies the device */ \
+_(address) /* list of addresses this device has */ \
+_(port_id) /* port CDP packet was sent out on */ \
+_(capabilities) /* funct. capabilities of the device */ \
+_(version) /* version */ \
+_(platform) /* hardware platform of this device */ \
+_(ipprefix) /* An IP network prefix */ \
+_(hello) /* Pprotocol piggyback hello msg */ \
+_(vtp_domain) /* VTP management domain */ \
+_(native_vlan) /* Native VLAN number */ \
+_(duplex) /* The interface duplex mode */ \
+_(appl_vlan) /* Appliance VLAN-ID TLV */ \
+_(trigger) /* For sending trigger TLV msgs. */ \
+_(power) /* Power consumption of that device */ \
+_(mtu) /* MTU defined for sending intf. */ \
+_(trust) /* Extended trust TLV */ \
+_(cos) /* COS for Untrusted Port TLV */ \
+_(sysname) /* System name (FQDN of device) */ \
+_(sysobject) /* OID of sysObjectID MIB object */ \
+_(mgmt_addr) /* SNMP manageable addrs. of device */ \
+_(physical_loc) /* Physical Location of the device */ \
+_(mgmt_addr2) /* External Port-ID */ \
+_(power_requested) \
+_(power_available) \
+_(port_unidirectional) \
+_(unknown_28) \
+_(energywise) \
+_(unknown_30) \
+_(spare_poe)
+
+typedef enum
+{
+#define _(t) CDP_TLV_##t,
+ foreach_cdp_tlv_type
+#undef _
+} cdp_tlv_code_t;
+
+/*
+ The address TLV looks as follows:
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Number of addresses |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | IDRP encoded address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ An address is encoded in IDRP format:
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | PT | PT Length | Protocol (variable) ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Address length | Address (variable) ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ PT: Protocol type
+ 1 = NLPID format
+ 2 = 802.2 format
+
+ PT Length:
+ Length of protocol field, 1 for PT = 1, and either 3 or 8 for
+ 802.2 format depending if SNAP is used for PT = 2.
+
+ The encodings for the other protocols have the following format:
+
+ field: <SSAP><DSAP><CTRL><-------OUI------><protocl_TYPE>
+ | | | | | | | | |
+ bytes: 0 1 2 3 4 5 6 7 8
+
+ where the first 3 bytes are 0xAAAA03 for SNAP encoded addresses.
+ The OUI is 000000 for ethernet and <protocl_TYPE>
+ is the assigned Ethernet type code for the particular protocol.
+ e.g. for DECnet the encoding is AAAA03 000000 6003.
+ for IPv6 the encoding is AAAA03 000000 86DD
+*/
+
+/*
+ * Capabilities.
+ */
+
+#define CDP_ROUTER_DEVICE 0x0001
+#define CDP_TB_DEVICE 0x0002
+#define CDP_SRB_DEVICE 0x0004
+#define CDP_SWITCH_DEVICE 0x0008
+#define CDP_HOST_DEVICE 0x0010
+#define CDP_IGMP_DEVICE 0x0020
+#define CDP_REPEATER_DEVICE 0x0040
+
+/*
+ The protocol-hello TLV looks as follows:
+
+ 0 1 2 3
+ 012345678901234567890123456789012345678
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OUI |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Protocol ID |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | up to 27 bytes of message |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+/*
+ * These macros define the valid values for the Duplex TLV.
+ */
+#define CDP_DUPLEX_TLV_HALF 0x0
+#define CDP_DUPLEX_TLV_FULL 0x1
+
+#endif /* __included_cdp_protocol_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cdp/cdp_test.c b/src/plugins/cdp/cdp_test.c
new file mode 100644
index 00000000000..33b54498f7f
--- /dev/null
+++ b/src/plugins/cdp/cdp_test.c
@@ -0,0 +1,179 @@
+/*
+ * cdp.c - vpp-api-test cdp protocol plug-in
+ *
+ * Copyright (c) 2011-2018 by Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+
+uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
+
+/* Declare message IDs */
+#include <cdp/cdp_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <cdp/cdp_all_api_h.h>
+#undef vl_typedefs
+
+/* declare message handlers for each api */
+
+#define vl_endianfun /* define message structures */
+#include <cdp/cdp_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <cdp/cdp_all_api_h.h>
+#undef vl_printfun
+
+/* Get the API version number. */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <cdp/cdp_all_api_h.h>
+#undef vl_api_version
+
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+ vat_main_t *vat_main;
+} cdp_test_main_t;
+
+cdp_test_main_t cdp_test_main;
+
+#define __plugin_msg_base cdp_test_main.msg_id_base
+#include <vlibapi/vat_helper_macros.h>
+
+#define foreach_standard_reply_retval_handler \
+_(cdp_enable_disable_reply)
+
+#define _(n) \
+ static void vl_api_##n##_t_handler \
+ (vl_api_##n##_t * mp) \
+ { \
+ vat_main_t * vam = cdp_test_main.vat_main; \
+ i32 retval = ntohl(mp->retval); \
+ if (vam->async_mode) { \
+ vam->async_errors += (retval < 0); \
+ } else { \
+ vam->retval = retval; \
+ vam->result_ready = 1; \
+ } \
+ }
+foreach_standard_reply_retval_handler;
+#undef _
+
+/*
+ * Table of message reply handlers, must include boilerplate handlers
+ * we just generated
+ */
+#define foreach_vpe_api_reply_msg \
+_(CDP_ENABLE_DISABLE_REPLY, cdp_enable_disable_reply)
+
+static int
+api_cdp_enable_disable (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ int enable_disable = 1;
+ vl_api_cdp_enable_disable_t *mp;
+ int ret;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "disable"))
+ enable_disable = 0;
+ else if (unformat (i, "enable"))
+ enable_disable = 1;
+ else
+ break;
+ }
+
+ /* Construct the API message */
+ M (CDP_ENABLE_DISABLE, mp);
+ mp->enable_disable = enable_disable;
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+ return ret;
+}
+
+/*
+ * List of messages that the api test plugin sends,
+ * and that the data plane plugin processes
+ */
+#define foreach_vpe_api_msg \
+_(cdp_enable_disable, "enable | disable")
+
+static void
+cdp_api_hookup (vat_main_t * vam)
+{
+ cdp_test_main_t *sm = &cdp_test_main;
+ /* Hook up handlers for replies from the data plane plug-in */
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \
+ #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_vpe_api_reply_msg;
+#undef _
+
+ /* API messages we can send */
+#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
+ foreach_vpe_api_msg;
+#undef _
+
+ /* Help strings */
+#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
+ foreach_vpe_api_msg;
+#undef _
+}
+
+clib_error_t *
+vat_plugin_register (vat_main_t * vam)
+{
+ cdp_test_main_t *sm = &cdp_test_main;
+ u8 *name;
+
+ sm->vat_main = vam;
+
+ /* Ask the vpp engine for the first assigned message-id */
+ name = format (0, "cdp_%08x%c", api_version, 0);
+ sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
+
+ if (sm->msg_id_base != (u16) ~ 0)
+ cdp_api_hookup (vam);
+
+ vec_free (name);
+
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */