chiark / gitweb /
bus-proxy: make sure sure eavesdrop= XML attributes are properly handled
[elogind.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 = -ENOTSUP;
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
93                 r = tlv_packet_read_string(tlv, &s, length);
94                 if (r < 0)
95                         goto out1;
96
97                 *data = (uint8_t *) s;
98
99                 break;
100         case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
101
102                 r = tlv_packet_read_bytes(tlv, data, length);
103                 if (r < 0)
104                         goto out1;
105
106                 break;
107         default:
108                 r = -ENOTSUP;
109                 break;
110         }
111
112         *type = subtype;
113
114  out1:
115         (void)lldp_tlv_packet_exit_container(tlv);
116
117  out2:
118         return r;
119 }
120
121 int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
122         int r;
123
124         assert_return(tlv, -EINVAL);
125
126         r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
127         if (r < 0)
128                 goto out;
129
130         r = tlv_packet_read_u16(tlv, ttl);
131
132         (void)lldp_tlv_packet_exit_container(tlv);
133
134  out:
135         return r;
136 }
137
138 int lldp_read_system_name(tlv_packet *tlv,
139                           uint16_t *length,
140                           char **data) {
141         char *s;
142         int r;
143
144         assert_return(tlv, -EINVAL);
145
146         r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
147         if (r < 0)
148                 return r;
149
150         r = tlv_packet_read_string(tlv, &s, length);
151         if (r < 0)
152                 goto out;
153
154         *data = (char *) s;
155
156  out:
157         (void)lldp_tlv_packet_exit_container(tlv);
158
159         return r;
160 }
161
162 int lldp_read_system_description(tlv_packet *tlv,
163                                  uint16_t *length,
164                                  char **data) {
165         char *s;
166         int r;
167
168         assert_return(tlv, -EINVAL);
169
170         r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
171         if (r < 0)
172                 return r;
173
174         r = tlv_packet_read_string(tlv, &s, length);
175         if (r < 0)
176                 goto out;
177
178         *data = (char *) s;
179
180  out:
181         (void)lldp_tlv_packet_exit_container(tlv);
182
183         return r;
184 }
185
186 int lldp_read_port_description(tlv_packet *tlv,
187                                uint16_t *length,
188                                char **data) {
189         char *s;
190         int r;
191
192         assert_return(tlv, -EINVAL);
193
194         r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
195         if (r < 0)
196                 return r;
197
198         r = tlv_packet_read_string(tlv, &s, length);
199         if (r < 0)
200                 goto out;
201
202         *data = (char *) s;
203
204  out:
205         (void)lldp_tlv_packet_exit_container(tlv);
206
207         return r;
208 }
209
210 int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
211         int r;
212
213         assert_return(tlv, -EINVAL);
214
215         r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
216         if (r < 0)
217                 return r;
218
219         r = tlv_packet_read_u16(tlv, data);
220         if (r < 0)
221                 goto out;
222
223         return 0;
224  out:
225
226         (void)lldp_tlv_packet_exit_container(tlv);
227
228         return r;
229 }
230
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 */
235
236 int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
237         lldp_neighbour_port *p;
238         uint16_t length, ttl;
239         uint8_t *data;
240         uint8_t type;
241         int r;
242
243         assert_return(c, -EINVAL);
244         assert_return(tlv, -EINVAL);
245
246         r = lldp_read_port_id(tlv, &type, &length, &data);
247         if (r < 0)
248                 return r;
249
250         /* Update the packet if we already have */
251         LIST_FOREACH(port, p, c->ports) {
252
253                 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
254
255                         r = lldp_read_ttl(tlv, &ttl);
256                         if (r < 0)
257                                 return r;
258
259                         p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
260
261                         tlv_packet_free(p->packet);
262                         p->packet = tlv;
263
264                         prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
265
266                         return 0;
267                 }
268         }
269
270         return -1;
271 }
272
273 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
274         lldp_neighbour_port *p, *q;
275         uint8_t *data;
276         uint16_t length;
277         uint8_t type;
278         int r;
279
280         assert_return(c, -EINVAL);
281         assert_return(tlv, -EINVAL);
282
283         r = lldp_read_port_id(tlv, &type, &length, &data);
284         if (r < 0)
285                 return r;
286
287         LIST_FOREACH_SAFE(port, p, q, c->ports) {
288
289                 /* Find the port */
290                 if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
291                         lldp_neighbour_port_remove_and_free(p);
292                         break;
293                 }
294         }
295
296         return 0;
297 }
298
299 int lldp_mib_add_objects(Prioq *by_expiry,
300                          Hashmap *neighbour_mib,
301                          tlv_packet *tlv) {
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;
308         int r;
309
310         assert_return(by_expiry, -EINVAL);
311         assert_return(neighbour_mib, -EINVAL);
312         assert_return(tlv, -EINVAL);
313
314         r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
315         if (r < 0)
316                 goto drop;
317
318         r = lldp_read_ttl(tlv, &ttl);
319         if (r < 0)
320                 goto drop;
321
322         /* Make hash key */
323         chassis_id.type = subtype;
324         chassis_id.length = length;
325         chassis_id.data = data;
326
327         /* Try to find the Chassis */
328         c = hashmap_get(neighbour_mib, &chassis_id);
329         if (!c) {
330
331                 /* Don't create chassis if ttl 0 is received . Silently drop it */
332                 if (ttl == 0) {
333                         log_lldp("TTL value 0 received. Skiping Chassis creation.");
334                         goto drop;
335                 }
336
337                 /* Admission Control: Can we store this packet ? */
338                 if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
339
340                         log_lldp("Exceeding number of chassie: %d. Dropping ...",
341                                  hashmap_size(neighbour_mib));
342                         goto drop;
343                 }
344
345                 r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
346                 if (r < 0)
347                         goto drop;
348
349                 new_chassis = true;
350
351                 r = hashmap_put(neighbour_mib, &c->chassis_id, c);
352                 if (r < 0)
353                         goto drop;
354
355         } else {
356
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 */
359                 if (ttl == 0) {
360                         log_lldp("TTL value 0 received . Deleting associated Port ...");
361
362                         lldp_mib_remove_objects(c, tlv);
363
364                         c = NULL;
365                         goto drop;
366                 }
367
368                 /* if we already have this port just update it */
369                 r = lldp_mib_update_objects(c, tlv);
370                 if (r >= 0) {
371                         c = NULL;
372                         return r;
373                 }
374
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));
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         REFCNT_INC(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         if (REFCNT_DEC(c->n_ref) <= 1) {
427                 hashmap_remove(c->neighbour_mib, &c->chassis_id);
428                 lldp_chassis_free(c);
429         }
430 }
431
432 void lldp_neighbour_port_free(lldp_neighbour_port *p) {
433
434         if(!p)
435                 return;
436
437         tlv_packet_free(p->packet);
438
439         free(p->data);
440         free(p);
441 }
442
443 int lldp_neighbour_port_new(lldp_chassis *c,
444                             tlv_packet *tlv,
445                             lldp_neighbour_port **ret) {
446         _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
447         uint16_t length, ttl;
448         uint8_t *data;
449         uint8_t type;
450         int r;
451
452         assert(tlv);
453
454         r = lldp_read_port_id(tlv, &type, &length, &data);
455         if (r < 0)
456                 return r;
457
458         r = lldp_read_ttl(tlv, &ttl);
459         if (r < 0)
460                 return r;
461
462         p = new0(lldp_neighbour_port, 1);
463         if (!p)
464                 return -ENOMEM;
465
466         p->c = c;
467         p->type = type;
468         p->length = length;
469         p->packet = tlv;
470         p->prioq_idx = PRIOQ_IDX_NULL;
471         p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
472
473         p->data = memdup(data, length);
474         if (!p->data)
475                 return -ENOMEM;
476
477         *ret = p;
478         p = NULL;
479
480         return 0;
481 }
482
483 void lldp_chassis_free(lldp_chassis *c) {
484
485         if (!c)
486                 return;
487
488         if (REFCNT_GET(c->n_ref) > 1)
489                 return;
490
491         free(c->chassis_id.data);
492         free(c);
493 }
494
495 int lldp_chassis_new(tlv_packet *tlv,
496                      Prioq *by_expiry,
497                      Hashmap *neighbour_mib,
498                      lldp_chassis **ret) {
499         _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
500         uint16_t length;
501         uint8_t *data;
502         uint8_t type;
503         int r;
504
505         assert(tlv);
506
507         r = lldp_read_chassis_id(tlv, &type, &length, &data);
508         if (r < 0)
509                 return r;
510
511         c = new0(lldp_chassis, 1);
512         if (!c)
513                 return -ENOMEM;
514
515         c->n_ref = REFCNT_INIT;
516         c->chassis_id.type = type;
517         c->chassis_id.length = length;
518
519         c->chassis_id.data = memdup(data, length);
520         if (!c->chassis_id.data)
521                 return -ENOMEM;
522
523         LIST_HEAD_INIT(c->ports);
524
525         c->by_expiry = by_expiry;
526         c->neighbour_mib = neighbour_mib;
527
528         *ret = c;
529         c = NULL;
530
531         return 0;
532 }