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