]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/ipmacsec.c
vdpa: add .gitignore
[mirror_iproute2.git] / ip / ipmacsec.c
1 /*
2 * ipmacsec.c "ip macsec".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Sabrina Dubroca <sd@queasysnail.net>
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <linux/genetlink.h>
17 #include <linux/if_ether.h>
18 #include <linux/if_macsec.h>
19
20 #include "rt_names.h"
21 #include "utils.h"
22 #include "ip_common.h"
23 #include "ll_map.h"
24 #include "libgenl.h"
25
26 static const char * const validate_str[] = {
27 [MACSEC_VALIDATE_DISABLED] = "disabled",
28 [MACSEC_VALIDATE_CHECK] = "check",
29 [MACSEC_VALIDATE_STRICT] = "strict",
30 };
31
32 static const char * const offload_str[] = {
33 [MACSEC_OFFLOAD_OFF] = "off",
34 [MACSEC_OFFLOAD_PHY] = "phy",
35 [MACSEC_OFFLOAD_MAC] = "mac",
36 };
37
38 struct sci {
39 __u64 sci;
40 __u16 port;
41 char abuf[6];
42 };
43
44 struct sa_desc {
45 __u8 an;
46 __u32 pn;
47 __u8 key_id[MACSEC_KEYID_LEN];
48 __u32 key_len;
49 __u8 key[MACSEC_MAX_KEY_LEN];
50 __u8 active;
51 };
52
53 struct cipher_args {
54 __u64 id;
55 __u8 icv_len;
56 };
57
58 struct txsc_desc {
59 int ifindex;
60 __u64 sci;
61 __be16 port;
62 struct cipher_args cipher;
63 __u32 window;
64 enum macsec_validation_type validate;
65 __u8 encoding_sa;
66 };
67
68 struct rxsc_desc {
69 int ifindex;
70 __u64 sci;
71 __u8 active;
72 };
73
74 #define MACSEC_BUFLEN 1024
75
76
77 /* netlink socket */
78 static struct rtnl_handle genl_rth;
79 static int genl_family = -1;
80
81 #define MACSEC_GENL_REQ(_req, _bufsiz, _cmd, _flags) \
82 GENL_REQUEST(_req, _bufsiz, genl_family, 0, MACSEC_GENL_VERSION, \
83 _cmd, _flags)
84
85
86 static void ipmacsec_usage(void)
87 {
88 fprintf(stderr,
89 "Usage: ip macsec add DEV tx sa { 0..3 } [ OPTS ] key ID KEY\n"
90 " ip macsec set DEV tx sa { 0..3 } [ OPTS ]\n"
91 " ip macsec del DEV tx sa { 0..3 }\n"
92 " ip macsec add DEV rx SCI [ on | off ]\n"
93 " ip macsec set DEV rx SCI [ on | off ]\n"
94 " ip macsec del DEV rx SCI\n"
95 " ip macsec add DEV rx SCI sa { 0..3 } [ OPTS ] key ID KEY\n"
96 " ip macsec set DEV rx SCI sa { 0..3 } [ OPTS ]\n"
97 " ip macsec del DEV rx SCI sa { 0..3 }\n"
98 " ip macsec show\n"
99 " ip macsec show DEV\n"
100 " ip macsec offload DEV [ off | phy | mac ]\n"
101 "where OPTS := [ pn <u32> ] [ on | off ]\n"
102 " ID := 128-bit hex string\n"
103 " KEY := 128-bit or 256-bit hex string\n"
104 " SCI := { sci <u64> | port { 1..2^16-1 } address <lladdr> }\n");
105
106 exit(-1);
107 }
108
109 static int get_an(__u8 *val, const char *arg)
110 {
111 int ret = get_u8(val, arg, 0);
112
113 if (ret)
114 return ret;
115
116 if (*val > 3)
117 return -1;
118
119 return 0;
120 }
121
122 static int get_sci(__u64 *sci, const char *arg)
123 {
124 return get_be64(sci, arg, 16);
125 }
126
127 static int get_port(__be16 *port, const char *arg)
128 {
129 return get_be16(port, arg, 0);
130 }
131
132 #define _STR(a) #a
133 #define STR(a) _STR(a)
134
135 static void get_icvlen(__u8 *icvlen, char *arg)
136 {
137 int ret = get_u8(icvlen, arg, 10);
138
139 if (ret)
140 invarg("expected ICV length", arg);
141
142 if (*icvlen < MACSEC_MIN_ICV_LEN || *icvlen > MACSEC_STD_ICV_LEN)
143 invarg("ICV length must be in the range {"
144 STR(MACSEC_MIN_ICV_LEN) ".." STR(MACSEC_STD_ICV_LEN)
145 "}", arg);
146 }
147
148 static bool get_sa(int *argcp, char ***argvp, __u8 *an)
149 {
150 int argc = *argcp;
151 char **argv = *argvp;
152 int ret;
153
154 if (argc <= 0 || strcmp(*argv, "sa") != 0)
155 return false;
156
157 NEXT_ARG();
158 ret = get_an(an, *argv);
159 if (ret)
160 invarg("expected an { 0..3 }", *argv);
161 argc--; argv++;
162
163 *argvp = argv;
164 *argcp = argc;
165 return true;
166 }
167
168 static int parse_sa_args(int *argcp, char ***argvp, struct sa_desc *sa)
169 {
170 int argc = *argcp;
171 char **argv = *argvp;
172 int ret;
173 bool active_set = false;
174
175 while (argc > 0) {
176 if (strcmp(*argv, "pn") == 0) {
177 if (sa->pn != 0)
178 duparg2("pn", "pn");
179 NEXT_ARG();
180 ret = get_u32(&sa->pn, *argv, 0);
181 if (ret)
182 invarg("expected pn", *argv);
183 if (sa->pn == 0)
184 invarg("expected pn != 0", *argv);
185 } else if (strcmp(*argv, "key") == 0) {
186 unsigned int len;
187
188 NEXT_ARG();
189 if (!hexstring_a2n(*argv, sa->key_id, MACSEC_KEYID_LEN,
190 &len))
191 invarg("expected key id", *argv);
192 NEXT_ARG();
193 if (!hexstring_a2n(*argv, sa->key, MACSEC_MAX_KEY_LEN,
194 &sa->key_len))
195 invarg("expected key", *argv);
196 } else if (strcmp(*argv, "on") == 0) {
197 if (active_set)
198 duparg2("on/off", "on");
199 sa->active = true;
200 active_set = true;
201 } else if (strcmp(*argv, "off") == 0) {
202 if (active_set)
203 duparg2("on/off", "off");
204 sa->active = false;
205 active_set = true;
206 } else {
207 fprintf(stderr, "macsec: unknown command \"%s\"?\n",
208 *argv);
209 ipmacsec_usage();
210 }
211
212 argv++; argc--;
213 }
214
215 *argvp = argv;
216 *argcp = argc;
217 return 0;
218 }
219
220 static __u64 make_sci(char *addr, __be16 port)
221 {
222 __u64 sci;
223
224 memcpy(&sci, addr, ETH_ALEN);
225 memcpy(((char *)&sci) + ETH_ALEN, &port, sizeof(port));
226
227 return sci;
228 }
229
230 static bool sci_complete(bool sci, bool port, bool addr, bool port_only)
231 {
232 return sci || (port && (addr || port_only));
233 }
234
235 static int get_sci_portaddr(struct sci *sci, int *argcp, char ***argvp,
236 bool port_only, bool optional)
237 {
238 int argc = *argcp;
239 char **argv = *argvp;
240 int ret;
241 bool p = false, a = false, s = false;
242
243 while (argc > 0) {
244 if (strcmp(*argv, "sci") == 0) {
245 if (p)
246 invarg("expected address", *argv);
247 if (a)
248 invarg("expected port", *argv);
249 NEXT_ARG();
250 ret = get_sci(&sci->sci, *argv);
251 if (ret)
252 invarg("expected sci", *argv);
253 s = true;
254 } else if (strcmp(*argv, "port") == 0) {
255 NEXT_ARG();
256 ret = get_port(&sci->port, *argv);
257 if (ret)
258 invarg("expected port", *argv);
259 if (sci->port == 0)
260 invarg("expected port != 0", *argv);
261 p = true;
262 } else if (strcmp(*argv, "address") == 0) {
263 NEXT_ARG();
264 ret = ll_addr_a2n(sci->abuf, sizeof(sci->abuf), *argv);
265 if (ret < 0)
266 invarg("expected lladdr", *argv);
267 a = true;
268 } else if (optional) {
269 break;
270 } else {
271 invarg("expected sci, port, or address", *argv);
272 }
273
274 argv++; argc--;
275
276 if (sci_complete(s, p, a, port_only))
277 break;
278 }
279
280 if (!optional && !sci_complete(s, p, a, port_only))
281 return -1;
282
283 if (p && a)
284 sci->sci = make_sci(sci->abuf, sci->port);
285
286 *argvp = argv;
287 *argcp = argc;
288
289 return p || a || s;
290 }
291
292 static bool parse_rxsci(int *argcp, char ***argvp, struct rxsc_desc *rxsc,
293 struct sa_desc *rxsa)
294 {
295 struct sci sci = { 0 };
296
297 if (*argcp == 0 ||
298 get_sci_portaddr(&sci, argcp, argvp, false, false) < 0) {
299 fprintf(stderr, "expected sci\n");
300 ipmacsec_usage();
301 }
302
303 rxsc->sci = sci.sci;
304
305 return get_sa(argcp, argvp, &rxsa->an);
306 }
307
308 static int parse_rxsci_args(int *argcp, char ***argvp, struct rxsc_desc *rxsc)
309 {
310 int argc = *argcp;
311 char **argv = *argvp;
312 bool active_set = false;
313
314 while (argc > 0) {
315 if (strcmp(*argv, "on") == 0) {
316 if (active_set)
317 duparg2("on/off", "on");
318 rxsc->active = true;
319 active_set = true;
320 } else if (strcmp(*argv, "off") == 0) {
321 if (active_set)
322 duparg2("on/off", "off");
323 rxsc->active = false;
324 active_set = true;
325 } else {
326 fprintf(stderr, "macsec: unknown command \"%s\"?\n",
327 *argv);
328 ipmacsec_usage();
329 }
330
331 argv++; argc--;
332 }
333
334 *argvp = argv;
335 *argcp = argc;
336 return 0;
337 }
338
339 enum cmd {
340 CMD_ADD,
341 CMD_DEL,
342 CMD_UPD,
343 CMD_OFFLOAD,
344 __CMD_MAX
345 };
346
347 static const enum macsec_nl_commands macsec_commands[__CMD_MAX][2][2] = {
348 [CMD_ADD] = {
349 [0] = {-1, MACSEC_CMD_ADD_RXSC},
350 [1] = {MACSEC_CMD_ADD_TXSA, MACSEC_CMD_ADD_RXSA},
351 },
352 [CMD_UPD] = {
353 [0] = {-1, MACSEC_CMD_UPD_RXSC},
354 [1] = {MACSEC_CMD_UPD_TXSA, MACSEC_CMD_UPD_RXSA},
355 },
356 [CMD_DEL] = {
357 [0] = {-1, MACSEC_CMD_DEL_RXSC},
358 [1] = {MACSEC_CMD_DEL_TXSA, MACSEC_CMD_DEL_RXSA},
359 },
360 [CMD_OFFLOAD] = {
361 [0] = {-1, MACSEC_CMD_UPD_OFFLOAD },
362 },
363 };
364
365 static int do_modify_nl(enum cmd c, enum macsec_nl_commands cmd, int ifindex,
366 struct rxsc_desc *rxsc, struct sa_desc *sa)
367 {
368 struct rtattr *attr_sa;
369
370 MACSEC_GENL_REQ(req, MACSEC_BUFLEN, cmd, NLM_F_REQUEST);
371
372 addattr32(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_IFINDEX, ifindex);
373 if (rxsc) {
374 struct rtattr *attr_rxsc;
375
376 attr_rxsc = addattr_nest(&req.n, MACSEC_BUFLEN,
377 MACSEC_ATTR_RXSC_CONFIG);
378 addattr64(&req.n, MACSEC_BUFLEN,
379 MACSEC_RXSC_ATTR_SCI, rxsc->sci);
380 if (c != CMD_DEL && rxsc->active != 0xff)
381 addattr8(&req.n, MACSEC_BUFLEN,
382 MACSEC_RXSC_ATTR_ACTIVE, rxsc->active);
383
384 addattr_nest_end(&req.n, attr_rxsc);
385 }
386
387 if (sa->an == 0xff)
388 goto talk;
389
390 attr_sa = addattr_nest(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_SA_CONFIG);
391
392 addattr8(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_AN, sa->an);
393
394 if (c != CMD_DEL) {
395 if (sa->pn)
396 addattr32(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_PN,
397 sa->pn);
398
399 if (sa->key_len) {
400 addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEYID,
401 sa->key_id, MACSEC_KEYID_LEN);
402 addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEY,
403 sa->key, sa->key_len);
404 }
405
406 if (sa->active != 0xff) {
407 addattr8(&req.n, MACSEC_BUFLEN,
408 MACSEC_SA_ATTR_ACTIVE, sa->active);
409 }
410 }
411
412 addattr_nest_end(&req.n, attr_sa);
413
414 talk:
415 if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
416 return -2;
417
418 return 0;
419 }
420
421 static bool check_sa_args(enum cmd c, struct sa_desc *sa)
422 {
423 if (c == CMD_ADD) {
424 if (!sa->key_len) {
425 fprintf(stderr, "cannot create SA without key\n");
426 return -1;
427 }
428
429 if (sa->pn == 0) {
430 fprintf(stderr, "must specify a packet number != 0\n");
431 return -1;
432 }
433 } else if (c == CMD_UPD) {
434 if (sa->key_len) {
435 fprintf(stderr, "cannot change key on SA\n");
436 return -1;
437 }
438 }
439
440 return 0;
441 }
442
443 static int do_modify_txsa(enum cmd c, int argc, char **argv, int ifindex)
444 {
445 struct sa_desc txsa = {0};
446 enum macsec_nl_commands cmd;
447
448 txsa.an = 0xff;
449 txsa.active = 0xff;
450
451 if (argc == 0 || !get_sa(&argc, &argv, &txsa.an))
452 ipmacsec_usage();
453
454 if (c == CMD_DEL)
455 goto modify;
456
457 if (parse_sa_args(&argc, &argv, &txsa))
458 return -1;
459
460 if (check_sa_args(c, &txsa))
461 return -1;
462
463 modify:
464 cmd = macsec_commands[c][1][0];
465 return do_modify_nl(c, cmd, ifindex, NULL, &txsa);
466 }
467
468 static int do_modify_rxsci(enum cmd c, int argc, char **argv, int ifindex)
469 {
470 struct rxsc_desc rxsc = {0};
471 struct sa_desc rxsa = {0};
472 bool sa_set;
473 enum macsec_nl_commands cmd;
474
475 rxsc.ifindex = ifindex;
476 rxsc.active = 0xff;
477 rxsa.an = 0xff;
478 rxsa.active = 0xff;
479
480 sa_set = parse_rxsci(&argc, &argv, &rxsc, &rxsa);
481
482 if (c == CMD_DEL)
483 goto modify;
484
485 if (sa_set && (parse_sa_args(&argc, &argv, &rxsa) ||
486 check_sa_args(c, &rxsa)))
487 return -1;
488 if (!sa_set && parse_rxsci_args(&argc, &argv, &rxsc))
489 return -1;
490
491 modify:
492 cmd = macsec_commands[c][sa_set][1];
493 return do_modify_nl(c, cmd, rxsc.ifindex, &rxsc, &rxsa);
494 }
495
496 static int do_modify(enum cmd c, int argc, char **argv)
497 {
498 int ifindex;
499
500 if (argc == 0)
501 ipmacsec_usage();
502
503 ifindex = ll_name_to_index(*argv);
504 if (!ifindex) {
505 fprintf(stderr, "Device \"%s\" does not exist.\n", *argv);
506 return -1;
507 }
508 argc--; argv++;
509
510 if (argc == 0)
511 ipmacsec_usage();
512
513 if (strcmp(*argv, "tx") == 0)
514 return do_modify_txsa(c, argc-1, argv+1, ifindex);
515 if (strcmp(*argv, "rx") == 0)
516 return do_modify_rxsci(c, argc-1, argv+1, ifindex);
517
518 ipmacsec_usage();
519 return -1;
520 }
521
522 static int do_offload(enum cmd c, int argc, char **argv)
523 {
524 enum macsec_offload offload;
525 struct rtattr *attr;
526 int ifindex, ret;
527
528 if (argc == 0)
529 ipmacsec_usage();
530
531 ifindex = ll_name_to_index(*argv);
532 if (!ifindex) {
533 fprintf(stderr, "Device \"%s\" does not exist.\n", *argv);
534 return -1;
535 }
536 argc--; argv++;
537
538 if (argc == 0)
539 ipmacsec_usage();
540
541 offload = parse_one_of("offload", *argv, offload_str, ARRAY_SIZE(offload_str), &ret);
542 if (ret)
543 ipmacsec_usage();
544
545 MACSEC_GENL_REQ(req, MACSEC_BUFLEN, macsec_commands[c][0][1], NLM_F_REQUEST);
546
547 addattr32(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_IFINDEX, ifindex);
548
549 attr = addattr_nest(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_OFFLOAD);
550 addattr8(&req.n, MACSEC_BUFLEN, MACSEC_OFFLOAD_ATTR_TYPE, offload);
551 addattr_nest_end(&req.n, attr);
552
553 if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
554 return -2;
555
556 return 0;
557 }
558
559 /* dump/show */
560 static struct {
561 int ifindex;
562 __u64 sci;
563 } filter;
564
565 static int validate_dump(struct rtattr **attrs)
566 {
567 return attrs[MACSEC_ATTR_IFINDEX] && attrs[MACSEC_ATTR_SECY] &&
568 attrs[MACSEC_ATTR_TXSA_LIST] && attrs[MACSEC_ATTR_RXSC_LIST] &&
569 attrs[MACSEC_ATTR_TXSC_STATS] && attrs[MACSEC_ATTR_SECY_STATS];
570
571 }
572
573 static int validate_secy_dump(struct rtattr **attrs)
574 {
575 return attrs[MACSEC_SECY_ATTR_SCI] &&
576 attrs[MACSEC_SECY_ATTR_ENCODING_SA] &&
577 attrs[MACSEC_SECY_ATTR_CIPHER_SUITE] &&
578 attrs[MACSEC_SECY_ATTR_ICV_LEN] &&
579 attrs[MACSEC_SECY_ATTR_PROTECT] &&
580 attrs[MACSEC_SECY_ATTR_REPLAY] &&
581 attrs[MACSEC_SECY_ATTR_OPER] &&
582 attrs[MACSEC_SECY_ATTR_VALIDATE] &&
583 attrs[MACSEC_SECY_ATTR_ENCRYPT] &&
584 attrs[MACSEC_SECY_ATTR_INC_SCI] &&
585 attrs[MACSEC_SECY_ATTR_ES] &&
586 attrs[MACSEC_SECY_ATTR_SCB];
587 }
588
589 static void print_flag(struct rtattr *attrs[], const char *desc,
590 int field)
591 {
592 __u8 flag;
593
594 if (!attrs[field])
595 return;
596
597 flag = rta_getattr_u8(attrs[field]);
598 if (is_json_context())
599 print_bool(PRINT_JSON, desc, NULL, flag);
600 else {
601 print_string(PRINT_FP, NULL, "%s ", desc);
602 print_string(PRINT_FP, NULL, "%s ",
603 flag ? "on" : "off");
604 }
605 }
606
607 static void print_key(struct rtattr *key)
608 {
609 SPRINT_BUF(keyid);
610
611 print_string(PRINT_ANY, "key", " key %s\n",
612 hexstring_n2a(RTA_DATA(key), RTA_PAYLOAD(key),
613 keyid, sizeof(keyid)));
614 }
615
616 #define CIPHER_NAME_GCM_AES_128 "GCM-AES-128"
617 #define CIPHER_NAME_GCM_AES_256 "GCM-AES-256"
618 #define DEFAULT_CIPHER_NAME CIPHER_NAME_GCM_AES_128
619
620 static const char *cs_id_to_name(__u64 cid)
621 {
622 switch (cid) {
623 case MACSEC_DEFAULT_CIPHER_ID:
624 return DEFAULT_CIPHER_NAME;
625 case MACSEC_CIPHER_ID_GCM_AES_128:
626 /* MACSEC_DEFAULT_CIPHER_ALT: */
627 return CIPHER_NAME_GCM_AES_128;
628 case MACSEC_CIPHER_ID_GCM_AES_256:
629 return CIPHER_NAME_GCM_AES_256;
630 default:
631 return "(unknown)";
632 }
633 }
634
635 static const char *validate_to_str(__u8 validate)
636 {
637 if (validate >= ARRAY_SIZE(validate_str))
638 return "(unknown)";
639
640 return validate_str[validate];
641 }
642
643 static const char *offload_to_str(__u8 offload)
644 {
645 if (offload >= ARRAY_SIZE(offload_str))
646 return "(unknown)";
647
648 return offload_str[offload];
649 }
650
651 static void print_attrs(struct rtattr *attrs[])
652 {
653 print_flag(attrs, "protect", MACSEC_SECY_ATTR_PROTECT);
654
655 if (attrs[MACSEC_SECY_ATTR_VALIDATE]) {
656 __u8 val = rta_getattr_u8(attrs[MACSEC_SECY_ATTR_VALIDATE]);
657
658 print_string(PRINT_ANY, "validate",
659 "validate %s ", validate_to_str(val));
660 }
661
662 print_flag(attrs, "sc", MACSEC_RXSC_ATTR_ACTIVE);
663 print_flag(attrs, "sa", MACSEC_SA_ATTR_ACTIVE);
664 print_flag(attrs, "encrypt", MACSEC_SECY_ATTR_ENCRYPT);
665 print_flag(attrs, "send_sci", MACSEC_SECY_ATTR_INC_SCI);
666 print_flag(attrs, "end_station", MACSEC_SECY_ATTR_ES);
667 print_flag(attrs, "scb", MACSEC_SECY_ATTR_SCB);
668 print_flag(attrs, "replay", MACSEC_SECY_ATTR_REPLAY);
669
670 if (attrs[MACSEC_SECY_ATTR_WINDOW]) {
671 __u32 win = rta_getattr_u32(attrs[MACSEC_SECY_ATTR_WINDOW]);
672
673 print_uint(PRINT_ANY, "window", "window %u ", win);
674 }
675
676 if (attrs[MACSEC_SECY_ATTR_CIPHER_SUITE]) {
677 __u64 cid = rta_getattr_u64(attrs[MACSEC_SECY_ATTR_CIPHER_SUITE]);
678
679 print_nl();
680 print_string(PRINT_ANY, "cipher_suite",
681 " cipher suite: %s,", cs_id_to_name(cid));
682 }
683
684 if (attrs[MACSEC_SECY_ATTR_ICV_LEN]) {
685 __u8 icv_len = rta_getattr_u8(attrs[MACSEC_SECY_ATTR_ICV_LEN]);
686
687 print_uint(PRINT_ANY, "icv_length",
688 " using ICV length %u\n", icv_len);
689 }
690 }
691
692 static __u64 getattr_u64(const struct rtattr *stat)
693 {
694 size_t len = RTA_PAYLOAD(stat);
695
696 switch (len) {
697 case sizeof(__u64):
698 return rta_getattr_u64(stat);
699 case sizeof(__u32):
700 return rta_getattr_u32(stat);
701 case sizeof(__u16):
702 return rta_getattr_u16(stat);
703 case sizeof(__u8):
704 return rta_getattr_u8(stat);
705 default:
706 fprintf(stderr, "invalid attribute length %zu\n",
707 len);
708 exit(-1);
709 }
710 }
711
712 static void print_fp_stats(const char *prefix,
713 const char *names[], unsigned int num,
714 struct rtattr *stats[])
715 {
716 unsigned int i;
717 int pad;
718
719 printf("%sstats:", prefix);
720
721 for (i = 1; i < num; i++) {
722 if (!names[i])
723 continue;
724 printf(" %s", names[i]);
725 }
726
727 printf("\n%s ", prefix);
728
729 for (i = 1; i < num; i++) {
730 if (!names[i])
731 continue;
732
733 pad = strlen(names[i]) + 1;
734 if (stats[i])
735 printf("%*llu", pad, getattr_u64(stats[i]));
736 else
737 printf("%*c", pad, '-');
738 }
739 printf("\n");
740 }
741
742 static void print_json_stats(const char *names[], unsigned int num,
743 struct rtattr *stats[])
744 {
745 unsigned int i;
746
747 for (i = 1; i < num; i++) {
748 if (!names[i] || !stats[i])
749 continue;
750
751 print_u64(PRINT_JSON, names[i],
752 NULL, getattr_u64(stats[i]));
753 }
754 }
755
756 static void print_stats(const char *prefix,
757 const char *names[], unsigned int num,
758 struct rtattr *stats[])
759 {
760
761 if (is_json_context())
762 print_json_stats(names, num, stats);
763 else
764 print_fp_stats(prefix, names, num, stats);
765 }
766
767 static const char *txsc_stats_names[NUM_MACSEC_TXSC_STATS_ATTR] = {
768 [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected",
769 [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted",
770 [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED] = "OutOctetsProtected",
771 [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED] = "OutOctetsEncrypted",
772 };
773
774 static void print_txsc_stats(const char *prefix, struct rtattr *attr)
775 {
776 struct rtattr *stats[MACSEC_TXSC_STATS_ATTR_MAX + 1];
777
778 if (!attr || show_stats == 0)
779 return;
780
781 parse_rtattr_nested(stats, MACSEC_TXSC_STATS_ATTR_MAX, attr);
782
783 print_stats(prefix, txsc_stats_names, NUM_MACSEC_TXSC_STATS_ATTR,
784 stats);
785 }
786
787 static const char *secy_stats_names[NUM_MACSEC_SECY_STATS_ATTR] = {
788 [MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED] = "OutPktsUntagged",
789 [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED] = "InPktsUntagged",
790 [MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG] = "OutPktsTooLong",
791 [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG] = "InPktsNoTag",
792 [MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG] = "InPktsBadTag",
793 [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI] = "InPktsUnknownSCI",
794 [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI] = "InPktsNoSCI",
795 [MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN] = "InPktsOverrun",
796 };
797
798 static void print_secy_stats(const char *prefix, struct rtattr *attr)
799 {
800 struct rtattr *stats[MACSEC_SECY_STATS_ATTR_MAX + 1];
801
802 if (!attr || show_stats == 0)
803 return;
804
805 parse_rtattr_nested(stats, MACSEC_SECY_STATS_ATTR_MAX, attr);
806
807 print_stats(prefix, secy_stats_names,
808 NUM_MACSEC_SECY_STATS_ATTR, stats);
809 }
810
811 static const char *rxsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = {
812 [MACSEC_SA_STATS_ATTR_IN_PKTS_OK] = "InPktsOK",
813 [MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid",
814 [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid",
815 [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA",
816 [MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA",
817 };
818
819 static void print_rxsa_stats(const char *prefix, struct rtattr *attr)
820 {
821 struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1];
822
823 if (!attr || show_stats == 0)
824 return;
825
826 parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX, attr);
827
828 print_stats(prefix, rxsa_stats_names, NUM_MACSEC_SA_STATS_ATTR, stats);
829 }
830
831 static const char *txsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = {
832 [MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected",
833 [MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted",
834 };
835
836 static void print_txsa_stats(const char *prefix, struct rtattr *attr)
837 {
838 struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1];
839
840 if (!attr || show_stats == 0)
841 return;
842
843 parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX, attr);
844
845 print_stats(prefix, txsa_stats_names, NUM_MACSEC_SA_STATS_ATTR, stats);
846 }
847
848 static void print_tx_sc(const char *prefix, __u64 sci, __u8 encoding_sa,
849 struct rtattr *txsc_stats, struct rtattr *secy_stats,
850 struct rtattr *sa)
851 {
852 struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1];
853 struct rtattr *a;
854 int rem;
855
856 print_string(PRINT_FP, NULL, "%s", prefix);
857 print_0xhex(PRINT_ANY, "sci",
858 "TXSC: %016llx", ntohll(sci));
859 print_uint(PRINT_ANY, "encoding_sa",
860 " on SA %d\n", encoding_sa);
861
862 print_secy_stats(prefix, secy_stats);
863 print_txsc_stats(prefix, txsc_stats);
864
865 open_json_array(PRINT_JSON, "sa_list");
866 rem = RTA_PAYLOAD(sa);
867 for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
868 bool state;
869
870 open_json_object(NULL);
871 parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX, a);
872 state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]);
873
874 print_string(PRINT_FP, NULL, "%s", prefix);
875 print_string(PRINT_FP, NULL, "%s", prefix);
876 print_uint(PRINT_ANY, "an", "%d:",
877 rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN]));
878 print_uint(PRINT_ANY, "pn", " PN %u,",
879 rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN]));
880
881 print_bool(PRINT_JSON, "active", NULL, state);
882 print_string(PRINT_FP, NULL,
883 " state %s,", state ? "on" : "off");
884 print_key(sa_attr[MACSEC_SA_ATTR_KEYID]);
885
886 print_txsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]);
887 close_json_object();
888 }
889 close_json_array(PRINT_JSON, NULL);
890 }
891
892 static const char *rxsc_stats_names[NUM_MACSEC_RXSC_STATS_ATTR] = {
893 [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED] = "InOctetsValidated",
894 [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED] = "InOctetsDecrypted",
895 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED] = "InPktsUnchecked",
896 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED] = "InPktsDelayed",
897 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK] = "InPktsOK",
898 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid",
899 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE] = "InPktsLate",
900 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid",
901 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA",
902 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA",
903 };
904
905 static void print_rxsc_stats(const char *prefix, struct rtattr *attr)
906 {
907 struct rtattr *stats[MACSEC_RXSC_STATS_ATTR_MAX + 1];
908
909 if (!attr || show_stats == 0)
910 return;
911
912 parse_rtattr_nested(stats, MACSEC_RXSC_STATS_ATTR_MAX, attr);
913
914 print_stats(prefix, rxsc_stats_names,
915 NUM_MACSEC_RXSC_STATS_ATTR, stats);
916 }
917
918 static void print_rx_sc(const char *prefix, __be64 sci, __u8 active,
919 struct rtattr *rxsc_stats, struct rtattr *sa)
920 {
921 struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1];
922 struct rtattr *a;
923 int rem;
924
925 print_string(PRINT_FP, NULL, "%s", prefix);
926 print_0xhex(PRINT_ANY, "sci",
927 "RXSC: %016llx,", ntohll(sci));
928 print_bool(PRINT_JSON, "active", NULL, active);
929 print_string(PRINT_FP, NULL,
930 " state %s\n", active ? "on" : "off");
931 print_rxsc_stats(prefix, rxsc_stats);
932
933 open_json_array(PRINT_JSON, "sa_list");
934 rem = RTA_PAYLOAD(sa);
935 for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
936 bool state;
937
938 open_json_object(NULL);
939 parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX, a);
940 state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]);
941
942 print_string(PRINT_FP, NULL, "%s", prefix);
943 print_string(PRINT_FP, NULL, "%s", prefix);
944 print_uint(PRINT_ANY, "an", "%u:",
945 rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN]));
946 print_uint(PRINT_ANY, "pn", " PN %u,",
947 rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN]));
948
949 print_bool(PRINT_JSON, "active", NULL, state);
950 print_string(PRINT_FP, NULL, " state %s,",
951 state ? "on" : "off");
952
953 print_key(sa_attr[MACSEC_SA_ATTR_KEYID]);
954
955 print_rxsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]);
956 close_json_object();
957 }
958 close_json_array(PRINT_JSON, NULL);
959 }
960
961 static void print_rxsc_list(struct rtattr *sc)
962 {
963 int rem = RTA_PAYLOAD(sc);
964 struct rtattr *c;
965
966 open_json_array(PRINT_JSON, "rx_sc");
967 for (c = RTA_DATA(sc); RTA_OK(c, rem); c = RTA_NEXT(c, rem)) {
968 struct rtattr *sc_attr[MACSEC_RXSC_ATTR_MAX + 1];
969
970 open_json_object(NULL);
971
972 parse_rtattr_nested(sc_attr, MACSEC_RXSC_ATTR_MAX, c);
973 print_rx_sc(" ",
974 rta_getattr_u64(sc_attr[MACSEC_RXSC_ATTR_SCI]),
975 rta_getattr_u32(sc_attr[MACSEC_RXSC_ATTR_ACTIVE]),
976 sc_attr[MACSEC_RXSC_ATTR_STATS],
977 sc_attr[MACSEC_RXSC_ATTR_SA_LIST]);
978 close_json_object();
979 }
980 close_json_array(PRINT_JSON, NULL);
981 }
982
983 static int process(struct nlmsghdr *n, void *arg)
984 {
985 struct genlmsghdr *ghdr;
986 struct rtattr *attrs[MACSEC_ATTR_MAX + 1];
987 struct rtattr *attrs_secy[MACSEC_SECY_ATTR_MAX + 1];
988 int len = n->nlmsg_len;
989 int ifindex;
990 __u64 sci;
991 __u8 encoding_sa;
992
993 if (n->nlmsg_type != genl_family)
994 return -1;
995
996 len -= NLMSG_LENGTH(GENL_HDRLEN);
997 if (len < 0)
998 return -1;
999
1000 ghdr = NLMSG_DATA(n);
1001 if (ghdr->cmd != MACSEC_CMD_GET_TXSC)
1002 return 0;
1003
1004 parse_rtattr(attrs, MACSEC_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
1005 if (!validate_dump(attrs)) {
1006 fprintf(stderr, "incomplete dump message\n");
1007 return -1;
1008 }
1009
1010 ifindex = rta_getattr_u32(attrs[MACSEC_ATTR_IFINDEX]);
1011 parse_rtattr_nested(attrs_secy, MACSEC_SECY_ATTR_MAX,
1012 attrs[MACSEC_ATTR_SECY]);
1013
1014 if (!validate_secy_dump(attrs_secy)) {
1015 fprintf(stderr, "incomplete dump message\n");
1016 return -1;
1017 }
1018
1019 sci = rta_getattr_u64(attrs_secy[MACSEC_SECY_ATTR_SCI]);
1020 encoding_sa = rta_getattr_u8(attrs_secy[MACSEC_SECY_ATTR_ENCODING_SA]);
1021
1022 if (filter.ifindex && ifindex != filter.ifindex)
1023 return 0;
1024
1025 if (filter.sci && sci != filter.sci)
1026 return 0;
1027
1028 open_json_object(NULL);
1029 print_uint(PRINT_ANY, "ifindex", "%u: ", ifindex);
1030 print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
1031 "%s: ", ll_index_to_name(ifindex));
1032
1033 print_attrs(attrs_secy);
1034
1035 print_tx_sc(" ", sci, encoding_sa,
1036 attrs[MACSEC_ATTR_TXSC_STATS],
1037 attrs[MACSEC_ATTR_SECY_STATS],
1038 attrs[MACSEC_ATTR_TXSA_LIST]);
1039
1040 if (attrs[MACSEC_ATTR_RXSC_LIST])
1041 print_rxsc_list(attrs[MACSEC_ATTR_RXSC_LIST]);
1042
1043 if (attrs[MACSEC_ATTR_OFFLOAD]) {
1044 struct rtattr *attrs_offload[MACSEC_OFFLOAD_ATTR_MAX + 1];
1045 __u8 offload;
1046
1047 parse_rtattr_nested(attrs_offload, MACSEC_OFFLOAD_ATTR_MAX,
1048 attrs[MACSEC_ATTR_OFFLOAD]);
1049
1050 offload = rta_getattr_u8(attrs_offload[MACSEC_OFFLOAD_ATTR_TYPE]);
1051 print_string(PRINT_ANY, "offload",
1052 " offload: %s ", offload_to_str(offload));
1053 print_nl();
1054 }
1055
1056 close_json_object();
1057
1058 return 0;
1059 }
1060
1061 static int do_dump(int ifindex)
1062 {
1063 MACSEC_GENL_REQ(req, MACSEC_BUFLEN, MACSEC_CMD_GET_TXSC,
1064 NLM_F_REQUEST | NLM_F_DUMP);
1065
1066 memset(&filter, 0, sizeof(filter));
1067 filter.ifindex = ifindex;
1068
1069 req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
1070 if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) {
1071 perror("Failed to send dump request");
1072 exit(1);
1073 }
1074
1075 new_json_obj(json);
1076 if (rtnl_dump_filter(&genl_rth, process, stdout) < 0) {
1077 delete_json_obj();
1078 fprintf(stderr, "Dump terminated\n");
1079 exit(1);
1080 }
1081 delete_json_obj();
1082
1083 return 0;
1084 }
1085
1086 static int do_show(int argc, char **argv)
1087 {
1088 int ifindex;
1089
1090 if (argc == 0)
1091 return do_dump(0);
1092
1093 ifindex = ll_name_to_index(*argv);
1094 if (ifindex == 0) {
1095 fprintf(stderr, "Device \"%s\" does not exist.\n", *argv);
1096 return -1;
1097 }
1098
1099 argc--, argv++;
1100 if (argc == 0)
1101 return do_dump(ifindex);
1102
1103 ipmacsec_usage();
1104 return -1;
1105 }
1106
1107 int do_ipmacsec(int argc, char **argv)
1108 {
1109 if (argc < 1)
1110 ipmacsec_usage();
1111
1112 if (matches(*argv, "help") == 0)
1113 ipmacsec_usage();
1114
1115 if (genl_init_handle(&genl_rth, MACSEC_GENL_NAME, &genl_family))
1116 exit(1);
1117
1118 if (matches(*argv, "show") == 0)
1119 return do_show(argc-1, argv+1);
1120
1121 if (matches(*argv, "add") == 0)
1122 return do_modify(CMD_ADD, argc-1, argv+1);
1123 if (matches(*argv, "set") == 0)
1124 return do_modify(CMD_UPD, argc-1, argv+1);
1125 if (matches(*argv, "delete") == 0)
1126 return do_modify(CMD_DEL, argc-1, argv+1);
1127 if (matches(*argv, "offload") == 0)
1128 return do_offload(CMD_OFFLOAD, argc-1, argv+1);
1129
1130 fprintf(stderr, "Command \"%s\" is unknown, try \"ip macsec help\".\n",
1131 *argv);
1132 exit(-1);
1133 }
1134
1135 /* device creation */
1136 static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
1137 {
1138 if (!tb)
1139 return;
1140
1141 if (tb[IFLA_MACSEC_SCI]) {
1142 if (is_json_context()) {
1143 SPRINT_BUF(b1);
1144
1145 snprintf(b1, sizeof(b1), "%016llx",
1146 ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI])));
1147 print_string(PRINT_JSON, "sci", NULL, b1);
1148 } else {
1149 fprintf(f, "sci %016llx ",
1150 ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI])));
1151 }
1152 }
1153
1154 print_flag(tb, "protect", IFLA_MACSEC_PROTECT);
1155
1156 if (tb[IFLA_MACSEC_CIPHER_SUITE]) {
1157 __u64 csid
1158 = rta_getattr_u64(tb[IFLA_MACSEC_CIPHER_SUITE]);
1159
1160 print_string(PRINT_ANY,
1161 "cipher_suite",
1162 "cipher %s ",
1163 cs_id_to_name(csid));
1164 }
1165
1166 if (tb[IFLA_MACSEC_ICV_LEN]) {
1167 if (is_json_context()) {
1168 char b2[4];
1169
1170 snprintf(b2, sizeof(b2), "%hhu",
1171 rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN]));
1172 print_uint(PRINT_JSON, "icv_len", NULL, atoi(b2));
1173 } else {
1174 fprintf(f, "icvlen %hhu ",
1175 rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN]));
1176 }
1177 }
1178
1179 if (tb[IFLA_MACSEC_ENCODING_SA]) {
1180 if (is_json_context()) {
1181 char b2[4];
1182
1183 snprintf(b2, sizeof(b2), "%hhu",
1184 rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA]));
1185 print_uint(PRINT_JSON, "encoding_sa", NULL, atoi(b2));
1186 } else {
1187 fprintf(f, "encodingsa %hhu ",
1188 rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA]));
1189 }
1190 }
1191
1192 if (tb[IFLA_MACSEC_VALIDATION]) {
1193 __u8 val = rta_getattr_u8(tb[IFLA_MACSEC_VALIDATION]);
1194
1195 print_string(PRINT_ANY,
1196 "validation",
1197 "validate %s ",
1198 validate_to_str(val));
1199 }
1200
1201 if (tb[IFLA_MACSEC_OFFLOAD]) {
1202 __u8 val = rta_getattr_u8(tb[IFLA_MACSEC_OFFLOAD]);
1203
1204 print_string(PRINT_ANY,
1205 "offload",
1206 "offload %s ",
1207 offload_to_str(val));
1208 }
1209
1210 const char *inc_sci, *es, *replay;
1211
1212 if (is_json_context()) {
1213 inc_sci = "inc_sci";
1214 replay = "replay_protect";
1215 es = "es";
1216 } else {
1217 inc_sci = "send_sci";
1218 es = "end_station";
1219 replay = "replay";
1220 }
1221
1222 print_flag(tb, "encrypt", IFLA_MACSEC_ENCRYPT);
1223 print_flag(tb, inc_sci, IFLA_MACSEC_INC_SCI);
1224 print_flag(tb, es, IFLA_MACSEC_ES);
1225 print_flag(tb, "scb", IFLA_MACSEC_SCB);
1226 print_flag(tb, replay, IFLA_MACSEC_REPLAY_PROTECT);
1227
1228 if (tb[IFLA_MACSEC_WINDOW])
1229 print_int(PRINT_ANY,
1230 "window",
1231 "window %d ",
1232 rta_getattr_u32(tb[IFLA_MACSEC_WINDOW]));
1233 }
1234
1235 static bool check_txsc_flags(bool es, bool scb, bool sci)
1236 {
1237 if (sci && (es || scb))
1238 return false;
1239 if (es && scb)
1240 return false;
1241 return true;
1242 }
1243
1244 static void usage(FILE *f)
1245 {
1246 fprintf(f,
1247 "Usage: ... macsec [ [ address <lladdr> ] port { 1..2^16-1 } | sci <u64> ]\n"
1248 " [ cipher { default | gcm-aes-128 | gcm-aes-256 } ]\n"
1249 " [ icvlen { 8..16 } ]\n"
1250 " [ encrypt { on | off } ]\n"
1251 " [ send_sci { on | off } ]\n"
1252 " [ end_station { on | off } ]\n"
1253 " [ scb { on | off } ]\n"
1254 " [ protect { on | off } ]\n"
1255 " [ replay { on | off} window { 0..2^32-1 } ]\n"
1256 " [ validate { strict | check | disabled } ]\n"
1257 " [ encodingsa { 0..3 } ]\n"
1258 " [ offload { mac | phy | off } ]\n"
1259 );
1260 }
1261
1262 static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
1263 struct nlmsghdr *n)
1264 {
1265 int ret;
1266 __u8 encoding_sa = 0xff;
1267 __u32 window = -1;
1268 enum macsec_offload offload;
1269 struct cipher_args cipher = {0};
1270 enum macsec_validation_type validate;
1271 bool es = false, scb = false, send_sci = false;
1272 int replay_protect = -1;
1273 struct sci sci = { 0 };
1274
1275 ret = get_sci_portaddr(&sci, &argc, &argv, true, true);
1276 if (ret < 0) {
1277 fprintf(stderr, "expected sci\n");
1278 return -1;
1279 }
1280
1281 if (ret > 0) {
1282 if (sci.sci)
1283 addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_SCI,
1284 &sci.sci, sizeof(sci.sci));
1285 else
1286 addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_PORT,
1287 &sci.port, sizeof(sci.port));
1288 }
1289
1290 while (argc > 0) {
1291 if (strcmp(*argv, "cipher") == 0) {
1292 NEXT_ARG();
1293 if (cipher.id)
1294 duparg("cipher", *argv);
1295 if (strcmp(*argv, "default") == 0)
1296 cipher.id = MACSEC_DEFAULT_CIPHER_ID;
1297 else if (strcmp(*argv, "gcm-aes-128") == 0 ||
1298 strcmp(*argv, "GCM-AES-128") == 0)
1299 cipher.id = MACSEC_CIPHER_ID_GCM_AES_128;
1300 else if (strcmp(*argv, "gcm-aes-256") == 0 ||
1301 strcmp(*argv, "GCM-AES-256") == 0)
1302 cipher.id = MACSEC_CIPHER_ID_GCM_AES_256;
1303 else
1304 invarg("expected: default, gcm-aes-128 or"
1305 " gcm-aes-256", *argv);
1306 } else if (strcmp(*argv, "icvlen") == 0) {
1307 NEXT_ARG();
1308 if (cipher.icv_len)
1309 duparg("icvlen", *argv);
1310 get_icvlen(&cipher.icv_len, *argv);
1311 } else if (strcmp(*argv, "encrypt") == 0) {
1312 NEXT_ARG();
1313 int i;
1314
1315 i = parse_on_off("encrypt", *argv, &ret);
1316 if (ret != 0)
1317 return ret;
1318 addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCRYPT, i);
1319 } else if (strcmp(*argv, "send_sci") == 0) {
1320 NEXT_ARG();
1321 int i;
1322
1323 i = parse_on_off("send_sci", *argv, &ret);
1324 if (ret != 0)
1325 return ret;
1326 send_sci = i;
1327 addattr8(n, MACSEC_BUFLEN,
1328 IFLA_MACSEC_INC_SCI, send_sci);
1329 } else if (strcmp(*argv, "end_station") == 0) {
1330 NEXT_ARG();
1331 int i;
1332
1333 i = parse_on_off("end_station", *argv, &ret);
1334 if (ret != 0)
1335 return ret;
1336 es = i;
1337 addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ES, es);
1338 } else if (strcmp(*argv, "scb") == 0) {
1339 NEXT_ARG();
1340 int i;
1341
1342 i = parse_on_off("scb", *argv, &ret);
1343 if (ret != 0)
1344 return ret;
1345 scb = i;
1346 addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_SCB, scb);
1347 } else if (strcmp(*argv, "protect") == 0) {
1348 NEXT_ARG();
1349 int i;
1350
1351 i = parse_on_off("protect", *argv, &ret);
1352 if (ret != 0)
1353 return ret;
1354 addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_PROTECT, i);
1355 } else if (strcmp(*argv, "replay") == 0) {
1356 NEXT_ARG();
1357 int i;
1358
1359 i = parse_on_off("replay", *argv, &ret);
1360 if (ret != 0)
1361 return ret;
1362 replay_protect = !!i;
1363 } else if (strcmp(*argv, "window") == 0) {
1364 NEXT_ARG();
1365 ret = get_u32(&window, *argv, 0);
1366 if (ret)
1367 invarg("expected replay window size", *argv);
1368 } else if (strcmp(*argv, "validate") == 0) {
1369 NEXT_ARG();
1370 validate = parse_one_of("validate", *argv, validate_str,
1371 ARRAY_SIZE(validate_str), &ret);
1372 if (ret != 0)
1373 return ret;
1374 addattr8(n, MACSEC_BUFLEN,
1375 IFLA_MACSEC_VALIDATION, validate);
1376 } else if (strcmp(*argv, "encodingsa") == 0) {
1377 if (encoding_sa != 0xff)
1378 duparg2("encodingsa", "encodingsa");
1379 NEXT_ARG();
1380 ret = get_an(&encoding_sa, *argv);
1381 if (ret)
1382 invarg("expected an { 0..3 }", *argv);
1383 } else if (strcmp(*argv, "offload") == 0) {
1384 NEXT_ARG();
1385 offload = parse_one_of("offload", *argv, offload_str,
1386 ARRAY_SIZE(offload_str), &ret);
1387 if (ret != 0)
1388 return ret;
1389 addattr8(n, MACSEC_BUFLEN,
1390 IFLA_MACSEC_OFFLOAD, offload);
1391 } else {
1392 fprintf(stderr, "macsec: unknown command \"%s\"?\n",
1393 *argv);
1394 usage(stderr);
1395 return -1;
1396 }
1397
1398 argv++; argc--;
1399 }
1400
1401 if (!check_txsc_flags(es, scb, send_sci)) {
1402 fprintf(stderr,
1403 "invalid combination of send_sci/end_station/scb\n");
1404 return -1;
1405 }
1406
1407 if (window != -1 && replay_protect == -1) {
1408 fprintf(stderr,
1409 "replay window set, but replay protection not enabled. did you mean 'replay on window %u'?\n",
1410 window);
1411 return -1;
1412 } else if (window == -1 && replay_protect == 1) {
1413 fprintf(stderr,
1414 "replay protection enabled, but no window set. did you mean 'replay on window VALUE'?\n");
1415 return -1;
1416 }
1417
1418 if (cipher.id)
1419 addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_CIPHER_SUITE,
1420 &cipher.id, sizeof(cipher.id));
1421 if (cipher.icv_len)
1422 addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_ICV_LEN,
1423 &cipher.icv_len, sizeof(cipher.icv_len));
1424
1425 if (replay_protect != -1) {
1426 addattr32(n, MACSEC_BUFLEN, IFLA_MACSEC_WINDOW, window);
1427 addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_REPLAY_PROTECT,
1428 replay_protect);
1429 }
1430
1431 if (encoding_sa != 0xff) {
1432 addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCODING_SA,
1433 &encoding_sa, sizeof(encoding_sa));
1434 }
1435
1436 return 0;
1437 }
1438
1439 static void macsec_print_help(struct link_util *lu, int argc, char **argv,
1440 FILE *f)
1441 {
1442 usage(f);
1443 }
1444
1445 struct link_util macsec_link_util = {
1446 .id = "macsec",
1447 .maxattr = IFLA_MACSEC_MAX,
1448 .parse_opt = macsec_parse_opt,
1449 .print_help = macsec_print_help,
1450 .print_opt = macsec_print_opt,
1451 };