]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/edp.c
641845557f9bf5df578082aecca8f5562c6552d8
[thirdparty/lldpd.git] / src / edp.c
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"
18
19 #ifdef ENABLE_EDP
20
21 #include <stdio.h>
22 #include <errno.h>
23 #include <arpa/inet.h>
24 #include <fnmatch.h>
25
26 static int seq = 0;
27
28 int
29 edp_send(struct lldpd *global, struct lldpd_chassis *chassis,
30 struct lldpd_hardware *hardware)
31 {
32 struct edp_header eh;
33 struct ethllc llc;
34 const u_int8_t mcastaddr[] = EDP_MULTICAST_ADDR;
35 const u_int8_t llcorg[] = LLC_ORG_EXTREME;
36 struct iovec *iov = NULL;
37 #ifdef ENABLE_DOT1
38 struct edp_tlv_vlan *ovlan = NULL;
39 struct lldpd_vlan *vlan;
40 unsigned int state = 0;
41 #endif
42 struct edp_tlv_head device;
43 struct edp_tlv_head null;
44 struct edp_tlv_info info;
45 u_int8_t edp_fakeversion[] = {7, 6, 4, 99};
46 unsigned int i, c, v, len;
47 /* Subsequent XXX can be replaced by other values. We place
48 them here to ensure the position of "" to be a bit
49 invariant with version changes. */
50 char *deviceslot[] = { "eth", "veth", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "", NULL };
51
52 #define IOV_NEW \
53 if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \
54 sizeof(struct iovec))) == NULL) \
55 fatal(NULL);
56
57 #ifdef ENABLE_DOT1
58 while (state != 2) {
59 free(iov); iov = NULL;
60 free(ovlan); ovlan = NULL;
61 #endif
62 c = v = -1;
63
64 /* Ether + LLC */
65 memset(&llc, 0, sizeof(llc));
66 memcpy(&llc.ether.shost, &hardware->h_lladdr,
67 sizeof(llc.ether.shost));
68 memcpy(&llc.ether.dhost, &mcastaddr,
69 sizeof(llc.ether.dhost));
70 llc.dsap = llc.ssap = 0xaa;
71 llc.control = 0x03;
72 memcpy(llc.org, llcorg, sizeof(llc.org));
73 llc.protoid = htons(LLC_PID_EDP);
74 IOV_NEW;
75 iov[c].iov_base = &llc;
76 iov[c].iov_len = sizeof(llc);
77
78 /* EDP header */
79 memset(&eh, 0, sizeof(eh));
80 eh.version = 1;
81 eh.sequence = htons(seq++);
82 if ((chassis->c_id_len != ETH_ALEN) ||
83 (chassis->c_id_subtype != LLDP_CHASSISID_SUBTYPE_LLADDR)) {
84 LLOG_WARNX("local chassis does not use MAC address as chassis ID!?");
85 return EINVAL;
86 }
87 memcpy(&eh.mac, chassis->c_id, ETH_ALEN);
88 IOV_NEW;
89 iov[c].iov_base = &eh;
90 iov[c].iov_len = sizeof(eh);
91
92 #ifdef ENABLE_DOT1
93 switch (state) {
94 case 0:
95 #endif
96 /* Display TLV */
97 memset(&device, 0, sizeof(device));
98 device.tlv_marker = EDP_TLV_MARKER;
99 device.tlv_type = EDP_TLV_DISPLAY;
100 device.tlv_len = htons(sizeof(device) + strlen(chassis->c_name) + 1);
101 IOV_NEW;
102 iov[c].iov_base = &device;
103 iov[c].iov_len = sizeof(device);
104 IOV_NEW;
105 iov[c].iov_base = chassis->c_name;
106 iov[c].iov_len = strlen(chassis->c_name) + 1;
107
108 /* Info TLV */
109 memset(&info, 0, sizeof(info));
110 info.head.tlv_marker = EDP_TLV_MARKER;
111 info.head.tlv_type = EDP_TLV_INFO;
112 info.head.tlv_len = htons(sizeof(info));
113 for (i=0; deviceslot[i] != NULL; i++) {
114 if (strncmp(hardware->h_ifname, deviceslot[i],
115 strlen(deviceslot[i])) == 0) {
116 info.slot = htons(i);
117 info.port = htons(atoi(hardware->h_ifname +
118 strlen(deviceslot[i])));
119 break;
120 }
121 }
122 if (deviceslot[i] == NULL) {
123 info.slot = htons(8);
124 info.port = htons(if_nametoindex(hardware->h_ifname));
125 }
126 memcpy(info.version, edp_fakeversion, sizeof(info.version));
127 info.connections[0] = info.connections[1] = 0xff;
128 IOV_NEW;
129 iov[c].iov_base = &info;
130 iov[c].iov_len = sizeof(info);
131 #ifdef ENABLE_DOT1
132 break;
133 case 1:
134 v = 0;
135 TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans,
136 v_entries)
137 v++;
138 if (v == 0) {
139 v = -1;
140 break;
141 }
142 if ((ovlan = (struct edp_tlv_vlan*)malloc(
143 v*sizeof(struct edp_tlv_vlan))) == NULL) {
144 LLOG_WARN("no room for vlans");
145 v = -1;
146 }
147 TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans,
148 v_entries) {
149 v--;
150 memset(&ovlan[v], 0, sizeof(ovlan[v]));
151 ovlan[v].head.tlv_marker = EDP_TLV_MARKER;
152 ovlan[v].head.tlv_type = EDP_TLV_VLAN;
153 ovlan[v].head.tlv_len = htons(sizeof(ovlan[v]) +
154 strlen(vlan->v_name) + 1);
155 ovlan[v].vid = htons(vlan->v_vid);
156 IOV_NEW;
157 iov[c].iov_base = &ovlan[v];
158 iov[c].iov_len = sizeof(ovlan[v]);
159 IOV_NEW;
160 iov[c].iov_base = vlan->v_name;
161 iov[c].iov_len = strlen(vlan->v_name) + 1;
162 }
163 break;
164 }
165
166 if ((state == 1) && (v == -1)) /* No VLAN, no need to send another TLV */
167 break;
168 #endif
169
170 /* Null TLV */
171 memset(&null, 0, sizeof(null));
172 null.tlv_marker = EDP_TLV_MARKER;
173 null.tlv_type = EDP_TLV_NULL;
174 null.tlv_len = htons(sizeof(null));
175 IOV_NEW;
176 iov[c].iov_base = &null;
177 iov[c].iov_len = sizeof(null);
178
179 c++;
180
181 /* Compute len and checksum */
182 len = 0;
183 for (i = 0; i < c; i++) {
184 len += iov[i].iov_len;
185 }
186 len -= sizeof(struct ieee8023);
187 llc.ether.size = htons(len);
188 len = len + sizeof(struct ieee8023) - sizeof(struct ethllc);
189 eh.len = htons(len);
190 eh.checksum = iov_checksum(&iov[1], c - 1, 0);
191
192 if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
193 hardware->h_raw, iov, c) == -1) {
194 LLOG_WARN("unable to send packet on real device for %s",
195 hardware->h_ifname);
196 #ifdef ENABLE_DOT1
197 free(ovlan);
198 #endif
199 free(iov);
200 return ENETDOWN;
201 }
202
203 #ifdef ENABLE_DOT1
204 state++;
205 }
206 #endif
207
208 hardware->h_tx_cnt++;
209 #ifdef ENABLE_DOT1
210 free(ovlan);
211 #endif
212 free(iov);
213
214 return 0;
215 }
216
217 int
218 edp_decode(struct lldpd *cfg, char *frame, int s,
219 struct lldpd_hardware *hardware,
220 struct lldpd_chassis **newchassis, struct lldpd_port **newport)
221 {
222 struct lldpd_chassis *chassis;
223 struct lldpd_port *port;
224 struct ethllc *llc;
225 struct edp_header *eh;
226 struct edp_tlv_head *tlv;
227 struct edp_tlv_info *info;
228 #ifdef ENABLE_DOT1
229 struct edp_tlv_vlan *vlan;
230 struct lldpd_vlan *lvlan, *lvlan_next;
231 #endif
232 const unsigned char edpaddr[] = EDP_MULTICAST_ADDR;
233 struct iovec iov;
234 int f, len, gotend = 0, gotvlans = 0;
235
236 if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
237 LLOG_WARN("failed to allocate remote chassis");
238 return -1;
239 }
240 if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
241 LLOG_WARN("failed to allocate remote port");
242 free(chassis);
243 return -1;
244 }
245 #ifdef ENABLE_DOT1
246 TAILQ_INIT(&port->p_vlans);
247 #endif
248
249 if (s < sizeof(struct ethllc) + sizeof(struct edp_header)) {
250 LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
251 goto malformed;
252 }
253
254 llc = (struct ethllc *)frame;
255 if (memcmp(&llc->ether.dhost, edpaddr, sizeof(edpaddr)) != 0) {
256 LLOG_INFO("frame not targeted at EDP multicast address received on %s",
257 hardware->h_ifname);
258 goto malformed;
259 }
260 if (ntohs(llc->ether.size) > s - sizeof(struct ieee8023)) {
261 LLOG_WARNX("incorrect 802.3 frame size reported on %s",
262 hardware->h_ifname);
263 goto malformed;
264 }
265 if (llc->protoid != htons(LLC_PID_EDP)) {
266 LLOG_DEBUG("incorrect LLC protocol ID received on %s",
267 hardware->h_ifname);
268 goto malformed;
269 }
270
271 f = sizeof(struct ethllc);
272 eh = (struct edp_header *)(frame + f);
273 if (eh->version != 1) {
274 LLOG_WARNX("incorrect EDP version (%d) for frame received on %s",
275 eh->version, hardware->h_ifname);
276 goto malformed;
277 }
278 if (eh->idtype != htons(0)) {
279 LLOG_WARNX("incorrect device id type for frame received on %s",
280 hardware->h_ifname);
281 goto malformed;
282 }
283 if (ntohs(eh->len) > s - f) {
284 LLOG_WARNX("incorrect size for EDP frame received on %s",
285 hardware->h_ifname);
286 goto malformed;
287 }
288 chassis->c_ttl = LLDPD_TTL;
289 chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
290 chassis->c_id_len = ETH_ALEN;
291 if ((chassis->c_id = (char *)malloc(ETH_ALEN)) == NULL) {
292 LLOG_WARN("unable to allocate memory for chassis ID");
293 goto malformed;
294 }
295 memcpy(chassis->c_id, eh->mac, ETH_ALEN);
296 /* We ignore reserved bytes and sequence number */
297 iov.iov_len = ntohs(eh->len);
298 iov.iov_base = frame + f;
299 if (iov_checksum(&iov, 1, 0) != 0) {
300 LLOG_WARNX("incorrect EDP checksum for frame received on %s",
301 hardware->h_ifname);
302 goto malformed;
303 }
304
305 f += sizeof(struct edp_header);
306 while ((f < s) && !gotend) {
307 if (f + sizeof(struct edp_tlv_head) > s) {
308 LLOG_WARNX("EDP TLV header is too large for "
309 "frame received on %s",
310 hardware->h_ifname);
311 goto malformed;
312 }
313 tlv = (struct edp_tlv_head *)(frame + f);
314 len = ntohs(tlv->tlv_len) - sizeof(struct edp_tlv_head);
315 if ((len < 0) || (f + sizeof(struct edp_tlv_head) + len > s)) {
316 LLOG_DEBUG("incorrect size in EDP TLV header for frame "
317 "received on %s",
318 hardware->h_ifname);
319 /* Some poor old Extreme Summit are quite bogus */
320 gotend = 1;
321 break;
322 }
323 f += sizeof(struct edp_tlv_head);
324 if (tlv->tlv_marker != EDP_TLV_MARKER) {
325 LLOG_WARNX("incorrect marker starting EDP TLV header for frame "
326 "received on %s",
327 hardware->h_ifname);
328 goto malformed;
329 }
330 switch (tlv->tlv_type) {
331 case EDP_TLV_INFO:
332 if (len != sizeof(struct edp_tlv_info) -
333 sizeof(struct edp_tlv_head)) {
334 LLOG_WARNX("wrong size for EDP TLV info for frame "
335 "received on %s (%d vs %d)",
336 hardware->h_ifname);
337 goto malformed;
338 }
339 info = (struct edp_tlv_info *)(frame + f -
340 sizeof(struct edp_tlv_head));
341 port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
342 if (asprintf(&port->p_id, "%d/%d",
343 ntohs(info->slot) + 1, ntohs(info->port) + 1) == -1) {
344 LLOG_WARN("unable to allocate memory for "
345 "port ID");
346 goto malformed;
347 }
348 port->p_id_len = strlen(port->p_id);
349 if (asprintf(&port->p_descr, "Slot %d / Port %d",
350 ntohs(info->slot) + 1, ntohs(info->port) + 1) == -1) {
351 LLOG_WARN("unable to allocate memory for "
352 "port description");
353 goto malformed;
354 }
355 if (asprintf(&chassis->c_descr,
356 "EDP enabled device, version %d.%d.%d.%d",
357 info->version[0], info->version[1],
358 info->version[2], info->version[3]) == -1) {
359 LLOG_WARN("unable to allocate memory for "
360 "chassis description");
361 goto malformed;
362 }
363 break;
364 case EDP_TLV_DISPLAY:
365 if ((chassis->c_name = (char *)calloc(1, len + 1)) == NULL) {
366 LLOG_WARN("unable to allocate memory for chassis "
367 "name");
368 goto malformed;
369 }
370 /* TLV display contains a lot of garbage */
371 strlcpy(chassis->c_name, frame + f, len);
372 break;
373 case EDP_TLV_NULL:
374 if (len != 0) {
375 LLOG_WARNX("null tlv with incorrect size in frame "
376 "received on %s",
377 hardware->h_ifname);
378 goto malformed;
379 }
380 if (f != s)
381 LLOG_DEBUG("extra data after edp frame on %s",
382 hardware->h_ifname);
383 gotend = 1;
384 break;
385 case EDP_TLV_VLAN:
386 #ifdef ENABLE_DOT1
387 if (len < sizeof(struct edp_tlv_vlan) -
388 sizeof(struct edp_tlv_head)) {
389 LLOG_WARNX("wrong size for EDP TLV vlan for frame "
390 "received on %s (%d vs %d)",
391 hardware->h_ifname);
392 goto malformed;
393 }
394 vlan = (struct edp_tlv_vlan *)(frame + f -
395 sizeof(struct edp_tlv_head));
396 if ((lvlan = (struct lldpd_vlan *)calloc(1,
397 sizeof(struct lldpd_vlan))) == NULL) {
398 LLOG_WARN("unable to allocate vlan");
399 goto malformed;
400 }
401 lvlan->v_vid = ntohs(vlan->vid);
402 if ((lvlan->v_name = (char *)calloc(1, len + 1 -
403 sizeof(struct edp_tlv_vlan) +
404 sizeof(struct edp_tlv_head))) == NULL) {
405 LLOG_WARN("unable to allocate vlan name");
406 goto malformed;
407 }
408 strlcpy(lvlan->v_name, frame + f + sizeof(struct edp_tlv_vlan) -
409 sizeof(struct edp_tlv_head), len -
410 sizeof(struct edp_tlv_vlan) +
411 sizeof(struct edp_tlv_head));
412 if (vlan->ip.s_addr != INADDR_ANY) {
413 if (chassis->c_mgmt.s_addr == INADDR_ANY)
414 chassis->c_mgmt.s_addr = vlan->ip.s_addr;
415 else
416 /* We need to guess the good one */
417 if (cfg->g_mgmt_pattern != NULL) {
418 /* We can try to use this to prefer an address */
419 char *ip;
420 ip = inet_ntoa(vlan->ip);
421 if (fnmatch(cfg->g_mgmt_pattern,
422 ip, 0) == 0)
423 chassis->c_mgmt.s_addr = vlan->ip.s_addr;
424 }
425 }
426 TAILQ_INSERT_TAIL(&port->p_vlans,
427 lvlan, v_entries);
428 #endif
429 gotvlans = 1;
430 break;
431 default:
432 LLOG_DEBUG("unknown EDP TLV type (%d) received on %s",
433 tlv->tlv_type, hardware->h_ifname);
434 hardware->h_rx_unrecognized_cnt++;
435 }
436 f += len;
437 }
438 if ((chassis->c_id == NULL) ||
439 (port->p_id == NULL) ||
440 (chassis->c_name == NULL) ||
441 (chassis->c_descr == NULL) ||
442 (port->p_descr == NULL) ||
443 (gotend == 0)) {
444 #ifdef ENABLE_DOT1
445 if (gotvlans && gotend) {
446 /* VLAN can be sent in a separate frames. We need to add
447 * those vlans to an existing chassis */
448 if (hardware->h_rchassis &&
449 (hardware->h_rchassis->c_id_subtype == chassis->c_id_subtype) &&
450 (hardware->h_rchassis->c_id_len == chassis->c_id_len) &&
451 (memcmp(hardware->h_rchassis->c_id, chassis->c_id,
452 chassis->c_id_len) == 0)) {
453 /* We attach the VLANs to current hardware */
454 lldpd_vlan_cleanup(hardware->h_rport);
455 for (lvlan = TAILQ_FIRST(&port->p_vlans);
456 lvlan != NULL;
457 lvlan = lvlan_next) {
458 lvlan_next = TAILQ_NEXT(lvlan, v_entries);
459 TAILQ_REMOVE(&port->p_vlans, lvlan, v_entries);
460 TAILQ_INSERT_TAIL(&hardware->h_rport->p_vlans,
461 lvlan, v_entries);
462 }
463 /* And the IP address */
464 hardware->h_rchassis->c_mgmt.s_addr =
465 chassis->c_mgmt.s_addr;
466 }
467 /* We discard the remaining frame */
468 goto malformed;
469 }
470 #else
471 if (gotvlans)
472 goto malformed;
473 #endif
474 LLOG_WARNX("some mandatory tlv are missing for frame received on %s",
475 hardware->h_ifname);
476 goto malformed;
477 }
478 *newchassis = chassis;
479 *newport = port;
480 return 1;
481
482 malformed:
483 lldpd_chassis_cleanup(chassis);
484 lldpd_port_cleanup(port);
485 return -1;
486 }
487
488 #endif /* ENABLE_EDP */