]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/macsec.c
Merge pull request #21838 from lnussel/logind-refactor
[thirdparty/systemd.git] / src / network / netdev / macsec.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/in.h>
4 #include <linux/if_arp.h>
5 #include <linux/if_ether.h>
6 #include <linux/if_macsec.h>
7 #include <linux/genetlink.h>
8
9 #include "conf-parser.h"
10 #include "fileio.h"
11 #include "hashmap.h"
12 #include "hexdecoct.h"
13 #include "macsec.h"
14 #include "memory-util.h"
15 #include "netlink-util.h"
16 #include "networkd-manager.h"
17 #include "path-util.h"
18 #include "socket-util.h"
19 #include "string-table.h"
20 #include "string-util.h"
21 #include "util.h"
22
23 static void security_association_clear(SecurityAssociation *sa) {
24 if (!sa)
25 return;
26
27 explicit_bzero_safe(sa->key, sa->key_len);
28 free(sa->key);
29 free(sa->key_file);
30 }
31
32 static void security_association_init(SecurityAssociation *sa) {
33 assert(sa);
34
35 sa->activate = -1;
36 sa->use_for_encoding = -1;
37 }
38
39 static ReceiveAssociation* macsec_receive_association_free(ReceiveAssociation *c) {
40 if (!c)
41 return NULL;
42
43 if (c->macsec && c->section)
44 ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section);
45
46 config_section_free(c->section);
47 security_association_clear(&c->sa);
48
49 return mfree(c);
50 }
51
52 DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
53
54 static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) {
55 _cleanup_(config_section_freep) ConfigSection *n = NULL;
56 _cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL;
57 int r;
58
59 assert(s);
60 assert(ret);
61 assert(filename);
62 assert(section_line > 0);
63
64 r = config_section_new(filename, section_line, &n);
65 if (r < 0)
66 return r;
67
68 c = ordered_hashmap_get(s->receive_associations_by_section, n);
69 if (c) {
70 *ret = TAKE_PTR(c);
71 return 0;
72 }
73
74 c = new(ReceiveAssociation, 1);
75 if (!c)
76 return -ENOMEM;
77
78 *c = (ReceiveAssociation) {
79 .macsec = s,
80 .section = TAKE_PTR(n),
81 };
82
83 security_association_init(&c->sa);
84
85 r = ordered_hashmap_ensure_put(&s->receive_associations_by_section, &config_section_hash_ops, c->section, c);
86 if (r < 0)
87 return r;
88
89 *ret = TAKE_PTR(c);
90
91 return 0;
92 }
93
94 static ReceiveChannel* macsec_receive_channel_free(ReceiveChannel *c) {
95 if (!c)
96 return NULL;
97
98 if (c->macsec) {
99 if (c->sci.as_uint64 > 0)
100 ordered_hashmap_remove_value(c->macsec->receive_channels, &c->sci.as_uint64, c);
101
102 if (c->section)
103 ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section);
104 }
105
106 config_section_free(c->section);
107
108 return mfree(c);
109 }
110
111 DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
112
113 static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel **ret) {
114 ReceiveChannel *c;
115
116 assert(s);
117
118 c = new(ReceiveChannel, 1);
119 if (!c)
120 return -ENOMEM;
121
122 *c = (ReceiveChannel) {
123 .macsec = s,
124 .sci.as_uint64 = sci,
125 };
126
127 *ret = c;
128 return 0;
129 }
130
131 static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) {
132 _cleanup_(config_section_freep) ConfigSection *n = NULL;
133 _cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL;
134 int r;
135
136 assert(s);
137 assert(ret);
138 assert(filename);
139 assert(section_line > 0);
140
141 r = config_section_new(filename, section_line, &n);
142 if (r < 0)
143 return r;
144
145 c = ordered_hashmap_get(s->receive_channels_by_section, n);
146 if (c) {
147 *ret = TAKE_PTR(c);
148 return 0;
149 }
150
151 r = macsec_receive_channel_new(s, 0, &c);
152 if (r < 0)
153 return r;
154
155 c->section = TAKE_PTR(n);
156
157 r = ordered_hashmap_ensure_put(&s->receive_channels_by_section, &config_section_hash_ops, c->section, c);
158 if (r < 0)
159 return r;
160
161 *ret = TAKE_PTR(c);
162
163 return 0;
164 }
165
166 static TransmitAssociation* macsec_transmit_association_free(TransmitAssociation *a) {
167 if (!a)
168 return NULL;
169
170 if (a->macsec && a->section)
171 ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section);
172
173 config_section_free(a->section);
174 security_association_clear(&a->sa);
175
176 return mfree(a);
177 }
178
179 DEFINE_SECTION_CLEANUP_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
180
181 static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) {
182 _cleanup_(config_section_freep) ConfigSection *n = NULL;
183 _cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL;
184 int r;
185
186 assert(s);
187 assert(ret);
188 assert(filename);
189 assert(section_line > 0);
190
191 r = config_section_new(filename, section_line, &n);
192 if (r < 0)
193 return r;
194
195 a = ordered_hashmap_get(s->transmit_associations_by_section, n);
196 if (a) {
197 *ret = TAKE_PTR(a);
198 return 0;
199 }
200
201 a = new(TransmitAssociation, 1);
202 if (!a)
203 return -ENOMEM;
204
205 *a = (TransmitAssociation) {
206 .macsec = s,
207 .section = TAKE_PTR(n),
208 };
209
210 security_association_init(&a->sa);
211
212 r = ordered_hashmap_ensure_put(&s->transmit_associations_by_section, &config_section_hash_ops, a->section, a);
213 if (r < 0)
214 return r;
215
216 *ret = TAKE_PTR(a);
217
218 return 0;
219 }
220
221 static int netdev_macsec_create_message(NetDev *netdev, int command, sd_netlink_message **ret) {
222 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
223 int r;
224
225 assert(netdev);
226 assert(netdev->ifindex > 0);
227
228 r = sd_genl_message_new(netdev->manager->genl, MACSEC_GENL_NAME, command, &m);
229 if (r < 0)
230 return r;
231
232 r = sd_netlink_message_append_u32(m, MACSEC_ATTR_IFINDEX, netdev->ifindex);
233 if (r < 0)
234 return r;
235
236 *ret = TAKE_PTR(m);
237
238 return 0;
239 }
240
241 static int netdev_macsec_fill_message_sci(NetDev *netdev, MACsecSCI *sci, sd_netlink_message *m) {
242 int r;
243
244 assert(netdev);
245 assert(m);
246 assert(sci);
247
248 r = sd_netlink_message_open_container(m, MACSEC_ATTR_RXSC_CONFIG);
249 if (r < 0)
250 return r;
251
252 r = sd_netlink_message_append_u64(m, MACSEC_RXSC_ATTR_SCI, sci->as_uint64);
253 if (r < 0)
254 return r;
255
256 r = sd_netlink_message_close_container(m);
257 if (r < 0)
258 return r;
259
260 return 0;
261 }
262
263 static int netdev_macsec_fill_message_sa(NetDev *netdev, SecurityAssociation *a, sd_netlink_message *m) {
264 int r;
265
266 assert(netdev);
267 assert(a);
268 assert(m);
269
270 r = sd_netlink_message_open_container(m, MACSEC_ATTR_SA_CONFIG);
271 if (r < 0)
272 return r;
273
274 r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_AN, a->association_number);
275 if (r < 0)
276 return r;
277
278 if (a->packet_number > 0) {
279 r = sd_netlink_message_append_u32(m, MACSEC_SA_ATTR_PN, a->packet_number);
280 if (r < 0)
281 return r;
282 }
283
284 if (a->key_len > 0) {
285 r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEYID, a->key_id, MACSEC_KEYID_LEN);
286 if (r < 0)
287 return r;
288
289 r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEY, a->key, a->key_len);
290 if (r < 0)
291 return r;
292 }
293
294 if (a->activate >= 0) {
295 r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_ACTIVE, a->activate);
296 if (r < 0)
297 return r;
298 }
299
300 r = sd_netlink_message_close_container(m);
301 if (r < 0)
302 return r;
303
304 return 0;
305 }
306
307 static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
308 int r;
309
310 assert(netdev);
311 assert(netdev->state != _NETDEV_STATE_INVALID);
312
313 r = sd_netlink_message_get_errno(m);
314 if (r == -EEXIST)
315 log_netdev_info(netdev,
316 "MACsec receive secure association exists, using it without changing parameters");
317 else if (r < 0) {
318 log_netdev_warning_errno(netdev, r,
319 "Failed to add receive secure association: %m");
320 netdev_enter_failed(netdev);
321
322 return 1;
323 }
324
325 log_netdev_debug(netdev, "Receive secure association is configured");
326
327 return 1;
328 }
329
330 static int netdev_macsec_configure_receive_association(NetDev *netdev, ReceiveAssociation *a) {
331 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
332 int r;
333
334 assert(netdev);
335 assert(a);
336
337 r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_RXSA, &m);
338 if (r < 0)
339 return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m");
340
341 r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
342 if (r < 0)
343 return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m");
344
345 r = netdev_macsec_fill_message_sci(netdev, &a->sci, m);
346 if (r < 0)
347 return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m");
348
349 r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_association_handler,
350 netdev_destroy_callback, netdev);
351 if (r < 0)
352 return log_netdev_error_errno(netdev, r, "Failed to configure receive secure association: %m");
353
354 netdev_ref(netdev);
355
356 return 0;
357 }
358
359 static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *m, ReceiveChannel *c) {
360 NetDev *netdev;
361 int r;
362
363 assert(c);
364 assert(c->macsec);
365
366 netdev = NETDEV(c->macsec);
367
368 assert(netdev->state != _NETDEV_STATE_INVALID);
369
370 r = sd_netlink_message_get_errno(m);
371 if (r == -EEXIST)
372 log_netdev_debug(netdev,
373 "MACsec receive channel exists, using it without changing parameters");
374 else if (r < 0) {
375 log_netdev_warning_errno(netdev, r,
376 "Failed to add receive secure channel: %m");
377 netdev_enter_failed(netdev);
378
379 return 1;
380 }
381
382 log_netdev_debug(netdev, "Receive channel is configured");
383
384 for (unsigned i = 0; i < c->n_rxsa; i++) {
385 r = netdev_macsec_configure_receive_association(netdev, c->rxsa[i]);
386 if (r < 0) {
387 log_netdev_warning_errno(netdev, r,
388 "Failed to configure receive security association: %m");
389 netdev_enter_failed(netdev);
390 return 1;
391 }
392 }
393
394 return 1;
395 }
396
397 static void receive_channel_destroy_callback(ReceiveChannel *c) {
398 assert(c);
399 assert(c->macsec);
400
401 netdev_unref(NETDEV(c->macsec));
402 }
403
404 static int netdev_macsec_configure_receive_channel(NetDev *netdev, ReceiveChannel *c) {
405 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
406 int r;
407
408 assert(netdev);
409 assert(c);
410
411 r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_RXSC, &m);
412 if (r < 0)
413 return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m");
414
415 r = netdev_macsec_fill_message_sci(netdev, &c->sci, m);
416 if (r < 0)
417 return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m");
418
419 r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_channel_handler,
420 receive_channel_destroy_callback, c);
421 if (r < 0)
422 return log_netdev_error_errno(netdev, r, "Failed to configure receive channel: %m");
423
424 netdev_ref(netdev);
425
426 return 0;
427 }
428
429 static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
430 int r;
431
432 assert(netdev);
433 assert(netdev->state != _NETDEV_STATE_INVALID);
434
435 r = sd_netlink_message_get_errno(m);
436 if (r == -EEXIST)
437 log_netdev_info(netdev,
438 "MACsec transmit secure association exists, using it without changing parameters");
439 else if (r < 0) {
440 log_netdev_warning_errno(netdev, r,
441 "Failed to add transmit secure association: %m");
442 netdev_enter_failed(netdev);
443
444 return 1;
445 }
446
447 log_netdev_debug(netdev, "Transmit secure association is configured");
448
449 return 1;
450 }
451
452 static int netdev_macsec_configure_transmit_association(NetDev *netdev, TransmitAssociation *a) {
453 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
454 int r;
455
456 assert(netdev);
457 assert(a);
458
459 r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_TXSA, &m);
460 if (r < 0)
461 return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m");
462
463 r = netdev_macsec_fill_message_sa(netdev, &a->sa, m);
464 if (r < 0)
465 return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m");
466
467 r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_transmit_association_handler,
468 netdev_destroy_callback, netdev);
469 if (r < 0)
470 return log_netdev_error_errno(netdev, r, "Failed to configure transmit secure association: %m");
471
472 netdev_ref(netdev);
473
474 return 0;
475 }
476
477 static int netdev_macsec_configure(NetDev *netdev, Link *link, sd_netlink_message *m) {
478 TransmitAssociation *a;
479 ReceiveChannel *c;
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) {
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) {
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 assert(v);
512
513 if (v->port > 0) {
514 r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port);
515 if (r < 0)
516 return r;
517 }
518
519 if (v->encrypt >= 0) {
520 r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt);
521 if (r < 0)
522 return r;
523 }
524
525 r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCODING_SA, v->encoding_an);
526 if (r < 0)
527 return r;
528
529 return 0;
530 }
531
532 int config_parse_macsec_port(
533 const char *unit,
534 const char *filename,
535 unsigned line,
536 const char *section,
537 unsigned section_line,
538 const char *lvalue,
539 int ltype,
540 const char *rvalue,
541 void *data,
542 void *userdata) {
543
544 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
545 _cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
546 MACsec *s = userdata;
547 uint16_t port;
548 void *dest;
549 int r;
550
551 assert(filename);
552 assert(section);
553 assert(lvalue);
554 assert(rvalue);
555 assert(data);
556
557 /* This parses port used to make Secure Channel Identifier (SCI) */
558
559 if (streq(section, "MACsec"))
560 dest = &s->port;
561 else if (streq(section, "MACsecReceiveChannel")) {
562 r = macsec_receive_channel_new_static(s, filename, section_line, &c);
563 if (r < 0)
564 return log_oom();
565
566 dest = &c->sci.port;
567 } else {
568 assert(streq(section, "MACsecReceiveAssociation"));
569
570 r = macsec_receive_association_new_static(s, filename, section_line, &b);
571 if (r < 0)
572 return log_oom();
573
574 dest = &b->sci.port;
575 }
576
577 r = parse_ip_port(rvalue, &port);
578 if (r < 0) {
579 log_syntax(unit, LOG_WARNING, filename, line, r,
580 "Failed to parse port '%s' for secure channel identifier. Ignoring assignment: %m",
581 rvalue);
582 return 0;
583 }
584
585 unaligned_write_be16(dest, port);
586
587 TAKE_PTR(b);
588 TAKE_PTR(c);
589
590 return 0;
591 }
592
593 int config_parse_macsec_hw_address(
594 const char *unit,
595 const char *filename,
596 unsigned line,
597 const char *section,
598 unsigned section_line,
599 const char *lvalue,
600 int ltype,
601 const char *rvalue,
602 void *data,
603 void *userdata) {
604
605 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
606 _cleanup_(macsec_receive_channel_free_or_set_invalidp) ReceiveChannel *c = NULL;
607 MACsec *s = userdata;
608 int r;
609
610 assert(filename);
611 assert(section);
612 assert(lvalue);
613 assert(rvalue);
614 assert(data);
615
616 if (streq(section, "MACsecReceiveChannel"))
617 r = macsec_receive_channel_new_static(s, filename, section_line, &c);
618 else
619 r = macsec_receive_association_new_static(s, filename, section_line, &b);
620 if (r < 0)
621 return log_oom();
622
623 r = parse_ether_addr(rvalue, b ? &b->sci.mac : &c->sci.mac);
624 if (r < 0) {
625 log_syntax(unit, LOG_WARNING, filename, line, r,
626 "Failed to parse MAC address for secure channel identifier. "
627 "Ignoring assignment: %s", rvalue);
628 return 0;
629 }
630
631 TAKE_PTR(b);
632 TAKE_PTR(c);
633
634 return 0;
635 }
636
637 int config_parse_macsec_packet_number(
638 const char *unit,
639 const char *filename,
640 unsigned line,
641 const char *section,
642 unsigned section_line,
643 const char *lvalue,
644 int ltype,
645 const char *rvalue,
646 void *data,
647 void *userdata) {
648
649 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
650 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
651 MACsec *s = userdata;
652 uint32_t val, *dest;
653 int r;
654
655 assert(filename);
656 assert(section);
657 assert(lvalue);
658 assert(rvalue);
659 assert(data);
660
661 if (streq(section, "MACsecTransmitAssociation"))
662 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
663 else
664 r = macsec_receive_association_new_static(s, filename, section_line, &b);
665 if (r < 0)
666 return log_oom();
667
668 dest = a ? &a->sa.packet_number : &b->sa.packet_number;
669
670 r = safe_atou32(rvalue, &val);
671 if (r < 0) {
672 log_syntax(unit, LOG_WARNING, filename, line, r,
673 "Failed to parse packet number. Ignoring assignment: %s", rvalue);
674 return 0;
675 }
676 if (streq(section, "MACsecTransmitAssociation") && val == 0) {
677 log_syntax(unit, LOG_WARNING, filename, line, 0,
678 "Invalid packet number. Ignoring assignment: %s", rvalue);
679 return 0;
680 }
681
682 *dest = val;
683 TAKE_PTR(a);
684 TAKE_PTR(b);
685
686 return 0;
687 }
688
689 int config_parse_macsec_key(
690 const char *unit,
691 const char *filename,
692 unsigned line,
693 const char *section,
694 unsigned section_line,
695 const char *lvalue,
696 int ltype,
697 const char *rvalue,
698 void *data,
699 void *userdata) {
700
701 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
702 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
703 _cleanup_(erase_and_freep) void *p = NULL;
704 MACsec *s = userdata;
705 SecurityAssociation *dest;
706 size_t l;
707 int r;
708
709 assert(filename);
710 assert(section);
711 assert(lvalue);
712 assert(rvalue);
713 assert(data);
714
715 (void) warn_file_is_world_accessible(filename, NULL, unit, line);
716
717 if (streq(section, "MACsecTransmitAssociation"))
718 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
719 else
720 r = macsec_receive_association_new_static(s, filename, section_line, &b);
721 if (r < 0)
722 return log_oom();
723
724 dest = a ? &a->sa : &b->sa;
725
726 r = unhexmem_full(rvalue, strlen(rvalue), true, &p, &l);
727 if (r < 0) {
728 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse key. Ignoring assignment: %m");
729 return 0;
730 }
731
732 if (l != 16) {
733 /* See DEFAULT_SAK_LEN in drivers/net/macsec.c */
734 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key length (%zu). Ignoring assignment", l);
735 return 0;
736 }
737
738 explicit_bzero_safe(dest->key, dest->key_len);
739 free_and_replace(dest->key, p);
740 dest->key_len = l;
741
742 TAKE_PTR(a);
743 TAKE_PTR(b);
744
745 return 0;
746 }
747
748 int config_parse_macsec_key_file(
749 const char *unit,
750 const char *filename,
751 unsigned line,
752 const char *section,
753 unsigned section_line,
754 const char *lvalue,
755 int ltype,
756 const char *rvalue,
757 void *data,
758 void *userdata) {
759
760 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
761 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
762 _cleanup_free_ char *path = NULL;
763 MACsec *s = userdata;
764 char **dest;
765 int r;
766
767 assert(filename);
768 assert(section);
769 assert(lvalue);
770 assert(rvalue);
771 assert(data);
772
773 if (streq(section, "MACsecTransmitAssociation"))
774 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
775 else
776 r = macsec_receive_association_new_static(s, filename, section_line, &b);
777 if (r < 0)
778 return log_oom();
779
780 dest = a ? &a->sa.key_file : &b->sa.key_file;
781
782 if (isempty(rvalue)) {
783 *dest = mfree(*dest);
784 return 0;
785 }
786
787 path = strdup(rvalue);
788 if (!path)
789 return log_oom();
790
791 if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0)
792 return 0;
793
794 free_and_replace(*dest, path);
795 TAKE_PTR(a);
796 TAKE_PTR(b);
797
798 return 0;
799 }
800
801 int config_parse_macsec_key_id(
802 const char *unit,
803 const char *filename,
804 unsigned line,
805 const char *section,
806 unsigned section_line,
807 const char *lvalue,
808 int ltype,
809 const char *rvalue,
810 void *data,
811 void *userdata) {
812
813 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
814 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
815 _cleanup_free_ void *p = NULL;
816 MACsec *s = userdata;
817 uint8_t *dest;
818 size_t l;
819 int r;
820
821 assert(filename);
822 assert(section);
823 assert(lvalue);
824 assert(rvalue);
825 assert(data);
826
827 if (streq(section, "MACsecTransmitAssociation"))
828 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
829 else
830 r = macsec_receive_association_new_static(s, filename, section_line, &b);
831 if (r < 0)
832 return log_oom();
833
834 r = unhexmem(rvalue, strlen(rvalue), &p, &l);
835 if (r == -ENOMEM)
836 return log_oom();
837 if (r < 0) {
838 log_syntax(unit, LOG_WARNING, filename, line, r,
839 "Failed to parse KeyId=%s, ignoring assignment: %m", rvalue);
840 return 0;
841 }
842 if (l > MACSEC_KEYID_LEN) {
843 log_syntax(unit, LOG_WARNING, filename, line, 0,
844 "Specified KeyId= is larger then the allowed maximum (%zu > %u), ignoring: %s",
845 l, MACSEC_KEYID_LEN, rvalue);
846 return 0;
847 }
848
849 dest = a ? a->sa.key_id : b->sa.key_id;
850 memcpy_safe(dest, p, l);
851 memzero(dest + l, MACSEC_KEYID_LEN - l);
852
853 TAKE_PTR(a);
854 TAKE_PTR(b);
855
856 return 0;
857 }
858
859 int config_parse_macsec_sa_activate(
860 const char *unit,
861 const char *filename,
862 unsigned line,
863 const char *section,
864 unsigned section_line,
865 const char *lvalue,
866 int ltype,
867 const char *rvalue,
868 void *data,
869 void *userdata) {
870
871 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
872 _cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
873 MACsec *s = userdata;
874 int *dest;
875 int r;
876
877 assert(filename);
878 assert(section);
879 assert(lvalue);
880 assert(rvalue);
881 assert(data);
882
883 if (streq(section, "MACsecTransmitAssociation"))
884 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
885 else
886 r = macsec_receive_association_new_static(s, filename, section_line, &b);
887 if (r < 0)
888 return log_oom();
889
890 dest = a ? &a->sa.activate : &b->sa.activate;
891
892 if (isempty(rvalue))
893 r = -1;
894 else {
895 r = parse_boolean(rvalue);
896 if (r < 0) {
897 log_syntax(unit, LOG_WARNING, filename, line, r,
898 "Failed to parse activation mode of %s security association. "
899 "Ignoring assignment: %s",
900 streq(section, "MACsecTransmitAssociation") ? "transmit" : "receive",
901 rvalue);
902 return 0;
903 }
904 }
905
906 *dest = r;
907 TAKE_PTR(a);
908 TAKE_PTR(b);
909
910 return 0;
911 }
912
913 int config_parse_macsec_use_for_encoding(
914 const char *unit,
915 const char *filename,
916 unsigned line,
917 const char *section,
918 unsigned section_line,
919 const char *lvalue,
920 int ltype,
921 const char *rvalue,
922 void *data,
923 void *userdata) {
924
925 _cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
926 MACsec *s = userdata;
927 int r;
928
929 assert(filename);
930 assert(section);
931 assert(lvalue);
932 assert(rvalue);
933 assert(data);
934
935 r = macsec_transmit_association_new_static(s, filename, section_line, &a);
936 if (r < 0)
937 return log_oom();
938
939 if (isempty(rvalue)) {
940 a->sa.use_for_encoding = -1;
941 TAKE_PTR(a);
942 return 0;
943 }
944
945 r = parse_boolean(rvalue);
946 if (r < 0) {
947 log_syntax(unit, LOG_WARNING, filename, line, r,
948 "Failed to parse %s= setting. Ignoring assignment: %s",
949 lvalue, rvalue);
950 return 0;
951 }
952
953 a->sa.use_for_encoding = r;
954 if (a->sa.use_for_encoding > 0)
955 a->sa.activate = true;
956
957 TAKE_PTR(a);
958
959 return 0;
960 }
961
962 static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
963 _cleanup_(erase_and_freep) uint8_t *key = NULL;
964 size_t key_len;
965 int r;
966
967 assert(netdev);
968 assert(sa);
969
970 if (!sa->key_file)
971 return 0;
972
973 (void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0);
974
975 r = read_full_file_full(
976 AT_FDCWD, sa->key_file, UINT64_MAX, SIZE_MAX,
977 READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
978 NULL, (char **) &key, &key_len);
979 if (r < 0)
980 return log_netdev_error_errno(netdev, r,
981 "Failed to read key from '%s', ignoring: %m",
982 sa->key_file);
983
984 if (key_len != 16)
985 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
986 "Invalid key length (%zu bytes), ignoring: %m", key_len);
987
988 explicit_bzero_safe(sa->key, sa->key_len);
989 free_and_replace(sa->key, key);
990 sa->key_len = key_len;
991
992 return 0;
993 }
994
995 static int macsec_receive_channel_verify(ReceiveChannel *c) {
996 NetDev *netdev;
997 int r;
998
999 assert(c);
1000 assert(c->macsec);
1001
1002 netdev = NETDEV(c->macsec);
1003
1004 if (section_is_invalid(c->section))
1005 return -EINVAL;
1006
1007 if (ether_addr_is_null(&c->sci.mac))
1008 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1009 "%s: MACsec receive channel without MAC address configured. "
1010 "Ignoring [MACsecReceiveChannel] section from line %u",
1011 c->section->filename, c->section->line);
1012
1013 if (c->sci.port == 0)
1014 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1015 "%s: MACsec receive channel without port configured. "
1016 "Ignoring [MACsecReceiveChannel] section from line %u",
1017 c->section->filename, c->section->line);
1018
1019 r = ordered_hashmap_ensure_put(&c->macsec->receive_channels, &uint64_hash_ops, &c->sci.as_uint64, c);
1020 if (r == -ENOMEM)
1021 return log_oom();
1022 if (r == -EEXIST)
1023 return log_netdev_error_errno(netdev, r,
1024 "%s: Multiple [MACsecReceiveChannel] sections have same SCI, "
1025 "Ignoring [MACsecReceiveChannel] section from line %u",
1026 c->section->filename, c->section->line);
1027 if (r < 0)
1028 return log_netdev_error_errno(netdev, r,
1029 "%s: Failed to store [MACsecReceiveChannel] section at hashmap, "
1030 "Ignoring [MACsecReceiveChannel] section from line %u",
1031 c->section->filename, c->section->line);
1032 return 0;
1033 }
1034
1035 static int macsec_transmit_association_verify(TransmitAssociation *t) {
1036 NetDev *netdev;
1037 int r;
1038
1039 assert(t);
1040 assert(t->macsec);
1041
1042 netdev = NETDEV(t->macsec);
1043
1044 if (section_is_invalid(t->section))
1045 return -EINVAL;
1046
1047 if (t->sa.packet_number == 0)
1048 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1049 "%s: MACsec transmit secure association without PacketNumber= configured. "
1050 "Ignoring [MACsecTransmitAssociation] section from line %u",
1051 t->section->filename, t->section->line);
1052
1053 r = macsec_read_key_file(netdev, &t->sa);
1054 if (r < 0)
1055 return r;
1056
1057 if (t->sa.key_len <= 0)
1058 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1059 "%s: MACsec transmit secure association without key configured. "
1060 "Ignoring [MACsecTransmitAssociation] section from line %u",
1061 t->section->filename, t->section->line);
1062
1063 return 0;
1064 }
1065
1066 static int macsec_receive_association_verify(ReceiveAssociation *a) {
1067 ReceiveChannel *c;
1068 NetDev *netdev;
1069 int r;
1070
1071 assert(a);
1072 assert(a->macsec);
1073
1074 netdev = NETDEV(a->macsec);
1075
1076 if (section_is_invalid(a->section))
1077 return -EINVAL;
1078
1079 r = macsec_read_key_file(netdev, &a->sa);
1080 if (r < 0)
1081 return r;
1082
1083 if (a->sa.key_len <= 0)
1084 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1085 "%s: MACsec receive secure association without key configured. "
1086 "Ignoring [MACsecReceiveAssociation] section from line %u",
1087 a->section->filename, a->section->line);
1088
1089 if (ether_addr_is_null(&a->sci.mac))
1090 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1091 "%s: MACsec receive secure association without MAC address configured. "
1092 "Ignoring [MACsecReceiveAssociation] section from line %u",
1093 a->section->filename, a->section->line);
1094
1095 if (a->sci.port == 0)
1096 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
1097 "%s: MACsec receive secure association without port configured. "
1098 "Ignoring [MACsecReceiveAssociation] section from line %u",
1099 a->section->filename, a->section->line);
1100
1101 c = ordered_hashmap_get(a->macsec->receive_channels, &a->sci.as_uint64);
1102 if (!c) {
1103 _cleanup_(macsec_receive_channel_freep) ReceiveChannel *new_channel = NULL;
1104
1105 r = macsec_receive_channel_new(a->macsec, a->sci.as_uint64, &new_channel);
1106 if (r < 0)
1107 return log_oom();
1108
1109 r = ordered_hashmap_ensure_put(&a->macsec->receive_channels, &uint64_hash_ops, &new_channel->sci.as_uint64, new_channel);
1110 if (r == -ENOMEM)
1111 return log_oom();
1112 if (r < 0)
1113 return log_netdev_error_errno(netdev, r,
1114 "%s: Failed to store receive channel at hashmap, "
1115 "Ignoring [MACsecReceiveAssociation] section from line %u",
1116 a->section->filename, a->section->line);
1117 c = TAKE_PTR(new_channel);
1118 }
1119 if (c->n_rxsa >= MACSEC_MAX_ASSOCIATION_NUMBER)
1120 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(ERANGE),
1121 "%s: Too many [MACsecReceiveAssociation] sections for the same receive channel, "
1122 "Ignoring [MACsecReceiveAssociation] section from line %u",
1123 a->section->filename, a->section->line);
1124
1125 a->sa.association_number = c->n_rxsa;
1126 c->rxsa[c->n_rxsa++] = a;
1127
1128 return 0;
1129 }
1130
1131 static int netdev_macsec_verify(NetDev *netdev, const char *filename) {
1132 MACsec *v = MACSEC(netdev);
1133 TransmitAssociation *a;
1134 ReceiveAssociation *n;
1135 ReceiveChannel *c;
1136 uint8_t an, encoding_an;
1137 bool use_for_encoding;
1138 int r;
1139
1140 assert(netdev);
1141 assert(v);
1142 assert(filename);
1143
1144 ORDERED_HASHMAP_FOREACH(c, v->receive_channels_by_section) {
1145 r = macsec_receive_channel_verify(c);
1146 if (r < 0)
1147 macsec_receive_channel_free(c);
1148 }
1149
1150 an = 0;
1151 use_for_encoding = false;
1152 encoding_an = 0;
1153 ORDERED_HASHMAP_FOREACH(a, v->transmit_associations_by_section) {
1154 r = macsec_transmit_association_verify(a);
1155 if (r < 0) {
1156 macsec_transmit_association_free(a);
1157 continue;
1158 }
1159
1160 if (an >= MACSEC_MAX_ASSOCIATION_NUMBER) {
1161 log_netdev_error(netdev,
1162 "%s: Too many [MACsecTransmitAssociation] sections configured. "
1163 "Ignoring [MACsecTransmitAssociation] section from line %u",
1164 a->section->filename, a->section->line);
1165 macsec_transmit_association_free(a);
1166 continue;
1167 }
1168
1169 a->sa.association_number = an++;
1170
1171 if (a->sa.use_for_encoding > 0) {
1172 if (use_for_encoding) {
1173 log_netdev_warning(netdev,
1174 "%s: Multiple security associations are set to be used for transmit channel."
1175 "Disabling UseForEncoding= in [MACsecTransmitAssociation] section from line %u",
1176 a->section->filename, a->section->line);
1177 a->sa.use_for_encoding = false;
1178 } else {
1179 encoding_an = a->sa.association_number;
1180 use_for_encoding = true;
1181 }
1182 }
1183 }
1184
1185 assert(encoding_an < MACSEC_MAX_ASSOCIATION_NUMBER);
1186 v->encoding_an = encoding_an;
1187
1188 ORDERED_HASHMAP_FOREACH(n, v->receive_associations_by_section) {
1189 r = macsec_receive_association_verify(n);
1190 if (r < 0)
1191 macsec_receive_association_free(n);
1192 }
1193
1194 return 0;
1195 }
1196
1197 static void macsec_init(NetDev *netdev) {
1198 MACsec *v;
1199
1200 assert(netdev);
1201
1202 v = MACSEC(netdev);
1203
1204 assert(v);
1205
1206 v->encrypt = -1;
1207 }
1208
1209 static void macsec_done(NetDev *netdev) {
1210 MACsec *t;
1211
1212 assert(netdev);
1213
1214 t = MACSEC(netdev);
1215
1216 assert(t);
1217
1218 ordered_hashmap_free_with_destructor(t->receive_channels, macsec_receive_channel_free);
1219 ordered_hashmap_free_with_destructor(t->receive_channels_by_section, macsec_receive_channel_free);
1220 ordered_hashmap_free_with_destructor(t->transmit_associations_by_section, macsec_transmit_association_free);
1221 ordered_hashmap_free_with_destructor(t->receive_associations_by_section, macsec_receive_association_free);
1222 }
1223
1224 const NetDevVTable macsec_vtable = {
1225 .object_size = sizeof(MACsec),
1226 .init = macsec_init,
1227 .sections = NETDEV_COMMON_SECTIONS "MACsec\0MACsecReceiveChannel\0MACsecTransmitAssociation\0MACsecReceiveAssociation\0",
1228 .fill_message_create = netdev_macsec_fill_message_create,
1229 .post_create = netdev_macsec_configure,
1230 .done = macsec_done,
1231 .create_type = NETDEV_CREATE_STACKED,
1232 .config_verify = netdev_macsec_verify,
1233 .iftype = ARPHRD_ETHER,
1234 .generate_mac = true,
1235 };