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