1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 Tom Gundersen
7 Copyright (C) 2014 Susant Sahani
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "lldp-internal.h"
25 /* We store maximum 1K chassis entries */
26 #define LLDP_MIB_MAX_CHASSIS 1024
28 /* Maximum Ports can be attached to any chassis */
29 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
31 int lldp_read_chassis_id(tlv_packet *tlv,
38 assert_return(tlv, -EINVAL);
40 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
44 r = tlv_packet_read_u8(tlv, &subtype);
49 case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
51 r = tlv_packet_read_bytes(tlv, data, length);
64 (void)lldp_tlv_packet_exit_container(tlv);
70 int lldp_read_port_id(tlv_packet *tlv,
78 assert_return(tlv, -EINVAL);
80 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
84 r = tlv_packet_read_u8(tlv, &subtype);
89 case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
90 case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
91 case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
93 r = tlv_packet_read_string(tlv, &s, length);
97 *data = (uint8_t *) s;
100 case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
102 r = tlv_packet_read_bytes(tlv, data, length);
115 (void)lldp_tlv_packet_exit_container(tlv);
121 int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
124 assert_return(tlv, -EINVAL);
126 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
130 r = tlv_packet_read_u16(tlv, ttl);
132 (void)lldp_tlv_packet_exit_container(tlv);
138 int lldp_read_system_name(tlv_packet *tlv,
144 assert_return(tlv, -EINVAL);
146 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
150 r = tlv_packet_read_string(tlv, &s, length);
157 (void)lldp_tlv_packet_exit_container(tlv);
162 int lldp_read_system_description(tlv_packet *tlv,
168 assert_return(tlv, -EINVAL);
170 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
174 r = tlv_packet_read_string(tlv, &s, length);
181 (void)lldp_tlv_packet_exit_container(tlv);
186 int lldp_read_port_description(tlv_packet *tlv,
192 assert_return(tlv, -EINVAL);
194 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
198 r = tlv_packet_read_string(tlv, &s, length);
205 (void)lldp_tlv_packet_exit_container(tlv);
210 int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
213 assert_return(tlv, -EINVAL);
215 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
219 r = tlv_packet_read_u16(tlv, data);
226 (void)lldp_tlv_packet_exit_container(tlv);
231 /* 10.5.5.2.2 mibUpdateObjects ()
232 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
233 * the TLVs contained in the received LLDPDU for the LLDP remote system
234 * indicated by the LLDP remote systems update process defined in 10.3.5 */
236 int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
237 lldp_neighbour_port *p;
238 uint16_t length, ttl;
243 assert_return(c, -EINVAL);
244 assert_return(tlv, -EINVAL);
246 r = lldp_read_port_id(tlv, &type, &length, &data);
250 /* Update the packet if we already have */
251 LIST_FOREACH(port, p, c->ports) {
253 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
255 r = lldp_read_ttl(tlv, &ttl);
259 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
261 tlv_packet_free(p->packet);
264 prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
273 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
274 lldp_neighbour_port *p, *q;
280 assert_return(c, -EINVAL);
281 assert_return(tlv, -EINVAL);
283 r = lldp_read_port_id(tlv, &type, &length, &data);
287 LIST_FOREACH_SAFE(port, p, q, c->ports) {
290 if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
291 lldp_neighbour_port_remove_and_free(p);
299 int lldp_mib_add_objects(Prioq *by_expiry,
300 Hashmap *neighbour_mib,
302 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
303 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
304 lldp_chassis_id chassis_id;
305 bool new_chassis = false;
306 uint8_t subtype, *data;
307 uint16_t ttl, length;
310 assert_return(by_expiry, -EINVAL);
311 assert_return(neighbour_mib, -EINVAL);
312 assert_return(tlv, -EINVAL);
314 r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
318 r = lldp_read_ttl(tlv, &ttl);
323 chassis_id.type = subtype;
324 chassis_id.length = length;
325 chassis_id.data = data;
327 /* Try to find the Chassis */
328 c = hashmap_get(neighbour_mib, &chassis_id);
331 /* Don't create chassis if ttl 0 is received . Silently drop it */
333 log_lldp("TTL value 0 received. Skiping Chassis creation.");
337 /* Admission Control: Can we store this packet ? */
338 if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
340 log_lldp("Exceeding number of chassie: %d. Dropping ...",
341 hashmap_size(neighbour_mib));
345 r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
351 r = hashmap_put(neighbour_mib, &c->chassis_id, c);
357 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
358 * system information associated with the LLDP agent/port is to be deleted */
360 log_lldp("TTL value 0 received . Deleting associated Port ...");
362 lldp_mib_remove_objects(c, tlv);
368 /* if we already have this port just update it */
369 r = lldp_mib_update_objects(c, tlv);
375 /* Admission Control: Can this port attached to the existing chassis ? */
376 if (REFCNT_GET(c->n_ref) >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
377 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...",
378 REFCNT_GET(c->n_ref));
385 /* This is a new port */
386 r = lldp_neighbour_port_new(c, tlv, &p);
390 r = prioq_put(c->by_expiry, p, &p->prioq_idx);
394 /* Attach new port to chassis */
395 LIST_PREPEND(port, c->ports, p);
396 REFCNT_INC(c->n_ref);
404 tlv_packet_free(tlv);
407 hashmap_remove(neighbour_mib, &c->chassis_id);
412 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
420 prioq_remove(c->by_expiry, p, &p->prioq_idx);
422 LIST_REMOVE(port, c->ports, p);
423 lldp_neighbour_port_free(p);
425 /* Drop the Chassis if no port is attached */
426 if (REFCNT_DEC(c->n_ref) <= 1) {
427 hashmap_remove(c->neighbour_mib, &c->chassis_id);
428 lldp_chassis_free(c);
432 void lldp_neighbour_port_free(lldp_neighbour_port *p) {
437 tlv_packet_free(p->packet);
443 int lldp_neighbour_port_new(lldp_chassis *c,
445 lldp_neighbour_port **ret) {
446 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p;
447 uint16_t length, ttl;
454 r = lldp_read_port_id(tlv, &type, &length, &data);
458 r = lldp_read_ttl(tlv, &ttl);
462 p = new0(lldp_neighbour_port, 1);
470 p->prioq_idx = PRIOQ_IDX_NULL;
471 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
473 p->data = memdup(data, length);
483 void lldp_chassis_free(lldp_chassis *c) {
488 if (REFCNT_GET(c->n_ref) > 1)
491 free(c->chassis_id.data);
495 int lldp_chassis_new(tlv_packet *tlv,
497 Hashmap *neighbour_mib,
498 lldp_chassis **ret) {
499 _cleanup_lldp_chassis_free_ lldp_chassis *c;
507 r = lldp_read_chassis_id(tlv, &type, &length, &data);
511 c = new0(lldp_chassis, 1);
515 c->n_ref = REFCNT_INIT;
516 c->chassis_id.type = type;
517 c->chassis_id.length = length;
519 c->chassis_id.data = memdup(data, length);
520 if (!c->chassis_id.data)
523 LIST_HEAD_INIT(c->ports);
525 c->by_expiry = by_expiry;
526 c->neighbour_mib = neighbour_mib;