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