Cisco Discovery Protocol, initial working attempt 52/252/1
authorDave Barach <dave@barachs.net>
Mon, 8 Feb 2016 20:57:35 +0000 (15:57 -0500)
committerDave Barach <dave@barachs.net>
Mon, 8 Feb 2016 20:57:55 +0000 (15:57 -0500)
There are multiple enhancement opportunities...

Change-Id: I976772dc3802f8284e8c6457c001d68184831e25
Signed-off-by: Dave Barach <dave@barachs.net>
vnet/Makefile.am
vnet/vnet/cdp/cdp.pg [new file with mode: 0644]
vnet/vnet/cdp/cdp_input.c [new file with mode: 0644]
vnet/vnet/cdp/cdp_node.c [new file with mode: 0644]
vnet/vnet/cdp/cdp_node.h [new file with mode: 0644]
vnet/vnet/cdp/cdp_periodic.c [new file with mode: 0644]
vnet/vnet/cdp/cdp_protocol.h [new file with mode: 0644]
vnet/vnet/llc/node.c
vpp/vnet/main.c

index bb138e6..44ce107 100644 (file)
@@ -191,9 +191,20 @@ libvnet_la_SOURCES +=                              \
 
 nobase_include_HEADERS +=                      \
   vnet/vxlan/vxlan.h                           \
-  vnet/vxlan/vxlan_packet.h         \
+  vnet/vxlan/vxlan_packet.h                    \
   vnet/vxlan/vxlan_error.def
 
+########################################
+# Layer 2 / CDP
+########################################
+libvnet_la_SOURCES +=                          \
+  vnet/cdp/cdp_input.c                         \
+  vnet/cdp/cdp_node.c                          \
+  vnet/cdp/cdp_periodic.c      
+
+nobase_include_HEADERS +=                      \
+  vnet/cdp/cdp_protocol.h
+
 ########################################
 # Layer 2/3 "classify"
 ########################################
diff --git a/vnet/vnet/cdp/cdp.pg b/vnet/vnet/cdp/cdp.pg
new file mode 100644 (file)
index 0000000..b6ba186
--- /dev/null
@@ -0,0 +1,7 @@
+packet-generator new {
+  name cdp
+  limit 1
+  node cdp-input
+  size 374-374
+  data { hex 0x02b46b96000100096978676265000500bf436973636f20494f5320536f6674776172652c2043333735304520536f66747761726520284333373530452d554e4956455253414c2d4d292c2056657273696f6e2031322e32283335295345352c2052454c4541534520534f4654574152452028666331290a436f707972696768742028632920313938362d3230303720627920436973636f2053797374656d732c20496e632e0a436f6d70696c6564205468752031392d4a756c2d30372031363a3137206279206e616368656e00060018636973636f2057532d4333373530452d3234544400020011000000010101cc0004000000000003001b54656e4769676162697445746865726e6574312f302f3100040008000000280008002400000c011200000000ffffffff010221ff000000000000001e7a50f000ff000000090004000a00060001000b0005010012000500001300050000160011000000010101cc000400000000001a00100000000100000000ffffffff }
+}
diff --git a/vnet/vnet/cdp/cdp_input.c b/vnet/vnet/cdp/cdp_input.c
new file mode 100644 (file)
index 0000000..d2d2306
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * 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 <vnet/cdp/cdp_node.h>
+
+cdp_main_t cdp_main;
+
+#define DEBUG_TLV_DUMP 0 /* 1=> dump TLV's to stdout while processing them */
+
+/* Reliable multicast messages we use to keep peers updated */
+mc_serialize_msg_t serialize_cdp_neighbor_msg;
+mc_serialize_msg_t serialize_cdp_keepalive_msg;
+
+/*
+ * 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_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_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");
+
+    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);
+    }));
+    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;
+    
+    vlib_cli_output (vm, "%U\n", format_cdp_neighbors, vm, cm);
+
+    return 0;
+}
+
+VLIB_CLI_COMMAND (show_cdp_command, static) = {
+    .path = "show cdp",
+    .short_help = "Show cdp command",
+    .function = show_cdp,
+};
+
+
+/*
+ * 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;
+}
diff --git a/vnet/vnet/cdp/cdp_node.c b/vnet/vnet/cdp/cdp_node.c
new file mode 100644 (file)
index 0000000..a826b12
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * 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 <vnet/cdp/cdp_node.h>
+#include <vnet/ethernet/packet.h>
+
+static vlib_node_registration_t cdp_process_node;
+
+/** \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;
+          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 
+ */
+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",
+  },
+};
+
+/*
+ * 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);
+
+    poll_time_remaining = 10.0 /* seconds */;
+    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;
+
+        default:
+            clib_warning ("BUG: event type 0x%wx", event_type);
+            break;
+        }
+        if (event_data)
+            _vec_len (event_data) = 0;
+
+        /* 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 
+ */
+VLIB_REGISTER_NODE (cdp_process_node, static) = {
+    .function = cdp_process,
+    .type = VLIB_NODE_TYPE_PROCESS,
+    .name = "cdp-process",
+};
+
+void vnet_cdp_node_reference(void) { }
diff --git a/vnet/vnet/cdp/cdp_node.h b/vnet/vnet/cdp/cdp_node.h
new file mode 100644 (file)
index 0000000..712ff14
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+#ifndef __included_cdp_node_h__
+#define __included_cdp_node_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 <vnet/cdp/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;
+
+    /* 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;
+
+    /* 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;
+
+cdp_main_t cdp_main;
+
+/* 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_SEND_NEIGHBOR,
+    CDP_EVENT_SEND_KEEPALIVE,
+} 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);
+
+serialize_function_t serialize_cdp_main, unserialize_cdp_main;
+
+#endif /* __included_cdp_node_h__ */
diff --git a/vnet/vnet/cdp/cdp_periodic.c b/vnet/vnet/cdp/cdp_periodic.c
new file mode 100644 (file)
index 0000000..ede9095
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * 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 <vnet/cdp/cdp_node.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));
+    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));
+    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));
+    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));
+    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);
+    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);
+        
+        /* Add the interface's ethernet source address */
+        hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
+        
+       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) ;
+
+        /* 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 */
+       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;
+
+    pool_foreach (n, cm->neighbors, 
+    ({
+      vec_add1 (n_list, n);
+    }));
+
+    /* 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);
diff --git a/vnet/vnet/cdp/cdp_protocol.h b/vnet/vnet/cdp/cdp_protocol.h
new file mode 100644 (file)
index 0000000..74cd79b
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+#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__ */
index 1e54a53..1c186fe 100644 (file)
@@ -296,6 +296,10 @@ llc_register_input_protocol (vlib_main_t * vm,
     clib_error_t * error = vlib_call_init_function (vm, llc_input_init);
     if (error)
       clib_error_report (error);
+    /* Otherwise, osi_input_init will wipe out e.g. the snap init */
+    error = vlib_call_init_function (vm, osi_input_init);
+    if (error)
+      clib_error_report (error);
   }
 
   pi = llc_get_protocol_info (lm, protocol);
index 1dffa7c..534adf0 100644 (file)
@@ -56,6 +56,12 @@ vpe_main_init (vlib_main_t * vm)
        return error;
     if ((error = vlib_call_init_function (vm, sixrd_init)))
        return error;
+    if ((error = vlib_call_init_function (vm, llc_init)))
+       return error;
+    if ((error = vlib_call_init_function (vm, snap_init)))
+       return error;
+    if ((error = vlib_call_init_function (vm, cdp_init)))
+       return error;
     if ((error = vlib_call_init_function (vm, nsh_gre_init)))
        return error;
     if ((error = vlib_call_init_function (vm, nsh_vxlan_gpe_init)))