FD.io VPP  v17.07-30-g839fa73
Vector Packet Processing
lldp_input.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011-2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief LLDP packet parsing implementation
18  */
19 #include <vnet/lldp/lldp_node.h>
21 #include <vlibmemory/api.h>
22 
23 typedef struct
24 {
31  u8 data[0]; /* this contains both chassis id (chassis_id_len bytes) and port
32  id (portid_len bytes) */
34 
35 static void
37 {
38  ASSERT (vlib_get_thread_index () == 0);
39 
40  lldp_intf_t *n = lldp_get_intf (&lldp_main, a->hw_if_index);
41  if (!n)
42  {
43  /* LLDP turned off for this interface, ignore the update */
44  return;
45  }
46  const u8 *chassis_id = a->data;
47  const u8 *portid = a->data + a->chassis_id_len;
48 
49  if (n->chassis_id)
50  {
51  _vec_len (n->chassis_id) = 0;
52  }
53  vec_add (n->chassis_id, chassis_id, a->chassis_id_len);
55  if (n->port_id)
56  {
57  _vec_len (n->port_id) = 0;
58  }
59  vec_add (n->port_id, portid, a->portid_len);
61  n->ttl = a->ttl;
62  n->last_heard = vlib_time_now (lldp_main.vlib_main);
63 }
64 
65 static void
66 lldp_rpc_update_peer (u32 hw_if_index, const u8 * chid, u8 chid_len,
67  u8 chid_subtype, const u8 * portid,
68  u8 portid_len, u8 portid_subtype, u16 ttl)
69 {
70  const size_t data_size =
71  sizeof (lldp_intf_update_t) + chid_len + portid_len;
72  u8 data[data_size];
74  u->hw_if_index = hw_if_index;
75  u->chassis_id_len = chid_len;
76  u->chassis_id_subtype = chid_subtype;
77  u->ttl = ttl;
78  u->portid_len = portid_len;
79  u->portid_subtype = portid_subtype;
80  clib_memcpy (u->data, chid, chid_len);
81  clib_memcpy (u->data + chid_len, portid, portid_len);
83 }
84 
86 lldp_tlv_get_code (const lldp_tlv_t * tlv)
87 {
88  return tlv->head.byte1 >> 1;
89 }
90 
91 void
92 lldp_tlv_set_code (lldp_tlv_t * tlv, lldp_tlv_code_t code)
93 {
94  tlv->head.byte1 = (tlv->head.byte1 & 1) + (code << 1);
95 }
96 
97 u16
98 lldp_tlv_get_length (const lldp_tlv_t * tlv)
99 {
100  return (((u16) (tlv->head.byte1 & 1)) << 8) + tlv->head.byte2;
101 }
102 
103 void
104 lldp_tlv_set_length (lldp_tlv_t * tlv, u16 length)
105 {
106  tlv->head.byte2 = length & ((1 << 8) - 1);
107  if (length > (1 << 8) - 1)
108  {
109  tlv->head.byte1 |= 1;
110  }
111  else
112  {
113  tlv->head.byte1 &= (1 << 8) - 2;
114  }
115 }
116 
118 
119 static int
120 lldp_packet_scan (u32 hw_if_index, const lldp_tlv_t * pkt)
121 {
122  const lldp_tlv_t *tlv = pkt;
123 
124 #define TLV_VIOLATES_PKT_BOUNDARY(pkt, tlv) \
125  (((((u8 *)tlv) + sizeof (lldp_tlv_t)) > ((u8 *)pkt + vec_len (pkt))) || \
126  ((((u8 *)tlv) + lldp_tlv_get_length (tlv)) > ((u8 *)pkt + vec_len (pkt))))
127 
128  /* first tlv is always chassis id, followed by port id and ttl tlvs */
129  if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
130  LLDP_TLV_NAME (chassis_id) != lldp_tlv_get_code (tlv))
131  {
132  return LLDP_ERROR_BAD_TLV;
133  }
134 
135  u16 l = lldp_tlv_get_length (tlv);
136  if (l < STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
138  l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
140  {
141  return LLDP_ERROR_BAD_TLV;
142  }
143 
144  u8 chid_subtype = ((lldp_chassis_id_tlv_t *) tlv)->subtype;
145  u8 *chid = ((lldp_chassis_id_tlv_t *) tlv)->id;
146  u8 chid_len = l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype);
147 
148  tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
149 
150  if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
151  LLDP_TLV_NAME (port_id) != lldp_tlv_get_code (tlv))
152  {
153  return LLDP_ERROR_BAD_TLV;
154  }
155  l = lldp_tlv_get_length (tlv);
156  if (l < STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype) +
158  l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
160  {
161  return LLDP_ERROR_BAD_TLV;
162  }
163 
164  u8 portid_subtype = ((lldp_port_id_tlv_t *) tlv)->subtype;
165  u8 *portid = ((lldp_port_id_tlv_t *) tlv)->id;
166  u8 portid_len = l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype);
167 
168  tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
169 
170  if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
171  LLDP_TLV_NAME (ttl) != lldp_tlv_get_code (tlv))
172  {
173  return LLDP_ERROR_BAD_TLV;
174  }
175  l = lldp_tlv_get_length (tlv);
176  if (l != STRUCT_SIZE_OF (lldp_ttl_tlv_t, ttl))
177  {
178  return LLDP_ERROR_BAD_TLV;
179  }
180  u16 ttl = ntohs (((lldp_ttl_tlv_t *) tlv)->ttl);
181  tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
182  while (!TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) &&
183  LLDP_TLV_NAME (pdu_end) != lldp_tlv_get_code (tlv))
184  {
185  switch (lldp_tlv_get_code (tlv))
186  {
187 #define F(num, type, str) \
188  case LLDP_TLV_NAME (type): \
189  /* ignore optional TLV */ \
190  break;
192 #undef F
193  default:
194  return LLDP_ERROR_BAD_TLV;
195  }
196  tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) +
197  lldp_tlv_get_length (tlv));
198  }
199  /* last tlv is pdu_end */
200  if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
201  LLDP_TLV_NAME (pdu_end) != lldp_tlv_get_code (tlv) ||
202  0 != lldp_tlv_get_length (tlv))
203  {
204  return LLDP_ERROR_BAD_TLV;
205  }
206  lldp_rpc_update_peer (hw_if_index, chid, chid_len, chid_subtype, portid,
207  portid_len, portid_subtype, ttl);
208  return LLDP_ERROR_NONE;
209 }
210 
211 lldp_intf_t *
212 lldp_get_intf (lldp_main_t * lm, u32 hw_if_index)
213 {
214  uword *p = hash_get (lm->intf_by_hw_if_index, hw_if_index);
215 
216  if (p)
217  {
218  return pool_elt_at_index (lm->intfs, p[0]);
219  }
220  return NULL;
221 }
222 
223 lldp_intf_t *
224 lldp_create_intf (lldp_main_t * lm, u32 hw_if_index)
225 {
226 
227  uword *p;
228  lldp_intf_t *n;
229  p = hash_get (lm->intf_by_hw_if_index, hw_if_index);
230 
231  if (p == 0)
232  {
233  pool_get (lm->intfs, n);
234  memset (n, 0, sizeof (*n));
235  n->hw_if_index = hw_if_index;
236  hash_set (lm->intf_by_hw_if_index, n->hw_if_index, n - lm->intfs);
237  }
238  else
239  {
240  n = pool_elt_at_index (lm->intfs, p[0]);
241  }
242  return n;
243 }
244 
245 /*
246  * lldp input routine
247  */
250 {
251  lldp_main_t *lm = &lldp_main;
252  lldp_error_t e;
253 
254  /* find our interface */
257  (b0)->sw_if_index
258  [VLIB_RX]);
259  lldp_intf_t *n = lldp_get_intf (lm, sw_interface->hw_if_index);
260 
261  if (!n)
262  {
263  /* lldp disabled on this interface, we're done */
264  return LLDP_ERROR_DISABLED;
265  }
266 
267  /* Actually scan the packet */
268  e = lldp_packet_scan (sw_interface->hw_if_index,
270 
271  return e;
272 }
273 
274 /*
275  * setup function
276  */
277 static clib_error_t *
279 {
280  clib_error_t *error;
281  lldp_main_t *lm = &lldp_main;
282 
283  if ((error = vlib_call_init_function (vm, lldp_template_init)))
284  return error;
285 
286  lm->vlib_main = vm;
287  lm->vnet_main = vnet_get_main ();
288  lm->msg_tx_hold = 4; /* default value per IEEE 802.1AB-2009 */
289  lm->msg_tx_interval = 30; /* default value per IEEE 802.1AB-2009 */
290 
291  return 0;
292 }
293 
295 
296 /*
297  * fd.io coding-style-patch-verification: ON
298  *
299  * Local Variables:
300  * eval: (c-set-style "gnu")
301  * End:
302  */
lldp_main_t lldp_main
Definition: lldp_input.c:117
#define LLDP_MAX_CHASS_ID_LEN
Definition: lldp_protocol.h:83
vnet_main_t * vnet_main
Definition: lldp_node.h:73
u32 hw_if_index
Definition: lldp_node.h:33
#define LLDP_MIN_PORT_ID_LEN
#define hash_set(h, key, value)
Definition: hash.h:254
a
Definition: bitmap.h:516
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
u8 * chassis_id
Definition: lldp_node.h:40
#define NULL
Definition: clib.h:55
lldp_intf_t * intfs
Definition: lldp_node.h:54
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:192
static vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
lldp_intf_t * lldp_get_intf(lldp_main_t *lm, u32 hw_if_index)
Definition: lldp_input.c:212
#define LLDP_MIN_CHASS_ID_LEN
Definition: lldp_protocol.h:82
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:200
u16 ttl
Definition: lldp_node.h:42
#define F(num, type, str)
#define vec_add(V, E, N)
Add N elements to end of vector V (no header, unspecified alignment)
Definition: vec.h:599
u16 lldp_tlv_get_length(const lldp_tlv_t *tlv)
Definition: lldp_input.c:98
u8 msg_tx_hold
Definition: lldp_node.h:85
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
u8 * port_id
Definition: lldp_node.h:41
void lldp_tlv_set_code(lldp_tlv_t *tlv, lldp_tlv_code_t code)
Definition: lldp_input.c:92
#define foreach_lldp_optional_tlv_type(F)
Definition: lldp_protocol.h:26
#define vlib_call_init_function(vm, x)
Definition: init.h:162
lldp_tlv_code_t lldp_tlv_get_code(const lldp_tlv_t *tlv)
Definition: lldp_input.c:86
#define TLV_VIOLATES_PKT_BOUNDARY(pkt, tlv)
#define hash_get(h, key)
Definition: hash.h:248
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:397
lldp_error_t
Definition: lldp_node.h:111
u16 msg_tx_interval
Definition: lldp_node.h:94
uword * intf_by_hw_if_index
Definition: lldp_node.h:57
static void lldp_rpc_update_peer(u32 hw_if_index, const u8 *chid, u8 chid_len, u8 chid_subtype, const u8 *portid, u8 portid_len, u8 portid_subtype, u16 ttl)
Definition: lldp_input.c:66
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:188
void lldp_tlv_set_length(lldp_tlv_t *tlv, u16 length)
Definition: lldp_input.c:104
static int lldp_packet_scan(u32 hw_if_index, const lldp_tlv_t *pkt)
Definition: lldp_input.c:120
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:185
LLDP protocol declarations.
#define clib_memcpy(a, b, c)
Definition: string.h:69
void vl_api_rpc_call_main_thread(void *fp, u8 *data, u32 data_length)
Definition: memory_vlib.c:1328
#define ASSERT(truth)
lldp_error_t lldp_input(vlib_main_t *vm, vlib_buffer_t *b0, u32 bi0)
Definition: lldp_input.c:249
unsigned int u32
Definition: types.h:88
u64 uword
Definition: types.h:112
static void lldp_rpc_update_peer_cb(const lldp_intf_update_t *a)
Definition: lldp_input.c:36
vlib_main_t * vlib_main
Definition: lldp_node.h:72
unsigned short u16
Definition: types.h:57
lldp_port_id_subtype_t port_id_subtype
Definition: lldp_node.h:43
unsigned char u8
Definition: types.h:56
#define LLDP_MAX_PORT_ID_LEN
#define vnet_buffer(b)
Definition: buffer.h:303
LLDP global declarations.
#define STRUCT_SIZE_OF(t, f)
Definition: clib.h:64
lldp_chassis_id_subtype_t chassis_id_subtype
Definition: lldp_node.h:44
lldp_intf_t * lldp_create_intf(lldp_main_t *lm, u32 hw_if_index)
Definition: lldp_input.c:224
lldp_tlv_code_t
Definition: lldp_protocol.h:46
static clib_error_t * lldp_init(vlib_main_t *vm)
Definition: lldp_input.c:278
static clib_error_t * lldp_template_init(vlib_main_t *vm)
Definition: lldp_output.c:191
Definition: defs.h:46
f64 last_heard
Definition: lldp_node.h:36
#define LLDP_TLV_NAME(t)
Definition: lldp_protocol.h:44