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