/* SPDX-License-Identifier: LGPL-2.1-only */ /* * 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) 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 sucess 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 sucess 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 __init xfrmi_init(void) { rtnl_link_register_info(&xfrmi_info_ops); } static void __exit xfrmi_exit(void) { rtnl_link_unregister_info(&xfrmi_info_ops); }