]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/lldp-internal.c
Merge pull request #1695 from evverx/fix-cap-bounding-merging
[thirdparty/systemd.git] / src / libsystemd-network / lldp-internal.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 2014 Tom Gundersen
7 Copyright (C) 2014 Susant Sahani
8
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.
13
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.
18
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/>.
21 ***/
22
23 #include "sd-lldp.h"
24
25 #include "alloc-util.h"
26 #include "lldp-internal.h"
27
28 /* We store maximum 1K chassis entries */
29 #define LLDP_MIB_MAX_CHASSIS 1024
30
31 /* Maximum Ports can be attached to any chassis */
32 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
33
34 /* 10.5.5.2.2 mibUpdateObjects ()
35 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
36 * the TLVs contained in the received LLDPDU for the LLDP remote system
37 * indicated by the LLDP remote systems update process defined in 10.3.5 */
38
39 int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
40 lldp_neighbour_port *p;
41 uint16_t length, ttl;
42 uint8_t *data;
43 uint8_t type;
44 int r;
45
46 assert_return(c, -EINVAL);
47 assert_return(tlv, -EINVAL);
48
49 r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
50 if (r < 0)
51 return r;
52
53 /* Update the packet if we already have */
54 LIST_FOREACH(port, p, c->ports) {
55
56 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
57
58 r = sd_lldp_packet_read_ttl(tlv, &ttl);
59 if (r < 0)
60 return r;
61
62 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
63
64 sd_lldp_packet_unref(p->packet);
65 p->packet = tlv;
66
67 prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
68
69 return 0;
70 }
71 }
72
73 return -1;
74 }
75
76 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
77 lldp_neighbour_port *p, *q;
78 uint8_t *data;
79 uint16_t length;
80 uint8_t type;
81 int r;
82
83 assert_return(c, -EINVAL);
84 assert_return(tlv, -EINVAL);
85
86 r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
87 if (r < 0)
88 return r;
89
90 LIST_FOREACH_SAFE(port, p, q, c->ports) {
91
92 /* Find the port */
93 if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
94 lldp_neighbour_port_remove_and_free(p);
95 break;
96 }
97 }
98
99 return 0;
100 }
101
102 int lldp_mib_add_objects(Prioq *by_expiry,
103 Hashmap *neighbour_mib,
104 tlv_packet *tlv) {
105 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
106 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
107 lldp_chassis_id chassis_id;
108 bool new_chassis = false;
109 uint8_t subtype, *data;
110 uint16_t ttl, length;
111 int r;
112
113 assert_return(by_expiry, -EINVAL);
114 assert_return(neighbour_mib, -EINVAL);
115 assert_return(tlv, -EINVAL);
116
117 r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
118 if (r < 0)
119 goto drop;
120
121 r = sd_lldp_packet_read_ttl(tlv, &ttl);
122 if (r < 0)
123 goto drop;
124
125 /* Make hash key */
126 chassis_id.type = subtype;
127 chassis_id.length = length;
128 chassis_id.data = data;
129
130 /* Try to find the Chassis */
131 c = hashmap_get(neighbour_mib, &chassis_id);
132 if (!c) {
133
134 /* Don't create chassis if ttl 0 is received . Silently drop it */
135 if (ttl == 0) {
136 log_lldp("TTL value 0 received. Skiping Chassis creation.");
137 goto drop;
138 }
139
140 /* Admission Control: Can we store this packet ? */
141 if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
142
143 log_lldp("Exceeding number of chassie: %d. Dropping ...",
144 hashmap_size(neighbour_mib));
145 goto drop;
146 }
147
148 r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
149 if (r < 0)
150 goto drop;
151
152 new_chassis = true;
153
154 r = hashmap_put(neighbour_mib, &c->chassis_id, c);
155 if (r < 0)
156 goto drop;
157
158 } else {
159
160 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
161 * system information associated with the LLDP agent/port is to be deleted */
162 if (ttl == 0) {
163 log_lldp("TTL value 0 received . Deleting associated Port ...");
164
165 lldp_mib_remove_objects(c, tlv);
166
167 c = NULL;
168 goto drop;
169 }
170
171 /* if we already have this port just update it */
172 r = lldp_mib_update_objects(c, tlv);
173 if (r >= 0) {
174 c = NULL;
175 return r;
176 }
177
178 /* Admission Control: Can this port attached to the existing chassis ? */
179 if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
180 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
181
182 c = NULL;
183 goto drop;
184 }
185 }
186
187 /* This is a new port */
188 r = lldp_neighbour_port_new(c, tlv, &p);
189 if (r < 0)
190 goto drop;
191
192 r = prioq_put(c->by_expiry, p, &p->prioq_idx);
193 if (r < 0)
194 goto drop;
195
196 /* Attach new port to chassis */
197 LIST_PREPEND(port, c->ports, p);
198 c->n_ref ++;
199
200 p = NULL;
201 c = NULL;
202
203 return 0;
204
205 drop:
206 sd_lldp_packet_unref(tlv);
207
208 if (new_chassis)
209 hashmap_remove(neighbour_mib, &c->chassis_id);
210
211 return r;
212 }
213
214 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
215 lldp_chassis *c;
216
217 assert(p);
218 assert(p->c);
219
220 c = p->c;
221
222 prioq_remove(c->by_expiry, p, &p->prioq_idx);
223
224 LIST_REMOVE(port, c->ports, p);
225 lldp_neighbour_port_free(p);
226
227 /* Drop the Chassis if no port is attached */
228 c->n_ref --;
229 if (c->n_ref <= 1) {
230 hashmap_remove(c->neighbour_mib, &c->chassis_id);
231 lldp_chassis_free(c);
232 }
233 }
234
235 void lldp_neighbour_port_free(lldp_neighbour_port *p) {
236
237 if(!p)
238 return;
239
240 sd_lldp_packet_unref(p->packet);
241
242 free(p->data);
243 free(p);
244 }
245
246 int lldp_neighbour_port_new(lldp_chassis *c,
247 tlv_packet *tlv,
248 lldp_neighbour_port **ret) {
249 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
250 uint16_t length, ttl;
251 uint8_t *data;
252 uint8_t type;
253 int r;
254
255 assert(tlv);
256
257 r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
258 if (r < 0)
259 return r;
260
261 r = sd_lldp_packet_read_ttl(tlv, &ttl);
262 if (r < 0)
263 return r;
264
265 p = new0(lldp_neighbour_port, 1);
266 if (!p)
267 return -ENOMEM;
268
269 p->c = c;
270 p->type = type;
271 p->length = length;
272 p->packet = tlv;
273 p->prioq_idx = PRIOQ_IDX_NULL;
274 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
275
276 p->data = memdup(data, length);
277 if (!p->data)
278 return -ENOMEM;
279
280 *ret = p;
281 p = NULL;
282
283 return 0;
284 }
285
286 void lldp_chassis_free(lldp_chassis *c) {
287
288 if (!c)
289 return;
290
291 if (c->n_ref > 1)
292 return;
293
294 free(c->chassis_id.data);
295 free(c);
296 }
297
298 int lldp_chassis_new(tlv_packet *tlv,
299 Prioq *by_expiry,
300 Hashmap *neighbour_mib,
301 lldp_chassis **ret) {
302 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
303 uint16_t length;
304 uint8_t *data;
305 uint8_t type;
306 int r;
307
308 assert(tlv);
309
310 r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
311 if (r < 0)
312 return r;
313
314 c = new0(lldp_chassis, 1);
315 if (!c)
316 return -ENOMEM;
317
318 c->n_ref = 1;
319 c->chassis_id.type = type;
320 c->chassis_id.length = length;
321
322 c->chassis_id.data = memdup(data, length);
323 if (!c->chassis_id.data)
324 return -ENOMEM;
325
326 LIST_HEAD_INIT(c->ports);
327
328 c->by_expiry = by_expiry;
329 c->neighbour_mib = neighbour_mib;
330
331 *ret = c;
332 c = NULL;
333
334 return 0;
335 }
336
337 int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
338 _cleanup_lldp_packet_unref_ tlv_packet *packet = NULL;
339 tlv_packet *p;
340 uint16_t length;
341 int r;
342
343 assert(fd);
344 assert(userdata);
345
346 r = tlv_packet_new(&packet);
347 if (r < 0)
348 return r;
349
350 length = read(fd, &packet->pdu, sizeof(packet->pdu));
351
352 /* Silently drop the packet */
353 if ((size_t) length > ETHER_MAX_LEN)
354 return 0;
355
356 packet->userdata = userdata;
357
358 p = packet;
359 packet = NULL;
360
361 return lldp_handle_packet(p, (uint16_t) length);
362 }