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:
92 case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
94 r = tlv_packet_read_string(tlv, &s, length);
98 *data = (uint8_t *) s;
101 case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
103 r = tlv_packet_read_bytes(tlv, data, length);
116 (void) lldp_tlv_packet_exit_container(tlv);
122 int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
125 assert_return(tlv, -EINVAL);
127 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
131 r = tlv_packet_read_u16(tlv, ttl);
133 (void) lldp_tlv_packet_exit_container(tlv);
139 int lldp_read_system_name(tlv_packet *tlv,
145 assert_return(tlv, -EINVAL);
147 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
151 r = tlv_packet_read_string(tlv, &s, length);
158 (void) lldp_tlv_packet_exit_container(tlv);
163 int lldp_read_system_description(tlv_packet *tlv,
169 assert_return(tlv, -EINVAL);
171 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
175 r = tlv_packet_read_string(tlv, &s, length);
182 (void) lldp_tlv_packet_exit_container(tlv);
187 int lldp_read_port_description(tlv_packet *tlv,
193 assert_return(tlv, -EINVAL);
195 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
199 r = tlv_packet_read_string(tlv, &s, length);
206 (void) lldp_tlv_packet_exit_container(tlv);
211 int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
214 assert_return(tlv, -EINVAL);
216 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
220 r = tlv_packet_read_u16(tlv, data);
227 (void) lldp_tlv_packet_exit_container(tlv);
232 /* 10.5.5.2.2 mibUpdateObjects ()
233 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
234 * the TLVs contained in the received LLDPDU for the LLDP remote system
235 * indicated by the LLDP remote systems update process defined in 10.3.5 */
237 int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
238 lldp_neighbour_port *p;
239 uint16_t length, ttl;
244 assert_return(c, -EINVAL);
245 assert_return(tlv, -EINVAL);
247 r = lldp_read_port_id(tlv, &type, &length, &data);
251 /* Update the packet if we already have */
252 LIST_FOREACH(port, p, c->ports) {
254 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
256 r = lldp_read_ttl(tlv, &ttl);
260 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
262 tlv_packet_free(p->packet);
265 prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
274 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
275 lldp_neighbour_port *p, *q;
281 assert_return(c, -EINVAL);
282 assert_return(tlv, -EINVAL);
284 r = lldp_read_port_id(tlv, &type, &length, &data);
288 LIST_FOREACH_SAFE(port, p, q, c->ports) {
291 if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
292 lldp_neighbour_port_remove_and_free(p);
300 int lldp_mib_add_objects(Prioq *by_expiry,
301 Hashmap *neighbour_mib,
303 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
304 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
305 lldp_chassis_id chassis_id;
306 bool new_chassis = false;
307 uint8_t subtype, *data;
308 uint16_t ttl, length;
311 assert_return(by_expiry, -EINVAL);
312 assert_return(neighbour_mib, -EINVAL);
313 assert_return(tlv, -EINVAL);
315 r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
319 r = lldp_read_ttl(tlv, &ttl);
324 chassis_id.type = subtype;
325 chassis_id.length = length;
326 chassis_id.data = data;
328 /* Try to find the Chassis */
329 c = hashmap_get(neighbour_mib, &chassis_id);
332 /* Don't create chassis if ttl 0 is received . Silently drop it */
334 log_lldp("TTL value 0 received. Skiping Chassis creation.");
338 /* Admission Control: Can we store this packet ? */
339 if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
341 log_lldp("Exceeding number of chassie: %d. Dropping ...",
342 hashmap_size(neighbour_mib));
346 r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
352 r = hashmap_put(neighbour_mib, &c->chassis_id, c);
358 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
359 * system information associated with the LLDP agent/port is to be deleted */
361 log_lldp("TTL value 0 received . Deleting associated Port ...");
363 lldp_mib_remove_objects(c, tlv);
369 /* if we already have this port just update it */
370 r = lldp_mib_update_objects(c, tlv);
376 /* Admission Control: Can this port attached to the existing chassis ? */
377 if (REFCNT_GET(c->n_ref) >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
378 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...",
379 REFCNT_GET(c->n_ref));
386 /* This is a new port */
387 r = lldp_neighbour_port_new(c, tlv, &p);
391 r = prioq_put(c->by_expiry, p, &p->prioq_idx);
395 /* Attach new port to chassis */
396 LIST_PREPEND(port, c->ports, p);
397 REFCNT_INC(c->n_ref);
405 tlv_packet_free(tlv);
408 hashmap_remove(neighbour_mib, &c->chassis_id);
413 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
421 prioq_remove(c->by_expiry, p, &p->prioq_idx);
423 LIST_REMOVE(port, c->ports, p);
424 lldp_neighbour_port_free(p);
426 /* Drop the Chassis if no port is attached */
427 if (REFCNT_DEC(c->n_ref) <= 1) {
428 hashmap_remove(c->neighbour_mib, &c->chassis_id);
429 lldp_chassis_free(c);
433 void lldp_neighbour_port_free(lldp_neighbour_port *p) {
438 tlv_packet_free(p->packet);
444 int lldp_neighbour_port_new(lldp_chassis *c,
446 lldp_neighbour_port **ret) {
447 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
448 uint16_t length, ttl;
455 r = lldp_read_port_id(tlv, &type, &length, &data);
459 r = lldp_read_ttl(tlv, &ttl);
463 p = new0(lldp_neighbour_port, 1);
471 p->prioq_idx = PRIOQ_IDX_NULL;
472 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
474 p->data = memdup(data, length);
484 void lldp_chassis_free(lldp_chassis *c) {
489 if (REFCNT_GET(c->n_ref) > 1)
492 free(c->chassis_id.data);
496 int lldp_chassis_new(tlv_packet *tlv,
498 Hashmap *neighbour_mib,
499 lldp_chassis **ret) {
500 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
508 r = lldp_read_chassis_id(tlv, &type, &length, &data);
512 c = new0(lldp_chassis, 1);
516 c->n_ref = REFCNT_INIT;
517 c->chassis_id.type = type;
518 c->chassis_id.length = length;
520 c->chassis_id.data = memdup(data, length);
521 if (!c->chassis_id.data)
524 LIST_HEAD_INIT(c->ports);
526 c->by_expiry = by_expiry;
527 c->neighbour_mib = neighbour_mib;