| /* SPDX-License-Identifier: LGPL-2.1-only */ |
| /* |
| * Copyright (c) 2019 Eyal Birger <[email protected]> |
| * |
| * 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 "nl-default.h" |
| |
| #include <netlink/netlink.h> |
| #include <netlink/attr.h> |
| #include <netlink/utils.h> |
| #include <netlink/object.h> |
| #include <netlink/route/rtnl.h> |
| #include <netlink/route/link/xfrmi.h> |
| |
| #include "nl-route.h" |
| #include "link-api.h" |
| |
| #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) |
| return err; |
| |
| err = xfrmi_alloc(link); |
| if (err < 0) |
| return err; |
| |
| 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; |
| } |
| |
| return 0; |
| } |
| |
| 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 |
| * @arg out_link The output value on success |
| * |
| * @return 0 on success or a negative error code |
| */ |
| int rtnl_link_xfrmi_get_link(struct rtnl_link *link, uint32_t *out_link) |
| { |
| struct xfrmi_info *xfrmi = link->l_info; |
| |
| IS_XFRMI_LINK_ASSERT(link); |
| |
| if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_LINK)) |
| return -NLE_NOATTR; |
| |
| *out_link = xfrmi->link; |
| return 0; |
| } |
| |
| /** |
| * 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 |
| * @arg out_if_id The output value on success |
| * |
| * @return 0 on success or a negative error code |
| */ |
| int rtnl_link_xfrmi_get_if_id(struct rtnl_link *link, uint32_t *out_if_id) |
| { |
| struct xfrmi_info *xfrmi = link->l_info; |
| |
| IS_XFRMI_LINK_ASSERT(link); |
| |
| if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID)) |
| return -NLE_NOATTR; |
| |
| *out_if_id = xfrmi->if_id; |
| return 0; |
| } |
| |
| static void _nl_init xfrmi_init(void) |
| { |
| rtnl_link_register_info(&xfrmi_info_ops); |
| } |
| |
| static void _nl_exit xfrmi_exit(void) |
| { |
| rtnl_link_unregister_info(&xfrmi_info_ops); |
| } |