]>
Commit | Line | Data |
---|---|---|
43c02e7b VB |
1 | /* |
2 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
3 | * | |
4 | * Permission to use, copy, modify, and distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
17 | #include "lldpd.h" | |
a8105c1b | 18 | #include "frame.h" |
43c02e7b VB |
19 | |
20 | #include <unistd.h> | |
21 | #include <errno.h> | |
22 | #include <time.h> | |
23 | #include <sys/types.h> | |
24 | #include <sys/socket.h> | |
25 | #include <sys/ioctl.h> | |
26 | #include <netpacket/packet.h> | |
27 | #include <linux/sockios.h> | |
28 | ||
29 | int | |
30 | lldp_send(struct lldpd *global, struct lldpd_chassis *chassis, | |
a8105c1b | 31 | struct lldpd_hardware *hardware) |
43c02e7b | 32 | { |
a8105c1b VB |
33 | struct lldpd_port *port; |
34 | struct lldpd_frame *frame; | |
35 | int length; | |
36 | u_int8_t *packet, *pos, *tlv; | |
37 | ||
38 | u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR; | |
a1347cd8 VB |
39 | #ifdef ENABLE_DOT1 |
40 | const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1; | |
a1347cd8 VB |
41 | struct lldpd_vlan *vlan; |
42 | #endif | |
43 | #ifdef ENABLE_DOT3 | |
44 | const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3; | |
a1347cd8 | 45 | #endif |
89840df0 | 46 | #ifdef ENABLE_LLDPMED |
115ff55c | 47 | int i; |
89840df0 | 48 | const u_int8_t med[] = LLDP_TLV_ORG_MED; |
89840df0 | 49 | #endif |
43c02e7b | 50 | |
a8105c1b VB |
51 | port = &hardware->h_lport; |
52 | length = hardware->h_mtu; | |
53 | if ((packet = (u_int8_t*)malloc(length)) == NULL) | |
54 | return ENOMEM; | |
55 | memset(packet, 0, length); | |
56 | pos = packet; | |
43c02e7b VB |
57 | |
58 | /* Ethernet header */ | |
a8105c1b VB |
59 | if (!( |
60 | /* LLDP multicast address */ | |
61 | POKE_BYTES(mcastaddr, sizeof(mcastaddr)) && | |
62 | /* Source MAC address */ | |
63 | POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr)) && | |
64 | /* LLDP frame */ | |
65 | POKE_UINT16(ETHERTYPE_LLDP))) | |
66 | goto toobig; | |
43c02e7b VB |
67 | |
68 | /* Chassis ID */ | |
a8105c1b VB |
69 | if (!( |
70 | POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) && | |
71 | POKE_UINT8(chassis->c_id_subtype) && | |
72 | POKE_BYTES(chassis->c_id, chassis->c_id_len) && | |
73 | POKE_END_LLDP_TLV)) | |
74 | goto toobig; | |
43c02e7b VB |
75 | |
76 | /* Port ID */ | |
a8105c1b VB |
77 | if (!( |
78 | POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) && | |
79 | POKE_UINT8(port->p_id_subtype) && | |
80 | POKE_BYTES(port->p_id, port->p_id_len) && | |
81 | POKE_END_LLDP_TLV)) | |
82 | goto toobig; | |
43c02e7b VB |
83 | |
84 | /* Time to live */ | |
a8105c1b VB |
85 | if (!( |
86 | POKE_START_LLDP_TLV(LLDP_TLV_TTL) && | |
87 | POKE_UINT16(chassis->c_ttl) && | |
88 | POKE_END_LLDP_TLV)) | |
89 | goto toobig; | |
43c02e7b VB |
90 | |
91 | /* System name */ | |
a8105c1b VB |
92 | if (!( |
93 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_NAME) && | |
94 | POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) && | |
95 | POKE_END_LLDP_TLV)) | |
96 | goto toobig; | |
43c02e7b VB |
97 | |
98 | /* System description */ | |
a8105c1b VB |
99 | if (!( |
100 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_DESCR) && | |
101 | POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) && | |
102 | POKE_END_LLDP_TLV)) | |
103 | goto toobig; | |
43c02e7b VB |
104 | |
105 | /* System capabilities */ | |
a8105c1b VB |
106 | if (!( |
107 | POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_CAP) && | |
108 | POKE_UINT16(chassis->c_cap_available) && | |
109 | POKE_UINT16(chassis->c_cap_enabled) && | |
110 | POKE_END_LLDP_TLV)) | |
111 | goto toobig; | |
43c02e7b VB |
112 | |
113 | if (chassis->c_mgmt.s_addr != INADDR_ANY) { | |
114 | /* Management address */ | |
a8105c1b VB |
115 | if (!( |
116 | POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) && | |
117 | /* Size of the address, including its type */ | |
118 | POKE_UINT8(sizeof(struct in_addr) + 1) && | |
119 | /* Address is IPv4 */ | |
120 | POKE_UINT8(LLDP_MGMT_ADDR_IP4) && | |
121 | POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)))) | |
122 | goto toobig; | |
43c02e7b VB |
123 | |
124 | /* Interface port type, OID */ | |
a8105c1b VB |
125 | if (chassis->c_mgmt_if == 0) { |
126 | if (!( | |
127 | /* We don't know the management interface */ | |
128 | POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) && | |
129 | POKE_UINT32(0))) | |
130 | goto toobig; | |
131 | } else { | |
132 | if (!( | |
133 | /* We have the index of the management interface */ | |
134 | POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) && | |
135 | POKE_UINT32(chassis->c_mgmt_if))) | |
136 | goto toobig; | |
43c02e7b | 137 | } |
a8105c1b VB |
138 | if (!( |
139 | /* We don't provide an OID for management */ | |
140 | POKE_UINT8(0) && | |
141 | POKE_END_LLDP_TLV)) | |
142 | goto toobig; | |
43c02e7b VB |
143 | } |
144 | ||
145 | /* Port description */ | |
a8105c1b VB |
146 | if (!( |
147 | POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) && | |
148 | POKE_BYTES(port->p_descr, strlen(port->p_descr)) && | |
149 | POKE_END_LLDP_TLV)) | |
150 | goto toobig; | |
43c02e7b | 151 | |
a1347cd8 | 152 | #ifdef ENABLE_DOT1 |
43c02e7b | 153 | /* VLANs */ |
a8105c1b VB |
154 | TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) { |
155 | if (!( | |
156 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
157 | POKE_BYTES(dot1, sizeof(dot1)) && | |
158 | POKE_UINT8(LLDP_TLV_DOT1_VLANNAME) && | |
159 | POKE_UINT16(vlan->v_vid) && | |
160 | POKE_UINT8(strlen(vlan->v_name)) && | |
161 | POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) && | |
162 | POKE_END_LLDP_TLV)) | |
163 | goto toobig; | |
43c02e7b | 164 | } |
a1347cd8 | 165 | #endif |
43c02e7b | 166 | |
a1347cd8 | 167 | #ifdef ENABLE_DOT3 |
43c02e7b | 168 | /* Aggregation status */ |
a8105c1b VB |
169 | if (!( |
170 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
171 | POKE_BYTES(dot3, sizeof(dot3)) && | |
172 | POKE_UINT8(LLDP_TLV_DOT3_LA) && | |
173 | /* Bit 0 = capability ; Bit 1 = status */ | |
174 | POKE_UINT8((port->p_aggregid) ? 3:1) && | |
175 | POKE_UINT32(port->p_aggregid) && | |
176 | POKE_END_LLDP_TLV)) | |
177 | goto toobig; | |
43c02e7b VB |
178 | |
179 | /* MAC/PHY */ | |
a8105c1b VB |
180 | if (!( |
181 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
182 | POKE_BYTES(dot3, sizeof(dot3)) && | |
183 | POKE_UINT8(LLDP_TLV_DOT3_MAC) && | |
184 | POKE_UINT8(port->p_autoneg_support | | |
185 | (port->p_autoneg_enabled << 1)) && | |
186 | POKE_UINT16(port->p_autoneg_advertised) && | |
187 | POKE_UINT16(port->p_mau_type) && | |
188 | POKE_END_LLDP_TLV)) | |
189 | goto toobig; | |
548109b2 VB |
190 | |
191 | /* MFS */ | |
a8105c1b VB |
192 | if (!( |
193 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
194 | POKE_BYTES(dot3, sizeof(dot3)) && | |
195 | POKE_UINT8(LLDP_TLV_DOT3_MFS) && | |
196 | POKE_UINT16(port->p_mfs) && | |
197 | POKE_END_LLDP_TLV)) | |
198 | goto toobig; | |
a1347cd8 | 199 | #endif |
43c02e7b | 200 | |
89840df0 | 201 | #ifdef ENABLE_LLDPMED |
740593ff | 202 | if (port->p_med_cap_enabled) { |
89840df0 | 203 | /* LLDP-MED cap */ |
a8105c1b VB |
204 | if (!( |
205 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
206 | POKE_BYTES(med, sizeof(med)) && | |
207 | POKE_UINT8(LLDP_TLV_MED_CAP) && | |
208 | POKE_UINT16(chassis->c_med_cap_available) && | |
bc08499a | 209 | POKE_UINT8(chassis->c_med_type) && |
a8105c1b VB |
210 | POKE_END_LLDP_TLV)) |
211 | goto toobig; | |
89840df0 VB |
212 | |
213 | /* LLDP-MED inventory */ | |
a8105c1b | 214 | #define LLDP_INVENTORY(value, subtype) \ |
89840df0 | 215 | if (value) { \ |
a8105c1b VB |
216 | if (!( \ |
217 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && \ | |
218 | POKE_BYTES(med, sizeof(med)) && \ | |
219 | POKE_UINT8(subtype) && \ | |
220 | POKE_BYTES(value, \ | |
221 | (strlen(value)>32)?32:strlen(value)) && \ | |
222 | POKE_END_LLDP_TLV)) \ | |
223 | goto toobig; \ | |
89840df0 | 224 | } |
e809a587 | 225 | |
740593ff | 226 | if (port->p_med_cap_enabled & LLDPMED_CAP_IV) { |
a8105c1b VB |
227 | LLDP_INVENTORY(chassis->c_med_hw, |
228 | LLDP_TLV_MED_IV_HW); | |
229 | LLDP_INVENTORY(chassis->c_med_fw, | |
230 | LLDP_TLV_MED_IV_FW); | |
231 | LLDP_INVENTORY(chassis->c_med_sw, | |
232 | LLDP_TLV_MED_IV_SW); | |
233 | LLDP_INVENTORY(chassis->c_med_sn, | |
234 | LLDP_TLV_MED_IV_SN); | |
235 | LLDP_INVENTORY(chassis->c_med_manuf, | |
236 | LLDP_TLV_MED_IV_MANUF); | |
237 | LLDP_INVENTORY(chassis->c_med_model, | |
238 | LLDP_TLV_MED_IV_MODEL); | |
239 | LLDP_INVENTORY(chassis->c_med_asset, | |
240 | LLDP_TLV_MED_IV_ASSET); | |
e809a587 | 241 | } |
115ff55c VB |
242 | |
243 | /* LLDP-MED location */ | |
244 | for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) { | |
740593ff | 245 | if (port->p_med_location[i].format == i + 1) { |
a8105c1b VB |
246 | if (!( |
247 | POKE_START_LLDP_TLV(LLDP_TLV_ORG) && | |
248 | POKE_BYTES(med, sizeof(med)) && | |
249 | POKE_UINT8(LLDP_TLV_MED_LOCATION) && | |
250 | POKE_UINT8(port->p_med_location[i].format) && | |
251 | POKE_BYTES(port->p_med_location[i].data, | |
252 | port->p_med_location[i].data_len) && | |
253 | POKE_END_LLDP_TLV)) | |
254 | goto toobig; | |
115ff55c VB |
255 | } |
256 | } | |
89840df0 VB |
257 | } |
258 | #endif | |
259 | ||
43c02e7b | 260 | /* END */ |
a8105c1b VB |
261 | if (!( |
262 | POKE_START_LLDP_TLV(LLDP_TLV_END) && | |
263 | POKE_END_LLDP_TLV)) | |
264 | goto toobig; | |
43c02e7b | 265 | |
f2dcb180 VB |
266 | if (write(hardware->h_raw, packet, |
267 | pos - packet) == -1) { | |
268 | LLOG_WARN("unable to send packet on real device for %s", | |
269 | hardware->h_ifname); | |
270 | free(packet); | |
271 | return ENETDOWN; | |
43c02e7b VB |
272 | } |
273 | ||
f2dcb180 VB |
274 | hardware->h_tx_cnt++; |
275 | ||
a8105c1b VB |
276 | /* We assume that LLDP frame is the reference */ |
277 | if ((frame = (struct lldpd_frame*)malloc( | |
278 | sizeof(int) + pos - packet)) != NULL) { | |
279 | frame->size = pos - packet; | |
280 | memcpy(&frame->frame, packet, frame->size); | |
43c02e7b | 281 | if ((hardware->h_llastframe == NULL) || |
a8105c1b VB |
282 | (hardware->h_llastframe->size != frame->size) || |
283 | (memcmp(hardware->h_llastframe->frame, frame->frame, | |
284 | frame->size) != 0)) { | |
43c02e7b | 285 | free(hardware->h_llastframe); |
a8105c1b VB |
286 | hardware->h_llastframe = frame; |
287 | hardware->h_llastchange = time(NULL); | |
43c02e7b | 288 | } else |
a8105c1b | 289 | free(frame); |
43c02e7b VB |
290 | } |
291 | ||
a8105c1b | 292 | free(packet); |
43c02e7b | 293 | return 0; |
a8105c1b VB |
294 | |
295 | toobig: | |
296 | free(packet); | |
297 | return E2BIG; | |
43c02e7b VB |
298 | } |
299 | ||
a8105c1b VB |
300 | #define CHECK_TLV_SIZE(x, name) \ |
301 | do { if (tlv_size < (x)) { \ | |
302 | LLOG_WARNX(name " TLV too short received on %s",\ | |
303 | hardware->h_ifname); \ | |
304 | goto malformed; \ | |
305 | } } while (0) | |
306 | ||
43c02e7b VB |
307 | int |
308 | lldp_decode(struct lldpd *cfg, char *frame, int s, | |
309 | struct lldpd_hardware *hardware, | |
310 | struct lldpd_chassis **newchassis, struct lldpd_port **newport) | |
311 | { | |
312 | struct lldpd_chassis *chassis; | |
313 | struct lldpd_port *port; | |
43c02e7b VB |
314 | const char lldpaddr[] = LLDP_MULTICAST_ADDR; |
315 | const char dot1[] = LLDP_TLV_ORG_DOT1; | |
316 | const char dot3[] = LLDP_TLV_ORG_DOT3; | |
6772b237 | 317 | const char med[] = LLDP_TLV_ORG_MED; |
a8105c1b VB |
318 | char orgid[3]; |
319 | int length, gotend = 0; | |
320 | int tlv_size, tlv_type, tlv_subtype; | |
321 | u_int8_t *pos, *tlv; | |
43c02e7b | 322 | char *b; |
a8105c1b | 323 | #ifdef ENABLE_DOT1 |
75b3469d VB |
324 | struct lldpd_vlan *vlan; |
325 | int vlan_len; | |
a8105c1b | 326 | #endif |
43c02e7b VB |
327 | |
328 | if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { | |
329 | LLOG_WARN("failed to allocate remote chassis"); | |
330 | return -1; | |
331 | } | |
332 | if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { | |
333 | LLOG_WARN("failed to allocate remote port"); | |
334 | free(chassis); | |
335 | return -1; | |
336 | } | |
a1347cd8 | 337 | #ifdef ENABLE_DOT1 |
43c02e7b | 338 | TAILQ_INIT(&port->p_vlans); |
a1347cd8 | 339 | #endif |
43c02e7b | 340 | |
a8105c1b VB |
341 | length = s; |
342 | pos = (u_int8_t*)frame; | |
343 | ||
344 | if (length < 2*ETH_ALEN + sizeof(u_int16_t)) { | |
43c02e7b VB |
345 | LLOG_WARNX("too short frame received on %s", hardware->h_ifname); |
346 | goto malformed; | |
347 | } | |
a8105c1b | 348 | if (PEEK_CMP(lldpaddr, ETH_ALEN) != 0) { |
43c02e7b VB |
349 | LLOG_INFO("frame not targeted at LLDP multicast address received on %s", |
350 | hardware->h_ifname); | |
351 | goto malformed; | |
352 | } | |
a8105c1b VB |
353 | PEEK_DISCARD(ETH_ALEN); /* Skip source address */ |
354 | if (PEEK_UINT16 != ETHERTYPE_LLDP) { | |
43c02e7b VB |
355 | LLOG_INFO("non LLDP frame received on %s", |
356 | hardware->h_ifname); | |
357 | goto malformed; | |
358 | } | |
359 | ||
a8105c1b VB |
360 | while (length && (!gotend)) { |
361 | if (length < 2) { | |
43c02e7b VB |
362 | LLOG_WARNX("tlv header too short received on %s", |
363 | hardware->h_ifname); | |
364 | goto malformed; | |
365 | } | |
a8105c1b VB |
366 | tlv_size = PEEK_UINT16; |
367 | tlv_type = tlv_size >> 9; | |
368 | tlv_size = tlv_size & 0x1ff; | |
369 | PEEK_SAVE(tlv); | |
370 | if (length < tlv_size) { | |
371 | LLOG_WARNX("frame too short for tlv received on %s", | |
43c02e7b VB |
372 | hardware->h_ifname); |
373 | goto malformed; | |
374 | } | |
a8105c1b | 375 | switch (tlv_type) { |
43c02e7b | 376 | case LLDP_TLV_END: |
a8105c1b | 377 | if (tlv_size != 0) { |
43c02e7b VB |
378 | LLOG_WARNX("lldp end received with size not null on %s", |
379 | hardware->h_ifname); | |
380 | goto malformed; | |
381 | } | |
a8105c1b | 382 | if (length) |
43c02e7b VB |
383 | LLOG_DEBUG("extra data after lldp end on %s", |
384 | hardware->h_ifname); | |
385 | gotend = 1; | |
386 | break; | |
387 | case LLDP_TLV_CHASSIS_ID: | |
388 | case LLDP_TLV_PORT_ID: | |
a8105c1b VB |
389 | CHECK_TLV_SIZE(2, "Port Id"); |
390 | tlv_subtype = PEEK_UINT8; | |
391 | if ((tlv_subtype == 0) || (tlv_subtype > 7)) { | |
43c02e7b VB |
392 | LLOG_WARNX("unknown subtype for tlv id received on %s", |
393 | hardware->h_ifname); | |
394 | goto malformed; | |
395 | } | |
a8105c1b | 396 | if ((b = (char *)calloc(1, tlv_size - 1)) == NULL) { |
43c02e7b VB |
397 | LLOG_WARN("unable to allocate memory for id tlv " |
398 | "received on %s", | |
399 | hardware->h_ifname); | |
400 | goto malformed; | |
401 | } | |
a8105c1b VB |
402 | PEEK_BYTES(b, tlv_size - 1); |
403 | if (tlv_type == LLDP_TLV_PORT_ID) { | |
404 | port->p_id_subtype = tlv_subtype; | |
43c02e7b | 405 | port->p_id = b; |
a8105c1b | 406 | port->p_id_len = tlv_size - 1; |
43c02e7b | 407 | } else { |
a8105c1b | 408 | chassis->c_id_subtype = tlv_subtype; |
43c02e7b | 409 | chassis->c_id = b; |
a8105c1b | 410 | chassis->c_id_len = tlv_size - 1; |
43c02e7b | 411 | } |
43c02e7b VB |
412 | break; |
413 | case LLDP_TLV_TTL: | |
a8105c1b VB |
414 | CHECK_TLV_SIZE(2, "TTL"); |
415 | chassis->c_ttl = PEEK_UINT16; | |
43c02e7b VB |
416 | break; |
417 | case LLDP_TLV_PORT_DESCR: | |
418 | case LLDP_TLV_SYSTEM_NAME: | |
419 | case LLDP_TLV_SYSTEM_DESCR: | |
a8105c1b | 420 | if (tlv_size < 1) { |
a700e935 | 421 | LLOG_DEBUG("empty tlv received on %s", |
43c02e7b | 422 | hardware->h_ifname); |
a700e935 | 423 | break; |
43c02e7b | 424 | } |
a8105c1b | 425 | if ((b = (char *)calloc(1, tlv_size + 1)) == NULL) { |
43c02e7b VB |
426 | LLOG_WARN("unable to allocate memory for string tlv " |
427 | "received on %s", | |
428 | hardware->h_ifname); | |
429 | goto malformed; | |
430 | } | |
a8105c1b VB |
431 | PEEK_BYTES(b, tlv_size); |
432 | if (tlv_type == LLDP_TLV_PORT_DESCR) | |
43c02e7b | 433 | port->p_descr = b; |
a8105c1b | 434 | else if (tlv_type == LLDP_TLV_SYSTEM_NAME) |
43c02e7b VB |
435 | chassis->c_name = b; |
436 | else chassis->c_descr = b; | |
437 | break; | |
438 | case LLDP_TLV_SYSTEM_CAP: | |
a8105c1b VB |
439 | CHECK_TLV_SIZE(4, "System capabilities"); |
440 | chassis->c_cap_available = PEEK_UINT16; | |
441 | chassis->c_cap_enabled = PEEK_UINT16; | |
43c02e7b VB |
442 | break; |
443 | case LLDP_TLV_MGMT_ADDR: | |
a8105c1b | 444 | CHECK_TLV_SIZE(11, "Management address"); |
43c02e7b | 445 | if ((chassis->c_mgmt.s_addr == INADDR_ANY) && |
a8105c1b VB |
446 | (PEEK_UINT8 == 1+sizeof(struct in_addr)) && |
447 | (PEEK_UINT8 == LLDP_MGMT_ADDR_IP4)) { | |
43c02e7b | 448 | /* We have an IPv4 address, we ignore anything else */ |
a8105c1b | 449 | PEEK_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)); |
43c02e7b VB |
450 | chassis->c_mgmt_if = 0; |
451 | /* We only handle ifIndex subtype */ | |
a8105c1b VB |
452 | if (PEEK_UINT8 == LLDP_MGMT_IFACE_IFINDEX) |
453 | chassis->c_mgmt_if = PEEK_UINT32; | |
43c02e7b | 454 | } |
43c02e7b VB |
455 | break; |
456 | case LLDP_TLV_ORG: | |
a8105c1b VB |
457 | CHECK_TLV_SIZE(4, "Organisational"); |
458 | PEEK_BYTES(orgid, sizeof(orgid)); | |
459 | tlv_subtype = PEEK_UINT8; | |
460 | if (memcmp(dot1, orgid, sizeof(orgid)) == 0) { | |
a1347cd8 | 461 | #ifndef ENABLE_DOT1 |
a1347cd8 VB |
462 | hardware->h_rx_unrecognized_cnt++; |
463 | #else | |
43c02e7b | 464 | /* Dot1 */ |
a8105c1b | 465 | switch (tlv_subtype) { |
75b3469d | 466 | case LLDP_TLV_DOT1_VLANNAME: |
a8105c1b | 467 | CHECK_TLV_SIZE(7, "VLAN"); |
43c02e7b VB |
468 | if ((vlan = (struct lldpd_vlan *)calloc(1, |
469 | sizeof(struct lldpd_vlan))) == NULL) { | |
efe3f9b0 VB |
470 | LLOG_WARN("unable to alloc vlan " |
471 | "structure for " | |
43c02e7b VB |
472 | "tlv received on %s", |
473 | hardware->h_ifname); | |
474 | goto malformed; | |
475 | } | |
a8105c1b VB |
476 | vlan->v_vid = PEEK_UINT16; |
477 | vlan_len = PEEK_UINT8; | |
478 | CHECK_TLV_SIZE(7 + vlan_len, "VLAN"); | |
43c02e7b VB |
479 | if ((vlan->v_name = |
480 | (char *)calloc(1, vlan_len + 1)) == NULL) { | |
481 | LLOG_WARN("unable to alloc vlan name for " | |
482 | "tlv received on %s", | |
483 | hardware->h_ifname); | |
484 | goto malformed; | |
485 | } | |
a8105c1b | 486 | PEEK_BYTES(vlan->v_name, vlan_len); |
43c02e7b VB |
487 | TAILQ_INSERT_TAIL(&port->p_vlans, |
488 | vlan, v_entries); | |
75b3469d VB |
489 | break; |
490 | case LLDP_TLV_DOT1_PVID: | |
a8105c1b VB |
491 | CHECK_TLV_SIZE(6, "PVID"); |
492 | port->p_pvid = PEEK_UINT16; | |
75b3469d VB |
493 | break; |
494 | default: | |
43c02e7b | 495 | /* Unknown Dot1 TLV, ignore it */ |
a1347cd8 VB |
496 | hardware->h_rx_unrecognized_cnt++; |
497 | } | |
498 | #endif | |
a8105c1b | 499 | } else if (memcmp(dot3, orgid, sizeof(orgid)) == 0) { |
a1347cd8 | 500 | #ifndef ENABLE_DOT3 |
a1347cd8 VB |
501 | hardware->h_rx_unrecognized_cnt++; |
502 | #else | |
43c02e7b | 503 | /* Dot3 */ |
a8105c1b | 504 | switch (tlv_subtype) { |
43c02e7b | 505 | case LLDP_TLV_DOT3_MAC: |
a8105c1b VB |
506 | CHECK_TLV_SIZE(9, "MAC/PHY"); |
507 | port->p_autoneg_support = PEEK_UINT8; | |
43c02e7b | 508 | port->p_autoneg_enabled = |
a8105c1b VB |
509 | port->p_autoneg_support && 0x2; |
510 | port->p_autoneg_support = | |
511 | port->p_autoneg_support && 0x1; | |
43c02e7b | 512 | port->p_autoneg_advertised = |
a8105c1b VB |
513 | PEEK_UINT16; |
514 | port->p_mau_type = PEEK_UINT16; | |
43c02e7b VB |
515 | break; |
516 | case LLDP_TLV_DOT3_LA: | |
a8105c1b | 517 | CHECK_TLV_SIZE(9, "Link aggregation"); |
a204514f | 518 | PEEK_DISCARD_UINT8; |
a8105c1b | 519 | port->p_aggregid = PEEK_UINT32; |
43c02e7b | 520 | break; |
548109b2 | 521 | case LLDP_TLV_DOT3_MFS: |
a8105c1b VB |
522 | CHECK_TLV_SIZE(6, "MFS"); |
523 | port->p_mfs = PEEK_UINT16; | |
548109b2 | 524 | break; |
43c02e7b VB |
525 | default: |
526 | /* Unknown Dot3 TLV, ignore it */ | |
37387046 | 527 | hardware->h_rx_unrecognized_cnt++; |
43c02e7b | 528 | } |
a1347cd8 | 529 | #endif |
a8105c1b | 530 | } else if (memcmp(med, orgid, sizeof(orgid)) == 0) { |
6772b237 VB |
531 | /* LLDP-MED */ |
532 | #ifndef ENABLE_LLDPMED | |
6772b237 VB |
533 | hardware->h_rx_unrecognized_cnt++; |
534 | #else | |
e3a44efb VB |
535 | u_int32_t policy; |
536 | int loctype; | |
a8105c1b | 537 | int power; |
e3a44efb | 538 | |
a8105c1b | 539 | switch (tlv_subtype) { |
6772b237 | 540 | case LLDP_TLV_MED_CAP: |
a8105c1b VB |
541 | CHECK_TLV_SIZE(7, "LLDP-MED capabilities"); |
542 | chassis->c_med_cap_available = PEEK_UINT16; | |
543 | chassis->c_med_type = PEEK_UINT8; | |
740593ff | 544 | port->p_med_cap_enabled |= |
40ecae87 | 545 | LLDPMED_CAP_CAP; |
6772b237 | 546 | break; |
efe3f9b0 | 547 | case LLDP_TLV_MED_POLICY: |
a8105c1b VB |
548 | CHECK_TLV_SIZE(8, "LLDP-MED policy"); |
549 | policy = PEEK_UINT32; | |
e3a44efb VB |
550 | if (((policy >> 24) < 1) || |
551 | ((policy >> 24) > LLDPMED_APPTYPE_LAST)) { | |
552 | LLOG_INFO("unknown policy field %d " | |
553 | "received on %s", | |
554 | hardware->h_ifname); | |
e3a44efb VB |
555 | break; |
556 | } | |
740593ff | 557 | port->p_med_policy[(policy >> 24) - 1].type = |
e3a44efb | 558 | (policy >> 24); |
740593ff | 559 | port->p_med_policy[(policy >> 24) - 1].unknown = |
e3a44efb | 560 | ((policy & 0x800000) != 0); |
740593ff | 561 | port->p_med_policy[(policy >> 24) - 1].tagged = |
e3a44efb | 562 | ((policy & 0x400000) != 0); |
740593ff | 563 | port->p_med_policy[(policy >> 24) - 1].vid = |
e3a44efb | 564 | (policy & 0x001FFE00) >> 9; |
740593ff | 565 | port->p_med_policy[(policy >> 24) - 1].priority = |
e3a44efb | 566 | (policy & 0x1C0) >> 6; |
740593ff | 567 | port->p_med_policy[(policy >> 24) - 1].dscp = |
e3a44efb | 568 | policy & 0x3F; |
740593ff | 569 | port->p_med_cap_enabled |= |
40ecae87 | 570 | LLDPMED_CAP_POLICY; |
efe3f9b0 VB |
571 | break; |
572 | case LLDP_TLV_MED_LOCATION: | |
a8105c1b VB |
573 | CHECK_TLV_SIZE(5, "LLDP-MED Location"); |
574 | loctype = PEEK_UINT8; | |
575 | if ((loctype < 1) || | |
576 | (loctype > LLDPMED_LOCFORMAT_LAST)) { | |
e3a44efb VB |
577 | LLOG_INFO("unknown location type " |
578 | "received on %s", | |
579 | hardware->h_ifname); | |
e3a44efb VB |
580 | break; |
581 | } | |
740593ff | 582 | if ((port->p_med_location[loctype - 1].data = |
a8105c1b | 583 | (char*)malloc(tlv_size - 5)) == NULL) { |
efe3f9b0 VB |
584 | LLOG_WARN("unable to allocate memory " |
585 | "for LLDP-MED location for " | |
586 | "frame received on %s", | |
587 | hardware->h_ifname); | |
588 | goto malformed; | |
589 | } | |
a8105c1b VB |
590 | PEEK_BYTES(port->p_med_location[loctype - 1].data, |
591 | tlv_size - 5); | |
740593ff | 592 | port->p_med_location[loctype - 1].data_len = |
a8105c1b | 593 | tlv_size - 5; |
740593ff | 594 | port->p_med_location[loctype - 1].format = loctype; |
740593ff | 595 | port->p_med_cap_enabled |= |
40ecae87 | 596 | LLDPMED_CAP_LOCATION; |
efe3f9b0 VB |
597 | break; |
598 | case LLDP_TLV_MED_MDI: | |
a8105c1b VB |
599 | CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI"); |
600 | power = PEEK_UINT8; | |
601 | switch (power & 0xC0) { | |
994812b9 | 602 | case 0x0: |
740593ff VB |
603 | port->p_med_pow_devicetype = LLDPMED_POW_TYPE_PSE; |
604 | port->p_med_cap_enabled |= | |
a49b3a79 | 605 | LLDPMED_CAP_MDI_PSE; |
a8105c1b | 606 | switch (power & 0x30) { |
994812b9 | 607 | case 0x0: |
740593ff | 608 | port->p_med_pow_source = |
994812b9 VB |
609 | LLDPMED_POW_SOURCE_UNKNOWN; |
610 | break; | |
611 | case 0x10: | |
740593ff | 612 | port->p_med_pow_source = |
994812b9 VB |
613 | LLDPMED_POW_SOURCE_PRIMARY; |
614 | break; | |
615 | case 0x20: | |
740593ff | 616 | port->p_med_pow_source = |
994812b9 VB |
617 | LLDPMED_POW_SOURCE_BACKUP; |
618 | break; | |
619 | default: | |
740593ff | 620 | port->p_med_pow_source = |
994812b9 VB |
621 | LLDPMED_POW_SOURCE_RESERVED; |
622 | } | |
623 | break; | |
624 | case 0x40: | |
740593ff VB |
625 | port->p_med_pow_devicetype = LLDPMED_POW_TYPE_PD; |
626 | port->p_med_cap_enabled |= | |
a49b3a79 | 627 | LLDPMED_CAP_MDI_PD; |
a8105c1b | 628 | switch (power & 0x30) { |
994812b9 | 629 | case 0x0: |
740593ff | 630 | port->p_med_pow_source = |
994812b9 VB |
631 | LLDPMED_POW_SOURCE_UNKNOWN; |
632 | break; | |
633 | case 0x10: | |
740593ff | 634 | port->p_med_pow_source = |
994812b9 VB |
635 | LLDPMED_POW_SOURCE_PSE; |
636 | break; | |
637 | case 0x20: | |
740593ff | 638 | port->p_med_pow_source = |
994812b9 VB |
639 | LLDPMED_POW_SOURCE_LOCAL; |
640 | break; | |
641 | default: | |
740593ff | 642 | port->p_med_pow_source = |
994812b9 VB |
643 | LLDPMED_POW_SOURCE_BOTH; |
644 | } | |
645 | break; | |
646 | default: | |
740593ff | 647 | port->p_med_pow_devicetype = |
994812b9 VB |
648 | LLDPMED_POW_TYPE_RESERVED; |
649 | } | |
a8105c1b | 650 | switch (power & 0x0F) { |
994812b9 | 651 | case 0x0: |
740593ff | 652 | port->p_med_pow_priority = |
994812b9 VB |
653 | LLDPMED_POW_PRIO_UNKNOWN; |
654 | break; | |
655 | case 0x1: | |
740593ff | 656 | port->p_med_pow_priority = |
994812b9 VB |
657 | LLDPMED_POW_PRIO_CRITICAL; |
658 | break; | |
659 | case 0x2: | |
740593ff | 660 | port->p_med_pow_priority = |
994812b9 VB |
661 | LLDPMED_POW_PRIO_HIGH; |
662 | break; | |
663 | case 0x3: | |
740593ff | 664 | port->p_med_pow_priority = |
994812b9 VB |
665 | LLDPMED_POW_PRIO_LOW; |
666 | break; | |
667 | default: | |
740593ff | 668 | port->p_med_pow_priority = |
994812b9 VB |
669 | LLDPMED_POW_PRIO_UNKNOWN; |
670 | } | |
a8105c1b | 671 | port->p_med_pow_val = PEEK_UINT16; |
efe3f9b0 | 672 | break; |
6772b237 VB |
673 | case LLDP_TLV_MED_IV_HW: |
674 | case LLDP_TLV_MED_IV_SW: | |
675 | case LLDP_TLV_MED_IV_FW: | |
676 | case LLDP_TLV_MED_IV_SN: | |
677 | case LLDP_TLV_MED_IV_MANUF: | |
678 | case LLDP_TLV_MED_IV_MODEL: | |
679 | case LLDP_TLV_MED_IV_ASSET: | |
a8105c1b | 680 | if (tlv_size <= 4) |
6772b237 VB |
681 | b = NULL; |
682 | else { | |
a8105c1b | 683 | if ((b = (char*)malloc(tlv_size - 3)) == |
efe3f9b0 VB |
684 | NULL) { |
685 | LLOG_WARN("unable to allocate " | |
686 | "memory for LLDP-MED " | |
687 | "inventory for frame " | |
688 | "received on %s", | |
689 | hardware->h_ifname); | |
690 | goto malformed; | |
691 | } | |
a8105c1b VB |
692 | PEEK_BYTES(b, tlv_size - 4); |
693 | b[tlv_size - 4] = '\0'; | |
6772b237 | 694 | } |
a8105c1b | 695 | switch (tlv_subtype) { |
6772b237 VB |
696 | case LLDP_TLV_MED_IV_HW: |
697 | chassis->c_med_hw = b; | |
698 | break; | |
699 | case LLDP_TLV_MED_IV_FW: | |
700 | chassis->c_med_fw = b; | |
701 | break; | |
702 | case LLDP_TLV_MED_IV_SW: | |
703 | chassis->c_med_sw = b; | |
704 | break; | |
705 | case LLDP_TLV_MED_IV_SN: | |
706 | chassis->c_med_sn = b; | |
707 | break; | |
708 | case LLDP_TLV_MED_IV_MANUF: | |
709 | chassis->c_med_manuf = b; | |
710 | break; | |
711 | case LLDP_TLV_MED_IV_MODEL: | |
397dee69 | 712 | chassis->c_med_model = b; |
6772b237 VB |
713 | break; |
714 | case LLDP_TLV_MED_IV_ASSET: | |
72c4c96f | 715 | chassis->c_med_asset = b; |
6772b237 VB |
716 | break; |
717 | default: | |
718 | LLOG_WARNX("should not be there!"); | |
719 | free(b); | |
720 | break; | |
721 | } | |
740593ff | 722 | port->p_med_cap_enabled |= |
40ecae87 | 723 | LLDPMED_CAP_IV; |
6772b237 VB |
724 | break; |
725 | default: | |
726 | /* Unknown LLDP MED, ignore it */ | |
6772b237 VB |
727 | hardware->h_rx_unrecognized_cnt++; |
728 | } | |
729 | #endif /* ENABLE_LLDPMED */ | |
43c02e7b | 730 | } else { |
9ca19b69 | 731 | LLOG_INFO("unknown org tlv received on %s", |
43c02e7b | 732 | hardware->h_ifname); |
37387046 | 733 | hardware->h_rx_unrecognized_cnt++; |
43c02e7b VB |
734 | } |
735 | break; | |
736 | default: | |
737 | LLOG_WARNX("unknown tlv (%d) received on %s", | |
a8105c1b VB |
738 | tlv_type, hardware->h_ifname); |
739 | goto malformed; | |
740 | } | |
741 | if (pos > tlv + tlv_size) { | |
742 | LLOG_WARNX("BUG: already past TLV!"); | |
43c02e7b VB |
743 | goto malformed; |
744 | } | |
a8105c1b | 745 | PEEK_DISCARD(tlv + tlv_size - pos); |
43c02e7b VB |
746 | } |
747 | ||
748 | /* Some random check */ | |
749 | if ((chassis->c_id == NULL) || | |
750 | (port->p_id == NULL) || | |
751 | (chassis->c_ttl == 0) || | |
752 | (gotend == 0)) { | |
753 | LLOG_WARNX("some mandatory tlv are missing for frame received on %s", | |
754 | hardware->h_ifname); | |
755 | goto malformed; | |
756 | } | |
757 | #define NOTRECEIVED "Not received" | |
758 | if (chassis->c_name == NULL) { | |
759 | if ((chassis->c_name = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
760 | LLOG_WARNX("unable to allocate null chassis name"); | |
761 | goto malformed; | |
762 | } | |
763 | memcpy(chassis->c_name, NOTRECEIVED, strlen(NOTRECEIVED)); | |
764 | } | |
765 | if (chassis->c_descr == NULL) { | |
766 | if ((chassis->c_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
767 | LLOG_WARNX("unable to allocate null chassis description"); | |
768 | goto malformed; | |
769 | } | |
770 | memcpy(chassis->c_descr, NOTRECEIVED, strlen(NOTRECEIVED)); | |
771 | } | |
772 | if (port->p_descr == NULL) { | |
773 | if ((port->p_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) { | |
774 | LLOG_WARNX("unable to allocate null port description"); | |
775 | goto malformed; | |
776 | } | |
777 | memcpy(port->p_descr, NOTRECEIVED, strlen(NOTRECEIVED)); | |
778 | } | |
779 | *newchassis = chassis; | |
780 | *newport = port; | |
781 | return 1; | |
782 | malformed: | |
d3322e23 | 783 | lldpd_chassis_cleanup(chassis); |
a0edeaf8 | 784 | lldpd_port_cleanup(port, 1); |
43c02e7b VB |
785 | return -1; |
786 | } |