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