2 * ipmacsec.c "ip macsec".
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.
9 * Authors: Sabrina Dubroca <sd@queasysnail.net>
16 #include <linux/genetlink.h>
17 #include <linux/if_ether.h>
18 #include <linux/if_macsec.h>
22 #include "ip_common.h"
26 static const char * const validate_str
[] = {
27 [MACSEC_VALIDATE_DISABLED
] = "disabled",
28 [MACSEC_VALIDATE_CHECK
] = "check",
29 [MACSEC_VALIDATE_STRICT
] = "strict",
32 static const char * const offload_str
[] = {
33 [MACSEC_OFFLOAD_OFF
] = "off",
34 [MACSEC_OFFLOAD_PHY
] = "phy",
35 [MACSEC_OFFLOAD_MAC
] = "mac",
47 __u8 key_id
[MACSEC_KEYID_LEN
];
49 __u8 key
[MACSEC_MAX_KEY_LEN
];
62 struct cipher_args cipher
;
64 enum macsec_validation_type validate
;
74 #define MACSEC_BUFLEN 1024
78 static struct rtnl_handle genl_rth
;
79 static int genl_family
= -1;
81 #define MACSEC_GENL_REQ(_req, _bufsiz, _cmd, _flags) \
82 GENL_REQUEST(_req, _bufsiz, genl_family, 0, MACSEC_GENL_VERSION, \
86 static void ipmacsec_usage(void)
89 "Usage: ip macsec add DEV tx sa { 0..3 } [ OPTS ] key ID KEY\n"
90 " ip macsec set DEV tx sa { 0..3 } [ OPTS ]\n"
91 " ip macsec del DEV tx sa { 0..3 }\n"
92 " ip macsec add DEV rx SCI [ on | off ]\n"
93 " ip macsec set DEV rx SCI [ on | off ]\n"
94 " ip macsec del DEV rx SCI\n"
95 " ip macsec add DEV rx SCI sa { 0..3 } [ OPTS ] key ID KEY\n"
96 " ip macsec set DEV rx SCI sa { 0..3 } [ OPTS ]\n"
97 " ip macsec del DEV rx SCI sa { 0..3 }\n"
99 " ip macsec show DEV\n"
100 " ip macsec offload DEV [ off | phy | mac ]\n"
101 "where OPTS := [ pn <u32> ] [ on | off ]\n"
102 " ID := 128-bit hex string\n"
103 " KEY := 128-bit or 256-bit hex string\n"
104 " SCI := { sci <u64> | port { 1..2^16-1 } address <lladdr> }\n");
109 static int get_an(__u8
*val
, const char *arg
)
111 int ret
= get_u8(val
, arg
, 0);
122 static int get_sci(__u64
*sci
, const char *arg
)
124 return get_be64(sci
, arg
, 16);
127 static int get_port(__be16
*port
, const char *arg
)
129 return get_be16(port
, arg
, 0);
133 #define STR(a) _STR(a)
135 static void get_icvlen(__u8
*icvlen
, char *arg
)
137 int ret
= get_u8(icvlen
, arg
, 10);
140 invarg("expected ICV length", arg
);
142 if (*icvlen
< MACSEC_MIN_ICV_LEN
|| *icvlen
> MACSEC_STD_ICV_LEN
)
143 invarg("ICV length must be in the range {"
144 STR(MACSEC_MIN_ICV_LEN
) ".." STR(MACSEC_STD_ICV_LEN
)
148 static bool get_sa(int *argcp
, char ***argvp
, __u8
*an
)
151 char **argv
= *argvp
;
154 if (argc
<= 0 || strcmp(*argv
, "sa") != 0)
158 ret
= get_an(an
, *argv
);
160 invarg("expected an { 0..3 }", *argv
);
168 static int parse_sa_args(int *argcp
, char ***argvp
, struct sa_desc
*sa
)
171 char **argv
= *argvp
;
173 bool active_set
= false;
176 if (strcmp(*argv
, "pn") == 0) {
180 ret
= get_u32(&sa
->pn
, *argv
, 0);
182 invarg("expected pn", *argv
);
184 invarg("expected pn != 0", *argv
);
185 } else if (strcmp(*argv
, "key") == 0) {
189 if (!hexstring_a2n(*argv
, sa
->key_id
, MACSEC_KEYID_LEN
,
191 invarg("expected key id", *argv
);
193 if (!hexstring_a2n(*argv
, sa
->key
, MACSEC_MAX_KEY_LEN
,
195 invarg("expected key", *argv
);
196 } else if (strcmp(*argv
, "on") == 0) {
198 duparg2("on/off", "on");
201 } else if (strcmp(*argv
, "off") == 0) {
203 duparg2("on/off", "off");
207 fprintf(stderr
, "macsec: unknown command \"%s\"?\n",
220 static __u64
make_sci(char *addr
, __be16 port
)
224 memcpy(&sci
, addr
, ETH_ALEN
);
225 memcpy(((char *)&sci
) + ETH_ALEN
, &port
, sizeof(port
));
230 static bool sci_complete(bool sci
, bool port
, bool addr
, bool port_only
)
232 return sci
|| (port
&& (addr
|| port_only
));
235 static int get_sci_portaddr(struct sci
*sci
, int *argcp
, char ***argvp
,
236 bool port_only
, bool optional
)
239 char **argv
= *argvp
;
241 bool p
= false, a
= false, s
= false;
244 if (strcmp(*argv
, "sci") == 0) {
246 invarg("expected address", *argv
);
248 invarg("expected port", *argv
);
250 ret
= get_sci(&sci
->sci
, *argv
);
252 invarg("expected sci", *argv
);
254 } else if (strcmp(*argv
, "port") == 0) {
256 ret
= get_port(&sci
->port
, *argv
);
258 invarg("expected port", *argv
);
260 invarg("expected port != 0", *argv
);
262 } else if (strcmp(*argv
, "address") == 0) {
264 ret
= ll_addr_a2n(sci
->abuf
, sizeof(sci
->abuf
), *argv
);
266 invarg("expected lladdr", *argv
);
268 } else if (optional
) {
271 invarg("expected sci, port, or address", *argv
);
276 if (sci_complete(s
, p
, a
, port_only
))
280 if (!optional
&& !sci_complete(s
, p
, a
, port_only
))
284 sci
->sci
= make_sci(sci
->abuf
, sci
->port
);
292 static bool parse_rxsci(int *argcp
, char ***argvp
, struct rxsc_desc
*rxsc
,
293 struct sa_desc
*rxsa
)
295 struct sci sci
= { 0 };
298 get_sci_portaddr(&sci
, argcp
, argvp
, false, false) < 0) {
299 fprintf(stderr
, "expected sci\n");
305 return get_sa(argcp
, argvp
, &rxsa
->an
);
308 static int parse_rxsci_args(int *argcp
, char ***argvp
, struct rxsc_desc
*rxsc
)
311 char **argv
= *argvp
;
312 bool active_set
= false;
315 if (strcmp(*argv
, "on") == 0) {
317 duparg2("on/off", "on");
320 } else if (strcmp(*argv
, "off") == 0) {
322 duparg2("on/off", "off");
323 rxsc
->active
= false;
326 fprintf(stderr
, "macsec: unknown command \"%s\"?\n",
347 static const enum macsec_nl_commands macsec_commands
[__CMD_MAX
][2][2] = {
349 [0] = {-1, MACSEC_CMD_ADD_RXSC
},
350 [1] = {MACSEC_CMD_ADD_TXSA
, MACSEC_CMD_ADD_RXSA
},
353 [0] = {-1, MACSEC_CMD_UPD_RXSC
},
354 [1] = {MACSEC_CMD_UPD_TXSA
, MACSEC_CMD_UPD_RXSA
},
357 [0] = {-1, MACSEC_CMD_DEL_RXSC
},
358 [1] = {MACSEC_CMD_DEL_TXSA
, MACSEC_CMD_DEL_RXSA
},
361 [0] = {-1, MACSEC_CMD_UPD_OFFLOAD
},
365 static int do_modify_nl(enum cmd c
, enum macsec_nl_commands cmd
, int ifindex
,
366 struct rxsc_desc
*rxsc
, struct sa_desc
*sa
)
368 struct rtattr
*attr_sa
;
370 MACSEC_GENL_REQ(req
, MACSEC_BUFLEN
, cmd
, NLM_F_REQUEST
);
372 addattr32(&req
.n
, MACSEC_BUFLEN
, MACSEC_ATTR_IFINDEX
, ifindex
);
374 struct rtattr
*attr_rxsc
;
376 attr_rxsc
= addattr_nest(&req
.n
, MACSEC_BUFLEN
,
377 MACSEC_ATTR_RXSC_CONFIG
);
378 addattr64(&req
.n
, MACSEC_BUFLEN
,
379 MACSEC_RXSC_ATTR_SCI
, rxsc
->sci
);
380 if (c
!= CMD_DEL
&& rxsc
->active
!= 0xff)
381 addattr8(&req
.n
, MACSEC_BUFLEN
,
382 MACSEC_RXSC_ATTR_ACTIVE
, rxsc
->active
);
384 addattr_nest_end(&req
.n
, attr_rxsc
);
390 attr_sa
= addattr_nest(&req
.n
, MACSEC_BUFLEN
, MACSEC_ATTR_SA_CONFIG
);
392 addattr8(&req
.n
, MACSEC_BUFLEN
, MACSEC_SA_ATTR_AN
, sa
->an
);
396 addattr32(&req
.n
, MACSEC_BUFLEN
, MACSEC_SA_ATTR_PN
,
400 addattr_l(&req
.n
, MACSEC_BUFLEN
, MACSEC_SA_ATTR_KEYID
,
401 sa
->key_id
, MACSEC_KEYID_LEN
);
402 addattr_l(&req
.n
, MACSEC_BUFLEN
, MACSEC_SA_ATTR_KEY
,
403 sa
->key
, sa
->key_len
);
406 if (sa
->active
!= 0xff) {
407 addattr8(&req
.n
, MACSEC_BUFLEN
,
408 MACSEC_SA_ATTR_ACTIVE
, sa
->active
);
412 addattr_nest_end(&req
.n
, attr_sa
);
415 if (rtnl_talk(&genl_rth
, &req
.n
, NULL
) < 0)
421 static bool check_sa_args(enum cmd c
, struct sa_desc
*sa
)
425 fprintf(stderr
, "cannot create SA without key\n");
430 fprintf(stderr
, "must specify a packet number != 0\n");
433 } else if (c
== CMD_UPD
) {
435 fprintf(stderr
, "cannot change key on SA\n");
443 static int do_modify_txsa(enum cmd c
, int argc
, char **argv
, int ifindex
)
445 struct sa_desc txsa
= {0};
446 enum macsec_nl_commands cmd
;
451 if (argc
== 0 || !get_sa(&argc
, &argv
, &txsa
.an
))
457 if (parse_sa_args(&argc
, &argv
, &txsa
))
460 if (check_sa_args(c
, &txsa
))
464 cmd
= macsec_commands
[c
][1][0];
465 return do_modify_nl(c
, cmd
, ifindex
, NULL
, &txsa
);
468 static int do_modify_rxsci(enum cmd c
, int argc
, char **argv
, int ifindex
)
470 struct rxsc_desc rxsc
= {0};
471 struct sa_desc rxsa
= {0};
473 enum macsec_nl_commands cmd
;
475 rxsc
.ifindex
= ifindex
;
480 sa_set
= parse_rxsci(&argc
, &argv
, &rxsc
, &rxsa
);
485 if (sa_set
&& (parse_sa_args(&argc
, &argv
, &rxsa
) ||
486 check_sa_args(c
, &rxsa
)))
488 if (!sa_set
&& parse_rxsci_args(&argc
, &argv
, &rxsc
))
492 cmd
= macsec_commands
[c
][sa_set
][1];
493 return do_modify_nl(c
, cmd
, rxsc
.ifindex
, &rxsc
, &rxsa
);
496 static int do_modify(enum cmd c
, int argc
, char **argv
)
503 ifindex
= ll_name_to_index(*argv
);
505 fprintf(stderr
, "Device \"%s\" does not exist.\n", *argv
);
513 if (strcmp(*argv
, "tx") == 0)
514 return do_modify_txsa(c
, argc
-1, argv
+1, ifindex
);
515 if (strcmp(*argv
, "rx") == 0)
516 return do_modify_rxsci(c
, argc
-1, argv
+1, ifindex
);
522 static int do_offload(enum cmd c
, int argc
, char **argv
)
524 enum macsec_offload offload
;
531 ifindex
= ll_name_to_index(*argv
);
533 fprintf(stderr
, "Device \"%s\" does not exist.\n", *argv
);
541 offload
= parse_one_of("offload", *argv
, offload_str
, ARRAY_SIZE(offload_str
), &ret
);
545 MACSEC_GENL_REQ(req
, MACSEC_BUFLEN
, macsec_commands
[c
][0][1], NLM_F_REQUEST
);
547 addattr32(&req
.n
, MACSEC_BUFLEN
, MACSEC_ATTR_IFINDEX
, ifindex
);
549 attr
= addattr_nest(&req
.n
, MACSEC_BUFLEN
, MACSEC_ATTR_OFFLOAD
);
550 addattr8(&req
.n
, MACSEC_BUFLEN
, MACSEC_OFFLOAD_ATTR_TYPE
, offload
);
551 addattr_nest_end(&req
.n
, attr
);
553 if (rtnl_talk(&genl_rth
, &req
.n
, NULL
) < 0)
565 static int validate_dump(struct rtattr
**attrs
)
567 return attrs
[MACSEC_ATTR_IFINDEX
] && attrs
[MACSEC_ATTR_SECY
] &&
568 attrs
[MACSEC_ATTR_TXSA_LIST
] && attrs
[MACSEC_ATTR_RXSC_LIST
] &&
569 attrs
[MACSEC_ATTR_TXSC_STATS
] && attrs
[MACSEC_ATTR_SECY_STATS
];
573 static int validate_secy_dump(struct rtattr
**attrs
)
575 return attrs
[MACSEC_SECY_ATTR_SCI
] &&
576 attrs
[MACSEC_SECY_ATTR_ENCODING_SA
] &&
577 attrs
[MACSEC_SECY_ATTR_CIPHER_SUITE
] &&
578 attrs
[MACSEC_SECY_ATTR_ICV_LEN
] &&
579 attrs
[MACSEC_SECY_ATTR_PROTECT
] &&
580 attrs
[MACSEC_SECY_ATTR_REPLAY
] &&
581 attrs
[MACSEC_SECY_ATTR_OPER
] &&
582 attrs
[MACSEC_SECY_ATTR_VALIDATE
] &&
583 attrs
[MACSEC_SECY_ATTR_ENCRYPT
] &&
584 attrs
[MACSEC_SECY_ATTR_INC_SCI
] &&
585 attrs
[MACSEC_SECY_ATTR_ES
] &&
586 attrs
[MACSEC_SECY_ATTR_SCB
];
589 static void print_flag(struct rtattr
*attrs
[], const char *desc
,
597 flag
= rta_getattr_u8(attrs
[field
]);
598 if (is_json_context())
599 print_bool(PRINT_JSON
, desc
, NULL
, flag
);
601 print_string(PRINT_FP
, NULL
, "%s ", desc
);
602 print_string(PRINT_FP
, NULL
, "%s ",
603 flag
? "on" : "off");
607 static void print_key(struct rtattr
*key
)
611 print_string(PRINT_ANY
, "key", " key %s\n",
612 hexstring_n2a(RTA_DATA(key
), RTA_PAYLOAD(key
),
613 keyid
, sizeof(keyid
)));
616 #define CIPHER_NAME_GCM_AES_128 "GCM-AES-128"
617 #define CIPHER_NAME_GCM_AES_256 "GCM-AES-256"
618 #define DEFAULT_CIPHER_NAME CIPHER_NAME_GCM_AES_128
620 static const char *cs_id_to_name(__u64 cid
)
623 case MACSEC_DEFAULT_CIPHER_ID
:
624 return DEFAULT_CIPHER_NAME
;
625 case MACSEC_CIPHER_ID_GCM_AES_128
:
626 /* MACSEC_DEFAULT_CIPHER_ALT: */
627 return CIPHER_NAME_GCM_AES_128
;
628 case MACSEC_CIPHER_ID_GCM_AES_256
:
629 return CIPHER_NAME_GCM_AES_256
;
635 static const char *validate_to_str(__u8 validate
)
637 if (validate
>= ARRAY_SIZE(validate_str
))
640 return validate_str
[validate
];
643 static const char *offload_to_str(__u8 offload
)
645 if (offload
>= ARRAY_SIZE(offload_str
))
648 return offload_str
[offload
];
651 static void print_attrs(struct rtattr
*attrs
[])
653 print_flag(attrs
, "protect", MACSEC_SECY_ATTR_PROTECT
);
655 if (attrs
[MACSEC_SECY_ATTR_VALIDATE
]) {
656 __u8 val
= rta_getattr_u8(attrs
[MACSEC_SECY_ATTR_VALIDATE
]);
658 print_string(PRINT_ANY
, "validate",
659 "validate %s ", validate_to_str(val
));
662 print_flag(attrs
, "sc", MACSEC_RXSC_ATTR_ACTIVE
);
663 print_flag(attrs
, "sa", MACSEC_SA_ATTR_ACTIVE
);
664 print_flag(attrs
, "encrypt", MACSEC_SECY_ATTR_ENCRYPT
);
665 print_flag(attrs
, "send_sci", MACSEC_SECY_ATTR_INC_SCI
);
666 print_flag(attrs
, "end_station", MACSEC_SECY_ATTR_ES
);
667 print_flag(attrs
, "scb", MACSEC_SECY_ATTR_SCB
);
668 print_flag(attrs
, "replay", MACSEC_SECY_ATTR_REPLAY
);
670 if (attrs
[MACSEC_SECY_ATTR_WINDOW
]) {
671 __u32 win
= rta_getattr_u32(attrs
[MACSEC_SECY_ATTR_WINDOW
]);
673 print_uint(PRINT_ANY
, "window", "window %u ", win
);
676 if (attrs
[MACSEC_SECY_ATTR_CIPHER_SUITE
]) {
677 __u64 cid
= rta_getattr_u64(attrs
[MACSEC_SECY_ATTR_CIPHER_SUITE
]);
680 print_string(PRINT_ANY
, "cipher_suite",
681 " cipher suite: %s,", cs_id_to_name(cid
));
684 if (attrs
[MACSEC_SECY_ATTR_ICV_LEN
]) {
685 __u8 icv_len
= rta_getattr_u8(attrs
[MACSEC_SECY_ATTR_ICV_LEN
]);
687 print_uint(PRINT_ANY
, "icv_length",
688 " using ICV length %u\n", icv_len
);
692 static __u64
getattr_u64(const struct rtattr
*stat
)
694 size_t len
= RTA_PAYLOAD(stat
);
698 return rta_getattr_u64(stat
);
700 return rta_getattr_u32(stat
);
702 return rta_getattr_u16(stat
);
704 return rta_getattr_u8(stat
);
706 fprintf(stderr
, "invalid attribute length %zu\n",
712 static void print_fp_stats(const char *prefix
,
713 const char *names
[], unsigned int num
,
714 struct rtattr
*stats
[])
719 printf("%sstats:", prefix
);
721 for (i
= 1; i
< num
; i
++) {
724 printf(" %s", names
[i
]);
727 printf("\n%s ", prefix
);
729 for (i
= 1; i
< num
; i
++) {
733 pad
= strlen(names
[i
]) + 1;
735 printf("%*llu", pad
, getattr_u64(stats
[i
]));
737 printf("%*c", pad
, '-');
742 static void print_json_stats(const char *names
[], unsigned int num
,
743 struct rtattr
*stats
[])
747 for (i
= 1; i
< num
; i
++) {
748 if (!names
[i
] || !stats
[i
])
751 print_u64(PRINT_JSON
, names
[i
],
752 NULL
, getattr_u64(stats
[i
]));
756 static void print_stats(const char *prefix
,
757 const char *names
[], unsigned int num
,
758 struct rtattr
*stats
[])
761 if (is_json_context())
762 print_json_stats(names
, num
, stats
);
764 print_fp_stats(prefix
, names
, num
, stats
);
767 static const char *txsc_stats_names
[NUM_MACSEC_TXSC_STATS_ATTR
] = {
768 [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED
] = "OutPktsProtected",
769 [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED
] = "OutPktsEncrypted",
770 [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED
] = "OutOctetsProtected",
771 [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED
] = "OutOctetsEncrypted",
774 static void print_txsc_stats(const char *prefix
, struct rtattr
*attr
)
776 struct rtattr
*stats
[MACSEC_TXSC_STATS_ATTR_MAX
+ 1];
778 if (!attr
|| show_stats
== 0)
781 parse_rtattr_nested(stats
, MACSEC_TXSC_STATS_ATTR_MAX
, attr
);
783 print_stats(prefix
, txsc_stats_names
, NUM_MACSEC_TXSC_STATS_ATTR
,
787 static const char *secy_stats_names
[NUM_MACSEC_SECY_STATS_ATTR
] = {
788 [MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED
] = "OutPktsUntagged",
789 [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED
] = "InPktsUntagged",
790 [MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG
] = "OutPktsTooLong",
791 [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG
] = "InPktsNoTag",
792 [MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG
] = "InPktsBadTag",
793 [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI
] = "InPktsUnknownSCI",
794 [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI
] = "InPktsNoSCI",
795 [MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN
] = "InPktsOverrun",
798 static void print_secy_stats(const char *prefix
, struct rtattr
*attr
)
800 struct rtattr
*stats
[MACSEC_SECY_STATS_ATTR_MAX
+ 1];
802 if (!attr
|| show_stats
== 0)
805 parse_rtattr_nested(stats
, MACSEC_SECY_STATS_ATTR_MAX
, attr
);
807 print_stats(prefix
, secy_stats_names
,
808 NUM_MACSEC_SECY_STATS_ATTR
, stats
);
811 static const char *rxsa_stats_names
[NUM_MACSEC_SA_STATS_ATTR
] = {
812 [MACSEC_SA_STATS_ATTR_IN_PKTS_OK
] = "InPktsOK",
813 [MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID
] = "InPktsInvalid",
814 [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID
] = "InPktsNotValid",
815 [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA
] = "InPktsNotUsingSA",
816 [MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA
] = "InPktsUnusedSA",
819 static void print_rxsa_stats(const char *prefix
, struct rtattr
*attr
)
821 struct rtattr
*stats
[MACSEC_SA_STATS_ATTR_MAX
+ 1];
823 if (!attr
|| show_stats
== 0)
826 parse_rtattr_nested(stats
, MACSEC_SA_STATS_ATTR_MAX
, attr
);
828 print_stats(prefix
, rxsa_stats_names
, NUM_MACSEC_SA_STATS_ATTR
, stats
);
831 static const char *txsa_stats_names
[NUM_MACSEC_SA_STATS_ATTR
] = {
832 [MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED
] = "OutPktsProtected",
833 [MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED
] = "OutPktsEncrypted",
836 static void print_txsa_stats(const char *prefix
, struct rtattr
*attr
)
838 struct rtattr
*stats
[MACSEC_SA_STATS_ATTR_MAX
+ 1];
840 if (!attr
|| show_stats
== 0)
843 parse_rtattr_nested(stats
, MACSEC_SA_STATS_ATTR_MAX
, attr
);
845 print_stats(prefix
, txsa_stats_names
, NUM_MACSEC_SA_STATS_ATTR
, stats
);
848 static void print_tx_sc(const char *prefix
, __u64 sci
, __u8 encoding_sa
,
849 struct rtattr
*txsc_stats
, struct rtattr
*secy_stats
,
852 struct rtattr
*sa_attr
[MACSEC_SA_ATTR_MAX
+ 1];
856 print_string(PRINT_FP
, NULL
, "%s", prefix
);
857 print_0xhex(PRINT_ANY
, "sci",
858 "TXSC: %016llx", ntohll(sci
));
859 print_uint(PRINT_ANY
, "encoding_sa",
860 " on SA %d\n", encoding_sa
);
862 print_secy_stats(prefix
, secy_stats
);
863 print_txsc_stats(prefix
, txsc_stats
);
865 open_json_array(PRINT_JSON
, "sa_list");
866 rem
= RTA_PAYLOAD(sa
);
867 for (a
= RTA_DATA(sa
); RTA_OK(a
, rem
); a
= RTA_NEXT(a
, rem
)) {
870 open_json_object(NULL
);
871 parse_rtattr_nested(sa_attr
, MACSEC_SA_ATTR_MAX
, a
);
872 state
= rta_getattr_u8(sa_attr
[MACSEC_SA_ATTR_ACTIVE
]);
874 print_string(PRINT_FP
, NULL
, "%s", prefix
);
875 print_string(PRINT_FP
, NULL
, "%s", prefix
);
876 print_uint(PRINT_ANY
, "an", "%d:",
877 rta_getattr_u8(sa_attr
[MACSEC_SA_ATTR_AN
]));
878 print_uint(PRINT_ANY
, "pn", " PN %u,",
879 rta_getattr_u32(sa_attr
[MACSEC_SA_ATTR_PN
]));
881 print_bool(PRINT_JSON
, "active", NULL
, state
);
882 print_string(PRINT_FP
, NULL
,
883 " state %s,", state
? "on" : "off");
884 print_key(sa_attr
[MACSEC_SA_ATTR_KEYID
]);
886 print_txsa_stats(prefix
, sa_attr
[MACSEC_SA_ATTR_STATS
]);
889 close_json_array(PRINT_JSON
, NULL
);
892 static const char *rxsc_stats_names
[NUM_MACSEC_RXSC_STATS_ATTR
] = {
893 [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED
] = "InOctetsValidated",
894 [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED
] = "InOctetsDecrypted",
895 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED
] = "InPktsUnchecked",
896 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED
] = "InPktsDelayed",
897 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK
] = "InPktsOK",
898 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID
] = "InPktsInvalid",
899 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE
] = "InPktsLate",
900 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID
] = "InPktsNotValid",
901 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA
] = "InPktsNotUsingSA",
902 [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA
] = "InPktsUnusedSA",
905 static void print_rxsc_stats(const char *prefix
, struct rtattr
*attr
)
907 struct rtattr
*stats
[MACSEC_RXSC_STATS_ATTR_MAX
+ 1];
909 if (!attr
|| show_stats
== 0)
912 parse_rtattr_nested(stats
, MACSEC_RXSC_STATS_ATTR_MAX
, attr
);
914 print_stats(prefix
, rxsc_stats_names
,
915 NUM_MACSEC_RXSC_STATS_ATTR
, stats
);
918 static void print_rx_sc(const char *prefix
, __be64 sci
, __u8 active
,
919 struct rtattr
*rxsc_stats
, struct rtattr
*sa
)
921 struct rtattr
*sa_attr
[MACSEC_SA_ATTR_MAX
+ 1];
925 print_string(PRINT_FP
, NULL
, "%s", prefix
);
926 print_0xhex(PRINT_ANY
, "sci",
927 "RXSC: %016llx,", ntohll(sci
));
928 print_bool(PRINT_JSON
, "active", NULL
, active
);
929 print_string(PRINT_FP
, NULL
,
930 " state %s\n", active
? "on" : "off");
931 print_rxsc_stats(prefix
, rxsc_stats
);
933 open_json_array(PRINT_JSON
, "sa_list");
934 rem
= RTA_PAYLOAD(sa
);
935 for (a
= RTA_DATA(sa
); RTA_OK(a
, rem
); a
= RTA_NEXT(a
, rem
)) {
938 open_json_object(NULL
);
939 parse_rtattr_nested(sa_attr
, MACSEC_SA_ATTR_MAX
, a
);
940 state
= rta_getattr_u8(sa_attr
[MACSEC_SA_ATTR_ACTIVE
]);
942 print_string(PRINT_FP
, NULL
, "%s", prefix
);
943 print_string(PRINT_FP
, NULL
, "%s", prefix
);
944 print_uint(PRINT_ANY
, "an", "%u:",
945 rta_getattr_u8(sa_attr
[MACSEC_SA_ATTR_AN
]));
946 print_uint(PRINT_ANY
, "pn", " PN %u,",
947 rta_getattr_u32(sa_attr
[MACSEC_SA_ATTR_PN
]));
949 print_bool(PRINT_JSON
, "active", NULL
, state
);
950 print_string(PRINT_FP
, NULL
, " state %s,",
951 state
? "on" : "off");
953 print_key(sa_attr
[MACSEC_SA_ATTR_KEYID
]);
955 print_rxsa_stats(prefix
, sa_attr
[MACSEC_SA_ATTR_STATS
]);
958 close_json_array(PRINT_JSON
, NULL
);
961 static void print_rxsc_list(struct rtattr
*sc
)
963 int rem
= RTA_PAYLOAD(sc
);
966 open_json_array(PRINT_JSON
, "rx_sc");
967 for (c
= RTA_DATA(sc
); RTA_OK(c
, rem
); c
= RTA_NEXT(c
, rem
)) {
968 struct rtattr
*sc_attr
[MACSEC_RXSC_ATTR_MAX
+ 1];
970 open_json_object(NULL
);
972 parse_rtattr_nested(sc_attr
, MACSEC_RXSC_ATTR_MAX
, c
);
974 rta_getattr_u64(sc_attr
[MACSEC_RXSC_ATTR_SCI
]),
975 rta_getattr_u32(sc_attr
[MACSEC_RXSC_ATTR_ACTIVE
]),
976 sc_attr
[MACSEC_RXSC_ATTR_STATS
],
977 sc_attr
[MACSEC_RXSC_ATTR_SA_LIST
]);
980 close_json_array(PRINT_JSON
, NULL
);
983 static int process(struct nlmsghdr
*n
, void *arg
)
985 struct genlmsghdr
*ghdr
;
986 struct rtattr
*attrs
[MACSEC_ATTR_MAX
+ 1];
987 struct rtattr
*attrs_secy
[MACSEC_SECY_ATTR_MAX
+ 1];
988 int len
= n
->nlmsg_len
;
993 if (n
->nlmsg_type
!= genl_family
)
996 len
-= NLMSG_LENGTH(GENL_HDRLEN
);
1000 ghdr
= NLMSG_DATA(n
);
1001 if (ghdr
->cmd
!= MACSEC_CMD_GET_TXSC
)
1004 parse_rtattr(attrs
, MACSEC_ATTR_MAX
, (void *) ghdr
+ GENL_HDRLEN
, len
);
1005 if (!validate_dump(attrs
)) {
1006 fprintf(stderr
, "incomplete dump message\n");
1010 ifindex
= rta_getattr_u32(attrs
[MACSEC_ATTR_IFINDEX
]);
1011 parse_rtattr_nested(attrs_secy
, MACSEC_SECY_ATTR_MAX
,
1012 attrs
[MACSEC_ATTR_SECY
]);
1014 if (!validate_secy_dump(attrs_secy
)) {
1015 fprintf(stderr
, "incomplete dump message\n");
1019 sci
= rta_getattr_u64(attrs_secy
[MACSEC_SECY_ATTR_SCI
]);
1020 encoding_sa
= rta_getattr_u8(attrs_secy
[MACSEC_SECY_ATTR_ENCODING_SA
]);
1022 if (filter
.ifindex
&& ifindex
!= filter
.ifindex
)
1025 if (filter
.sci
&& sci
!= filter
.sci
)
1028 open_json_object(NULL
);
1029 print_uint(PRINT_ANY
, "ifindex", "%u: ", ifindex
);
1030 print_color_string(PRINT_ANY
, COLOR_IFNAME
, "ifname",
1031 "%s: ", ll_index_to_name(ifindex
));
1033 print_attrs(attrs_secy
);
1035 print_tx_sc(" ", sci
, encoding_sa
,
1036 attrs
[MACSEC_ATTR_TXSC_STATS
],
1037 attrs
[MACSEC_ATTR_SECY_STATS
],
1038 attrs
[MACSEC_ATTR_TXSA_LIST
]);
1040 if (attrs
[MACSEC_ATTR_RXSC_LIST
])
1041 print_rxsc_list(attrs
[MACSEC_ATTR_RXSC_LIST
]);
1043 if (attrs
[MACSEC_ATTR_OFFLOAD
]) {
1044 struct rtattr
*attrs_offload
[MACSEC_OFFLOAD_ATTR_MAX
+ 1];
1047 parse_rtattr_nested(attrs_offload
, MACSEC_OFFLOAD_ATTR_MAX
,
1048 attrs
[MACSEC_ATTR_OFFLOAD
]);
1050 offload
= rta_getattr_u8(attrs_offload
[MACSEC_OFFLOAD_ATTR_TYPE
]);
1051 print_string(PRINT_ANY
, "offload",
1052 " offload: %s ", offload_to_str(offload
));
1056 close_json_object();
1061 static int do_dump(int ifindex
)
1063 MACSEC_GENL_REQ(req
, MACSEC_BUFLEN
, MACSEC_CMD_GET_TXSC
,
1064 NLM_F_REQUEST
| NLM_F_DUMP
);
1066 memset(&filter
, 0, sizeof(filter
));
1067 filter
.ifindex
= ifindex
;
1069 req
.n
.nlmsg_seq
= genl_rth
.dump
= ++genl_rth
.seq
;
1070 if (rtnl_send(&genl_rth
, &req
, req
.n
.nlmsg_len
) < 0) {
1071 perror("Failed to send dump request");
1076 if (rtnl_dump_filter(&genl_rth
, process
, stdout
) < 0) {
1078 fprintf(stderr
, "Dump terminated\n");
1086 static int do_show(int argc
, char **argv
)
1093 ifindex
= ll_name_to_index(*argv
);
1095 fprintf(stderr
, "Device \"%s\" does not exist.\n", *argv
);
1101 return do_dump(ifindex
);
1107 int do_ipmacsec(int argc
, char **argv
)
1112 if (matches(*argv
, "help") == 0)
1115 if (genl_init_handle(&genl_rth
, MACSEC_GENL_NAME
, &genl_family
))
1118 if (matches(*argv
, "show") == 0)
1119 return do_show(argc
-1, argv
+1);
1121 if (matches(*argv
, "add") == 0)
1122 return do_modify(CMD_ADD
, argc
-1, argv
+1);
1123 if (matches(*argv
, "set") == 0)
1124 return do_modify(CMD_UPD
, argc
-1, argv
+1);
1125 if (matches(*argv
, "delete") == 0)
1126 return do_modify(CMD_DEL
, argc
-1, argv
+1);
1127 if (matches(*argv
, "offload") == 0)
1128 return do_offload(CMD_OFFLOAD
, argc
-1, argv
+1);
1130 fprintf(stderr
, "Command \"%s\" is unknown, try \"ip macsec help\".\n",
1135 /* device creation */
1136 static void macsec_print_opt(struct link_util
*lu
, FILE *f
, struct rtattr
*tb
[])
1141 if (tb
[IFLA_MACSEC_SCI
]) {
1142 if (is_json_context()) {
1145 snprintf(b1
, sizeof(b1
), "%016llx",
1146 ntohll(rta_getattr_u64(tb
[IFLA_MACSEC_SCI
])));
1147 print_string(PRINT_JSON
, "sci", NULL
, b1
);
1149 fprintf(f
, "sci %016llx ",
1150 ntohll(rta_getattr_u64(tb
[IFLA_MACSEC_SCI
])));
1154 print_flag(tb
, "protect", IFLA_MACSEC_PROTECT
);
1156 if (tb
[IFLA_MACSEC_CIPHER_SUITE
]) {
1158 = rta_getattr_u64(tb
[IFLA_MACSEC_CIPHER_SUITE
]);
1160 print_string(PRINT_ANY
,
1163 cs_id_to_name(csid
));
1166 if (tb
[IFLA_MACSEC_ICV_LEN
]) {
1167 if (is_json_context()) {
1170 snprintf(b2
, sizeof(b2
), "%hhu",
1171 rta_getattr_u8(tb
[IFLA_MACSEC_ICV_LEN
]));
1172 print_uint(PRINT_JSON
, "icv_len", NULL
, atoi(b2
));
1174 fprintf(f
, "icvlen %hhu ",
1175 rta_getattr_u8(tb
[IFLA_MACSEC_ICV_LEN
]));
1179 if (tb
[IFLA_MACSEC_ENCODING_SA
]) {
1180 if (is_json_context()) {
1183 snprintf(b2
, sizeof(b2
), "%hhu",
1184 rta_getattr_u8(tb
[IFLA_MACSEC_ENCODING_SA
]));
1185 print_uint(PRINT_JSON
, "encoding_sa", NULL
, atoi(b2
));
1187 fprintf(f
, "encodingsa %hhu ",
1188 rta_getattr_u8(tb
[IFLA_MACSEC_ENCODING_SA
]));
1192 if (tb
[IFLA_MACSEC_VALIDATION
]) {
1193 __u8 val
= rta_getattr_u8(tb
[IFLA_MACSEC_VALIDATION
]);
1195 print_string(PRINT_ANY
,
1198 validate_to_str(val
));
1201 if (tb
[IFLA_MACSEC_OFFLOAD
]) {
1202 __u8 val
= rta_getattr_u8(tb
[IFLA_MACSEC_OFFLOAD
]);
1204 print_string(PRINT_ANY
,
1207 offload_to_str(val
));
1210 const char *inc_sci
, *es
, *replay
;
1212 if (is_json_context()) {
1213 inc_sci
= "inc_sci";
1214 replay
= "replay_protect";
1217 inc_sci
= "send_sci";
1222 print_flag(tb
, "encrypt", IFLA_MACSEC_ENCRYPT
);
1223 print_flag(tb
, inc_sci
, IFLA_MACSEC_INC_SCI
);
1224 print_flag(tb
, es
, IFLA_MACSEC_ES
);
1225 print_flag(tb
, "scb", IFLA_MACSEC_SCB
);
1226 print_flag(tb
, replay
, IFLA_MACSEC_REPLAY_PROTECT
);
1228 if (tb
[IFLA_MACSEC_WINDOW
])
1229 print_int(PRINT_ANY
,
1232 rta_getattr_u32(tb
[IFLA_MACSEC_WINDOW
]));
1235 static bool check_txsc_flags(bool es
, bool scb
, bool sci
)
1237 if (sci
&& (es
|| scb
))
1244 static void usage(FILE *f
)
1247 "Usage: ... macsec [ [ address <lladdr> ] port { 1..2^16-1 } | sci <u64> ]\n"
1248 " [ cipher { default | gcm-aes-128 | gcm-aes-256 } ]\n"
1249 " [ icvlen { 8..16 } ]\n"
1250 " [ encrypt { on | off } ]\n"
1251 " [ send_sci { on | off } ]\n"
1252 " [ end_station { on | off } ]\n"
1253 " [ scb { on | off } ]\n"
1254 " [ protect { on | off } ]\n"
1255 " [ replay { on | off} window { 0..2^32-1 } ]\n"
1256 " [ validate { strict | check | disabled } ]\n"
1257 " [ encodingsa { 0..3 } ]\n"
1258 " [ offload { mac | phy | off } ]\n"
1262 static int macsec_parse_opt(struct link_util
*lu
, int argc
, char **argv
,
1266 __u8 encoding_sa
= 0xff;
1268 enum macsec_offload offload
;
1269 struct cipher_args cipher
= {0};
1270 enum macsec_validation_type validate
;
1271 bool es
= false, scb
= false, send_sci
= false;
1272 int replay_protect
= -1;
1273 struct sci sci
= { 0 };
1275 ret
= get_sci_portaddr(&sci
, &argc
, &argv
, true, true);
1277 fprintf(stderr
, "expected sci\n");
1283 addattr_l(n
, MACSEC_BUFLEN
, IFLA_MACSEC_SCI
,
1284 &sci
.sci
, sizeof(sci
.sci
));
1286 addattr_l(n
, MACSEC_BUFLEN
, IFLA_MACSEC_PORT
,
1287 &sci
.port
, sizeof(sci
.port
));
1291 if (strcmp(*argv
, "cipher") == 0) {
1294 duparg("cipher", *argv
);
1295 if (strcmp(*argv
, "default") == 0)
1296 cipher
.id
= MACSEC_DEFAULT_CIPHER_ID
;
1297 else if (strcmp(*argv
, "gcm-aes-128") == 0 ||
1298 strcmp(*argv
, "GCM-AES-128") == 0)
1299 cipher
.id
= MACSEC_CIPHER_ID_GCM_AES_128
;
1300 else if (strcmp(*argv
, "gcm-aes-256") == 0 ||
1301 strcmp(*argv
, "GCM-AES-256") == 0)
1302 cipher
.id
= MACSEC_CIPHER_ID_GCM_AES_256
;
1304 invarg("expected: default, gcm-aes-128 or"
1305 " gcm-aes-256", *argv
);
1306 } else if (strcmp(*argv
, "icvlen") == 0) {
1309 duparg("icvlen", *argv
);
1310 get_icvlen(&cipher
.icv_len
, *argv
);
1311 } else if (strcmp(*argv
, "encrypt") == 0) {
1315 i
= parse_on_off("encrypt", *argv
, &ret
);
1318 addattr8(n
, MACSEC_BUFLEN
, IFLA_MACSEC_ENCRYPT
, i
);
1319 } else if (strcmp(*argv
, "send_sci") == 0) {
1323 i
= parse_on_off("send_sci", *argv
, &ret
);
1327 addattr8(n
, MACSEC_BUFLEN
,
1328 IFLA_MACSEC_INC_SCI
, send_sci
);
1329 } else if (strcmp(*argv
, "end_station") == 0) {
1333 i
= parse_on_off("end_station", *argv
, &ret
);
1337 addattr8(n
, MACSEC_BUFLEN
, IFLA_MACSEC_ES
, es
);
1338 } else if (strcmp(*argv
, "scb") == 0) {
1342 i
= parse_on_off("scb", *argv
, &ret
);
1346 addattr8(n
, MACSEC_BUFLEN
, IFLA_MACSEC_SCB
, scb
);
1347 } else if (strcmp(*argv
, "protect") == 0) {
1351 i
= parse_on_off("protect", *argv
, &ret
);
1354 addattr8(n
, MACSEC_BUFLEN
, IFLA_MACSEC_PROTECT
, i
);
1355 } else if (strcmp(*argv
, "replay") == 0) {
1359 i
= parse_on_off("replay", *argv
, &ret
);
1362 replay_protect
= !!i
;
1363 } else if (strcmp(*argv
, "window") == 0) {
1365 ret
= get_u32(&window
, *argv
, 0);
1367 invarg("expected replay window size", *argv
);
1368 } else if (strcmp(*argv
, "validate") == 0) {
1370 validate
= parse_one_of("validate", *argv
, validate_str
,
1371 ARRAY_SIZE(validate_str
), &ret
);
1374 addattr8(n
, MACSEC_BUFLEN
,
1375 IFLA_MACSEC_VALIDATION
, validate
);
1376 } else if (strcmp(*argv
, "encodingsa") == 0) {
1377 if (encoding_sa
!= 0xff)
1378 duparg2("encodingsa", "encodingsa");
1380 ret
= get_an(&encoding_sa
, *argv
);
1382 invarg("expected an { 0..3 }", *argv
);
1383 } else if (strcmp(*argv
, "offload") == 0) {
1385 offload
= parse_one_of("offload", *argv
, offload_str
,
1386 ARRAY_SIZE(offload_str
), &ret
);
1389 addattr8(n
, MACSEC_BUFLEN
,
1390 IFLA_MACSEC_OFFLOAD
, offload
);
1392 fprintf(stderr
, "macsec: unknown command \"%s\"?\n",
1401 if (!check_txsc_flags(es
, scb
, send_sci
)) {
1403 "invalid combination of send_sci/end_station/scb\n");
1407 if (window
!= -1 && replay_protect
== -1) {
1409 "replay window set, but replay protection not enabled. did you mean 'replay on window %u'?\n",
1412 } else if (window
== -1 && replay_protect
== 1) {
1414 "replay protection enabled, but no window set. did you mean 'replay on window VALUE'?\n");
1419 addattr_l(n
, MACSEC_BUFLEN
, IFLA_MACSEC_CIPHER_SUITE
,
1420 &cipher
.id
, sizeof(cipher
.id
));
1422 addattr_l(n
, MACSEC_BUFLEN
, IFLA_MACSEC_ICV_LEN
,
1423 &cipher
.icv_len
, sizeof(cipher
.icv_len
));
1425 if (replay_protect
!= -1) {
1426 addattr32(n
, MACSEC_BUFLEN
, IFLA_MACSEC_WINDOW
, window
);
1427 addattr8(n
, MACSEC_BUFLEN
, IFLA_MACSEC_REPLAY_PROTECT
,
1431 if (encoding_sa
!= 0xff) {
1432 addattr_l(n
, MACSEC_BUFLEN
, IFLA_MACSEC_ENCODING_SA
,
1433 &encoding_sa
, sizeof(encoding_sa
));
1439 static void macsec_print_help(struct link_util
*lu
, int argc
, char **argv
,
1445 struct link_util macsec_link_util
= {
1447 .maxattr
= IFLA_MACSEC_MAX
,
1448 .parse_opt
= macsec_parse_opt
,
1449 .print_help
= macsec_print_help
,
1450 .print_opt
= macsec_print_opt
,