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"
26 /* We store maximum 1K chassis entries */
27 #define LLDP_MIB_MAX_CHASSIS 1024
29 /* Maximum Ports can be attached to any chassis */
30 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
32 /* 10.5.5.2.2 mibUpdateObjects ()
33 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
34 * the TLVs contained in the received LLDPDU for the LLDP remote system
35 * indicated by the LLDP remote systems update process defined in 10.3.5 */
37 int lldp_mib_update_objects(lldp_chassis
*c
, tlv_packet
*tlv
) {
38 lldp_neighbour_port
*p
;
44 assert_return(c
, -EINVAL
);
45 assert_return(tlv
, -EINVAL
);
47 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
51 /* Update the packet if we already have */
52 LIST_FOREACH(port
, p
, c
->ports
) {
54 if ((p
->type
== type
&& p
->length
== length
&& !memcmp(p
->data
, data
, p
->length
))) {
56 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
60 p
->until
= ttl
* USEC_PER_SEC
+ now(clock_boottime_or_monotonic());
62 sd_lldp_packet_unref(p
->packet
);
65 prioq_reshuffle(p
->c
->by_expiry
, p
, &p
->prioq_idx
);
74 int lldp_mib_remove_objects(lldp_chassis
*c
, tlv_packet
*tlv
) {
75 lldp_neighbour_port
*p
, *q
;
81 assert_return(c
, -EINVAL
);
82 assert_return(tlv
, -EINVAL
);
84 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
88 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
91 if (p
->type
== type
&& p
->length
== length
&& !memcmp(p
->data
, data
, p
->length
)) {
92 lldp_neighbour_port_remove_and_free(p
);
100 int lldp_mib_add_objects(Prioq
*by_expiry
,
101 Hashmap
*neighbour_mib
,
103 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port
*p
= NULL
;
104 _cleanup_lldp_chassis_free_ lldp_chassis
*c
= NULL
;
105 lldp_chassis_id chassis_id
;
106 bool new_chassis
= false;
107 uint8_t subtype
, *data
;
108 uint16_t ttl
, length
;
111 assert_return(by_expiry
, -EINVAL
);
112 assert_return(neighbour_mib
, -EINVAL
);
113 assert_return(tlv
, -EINVAL
);
115 r
= sd_lldp_packet_read_chassis_id(tlv
, &subtype
, &data
, &length
);
119 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
124 chassis_id
.type
= subtype
;
125 chassis_id
.length
= length
;
126 chassis_id
.data
= data
;
128 /* Try to find the Chassis */
129 c
= hashmap_get(neighbour_mib
, &chassis_id
);
132 /* Don't create chassis if ttl 0 is received . Silently drop it */
134 log_lldp("TTL value 0 received. Skiping Chassis creation.");
138 /* Admission Control: Can we store this packet ? */
139 if (hashmap_size(neighbour_mib
) >= LLDP_MIB_MAX_CHASSIS
) {
141 log_lldp("Exceeding number of chassie: %d. Dropping ...",
142 hashmap_size(neighbour_mib
));
146 r
= lldp_chassis_new(tlv
, by_expiry
, neighbour_mib
, &c
);
152 r
= hashmap_put(neighbour_mib
, &c
->chassis_id
, c
);
158 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
159 * system information associated with the LLDP agent/port is to be deleted */
161 log_lldp("TTL value 0 received . Deleting associated Port ...");
163 lldp_mib_remove_objects(c
, tlv
);
169 /* if we already have this port just update it */
170 r
= lldp_mib_update_objects(c
, tlv
);
176 /* Admission Control: Can this port attached to the existing chassis ? */
177 if (c
->n_ref
>= LLDP_MIB_MAX_PORT_PER_CHASSIS
) {
178 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c
->n_ref
);
185 /* This is a new port */
186 r
= lldp_neighbour_port_new(c
, tlv
, &p
);
190 r
= prioq_put(c
->by_expiry
, p
, &p
->prioq_idx
);
194 /* Attach new port to chassis */
195 LIST_PREPEND(port
, c
->ports
, p
);
204 sd_lldp_packet_unref(tlv
);
207 hashmap_remove(neighbour_mib
, &c
->chassis_id
);
212 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port
*p
) {
220 prioq_remove(c
->by_expiry
, p
, &p
->prioq_idx
);
222 LIST_REMOVE(port
, c
->ports
, p
);
223 lldp_neighbour_port_free(p
);
225 /* Drop the Chassis if no port is attached */
228 hashmap_remove(c
->neighbour_mib
, &c
->chassis_id
);
229 lldp_chassis_free(c
);
233 void lldp_neighbour_port_free(lldp_neighbour_port
*p
) {
238 sd_lldp_packet_unref(p
->packet
);
244 int lldp_neighbour_port_new(lldp_chassis
*c
,
246 lldp_neighbour_port
**ret
) {
247 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port
*p
= NULL
;
248 uint16_t length
, ttl
;
255 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
259 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
263 p
= new0(lldp_neighbour_port
, 1);
271 p
->prioq_idx
= PRIOQ_IDX_NULL
;
272 p
->until
= ttl
* USEC_PER_SEC
+ now(clock_boottime_or_monotonic());
274 p
->data
= memdup(data
, length
);
284 void lldp_chassis_free(lldp_chassis
*c
) {
292 free(c
->chassis_id
.data
);
296 int lldp_chassis_new(tlv_packet
*tlv
,
298 Hashmap
*neighbour_mib
,
299 lldp_chassis
**ret
) {
300 _cleanup_lldp_chassis_free_ lldp_chassis
*c
= NULL
;
308 r
= sd_lldp_packet_read_chassis_id(tlv
, &type
, &data
, &length
);
312 c
= new0(lldp_chassis
, 1);
317 c
->chassis_id
.type
= type
;
318 c
->chassis_id
.length
= length
;
320 c
->chassis_id
.data
= memdup(data
, length
);
321 if (!c
->chassis_id
.data
)
324 LIST_HEAD_INIT(c
->ports
);
326 c
->by_expiry
= by_expiry
;
327 c
->neighbour_mib
= neighbour_mib
;
335 int lldp_receive_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
336 _cleanup_lldp_packet_unref_ tlv_packet
*packet
= NULL
;
344 r
= tlv_packet_new(&packet
);
348 length
= read(fd
, &packet
->pdu
, sizeof(packet
->pdu
));
350 /* Silently drop the packet */
351 if ((size_t) length
> ETHER_MAX_LEN
)
354 packet
->userdata
= userdata
;
359 return lldp_handle_packet(p
, (uint16_t) length
);