Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 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> |
Klement Sekera | 48a9a54 | 2016-09-20 15:45:58 +0200 | [diff] [blame] | 21 | #include <vlibmemory/api.h> |
| 22 | |
| 23 | typedef struct |
| 24 | { |
| 25 | u32 hw_if_index; |
| 26 | u8 chassis_id_len; |
| 27 | u8 chassis_id_subtype; |
| 28 | u8 portid_len; |
| 29 | u8 portid_subtype; |
| 30 | u16 ttl; |
| 31 | u8 data[0]; /* this contains both chassis id (chassis_id_len bytes) and port |
| 32 | id (portid_len bytes) */ |
| 33 | } lldp_intf_update_t; |
| 34 | |
| 35 | static void |
| 36 | lldp_rpc_update_peer_cb (const lldp_intf_update_t * a) |
| 37 | { |
| 38 | ASSERT (os_get_cpu_number () == 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); |
| 54 | n->chassis_id_subtype = a->chassis_id_subtype; |
| 55 | if (n->port_id) |
| 56 | { |
| 57 | _vec_len (n->port_id) = 0; |
| 58 | } |
| 59 | vec_add (n->port_id, portid, a->portid_len); |
| 60 | n->port_id_subtype = a->portid_subtype; |
| 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]; |
| 73 | lldp_intf_update_t *u = (lldp_intf_update_t *) data; |
| 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); |
| 82 | vl_api_rpc_call_main_thread (lldp_rpc_update_peer_cb, data, data_size); |
| 83 | } |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 84 | |
| 85 | lldp_tlv_code_t |
| 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 | |
| 117 | lldp_main_t lldp_main; |
| 118 | |
| 119 | static int |
Klement Sekera | 48a9a54 | 2016-09-20 15:45:58 +0200 | [diff] [blame] | 120 | lldp_packet_scan (u32 hw_if_index, const lldp_tlv_t * pkt) |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 121 | { |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 122 | const lldp_tlv_t *tlv = pkt; |
| 123 | |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 124 | #define TLV_VIOLATES_PKT_BOUNDARY(pkt, tlv) \ |
Klement Sekera | 48a9a54 | 2016-09-20 15:45:58 +0200 | [diff] [blame] | 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)))) |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 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) + |
| 137 | LLDP_MIN_CHASS_ID_LEN || |
| 138 | l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) + |
| 139 | LLDP_MAX_CHASS_ID_LEN) |
| 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) + |
| 157 | LLDP_MIN_PORT_ID_LEN || |
| 158 | l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) + |
| 159 | LLDP_MAX_PORT_ID_LEN) |
| 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 | { |
Klement Sekera | 48a9a54 | 2016-09-20 15:45:58 +0200 | [diff] [blame] | 187 | #define F(num, type, str) \ |
| 188 | case LLDP_TLV_NAME (type): \ |
| 189 | /* ignore optional TLV */ \ |
| 190 | break; |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 191 | foreach_lldp_optional_tlv_type (F); |
| 192 | #undef F |
| 193 | default: |
| 194 | return LLDP_ERROR_BAD_TLV; |
| 195 | } |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 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 | } |
Klement Sekera | 48a9a54 | 2016-09-20 15:45:58 +0200 | [diff] [blame] | 206 | lldp_rpc_update_peer (hw_if_index, chid, chid_len, chid_subtype, portid, |
| 207 | portid_len, portid_subtype, ttl); |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 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 | */ |
| 248 | lldp_error_t |
| 249 | lldp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0) |
| 250 | { |
| 251 | lldp_main_t *lm = &lldp_main; |
| 252 | lldp_error_t e; |
| 253 | |
| 254 | /* find our interface */ |
Klement Sekera | 48a9a54 | 2016-09-20 15:45:58 +0200 | [diff] [blame] | 255 | vnet_sw_interface_t *sw_interface = vnet_get_sw_interface (lm->vnet_main, |
| 256 | vnet_buffer |
| 257 | (b0)->sw_if_index |
| 258 | [VLIB_RX]); |
| 259 | lldp_intf_t *n = lldp_get_intf (lm, sw_interface->hw_if_index); |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 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 */ |
Klement Sekera | 48a9a54 | 2016-09-20 15:45:58 +0200 | [diff] [blame] | 268 | e = lldp_packet_scan (sw_interface->hw_if_index, |
| 269 | vlib_buffer_get_current (b0)); |
Klement Sekera | de4582b | 2016-09-13 12:27:08 +0200 | [diff] [blame] | 270 | |
| 271 | return e; |
| 272 | } |
| 273 | |
| 274 | /* |
| 275 | * setup function |
| 276 | */ |
| 277 | static clib_error_t * |
| 278 | lldp_init (vlib_main_t * vm) |
| 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 | |
| 294 | VLIB_INIT_FUNCTION (lldp_init); |
| 295 | |
| 296 | /* |
| 297 | * fd.io coding-style-patch-verification: ON |
| 298 | * |
| 299 | * Local Variables: |
| 300 | * eval: (c-set-style "gnu") |
| 301 | * End: |
| 302 | */ |