]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/protocols/edp.c
9cd55f6a8933e2e478b25bf9ecd8a498a7d4bc7a
[thirdparty/lldpd.git] / src / daemon / protocols / edp.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
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"
19 #include "frame.h"
20
21 #ifdef ENABLE_EDP
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <arpa/inet.h>
27 #include <fnmatch.h>
28
29 static int seq = 0;
30
31 int
32 edp_send(struct lldpd *global,
33 struct lldpd_hardware *hardware)
34 {
35 const u_int8_t mcastaddr[] = EDP_MULTICAST_ADDR;
36 const u_int8_t llcorg[] = LLC_ORG_EXTREME;
37 struct lldpd_chassis *chassis;
38 int length, i, v;
39 u_int8_t *packet, *pos, *pos_llc, *pos_len_eh, *pos_len_edp, *pos_edp, *tlv, *end;
40 u_int16_t checksum;
41 #ifdef ENABLE_DOT1
42 struct lldpd_vlan *vlan;
43 unsigned int state = 0;
44 #endif
45 u_int8_t edp_fakeversion[] = {7, 6, 4, 99};
46 /* Subsequent XXX can be replaced by other values. We place
47 them here to ensure the position of "" to be a bit
48 invariant with version changes. */
49 char *deviceslot[] = { "eth", "veth", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "", NULL };
50
51 log_debug("edp", "send EDP frame on port %s", hardware->h_ifname);
52
53 chassis = hardware->h_lport.p_chassis;
54 #ifdef ENABLE_DOT1
55 while (state != 2) {
56 #endif
57 length = hardware->h_mtu;
58 if ((packet = (u_int8_t*)calloc(1, length)) == NULL)
59 return ENOMEM;
60 pos = packet;
61 v = 0;
62
63 /* Ethernet header */
64 if (!(
65 POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
66 POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
67 POKE_SAVE(pos_len_eh) && /* We compute the len later */
68 POKE_UINT16(0)))
69 goto toobig;
70
71 /* LLC */
72 if (!(
73 POKE_SAVE(pos_llc) && /* We need to save our
74 current position to
75 compute ethernet len */
76 /* SSAP and DSAP */
77 POKE_UINT8(0xaa) && POKE_UINT8(0xaa) &&
78 /* Control field */
79 POKE_UINT8(0x03) &&
80 /* ORG */
81 POKE_BYTES(llcorg, sizeof(llcorg)) &&
82 POKE_UINT16(LLC_PID_EDP)))
83 goto toobig;
84
85 /* EDP header */
86 if ((chassis->c_id_len != ETHER_ADDR_LEN) ||
87 (chassis->c_id_subtype != LLDP_CHASSISID_SUBTYPE_LLADDR)) {
88 log_warnx("edp", "local chassis does not use MAC address as chassis ID!?");
89 free(packet);
90 return EINVAL;
91 }
92 if (!(
93 POKE_SAVE(pos_edp) && /* Save the start of EDP frame */
94 POKE_UINT8(1) && POKE_UINT8(0) &&
95 POKE_SAVE(pos_len_edp) && /* We compute the len
96 and the checksum
97 later */
98 POKE_UINT32(0) && /* Len + Checksum */
99 POKE_UINT16(seq) &&
100 POKE_UINT16(0) &&
101 POKE_BYTES(chassis->c_id, ETHER_ADDR_LEN)))
102 goto toobig;
103 seq++;
104
105 #ifdef ENABLE_DOT1
106 switch (state) {
107 case 0:
108 #endif
109 /* Display TLV */
110 if (!(
111 POKE_START_EDP_TLV(EDP_TLV_DISPLAY) &&
112 POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
113 POKE_UINT8(0) && /* Add a NULL character
114 for better
115 compatibility */
116 POKE_END_EDP_TLV))
117 goto toobig;
118
119 /* Info TLV */
120 if (!(
121 POKE_START_EDP_TLV(EDP_TLV_INFO)))
122 goto toobig;
123 /* We try to emulate the slot thing */
124 for (i=0; deviceslot[i] != NULL; i++) {
125 if (strncmp(hardware->h_ifname, deviceslot[i],
126 strlen(deviceslot[i])) == 0) {
127 if (!(
128 POKE_UINT16(i) &&
129 POKE_UINT16(atoi(hardware->h_ifname +
130 strlen(deviceslot[i])))))
131 goto toobig;
132 break;
133 }
134 }
135 /* If we don't find a "slot", we say that the
136 interface is in slot 8 */
137 if (deviceslot[i] == NULL) {
138 if (!(
139 POKE_UINT16(8) &&
140 POKE_UINT16(hardware->h_ifindex)))
141 goto toobig;
142 }
143 if (!(
144 POKE_UINT16(0) && /* vchassis */
145 POKE_UINT32(0) && POKE_UINT16(0) && /* Reserved */
146 /* Version */
147 POKE_BYTES(edp_fakeversion, sizeof(edp_fakeversion)) &&
148 /* Connections, we say that we won't
149 have more interfaces than this
150 mask. */
151 POKE_UINT32(0xffffffff) &&
152 POKE_UINT32(0) && POKE_UINT32(0) && POKE_UINT32(0) &&
153 POKE_END_EDP_TLV))
154 goto toobig;
155
156 #ifdef ENABLE_DOT1
157 break;
158 case 1:
159 TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans,
160 v_entries) {
161 v++;
162 if (!(
163 POKE_START_EDP_TLV(EDP_TLV_VLAN) &&
164 POKE_UINT8(0) && /* Flags: no IP address */
165 POKE_UINT8(0) && /* Reserved */
166 POKE_UINT16(vlan->v_vid) &&
167 POKE_UINT32(0) && /* Reserved */
168 POKE_UINT32(0) && /* IP address */
169 /* VLAN name */
170 POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) &&
171 POKE_UINT8(0) &&
172 POKE_END_EDP_TLV))
173 goto toobig;
174 }
175 break;
176 }
177
178 if ((state == 1) && (v == 0)) {
179 /* No VLAN, no need to send another TLV */
180 free(packet);
181 break;
182 }
183 #endif
184
185 /* Null TLV */
186 if (!(
187 POKE_START_EDP_TLV(EDP_TLV_NULL) &&
188 POKE_END_EDP_TLV &&
189 POKE_SAVE(end)))
190 goto toobig;
191
192 /* Compute len and checksum */
193 i = end - pos_llc; /* Ethernet length */
194 v = end - pos_edp; /* EDP length */
195 POKE_RESTORE(pos_len_eh);
196 if (!(POKE_UINT16(i))) goto toobig;
197 POKE_RESTORE(pos_len_edp);
198 if (!(POKE_UINT16(v))) goto toobig;
199 checksum = frame_checksum(pos_edp, v, 0);
200 if (!(POKE_UINT16(checksum))) goto toobig;
201
202 if (interfaces_send_helper(global, hardware,
203 (char *)packet, end - packet) == -1) {
204 log_warn("edp", "unable to send packet on real device for %s",
205 hardware->h_ifname);
206 free(packet);
207 return ENETDOWN;
208 }
209 free(packet);
210
211 #ifdef ENABLE_DOT1
212 state++;
213 }
214 #endif
215
216 hardware->h_tx_cnt++;
217 return 0;
218 toobig:
219 free(packet);
220 return E2BIG;
221 }
222
223 #define CHECK_TLV_SIZE(x, name) \
224 do { if (tlv_len < (x)) { \
225 log_warnx("edp", name " EDP TLV too short received on %s", \
226 hardware->h_ifname); \
227 goto malformed; \
228 } } while (0)
229
230 int
231 edp_decode(struct lldpd *cfg, char *frame, int s,
232 struct lldpd_hardware *hardware,
233 struct lldpd_chassis **newchassis, struct lldpd_port **newport)
234 {
235 struct lldpd_chassis *chassis;
236 struct lldpd_port *port;
237 #ifdef ENABLE_DOT1
238 struct lldpd_mgmt *mgmt, *mgmt_next, *m;
239 struct lldpd_vlan *lvlan = NULL, *lvlan_next;
240 #endif
241 const unsigned char edpaddr[] = EDP_MULTICAST_ADDR;
242 int length, gotend = 0, gotvlans = 0, edp_len, tlv_len, tlv_type;
243 int edp_port, edp_slot;
244 u_int8_t *pos, *pos_edp, *tlv;
245 u_int8_t version[4];
246 #ifdef ENABLE_DOT1
247 struct in_addr address;
248 struct lldpd_port *oport;
249 #endif
250
251 log_debug("edp", "decode EDP frame on port %s",
252 hardware->h_ifname);
253
254 if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
255 log_warn("edp", "failed to allocate remote chassis");
256 return -1;
257 }
258 TAILQ_INIT(&chassis->c_mgmt);
259 if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
260 log_warn("edp", "failed to allocate remote port");
261 free(chassis);
262 return -1;
263 }
264 #ifdef ENABLE_DOT1
265 TAILQ_INIT(&port->p_vlans);
266 #endif
267
268 length = s;
269 pos = (u_int8_t*)frame;
270
271 if (length < 2*ETHER_ADDR_LEN + sizeof(u_int16_t) + 8 /* LLC */ +
272 10 + ETHER_ADDR_LEN /* EDP header */) {
273 log_warnx("edp", "too short EDP frame received on %s", hardware->h_ifname);
274 goto malformed;
275 }
276
277 if (PEEK_CMP(edpaddr, sizeof(edpaddr)) != 0) {
278 log_info("edp", "frame not targeted at EDP multicast address received on %s",
279 hardware->h_ifname);
280 goto malformed;
281 }
282 PEEK_DISCARD(ETHER_ADDR_LEN); PEEK_DISCARD_UINT16;
283 PEEK_DISCARD(6); /* LLC: DSAP + SSAP + control + org */
284 if (PEEK_UINT16 != LLC_PID_EDP) {
285 log_debug("edp", "incorrect LLC protocol ID received on %s",
286 hardware->h_ifname);
287 goto malformed;
288 }
289
290 (void)PEEK_SAVE(pos_edp); /* Save the start of EDP packet */
291 if (PEEK_UINT8 != 1) {
292 log_warnx("edp", "incorrect EDP version for frame received on %s",
293 hardware->h_ifname);
294 goto malformed;
295 }
296 PEEK_DISCARD_UINT8; /* Reserved */
297 edp_len = PEEK_UINT16;
298 PEEK_DISCARD_UINT16; /* Checksum */
299 PEEK_DISCARD_UINT16; /* Sequence */
300 if (PEEK_UINT16 != 0) { /* ID Type = 0 = MAC */
301 log_warnx("edp", "incorrect device id type for frame received on %s",
302 hardware->h_ifname);
303 goto malformed;
304 }
305 if (edp_len > length + 10) {
306 log_warnx("edp", "incorrect size for EDP frame received on %s",
307 hardware->h_ifname);
308 goto malformed;
309 }
310 port->p_ttl = cfg?cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold:0;
311 chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
312 chassis->c_id_len = ETHER_ADDR_LEN;
313 if ((chassis->c_id = (char *)malloc(ETHER_ADDR_LEN)) == NULL) {
314 log_warn("edp", "unable to allocate memory for chassis ID");
315 goto malformed;
316 }
317 PEEK_BYTES(chassis->c_id, ETHER_ADDR_LEN);
318
319 /* Let's check checksum */
320 if (frame_checksum(pos_edp, edp_len, 0) != 0) {
321 log_warnx("edp", "incorrect EDP checksum for frame received on %s",
322 hardware->h_ifname);
323 goto malformed;
324 }
325
326 while (length && !gotend) {
327 if (length < 4) {
328 log_warnx("edp", "EDP TLV header is too large for "
329 "frame received on %s",
330 hardware->h_ifname);
331 goto malformed;
332 }
333 if (PEEK_UINT8 != EDP_TLV_MARKER) {
334 log_warnx("edp", "incorrect marker starting EDP TLV header for frame "
335 "received on %s",
336 hardware->h_ifname);
337 goto malformed;
338 }
339 tlv_type = PEEK_UINT8;
340 tlv_len = PEEK_UINT16 - 4;
341 (void)PEEK_SAVE(tlv);
342 if ((tlv_len < 0) || (tlv_len > length)) {
343 log_debug("edp", "incorrect size in EDP TLV header for frame "
344 "received on %s",
345 hardware->h_ifname);
346 /* Some poor old Extreme Summit are quite bogus */
347 gotend = 1;
348 break;
349 }
350 switch (tlv_type) {
351 case EDP_TLV_INFO:
352 CHECK_TLV_SIZE(32, "Info");
353 port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
354 edp_slot = PEEK_UINT16; edp_port = PEEK_UINT16;
355 if (asprintf(&port->p_id, "%d/%d",
356 edp_slot + 1, edp_port + 1) == -1) {
357 log_warn("edp", "unable to allocate memory for "
358 "port ID");
359 goto malformed;
360 }
361 port->p_id_len = strlen(port->p_id);
362 if (asprintf(&port->p_descr, "Slot %d / Port %d",
363 edp_slot + 1, edp_port + 1) == -1) {
364 log_warn("edp", "unable to allocate memory for "
365 "port description");
366 goto malformed;
367 }
368 PEEK_DISCARD_UINT16; /* vchassis */
369 PEEK_DISCARD(6); /* Reserved */
370 PEEK_BYTES(version, 4);
371 if (asprintf(&chassis->c_descr,
372 "EDP enabled device, version %d.%d.%d.%d",
373 version[0], version[1],
374 version[2], version[3]) == -1) {
375 log_warn("edp", "unable to allocate memory for "
376 "chassis description");
377 goto malformed;
378 }
379 break;
380 case EDP_TLV_DISPLAY:
381 if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == NULL) {
382 log_warn("edp", "unable to allocate memory for chassis "
383 "name");
384 goto malformed;
385 }
386 /* TLV display contains a lot of garbage */
387 PEEK_BYTES(chassis->c_name, tlv_len);
388 break;
389 case EDP_TLV_NULL:
390 if (tlv_len != 0) {
391 log_warnx("edp", "null tlv with incorrect size in frame "
392 "received on %s",
393 hardware->h_ifname);
394 goto malformed;
395 }
396 if (length)
397 log_debug("edp", "extra data after edp frame on %s",
398 hardware->h_ifname);
399 gotend = 1;
400 break;
401 case EDP_TLV_VLAN:
402 #ifdef ENABLE_DOT1
403 CHECK_TLV_SIZE(12, "VLAN");
404 if ((lvlan = (struct lldpd_vlan *)calloc(1,
405 sizeof(struct lldpd_vlan))) == NULL) {
406 log_warn("edp", "unable to allocate vlan");
407 goto malformed;
408 }
409 PEEK_DISCARD_UINT16; /* Flags + reserved */
410 lvlan->v_vid = PEEK_UINT16; /* VID */
411 PEEK_DISCARD(4); /* Reserved */
412 PEEK_BYTES(&address, sizeof(address));
413
414 if (address.s_addr != INADDR_ANY) {
415 mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address,
416 sizeof(struct in_addr), 0);
417 if (mgmt == NULL) {
418 log_warn("edp", "Out of memory");
419 goto malformed;
420 }
421 TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
422 }
423
424 if ((lvlan->v_name = (char *)calloc(1,
425 tlv_len + 1 - 12)) == NULL) {
426 log_warn("edp", "unable to allocate vlan name");
427 goto malformed;
428 }
429 PEEK_BYTES(lvlan->v_name, tlv_len - 12);
430
431 TAILQ_INSERT_TAIL(&port->p_vlans,
432 lvlan, v_entries);
433 lvlan = NULL;
434 #endif
435 gotvlans = 1;
436 break;
437 default:
438 log_debug("edp", "unknown EDP TLV type (%d) received on %s",
439 tlv_type, hardware->h_ifname);
440 hardware->h_rx_unrecognized_cnt++;
441 }
442 PEEK_DISCARD(tlv + tlv_len - pos);
443 }
444 if ((chassis->c_id == NULL) ||
445 (port->p_id == NULL) ||
446 (chassis->c_name == NULL) ||
447 (chassis->c_descr == NULL) ||
448 (port->p_descr == NULL) ||
449 (gotend == 0)) {
450 #ifdef ENABLE_DOT1
451 if (gotvlans && gotend) {
452 /* VLAN can be sent in a separate frames. We need to add
453 * those vlans to an existing port */
454 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
455 if (!((oport->p_protocol == LLDPD_MODE_EDP) &&
456 (oport->p_chassis->c_id_subtype ==
457 chassis->c_id_subtype) &&
458 (oport->p_chassis->c_id_len == chassis->c_id_len) &&
459 (memcmp(oport->p_chassis->c_id, chassis->c_id,
460 chassis->c_id_len) == 0)))
461 continue;
462 /* We attach the VLANs to the found port */
463 lldpd_vlan_cleanup(oport);
464 for (lvlan = TAILQ_FIRST(&port->p_vlans);
465 lvlan != NULL;
466 lvlan = lvlan_next) {
467 lvlan_next = TAILQ_NEXT(lvlan, v_entries);
468 TAILQ_REMOVE(&port->p_vlans, lvlan, v_entries);
469 TAILQ_INSERT_TAIL(&oport->p_vlans,
470 lvlan, v_entries);
471 }
472 /* And the IP addresses */
473 for (mgmt = TAILQ_FIRST(&chassis->c_mgmt);
474 mgmt != NULL;
475 mgmt = mgmt_next) {
476 mgmt_next = TAILQ_NEXT(mgmt, m_entries);
477 TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries);
478 /* Don't add an address that already exists! */
479 TAILQ_FOREACH(m, &chassis->c_mgmt, m_entries)
480 if (m->m_family == mgmt->m_family &&
481 !memcmp(&m->m_addr, &mgmt->m_addr,
482 sizeof(m->m_addr))) break;
483 if (m == NULL)
484 TAILQ_INSERT_TAIL(&oport->p_chassis->c_mgmt,
485 mgmt, m_entries);
486 }
487 }
488 /* We discard the remaining frame */
489 goto malformed;
490 }
491 #else
492 if (gotvlans)
493 goto malformed;
494 #endif
495 log_warnx("edp", "some mandatory tlv are missing for frame received on %s",
496 hardware->h_ifname);
497 goto malformed;
498 }
499 *newchassis = chassis;
500 *newport = port;
501 return 1;
502
503 malformed:
504 #ifdef ENABLE_DOT1
505 free(lvlan);
506 #endif
507 lldpd_chassis_cleanup(chassis, 1);
508 lldpd_port_cleanup(port, 1);
509 free(port);
510 return -1;
511 }
512
513 #endif /* ENABLE_EDP */