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 *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 ipmacsec_usage(void)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 one_of(const char * msg,const char * realval,const char ** list,size_t len,int * index)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 get_an(__u8 * val,const char * arg)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 get_sci(__u64 * sci,const char * arg)135 static int get_sci(__u64 *sci, const char *arg) 136 { 137 return get_be64(sci, arg, 16); 138 } 139 get_port(__be16 * port,const char * arg)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 get_icvlen(__u8 * icvlen,char * arg)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 get_sa(int * argcp,char *** argvp,__u8 * an)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 parse_sa_args(int * argcp,char *** argvp,struct sa_desc * sa)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 make_sci(char * addr,__be16 port)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 sci_complete(bool sci,bool port,bool addr,bool port_only)243 static bool sci_complete(bool sci, bool port, bool addr, bool port_only) 244 { 245 return sci || (port && (addr || port_only)); 246 } 247 get_sci_portaddr(struct sci * sci,int * argcp,char *** argvp,bool port_only,bool optional)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 parse_rxsci(int * argcp,char *** argvp,struct rxsc_desc * rxsc,struct sa_desc * rxsa)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 parse_rxsci_args(int * argcp,char *** argvp,struct rxsc_desc * rxsc)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 do_modify_nl(enum cmd c,enum macsec_nl_commands cmd,int ifindex,struct rxsc_desc * rxsc,struct sa_desc * sa)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 check_sa_args(enum cmd c,struct sa_desc * sa)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 do_modify_txsa(enum cmd c,int argc,char ** argv,int ifindex)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 do_modify_rxsci(enum cmd c,int argc,char ** argv,int ifindex)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 do_modify(enum cmd c,int argc,char ** argv)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 validate_dump(struct rtattr ** attrs)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 validate_secy_dump(struct rtattr ** attrs)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 print_flag(FILE * f,struct rtattr * attrs[],const char * desc,int field)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 cs_id_to_name(__u64 cid)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 print_cipher_suite(const char * prefix,__u64 cid,__u8 icv_len)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 print_attrs(const char * prefix,struct rtattr * attrs[])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 print_one_stat(const char ** names,struct rtattr ** attr,int idx,bool long_stat)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 print_txsc_stats(const char * prefix,struct rtattr * attr)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 print_secy_stats(const char * prefix,struct rtattr * attr)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 print_rxsa_stats(const char * prefix,struct rtattr * attr)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 print_txsa_stats(const char * prefix,struct rtattr * attr)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 print_tx_sc(const char * prefix,__u64 sci,__u8 encoding_sa,struct rtattr * txsc_stats,struct rtattr * secy_stats,struct rtattr * sa)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 print_rxsc_stats(const char * prefix,struct rtattr * attr)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 print_rx_sc(const char * prefix,__u64 sci,__u8 active,struct rtattr * rxsc_stats,struct rtattr * sa)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 process(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)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 do_dump(int ifindex)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 do_show(int argc,char ** argv)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 do_ipmacsec(int argc,char ** argv)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 */ macsec_print_opt(struct link_util * lu,FILE * f,struct rtattr * tb[])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 check_txsc_flags(bool es,bool scb,bool sci)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 usage(FILE * f)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 macsec_parse_opt(struct link_util * lu,int argc,char ** argv,struct nlmsghdr * hdr)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 macsec_print_help(struct link_util * lu,int argc,char ** argv,FILE * f)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