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