]>
Commit | Line | Data |
---|---|---|
4b292b55 | 1 | /* -*- mode: c; c-file-style: "openbsd" -*- */ |
43c02e7b VB |
2 | /* |
3 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
4 | * | |
51434125 | 5 | * Permission to use, copy, modify, and/or distribute this software for any |
43c02e7b VB |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include "lldpd.h" | |
a8105c1b | 19 | #include "frame.h" |
43c02e7b VB |
20 | |
21 | #include <unistd.h> | |
22 | #include <errno.h> | |
23 | #include <time.h> | |
24 | #include <sys/types.h> | |
25 | #include <sys/socket.h> | |
26 | #include <sys/ioctl.h> | |
43c02e7b | 27 | |
e86777ab | 28 | static int |
e6b36c87 JV |
29 | lldpd_af_to_lldp_proto(int af) |
30 | { | |
31 | switch (af) { | |
32 | case LLDPD_AF_IPV4: | |
33 | return LLDP_MGMT_ADDR_IP4; | |
34 | case LLDPD_AF_IPV6: | |
35 | return LLDP_MGMT_ADDR_IP6; | |
36 | default: | |
37 | return LLDP_MGMT_ADDR_NONE; | |
38 | } | |
39 | } | |
40 | ||
e86777ab | 41 | static int |
e6b36c87 JV |
42 | lldpd_af_from_lldp_proto(int proto) |
43 | { | |
44 | switch (proto) { | |
45 | case LLDP_MGMT_ADDR_IP4: | |
46 | return LLDPD_AF_IPV4; | |
47 | case LLDP_MGMT_ADDR_IP6: | |
48 | return LLDPD_AF_IPV6; | |
49 | default: | |
50 | return LLDPD_AF_UNSPEC; | |
51 | } | |
52 | } | |
53 | ||
acb5f65b VB |
54 | static int _lldp_send(struct lldpd *global, |
55 | struct lldpd_hardware *hardware, | |
56 | u_int8_t c_id_subtype, | |
57 | char *c_id, | |
58 | int c_id_len, | |
59 | u_int8_t p_id_subtype, | |
60 | char *p_id, | |
61 | int p_id_len, | |
62 | int shutdown) | |
43c02e7b | 63 | { |
a8105c1b | 64 | struct lldpd_port *port; |
77507b69 | 65 | struct lldpd_chassis *chassis; |
a8105c1b VB |
66 | struct lldpd_frame *frame; |
67 | int length; | |
68 | u_int8_t *packet, *pos, *tlv; | |
e6b36c87 JV |
69 | struct lldpd_mgmt *mgmt; |
70 | int proto; | |
a8105c1b | 71 | |
1eadc9a1 VB |
72 | u_int8_t mcastaddr_regular[] = LLDP_ADDR_NEAREST_BRIDGE; |
73 | u_int8_t mcastaddr_nontpmr[] = LLDP_ADDR_NEAREST_NONTPMR_BRIDGE; | |
74 | u_int8_t mcastaddr_customer[] = LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE; | |
75 | u_int8_t *mcastaddr; | |
a1347cd8 VB |
76 | #ifdef ENABLE_DOT1 |
77 | const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1; | |
a1347cd8 | 78 | struct lldpd_vlan *vlan; |
9757bfbc SK |
79 | struct lldpd_ppvid *ppvid; |
80 | struct lldpd_pi *pi; | |
a1347cd8 VB |
81 | #endif |
82 | #ifdef ENABLE_DOT3 | |
83 | const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3; | |
a1347cd8 | 84 | #endif |
89840df0 | 85 | #ifdef ENABLE_LLDPMED |
115ff55c | 86 | int i; |
89840df0 | 87 | const u_int8_t med[] = LLDP_TLV_ORG_MED; |
89840df0 | 88 | #endif |
fb1b78bb | 89 | #ifdef ENABLE_CUSTOM |
cd5de7a2 | 90 | struct lldpd_custom *custom; |
fb1b78bb | 91 | #endif |
a8105c1b | 92 | port = &hardware->h_lport; |
77507b69 | 93 | chassis = port->p_chassis; |
a8105c1b | 94 | length = hardware->h_mtu; |
b0cb07f7 | 95 | if ((packet = (u_int8_t*)calloc(1, length)) == NULL) |
a8105c1b | 96 | return ENOMEM; |
a8105c1b | 97 | pos = packet; |
43c02e7b VB |
98 | |
99 | /* Ethernet header */ | |
1eadc9a1 VB |
100 | switch (global->g_config.c_lldp_agent_type) { |
101 | case LLDP_AGENT_TYPE_NEAREST_NONTPMR_BRIDGE: mcastaddr = mcastaddr_nontpmr; break; | |
102 | case LLDP_AGENT_TYPE_NEAREST_CUSTOMER_BRIDGE: mcastaddr = mcastaddr_customer; break; | |
103 | case LLDP_AGENT_TYPE_NEAREST_BRIDGE: | |
104 | default: mcastaddr = mcastaddr_regular; break; | |
105 | } | |
a8105c1b VB |
106 | if (!( |
107 | /* LLDP multicast address */ | |
1eadc9a1 | 108 | POKE_BYTES(mcastaddr, ETHER_ADDR_LEN) && |
a8105c1b | 109 | /* Source MAC address */ |
ed409ccd | 110 | POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) && |
a8105c1b VB |
111 | /* LLDP frame */ |
112 | POKE_UINT16(ETHERTYPE_LLDP))) | |
113 | goto toobig; | |
43c02e7b VB |
114 | |
115 | /* Chassis ID */ | |
a8105c1b VB |
116 | if (!( |
117 | POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) && | |
acb5f65b VB |
118 | POKE_UINT8(c_id_subtype) && |
119 | POKE_BYTES(c_id, c_id_len) && | |
a8105c1b VB |
120 | POKE_END_LLDP_TLV)) |
121 | goto toobig; | |
43c02e7b VB |
122 | |
123 | /* Port ID */ | |
a8105c1b VB |
124 | if (!( |
125 | POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) && | |
acb5f65b VB |
126 | POKE_UINT8(p_id_subtype) && |
127 | POKE_BYTES(p_id, p_id_len) && | |
a8105c1b VB |
128 | POKE_END_LLDP_TLV)) |
129 | goto toobig; | |
43c02e7b VB |
130 | |
131 | /* Time to live */ | |
a8105c1b VB |
132 | if (!( |
133 | POKE_START_LLDP_TLV(LLDP_TLV_TTL) && | |
71b0f981 | 134 | POKE_UINT16(shutdown?0:(global?global->g_config.c_ttl:180)) && |
a8105c1b VB |
135 | POKE_END_LLDP_TLV)) |
136 | goto toobig; | |
43c02e7b | 137 | |
acb5f65b VB |
138 | if (shutdown) |
139 | goto end; | |
140 | ||
43c02e7b | 141 | /* System name */ |
12313820 VB |
142 | if (chassis->c_name && *chassis->c_name != '\0') { |
143 | if (!( | |
144 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_NAME) && | |
145 | POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) && | |
146 | POKE_END_LLDP_TLV)) | |
147 | goto toobig; | |
148 | } | |
43c02e7b | 149 | |
d7985042 VB |
150 | /* System description (skip it if empty) */ |
151 | if (chassis->c_descr && *chassis->c_descr != '\0') { | |
152 | if (!( | |
153 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_DESCR) && | |
154 | POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) && | |
155 | POKE_END_LLDP_TLV)) | |
156 | goto toobig; | |
157 | } | |
43c02e7b VB |
158 | |
159 | /* System capabilities */ | |
ca838758 | 160 | if (global->g_config.c_cap_advertise && chassis->c_cap_available) { |
7d678398 AA |
161 | if (!( |
162 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_CAP) && | |
163 | POKE_UINT16(chassis->c_cap_available) && | |
164 | POKE_UINT16(chassis->c_cap_enabled) && | |
165 | POKE_END_LLDP_TLV)) | |
166 | goto toobig; | |
167 | } | |
43c02e7b | 168 | |
e6b36c87 JV |
169 | /* Management addresses */ |
170 | TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { | |
171 | proto = lldpd_af_to_lldp_proto(mgmt->m_family); | |
9221b5c2 | 172 | if (proto == LLDP_MGMT_ADDR_NONE) continue; |
a8105c1b | 173 | if (!( |
e6b36c87 JV |
174 | POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) && |
175 | /* Size of the address, including its type */ | |
176 | POKE_UINT8(mgmt->m_addrsize + 1) && | |
177 | POKE_UINT8(proto) && | |
178 | POKE_BYTES(&mgmt->m_addr, mgmt->m_addrsize))) | |
a8105c1b | 179 | goto toobig; |
43c02e7b VB |
180 | |
181 | /* Interface port type, OID */ | |
e6b36c87 | 182 | if (mgmt->m_iface == 0) { |
a8105c1b | 183 | if (!( |
e6b36c87 JV |
184 | /* We don't know the management interface */ |
185 | POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) && | |
186 | POKE_UINT32(0))) | |
a8105c1b VB |
187 | goto toobig; |
188 | } else { | |
189 | if (!( | |
e6b36c87 JV |
190 | /* We have the index of the management interface */ |
191 | POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) && | |
192 | POKE_UINT32(mgmt->m_iface))) | |
a8105c1b | 193 | goto toobig; |
43c02e7b | 194 | } |
a8105c1b | 195 | if (!( |
e6b36c87 JV |
196 | /* We don't provide an OID for management */ |
197 | POKE_UINT8(0) && | |
198 | POKE_END_LLDP_TLV)) | |
a8105c1b | 199 | goto toobig; |
43c02e7b VB |
200 | } |
201 | ||
202 | /* Port description */ | |
12313820 VB |
203 | if (port->p_descr && *port->p_descr != '\0') { |
204 | if (!( | |
205 | POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) && | |
206 | POKE_BYTES(port->p_descr, strlen(port->p_descr)) && | |
207 | POKE_END_LLDP_TLV)) | |
208 | goto toobig; | |
209 | } | |
43c02e7b | 210 | |
a1347cd8 | 211 | #ifdef ENABLE_DOT1 |
9757bfbc SK |
212 | /* Port VLAN ID */ |
213 | if(port->p_pvid != 0) { | |
214 | if (!( | |
215 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
216 | POKE_BYTES(dot1, sizeof(dot1)) && | |
217 | POKE_UINT8(LLDP_TLV_DOT1_PVID) && | |
218 | POKE_UINT16(port->p_pvid) && | |
219 | POKE_END_LLDP_TLV)) { | |
220 | goto toobig; | |
221 | } | |
222 | } | |
223 | /* Port and Protocol VLAN IDs */ | |
224 | TAILQ_FOREACH(ppvid, &port->p_ppvids, p_entries) { | |
225 | if (!( | |
226 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
227 | POKE_BYTES(dot1, sizeof(dot1)) && | |
228 | POKE_UINT8(LLDP_TLV_DOT1_PPVID) && | |
229 | POKE_UINT8(ppvid->p_cap_status) && | |
230 | POKE_UINT16(ppvid->p_ppvid) && | |
231 | POKE_END_LLDP_TLV)) { | |
232 | goto toobig; | |
233 | } | |
234 | } | |
43c02e7b | 235 | /* VLANs */ |
a8105c1b VB |
236 | TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) { |
237 | if (!( | |
238 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
239 | POKE_BYTES(dot1, sizeof(dot1)) && | |
240 | POKE_UINT8(LLDP_TLV_DOT1_VLANNAME) && | |
241 | POKE_UINT16(vlan->v_vid) && | |
242 | POKE_UINT8(strlen(vlan->v_name)) && | |
243 | POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) && | |
244 | POKE_END_LLDP_TLV)) | |
245 | goto toobig; | |
43c02e7b | 246 | } |
9757bfbc SK |
247 | /* Protocol Identities */ |
248 | TAILQ_FOREACH(pi, &port->p_pids, p_entries) { | |
249 | if (!( | |
250 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
251 | POKE_BYTES(dot1, sizeof(dot1)) && | |
252 | POKE_UINT8(LLDP_TLV_DOT1_PI) && | |
48acfcaf VB |
253 | POKE_UINT8(pi->p_pi_len) && |
254 | POKE_BYTES(pi->p_pi, pi->p_pi_len) && | |
9757bfbc SK |
255 | POKE_END_LLDP_TLV)) |
256 | goto toobig; | |
257 | } | |
a1347cd8 | 258 | #endif |
43c02e7b | 259 | |
a1347cd8 | 260 | #ifdef ENABLE_DOT3 |
43c02e7b | 261 | /* Aggregation status */ |
a8105c1b VB |
262 | if (!( |
263 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
264 | POKE_BYTES(dot3, sizeof(dot3)) && | |
265 | POKE_UINT8(LLDP_TLV_DOT3_LA) && | |
266 | /* Bit 0 = capability ; Bit 1 = status */ | |
267 | POKE_UINT8((port->p_aggregid) ? 3:1) && | |
268 | POKE_UINT32(port->p_aggregid) && | |
269 | POKE_END_LLDP_TLV)) | |
270 | goto toobig; | |
43c02e7b VB |
271 | |
272 | /* MAC/PHY */ | |
a8105c1b VB |
273 | if (!( |
274 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
275 | POKE_BYTES(dot3, sizeof(dot3)) && | |
276 | POKE_UINT8(LLDP_TLV_DOT3_MAC) && | |
3fd015c0 VB |
277 | POKE_UINT8(port->p_macphy.autoneg_support | |
278 | (port->p_macphy.autoneg_enabled << 1)) && | |
279 | POKE_UINT16(port->p_macphy.autoneg_advertised) && | |
280 | POKE_UINT16(port->p_macphy.mau_type) && | |
a8105c1b VB |
281 | POKE_END_LLDP_TLV)) |
282 | goto toobig; | |
548109b2 VB |
283 | |
284 | /* MFS */ | |
683b1710 VB |
285 | if (port->p_mfs) { |
286 | if (!( | |
287 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
288 | POKE_BYTES(dot3, sizeof(dot3)) && | |
289 | POKE_UINT8(LLDP_TLV_DOT3_MFS) && | |
290 | POKE_UINT16(port->p_mfs) && | |
291 | POKE_END_LLDP_TLV)) | |
292 | goto toobig; | |
293 | } | |
42ee7382 VB |
294 | /* Power */ |
295 | if (port->p_power.devicetype) { | |
296 | if (!( | |
297 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
298 | POKE_BYTES(dot3, sizeof(dot3)) && | |
299 | POKE_UINT8(LLDP_TLV_DOT3_POWER) && | |
300 | POKE_UINT8(( | |
301 | (((2 - port->p_power.devicetype) %(1<< 1))<<0) | | |
302 | (( port->p_power.supported %(1<< 1))<<1) | | |
303 | (( port->p_power.enabled %(1<< 1))<<2) | | |
304 | (( port->p_power.paircontrol %(1<< 1))<<3))) && | |
305 | POKE_UINT8(port->p_power.pairs) && | |
608cb51c VB |
306 | POKE_UINT8(port->p_power.class))) |
307 | goto toobig; | |
308 | /* 802.3at */ | |
309 | if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { | |
310 | if (!( | |
311 | POKE_UINT8(( | |
312 | (((port->p_power.powertype == | |
313 | LLDP_DOT3_POWER_8023AT_TYPE1)?1:0) << 7) | | |
314 | (((port->p_power.devicetype == | |
315 | LLDP_DOT3_POWER_PSE)?0:1) << 6) | | |
316 | ((port->p_power.source %(1<< 2))<<4) | | |
317 | ((port->p_power.priority %(1<< 2))<<0))) && | |
318 | POKE_UINT16(port->p_power.requested) && | |
319 | POKE_UINT16(port->p_power.allocated))) | |
320 | goto toobig; | |
321 | } | |
322 | if (!(POKE_END_LLDP_TLV)) | |
42ee7382 VB |
323 | goto toobig; |
324 | } | |
a1347cd8 | 325 | #endif |
43c02e7b | 326 | |
89840df0 | 327 | #ifdef ENABLE_LLDPMED |
740593ff | 328 | if (port->p_med_cap_enabled) { |
89840df0 | 329 | /* LLDP-MED cap */ |
03d178b6 VB |
330 | if (port->p_med_cap_enabled & LLDP_MED_CAP_CAP) { |
331 | if (!( | |
332 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
333 | POKE_BYTES(med, sizeof(med)) && | |
334 | POKE_UINT8(LLDP_TLV_MED_CAP) && | |
335 | POKE_UINT16(chassis->c_med_cap_available) && | |
336 | POKE_UINT8(chassis->c_med_type) && | |
337 | POKE_END_LLDP_TLV)) | |
338 | goto toobig; | |
339 | } | |
89840df0 VB |
340 | |
341 | /* LLDP-MED inventory */ | |
a8105c1b | 342 | #define LLDP_INVENTORY(value, subtype) \ |
89840df0 | 343 | if (value) { \ |
a8105c1b VB |
344 | if (!( \ |
345 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && \ | |
346 | POKE_BYTES(med, sizeof(med)) && \ | |
347 | POKE_UINT8(subtype) && \ | |
348 | POKE_BYTES(value, \ | |
349 | (strlen(value)>32)?32:strlen(value)) && \ | |
350 | POKE_END_LLDP_TLV)) \ | |
351 | goto toobig; \ | |
89840df0 | 352 | } |
e809a587 | 353 | |
4b292b55 | 354 | if (port->p_med_cap_enabled & LLDP_MED_CAP_IV) { |
a8105c1b VB |
355 | LLDP_INVENTORY(chassis->c_med_hw, |
356 | LLDP_TLV_MED_IV_HW); | |
357 | LLDP_INVENTORY(chassis->c_med_fw, | |
358 | LLDP_TLV_MED_IV_FW); | |
359 | LLDP_INVENTORY(chassis->c_med_sw, | |
360 | LLDP_TLV_MED_IV_SW); | |
361 | LLDP_INVENTORY(chassis->c_med_sn, | |
362 | LLDP_TLV_MED_IV_SN); | |
363 | LLDP_INVENTORY(chassis->c_med_manuf, | |
364 | LLDP_TLV_MED_IV_MANUF); | |
365 | LLDP_INVENTORY(chassis->c_med_model, | |
366 | LLDP_TLV_MED_IV_MODEL); | |
367 | LLDP_INVENTORY(chassis->c_med_asset, | |
368 | LLDP_TLV_MED_IV_ASSET); | |
e809a587 | 369 | } |
115ff55c VB |
370 | |
371 | /* LLDP-MED location */ | |
4b292b55 | 372 | for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) { |
740593ff | 373 | if (port->p_med_location[i].format == i + 1) { |
a8105c1b VB |
374 | if (!( |
375 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
376 | POKE_BYTES(med, sizeof(med)) && | |
377 | POKE_UINT8(LLDP_TLV_MED_LOCATION) && | |
378 | POKE_UINT8(port->p_med_location[i].format) && | |
379 | POKE_BYTES(port->p_med_location[i].data, | |
380 | port->p_med_location[i].data_len) && | |
381 | POKE_END_LLDP_TLV)) | |
382 | goto toobig; | |
115ff55c VB |
383 | } |
384 | } | |
86f24df3 VB |
385 | |
386 | /* LLDP-MED network policy */ | |
4b292b55 | 387 | for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) { |
86f24df3 VB |
388 | if (port->p_med_policy[i].type == i + 1) { |
389 | if (!( | |
390 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
391 | POKE_BYTES(med, sizeof(med)) && | |
392 | POKE_UINT8(LLDP_TLV_MED_POLICY) && | |
393 | POKE_UINT32(( | |
394 | ((port->p_med_policy[i].type %(1<< 8))<<24) | | |
395 | ((port->p_med_policy[i].unknown %(1<< 1))<<23) | | |
396 | ((port->p_med_policy[i].tagged %(1<< 1))<<22) | | |
397 | /*((0 %(1<< 1))<<21) |*/ | |
398 | ((port->p_med_policy[i].vid %(1<<12))<< 9) | | |
399 | ((port->p_med_policy[i].priority %(1<< 3))<< 6) | | |
400 | ((port->p_med_policy[i].dscp %(1<< 6))<< 0) )) && | |
401 | POKE_END_LLDP_TLV)) | |
402 | goto toobig; | |
403 | } | |
404 | } | |
009ddd23 VB |
405 | |
406 | /* LLDP-MED POE-MDI */ | |
4b292b55 VB |
407 | if ((port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PSE) || |
408 | (port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PD)) { | |
009ddd23 VB |
409 | int devicetype = 0, source = 0; |
410 | if (!( | |
411 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
412 | POKE_BYTES(med, sizeof(med)) && | |
413 | POKE_UINT8(LLDP_TLV_MED_MDI))) | |
414 | goto toobig; | |
415 | switch (port->p_med_power.devicetype) { | |
4b292b55 | 416 | case LLDP_MED_POW_TYPE_PSE: |
009ddd23 VB |
417 | devicetype = 0; |
418 | switch (port->p_med_power.source) { | |
4b292b55 VB |
419 | case LLDP_MED_POW_SOURCE_PRIMARY: source = 1; break; |
420 | case LLDP_MED_POW_SOURCE_BACKUP: source = 2; break; | |
421 | case LLDP_MED_POW_SOURCE_RESERVED: source = 3; break; | |
009ddd23 VB |
422 | default: source = 0; break; |
423 | } | |
424 | break; | |
4b292b55 | 425 | case LLDP_MED_POW_TYPE_PD: |
009ddd23 VB |
426 | devicetype = 1; |
427 | switch (port->p_med_power.source) { | |
4b292b55 VB |
428 | case LLDP_MED_POW_SOURCE_PSE: source = 1; break; |
429 | case LLDP_MED_POW_SOURCE_LOCAL: source = 2; break; | |
430 | case LLDP_MED_POW_SOURCE_BOTH: source = 3; break; | |
009ddd23 VB |
431 | default: source = 0; break; |
432 | } | |
433 | break; | |
434 | } | |
435 | if (!( | |
436 | POKE_UINT8(( | |
437 | ((devicetype %(1<< 2))<<6) | | |
438 | ((source %(1<< 2))<<4) | | |
439 | ((port->p_med_power.priority %(1<< 4))<<0) )) && | |
440 | POKE_UINT16(port->p_med_power.val) && | |
441 | POKE_END_LLDP_TLV)) | |
442 | goto toobig; | |
443 | } | |
89840df0 VB |
444 | } |
445 | #endif | |
446 | ||
fb1b78bb | 447 | #ifdef ENABLE_CUSTOM |
cd5de7a2 AA |
448 | TAILQ_FOREACH(custom, &port->p_custom_list, next) { |
449 | if (!( | |
450 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
451 | POKE_BYTES(custom->oui, sizeof(custom->oui)) && | |
452 | POKE_UINT8(custom->subtype) && | |
453 | POKE_BYTES(custom->oui_info, custom->oui_info_len) && | |
454 | POKE_END_LLDP_TLV)) | |
455 | goto toobig; | |
456 | } | |
fb1b78bb | 457 | #endif |
cd5de7a2 | 458 | |
acb5f65b | 459 | end: |
43c02e7b | 460 | /* END */ |
a8105c1b VB |
461 | if (!( |
462 | POKE_START_LLDP_TLV(LLDP_TLV_END) && | |
463 | POKE_END_LLDP_TLV)) | |
464 | goto toobig; | |
43c02e7b | 465 | |
5347914e | 466 | if (interfaces_send_helper(global, hardware, |
6e75df87 | 467 | (char *)packet, pos - packet) == -1) { |
6f8925be | 468 | log_warn("lldp", "unable to send packet on real device for %s", |
f2dcb180 VB |
469 | hardware->h_ifname); |
470 | free(packet); | |
471 | return ENETDOWN; | |
43c02e7b VB |
472 | } |
473 | ||
f2dcb180 VB |
474 | hardware->h_tx_cnt++; |
475 | ||
a8105c1b | 476 | /* We assume that LLDP frame is the reference */ |
acb5f65b | 477 | if (!shutdown && (frame = (struct lldpd_frame*)malloc( |
a8105c1b VB |
478 | sizeof(int) + pos - packet)) != NULL) { |
479 | frame->size = pos - packet; | |
480 | memcpy(&frame->frame, packet, frame->size); | |
77507b69 VB |
481 | if ((hardware->h_lport.p_lastframe == NULL) || |
482 | (hardware->h_lport.p_lastframe->size != frame->size) || | |
483 | (memcmp(hardware->h_lport.p_lastframe->frame, frame->frame, | |
a8105c1b | 484 | frame->size) != 0)) { |
77507b69 | 485 | free(hardware->h_lport.p_lastframe); |
acb5f65b VB |
486 | hardware->h_lport.p_lastframe = frame; |
487 | hardware->h_lport.p_lastchange = time(NULL); | |
488 | } else free(frame); | |
43c02e7b VB |
489 | } |
490 | ||
a8105c1b | 491 | free(packet); |
43c02e7b | 492 | return 0; |
a8105c1b VB |
493 | |
494 | toobig: | |
495 | free(packet); | |
496 | return E2BIG; | |
43c02e7b VB |
497 | } |
498 | ||
e770b720 VB |
499 | /* Send a shutdown LLDPDU. */ |
500 | int | |
acb5f65b VB |
501 | lldp_send_shutdown(struct lldpd *global, |
502 | struct lldpd_hardware *hardware) | |
503 | { | |
e770b720 VB |
504 | if (hardware->h_lchassis_previous_id == NULL || |
505 | hardware->h_lport_previous_id == NULL) | |
506 | return 0; | |
acb5f65b VB |
507 | return _lldp_send(global, hardware, |
508 | hardware->h_lchassis_previous_id_subtype, | |
509 | hardware->h_lchassis_previous_id, | |
510 | hardware->h_lchassis_previous_id_len, | |
511 | hardware->h_lport_previous_id_subtype, | |
512 | hardware->h_lport_previous_id, | |
513 | hardware->h_lport_previous_id_len, | |
514 | 1); | |
515 | } | |
516 | ||
517 | int | |
518 | lldp_send(struct lldpd *global, | |
519 | struct lldpd_hardware *hardware) | |
520 | { | |
521 | struct lldpd_port *port = &hardware->h_lport; | |
522 | struct lldpd_chassis *chassis = port->p_chassis; | |
523 | int ret; | |
524 | ||
525 | /* Check if we have a change. */ | |
526 | if (hardware->h_lchassis_previous_id != NULL && | |
527 | hardware->h_lport_previous_id != NULL && | |
528 | (hardware->h_lchassis_previous_id_subtype != chassis->c_id_subtype || | |
529 | hardware->h_lchassis_previous_id_len != chassis->c_id_len || | |
530 | hardware->h_lport_previous_id_subtype != port->p_id_subtype || | |
531 | hardware->h_lport_previous_id_len != port->p_id_len || | |
532 | memcmp(hardware->h_lchassis_previous_id, | |
533 | chassis->c_id, chassis->c_id_len) || | |
534 | memcmp(hardware->h_lport_previous_id, | |
535 | port->p_id, port->p_id_len))) { | |
536 | log_info("lldp", "MSAP has changed for port %s, sending a shutdown LLDPDU", | |
537 | hardware->h_ifname); | |
538 | if ((ret = lldp_send_shutdown(global, hardware)) != 0) | |
539 | return ret; | |
540 | } | |
541 | ||
542 | log_debug("lldp", "send LLDP PDU to %s", | |
543 | hardware->h_ifname); | |
544 | ||
545 | if ((ret = _lldp_send(global, hardware, | |
546 | chassis->c_id_subtype, | |
547 | chassis->c_id, | |
548 | chassis->c_id_len, | |
549 | port->p_id_subtype, | |
550 | port->p_id, | |
551 | port->p_id_len, | |
552 | 0)) != 0) | |
553 | return ret; | |
554 | ||
555 | /* Record current chassis and port ID */ | |
556 | free(hardware->h_lchassis_previous_id); | |
557 | hardware->h_lchassis_previous_id_subtype = chassis->c_id_subtype; | |
558 | hardware->h_lchassis_previous_id_len = chassis->c_id_len; | |
559 | if ((hardware->h_lchassis_previous_id = malloc(chassis->c_id_len)) != NULL) | |
560 | memcpy(hardware->h_lchassis_previous_id, chassis->c_id, | |
561 | chassis->c_id_len); | |
562 | free(hardware->h_lport_previous_id); | |
563 | hardware->h_lport_previous_id_subtype = port->p_id_subtype; | |
564 | hardware->h_lport_previous_id_len = port->p_id_len; | |
565 | if ((hardware->h_lport_previous_id = malloc(port->p_id_len)) != NULL) | |
566 | memcpy(hardware->h_lport_previous_id, port->p_id, | |
567 | port->p_id_len); | |
568 | ||
569 | return 0; | |
570 | } | |
571 | ||
a8105c1b VB |
572 | #define CHECK_TLV_SIZE(x, name) \ |
573 | do { if (tlv_size < (x)) { \ | |
6f8925be | 574 | log_warnx("lldp", name " TLV too short received on %s", \ |
a8105c1b VB |
575 | hardware->h_ifname); \ |
576 | goto malformed; \ | |
577 | } } while (0) | |
578 | ||
43c02e7b VB |
579 | int |
580 | lldp_decode(struct lldpd *cfg, char *frame, int s, | |
581 | struct lldpd_hardware *hardware, | |
582 | struct lldpd_chassis **newchassis, struct lldpd_port **newport) | |
583 | { | |
584 | struct lldpd_chassis *chassis; | |
585 | struct lldpd_port *port; | |
a98ed042 | 586 | char lldpaddr[ETHER_ADDR_LEN]; |
43c02e7b VB |
587 | const char dot1[] = LLDP_TLV_ORG_DOT1; |
588 | const char dot3[] = LLDP_TLV_ORG_DOT3; | |
6772b237 | 589 | const char med[] = LLDP_TLV_ORG_MED; |
5317a14a | 590 | const char dcbx[] = LLDP_TLV_ORG_DCBX; |
a3cae2a7 | 591 | unsigned char orgid[3]; |
5a215d4b | 592 | int length, gotend = 0, ttl_received = 0; |
a8105c1b VB |
593 | int tlv_size, tlv_type, tlv_subtype; |
594 | u_int8_t *pos, *tlv; | |
43c02e7b | 595 | char *b; |
a8105c1b | 596 | #ifdef ENABLE_DOT1 |
506273e9 | 597 | struct lldpd_vlan *vlan = NULL; |
75b3469d | 598 | int vlan_len; |
9757bfbc | 599 | struct lldpd_ppvid *ppvid; |
506273e9 | 600 | struct lldpd_pi *pi = NULL; |
a8105c1b | 601 | #endif |
e6b36c87 JV |
602 | struct lldpd_mgmt *mgmt; |
603 | int af; | |
604 | u_int8_t addr_str_length, addr_str_buffer[32]; | |
605 | u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype; | |
606 | u_int32_t iface_number, iface; | |
fb1b78bb | 607 | #ifdef ENABLE_CUSTOM |
1cce9f1a | 608 | struct lldpd_custom *custom = NULL; |
fb1b78bb | 609 | #endif |
43c02e7b | 610 | |
6f8925be VB |
611 | log_debug("lldp", "receive LLDP PDU on %s", |
612 | hardware->h_ifname); | |
613 | ||
43c02e7b | 614 | if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { |
6f8925be | 615 | log_warn("lldp", "failed to allocate remote chassis"); |
43c02e7b VB |
616 | return -1; |
617 | } | |
e6b36c87 | 618 | TAILQ_INIT(&chassis->c_mgmt); |
43c02e7b | 619 | if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { |
6f8925be | 620 | log_warn("lldp", "failed to allocate remote port"); |
43c02e7b VB |
621 | free(chassis); |
622 | return -1; | |
623 | } | |
a1347cd8 | 624 | #ifdef ENABLE_DOT1 |
43c02e7b | 625 | TAILQ_INIT(&port->p_vlans); |
9757bfbc SK |
626 | TAILQ_INIT(&port->p_ppvids); |
627 | TAILQ_INIT(&port->p_pids); | |
a1347cd8 | 628 | #endif |
fb1b78bb | 629 | #ifdef ENABLE_CUSTOM |
cd5de7a2 | 630 | TAILQ_INIT(&port->p_custom_list); |
fb1b78bb | 631 | #endif |
43c02e7b | 632 | |
a8105c1b VB |
633 | length = s; |
634 | pos = (u_int8_t*)frame; | |
635 | ||
4e5f34c5 | 636 | if (length < 2*ETHER_ADDR_LEN + sizeof(u_int16_t)) { |
6f8925be | 637 | log_warnx("lldp", "too short frame received on %s", hardware->h_ifname); |
43c02e7b VB |
638 | goto malformed; |
639 | } | |
a98ed042 VB |
640 | PEEK_BYTES(lldpaddr, ETHER_ADDR_LEN); |
641 | if (memcmp(lldpaddr, (const char [])LLDP_ADDR_NEAREST_BRIDGE, ETHER_ADDR_LEN) && | |
642 | memcmp(lldpaddr, (const char [])LLDP_ADDR_NEAREST_NONTPMR_BRIDGE, ETHER_ADDR_LEN) && | |
643 | memcmp(lldpaddr, (const char [])LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE, ETHER_ADDR_LEN)) { | |
6f8925be | 644 | log_info("lldp", "frame not targeted at LLDP multicast address received on %s", |
43c02e7b VB |
645 | hardware->h_ifname); |
646 | goto malformed; | |
647 | } | |
4e5f34c5 | 648 | PEEK_DISCARD(ETHER_ADDR_LEN); /* Skip source address */ |
a8105c1b | 649 | if (PEEK_UINT16 != ETHERTYPE_LLDP) { |
6f8925be | 650 | log_info("lldp", "non LLDP frame received on %s", |
43c02e7b VB |
651 | hardware->h_ifname); |
652 | goto malformed; | |
653 | } | |
654 | ||
a8105c1b VB |
655 | while (length && (!gotend)) { |
656 | if (length < 2) { | |
6f8925be | 657 | log_warnx("lldp", "tlv header too short received on %s", |
43c02e7b VB |
658 | hardware->h_ifname); |
659 | goto malformed; | |
660 | } | |
a8105c1b VB |
661 | tlv_size = PEEK_UINT16; |
662 | tlv_type = tlv_size >> 9; | |
663 | tlv_size = tlv_size & 0x1ff; | |
2d1fe392 | 664 | (void)PEEK_SAVE(tlv); |
a8105c1b | 665 | if (length < tlv_size) { |
6f8925be | 666 | log_warnx("lldp", "frame too short for tlv received on %s", |
43c02e7b VB |
667 | hardware->h_ifname); |
668 | goto malformed; | |
669 | } | |
a8105c1b | 670 | switch (tlv_type) { |
43c02e7b | 671 | case LLDP_TLV_END: |
a8105c1b | 672 | if (tlv_size != 0) { |
6f8925be | 673 | log_warnx("lldp", "lldp end received with size not null on %s", |
43c02e7b VB |
674 | hardware->h_ifname); |
675 | goto malformed; | |
676 | } | |
a8105c1b | 677 | if (length) |
6f8925be | 678 | log_debug("lldp", "extra data after lldp end on %s", |
43c02e7b VB |
679 | hardware->h_ifname); |
680 | gotend = 1; | |
681 | break; | |
682 | case LLDP_TLV_CHASSIS_ID: | |
683 | case LLDP_TLV_PORT_ID: | |
a8105c1b VB |
684 | CHECK_TLV_SIZE(2, "Port Id"); |
685 | tlv_subtype = PEEK_UINT8; | |
686 | if ((tlv_subtype == 0) || (tlv_subtype > 7)) { | |
6f8925be | 687 | log_warnx("lldp", "unknown subtype for tlv id received on %s", |
43c02e7b VB |
688 | hardware->h_ifname); |
689 | goto malformed; | |
690 | } | |
a8105c1b | 691 | if ((b = (char *)calloc(1, tlv_size - 1)) == NULL) { |
6f8925be | 692 | log_warn("lldp", "unable to allocate memory for id tlv " |
43c02e7b VB |
693 | "received on %s", |
694 | hardware->h_ifname); | |
695 | goto malformed; | |
696 | } | |
a8105c1b VB |
697 | PEEK_BYTES(b, tlv_size - 1); |
698 | if (tlv_type == LLDP_TLV_PORT_ID) { | |
699 | port->p_id_subtype = tlv_subtype; | |
43c02e7b | 700 | port->p_id = b; |
a8105c1b | 701 | port->p_id_len = tlv_size - 1; |
43c02e7b | 702 | } else { |
a8105c1b | 703 | chassis->c_id_subtype = tlv_subtype; |
43c02e7b | 704 | chassis->c_id = b; |
a8105c1b | 705 | chassis->c_id_len = tlv_size - 1; |
43c02e7b | 706 | } |
43c02e7b VB |
707 | break; |
708 | case LLDP_TLV_TTL: | |
a8105c1b | 709 | CHECK_TLV_SIZE(2, "TTL"); |
78346c89 | 710 | port->p_ttl = PEEK_UINT16; |
5a215d4b | 711 | ttl_received = 1; |
43c02e7b VB |
712 | break; |
713 | case LLDP_TLV_PORT_DESCR: | |
714 | case LLDP_TLV_SYSTEM_NAME: | |
715 | case LLDP_TLV_SYSTEM_DESCR: | |
a8105c1b | 716 | if (tlv_size < 1) { |
6f8925be | 717 | log_debug("lldp", "empty tlv received on %s", |
43c02e7b | 718 | hardware->h_ifname); |
a700e935 | 719 | break; |
43c02e7b | 720 | } |
a8105c1b | 721 | if ((b = (char *)calloc(1, tlv_size + 1)) == NULL) { |
6f8925be | 722 | log_warn("lldp", "unable to allocate memory for string tlv " |
43c02e7b VB |
723 | "received on %s", |
724 | hardware->h_ifname); | |
725 | goto malformed; | |
726 | } | |
a8105c1b VB |
727 | PEEK_BYTES(b, tlv_size); |
728 | if (tlv_type == LLDP_TLV_PORT_DESCR) | |
43c02e7b | 729 | port->p_descr = b; |
a8105c1b | 730 | else if (tlv_type == LLDP_TLV_SYSTEM_NAME) |
43c02e7b VB |
731 | chassis->c_name = b; |
732 | else chassis->c_descr = b; | |
733 | break; | |
734 | case LLDP_TLV_SYSTEM_CAP: | |
a8105c1b VB |
735 | CHECK_TLV_SIZE(4, "System capabilities"); |
736 | chassis->c_cap_available = PEEK_UINT16; | |
737 | chassis->c_cap_enabled = PEEK_UINT16; | |
43c02e7b VB |
738 | break; |
739 | case LLDP_TLV_MGMT_ADDR: | |
e6b36c87 JV |
740 | CHECK_TLV_SIZE(1, "Management address"); |
741 | addr_str_length = PEEK_UINT8; | |
dd4f16e7 VB |
742 | if (addr_str_length > sizeof(addr_str_buffer)) { |
743 | log_warnx("lldp", "too large management address on %s", | |
744 | hardware->h_ifname); | |
745 | goto malformed; | |
746 | } | |
feed548d | 747 | CHECK_TLV_SIZE(1 + addr_str_length, "Management address"); |
e6b36c87 JV |
748 | PEEK_BYTES(addr_str_buffer, addr_str_length); |
749 | addr_length = addr_str_length - 1; | |
750 | addr_family = addr_str_buffer[0]; | |
751 | addr_ptr = &addr_str_buffer[1]; | |
feed548d | 752 | CHECK_TLV_SIZE(1 + addr_str_length + 5, "Management address"); |
e6b36c87 JV |
753 | iface_subtype = PEEK_UINT8; |
754 | iface_number = PEEK_UINT32; | |
dd4f16e7 | 755 | |
e6b36c87 JV |
756 | af = lldpd_af_from_lldp_proto(addr_family); |
757 | if (af == LLDPD_AF_UNSPEC) | |
758 | break; | |
759 | if (iface_subtype == LLDP_MGMT_IFACE_IFINDEX) | |
760 | iface = iface_number; | |
761 | else | |
762 | iface = 0; | |
763 | mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface); | |
764 | if (mgmt == NULL) { | |
9221b5c2 VB |
765 | if (errno == ENOMEM) |
766 | log_warn("lldp", "unable to allocate memory " | |
767 | "for management address"); | |
768 | else | |
769 | log_warn("lldp", "too large management address " | |
770 | "received on %s", hardware->h_ifname); | |
ff75b38e | 771 | goto malformed; |
43c02e7b | 772 | } |
e6b36c87 | 773 | TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); |
43c02e7b VB |
774 | break; |
775 | case LLDP_TLV_ORG: | |
dd4f16e7 | 776 | CHECK_TLV_SIZE(1 + (int)sizeof(orgid), "Organisational"); |
a8105c1b VB |
777 | PEEK_BYTES(orgid, sizeof(orgid)); |
778 | tlv_subtype = PEEK_UINT8; | |
779 | if (memcmp(dot1, orgid, sizeof(orgid)) == 0) { | |
a1347cd8 | 780 | #ifndef ENABLE_DOT1 |
a1347cd8 VB |
781 | hardware->h_rx_unrecognized_cnt++; |
782 | #else | |
43c02e7b | 783 | /* Dot1 */ |
a8105c1b | 784 | switch (tlv_subtype) { |
75b3469d | 785 | case LLDP_TLV_DOT1_VLANNAME: |
a8105c1b | 786 | CHECK_TLV_SIZE(7, "VLAN"); |
43c02e7b VB |
787 | if ((vlan = (struct lldpd_vlan *)calloc(1, |
788 | sizeof(struct lldpd_vlan))) == NULL) { | |
6f8925be | 789 | log_warn("lldp", "unable to alloc vlan " |
efe3f9b0 | 790 | "structure for " |
43c02e7b VB |
791 | "tlv received on %s", |
792 | hardware->h_ifname); | |
793 | goto malformed; | |
794 | } | |
a8105c1b VB |
795 | vlan->v_vid = PEEK_UINT16; |
796 | vlan_len = PEEK_UINT8; | |
797 | CHECK_TLV_SIZE(7 + vlan_len, "VLAN"); | |
43c02e7b VB |
798 | if ((vlan->v_name = |
799 | (char *)calloc(1, vlan_len + 1)) == NULL) { | |
6f8925be | 800 | log_warn("lldp", "unable to alloc vlan name for " |
43c02e7b VB |
801 | "tlv received on %s", |
802 | hardware->h_ifname); | |
803 | goto malformed; | |
804 | } | |
a8105c1b | 805 | PEEK_BYTES(vlan->v_name, vlan_len); |
43c02e7b VB |
806 | TAILQ_INSERT_TAIL(&port->p_vlans, |
807 | vlan, v_entries); | |
506273e9 | 808 | vlan = NULL; |
75b3469d VB |
809 | break; |
810 | case LLDP_TLV_DOT1_PVID: | |
a8105c1b VB |
811 | CHECK_TLV_SIZE(6, "PVID"); |
812 | port->p_pvid = PEEK_UINT16; | |
75b3469d | 813 | break; |
9757bfbc SK |
814 | case LLDP_TLV_DOT1_PPVID: |
815 | CHECK_TLV_SIZE(7, "PPVID"); | |
816 | /* validation needed */ | |
817 | /* PPVID has to be unique if more than | |
818 | one PPVID TLVs are received - | |
819 | discard if duplicate */ | |
820 | /* if support bit is not set and | |
821 | enabled bit is set - PPVID TLV is | |
822 | considered error and discarded */ | |
823 | /* if PPVID > 4096 - bad and discard */ | |
824 | if ((ppvid = (struct lldpd_ppvid *)calloc(1, | |
825 | sizeof(struct lldpd_ppvid))) == NULL) { | |
6f8925be | 826 | log_warn("lldp", "unable to alloc ppvid " |
9757bfbc SK |
827 | "structure for " |
828 | "tlv received on %s", | |
829 | hardware->h_ifname); | |
830 | goto malformed; | |
831 | } | |
832 | ppvid->p_cap_status = PEEK_UINT8; | |
833 | ppvid->p_ppvid = PEEK_UINT16; | |
834 | TAILQ_INSERT_TAIL(&port->p_ppvids, | |
835 | ppvid, p_entries); | |
836 | break; | |
837 | case LLDP_TLV_DOT1_PI: | |
838 | /* validation needed */ | |
839 | /* PI has to be unique if more than | |
840 | one PI TLVs are received - discard | |
841 | if duplicate ?? */ | |
842 | CHECK_TLV_SIZE(5, "PI"); | |
843 | if ((pi = (struct lldpd_pi *)calloc(1, | |
844 | sizeof(struct lldpd_pi))) == NULL) { | |
6f8925be | 845 | log_warn("lldp", "unable to alloc PI " |
9757bfbc SK |
846 | "structure for " |
847 | "tlv received on %s", | |
848 | hardware->h_ifname); | |
849 | goto malformed; | |
850 | } | |
48acfcaf | 851 | pi->p_pi_len = PEEK_UINT8; |
78e82fcf | 852 | CHECK_TLV_SIZE(5 + pi->p_pi_len, "PI"); |
9757bfbc | 853 | if ((pi->p_pi = |
48acfcaf | 854 | (char *)calloc(1, pi->p_pi_len)) == NULL) { |
6f8925be | 855 | log_warn("lldp", "unable to alloc pid name for " |
9757bfbc SK |
856 | "tlv received on %s", |
857 | hardware->h_ifname); | |
858 | goto malformed; | |
859 | } | |
48acfcaf | 860 | PEEK_BYTES(pi->p_pi, pi->p_pi_len); |
9757bfbc SK |
861 | TAILQ_INSERT_TAIL(&port->p_pids, |
862 | pi, p_entries); | |
506273e9 | 863 | pi = NULL; |
9757bfbc | 864 | break; |
75b3469d | 865 | default: |
43c02e7b | 866 | /* Unknown Dot1 TLV, ignore it */ |
a1347cd8 VB |
867 | hardware->h_rx_unrecognized_cnt++; |
868 | } | |
869 | #endif | |
a8105c1b | 870 | } else if (memcmp(dot3, orgid, sizeof(orgid)) == 0) { |
a1347cd8 | 871 | #ifndef ENABLE_DOT3 |
a1347cd8 VB |
872 | hardware->h_rx_unrecognized_cnt++; |
873 | #else | |
43c02e7b | 874 | /* Dot3 */ |
a8105c1b | 875 | switch (tlv_subtype) { |
43c02e7b | 876 | case LLDP_TLV_DOT3_MAC: |
a8105c1b | 877 | CHECK_TLV_SIZE(9, "MAC/PHY"); |
3fd015c0 VB |
878 | port->p_macphy.autoneg_support = PEEK_UINT8; |
879 | port->p_macphy.autoneg_enabled = | |
0a36d97b | 880 | (port->p_macphy.autoneg_support & 0x2) >> 1; |
3fd015c0 | 881 | port->p_macphy.autoneg_support = |
befbdf89 | 882 | port->p_macphy.autoneg_support & 0x1; |
3fd015c0 | 883 | port->p_macphy.autoneg_advertised = |
a8105c1b | 884 | PEEK_UINT16; |
3fd015c0 | 885 | port->p_macphy.mau_type = PEEK_UINT16; |
43c02e7b VB |
886 | break; |
887 | case LLDP_TLV_DOT3_LA: | |
a8105c1b | 888 | CHECK_TLV_SIZE(9, "Link aggregation"); |
a204514f | 889 | PEEK_DISCARD_UINT8; |
a8105c1b | 890 | port->p_aggregid = PEEK_UINT32; |
43c02e7b | 891 | break; |
548109b2 | 892 | case LLDP_TLV_DOT3_MFS: |
a8105c1b VB |
893 | CHECK_TLV_SIZE(6, "MFS"); |
894 | port->p_mfs = PEEK_UINT16; | |
548109b2 | 895 | break; |
befbdf89 VB |
896 | case LLDP_TLV_DOT3_POWER: |
897 | CHECK_TLV_SIZE(7, "Power"); | |
898 | port->p_power.devicetype = PEEK_UINT8; | |
899 | port->p_power.supported = | |
0a36d97b | 900 | (port->p_power.devicetype & 0x2) >> 1; |
befbdf89 | 901 | port->p_power.enabled = |
0a36d97b | 902 | (port->p_power.devicetype & 0x4) >> 2; |
befbdf89 | 903 | port->p_power.paircontrol = |
181351d6 | 904 | (port->p_power.devicetype & 0x8) >> 3; |
befbdf89 VB |
905 | port->p_power.devicetype = |
906 | (port->p_power.devicetype & 0x1)? | |
907 | LLDP_DOT3_POWER_PSE:LLDP_DOT3_POWER_PD; | |
908 | port->p_power.pairs = PEEK_UINT8; | |
909 | port->p_power.class = PEEK_UINT8; | |
608cb51c VB |
910 | /* 802.3at? */ |
911 | if (tlv_size >= 12) { | |
912 | port->p_power.powertype = PEEK_UINT8; | |
913 | port->p_power.source = | |
914 | (port->p_power.powertype & (1<<5 | 1<<4)) >> 4; | |
915 | port->p_power.priority = | |
916 | (port->p_power.powertype & (1<<1 | 1<<0)); | |
917 | port->p_power.powertype = | |
918 | (port->p_power.powertype & (1<<7))? | |
919 | LLDP_DOT3_POWER_8023AT_TYPE1: | |
920 | LLDP_DOT3_POWER_8023AT_TYPE2; | |
921 | port->p_power.requested = PEEK_UINT16; | |
922 | port->p_power.allocated = PEEK_UINT16; | |
923 | } else | |
924 | port->p_power.powertype = | |
925 | LLDP_DOT3_POWER_8023AT_OFF; | |
befbdf89 | 926 | break; |
43c02e7b VB |
927 | default: |
928 | /* Unknown Dot3 TLV, ignore it */ | |
37387046 | 929 | hardware->h_rx_unrecognized_cnt++; |
43c02e7b | 930 | } |
a1347cd8 | 931 | #endif |
a8105c1b | 932 | } else if (memcmp(med, orgid, sizeof(orgid)) == 0) { |
6772b237 VB |
933 | /* LLDP-MED */ |
934 | #ifndef ENABLE_LLDPMED | |
6772b237 VB |
935 | hardware->h_rx_unrecognized_cnt++; |
936 | #else | |
e3a44efb | 937 | u_int32_t policy; |
cdece23a VB |
938 | unsigned loctype; |
939 | unsigned power; | |
e3a44efb | 940 | |
a8105c1b | 941 | switch (tlv_subtype) { |
6772b237 | 942 | case LLDP_TLV_MED_CAP: |
a8105c1b VB |
943 | CHECK_TLV_SIZE(7, "LLDP-MED capabilities"); |
944 | chassis->c_med_cap_available = PEEK_UINT16; | |
945 | chassis->c_med_type = PEEK_UINT8; | |
740593ff | 946 | port->p_med_cap_enabled |= |
4b292b55 | 947 | LLDP_MED_CAP_CAP; |
6772b237 | 948 | break; |
efe3f9b0 | 949 | case LLDP_TLV_MED_POLICY: |
a8105c1b VB |
950 | CHECK_TLV_SIZE(8, "LLDP-MED policy"); |
951 | policy = PEEK_UINT32; | |
e3a44efb | 952 | if (((policy >> 24) < 1) || |
4b292b55 | 953 | ((policy >> 24) > LLDP_MED_APPTYPE_LAST)) { |
6f8925be | 954 | log_info("lldp", "unknown policy field %d " |
e3a44efb | 955 | "received on %s", |
e51da5ec | 956 | policy, |
e3a44efb | 957 | hardware->h_ifname); |
e3a44efb VB |
958 | break; |
959 | } | |
740593ff | 960 | port->p_med_policy[(policy >> 24) - 1].type = |
e3a44efb | 961 | (policy >> 24); |
740593ff | 962 | port->p_med_policy[(policy >> 24) - 1].unknown = |
e3a44efb | 963 | ((policy & 0x800000) != 0); |
740593ff | 964 | port->p_med_policy[(policy >> 24) - 1].tagged = |
e3a44efb | 965 | ((policy & 0x400000) != 0); |
740593ff | 966 | port->p_med_policy[(policy >> 24) - 1].vid = |
e3a44efb | 967 | (policy & 0x001FFE00) >> 9; |
740593ff | 968 | port->p_med_policy[(policy >> 24) - 1].priority = |
e3a44efb | 969 | (policy & 0x1C0) >> 6; |
740593ff | 970 | port->p_med_policy[(policy >> 24) - 1].dscp = |
e3a44efb | 971 | policy & 0x3F; |
740593ff | 972 | port->p_med_cap_enabled |= |
4b292b55 | 973 | LLDP_MED_CAP_POLICY; |
efe3f9b0 VB |
974 | break; |
975 | case LLDP_TLV_MED_LOCATION: | |
a8105c1b VB |
976 | CHECK_TLV_SIZE(5, "LLDP-MED Location"); |
977 | loctype = PEEK_UINT8; | |
978 | if ((loctype < 1) || | |
4b292b55 | 979 | (loctype > LLDP_MED_LOCFORMAT_LAST)) { |
6f8925be | 980 | log_info("lldp", "unknown location type " |
e3a44efb VB |
981 | "received on %s", |
982 | hardware->h_ifname); | |
e3a44efb VB |
983 | break; |
984 | } | |
740593ff | 985 | if ((port->p_med_location[loctype - 1].data = |
a8105c1b | 986 | (char*)malloc(tlv_size - 5)) == NULL) { |
6f8925be | 987 | log_warn("lldp", "unable to allocate memory " |
efe3f9b0 VB |
988 | "for LLDP-MED location for " |
989 | "frame received on %s", | |
990 | hardware->h_ifname); | |
991 | goto malformed; | |
992 | } | |
a8105c1b VB |
993 | PEEK_BYTES(port->p_med_location[loctype - 1].data, |
994 | tlv_size - 5); | |
740593ff | 995 | port->p_med_location[loctype - 1].data_len = |
a8105c1b | 996 | tlv_size - 5; |
740593ff | 997 | port->p_med_location[loctype - 1].format = loctype; |
740593ff | 998 | port->p_med_cap_enabled |= |
4b292b55 | 999 | LLDP_MED_CAP_LOCATION; |
efe3f9b0 VB |
1000 | break; |
1001 | case LLDP_TLV_MED_MDI: | |
a8105c1b VB |
1002 | CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI"); |
1003 | power = PEEK_UINT8; | |
1004 | switch (power & 0xC0) { | |
994812b9 | 1005 | case 0x0: |
4b292b55 | 1006 | port->p_med_power.devicetype = LLDP_MED_POW_TYPE_PSE; |
740593ff | 1007 | port->p_med_cap_enabled |= |
4b292b55 | 1008 | LLDP_MED_CAP_MDI_PSE; |
a8105c1b | 1009 | switch (power & 0x30) { |
994812b9 | 1010 | case 0x0: |
6d08df0e | 1011 | port->p_med_power.source = |
4b292b55 | 1012 | LLDP_MED_POW_SOURCE_UNKNOWN; |
994812b9 VB |
1013 | break; |
1014 | case 0x10: | |
6d08df0e | 1015 | port->p_med_power.source = |
4b292b55 | 1016 | LLDP_MED_POW_SOURCE_PRIMARY; |
994812b9 VB |
1017 | break; |
1018 | case 0x20: | |
6d08df0e | 1019 | port->p_med_power.source = |
4b292b55 | 1020 | LLDP_MED_POW_SOURCE_BACKUP; |
994812b9 VB |
1021 | break; |
1022 | default: | |
6d08df0e | 1023 | port->p_med_power.source = |
4b292b55 | 1024 | LLDP_MED_POW_SOURCE_RESERVED; |
994812b9 VB |
1025 | } |
1026 | break; | |
1027 | case 0x40: | |
4b292b55 | 1028 | port->p_med_power.devicetype = LLDP_MED_POW_TYPE_PD; |
740593ff | 1029 | port->p_med_cap_enabled |= |
4b292b55 | 1030 | LLDP_MED_CAP_MDI_PD; |
a8105c1b | 1031 | switch (power & 0x30) { |
994812b9 | 1032 | case 0x0: |
6d08df0e | 1033 | port->p_med_power.source = |
4b292b55 | 1034 | LLDP_MED_POW_SOURCE_UNKNOWN; |
994812b9 VB |
1035 | break; |
1036 | case 0x10: | |
6d08df0e | 1037 | port->p_med_power.source = |
4b292b55 | 1038 | LLDP_MED_POW_SOURCE_PSE; |
994812b9 VB |
1039 | break; |
1040 | case 0x20: | |
6d08df0e | 1041 | port->p_med_power.source = |
4b292b55 | 1042 | LLDP_MED_POW_SOURCE_LOCAL; |
994812b9 VB |
1043 | break; |
1044 | default: | |
6d08df0e | 1045 | port->p_med_power.source = |
4b292b55 | 1046 | LLDP_MED_POW_SOURCE_BOTH; |
994812b9 VB |
1047 | } |
1048 | break; | |
1049 | default: | |
6d08df0e | 1050 | port->p_med_power.devicetype = |
4b292b55 | 1051 | LLDP_MED_POW_TYPE_RESERVED; |
994812b9 | 1052 | } |
cdece23a | 1053 | if ((power & 0x0F) > LLDP_MED_POW_PRIO_LOW) |
6d08df0e | 1054 | port->p_med_power.priority = |
4b292b55 | 1055 | LLDP_MED_POW_PRIO_UNKNOWN; |
009ddd23 | 1056 | else |
6d08df0e | 1057 | port->p_med_power.priority = |
009ddd23 | 1058 | power & 0x0F; |
6d08df0e | 1059 | port->p_med_power.val = PEEK_UINT16; |
efe3f9b0 | 1060 | break; |
6772b237 VB |
1061 | case LLDP_TLV_MED_IV_HW: |
1062 | case LLDP_TLV_MED_IV_SW: | |
1063 | case LLDP_TLV_MED_IV_FW: | |
1064 | case LLDP_TLV_MED_IV_SN: | |
1065 | case LLDP_TLV_MED_IV_MANUF: | |
1066 | case LLDP_TLV_MED_IV_MODEL: | |
1067 | case LLDP_TLV_MED_IV_ASSET: | |
a8105c1b | 1068 | if (tlv_size <= 4) |
5ecdbc53 | 1069 | b = NULL; |
6772b237 | 1070 | else { |
a8105c1b | 1071 | if ((b = (char*)malloc(tlv_size - 3)) == |
efe3f9b0 | 1072 | NULL) { |
6f8925be | 1073 | log_warn("lldp", "unable to allocate " |
efe3f9b0 VB |
1074 | "memory for LLDP-MED " |
1075 | "inventory for frame " | |
1076 | "received on %s", | |
1077 | hardware->h_ifname); | |
1078 | goto malformed; | |
1079 | } | |
a8105c1b VB |
1080 | PEEK_BYTES(b, tlv_size - 4); |
1081 | b[tlv_size - 4] = '\0'; | |
6772b237 | 1082 | } |
a8105c1b | 1083 | switch (tlv_subtype) { |
6772b237 VB |
1084 | case LLDP_TLV_MED_IV_HW: |
1085 | chassis->c_med_hw = b; | |
1086 | break; | |
1087 | case LLDP_TLV_MED_IV_FW: | |
1088 | chassis->c_med_fw = b; | |
1089 | break; | |
1090 | case LLDP_TLV_MED_IV_SW: | |
1091 | chassis->c_med_sw = b; | |
1092 | break; | |
1093 | case LLDP_TLV_MED_IV_SN: | |
1094 | chassis->c_med_sn = b; | |
1095 | break; | |
1096 | case LLDP_TLV_MED_IV_MANUF: | |
1097 | chassis->c_med_manuf = b; | |
1098 | break; | |
1099 | case LLDP_TLV_MED_IV_MODEL: | |
397dee69 | 1100 | chassis->c_med_model = b; |
6772b237 VB |
1101 | break; |
1102 | case LLDP_TLV_MED_IV_ASSET: | |
72c4c96f | 1103 | chassis->c_med_asset = b; |
6772b237 | 1104 | break; |
6772b237 | 1105 | } |
740593ff | 1106 | port->p_med_cap_enabled |= |
4b292b55 | 1107 | LLDP_MED_CAP_IV; |
6772b237 VB |
1108 | break; |
1109 | default: | |
1110 | /* Unknown LLDP MED, ignore it */ | |
6772b237 VB |
1111 | hardware->h_rx_unrecognized_cnt++; |
1112 | } | |
1113 | #endif /* ENABLE_LLDPMED */ | |
5317a14a ST |
1114 | } else if (memcmp(dcbx, orgid, sizeof(orgid)) == 0) { |
1115 | log_debug("lldp", "unsupported DCBX tlv received on %s - ignore", | |
1116 | hardware->h_ifname); | |
1117 | hardware->h_rx_unrecognized_cnt++; | |
43c02e7b | 1118 | } else { |
9c242613 | 1119 | log_debug("lldp", "unknown org tlv [%02x:%02x:%02x] received on %s", |
5317a14a | 1120 | orgid[0], orgid[1], orgid[2], |
43c02e7b | 1121 | hardware->h_ifname); |
37387046 | 1122 | hardware->h_rx_unrecognized_cnt++; |
fb1b78bb | 1123 | #ifdef ENABLE_CUSTOM |
cd5de7a2 | 1124 | custom = (struct lldpd_custom*)calloc(1, sizeof(struct lldpd_custom)); |
1cce9f1a VB |
1125 | if (!custom) { |
1126 | log_warn("lldp", | |
1127 | "unable to allocate memory for custom TLV"); | |
1128 | goto malformed; | |
1129 | } | |
cd5de7a2 AA |
1130 | custom->oui_info_len = tlv_size > 4 ? tlv_size - 4 : 0; |
1131 | memcpy(custom->oui, orgid, sizeof(custom->oui)); | |
1132 | custom->subtype = tlv_subtype; | |
8caf4341 AA |
1133 | if (custom->oui_info_len > 0) { |
1134 | custom->oui_info = malloc(custom->oui_info_len); | |
1cce9f1a VB |
1135 | if (!custom->oui_info) { |
1136 | log_warn("lldp", | |
1137 | "unable to allocate memory for custom TLV data"); | |
1138 | goto malformed; | |
1139 | } | |
cd5de7a2 | 1140 | PEEK_BYTES(custom->oui_info, custom->oui_info_len); |
8caf4341 | 1141 | } |
cd5de7a2 | 1142 | TAILQ_INSERT_TAIL(&port->p_custom_list, custom, next); |
1cce9f1a | 1143 | custom = NULL; |
fb1b78bb | 1144 | #endif |
43c02e7b VB |
1145 | } |
1146 | break; | |
1147 | default: | |
6f8925be | 1148 | log_warnx("lldp", "unknown tlv (%d) received on %s", |
a8105c1b VB |
1149 | tlv_type, hardware->h_ifname); |
1150 | goto malformed; | |
1151 | } | |
1152 | if (pos > tlv + tlv_size) { | |
6f8925be | 1153 | log_warnx("lldp", "BUG: already past TLV!"); |
43c02e7b VB |
1154 | goto malformed; |
1155 | } | |
a8105c1b | 1156 | PEEK_DISCARD(tlv + tlv_size - pos); |
43c02e7b VB |
1157 | } |
1158 | ||
1159 | /* Some random check */ | |
1160 | if ((chassis->c_id == NULL) || | |
1161 | (port->p_id == NULL) || | |
5a215d4b | 1162 | (!ttl_received) || |
43c02e7b | 1163 | (gotend == 0)) { |
6f8925be | 1164 | log_warnx("lldp", "some mandatory tlv are missing for frame received on %s", |
43c02e7b VB |
1165 | hardware->h_ifname); |
1166 | goto malformed; | |
1167 | } | |
43c02e7b VB |
1168 | *newchassis = chassis; |
1169 | *newport = port; | |
1170 | return 1; | |
1171 | malformed: | |
1cce9f1a VB |
1172 | #ifdef ENABLE_CUSTOM |
1173 | free(custom); | |
1174 | #endif | |
506273e9 VB |
1175 | #ifdef ENABLE_DOT1 |
1176 | free(vlan); | |
1177 | free(pi); | |
1178 | #endif | |
77507b69 | 1179 | lldpd_chassis_cleanup(chassis, 1); |
4b292b55 | 1180 | lldpd_port_cleanup(port, 1); |
4e624dc2 | 1181 | free(port); |
43c02e7b VB |
1182 | return -1; |
1183 | } |