]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/macsec.c
network: explicitly clear security key for macsec
[thirdparty/systemd.git] / src / network / netdev / macsec.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <arpa/inet.h>
4 #include <linux/if_ether.h>
5 #include <linux/if_macsec.h>
6 #include <linux/genetlink.h>
7
8 #include "conf-parser.h"
9 #include "hashmap.h"
10 #include "hexdecoct.h"
11 #include "macsec.h"
12 #include "memory-util.h"
13 #include "missing.h"
14 #include "netlink-util.h"
15 #include "network-internal.h"
16 #include "networkd-address.h"
17 #include "networkd-manager.h"
18 #include "sd-netlink.h"
19 #include "socket-util.h"
20 #include "string-table.h"
21 #include "string-util.h"
22 #include "util.h"
23
24 static void security_association_clear(SecurityAssociation *sa) {
25 if (!sa)
26 return;
27
28 explicit_bzero_safe(sa->key, sa->key_len);
29 free(sa->key);
30 }
31
32 static void macsec_receive_association_free(ReceiveAssociation *c) {
33 if (!c)
34 return;
35
36 if (c->macsec && c->section)
37 ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section);
38
39 network_config_section_free(c->section);
40 security_association_clear(&c->sa);
41
42 free(c);
43 }
44
45 DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
46
47 static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) {
48 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
49 _cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL;
50 int r;
51
52 assert(s);
53 assert(ret);
54 assert(filename);
55 assert(section_line > 0);
56
57 r = network_config_section_new(filename, section_line, &n);
58 if (r < 0)
59 return r;
60
61 c = ordered_hashmap_get(s->receive_associations_by_section, n);
62 if (c) {
63 *ret = TAKE_PTR(c);
64 return 0;
65 }
66
67 c = new(ReceiveAssociation, 1);
68 if (!c)
69 return -ENOMEM;
70
71 *c = (ReceiveAssociation) {
72 .macsec = s,
73 .section = TAKE_PTR(n),
74 };
75
76 r = ordered_hashmap_ensure_allocated(&s->receive_associations_by_section, &network_config_hash_ops);
77 if (r < 0)
78 return r;
79
80 r = ordered_hashmap_put(s->receive_associations_by_section, c->section, c);
81 if (r < 0)
82 return r;
83
84 *ret = TAKE_PTR(c);
85
86 return 0;
87 }
88
89 static void macsec_receive_channel_free(ReceiveChannel *c) {
90 if (!c)
91 return;
92
93 if (c->macsec) {
94 if (c->sci.as_uint64 > 0)
95 ordered_hashmap_remove(c->macsec->receive_channels, &c->sci.as_uint64);
96
97 if (c->section)
98 ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section);
99 }
100
101 network_config_section_free(c->section);
102
103 free(c);
104 }
105
106 DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
107
108 static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel **ret) {
109 ReceiveChannel *c;
110
111 assert(s);
112
113 c = new(ReceiveChannel, 1);
114 if (!c)
115 return -ENOMEM;
116
117 *c = (ReceiveChannel) {
118 .macsec = s,
119 .sci.as_uint64 = sci,
120 };
121
122 *ret = c;
123 return 0;
124 }
125
126 static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) {
127 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
128 _cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL;
129 int r;
130
131 assert(s);
132 assert(ret);
133 assert(filename);
134 assert(section_line > 0);
135
136 r = network_config_section_new(filename, section_line, &n);
137 if (r < 0)
138 return r;
139
140 c = ordered_hashmap_get(s->receive_channels_by_section, n);
141 if (c) {
142 *ret = TAKE_PTR(c);
143 return 0;
144 }
145
146 r = macsec_receive_channel_new(s, 0, &c);
147 if (r < 0)
148 return r;
149
150 c->section = TAKE_PTR(n);
151
152 r = ordered_hashmap_ensure_allocated(&s->receive_channels_by_section, &network_config_hash_ops);
153 if (r < 0)
154 return r;
155
156 r = ordered_hashmap_put(s->receive_channels_by_section, c->section, c);
157 if (r < 0)
158 return r;
159
160 *ret = TAKE_PTR(c);
161
162 return 0;
163 }
164
165 static void macsec_transmit_association_free(TransmitAssociation *a) {
166 if (!a)
167 return;
168
169 if (a->macsec && a->section)
170 ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section);
171
172 network_config_section_free(a->section);
173 security_association_clear(&a->sa);
174
175 free(a);
176 }
177
178 DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
179
180 static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) {
181 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
182 _cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL;
183 int r;
184
185 assert(s);
186 assert(ret);
187 assert(filename);
188 assert(section_line > 0);
189
190 r = network_config_section_new(filename, section_line, &n);
191 if (r < 0)
192 return r;
193
194 a = ordered_hashmap_get(s->transmit_associations_by_section, n);
195 if (a) {
196 *ret = TAKE_PTR(a);
197 return 0;
198 }
199
200 a = new(TransmitAssociation, 1);
201 if (!a)
202 return -ENOMEM;
203
204 *a = (TransmitAssociation) {
205 .macsec = s,
206 .section = TAKE_PTR(n),
207 };
208
209 r = ordered_hashmap_ensure_allocated(&s->transmit_associations_by_section, &network_config_hash_ops);
210 if (r < 0)
211 return r;
212
213 r = ordered_hashmap_put(s->transmit_associations_by_section, a->section, a);
214 if (r < 0)
215 return r;
216
217 *ret = TAKE_PTR(a);
218
219 return 0;
220 }
221
222 static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_message **ret) {
223 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
224 int r;
225
226 assert(netdev);
227 assert(netdev->ifindex > 0);
228
229 r = sd_genl_message_new(netdev->manager->genl, SD_GENL_MACSEC, command, &m);
230 if (r < 0)
231 return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
232
233 r = sd_netlink_message_append_u32(m, MACSEC_ATTR_IFINDEX, netdev->ifindex);
234 if (r < 0)
235 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_IFINDEX attribute: %m");
236
237 *ret = TAKE_PTR(m);
238
239 return 0;
240 }
241
242 static int netdev_macsec_fill_message_sci(NetDev *netdev, MACsecSCI *sci, sd_netlink_message *m) {
243 int r;
244
245 assert(netdev);
246 assert(m);
247 assert(sci);
248
249 r = sd_netlink_message_open_container(m, MACSEC_ATTR_RXSC_CONFIG);
250 if (r < 0)
251 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
252
253 r = sd_netlink_message_append_u64(m, MACSEC_RXSC_ATTR_SCI, sci->as_uint64);
254 if (r < 0)
255 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_RXSC_ATTR_SCI attribute: %m");
256
257 r = sd_netlink_message_close_container(m);
258 if (r < 0)
259 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m");
260
261 return 0;
262 }
263
264 static int netdev_macsec_fill_message_sa(NetDev *netdev, SecurityAssociation *a, sd_netlink_message *m) {
265 int r;
266
267 assert(netdev);
268 assert(a);
269 assert(m);
270
271 r = sd_netlink_message_open_container(m, MACSEC_ATTR_SA_CONFIG);
272 if (r < 0)
273 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
274
275 r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_AN, a->association_number);
276 if (r < 0)
277 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_AN attribute: %m");
278
279 if (a->packet_number > 0) {
280 r = sd_netlink_message_append_u32(m, MACSEC_SA_ATTR_PN, a->packet_number);
281 if (r < 0)
282 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_PN attribute: %m");
283 }
284
285 if (a->key_len > 0) {
286 r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEYID, a->key_id, MACSEC_KEYID_LEN);
287 if (r < 0)
288 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEYID attribute: %m");
289
290 r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEY, a->key, a->key_len);
291 if (r < 0)
292 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEY attribute: %m");
293 }
294
295 r = sd_netlink_message_close_container(m);
296 if (r < 0)
297 return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m");
298
299 return 0;
300 }
301
302 static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
303 int r;
304
305 assert(netdev);
306 assert(netdev->state != _NETDEV_STATE_INVALID);
307
308 r = sd_netlink_message_get_errno(m);
309 if (r == -EEXIST)
310 log_netdev_info(netdev,
311 "MACsec receive secure association exists, "
312 "using existing without changing its parameters");
313 else if (r < 0) {
314 log_netdev_warning_errno(netdev, r,
315 "Failed to add receive secure association: %m");
316 netdev_drop(netdev);
317
318 return 1;
319 }
320
321 log_netdev_debug(netdev, "Receive secure association is configured");
322
323 return 1;
324 }
325
326 static int netdev_macsec_configure_receive_association(NetDev *netdev, ReceiveAssociation *a) {
327 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
328 int r;
329
330 assert(netdev);
331 assert(a);
332
333 r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSA, &m);
334 if (r < 0)
335 return r;
336
337 r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
338 if (r < 0)
339 return r;
340
341 r = netdev_macsec_fill_message_sci(netdev, &a->sci, m);
342 if (r < 0)
343 return r;
344
345 r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_association_handler,
346 netdev_destroy_callback, netdev);
347 if (r < 0)
348 return log_netdev_error_errno(netdev, r, "Failed to configure receive secure association: %m");
349
350 netdev_ref(netdev);
351
352 return 0;
353 }
354
355 static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *m, ReceiveChannel *c) {
356 NetDev *netdev;
357 unsigned i;
358 int r;
359
360 assert(c);
361 assert(c->macsec);
362
363 netdev = NETDEV(c->macsec);
364
365 assert(netdev->state != _NETDEV_STATE_INVALID);
366
367 r = sd_netlink_message_get_errno(m);
368 if (r == -EEXIST)
369 log_netdev_debug(netdev,
370 "MACsec receive channel exists, "
371 "using existing without changing its parameters");
372 else if (r < 0) {
373 log_netdev_warning_errno(netdev, r,
374 "Failed to add receive secure channel: %m");
375 netdev_drop(netdev);
376
377 return 1;
378 }
379
380 log_netdev_debug(netdev, "Receive channel is configured");
381
382 for (i = 0; i < c->n_rxsa; i++) {
383 r = netdev_macsec_configure_receive_association(netdev, c->rxsa[i]);
384 if (r < 0) {
385 log_netdev_warning_errno(netdev, r,
386 "Failed to configure receive security association: %m");
387 netdev_drop(netdev);
388 return 1;
389 }
390 }
391
392 return 1;
393 }
394
395 static void receive_channel_destroy_callback(ReceiveChannel *c) {
396 assert(c);
397 assert(c->macsec);
398
399 netdev_unref(NETDEV(c->macsec));
400 }
401
402 static int netdev_macsec_configure_receive_channel(NetDev *netdev, ReceiveChannel *c) {
403 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
404 int r;
405
406 assert(netdev);
407 assert(c);
408
409 r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSC, &m);
410 if (r < 0)
411 return r;
412
413 r = netdev_macsec_fill_message_sci(netdev, &c->sci, m);
414 if (r < 0)
415 return r;
416
417 r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_channel_handler,
418 receive_channel_destroy_callback, c);
419 if (r < 0)
420 return log_netdev_error_errno(netdev, r, "Failed to configure receive channel: %m");
421
422 netdev_ref(netdev);
423
424 return 0;
425 }
426
427 static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
428 int r;
429
430 assert(netdev);
431 assert(netdev->state != _NETDEV_STATE_INVALID);
432
433 r = sd_netlink_message_get_errno(m);
434 if (r == -EEXIST)
435 log_netdev_info(netdev,
436 "MACsec transmit secure association exists, "
437 "using existing without changing its parameters");
438 else if (r < 0) {
439 log_netdev_warning_errno(netdev, r,
440 "Failed to add transmit secure association: %m");
441 netdev_drop(netdev);
442
443 return 1;
444 }
445
446 log_netdev_debug(netdev, "Transmit secure association is configured");
447
448 return 1;
449 }
450
451 static int netdev_macsec_configure_transmit_association(NetDev *netdev, TransmitAssociation *a) {
452 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
453 int r;
454
455 assert(netdev);
456 assert(a);
457
458 r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_TXSA, &m);
459 if (r < 0)
460 return r;
461
462 r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
463 if (r < 0)
464 return r;
465
466 r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_transmit_association_handler,
467 netdev_destroy_callback, netdev);
468 if (r < 0)
469 return log_netdev_error_errno(netdev, r, "Failed to configure transmit secure association: %m");
470
471 netdev_ref(netdev);
472
473 return 0;
474 }
475
476 static int netdev_macsec_configure(NetDev *netdev, Link *link, sd_netlink_message *m) {
477 TransmitAssociation *a;
478 ReceiveChannel *c;
479 Iterator i;
480 MACsec *s;
481 int r;
482
483 assert(netdev);
484 s = MACSEC(netdev);
485 assert(s);
486
487 ORDERED_HASHMAP_FOREACH(a, s->transmit_associations_by_section, i) {
488 r = netdev_macsec_configure_transmit_association(netdev, a);
489 if (r < 0)
490 return r;
491 }
492
493 ORDERED_HASHMAP_FOREACH(c, s->receive_channels, i) {
494 r = netdev_macsec_configure_receive_channel(netdev, c);
495 if (r < 0)
496 return r;
497 }
498
499 return 0;
500 }
501
502 static int netdev_macsec_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
503 MACsec *v;
504 int r;
505
506 assert(netdev);
507 assert(m);
508
509 v = MACSEC(netdev);
510
511 if (v->port > 0) {
512 r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port);
513 if (r < 0)
514 return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_PORT attribute: %m");
515 }
516
517 if (v->encrypt >= 0) {
518 r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt);
519 if (r < 0)
520 return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCRYPT attribute: %m");
521 }
522
523 return r;
524 }
525
526 int config_parse_macsec_port(
527 const char *unit,
528 const char *filename,
529 unsigned line,
530 const char *section,
531 unsigned section_line,
532 const char *lvalue,
533 int ltype,
534 const char *rvalue,
535 void *data,
536 void *userdata) {
537
538 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
539 _cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
540 MACsec *s = userdata;
541 uint16_t port;
542 be16_t *dest;
543 int r;
544
545 assert(filename);
546 assert(section);
547 assert(lvalue);
548 assert(rvalue);
549 assert(data);
550
551 /* This parses port used to make Secure Channel Identifier (SCI) */
552
553 if (streq(section, "MACsec"))
554 dest = &s->port;
555 else if (streq(section, "MACsecReceiveChannel")) {
556 r = macsec_receive_channel_new_static(s, filename, section_line, &c);
557 if (r < 0)
558 return r;
559
560 dest = &c->sci.port;
561 } else {
562 assert(streq(section, "MACsecReceiveAssociation"));
563
564 r = macsec_receive_association_new_static(s, filename, section_line, &b);
565 if (r < 0)
566 return r;
567
568 dest = &b->sci.port;
569 }
570
571 r = parse_ip_port(rvalue, &port);
572 if (r < 0) {
573 log_syntax(unit, LOG_ERR, filename, line, r,
574 "Failed to parse port '%s' for secure channel identifier. Ignoring assignment: %m",
575 rvalue);
576 return 0;
577 }
578
579 *dest = htobe16(port);
580
581 TAKE_PTR(b);
582 TAKE_PTR(c);
583
584 return 0;
585 }
586
587 int config_parse_macsec_hw_address(
588 const char *unit,
589 const char *filename,
590 unsigned line,
591 const char *section,
592 unsigned section_line,
593 const char *lvalue,
594 int ltype,
595 const char *rvalue,
596 void *data,
597 void *userdata) {
598
599 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
600 _cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
601 MACsec *s = userdata;
602 int r;
603
604 assert(filename);
605 assert(section);
606 assert(lvalue);
607 assert(rvalue);
608 assert(data);
609
610 if (streq(section, "MACsecReceiveChannel"))
611 r = macsec_receive_channel_new_static(s, filename, section_line, &c);
612 else
613 r = macsec_receive_association_new_static(s, filename, section_line, &b);
614 if (r < 0)
615 return r;
616
617 r = ether_addr_from_string(rvalue, b ? &b->sci.mac : &c->sci.mac);
618 if (r < 0) {
619 log_syntax(unit, LOG_ERR, filename, line, r,
620 "Failed to parse MAC address for secure channel identifier. "
621 "Ignoring assignment: %s", rvalue);
622 return 0;
623 }
624
625 TAKE_PTR(b);
626 TAKE_PTR(c);
627
628 return 0;
629 }
630
631 int config_parse_macsec_packet_number(
632 const char *unit,
633 const char *filename,
634 unsigned line,
635 const char *section,
636 unsigned section_line,
637 const char *lvalue,
638 int ltype,
639 const char *rvalue,
640 void *data,
641 void *userdata) {
642
643 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
644 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
645 MACsec *s = userdata;
646 uint32_t val, *dest;
647 int r;
648
649 assert(filename);
650 assert(section);
651 assert(lvalue);
652 assert(rvalue);
653 assert(data);
654
655 if (streq(section, "MACsecTransmitAssociation"))
656 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
657 else
658 r = macsec_receive_association_new_static(s, filename, section_line, &b);
659 if (r < 0)
660 return r;
661
662 dest = a ? &a->sa.packet_number : &b->sa.packet_number;
663
664 r = safe_atou32(rvalue, &val);
665 if (r < 0) {
666 log_syntax(unit, LOG_ERR, filename, line, r,
667 "Failed to parse packet number. Ignoring assignment: %s", rvalue);
668 return 0;
669 }
670 if (streq(section, "MACsecTransmitAssociation") && val == 0) {
671 log_syntax(unit, LOG_ERR, filename, line, 0,
672 "Invalid packet number. Ignoring assignment: %s", rvalue);
673 return 0;
674 }
675
676 *dest = val;
677 TAKE_PTR(a);
678 TAKE_PTR(b);
679
680 return 0;
681 }
682
683 int config_parse_macsec_key(
684 const char *unit,
685 const char *filename,
686 unsigned line,
687 const char *section,
688 unsigned section_line,
689 const char *lvalue,
690 int ltype,
691 const char *rvalue,
692 void *data,
693 void *userdata) {
694
695 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
696 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
697 _cleanup_free_ void *p;
698 MACsec *s = userdata;
699 SecurityAssociation *dest;
700 size_t l;
701 int r;
702
703 assert(filename);
704 assert(section);
705 assert(lvalue);
706 assert(rvalue);
707 assert(data);
708
709 if (streq(section, "MACsecTransmitAssociation"))
710 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
711 else
712 r = macsec_receive_association_new_static(s, filename, section_line, &b);
713 if (r < 0)
714 return r;
715
716 dest = a ? &a->sa : &b->sa;
717
718 r = unhexmem_full(rvalue, strlen(rvalue), true, &p, &l);
719 if (r < 0) {
720 log_syntax(unit, LOG_ERR, filename, line, r,
721 "Failed to parse key. Ignoring assignment: %m");
722 return 0;
723 }
724 if (l != 16) {
725 /* See DEFAULT_SAK_LEN in drivers/net/macsec.c */
726 explicit_bzero_safe(p, l);
727 log_syntax(unit, LOG_ERR, filename, line, 0,
728 "Invalid key length (%zu). Ignoring assignment", l);
729 return 0;
730 }
731
732 free_and_replace(dest->key, p);
733 dest->key_len = l;
734
735 TAKE_PTR(a);
736 TAKE_PTR(b);
737
738 return 0;
739 }
740
741 int config_parse_macsec_key_id(
742 const char *unit,
743 const char *filename,
744 unsigned line,
745 const char *section,
746 unsigned section_line,
747 const char *lvalue,
748 int ltype,
749 const char *rvalue,
750 void *data,
751 void *userdata) {
752
753 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
754 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
755 _cleanup_free_ void *p;
756 MACsec *s = userdata;
757 uint8_t *dest;
758 size_t l;
759 int r;
760
761 assert(filename);
762 assert(section);
763 assert(lvalue);
764 assert(rvalue);
765 assert(data);
766
767 if (streq(section, "MACsecTransmitAssociation"))
768 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
769 else
770 r = macsec_receive_association_new_static(s, filename, section_line, &b);
771 if (r < 0)
772 return r;
773
774 r = unhexmem(rvalue, strlen(rvalue), &p, &l);
775 if (r < 0) {
776 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse key id. Ignoring assignment: %s", rvalue);
777 return 0;
778 }
779 if (l > MACSEC_KEYID_LEN) {
780 log_syntax(unit, LOG_ERR, filename, line, 0,
781 "The size of key id is too large (%zu), maximum of %zu permitted. "
782 "Ignoring assignment: %s", l, (size_t) MACSEC_KEYID_LEN, rvalue);
783 return 0;
784 }
785
786 dest = a ? a->sa.key_id : b->sa.key_id;
787 memcpy_safe(dest, p, l);
788 memzero(dest + l, MACSEC_KEYID_LEN - l);
789
790 TAKE_PTR(a);
791 TAKE_PTR(b);
792
793 return 0;
794 }
795
796 static int macsec_receive_channel_verify(ReceiveChannel *c) {
797 NetDev *netdev;
798 int r;
799
800 assert(c);
801 assert(c->macsec);
802
803 netdev = NETDEV(c->macsec);
804
805 if (section_is_invalid(c->section))
806 return -EINVAL;
807
808 if (ether_addr_is_null(&c->sci.mac))
809 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
810 "%s: MACsec receive channel without MAC address configured. "
811 "Ignoring [MACsecReceiveChannel] section from line %u",
812 c->section->filename, c->section->line);
813
814 if (c->sci.port == 0)
815 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
816 "%s: MACsec receive channel without port configured. "
817 "Ignoring [MACsecReceiveChannel] section from line %u",
818 c->section->filename, c->section->line);
819
820 r = ordered_hashmap_ensure_allocated(&c->macsec->receive_channels, &uint64_hash_ops);
821 if (r < 0)
822 return log_oom();
823
824 r = ordered_hashmap_put(c->macsec->receive_channels, &c->sci.as_uint64, c);
825 if (r == -EEXIST)
826 return log_netdev_error_errno(netdev, r,
827 "%s: Multiple [MACsecReceiveChannel] sections have same SCI, "
828 "Ignoring [MACsecReceiveChannel] section from line %u",
829 c->section->filename, c->section->line);
830 if (r < 0)
831 return log_netdev_error_errno(netdev, r,
832 "%s: Failed to store [MACsecReceiveChannel] section at hashmap, "
833 "Ignoring [MACsecReceiveChannel] section from line %u",
834 c->section->filename, c->section->line);
835 return 0;
836 }
837
838 static int macsec_transmit_association_verify(TransmitAssociation *t) {
839 NetDev *netdev;
840
841 assert(t);
842 assert(t->macsec);
843
844 netdev = NETDEV(t->macsec);
845
846 if (section_is_invalid(t->section))
847 return -EINVAL;
848
849 if (t->sa.packet_number == 0)
850 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
851 "%s: MACsec transmit secure association without PacketNumber= configured. "
852 "Ignoring [MACsecTransmitAssociation] section from line %u",
853 t->section->filename, t->section->line);
854
855 if (t->sa.key_len <= 0)
856 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
857 "%s: MACsec transmit secure association without key configured. "
858 "Ignoring [MACsecTransmitAssociation] section from line %u",
859 t->section->filename, t->section->line);
860
861 return 0;
862 }
863
864 static int macsec_receive_association_verify(ReceiveAssociation *a) {
865 ReceiveChannel *c;
866 NetDev *netdev;
867 int r;
868
869 assert(a);
870 assert(a->macsec);
871
872 netdev = NETDEV(a->macsec);
873
874 if (section_is_invalid(a->section))
875 return -EINVAL;
876
877 if (a->sa.key_len <= 0)
878 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
879 "%s: MACsec receive secure association without key configured. "
880 "Ignoring [MACsecReceiveAssociation] section from line %u",
881 a->section->filename, a->section->line);
882
883 if (ether_addr_is_null(&a->sci.mac))
884 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
885 "%s: MACsec receive secure association without MAC address configured. "
886 "Ignoring [MACsecReceiveAssociation] section from line %u",
887 a->section->filename, a->section->line);
888
889 if (a->sci.port == 0)
890 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
891 "%s: MACsec receive secure association without port configured. "
892 "Ignoring [MACsecReceiveAssociation] section from line %u",
893 a->section->filename, a->section->line);
894
895 c = ordered_hashmap_get(a->macsec->receive_channels, &a->sci.as_uint64);
896 if (!c) {
897 _cleanup_(macsec_receive_channel_freep) ReceiveChannel *new_channel = NULL;
898
899 r = macsec_receive_channel_new(a->macsec, a->sci.as_uint64, &new_channel);
900 if (r < 0)
901 return log_oom();
902
903 r = ordered_hashmap_ensure_allocated(&a->macsec->receive_channels, &uint64_hash_ops);
904 if (r < 0)
905 return log_oom();
906
907 r = ordered_hashmap_put(a->macsec->receive_channels, &new_channel->sci.as_uint64, new_channel);
908 if (r < 0)
909 return log_netdev_error_errno(netdev, r,
910 "%s: Failed to store receive channel at hashmap, "
911 "Ignoring [MACsecReceiveAssociation] section from line %u",
912 a->section->filename, a->section->line);
913 c = TAKE_PTR(new_channel);
914 }
915 if (c->n_rxsa >= MACSEC_MAX_ASSOCIATION_NUMBER)
916 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(ERANGE),
917 "%s: Too many [MACsecReceiveAssociation] sections for the same receive channel, "
918 "Ignoring [MACsecReceiveAssociation] section from line %u",
919 a->section->filename, a->section->line);
920
921 a->sa.association_number = c->n_rxsa;
922 c->rxsa[c->n_rxsa++] = a;
923
924 return 0;
925 }
926
927 static int netdev_macsec_verify(NetDev *netdev, const char *filename) {
928 MACsec *v = MACSEC(netdev);
929 TransmitAssociation *a;
930 ReceiveAssociation *n;
931 ReceiveChannel *c;
932 Iterator i;
933 uint8_t an;
934 int r;
935
936 assert(netdev);
937 assert(v);
938 assert(filename);
939
940 ORDERED_HASHMAP_FOREACH(c, v->receive_channels_by_section, i) {
941 r = macsec_receive_channel_verify(c);
942 if (r < 0)
943 macsec_receive_channel_free(c);
944 }
945
946 an = 0;
947 ORDERED_HASHMAP_FOREACH(a, v->transmit_associations_by_section, i) {
948 r = macsec_transmit_association_verify(a);
949 if (r < 0) {
950 macsec_transmit_association_free(a);
951 continue;
952 }
953
954 if (an >= MACSEC_MAX_ASSOCIATION_NUMBER) {
955 log_netdev_error(netdev,
956 "%s: Too many [MACsecTransmitAssociation] sections configured. "
957 "Ignoring [MACsecTransmitAssociation] section from line %u",
958 a->section->filename, a->section->line);
959 macsec_transmit_association_free(a);
960 continue;
961 }
962
963 a->sa.association_number = an++;
964 }
965
966 ORDERED_HASHMAP_FOREACH(n, v->receive_associations_by_section, i) {
967 r = macsec_receive_association_verify(n);
968 if (r < 0)
969 macsec_receive_association_free(n);
970 }
971
972 return 0;
973 }
974
975 static void macsec_init(NetDev *netdev) {
976 MACsec *v;
977
978 assert(netdev);
979
980 v = MACSEC(netdev);
981
982 assert(v);
983
984 v->encrypt = -1;
985 }
986
987 static void macsec_done(NetDev *netdev) {
988 MACsec *t;
989
990 assert(netdev);
991
992 t = MACSEC(netdev);
993
994 assert(t);
995
996 ordered_hashmap_free_with_destructor(t->receive_channels, macsec_receive_channel_free);
997 ordered_hashmap_free_with_destructor(t->receive_channels_by_section, macsec_receive_channel_free);
998 ordered_hashmap_free_with_destructor(t->transmit_associations_by_section, macsec_transmit_association_free);
999 ordered_hashmap_free_with_destructor(t->receive_associations_by_section, macsec_receive_association_free);
1000 }
1001
1002 const NetDevVTable macsec_vtable = {
1003 .object_size = sizeof(MACsec),
1004 .init = macsec_init,
1005 .sections = "Match\0NetDev\0MACsec\0MACsecReceiveChannel\0MACsecTransmitAssociation\0MACsecReceiveAssociation\0",
1006 .fill_message_create = netdev_macsec_fill_message_create,
1007 .post_create = netdev_macsec_configure,
1008 .done = macsec_done,
1009 .create_type = NETDEV_CREATE_STACKED,
1010 .config_verify = netdev_macsec_verify,
1011 };