summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSabrina Dubroca <sd@queasysnail.net>2016-06-24 16:34:39 +0200
committerSabrina Dubroca <sd@queasysnail.net>2016-06-25 18:27:14 +0200
commit885ff4ae1efce35f8db378b2533025c3c931823c (patch)
tree5c848f99f4608144cbeefd796848beb4e4e0c878
parent6ecba6e44022a2d0f84e86579769b46086a182ca (diff)
downloadlibnl-885ff4ae1efce35f8db378b2533025c3c931823c.tar.gz
lib/route: add macsec support
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
-rw-r--r--include/netlink/route/link/macsec.h73
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/route/link/macsec.c786
-rw-r--r--libnl-route-3.sym27
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/test-create-macsec.c47
7 files changed, 936 insertions, 1 deletions
diff --git a/include/netlink/route/link/macsec.h b/include/netlink/route/link/macsec.h
new file mode 100644
index 00000000..d9cf7da5
--- /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 320c511c..21d28caf 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 00000000..671a0b97
--- /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 ee1677b1..13e1a514 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 9666f279..247b0e6f 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 5cf43e94..c31fb527 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 00000000..abdb7f60
--- /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;
+}