VPP-344 Add LLDP feature
[vpp.git] / vnet / vnet / lldp / lldp_input.c
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>
20 #include <vnet/lldp/lldp_protocol.h>
21
22 lldp_tlv_code_t
23 lldp_tlv_get_code (const lldp_tlv_t * tlv)
24 {
25   return tlv->head.byte1 >> 1;
26 }
27
28 void
29 lldp_tlv_set_code (lldp_tlv_t * tlv, lldp_tlv_code_t code)
30 {
31   tlv->head.byte1 = (tlv->head.byte1 & 1) + (code << 1);
32 }
33
34 u16
35 lldp_tlv_get_length (const lldp_tlv_t * tlv)
36 {
37   return (((u16) (tlv->head.byte1 & 1)) << 8) + tlv->head.byte2;
38 }
39
40 void
41 lldp_tlv_set_length (lldp_tlv_t * tlv, u16 length)
42 {
43   tlv->head.byte2 = length & ((1 << 8) - 1);
44   if (length > (1 << 8) - 1)
45     {
46       tlv->head.byte1 |= 1;
47     }
48   else
49     {
50       tlv->head.byte1 &= (1 << 8) - 2;
51     }
52 }
53
54 lldp_main_t lldp_main;
55
56 static int
57 lldp_packet_scan (lldp_main_t * lm, lldp_intf_t * n, const lldp_tlv_t * pkt)
58 {
59   lldp_error_t e = LLDP_ERROR_NONE;
60   const lldp_tlv_t *tlv = pkt;
61
62 /* first check if the header fits in before extracting data from it */
63 #define TLV_VIOLATES_PKT_BOUNDARY(pkt, tlv)                               \
64     (((((u8 *)tlv) + sizeof(lldp_tlv_t)) > ((u8 *)pkt + vec_len(pkt))) || \
65      ((((u8 *)tlv) + lldp_tlv_get_length(tlv)) > ((u8 *)pkt + vec_len(pkt))))
66
67   /* first tlv is always chassis id, followed by port id and ttl tlvs */
68   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
69       LLDP_TLV_NAME (chassis_id) != lldp_tlv_get_code (tlv))
70     {
71       return LLDP_ERROR_BAD_TLV;
72     }
73
74   u16 l = lldp_tlv_get_length (tlv);
75   if (l < STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
76       LLDP_MIN_CHASS_ID_LEN ||
77       l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
78       LLDP_MAX_CHASS_ID_LEN)
79     {
80       return LLDP_ERROR_BAD_TLV;
81     }
82
83   u8 chid_subtype = ((lldp_chassis_id_tlv_t *) tlv)->subtype;
84   u8 *chid = ((lldp_chassis_id_tlv_t *) tlv)->id;
85   u8 chid_len = l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype);
86
87   tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
88
89   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
90       LLDP_TLV_NAME (port_id) != lldp_tlv_get_code (tlv))
91     {
92       return LLDP_ERROR_BAD_TLV;
93     }
94   l = lldp_tlv_get_length (tlv);
95   if (l < STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype) +
96       LLDP_MIN_PORT_ID_LEN ||
97       l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
98       LLDP_MAX_PORT_ID_LEN)
99     {
100       return LLDP_ERROR_BAD_TLV;
101     }
102
103   u8 portid_subtype = ((lldp_port_id_tlv_t *) tlv)->subtype;
104   u8 *portid = ((lldp_port_id_tlv_t *) tlv)->id;
105   u8 portid_len = l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype);
106
107   tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
108
109   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
110       LLDP_TLV_NAME (ttl) != lldp_tlv_get_code (tlv))
111     {
112       return LLDP_ERROR_BAD_TLV;
113     }
114   l = lldp_tlv_get_length (tlv);
115   if (l != STRUCT_SIZE_OF (lldp_ttl_tlv_t, ttl))
116     {
117       return LLDP_ERROR_BAD_TLV;
118     }
119   u16 ttl = ntohs (((lldp_ttl_tlv_t *) tlv)->ttl);
120   tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
121   while (!TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) &&
122          LLDP_TLV_NAME (pdu_end) != lldp_tlv_get_code (tlv))
123     {
124       switch (lldp_tlv_get_code (tlv))
125         {
126 #define F(num, type, str)         \
127     case LLDP_TLV_NAME(type):     \
128         /* ignore optional TLV */ \
129         break;
130           foreach_lldp_optional_tlv_type (F);
131 #undef F
132         default:
133           return LLDP_ERROR_BAD_TLV;
134         }
135       if (e)
136         {
137           return e;
138         }
139       tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) +
140                             lldp_tlv_get_length (tlv));
141     }
142   /* last tlv is pdu_end */
143   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
144       LLDP_TLV_NAME (pdu_end) != lldp_tlv_get_code (tlv) ||
145       0 != lldp_tlv_get_length (tlv))
146     {
147       return LLDP_ERROR_BAD_TLV;
148     }
149   /* LLDP PDU validated, now store data */
150   if (n->chassis_id)
151     {
152       _vec_len (n->chassis_id) = 0;
153     }
154   vec_add (n->chassis_id, chid, chid_len);
155   n->chassis_id_subtype = chid_subtype;
156   if (n->port_id)
157     {
158       _vec_len (n->port_id) = 0;
159     }
160   vec_add (n->port_id, portid, portid_len);
161   n->port_id_subtype = portid_subtype;
162   n->ttl = ttl;
163   return LLDP_ERROR_NONE;
164 }
165
166 lldp_intf_t *
167 lldp_get_intf (lldp_main_t * lm, u32 hw_if_index)
168 {
169   uword *p = hash_get (lm->intf_by_hw_if_index, hw_if_index);
170
171   if (p)
172     {
173       return pool_elt_at_index (lm->intfs, p[0]);
174     }
175   return NULL;
176 }
177
178 lldp_intf_t *
179 lldp_create_intf (lldp_main_t * lm, u32 hw_if_index)
180 {
181
182   uword *p;
183   lldp_intf_t *n;
184   p = hash_get (lm->intf_by_hw_if_index, hw_if_index);
185
186   if (p == 0)
187     {
188       pool_get (lm->intfs, n);
189       memset (n, 0, sizeof (*n));
190       n->hw_if_index = hw_if_index;
191       hash_set (lm->intf_by_hw_if_index, n->hw_if_index, n - lm->intfs);
192     }
193   else
194     {
195       n = pool_elt_at_index (lm->intfs, p[0]);
196     }
197   return n;
198 }
199
200 /*
201  * lldp input routine
202  */
203 lldp_error_t
204 lldp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0)
205 {
206   lldp_main_t *lm = &lldp_main;
207   lldp_error_t e;
208
209   /* find our interface */
210   lldp_intf_t *n = lldp_get_intf (lm, vnet_buffer (b0)->sw_if_index[VLIB_RX]);
211
212   if (!n)
213     {
214       /* lldp disabled on this interface, we're done */
215       return LLDP_ERROR_DISABLED;
216     }
217
218   /* Actually scan the packet */
219   e = lldp_packet_scan (lm, n, vlib_buffer_get_current (b0));
220
221   if (LLDP_ERROR_NONE == e)
222     {
223       n->last_heard = vlib_time_now (vm);
224     }
225
226   return e;
227 }
228
229 /*
230  * setup function
231  */
232 static clib_error_t *
233 lldp_init (vlib_main_t * vm)
234 {
235   clib_error_t *error;
236   lldp_main_t *lm = &lldp_main;
237
238   if ((error = vlib_call_init_function (vm, lldp_template_init)))
239     return error;
240
241   lm->vlib_main = vm;
242   lm->vnet_main = vnet_get_main ();
243   lm->msg_tx_hold = 4;          /* default value per IEEE 802.1AB-2009 */
244   lm->msg_tx_interval = 30;     /* default value per IEEE 802.1AB-2009 */
245
246   return 0;
247 }
248
249 VLIB_INIT_FUNCTION (lldp_init);
250
251 /*
252  * fd.io coding-style-patch-verification: ON
253  *
254  * Local Variables:
255  * eval: (c-set-style "gnu")
256  * End:
257  */