diff options
-rw-r--r-- | include/linux-private/linux/if_link.h | 30 | ||||
-rw-r--r-- | include/linux-private/linux/if_macsec.h | 169 | ||||
-rw-r--r-- | include/netlink/route/link/macsec.h | 73 | ||||
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/route/link/macsec.c | 786 | ||||
-rw-r--r-- | libnl-route-3.sym | 27 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rw-r--r-- | tests/test-create-macsec.c | 47 |
9 files changed, 1135 insertions, 1 deletions
diff --git a/include/linux-private/linux/if_link.h b/include/linux-private/linux/if_link.h index af4a8f9..cd7213d 100644 --- a/include/linux-private/linux/if_link.h +++ b/include/linux-private/linux/if_link.h @@ -346,6 +346,36 @@ enum { #define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1) +/* MACSEC section */ +enum { + IFLA_MACSEC_UNSPEC, + IFLA_MACSEC_SCI, + IFLA_MACSEC_PORT, + IFLA_MACSEC_ICV_LEN, + IFLA_MACSEC_CIPHER_SUITE, + IFLA_MACSEC_WINDOW, + IFLA_MACSEC_ENCODING_SA, + IFLA_MACSEC_ENCRYPT, + IFLA_MACSEC_PROTECT, + IFLA_MACSEC_INC_SCI, + IFLA_MACSEC_ES, + IFLA_MACSEC_SCB, + IFLA_MACSEC_REPLAY_PROTECT, + IFLA_MACSEC_VALIDATION, + IFLA_MACSEC_PAD, + __IFLA_MACSEC_MAX, +}; + +#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) + +enum macsec_validation_type { + MACSEC_VALIDATE_DISABLED = 0, + MACSEC_VALIDATE_CHECK = 1, + MACSEC_VALIDATE_STRICT = 2, + __MACSEC_VALIDATE_END, + MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, +}; + /* IPVLAN section */ enum { IFLA_IPVLAN_UNSPEC, diff --git a/include/linux-private/linux/if_macsec.h b/include/linux-private/linux/if_macsec.h new file mode 100644 index 0000000..cbd4faa --- /dev/null +++ b/include/linux-private/linux/if_macsec.h @@ -0,0 +1,169 @@ +/* + * include/uapi/linux/if_macsec.h - MACsec device + * + * Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _MACSEC_H +#define _MACSEC_H + +#include <linux/types.h> + +#define MACSEC_GENL_NAME "macsec" +#define MACSEC_GENL_VERSION 1 + +#define MACSEC_MAX_KEY_LEN 128 + +#define MACSEC_KEYID_LEN 16 + +#define MACSEC_DEFAULT_CIPHER_ID 0x0080020001000001ULL +#define MACSEC_DEFAULT_CIPHER_ALT 0x0080C20001000001ULL + +#define MACSEC_MIN_ICV_LEN 8 +#define MACSEC_MAX_ICV_LEN 32 + +enum macsec_attrs { + MACSEC_ATTR_UNSPEC, + MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */ + MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */ + MACSEC_ATTR_SA_CONFIG, /* config, nested macsec_sa_attrs */ + MACSEC_ATTR_SECY, /* dump, nested macsec_secy_attrs */ + MACSEC_ATTR_TXSA_LIST, /* dump, nested, macsec_sa_attrs for each TXSA */ + MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */ + MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */ + MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */ + __MACSEC_ATTR_END, + NUM_MACSEC_ATTR = __MACSEC_ATTR_END, + MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1, +}; + +enum macsec_secy_attrs { + MACSEC_SECY_ATTR_UNSPEC, + MACSEC_SECY_ATTR_SCI, + MACSEC_SECY_ATTR_ENCODING_SA, + MACSEC_SECY_ATTR_WINDOW, + MACSEC_SECY_ATTR_CIPHER_SUITE, + MACSEC_SECY_ATTR_ICV_LEN, + MACSEC_SECY_ATTR_PROTECT, + MACSEC_SECY_ATTR_REPLAY, + MACSEC_SECY_ATTR_OPER, + MACSEC_SECY_ATTR_VALIDATE, + MACSEC_SECY_ATTR_ENCRYPT, + MACSEC_SECY_ATTR_INC_SCI, + MACSEC_SECY_ATTR_ES, + MACSEC_SECY_ATTR_SCB, + MACSEC_SECY_ATTR_PAD, + __MACSEC_SECY_ATTR_END, + NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END, + MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1, +}; + +enum macsec_rxsc_attrs { + MACSEC_RXSC_ATTR_UNSPEC, + MACSEC_RXSC_ATTR_SCI, /* config/dump, u64 */ + MACSEC_RXSC_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */ + MACSEC_RXSC_ATTR_STATS, /* dump, nested, macsec_rxsc_stats_attr */ + MACSEC_RXSC_ATTR_PAD, + __MACSEC_RXSC_ATTR_END, + NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END, + MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1, +}; + +enum macsec_sa_attrs { + MACSEC_SA_ATTR_UNSPEC, + MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */ + MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_SA_ATTR_PN, /* config/dump, u32 */ + MACSEC_SA_ATTR_KEY, /* config, data */ + MACSEC_SA_ATTR_KEYID, /* config/dump, 128-bit */ + MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */ + MACSEC_SA_ATTR_PAD, + __MACSEC_SA_ATTR_END, + NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END, + MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, +}; + +enum macsec_nl_commands { + MACSEC_CMD_GET_TXSC, + MACSEC_CMD_ADD_RXSC, + MACSEC_CMD_DEL_RXSC, + MACSEC_CMD_UPD_RXSC, + MACSEC_CMD_ADD_TXSA, + MACSEC_CMD_DEL_TXSA, + MACSEC_CMD_UPD_TXSA, + MACSEC_CMD_ADD_RXSA, + MACSEC_CMD_DEL_RXSA, + MACSEC_CMD_UPD_RXSA, +}; + +/* u64 per-RXSC stats */ +enum macsec_rxsc_stats_attr { + MACSEC_RXSC_STATS_ATTR_UNSPEC, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_RXSC_STATS_ATTR_PAD, + __MACSEC_RXSC_STATS_ATTR_END, + NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END, + MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1, +}; + +/* u32 per-{RX,TX}SA stats */ +enum macsec_sa_stats_attr { + MACSEC_SA_STATS_ATTR_UNSPEC, + MACSEC_SA_STATS_ATTR_IN_PKTS_OK, + MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, + __MACSEC_SA_STATS_ATTR_END, + NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END, + MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1, +}; + +/* u64 per-TXSC stats */ +enum macsec_txsc_stats_attr { + MACSEC_TXSC_STATS_ATTR_UNSPEC, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_PAD, + __MACSEC_TXSC_STATS_ATTR_END, + NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END, + MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1, +}; + +/* u64 per-SecY stats */ +enum macsec_secy_stats_attr { + MACSEC_SECY_STATS_ATTR_UNSPEC, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN, + MACSEC_SECY_STATS_ATTR_PAD, + __MACSEC_SECY_STATS_ATTR_END, + NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END, + MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1, +}; + +#endif /* _MACSEC_H */ diff --git a/include/netlink/route/link/macsec.h b/include/netlink/route/link/macsec.h new file mode 100644 index 0000000..d9cf7da --- /dev/null +++ b/include/netlink/route/link/macsec.h @@ -0,0 +1,73 @@ +/* + * netlink/route/link/macsec.h MACsec Link Info + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2016 Sabrina Dubroca <sd@queasysnail.net> + */ + +#ifndef NETLINK_LINK_MACSEC_H_ +#define NETLINK_LINK_MACSEC_H_ + +#include <netlink/netlink.h> +#include <netlink/route/link.h> +#include <linux/if_link.h> +#include <linux/if_macsec.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_link *rtnl_link_macsec_alloc(void); + +int rtnl_link_macsec_set_sci(struct rtnl_link *, uint64_t); +int rtnl_link_macsec_get_sci(struct rtnl_link *, uint64_t *); + +int rtnl_link_macsec_set_port(struct rtnl_link *, uint16_t); +int rtnl_link_macsec_get_port(struct rtnl_link *, uint16_t *); + +int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *, uint64_t); +int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *, uint64_t *); + +int rtnl_link_macsec_set_icv_len(struct rtnl_link *, uint16_t); +int rtnl_link_macsec_get_icv_len(struct rtnl_link *, uint16_t *); + +int rtnl_link_macsec_set_protect(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_protect(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_encrypt(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_encrypt(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_validation_type(struct rtnl_link *, + enum macsec_validation_type); +int rtnl_link_macsec_get_validation_type(struct rtnl_link *, + enum macsec_validation_type *); + +int rtnl_link_macsec_set_replay_protect(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_replay_protect(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_window(struct rtnl_link *, uint32_t); +int rtnl_link_macsec_get_window(struct rtnl_link *, uint32_t *); + +int rtnl_link_macsec_set_send_sci(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_send_sci(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_end_station(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_end_station(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_scb(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_scb(struct rtnl_link *, uint8_t *); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 320c511..21d28ca 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -111,7 +111,7 @@ libnl_route_3_la_SOURCES = \ route/link/vxlan.c route/link/veth.c route/link/ipip.c \ route/link/ipgre.c route/link/sit.c route/link/ipvti.c \ route/link/ip6tnl.c route/link/ifb.c route/link/ipvlan.c \ - route/link/vrf.c \ + route/link/vrf.c route/link/macsec.c \ \ route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \ route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \ diff --git a/lib/route/link/macsec.c b/lib/route/link/macsec.c new file mode 100644 index 0000000..671a0b9 --- /dev/null +++ b/lib/route/link/macsec.c @@ -0,0 +1,786 @@ +/* + * lib/route/link/macsec.c MACsec Link Info + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2016 Sabrina Dubroca <sd@queasysnail.net> + */ + +#include <netlink-private/netlink.h> +#include <netlink/netlink.h> +#include <netlink/attr.h> +#include <netlink/utils.h> +#include <netlink/object.h> +#include <netlink/route/rtnl.h> +#include <netlink-private/route/link/api.h> + +#include <linux/if_macsec.h> + +#define MACSEC_ATTR_SCI (1 << 0) +#define MACSEC_ATTR_ICV_LEN (1 << 1) +#define MACSEC_ATTR_CIPHER_SUITE (1 << 2) +#define MACSEC_ATTR_WINDOW (1 << 3) +#define MACSEC_ATTR_ENCODING_SA (1 << 4) +#define MACSEC_ATTR_ENCRYPT (1 << 5) +#define MACSEC_ATTR_PROTECT (1 << 6) +#define MACSEC_ATTR_INC_SCI (1 << 7) +#define MACSEC_ATTR_ES (1 << 8) +#define MACSEC_ATTR_SCB (1 << 9) +#define MACSEC_ATTR_REPLAY_PROTECT (1 << 10) +#define MACSEC_ATTR_VALIDATION (1 << 11) +#define MACSEC_ATTR_PORT (1 << 12) + +struct macsec_info { + int ifindex; + uint64_t sci; + uint16_t port; + uint64_t cipher_suite; + uint16_t icv_len; + uint32_t window; + enum macsec_validation_type validate; + uint8_t encoding_sa; + + uint8_t send_sci, end_station, scb, replay_protect, protect, encrypt; + + uint32_t ce_mask; +}; + +static struct nla_policy macsec_policy[IFLA_MACSEC_MAX+1] = { + [IFLA_MACSEC_SCI] = { .type = NLA_U64 }, + [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 }, + [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 }, + [IFLA_MACSEC_WINDOW] = { .type = NLA_U32 }, + [IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 }, + [IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 }, + [IFLA_MACSEC_PROTECT] = { .type = NLA_U8 }, + [IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 }, + [IFLA_MACSEC_ES] = { .type = NLA_U8 }, + [IFLA_MACSEC_SCB] = { .type = NLA_U8 }, + [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 }, + [IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 }, +}; + +#define DEFAULT_ICV_LEN 16 + +static int macsec_alloc(struct rtnl_link *link) +{ + struct macsec_info *info; + + if (!link->l_info) { + link->l_info = malloc(sizeof(struct macsec_info)); + if (!link->l_info) + return -NLE_NOMEM; + } + + memset(link->l_info, 0, sizeof(struct macsec_info)); + info = link->l_info; + + info->cipher_suite = MACSEC_DEFAULT_CIPHER_ID; + info->icv_len = DEFAULT_ICV_LEN; + info->ce_mask = MACSEC_ATTR_CIPHER_SUITE | MACSEC_ATTR_ICV_LEN; + + return 0; +} + +static int macsec_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_MACSEC_MAX+1]; + struct macsec_info *info; + int err; + + NL_DBG(3, "Parsing MACsec link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_MACSEC_MAX, data, macsec_policy)) < 0) + goto errout; + + if ((err = macsec_alloc(link)) < 0) + goto errout; + + info = link->l_info; + + if (tb[IFLA_MACSEC_SCI]) { + info->sci = nla_get_u64(tb[IFLA_MACSEC_SCI]); + info->ce_mask |= MACSEC_ATTR_SCI; + } + + if (tb[IFLA_MACSEC_PROTECT]) { + info->protect = nla_get_u8(tb[IFLA_MACSEC_PROTECT]); + info->ce_mask |= MACSEC_ATTR_PROTECT; + } + + if (tb[IFLA_MACSEC_CIPHER_SUITE]) { + info->cipher_suite = nla_get_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); + info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE; + } + + if (tb[IFLA_MACSEC_ICV_LEN]) { + info->icv_len = nla_get_u8(tb[IFLA_MACSEC_ICV_LEN]); + info->ce_mask |= MACSEC_ATTR_ICV_LEN; + } + + if (tb[IFLA_MACSEC_ENCODING_SA]) { + info->encoding_sa = nla_get_u8(tb[IFLA_MACSEC_ENCODING_SA]); + info->ce_mask |= MACSEC_ATTR_ENCODING_SA; + } + + if (tb[IFLA_MACSEC_VALIDATION]) { + info->validate = nla_get_u8(tb[IFLA_MACSEC_VALIDATION]); + info->ce_mask |= MACSEC_ATTR_VALIDATION; + } + + if (tb[IFLA_MACSEC_ENCRYPT]) { + info->encrypt = nla_get_u8(tb[IFLA_MACSEC_ENCRYPT]); + info->ce_mask |= MACSEC_ATTR_ENCRYPT; + } + + if (tb[IFLA_MACSEC_INC_SCI]) { + info->send_sci = nla_get_u8(tb[IFLA_MACSEC_INC_SCI]); + info->ce_mask |= MACSEC_ATTR_INC_SCI; + } + + if (tb[IFLA_MACSEC_ES]) { + info->end_station = nla_get_u8(tb[IFLA_MACSEC_ES]); + info->ce_mask |= MACSEC_ATTR_ES; + } + + if (tb[IFLA_MACSEC_SCB]) { + info->scb = nla_get_u8(tb[IFLA_MACSEC_SCB]); + info->ce_mask |= MACSEC_ATTR_SCB; + } + + if (tb[IFLA_MACSEC_REPLAY_PROTECT]) { + info->replay_protect = nla_get_u8(tb[IFLA_MACSEC_REPLAY_PROTECT]); + info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT; + } + + if (tb[IFLA_MACSEC_WINDOW]) { + info->window = nla_get_u32(tb[IFLA_MACSEC_WINDOW]); + info->ce_mask |= MACSEC_ATTR_WINDOW; + } + + err = 0; +errout: + return err; +} + +static void macsec_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static const char *values_on_off[] = { "off", "on" }; + +static const char *VALIDATE_STR[] = { + [MACSEC_VALIDATE_DISABLED] = "disabled", + [MACSEC_VALIDATE_CHECK] = "check", + [MACSEC_VALIDATE_STRICT] = "strict", +}; + +static char *replay_protect_str(char *buf, uint8_t replay_protect, uint8_t window) +{ + if (replay_protect == 1) { + sprintf(buf, "replay_protect on window %d", window); + } else if (replay_protect == 0) { + sprintf(buf, "replay_protect off"); + } else { + buf[0] = '\0'; + } + + return buf; +} + +#define PRINT_FLAG(buf, i, field, c) ({ if (i->field == 1) *buf++ = c; }) +static char *flags_str(char *buf, unsigned char len, struct macsec_info *info) +{ + char *tmp = buf; + memset(tmp, 0, len); + + PRINT_FLAG(tmp, info, protect, 'P'); + PRINT_FLAG(tmp, info, encrypt, 'E'); + PRINT_FLAG(tmp, info, send_sci, 'S'); + PRINT_FLAG(tmp, info, end_station, 'e'); + PRINT_FLAG(tmp, info, scb, 's'); + PRINT_FLAG(tmp, info, replay_protect, 'R'); + + *tmp++ = ' '; + *tmp++ = 'v'; + switch (info->validate) { + case MACSEC_VALIDATE_DISABLED: + *tmp++ = 'd'; + break; + case MACSEC_VALIDATE_CHECK: + *tmp++ = 'c'; + break; + case MACSEC_VALIDATE_STRICT: + *tmp++ = 's'; + break; + default: + break; + } + + sprintf(tmp, " %d", info->encoding_sa); + + return buf; +} + +static void macsec_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct macsec_info *info = link->l_info; + char tmp[128]; + + nl_dump(p, "sci %016llx <%s>", info->sci, flags_str(tmp, sizeof(tmp), info)); +} + +static void macsec_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct macsec_info *info = link->l_info; + char tmp[128]; + + nl_dump(p, " sci %016llx protect %s encoding_sa %d encrypt %s send_sci %s validate %s %s\n", + info->sci, values_on_off[info->protect], info->encoding_sa, values_on_off[info->encrypt], values_on_off[info->send_sci], + VALIDATE_STR[info->validate], + replay_protect_str(tmp, info->replay_protect, info->window)); + nl_dump(p, " cipher suite: %016llx, icv_len %d\n", + info->cipher_suite, info->icv_len); +} + +static int macsec_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct macsec_info *copy, *info = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "macsec")) < 0) + return err; + copy = dst->l_info; + + if (!info || !copy) + return -NLE_NOMEM; + + memcpy(copy, info, sizeof(struct macsec_info)); + + return 0; +} + +static int macsec_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct macsec_info *info = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (info->ce_mask & MACSEC_ATTR_SCI) + NLA_PUT_U64(msg, IFLA_MACSEC_SCI, info->sci); + else if (info->ce_mask & MACSEC_ATTR_PORT) + NLA_PUT_U16(msg, IFLA_MACSEC_PORT, htons(info->port)); + + if ((info->ce_mask & MACSEC_ATTR_ENCRYPT)) + NLA_PUT_U8(msg, IFLA_MACSEC_ENCRYPT, info->encrypt); + + if (info->cipher_suite != MACSEC_DEFAULT_CIPHER_ID || info->icv_len != DEFAULT_ICV_LEN) { + NLA_PUT_U64(msg, IFLA_MACSEC_CIPHER_SUITE, info->cipher_suite); + NLA_PUT_U8(msg, IFLA_MACSEC_ICV_LEN, info->icv_len); + } + + if ((info->ce_mask & MACSEC_ATTR_INC_SCI)) + NLA_PUT_U8(msg, IFLA_MACSEC_INC_SCI, info->send_sci); + + if ((info->ce_mask & MACSEC_ATTR_ES)) + NLA_PUT_U8(msg, IFLA_MACSEC_ES, info->end_station); + + if ((info->ce_mask & MACSEC_ATTR_SCB)) + NLA_PUT_U8(msg, IFLA_MACSEC_SCB, info->scb); + + if ((info->ce_mask & MACSEC_ATTR_PROTECT)) + NLA_PUT_U8(msg, IFLA_MACSEC_PROTECT, info->protect); + + if ((info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT)) { + if (info->replay_protect && !(info->ce_mask & MACSEC_ATTR_WINDOW)) + return -NLE_INVAL; + + NLA_PUT_U8(msg, IFLA_MACSEC_REPLAY_PROTECT, info->replay_protect); + NLA_PUT_U32(msg, IFLA_MACSEC_WINDOW, info->window); + } + + if ((info->ce_mask & MACSEC_ATTR_VALIDATION)) + NLA_PUT_U8(msg, IFLA_MACSEC_VALIDATION, info->validate); + + if ((info->ce_mask & MACSEC_ATTR_ENCODING_SA)) + NLA_PUT_U8(msg, IFLA_MACSEC_ENCODING_SA, info->encoding_sa); + + nla_nest_end(msg, data); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int macsec_compare(struct rtnl_link *link_a, struct rtnl_link *link_b, + int flags) +{ + struct macsec_info *a = link_a->l_info; + struct macsec_info *b = link_b->l_info; + int diff = 0; + uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : ~0; + +#define MACSEC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MACSEC_ATTR_##ATTR, a, b, EXPR) + + if (a->ce_mask & MACSEC_ATTR_SCI && b->ce_mask & MACSEC_ATTR_SCI) + diff |= MACSEC_DIFF(SCI, a->sci != b->sci); + else if (a->ce_mask & MACSEC_ATTR_PORT && b->ce_mask & MACSEC_ATTR_PORT) + diff |= MACSEC_DIFF(PORT, a->port != b->port); + + if (a->ce_mask & MACSEC_ATTR_CIPHER_SUITE && b->ce_mask & MACSEC_ATTR_CIPHER_SUITE) { + diff |= MACSEC_DIFF(ICV_LEN, a->icv_len != b->icv_len); + diff |= MACSEC_DIFF(CIPHER_SUITE, a->cipher_suite != b->cipher_suite); + } + + if (a->ce_mask & MACSEC_ATTR_REPLAY_PROTECT && b->ce_mask & MACSEC_ATTR_REPLAY_PROTECT) { + int d = MACSEC_DIFF(REPLAY_PROTECT, a->replay_protect != b->replay_protect); + if (a->replay_protect && b->replay_protect) + d |= MACSEC_DIFF(WINDOW, a->window != b->window); + diff |= d; + } + + diff |= MACSEC_DIFF(ENCODING_SA, a->encoding_sa != b->encoding_sa); + diff |= MACSEC_DIFF(ENCRYPT, a->encrypt != b->encrypt); + diff |= MACSEC_DIFF(PROTECT, a->protect != b->protect); + diff |= MACSEC_DIFF(INC_SCI, a->send_sci != b->send_sci); + diff |= MACSEC_DIFF(ES, a->end_station != b->end_station); + diff |= MACSEC_DIFF(SCB, a->scb != b->scb); + diff |= MACSEC_DIFF(VALIDATION, a->validate != b->validate); +#undef MACSEC_DIFF + + return diff; +} + + +static struct rtnl_link_info_ops macsec_info_ops = { + .io_name = "macsec", + .io_alloc = macsec_alloc, + .io_parse = macsec_parse, + .io_dump = { + [NL_DUMP_LINE] = macsec_dump_line, + [NL_DUMP_DETAILS] = macsec_dump_details, + }, + .io_clone = macsec_clone, + .io_put_attrs = macsec_put_attrs, + .io_free = macsec_free, + .io_compare = macsec_compare, +}; + +static void __init macsec_init(void) +{ + rtnl_link_register_info(&macsec_info_ops); +} + +static void __exit macsec_exit(void) +{ + rtnl_link_unregister_info(&macsec_info_ops); +} + +#define IS_MACSEC_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &macsec_info_ops) { \ + APPBUG("Link is not a MACsec link. set type \"macsec\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +struct rtnl_link *rtnl_link_macsec_alloc(void) +{ + struct rtnl_link *link = rtnl_link_alloc(); + + if (!link) + return NULL; + + if (rtnl_link_set_type(link, "macsec") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +int rtnl_link_macsec_set_sci(struct rtnl_link *link, uint64_t sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->sci = sci; + info->ce_mask |= MACSEC_ATTR_SCI; + + return 0; +} + +int rtnl_link_macsec_get_sci(struct rtnl_link *link, uint64_t *sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_SCI)) + return -NLE_NOATTR; + + if (sci) + *sci = info->sci; + + return 0; +} + +int rtnl_link_macsec_set_port(struct rtnl_link *link, uint16_t port) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->port = port; + info->ce_mask |= MACSEC_ATTR_PORT; + + return 0; +} + +int rtnl_link_macsec_get_port(struct rtnl_link *link, uint16_t *port) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_PORT)) + return -NLE_NOATTR; + + if (port) + *port = info->port; + + return 0; +} + +int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *link, uint64_t cipher_suite) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->cipher_suite = cipher_suite; + info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE; + + return 0; +} + +int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *link, uint64_t *cs) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_CIPHER_SUITE)) + return -NLE_NOATTR; + + if (cs) + *cs = info->cipher_suite; + + return 0; +} + +int rtnl_link_macsec_set_icv_len(struct rtnl_link *link, uint16_t icv_len) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (icv_len > MACSEC_MAX_ICV_LEN) + return -NLE_INVAL; + + info->icv_len = icv_len; + info->ce_mask |= MACSEC_ATTR_ICV_LEN; + + return 0; +} + +int rtnl_link_macsec_get_icv_len(struct rtnl_link *link, uint16_t *icv_len) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ICV_LEN)) + return -NLE_NOATTR; + + if (icv_len) + *icv_len = info->icv_len; + + return 0; +} + +int rtnl_link_macsec_set_protect(struct rtnl_link *link, uint8_t protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (protect > 1) + return -NLE_INVAL; + + info->protect = protect; + info->ce_mask |= MACSEC_ATTR_PROTECT; + + return 0; +} + +int rtnl_link_macsec_get_protect(struct rtnl_link *link, uint8_t *protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_PROTECT)) + return -NLE_NOATTR; + + if (protect) + *protect = info->protect; + + return 0; +} + +int rtnl_link_macsec_set_encrypt(struct rtnl_link *link, uint8_t encrypt) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (encrypt > 1) + return -NLE_INVAL; + + info->encrypt = encrypt; + info->ce_mask |= MACSEC_ATTR_ENCRYPT; + + return 0; +} + +int rtnl_link_macsec_get_encrypt(struct rtnl_link *link, uint8_t *encrypt) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ENCRYPT)) + return -NLE_NOATTR; + + if (encrypt) + *encrypt = info->encrypt; + + return 0; +} + +int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *link, uint8_t encoding_sa) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (encoding_sa > 3) + return -NLE_INVAL; + + info->encoding_sa = encoding_sa; + info->ce_mask |= MACSEC_ATTR_ENCODING_SA; + + return 0; +} + +int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *link, uint8_t *encoding_sa) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ENCODING_SA)) + return -NLE_NOATTR; + + if (encoding_sa) + *encoding_sa = info->encoding_sa; + + return 0; +} + +int rtnl_link_macsec_set_validation_type(struct rtnl_link *link, enum macsec_validation_type validate) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (validate > 1) + return -NLE_INVAL; + + info->validate = validate; + info->ce_mask |= MACSEC_ATTR_VALIDATION; + + return 0; +} + +int rtnl_link_macsec_get_validation_type(struct rtnl_link *link, enum macsec_validation_type *validate) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_VALIDATION)) + return -NLE_NOATTR; + + if (validate) + *validate = info->validate; + + return 0; +} + +int rtnl_link_macsec_set_replay_protect(struct rtnl_link *link, uint8_t replay_protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (replay_protect > 1) + return -NLE_INVAL; + + info->replay_protect = replay_protect; + info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT; + + return 0; +} + +int rtnl_link_macsec_get_replay_protect(struct rtnl_link *link, uint8_t *replay_protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT)) + return -NLE_NOATTR; + + if (replay_protect) + *replay_protect = info->replay_protect; + + return 0; +} + +int rtnl_link_macsec_set_window(struct rtnl_link *link, uint32_t window) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->window = window; + info->ce_mask |= MACSEC_ATTR_WINDOW; + + return 0; +} + +int rtnl_link_macsec_get_window(struct rtnl_link *link, uint32_t *window) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_WINDOW)) + return -NLE_NOATTR; + + if (window) + *window = info->window; + + return 0; +} + +int rtnl_link_macsec_set_send_sci(struct rtnl_link *link, uint8_t send_sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (send_sci > 1) + return -NLE_INVAL; + + info->send_sci = send_sci; + info->ce_mask |= MACSEC_ATTR_INC_SCI; + + return 0; +} + +int rtnl_link_macsec_get_send_sci(struct rtnl_link *link, uint8_t *send_sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_INC_SCI)) + return -NLE_NOATTR; + + if (send_sci) + *send_sci = info->send_sci; + + return 0; +} + +int rtnl_link_macsec_set_end_station(struct rtnl_link *link, uint8_t end_station) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (end_station > 1) + return -NLE_INVAL; + + info->end_station = end_station; + info->ce_mask |= MACSEC_ATTR_ES; + + return 0; +} + +int rtnl_link_macsec_get_end_station(struct rtnl_link *link, uint8_t *es) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ES)) + return -NLE_NOATTR; + + if (es) + *es = info->end_station; + + return 0; +} + +int rtnl_link_macsec_set_scb(struct rtnl_link *link, uint8_t scb) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (scb > 1) + return -NLE_INVAL; + + info->scb = scb; + info->ce_mask |= MACSEC_ATTR_SCB; + + return 0; +} + +int rtnl_link_macsec_get_scb(struct rtnl_link *link, uint8_t *scb) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_SCB)) + return -NLE_NOATTR; + + if (scb) + *scb = info->scb; + + return 0; +} diff --git a/libnl-route-3.sym b/libnl-route-3.sym index ee1677b..13e1a51 100644 --- a/libnl-route-3.sym +++ b/libnl-route-3.sym @@ -895,6 +895,33 @@ global: rtnl_link_is_vrf; rtnl_link_ipgretap_add; rtnl_link_ipgretap_alloc; + rtnl_link_macsec_alloc; + rtnl_link_macsec_set_sci; + rtnl_link_macsec_get_sci; + rtnl_link_macsec_set_port; + rtnl_link_macsec_get_port; + rtnl_link_macsec_set_cipher_suite; + rtnl_link_macsec_get_cipher_suite; + rtnl_link_macsec_set_icv_len; + rtnl_link_macsec_get_icv_len; + rtnl_link_macsec_set_protect; + rtnl_link_macsec_get_protect; + rtnl_link_macsec_set_encrypt; + rtnl_link_macsec_get_encrypt; + rtnl_link_macsec_set_encoding_sa; + rtnl_link_macsec_get_encoding_sa; + rtnl_link_macsec_set_validation_type; + rtnl_link_macsec_get_validation_type; + rtnl_link_macsec_set_replay_protect; + rtnl_link_macsec_get_replay_protect; + rtnl_link_macsec_set_window; + rtnl_link_macsec_get_window; + rtnl_link_macsec_set_send_sci; + rtnl_link_macsec_get_send_sci; + rtnl_link_macsec_set_end_station; + rtnl_link_macsec_get_end_station; + rtnl_link_macsec_set_scb; + rtnl_link_macsec_get_scb; rtnl_link_macvtap_alloc; rtnl_link_macvtap_flags2str; rtnl_link_macvtap_get_flags; diff --git a/tests/.gitignore b/tests/.gitignore index 9666f27..247b0e6 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -14,6 +14,7 @@ /test-create-ipip /test-create-ipvlan /test-create-ipvti +/test-create-macsec /test-create-macvlan /test-create-macvtap /test-create-sit diff --git a/tests/Makefile.am b/tests/Makefile.am index 5cf43e9..c31fb52 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,6 +35,7 @@ check_PROGRAMS = \ test-create-ipgretap \ test-create-ipip \ test-create-ipvti \ + test-create-macsec \ test-create-macvlan \ test-create-macvtap \ test-create-ipvlan \ diff --git a/tests/test-create-macsec.c b/tests/test-create-macsec.c new file mode 100644 index 0000000..abdb7f6 --- /dev/null +++ b/tests/test-create-macsec.c @@ -0,0 +1,47 @@ +#include <netlink/netlink.h> +#include <netlink/route/link.h> +#include <netlink/route/link/macsec.h> + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + int err, master_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + + link = rtnl_link_macsec_alloc(); + + rtnl_link_set_link(link, master_index); + + rtnl_link_macsec_set_port(link, 10); + rtnl_link_macsec_set_encrypt(link, 1); + rtnl_link_macsec_set_replay_protect(link, 1); + rtnl_link_macsec_set_window(link, 200); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + + return 0; +} |