2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
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.
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.
23 #include <arpa/inet.h>
29 edp_send(struct lldpd
*global
, struct lldpd_chassis
*chassis
,
30 struct lldpd_hardware
*hardware
)
34 const u_int8_t mcastaddr
[] = EDP_MULTICAST_ADDR
;
35 const u_int8_t llcorg
[] = LLC_ORG_EXTREME
;
36 struct iovec
*iov
= NULL
;
38 struct edp_tlv_vlan
*ovlan
= NULL
;
39 struct lldpd_vlan
*vlan
;
40 unsigned int state
= 0;
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
};
53 if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \
54 sizeof(struct iovec))) == NULL) \
59 free(iov
); iov
= NULL
;
60 free(ovlan
); ovlan
= NULL
;
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;
72 memcpy(llc
.org
, llcorg
, sizeof(llc
.org
));
73 llc
.protoid
= htons(LLC_PID_EDP
);
75 iov
[c
].iov_base
= &llc
;
76 iov
[c
].iov_len
= sizeof(llc
);
79 memset(&eh
, 0, sizeof(eh
));
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!?");
87 memcpy(&eh
.mac
, chassis
->c_id
, ETH_ALEN
);
89 iov
[c
].iov_base
= &eh
;
90 iov
[c
].iov_len
= sizeof(eh
);
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);
102 iov
[c
].iov_base
= &device
;
103 iov
[c
].iov_len
= sizeof(device
);
105 iov
[c
].iov_base
= chassis
->c_name
;
106 iov
[c
].iov_len
= strlen(chassis
->c_name
) + 1;
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
])));
122 if (deviceslot
[i
] == NULL
) {
123 info
.slot
= htons(8);
124 info
.port
= htons(if_nametoindex(hardware
->h_ifname
));
126 memcpy(info
.version
, edp_fakeversion
, sizeof(info
.version
));
127 info
.connections
[0] = info
.connections
[1] = 0xff;
129 iov
[c
].iov_base
= &info
;
130 iov
[c
].iov_len
= sizeof(info
);
135 TAILQ_FOREACH(vlan
, &hardware
->h_lport
.p_vlans
,
142 if ((ovlan
= (struct edp_tlv_vlan
*)malloc(
143 v
*sizeof(struct edp_tlv_vlan
))) == NULL
) {
144 LLOG_WARN("no room for vlans");
147 TAILQ_FOREACH(vlan
, &hardware
->h_lport
.p_vlans
,
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
);
157 iov
[c
].iov_base
= &ovlan
[v
];
158 iov
[c
].iov_len
= sizeof(ovlan
[v
]);
160 iov
[c
].iov_base
= vlan
->v_name
;
161 iov
[c
].iov_len
= strlen(vlan
->v_name
) + 1;
166 if ((state
== 1) && (v
== -1)) /* No VLAN, no need to send another 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
));
176 iov
[c
].iov_base
= &null
;
177 iov
[c
].iov_len
= sizeof(null
);
181 /* Compute len and checksum */
183 for (i
= 0; i
< c
; i
++) {
184 len
+= iov
[i
].iov_len
;
186 len
-= sizeof(struct ieee8023
);
187 llc
.ether
.size
= htons(len
);
188 len
= len
+ sizeof(struct ieee8023
) - sizeof(struct ethllc
);
190 eh
.checksum
= iov_checksum(&iov
[1], c
- 1, 0);
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",
208 hardware
->h_tx_cnt
++;
218 edp_decode(struct lldpd
*cfg
, char *frame
, int s
,
219 struct lldpd_hardware
*hardware
,
220 struct lldpd_chassis
**newchassis
, struct lldpd_port
**newport
)
222 struct lldpd_chassis
*chassis
;
223 struct lldpd_port
*port
;
225 struct edp_header
*eh
;
226 struct edp_tlv_head
*tlv
;
227 struct edp_tlv_info
*info
;
229 struct edp_tlv_vlan
*vlan
;
230 struct lldpd_vlan
*lvlan
, *lvlan_next
;
232 const unsigned char edpaddr
[] = EDP_MULTICAST_ADDR
;
234 int f
, len
, gotend
= 0, gotvlans
= 0;
236 if ((chassis
= calloc(1, sizeof(struct lldpd_chassis
))) == NULL
) {
237 LLOG_WARN("failed to allocate remote chassis");
240 if ((port
= calloc(1, sizeof(struct lldpd_port
))) == NULL
) {
241 LLOG_WARN("failed to allocate remote port");
246 TAILQ_INIT(&port
->p_vlans
);
249 if (s
< sizeof(struct ethllc
) + sizeof(struct edp_header
)) {
250 LLOG_WARNX("too short frame received on %s", hardware
->h_ifname
);
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",
260 if (ntohs(llc
->ether
.size
) > s
- sizeof(struct ieee8023
)) {
261 LLOG_WARNX("incorrect 802.3 frame size reported on %s",
265 if (llc
->protoid
!= htons(LLC_PID_EDP
)) {
266 LLOG_DEBUG("incorrect LLC protocol ID received on %s",
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
);
278 if (eh
->idtype
!= htons(0)) {
279 LLOG_WARNX("incorrect device id type for frame received on %s",
283 if (ntohs(eh
->len
) > s
- f
) {
284 LLOG_WARNX("incorrect size for EDP frame received on %s",
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");
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",
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",
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 "
319 /* Some poor old Extreme Summit are quite bogus */
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 "
330 switch (tlv
->tlv_type
) {
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)",
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 "
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 "
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");
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 "
370 /* TLV display contains a lot of garbage */
371 strlcpy(chassis
->c_name
, frame
+ f
, len
);
375 LLOG_WARNX("null tlv with incorrect size in frame "
381 LLOG_DEBUG("extra data after edp frame on %s",
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)",
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");
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");
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
;
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 */
420 ip
= inet_ntoa(vlan
->ip
);
421 if (fnmatch(cfg
->g_mgmt_pattern
,
423 chassis
->c_mgmt
.s_addr
= vlan
->ip
.s_addr
;
426 TAILQ_INSERT_TAIL(&port
->p_vlans
,
432 LLOG_DEBUG("unknown EDP TLV type (%d) received on %s",
433 tlv
->tlv_type
, hardware
->h_ifname
);
434 hardware
->h_rx_unrecognized_cnt
++;
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
) ||
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
);
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
,
463 /* And the IP address */
464 hardware
->h_rchassis
->c_mgmt
.s_addr
=
465 chassis
->c_mgmt
.s_addr
;
467 /* We discard the remaining frame */
474 LLOG_WARNX("some mandatory tlv are missing for frame received on %s",
478 *newchassis
= chassis
;
483 lldpd_chassis_cleanup(chassis
);
484 lldpd_port_cleanup(port
);
488 #endif /* ENABLE_EDP */