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