summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStanislav Zaikin <zstaseg@gmail.com>2023-07-27 09:02:51 (GMT)
committerThomas Haller <thaller@redhat.com>2023-07-31 13:11:28 (GMT)
commit780d06ae8bee5b2a8b3072a215de71929363e8d4 (patch)
treeab2bbfacb7ee78f9c20b2449dacf21d5e8915f7d
parent1b6433d9f6d1e6dd513fe9216b7ac7b6ef9e0d0e (diff)
downloadlibnl-780d06ae8bee5b2a8b3072a215de71929363e8d4.zip
libnl-780d06ae8bee5b2a8b3072a215de71929363e8d4.tar.gz
libnl-780d06ae8bee5b2a8b3072a215de71929363e8d4.tar.bz2
route: add nh type
-rw-r--r--Makefile.am2
-rw-r--r--include/netlink-private/nl-auto.h5
-rw-r--r--include/netlink-private/types.h23
-rw-r--r--include/netlink/route/nh.h57
-rw-r--r--lib/route/nh.c568
-rw-r--r--libnl-route-3.sym14
6 files changed, 669 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index cdac799..b2796b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -110,6 +110,7 @@ libnlinclude_netlink_route_HEADERS = \
include/netlink/route/neightbl.h \
include/netlink/route/netconf.h \
include/netlink/route/nexthop.h \
+ include/netlink/route/nh.h \
include/netlink/route/pktloc.h \
include/netlink/route/qdisc.h \
include/netlink/route/route.h \
@@ -437,6 +438,7 @@ lib_libnl_route_3_la_SOURCES = \
lib/route/neightbl.c \
lib/route/netconf.c \
lib/route/nexthop.c \
+ lib/route/nh.c \
lib/route/nexthop_encap.c \
lib/route/nh_encap_mpls.c \
lib/route/pktloc.c \
diff --git a/include/netlink-private/nl-auto.h b/include/netlink-private/nl-auto.h
index 4895169..9e09776 100644
--- a/include/netlink-private/nl-auto.h
+++ b/include/netlink-private/nl-auto.h
@@ -69,6 +69,11 @@ void rtnl_route_nh_free(struct rtnl_nexthop *);
#define _nl_auto_rtnl_nexthop _nl_auto(_nl_auto_rtnl_nexthop_fcn)
_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_nexthop *, _nl_auto_rtnl_nexthop_fcn, rtnl_route_nh_free);
+struct rtnl_nh;
+void rtnl_nh_put(struct rtnl_nh *);
+#define _nl_auto_rtnl_nh _nl_auto(_nl_auto_rtnl_nh_fcn)
+_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_nh *, _nl_auto_rtnl_nh_fcn, rtnl_nh_put);
+
struct nl_cache;
void nl_cache_put(struct nl_cache *);
#define _nl_auto_nl_cache _nl_auto(_nl_auto_nl_cache_fcn)
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index 7075e69..c36e01a 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -17,6 +17,7 @@
#include <netlink-private/object-api.h>
#include <netlink-private/route/tc-api.h>
#include <netlink-private/route/link/sriov.h>
+#include <netlink/route/nh.h>
#include <netlink-private/route/nexthop-encap.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
@@ -324,6 +325,28 @@ struct rtnl_nexthop
struct rtnl_nh_encap * rtnh_encap;
};
+struct rtnl_nexthop_group
+{
+ struct nl_list_head nexthop_list;
+ int ce_refcnt;
+ uint32_t id;
+ uint8_t weight;
+};
+
+struct rtnl_nh
+{
+ NLHDR_COMMON
+
+ uint8_t nh_family;
+ uint32_t nh_flags;
+
+ uint32_t nh_id;
+ uint32_t nh_group_type;
+ nl_nh_group_t * nh_group;
+ uint32_t nh_oif;
+ struct nl_addr * nh_gateway;
+};
+
struct rtnl_route
{
NLHDR_COMMON
diff --git a/include/netlink/route/nh.h b/include/netlink/route/nh.h
new file mode 100644
index 0000000..fec82dd
--- /dev/null
+++ b/include/netlink/route/nh.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (c) 2022 Stanislav Zaikin <zstaseg@gmail.com>
+ */
+
+#ifndef NETLINK_ROUTE_NEXTHOP2_H_
+#define NETLINK_ROUTE_NEXTHOP2_H_
+
+#include <netlink/netlink.h>
+#include <netlink/addr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtnl_nh;
+
+typedef struct nl_nh_group_info {
+ uint32_t nh_id; /*!< VLAN number */
+ uint8_t weight; /*!< VLAN QOS value */
+} nl_nh_group_info_t;
+
+typedef struct nl_nh_group {
+ int ce_refcnt;
+ unsigned size;
+ nl_nh_group_info_t *entries;
+} nl_nh_group_t;
+
+extern int rtnl_nh_alloc_cache(struct nl_sock *sk, int family,
+ struct nl_cache **result);
+extern struct rtnl_nh *rtnl_nh_alloc(void);
+extern struct rtnl_nh *rtnl_nh_clone(struct rtnl_nh *);
+extern void rtnl_nh_put(struct rtnl_nh *);
+
+extern struct rtnl_nh *rtnl_nh_get(struct nl_cache *cache, int nhid);
+
+extern int rtnl_nh_compare(struct rtnl_nh *, struct rtnl_nh *, uint32_t, int);
+
+extern void rtnl_nh_dump(struct rtnl_nh *, struct nl_dump_params *);
+
+extern int rtnl_nh_set_gateway(struct rtnl_nh *, struct nl_addr *);
+extern struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *);
+
+extern int rtnl_nh_set_fdb(struct rtnl_nh *, int value);
+extern int rtnl_nh_get_fdb(struct rtnl_nh *);
+
+extern int rtnl_nh_get_group_entry(struct rtnl_nh *, int n);
+extern int rtnl_nh_get_group_size(struct rtnl_nh *);
+
+extern int rtnl_nh_get_id(struct rtnl_nh *);
+extern int rtnl_nh_get_oif(struct rtnl_nh *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/route/nh.c b/lib/route/nh.c
new file mode 100644
index 0000000..84b0af5
--- /dev/null
+++ b/lib/route/nh.c
@@ -0,0 +1,568 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (c) 2022 Stanislav Zaikin <zstaseg@gmail.com>
+ */
+
+#include <linux-private/linux/nexthop.h>
+#include <netlink-private/netlink.h>
+#include <netlink/route/nh.h>
+#include <netlink/hashtable.h>
+
+/** @cond SKIP */
+#define NH_ATTR_FLAGS (1 << 0)
+#define NH_ATTR_ID (1 << 1)
+#define NH_ATTR_GROUP (1 << 2)
+#define NH_ATTR_FLAG_BLACKHOLE (1 << 3)
+#define NH_ATTR_OIF (1 << 4)
+#define NH_ATTR_GATEWAY (1 << 5)
+#define NH_ATTR_FLAG_GROUPS (1 << 6)
+#define NH_ATTR_FLAG_FDB (1 << 8)
+/** @endcond */
+
+struct nla_policy rtnl_nh_policy[NHA_MAX + 1] = {
+ [NHA_UNSPEC] = { .type = NLA_UNSPEC },
+ [NHA_ID] = { .type = NLA_U32 },
+ [NHA_GROUP] = { .type = NLA_NESTED },
+ [NHA_GROUP_TYPE] = { .type = NLA_U16 },
+ [NHA_BLACKHOLE] = { .type = NLA_UNSPEC },
+ [NHA_OIF] = { .type = NLA_U32 },
+};
+
+static struct nl_cache_ops rtnl_nh_ops;
+static struct nl_object_ops nh_obj_ops;
+
+static nl_nh_group_t *rtnl_nh_grp_alloc(unsigned size)
+{
+ nl_nh_group_t *nhg;
+
+ _nl_assert(size <= (unsigned)INT_MAX);
+
+ if (!(nhg = calloc(1, sizeof(*nhg))))
+ return NULL;
+
+ nhg->size = size;
+
+ if (!(nhg->entries = calloc(size, sizeof(*nhg->entries)))) {
+ free(nhg);
+ return NULL;
+ }
+
+ nhg->ce_refcnt = 1;
+
+ return nhg;
+}
+
+static void rtnl_nh_grp_put(nl_nh_group_t *nhg)
+{
+ if (!nhg)
+ return;
+
+ _nl_assert(nhg->ce_refcnt > 0);
+
+ nhg->ce_refcnt--;
+
+ if (nhg->ce_refcnt > 0)
+ return;
+
+ free(nhg);
+}
+
+static int rtnh_nh_grp_cmp(const nl_nh_group_t *a, const nl_nh_group_t *b)
+{
+ unsigned i;
+
+ _NL_CMP_SELF(a, b);
+ _NL_CMP_DIRECT(a->size, b->size);
+ for (i = 0; i < a->size; i++) {
+ _NL_CMP_DIRECT(a->entries[i].nh_id, b->entries[i].nh_id);
+ _NL_CMP_DIRECT(a->entries[i].weight, b->entries[i].weight);
+ }
+ return 0;
+}
+
+static int rtnh_nh_grp_clone(nl_nh_group_t *src, nl_nh_group_t **dst)
+{
+ nl_nh_group_t *ret;
+ unsigned i;
+
+ ret = rtnl_nh_grp_alloc(src->size);
+
+ if (!ret)
+ return -NLE_NOMEM;
+
+ for (i = 0; i < src->size; i++) {
+ ret->entries[i].nh_id = src->entries[i].nh_id;
+ ret->entries[i].weight = src->entries[i].weight;
+ }
+
+ *dst = ret;
+
+ return NLE_SUCCESS;
+}
+
+struct rtnl_nh *rtnl_nh_alloc(void)
+{
+ return (struct rtnl_nh *)nl_object_alloc(&nh_obj_ops);
+}
+
+static int nh_clone(struct nl_object *_src, struct nl_object *_dst)
+{
+ struct rtnl_nh *dst = nl_object_priv(_dst);
+ struct rtnl_nh *src = nl_object_priv(_src);
+
+ dst->nh_flags = src->nh_flags;
+ dst->nh_family = src->nh_family;
+ dst->nh_id = src->nh_id;
+ dst->nh_oif = src->nh_oif;
+ dst->ce_mask = src->ce_mask;
+
+ if (src->nh_gateway) {
+ dst->nh_gateway = nl_addr_clone(src->nh_gateway);
+ if (!dst->nh_gateway) {
+ return -NLE_NOMEM;
+ }
+ }
+
+ if (src->nh_group) {
+ if (rtnh_nh_grp_clone(src->nh_group, &dst->nh_group) < 0) {
+ return -NLE_NOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static void nh_free(struct nl_object *obj)
+{
+ struct rtnl_nh *nh = nl_object_priv(obj);
+ nl_addr_put(nh->nh_gateway);
+
+ if (nh->nh_group)
+ rtnl_nh_grp_put(nh->nh_group);
+}
+
+void rtnl_nh_put(struct rtnl_nh *nh)
+{
+ struct nl_object *obj = (struct nl_object *)nh;
+
+ nl_object_put(obj);
+}
+
+static void nexthop_keygen(struct nl_object *obj, uint32_t *hashkey,
+ uint32_t table_sz)
+{
+ struct rtnl_nh *nexthop = nl_object_priv(obj);
+ unsigned int lkey_sz;
+ struct nexthop_hash_key {
+ uint32_t nh_id;
+ } __attribute__((packed)) lkey;
+
+ lkey_sz = sizeof(lkey);
+ lkey.nh_id = nexthop->nh_id;
+
+ *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz;
+
+ return;
+}
+
+int rtnl_nh_set_gateway(struct rtnl_nh *nexthop, struct nl_addr *addr)
+{
+ if (nexthop->ce_mask & NH_ATTR_GATEWAY) {
+ nl_addr_put(nexthop->nh_gateway);
+ }
+
+ nexthop->nh_gateway = nl_addr_clone(addr);
+ nexthop->ce_mask |= NH_ATTR_GATEWAY;
+
+ return 0;
+}
+
+struct nl_addr *rtnl_nh_get_gateway(struct rtnl_nh *nexthop)
+{
+ return nexthop->nh_gateway;
+}
+
+int rtnl_nh_set_fdb(struct rtnl_nh *nexthop, int value)
+{
+ if (value)
+ nexthop->ce_mask |= NH_ATTR_FLAG_FDB;
+ else
+ nexthop->ce_mask &= ~NH_ATTR_FLAG_FDB;
+
+ return 0;
+}
+
+int rtnl_nh_get_oif(struct rtnl_nh *nexthop)
+{
+ if (nexthop->ce_mask & NH_ATTR_OIF)
+ return nexthop->nh_oif;
+
+ return 0;
+}
+
+int rtnl_nh_get_fdb(struct rtnl_nh *nexthop)
+{
+ return nexthop->ce_mask & NH_ATTR_FLAG_FDB;
+}
+
+int rtnl_nh_get_group_entry(struct rtnl_nh *nexthop, int n)
+{
+ if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group)
+ return -NLE_MISSING_ATTR;
+
+ if (n < 0 || ((unsigned)n) >= nexthop->nh_group->size)
+ return -NLE_INVAL;
+
+ return nexthop->nh_group->entries[n].nh_id;
+}
+
+int rtnl_nh_get_group_size(struct rtnl_nh *nexthop)
+{
+ if (!(nexthop->ce_mask & NH_ATTR_GROUP) || !nexthop->nh_group)
+ return -NLE_MISSING_ATTR;
+
+ _nl_assert(nexthop->nh_group->size <= INT_MAX);
+
+ return (int)nexthop->nh_group->size;
+}
+
+static int rtnl_nh_grp_info(unsigned size, const struct nexthop_grp *vi,
+ nl_nh_group_t **nvi)
+{
+ nl_nh_group_t *ret;
+ unsigned i;
+
+ if (!(ret = rtnl_nh_grp_alloc(size)))
+ return -NLE_NOMEM;
+
+ for (i = 0; i < size; i++) {
+ ret->entries[i].nh_id = vi[i].id;
+ ret->entries[i].weight = vi[i].weight;
+ }
+
+ *nvi = ret;
+ return NLE_SUCCESS;
+}
+
+int rtnl_nh_get_id(struct rtnl_nh *nh)
+{
+ if (nh->ce_mask & NH_ATTR_ID)
+ return nh->nh_id;
+
+ return -NLE_INVAL;
+}
+
+static int nexthop_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+ _nl_auto_rtnl_nh struct rtnl_nh *nexthop = NULL;
+ struct nhmsg *ifi;
+ struct nlattr *tb[NHA_MAX + 1];
+ int err;
+ int family;
+
+ nexthop = rtnl_nh_alloc();
+ if (nexthop == NULL)
+ return -NLE_NOMEM;
+
+ nexthop->ce_msgtype = n->nlmsg_type;
+
+ if (!nlmsg_valid_hdr(n, sizeof(*ifi)))
+ return -NLE_MSG_TOOSHORT;
+
+ ifi = nlmsg_data(n);
+ family = ifi->nh_family;
+ nexthop->nh_family = family;
+ nexthop->nh_flags = ifi->nh_flags;
+ nexthop->ce_mask = (NH_ATTR_FLAGS);
+
+ err = nlmsg_parse(n, sizeof(*ifi), tb, NHA_MAX, rtnl_nh_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[NHA_ID]) {
+ nexthop->nh_id = nla_get_u32(tb[NHA_ID]);
+ nexthop->ce_mask |= NH_ATTR_ID;
+ }
+
+ if (tb[NHA_OIF]) {
+ nexthop->nh_oif = nla_get_u32(tb[NHA_OIF]);
+ nexthop->ce_mask |= NH_ATTR_OIF;
+ }
+
+ if (tb[NHA_GATEWAY]) {
+ nexthop->nh_gateway =
+ nl_addr_alloc_attr(tb[NHA_GATEWAY], family);
+ nexthop->ce_mask |= NH_ATTR_GATEWAY;
+ }
+
+ if (tb[NHA_BLACKHOLE]) {
+ nexthop->ce_mask |= NH_ATTR_FLAG_BLACKHOLE;
+ }
+
+ if (tb[NHA_GROUPS]) {
+ nexthop->ce_mask |= NH_ATTR_FLAG_GROUPS;
+ }
+
+ if (tb[NHA_FDB]) {
+ nexthop->ce_mask |= NH_ATTR_FLAG_FDB;
+ }
+
+ if (tb[NHA_GROUP]) {
+ nl_nh_group_t *nh_group = NULL;
+ const void *data;
+ unsigned size;
+ unsigned len;
+
+ data = nla_data(tb[NHA_GROUP]);
+ len = nla_len(tb[NHA_GROUP]);
+ size = len / sizeof(struct nexthop_grp);
+
+ err = rtnl_nh_grp_info(size, (const struct nexthop_grp *)data,
+ &nh_group);
+ if (err < 0) {
+ return err;
+ }
+
+ nexthop->nh_group = nh_group;
+ nexthop->ce_mask |= NH_ATTR_GROUP;
+ }
+
+ return pp->pp_cb((struct nl_object *)nexthop, pp);
+}
+
+static int nexthop_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+ _nl_auto_nl_msg struct nl_msg *msg = NULL;
+ int family = cache->c_iarg1;
+ struct nhmsg hdr = { .nh_family = family };
+ int err;
+
+ msg = nlmsg_alloc_simple(RTM_GETNEXTHOP, NLM_F_DUMP);
+ if (!msg)
+ return -NLE_NOMEM;
+
+ if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0)
+ return -NLE_MSGSIZE;
+
+ err = nl_send_auto(sk, msg);
+ if (err < 0)
+ return err;
+
+ return NLE_SUCCESS;
+}
+
+static void dump_nh_group(nl_nh_group_t *group, struct nl_dump_params *dp)
+{
+ unsigned i;
+
+ nl_dump(dp, " nh_grp:");
+ for (i = 0; i < group->size; i++) {
+ nl_dump(dp, " %u", group->entries[i].nh_id);
+ }
+}
+
+static void nh_dump_line(struct nl_object *obj, struct nl_dump_params *dp)
+{
+ struct nl_cache *cache;
+ char buf[128];
+ struct rtnl_nh *nh = nl_object_priv(obj);
+
+ cache = nl_cache_mngt_require_safe("route/nh");
+
+ if (nh->ce_mask & NH_ATTR_ID)
+ nl_dump(dp, "nhid %u", nh->nh_id);
+
+ if (nh->ce_mask & NH_ATTR_OIF)
+ nl_dump(dp, " oif %d", nh->nh_oif);
+
+ if (nh->ce_mask & NH_ATTR_GATEWAY)
+ nl_dump(dp, " via %s",
+ nl_addr2str(nh->nh_gateway, buf, sizeof(buf)));
+
+ if (nh->ce_mask & NH_ATTR_FLAG_BLACKHOLE)
+ nl_dump(dp, " blackhole");
+
+ if (nh->ce_mask & NH_ATTR_FLAG_GROUPS)
+ nl_dump(dp, " groups");
+
+ if (nh->ce_mask & NH_ATTR_GROUP)
+ dump_nh_group(nh->nh_group, dp);
+
+ if (nh->ce_mask & NH_ATTR_FLAG_FDB)
+ nl_dump(dp, " fdb");
+
+ nl_dump(dp, "\n");
+
+ if (cache)
+ nl_cache_put(cache);
+}
+
+static void nh_dump_details(struct nl_object *nh, struct nl_dump_params *dp)
+{
+ nh_dump_line(nh, dp);
+}
+
+static uint64_t nh_compare(struct nl_object *a, struct nl_object *b,
+ uint64_t attrs, int loose)
+{
+ int diff = 0;
+ struct rtnl_nh *src = nl_object_priv(a);
+ struct rtnl_nh *dst = nl_object_priv(b);
+
+#define NH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NH_ATTR_##ATTR, a, b, EXPR)
+
+ diff |= NH_DIFF(ID, src->nh_id != dst->nh_id);
+ diff |= NH_DIFF(GATEWAY, nl_addr_cmp(src->nh_gateway, dst->nh_gateway));
+ diff |= NH_DIFF(OIF, src->nh_oif != dst->nh_oif);
+ diff |= NH_DIFF(GROUP, rtnh_nh_grp_cmp(src->nh_group, dst->nh_group));
+ diff |= NH_DIFF(FLAG_FDB, false);
+ diff |= NH_DIFF(FLAG_GROUPS, false);
+ diff |= NH_DIFF(FLAG_BLACKHOLE, false);
+#undef NH_DIFF
+
+ return diff;
+}
+
+struct rtnl_nh *rtnl_nh_get(struct nl_cache *cache, int nhid)
+{
+ struct rtnl_nh *nh;
+
+ if (cache->c_ops != &rtnl_nh_ops)
+ return NULL;
+
+ nl_list_for_each_entry (nh, &cache->c_items, ce_list) {
+ if (nh->nh_id == nhid) {
+ nl_object_get((struct nl_object *)nh);
+ return nh;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Allocate nexthop cache and fill in all configured nexthops.
+ * @arg sk Netnexthop socket.
+ * @arg family nexthop address family or AF_UNSPEC
+ * @arg result Pointer to store resulting cache.
+ * @arg flags Flags to set in nexthop cache before filling
+ *
+ * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop
+ * message is sent to the kernel requesting a full dump of all configured
+ * nexthops. The returned messages are parsed and filled into the cache. If
+ * the operation succeeds, the resulting cache will contain a nexthop object for
+ * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the
+ * cache is still empty.
+ *
+ * If \c family is set to an address family other than \c AF_UNSPEC the
+ * contents of the cache can be limited to a specific address family.
+ * Currently the following address families are supported:
+ * - AF_BRIDGE
+ * - AF_INET6
+ *
+ * @route_doc{nexthop_list, Get List of nexthops}
+ * @see rtnl_nh_get()
+ * @see rtnl_nh_get_by_name()
+ * @return 0 on success or a negative error code.
+ */
+static int rtnl_nh_alloc_cache_flags(struct nl_sock *sk, int family,
+ struct nl_cache **result,
+ unsigned int flags)
+{
+ struct nl_cache *cache;
+ int err;
+
+ cache = nl_cache_alloc(&rtnl_nh_ops);
+ if (!cache)
+ return -NLE_NOMEM;
+
+ cache->c_iarg1 = family;
+
+ if (flags)
+ nl_cache_set_flags(cache, flags);
+
+ if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
+ nl_cache_free(cache);
+ return err;
+ }
+
+ *result = cache;
+ return 0;
+}
+
+/**
+ * Allocate nexthop cache and fill in all configured nexthops.
+ * @arg sk Netnexthop socket.
+ * @arg family nexthop address family or AF_UNSPEC
+ * @arg result Pointer to store resulting cache.
+ *
+ * Allocates and initializes a new nexthop cache. If \c sk is valid, a netnexthop
+ * message is sent to the kernel requesting a full dump of all configured
+ * nexthops. The returned messages are parsed and filled into the cache. If
+ * the operation succeeds, the resulting cache will contain a nexthop object for
+ * each nexthop configured in the kernel. If \c sk is NULL, returns 0 but the
+ * cache is still empty.
+ *
+ * If \c family is set to an address family other than \c AF_UNSPEC the
+ * contents of the cache can be limited to a specific address family.
+ * Currently the following address families are supported:
+ * - AF_BRIDGE
+ * - AF_INET6
+ *
+ * @route_doc{nexthop_list, Get List of nexthops}
+ * @see rtnl_nh_get()
+ * @see rtnl_nh_get_by_name()
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_nh_alloc_cache(struct nl_sock *sk, int family,
+ struct nl_cache **result)
+{
+ return rtnl_nh_alloc_cache_flags(sk, family, result, 0);
+}
+
+static struct nl_object_ops nh_obj_ops = {
+ .oo_name = "route/nh",
+ .oo_size = sizeof(struct rtnl_nh),
+ .oo_free_data = nh_free,
+ .oo_clone = nh_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = nh_dump_line,
+ [NL_DUMP_DETAILS] = nh_dump_details,
+ },
+ .oo_compare = nh_compare,
+ .oo_keygen = nexthop_keygen,
+ .oo_attrs2str = rtnl_route_nh_flags2str,
+ .oo_id_attrs = NH_ATTR_ID,
+};
+
+static struct nl_af_group nh_groups[] = {
+ { AF_UNSPEC, RTNLGRP_NEXTHOP },
+ { END_OF_GROUP_LIST },
+};
+
+static struct nl_cache_ops rtnl_nh_ops = {
+ .co_name = "route/nh",
+ .co_hdrsize = sizeof(struct nhmsg),
+ .co_msgtypes = {
+ { RTM_NEWNEXTHOP, NL_ACT_NEW, "new" },
+ { RTM_DELNEXTHOP, NL_ACT_DEL, "del" },
+ { RTM_GETNEXTHOP, NL_ACT_GET, "get" },
+ END_OF_MSGTYPES_LIST,
+ },
+ .co_protocol = NETLINK_ROUTE,
+ .co_groups = nh_groups,
+ .co_request_update = nexthop_request_update,
+ .co_msg_parser = nexthop_msg_parser,
+ .co_obj_ops = &nh_obj_ops,
+};
+
+static void _nl_init nexthop_init(void)
+{
+ nl_cache_mngt_register(&rtnl_nh_ops);
+}
+
+static void _nl_exit nexthop_exit(void)
+{
+ nl_cache_mngt_unregister(&rtnl_nh_ops);
+}
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index 7d99e7f..275b616 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -1288,4 +1288,18 @@ global:
rtnl_link_inet6_get_conf;
rtnl_neigh_get_nhid;
rtnl_neigh_set_nhid;
+ rtnl_nh_alloc;
+ rtnl_nh_alloc_cache;
+ rtnl_nh_clone;
+ rtnl_nh_compare;
+ rtnl_nh_dump;
+ rtnl_nh_get;
+ rtnl_nh_get_fdb;
+ rtnl_nh_get_gateway;
+ rtnl_nh_get_group_entry;
+ rtnl_nh_get_group_size;
+ rtnl_nh_get_id;
+ rtnl_nh_put;
+ rtnl_nh_set_fdb;
+ rtnl_nh_set_gateway;
} libnl_3_7;