chiark / gitweb /
test-lldp: initialize structs when we define them
[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         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 = -ENOTSUP;
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 (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));
380
381                         c = NULL;
382                         goto drop;
383                 }
384         }
385
386         /* This is a new port */
387         r = lldp_neighbour_port_new(c, tlv, &p);
388         if (r < 0)
389                 goto drop;
390
391         r = prioq_put(c->by_expiry, p, &p->prioq_idx);
392         if (r < 0)
393                 goto drop;
394
395         /* Attach new port to chassis */
396         LIST_PREPEND(port, c->ports, p);
397         REFCNT_INC(c->n_ref);
398
399         p = NULL;
400         c = NULL;
401
402         return 0;
403
404  drop:
405         tlv_packet_free(tlv);
406
407         if (new_chassis)
408                 hashmap_remove(neighbour_mib, &c->chassis_id);
409
410         return r;
411 }
412
413 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
414         lldp_chassis *c;
415
416         assert(p);
417         assert(p->c);
418
419         c = p->c;
420
421         prioq_remove(c->by_expiry, p, &p->prioq_idx);
422
423         LIST_REMOVE(port, c->ports, p);
424         lldp_neighbour_port_free(p);
425
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);
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 (REFCNT_GET(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 = REFCNT_INIT;
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 }