aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/lldp/lldp_protocol.h
blob: e641b26e20d0528c26b0e76a9953f0c30d8917cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * 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_lldp_protocol_h__
#define __included_lldp_protocol_h__
/**
 * @file
 * @brief LLDP protocol declarations
 */
#include <vnet/srp/packet.h>

/*
 * optional TLV codes.
 */
#define foreach_lldp_optional_tlv_type(F) \
  F (4, port_desc, "Port Description")    \
  F (5, sys_name, "System name")          \
  F (6, sys_desc, "System Description")   \
  F (7, sys_caps, "System Capabilities")  \
  F (8, mgmt_addr, "Management Address")  \
  F (127, org_spec, "Organizationally Specific TLV")

/*
 * all TLV codes.
 */
#define foreach_lldp_tlv_type(F)  \
  F (0, pdu_end, "End of LLDPDU") \
  F (1, chassis_id, "Chassis ID") \
  F (2, port_id, "Port ID")       \
  F (3, ttl, "Time To Live")      \
  foreach_lldp_optional_tlv_type (F)

#define LLDP_TLV_NAME(t) LLDP_TLV_##t

typedef enum
{
#define F(n, t, s) LLDP_TLV_NAME (t) = n,
  foreach_lldp_tlv_type (F)
#undef F
} lldp_tlv_code_t;

struct lldp_tlv_head
{
  u8 byte1;			/* contains TLV code in the upper 7 bits + MSB of length */
  u8 byte2;			/* contains the lower bits of length */
};

/* *INDENT-OFF* */
typedef CLIB_PACKED (struct {
  struct lldp_tlv_head head;
  u8 v[0];
}) lldp_tlv_t;
/* *INDENT-ON* */

lldp_tlv_code_t lldp_tlv_get_code (const lldp_tlv_t * tlv);
void lldp_tlv_set_code (lldp_tlv_t * tlv, lldp_tlv_code_t code);
u16 lldp_tlv_get_length (const lldp_tlv_t * tlv);
void lldp_tlv_set_length (lldp_tlv_t * tlv, u16 length);

#define foreach_chassis_id_subtype(F)      \
  F (0, reserved, "Reserved")              \
  F (1, chassis_comp, "Chassis component") \
  F (2, intf_alias, "Interface alias")     \
  F (3, port_comp, "Port component")       \
  F (4, mac_addr, "MAC address")           \
  F (5, net_addr, "Network address")       \
  F (6, intf_name, "Interface name")       \
  F (7, local, "Locally assigned")

#define LLDP_CHASS_ID_SUBTYPE_NAME(t) LLDP_CHASS_ID_SUBTYPE_##t
#define LLDP_MIN_CHASS_ID_LEN (1)
#define LLDP_MAX_CHASS_ID_LEN (255)

typedef enum
{
#define F(n, t, s) LLDP_CHASS_ID_SUBTYPE_NAME (t) = n,
  foreach_chassis_id_subtype (F)
#undef F
} lldp_chassis_id_subtype_t;

/* *INDENT-OFF* */
typedef CLIB_PACKED (struct {
  struct lldp_tlv_head head;
  u8 subtype;
  u8 id[0];
}) lldp_chassis_id_tlv_t;
/* *INDENT-ON* */

#define foreach_port_id_subtype(F)            \
  F (0, reserved, "Reserved")                 \
  F (1, intf_alias, "Interface alias")        \
  F (2, port_comp, "Port component")          \
  F (3, mac_addr, "MAC address")              \
  F (4, net_addr, "Network address")          \
  F (5, intf_name, "Interface name")          \
  F (6, agent_circuit_id, "Agent circuit ID") \
  F (7, local, "Locally assigned")

#define LLDP_PORT_ID_SUBTYPE_NAME(t) LLDP_PORT_ID_SUBTYPE_##t
#define LLDP_MIN_PORT_ID_LEN (1)
#define LLDP_MAX_PORT_ID_LEN (255)

typedef enum
{
#define F(n, t, s) LLDP_PORT_ID_SUBTYPE_NAME (t) = n,
  foreach_port_id_subtype (F)
#undef F
} lldp_port_id_subtype_t;

/* *INDENT-OFF* */
typedef CLIB_PACKED (struct {
  struct lldp_tlv_head head;
  u8 subtype;
  u8 id[0];
}) lldp_port_id_tlv_t;

typedef CLIB_PACKED (struct {
  struct lldp_tlv_head head;
  u16 ttl;
}) lldp_ttl_tlv_t;
/* *INDENT-ON* */

#endif /* __included_lldp_protocol_h__ */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
pi messages always follow a @ref msgbuf_t header: ```{.c} typedef struct msgbuf_ { unix_shared_memory_queue_t *q; u32 data_len; u32 gc_mark_timestamp; u8 data[0]; } msgbuf_t; ``` This structure makes it easy to trace messages without having to decode them - simply save data_len bytes - and allows @ref vl_msg_api_free() to rapidly dispose of message buffers: ```{.c} void vl_msg_api_free (void *a) { msgbuf_t *rv; api_main_t *am = &api_main; rv = (msgbuf_t *) (((u8 *) a) - offsetof (msgbuf_t, data)); /* * Here's the beauty of the scheme. Only one proc/thread has * control of a given message buffer. To free a buffer, we just * clear the queue field, and leave. No locks, no hits, no errors... */ if (rv->q) { rv->q = 0; rv->gc_mark_timestamp = 0; return; } <snip> } ``` ## Message Tracing and Replay It's extremely important that VPP can capture and replay sizeable binary API traces. System-level issues involving hundreds of thousands of API transactions can be re-run in a second or less. Partial replay allows one to binary-search for the point where the wheels fall off. One can add scaffolding to the data plane, to trigger when complex conditions obtain. With binary API trace, print, and replay, system-level bug reports of the form "after 300,000 API transactions, the VPP data-plane stopped forwarding traffic, FIX IT!" can be solved offline. More often than not, one discovers that a control-plane client misprograms the data plane after a long time or under complex circumstances. Without direct evidence, "it's a data-plane problem!" See @ref src/vlibmemory/memory_vlib.c @ref vl_msg_api_process_file(), and @ref src/vlibapi/api_shared.c. See also the debug CLI command "api trace" ## Client connection details Establishing a binary API connection to VPP from a C-language client is easy: ```{.c} int connect_to_vpe (char *client_name, int client_message_queue_length) { vat_main_t *vam = &vat_main; api_main_t *am = &api_main; if (vl_client_connect_to_vlib ("/vpe-api", client_name, client_message_queue_length) < 0) return -1; /* Memorize vpp's binary API message input queue address */ vam->vl_input_queue = am->shmem_hdr->vl_input_queue; /* And our client index */ vam->my_client_index = am->my_client_index; return 0; } ``` 32 is a typical value for client_message_queue_length. VPP cannot block when it needs to send an API message to a binary API client, and the VPP-side binary API message handlers are very fast. When sending asynchronous messages, make sure to scrape the binary API rx ring with some enthusiasm. ### binary API message RX pthread Calling @ref vl_client_connect_to_vlib spins up a binary API message RX pthread: ```{.c} static void * rx_thread_fn (void *arg) { unix_shared_memory_queue_t *q; memory_client_main_t *mm = &memory_client_main; api_main_t *am = &api_main; q = am->vl_input_queue; /* So we can make the rx thread terminate cleanly */ if (setjmp (mm->rx_thread_jmpbuf) == 0) { mm->rx_thread_jmpbuf_valid = 1; while (1) { vl_msg_api_queue_handler (q); } } pthread_exit (0); } ``` To handle the binary API message queue yourself, use @ref vl_client_connect_to_vlib_no_rx_pthread. In turn, vl_msg_api_queue_handler(...) uses mutex/condvar signalling to wake up, process VPP -> client traffic, then sleep. VPP supplies a condvar broadcast when the VPP -> client API message queue transitions from empty to nonempty. VPP checks its own binary API input queue at a very high rate. VPP invokes message handlers in "process" context [aka cooperative multitasking thread context] at a variable rate, depending on data-plane packet processing requirements. ## Client disconnection details To disconnect from VPP, call @ref vl_client_disconnect_from_vlib. Please arrange to call this function if the client application terminates abnormally. VPP makes every effort to hold a decent funeral for dead clients, but VPP can't guarantee to free leaked memory in the shared binary API segment. ## Sending binary API messages to VPP The point of the exercise is to send binary API messages to VPP, and to receive replies from VPP. Many VPP binary APIs comprise a client request message, and a simple status reply. For example, to set the admin status of an interface, one codes: ```{.c} vl_api_sw_interface_set_flags_t *mp; mp = vl_msg_api_alloc (sizeof (*mp)); memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_SW_INTERFACE_SET_FLAGS); mp->client_index = api_main.my_client_index; mp->sw_if_index = clib_host_to_net_u32 (<interface-sw-if-index>); vl_msg_api_send (api_main.shmem_hdr->vl_input_queue, (u8 *)mp); ``` Key points: * Use @ref vl_msg_api_alloc to allocate message buffers * Allocated message buffers are not initialized, and must be presumed to contain trash. * Don't forget to set the _vl_msg_id field! * As of this writing, binary API message IDs and data are sent in network byte order * The client-library global data structure @ref api_main keeps track of sufficient pointers and handles used to communicate with VPP ## Receiving binary API messages from VPP Unless you've made other arrangements (see @ref vl_client_connect_to_vlib_no_rx_pthread), *messages are received on a separate rx pthread*. Synchronization with the client application main thread is the responsibility of the application! Set up message handlers about as follows: ```{.c} #define vl_typedefs /* define message structures */ #include <vpp/api/vpe_all_api_h.h> #undef vl_typedefs /* declare message handlers for each api */ #define vl_endianfun /* define message structures */ #include <vpp/api/vpe_all_api_h.h> #undef vl_endianfun /* instantiate all the print functions we know about */ #define vl_print(handle, ...) #define vl_printfun #include <vpp/api/vpe_all_api_h.h> #undef vl_printfun /* Define a list of all message that the client handles */ #define foreach_vpe_api_reply_msg \ _(SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply) static clib_error_t * my_api_hookup (vlib_main_t * vm) { api_main_t *am = &api_main; #define _(N,n) \ vl_msg_api_set_handlers(VL_API_##N, #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_msg; #undef _ return 0; } ``` The key API used to establish message handlers is @ref vl_msg_api_set_handlers , which sets values in multiple parallel vectors in the @ref api_main_t structure. As of this writing: not all vector element values can be set through the API. You'll see sporadic API message registrations followed by minor adjustments of this form: ```{.c} /* * Thread-safe API messages */ am->is_mp_safe[VL_API_IP_ADD_DEL_ROUTE] = 1; am->is_mp_safe[VL_API_GET_NODE_GRAPH] = 1; ```