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