]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/lldp-internal.c
Merge pull request #1055 from poettering/dhcp-updates
[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 "lldp-internal.h"
24
25 /* We store maximum 1K chassis entries */
26 #define LLDP_MIB_MAX_CHASSIS 1024
27
28 /* Maximum Ports can be attached to any chassis */
29 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
30
31 int lldp_read_chassis_id(tlv_packet *tlv,
32 uint8_t *type,
33 uint16_t *length,
34 uint8_t **data) {
35 uint8_t subtype;
36 int r;
37
38 assert_return(tlv, -EINVAL);
39
40 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
41 if (r < 0)
42 goto out2;
43
44 r = tlv_packet_read_u8(tlv, &subtype);
45 if (r < 0)
46 goto out1;
47
48 switch (subtype) {
49 case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
50
51 r = tlv_packet_read_bytes(tlv, data, length);
52 if (r < 0)
53 goto out1;
54
55 break;
56 default:
57 r = -EOPNOTSUPP;
58 break;
59 }
60
61 *type = subtype;
62
63 out1:
64 (void) lldp_tlv_packet_exit_container(tlv);
65
66 out2:
67 return r;
68 }
69
70 int lldp_read_port_id(tlv_packet *tlv,
71 uint8_t *type,
72 uint16_t *length,
73 uint8_t **data) {
74 uint8_t subtype;
75 char *s;
76 int r;
77
78 assert_return(tlv, -EINVAL);
79
80 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
81 if (r < 0)
82 goto out2;
83
84 r = tlv_packet_read_u8(tlv, &subtype);
85 if (r < 0)
86 goto out1;
87
88 switch (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:
93
94 r = tlv_packet_read_string(tlv, &s, length);
95 if (r < 0)
96 goto out1;
97
98 *data = (uint8_t *) s;
99
100 break;
101 case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
102
103 r = tlv_packet_read_bytes(tlv, data, length);
104 if (r < 0)
105 goto out1;
106
107 break;
108 default:
109 r = -EOPNOTSUPP;
110 break;
111 }
112
113 *type = subtype;
114
115 out1:
116 (void) lldp_tlv_packet_exit_container(tlv);
117
118 out2:
119 return r;
120 }
121
122 int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
123 int r;
124
125 assert_return(tlv, -EINVAL);
126
127 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
128 if (r < 0)
129 goto out;
130
131 r = tlv_packet_read_u16(tlv, ttl);
132
133 (void) lldp_tlv_packet_exit_container(tlv);
134
135 out:
136 return r;
137 }
138
139 int lldp_read_system_name(tlv_packet *tlv,
140 uint16_t *length,
141 char **data) {
142 char *s;
143 int r;
144
145 assert_return(tlv, -EINVAL);
146
147 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
148 if (r < 0)
149 return r;
150
151 r = tlv_packet_read_string(tlv, &s, length);
152 if (r < 0)
153 goto out;
154
155 *data = (char *) s;
156
157 out:
158 (void) lldp_tlv_packet_exit_container(tlv);
159
160 return r;
161 }
162
163 int lldp_read_system_description(tlv_packet *tlv,
164 uint16_t *length,
165 char **data) {
166 char *s;
167 int r;
168
169 assert_return(tlv, -EINVAL);
170
171 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
172 if (r < 0)
173 return r;
174
175 r = tlv_packet_read_string(tlv, &s, length);
176 if (r < 0)
177 goto out;
178
179 *data = (char *) s;
180
181 out:
182 (void) lldp_tlv_packet_exit_container(tlv);
183
184 return r;
185 }
186
187 int lldp_read_port_description(tlv_packet *tlv,
188 uint16_t *length,
189 char **data) {
190 char *s;
191 int r;
192
193 assert_return(tlv, -EINVAL);
194
195 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
196 if (r < 0)
197 return r;
198
199 r = tlv_packet_read_string(tlv, &s, length);
200 if (r < 0)
201 goto out;
202
203 *data = (char *) s;
204
205 out:
206 (void) lldp_tlv_packet_exit_container(tlv);
207
208 return r;
209 }
210
211 int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
212 int r;
213
214 assert_return(tlv, -EINVAL);
215
216 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
217 if (r < 0)
218 return r;
219
220 r = tlv_packet_read_u16(tlv, data);
221 if (r < 0)
222 goto out;
223
224 return 0;
225 out:
226
227 (void) lldp_tlv_packet_exit_container(tlv);
228
229 return r;
230 }
231
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 */
236
237 int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
238 lldp_neighbour_port *p;
239 uint16_t length, ttl;
240 uint8_t *data;
241 uint8_t type;
242 int r;
243
244 assert_return(c, -EINVAL);
245 assert_return(tlv, -EINVAL);
246
247 r = lldp_read_port_id(tlv, &type, &length, &data);
248 if (r < 0)
249 return r;
250
251 /* Update the packet if we already have */
252 LIST_FOREACH(port, p, c->ports) {
253
254 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
255
256 r = lldp_read_ttl(tlv, &ttl);
257 if (r < 0)
258 return r;
259
260 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
261
262 tlv_packet_free(p->packet);
263 p->packet = tlv;
264
265 prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
266
267 return 0;
268 }
269 }
270
271 return -1;
272 }
273
274 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
275 lldp_neighbour_port *p, *q;
276 uint8_t *data;
277 uint16_t length;
278 uint8_t type;
279 int r;
280
281 assert_return(c, -EINVAL);
282 assert_return(tlv, -EINVAL);
283
284 r = lldp_read_port_id(tlv, &type, &length, &data);
285 if (r < 0)
286 return r;
287
288 LIST_FOREACH_SAFE(port, p, q, c->ports) {
289
290 /* Find the port */
291 if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
292 lldp_neighbour_port_remove_and_free(p);
293 break;
294 }
295 }
296
297 return 0;
298 }
299
300 int lldp_mib_add_objects(Prioq *by_expiry,
301 Hashmap *neighbour_mib,
302 tlv_packet *tlv) {
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;
309 int r;
310
311 assert_return(by_expiry, -EINVAL);
312 assert_return(neighbour_mib, -EINVAL);
313 assert_return(tlv, -EINVAL);
314
315 r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
316 if (r < 0)
317 goto drop;
318
319 r = lldp_read_ttl(tlv, &ttl);
320 if (r < 0)
321 goto drop;
322
323 /* Make hash key */
324 chassis_id.type = subtype;
325 chassis_id.length = length;
326 chassis_id.data = data;
327
328 /* Try to find the Chassis */
329 c = hashmap_get(neighbour_mib, &chassis_id);
330 if (!c) {
331
332 /* Don't create chassis if ttl 0 is received . Silently drop it */
333 if (ttl == 0) {
334 log_lldp("TTL value 0 received. Skiping Chassis creation.");
335 goto drop;
336 }
337
338 /* Admission Control: Can we store this packet ? */
339 if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
340
341 log_lldp("Exceeding number of chassie: %d. Dropping ...",
342 hashmap_size(neighbour_mib));
343 goto drop;
344 }
345
346 r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
347 if (r < 0)
348 goto drop;
349
350 new_chassis = true;
351
352 r = hashmap_put(neighbour_mib, &c->chassis_id, c);
353 if (r < 0)
354 goto drop;
355
356 } else {
357
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 */
360 if (ttl == 0) {
361 log_lldp("TTL value 0 received . Deleting associated Port ...");
362
363 lldp_mib_remove_objects(c, tlv);
364
365 c = NULL;
366 goto drop;
367 }
368
369 /* if we already have this port just update it */
370 r = lldp_mib_update_objects(c, tlv);
371 if (r >= 0) {
372 c = NULL;
373 return r;
374 }
375
376 /* Admission Control: Can this port attached to the existing chassis ? */
377 if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
378 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
379
380 c = NULL;
381 goto drop;
382 }
383 }
384
385 /* This is a new port */
386 r = lldp_neighbour_port_new(c, tlv, &p);
387 if (r < 0)
388 goto drop;
389
390 r = prioq_put(c->by_expiry, p, &p->prioq_idx);
391 if (r < 0)
392 goto drop;
393
394 /* Attach new port to chassis */
395 LIST_PREPEND(port, c->ports, p);
396 c->n_ref ++;
397
398 p = NULL;
399 c = NULL;
400
401 return 0;
402
403 drop:
404 tlv_packet_free(tlv);
405
406 if (new_chassis)
407 hashmap_remove(neighbour_mib, &c->chassis_id);
408
409 return r;
410 }
411
412 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
413 lldp_chassis *c;
414
415 assert(p);
416 assert(p->c);
417
418 c = p->c;
419
420 prioq_remove(c->by_expiry, p, &p->prioq_idx);
421
422 LIST_REMOVE(port, c->ports, p);
423 lldp_neighbour_port_free(p);
424
425 /* Drop the Chassis if no port is attached */
426 c->n_ref --;
427 if (c->n_ref <= 1) {
428 hashmap_remove(c->neighbour_mib, &c->chassis_id);
429 lldp_chassis_free(c);
430 }
431 }
432
433 void lldp_neighbour_port_free(lldp_neighbour_port *p) {
434
435 if(!p)
436 return;
437
438 tlv_packet_free(p->packet);
439
440 free(p->data);
441 free(p);
442 }
443
444 int lldp_neighbour_port_new(lldp_chassis *c,
445 tlv_packet *tlv,
446 lldp_neighbour_port **ret) {
447 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
448 uint16_t length, ttl;
449 uint8_t *data;
450 uint8_t type;
451 int r;
452
453 assert(tlv);
454
455 r = lldp_read_port_id(tlv, &type, &length, &data);
456 if (r < 0)
457 return r;
458
459 r = lldp_read_ttl(tlv, &ttl);
460 if (r < 0)
461 return r;
462
463 p = new0(lldp_neighbour_port, 1);
464 if (!p)
465 return -ENOMEM;
466
467 p->c = c;
468 p->type = type;
469 p->length = length;
470 p->packet = tlv;
471 p->prioq_idx = PRIOQ_IDX_NULL;
472 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
473
474 p->data = memdup(data, length);
475 if (!p->data)
476 return -ENOMEM;
477
478 *ret = p;
479 p = NULL;
480
481 return 0;
482 }
483
484 void lldp_chassis_free(lldp_chassis *c) {
485
486 if (!c)
487 return;
488
489 if (c->n_ref > 1)
490 return;
491
492 free(c->chassis_id.data);
493 free(c);
494 }
495
496 int lldp_chassis_new(tlv_packet *tlv,
497 Prioq *by_expiry,
498 Hashmap *neighbour_mib,
499 lldp_chassis **ret) {
500 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
501 uint16_t length;
502 uint8_t *data;
503 uint8_t type;
504 int r;
505
506 assert(tlv);
507
508 r = lldp_read_chassis_id(tlv, &type, &length, &data);
509 if (r < 0)
510 return r;
511
512 c = new0(lldp_chassis, 1);
513 if (!c)
514 return -ENOMEM;
515
516 c->n_ref = 1;
517 c->chassis_id.type = type;
518 c->chassis_id.length = length;
519
520 c->chassis_id.data = memdup(data, length);
521 if (!c->chassis_id.data)
522 return -ENOMEM;
523
524 LIST_HEAD_INIT(c->ports);
525
526 c->by_expiry = by_expiry;
527 c->neighbour_mib = neighbour_mib;
528
529 *ret = c;
530 c = NULL;
531
532 return 0;
533 }