From d147019c4bbb856be13042d89e95b859f01c808d Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sun, 7 Apr 2019 17:09:34 +0300 Subject: [PATCH] xfrmi: introduce XFRM interfaces support XFRM interfaces were introduced in kernel 4.19. This commit adds link support for these interfaces. Signed-off-by: Eyal Birger --- Makefile.am | 5 + doc/route.txt | 43 ++++ include/netlink/route/link/xfrmi.h | 37 ++++ lib/route/link/xfrmi.c | 312 +++++++++++++++++++++++++++++ libnl-route-3.sym | 6 + tests/.gitignore | 1 + tests/test-create-xfrmi.c | 49 +++++ 7 files changed, 453 insertions(+) create mode 100644 include/netlink/route/link/xfrmi.h create mode 100644 lib/route/link/xfrmi.c create mode 100644 tests/test-create-xfrmi.c diff --git a/Makefile.am b/Makefile.am index 88830be..b2e8737 100644 --- a/Makefile.am +++ b/Makefile.am @@ -166,6 +166,7 @@ libnlinclude_netlink_route_link_HEADERS = \ include/netlink/route/link/vlan.h \ include/netlink/route/link/vrf.h \ include/netlink/route/link/vxlan.h \ + include/netlink/route/link/xfrmi.h \ $(NULL) libnlinclude_netlink_route_qdiscdir = $(libnlincludedir)/netlink/route/qdisc libnlinclude_netlink_route_qdisc_HEADERS = \ @@ -412,6 +413,7 @@ lib_libnl_route_3_la_SOURCES = \ lib/route/link/vlan.c \ lib/route/link/vrf.c \ lib/route/link/vxlan.c \ + lib/route/link/xfrmi.c \ lib/route/neigh.c \ lib/route/neightbl.c \ lib/route/netconf.c \ @@ -852,6 +854,7 @@ check_PROGRAMS += \ tests/test-create-vlan \ tests/test-create-vrf \ tests/test-create-vxlan \ + tests/test-create-xfrmi \ tests/test-delete-link \ tests/test-loopback-up-down \ tests/test-socket-creation \ @@ -896,6 +899,8 @@ tests_test_create_vrf_CPPFLAGS = $(tests_cppflags) tests_test_create_vrf_LDADD = $(tests_ldadd) tests_test_create_vxlan_CPPFLAGS = $(tests_cppflags) tests_test_create_vxlan_LDADD = $(tests_ldadd) +tests_test_create_xfrmi_CPPFLAGS = $(tests_cppflags) +tests_test_create_xfrmi_LDADD = $(tests_ldadd) tests_test_delete_link_CPPFLAGS = $(tests_cppflags) tests_test_delete_link_LDADD = $(tests_ldadd) tests_test_loopback_up_down_CPPFLAGS = $(tests_cppflags) diff --git a/doc/route.txt b/doc/route.txt index 6a0dbb9..9d4c23a 100644 --- a/doc/route.txt +++ b/doc/route.txt @@ -1257,6 +1257,49 @@ rtnl_link_put(link); ----- +[[link_xfrmi]] +==== XFRMI + +[source,c] +----- +extern struct rtnl_link *rtnl_link_xfrmi_alloc(void); + +extern int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index); +extern uint32_t rtnl_link_xfrmi_get_link(struct rtnl_link *link); + +extern int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id); +extern uint32_t rtnl_link_xfrmi_get_if_id(struct rtnl_link *link); + +----- + +.Example: Add a xfrmi device +[source,c] +----- +struct rtnl_link *link +struct in_addr addr + +/* allocate new link object of type xfrmi */ +if(!(link = rtnl_link_xfrmi_alloc())) + /* error */ + +/* set xfrmi name */ +if ((err = rtnl_link_set_name(link, "ipsec0")) < 0) + /* error */ + +/* set link index */ +if ((err = rtnl_link_xfrmi_set_link(link, if_index)) < 0) + /* error */ + +/* set if_id */ +if ((err = rtnl_link_xfrmi_set_if_id(link, 16)) < 0) + /* error */ + +if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) + /* error */ + +rtnl_link_put(link); +----- + == Neighbouring == Routing diff --git a/include/netlink/route/link/xfrmi.h b/include/netlink/route/link/xfrmi.h new file mode 100644 index 0000000..f361229 --- /dev/null +++ b/include/netlink/route/link/xfrmi.h @@ -0,0 +1,37 @@ +/* + * netlink/route/link/xfrmi.h XFRMI interface + * + * 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) 2019 Eyal Birger + * + * Based on netlink/route/link/ipvti.h + */ + +#ifndef NETLINK_LINK_XFRMI_H_ +#define NETLINK_LINK_XFRMI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern struct rtnl_link *rtnl_link_xfrmi_alloc(void); + + extern int rtnl_link_is_xfrmi(struct rtnl_link *link); + + extern int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_xfrmi_get_link(struct rtnl_link *link); + + extern int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id); + extern uint32_t rtnl_link_xfrmi_get_if_id(struct rtnl_link *link); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/route/link/xfrmi.c b/lib/route/link/xfrmi.c new file mode 100644 index 0000000..65533a6 --- /dev/null +++ b/lib/route/link/xfrmi.c @@ -0,0 +1,312 @@ +/* + * lib/route/link/xfrmi.c XFRMI 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) 2019 Eyal Birger + * + * Based on lib/route/link/ipvti.c + */ + +/** + * @ingroup link + * @defgroup xfrmi XFRMI + * xfrmi link module + * + * @details + * \b Link Type Name: "xfrmi" + * + * @route_doc{link_xfrmi, XFRMI Documentation} + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define XFRMI_ATTR_LINK (1 << 0) +#define XFRMI_ATTR_IF_ID (1 << 1) + +#define XFRMI_LINK_TYPE_NAME "xfrm" + +struct xfrmi_info { + uint32_t link; + uint32_t if_id; + uint32_t xfrmi_mask; +}; + +static struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { + [IFLA_XFRM_LINK] = { .type = NLA_U32 }, + [IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, +}; + +static int xfrmi_alloc(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*xfrmi)); + else { + xfrmi = calloc(1, sizeof(*xfrmi)); + if (!xfrmi) + return -NLE_NOMEM; + + link->l_info = xfrmi; + } + + return 0; +} + +static int xfrmi_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_XFRM_MAX + 1]; + struct xfrmi_info *xfrmi; + int err; + + NL_DBG(3, "Parsing XFRMI link info\n"); + + err = nla_parse_nested(tb, IFLA_XFRM_MAX, data, xfrmi_policy); + if (err < 0) + goto errout; + + err = xfrmi_alloc(link); + if (err < 0) + goto errout; + + xfrmi = link->l_info; + + if (tb[IFLA_XFRM_LINK]) { + xfrmi->link = nla_get_u32(tb[IFLA_XFRM_LINK]); + xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK; + } + + if (tb[IFLA_XFRM_IF_ID]) { + xfrmi->if_id = nla_get_u32(tb[IFLA_XFRM_IF_ID]); + xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID; + } + + err = 0; + +errout: + return err; +} + +static int xfrmi_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_XFRM_LINK, xfrmi->link); + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID) + NLA_PUT_U32(msg, IFLA_XFRM_IF_ID, xfrmi->if_id); + + nla_nest_end(msg, data); + +nla_put_failure: + return 0; +} + +static void xfrmi_free(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + + free(xfrmi); + link->l_info = NULL; +} + +static void xfrmi_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "xfrmi : %s", link->l_name); +} + +static void xfrmi_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct xfrmi_info *xfrmi = link->l_info; + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK) { + struct rtnl_link *parent; + char *name; + + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, xfrmi->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", xfrmi->link); + } + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID) { + nl_dump(p, " if_id "); + nl_dump_line(p, "%x\n", xfrmi->if_id); + } +} + +static int xfrmi_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct xfrmi_info *xfrmi_dst, *xfrmi_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, XFRMI_LINK_TYPE_NAME); + if (err < 0) + return err; + + xfrmi_dst = dst->l_info; + + if (!xfrmi_dst || !xfrmi_src) + BUG(); + + memcpy(xfrmi_dst, xfrmi_src, sizeof(struct xfrmi_info)); + + return 0; +} + +static struct rtnl_link_info_ops xfrmi_info_ops = { + .io_name = XFRMI_LINK_TYPE_NAME, + .io_alloc = xfrmi_alloc, + .io_parse = xfrmi_parse, + .io_dump = { + [NL_DUMP_LINE] = xfrmi_dump_line, + [NL_DUMP_DETAILS] = xfrmi_dump_details, + }, + .io_clone = xfrmi_clone, + .io_put_attrs = xfrmi_put_attrs, + .io_free = xfrmi_free, +}; + +#define IS_XFRMI_LINK_ASSERT(link) do { \ + if ((link)->l_info_ops != &xfrmi_info_ops) { \ + APPBUG("Link is not a xfrmi link. set type \"xfrmi\" first."); \ + return -NLE_OPNOTSUPP; \ + } \ + } while(0) + +struct rtnl_link *rtnl_link_xfrmi_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, XFRMI_LINK_TYPE_NAME); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a XFRMI link + * @arg link Link object + * + * @return True if link is a IXFRMI link, otherwise 0 is returned. + */ +int rtnl_link_is_xfrmi(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, + XFRMI_LINK_TYPE_NAME); +} + +/** + * Set XFRMI link interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + xfrmi->link = index; + xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK; + + return 0; +} + +/** + * Get XFRMI link interface index + * @arg link Link object + * + * @return interface index + */ +uint32_t rtnl_link_xfrmi_get_link(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + return xfrmi->link; +} + +/** + * Set XFRMI if_id + * @arg link Link object + * @arg if_id xfrm if_id + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + xfrmi->if_id = if_id; + xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID; + + return 0; +} + +/** + * Get XFRMI if_id + * @arg link Link object + * + * @return if_id + */ +uint32_t rtnl_link_xfrmi_get_if_id(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + return xfrmi->if_id; +} + +static void __init xfrmi_init(void) +{ + rtnl_link_register_info(&xfrmi_info_ops); +} + +static void __exit xfrmi_exit(void) +{ + rtnl_link_unregister_info(&xfrmi_info_ops); +} diff --git a/libnl-route-3.sym b/libnl-route-3.sym index 5a82d85..9925a41 100644 --- a/libnl-route-3.sym +++ b/libnl-route-3.sym @@ -1090,6 +1090,12 @@ global: rtnl_link_get_slave_type; rtnl_link_is_geneve; rtnl_link_set_slave_type; + rtnl_link_is_xfrmi; + rtnl_link_xfrmi_alloc; + rtnl_link_xfrmi_get_link; + rtnl_link_xfrmi_get_if_id; + rtnl_link_xfrmi_set_link; + rtnl_link_xfrmi_set_if_id; rtnl_mall_append_action; rtnl_mall_del_action; rtnl_mall_get_classid; diff --git a/tests/.gitignore b/tests/.gitignore index 425a7c4..90af67a 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -23,6 +23,7 @@ /test-create-vlan /test-create-vrf /test-create-vxlan +/test-create-xfrmi /test-delete-link /test-genl /test-loopback-up-down diff --git a/tests/test-create-xfrmi.c b/tests/test-create-xfrmi.c new file mode 100644 index 0000000..3a01a4f --- /dev/null +++ b/tests/test-create-xfrmi.c @@ -0,0 +1,49 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + struct rtnl_link *link; + struct nl_sock *sk; + int err, if_index; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + + err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); + if (err < 0) { + nl_perror(err, "Unable to allocate cache"); + return err; + } + + if_index = rtnl_link_name2i(link_cache, "eth0"); + if (!if_index) { + fprintf(stderr, "Unable to lookup eth0"); + return -1; + } + + link = rtnl_link_xfrmi_alloc(); + if (!link) { + nl_perror(err, "Unable to allocate link"); + return -1; + + } + + rtnl_link_set_name(link, "ipsec0"); + rtnl_link_xfrmi_set_link(link, if_index); + rtnl_link_xfrmi_set_if_id(link, 16); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + if (err < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + + rtnl_link_put(link); + nl_close(sk); + return 0; +} -- 2.40.0