[RFC PATCH 4/5] Add support for a new bridge link cache route/bridge_link

roopa at cumulusnetworks.com roopa at cumulusnetworks.com
Mon Nov 5 08:37:37 EST 2012


From: roopa <roopa at cumulusnetworks.com>

This patch adds support for a new rtnl cache ('route/bridge_link')
for bridge port link cache management. The kernel bridge code uses
RTM_NEWLINK and RTM_DELLINK with family AF_BRIDGE to notify
bridge port addition/deletion.

This patch adds new rtnl bridge link cache ops rtnl_bridge_link_ops
and bridge link object ops bridge_link_obj_ops respectively. This cache
is similar to the link cache but tailored to the AF_BRIDGE link
operations. At any point this cache will only contain link
objects associated with a bridge.

Note: Since the bridge link cache code is almost similar to link cache,
eventually bridge link cache can be made to use the link cache object
ops ie rtnl_link_object_ops to avoid some code duplication. Its not already
done that way in the current patch because it seems to conflict with
NL_AUTO_PROVIDE. With NL_AUTO_PROVIDE, the caller of
nl_cache_mngt_require('route/bridge_link)' could get 'route/link' in return,
since they share the same object type. (This needs further thought)

Signed-off-by: Roopa Prabhu <roopa at cumulusnetworks.com>
Reviewed-by: Wilson Kok <wkok at cumulusnetworks.com>
Reviewed-by: Nolan Leake <nolan at cumulusnetworks.com>
Reviewed-by: Shrijeet Mukherjee <shm at cumulusnetworks.com>
---
 include/Makefile.am                 |    1 +
 include/netlink/route/bridge_link.h |   56 ++
 lib/Makefile.am                     |    2 +-
 lib/route/bridge_link.c             |  263 ++++++++++
 lib/route/bridge_link_obj.c         |  960 +++++++++++++++++++++++++++++++++++
 5 files changed, 1281 insertions(+), 1 deletions(-)
 create mode 100644 include/netlink/route/bridge_link.h
 create mode 100644 lib/route/bridge_link.c
 create mode 100644 lib/route/bridge_link_obj.c

diff --git a/include/Makefile.am b/include/Makefile.am
index 80c615c..cf0e004 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -60,6 +60,7 @@ nobase_libnlinclude_HEADERS = \
 	netlink/route/class.h \
 	netlink/route/classifier.h \
 	netlink/route/link.h \
+	netlink/route/bridge_link.h \
 	netlink/route/neighbour.h \
 	netlink/route/neightbl.h \
 	netlink/route/nexthop.h \
diff --git a/include/netlink/route/bridge_link.h b/include/netlink/route/bridge_link.h
new file mode 100644
index 0000000..761e6f3
--- /dev/null
+++ b/include/netlink/route/bridge_link.h
@@ -0,0 +1,56 @@
+/*
+ * netlink/route/bridge_link.h		Bridge Port Links (Interfaces)
+ *
+ *	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) 2012 Cumulus Networks, Inc
+ */
+
+#ifndef NETLINK_BRIDGE_LINK_H_
+#define NETLINK_BRIDGE_LINK_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+#include <linux/if.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct nl_object_ops bridge_link_obj_ops;
+
+extern void	rtnl_bridge_link_put(struct rtnl_link *);
+
+extern struct rtnl_link *rtnl_bridge_link_alloc(void);
+
+extern int rtnl_bridge_link_parse(struct nlmsghdr *n, struct rtnl_link **result);
+
+extern struct rtnl_link *rtnl_bridge_link_get(struct nl_cache *, int);
+extern struct rtnl_link *rtnl_bridge_link_get_by_name(struct nl_cache *, const char *);
+
+extern int	rtnl_bridge_link_get_kernel(struct nl_sock *, int, const char *,
+					    struct rtnl_link **);
+
+/* Name <-> Index Translations */
+extern char * 	rtnl_bridge_link_i2name(struct nl_cache *, int, char *, size_t);
+extern int	rtnl_bridge_link_name2i(struct nl_cache *, const char *);
+
+
+extern void	rtnl_bridge_link_set_ifindex(struct rtnl_link *, int);
+extern int	rtnl_bridge_link_get_ifindex(struct rtnl_link *);
+
+extern void	rtnl_bridge_link_set_family(struct rtnl_link *, int);
+extern int	rtnl_bridge_link_get_family(struct rtnl_link *);
+
+extern void	rtnl_bridge_link_set_master(struct rtnl_link *, int);
+extern int	rtnl_bridge_link_get_master(struct rtnl_link *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e31e594..35ace10 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -78,7 +78,7 @@ libnl_route_3_la_SOURCES = \
 	\
 	fib_lookup/lookup.c fib_lookup/request.c \
 	\
-	route/pktloc.c
+	route/pktloc.c route/bridge_link_obj.c route/bridge_link.c
 
 nodist_libnl_route_3_la_SOURCES = \
 	route/pktloc_syntax.c route/pktloc_syntax.h \
diff --git a/lib/route/bridge_link.c b/lib/route/bridge_link.c
new file mode 100644
index 0000000..f657b40
--- /dev/null
+++ b/lib/route/bridge_link.c
@@ -0,0 +1,263 @@
+/*
+ * lib/route/bridge_link.c	Bridge port Links (Bridge Port Interfaces)
+ *
+ *	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) 2012 Cumulus Networks, Inc
+ */
+
+/**
+ * @ingroup rtnl
+ * @defgroup bridge_link Bridge port Links (Bridge Port Link)
+ *
+ * @details
+ * @route_doc{route_bridge_link, Bridge Port Link Documentation}
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/hashtable.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/bridge_link.h>
+#include <netlink/route/link/api.h>
+#include <netlink/route/link/bridge.h>
+
+static struct nl_cache_ops rtnl_bridge_link_ops;
+/** @endcond */
+
+static int bridge_link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+				  struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+	struct rtnl_link *link;
+	int err;
+
+	if ((err = rtnl_bridge_link_parse(n, &link)) < 0)
+		return err;
+
+	err = pp->pp_cb((struct nl_object *) link, pp);
+
+	rtnl_bridge_link_put(link);
+
+	return err;
+}
+
+/**
+ * Lookup bridge port link in cache by interface index
+ * @arg cache		bridge Link cache
+ * @arg ifindex		Interface index
+ *
+ * Searches through the provided cache looking for a link with matching
+ * interface index.
+ *
+ * @attention The reference counter of the returned link object will be
+ *            incremented. Use rtnl_bridge_link_put() to release the reference.
+ *
+ * @return Link object or NULL if no match was found.
+ */
+struct rtnl_link *rtnl_bridge_link_get(struct nl_cache *cache, int ifindex)
+{
+	struct rtnl_link *link, *needle;
+
+	if (cache->c_ops != &rtnl_bridge_link_ops)
+		return NULL;
+
+	needle = rtnl_bridge_link_alloc();
+	if (!needle)
+		return NULL;
+
+	rtnl_bridge_link_set_ifindex(needle, ifindex);
+
+	link = (struct rtnl_link *)nl_cache_lookup(cache, OBJ_CAST(needle));
+
+	rtnl_bridge_link_put(needle);
+
+	return link;
+}
+
+/**
+ * Lookup bridge port link in cache by bridge port link name
+ * @arg cache		Bridge Link cache
+ * @arg name		Name of link
+ *
+ * Searches through the provided cache looking for a link with matching
+ * link name
+ *
+ * @attention The reference counter of the returned link object will be
+ *            incremented. Use rtnl_bridge_link_put() to release the reference.
+ *
+ * @route_doc{link_list, Get List of Links}
+ * @see rtnl_bridge_link_get()
+ * @return Link object or NULL if no match was found.
+ */
+struct rtnl_link *rtnl_bridge_link_get_by_name(struct nl_cache *cache,
+					       const char *name)
+{
+	struct rtnl_link *link;
+
+	if (cache->c_ops != &rtnl_bridge_link_ops)
+		return NULL;
+
+	nl_list_for_each_entry(link, &cache->c_items, ce_list) {
+		if (!strcmp(name, link->l_name)) {
+			nl_object_get((struct nl_object *) link);
+			return link;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * Get a bridge link object directly from kernel
+ * @arg sk		Netlink socket
+ * @arg ifindex		Interface index
+ * @arg name		Name of link
+ * @arg result		Pointer to store resulting link object
+ *
+ * This function builds a \c RTM_GETLINK netlink message to request
+ * a specific link directly from the kernel. The returned answer is
+ * parsed into a struct rtnl_link object and returned via the result
+ * pointer or -NLE_OBJ_NOTFOUND is returned if no matching link was
+ * found.
+ *
+ * @route_doc{link_direct_lookup, Lookup Single Link (Direct Lookup)}
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_bridge_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name,
+				struct rtnl_link **result)
+{
+	struct nl_msg *msg = NULL;
+	struct nl_object *obj;
+	int err;
+
+	if ((err = rtnl_bridge_link_build_get_request(ifindex, name, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	if ((err = nl_pickup(sk, bridge_link_msg_parser, &obj)) < 0)
+		return err;
+
+	/* We have used link_msg_parser(), object is definitely a link */
+	*result = (struct rtnl_link *) obj;
+
+	/* If an object has been returned, we also need to wait for the ACK */
+	 if (err == 0 && obj)
+		 nl_wait_for_ack(sk);
+
+	return 0;
+}
+
+
+/**
+ * Translate interface index to corresponding bridge port link name
+ * @arg cache		bridge port Link cache
+ * @arg ifindex		Interface index
+ * @arg dst		String to store name
+ * @arg len		Length of destination string
+ *
+ * Translates the specified interface index to the corresponding
+ * link name and stores the name in the destination string.
+ *
+ * @return Name of link or NULL if no match was found.
+ */
+char * rtnl_bridge_link_i2name(struct nl_cache *cache, int ifindex, char *dst,
+			       size_t len)
+{
+	struct rtnl_link *link = rtnl_bridge_link_get(cache, ifindex);
+
+	if (link) {
+		strncpy(dst, link->l_name, len - 1);
+		rtnl_bridge_link_put(link);
+		return dst;
+	}
+
+	return NULL;
+}
+
+/**
+ * Translate bridge link name to corresponding interface index
+ * @arg cache		Bridge Port Link cache
+ * @arg name		Name of link
+ *
+ * @return Interface index or 0 if no match was found.
+ */
+int rtnl_bridge_link_name2i(struct nl_cache *cache, const char *name)
+{
+	int ifindex = 0;
+	struct rtnl_link *link;
+
+	link = rtnl_bridge_link_get_by_name(cache, name);
+	if (link) {
+		ifindex = link->l_index;
+		rtnl_bridge_link_put(link);
+	}
+
+	return ifindex;
+}
+
+/** @} */
+
+static int bridge_link_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+	int family = AF_BRIDGE;
+
+	return nl_rtgen_request(sk, RTM_GETLINK, family, NLM_F_DUMP);
+}
+
+static int bridge_link_event_filter(struct nl_cache *cache, struct nl_object *obj)
+{
+	struct rtnl_link *link = (struct rtnl_link *) obj;
+
+	/*
+	 * Ignore All other messages except bridging
+	 */
+	if (link->l_family != AF_BRIDGE)
+		return NL_SKIP;
+
+	return NL_OK;
+}
+
+static struct nl_af_group bridge_link_groups[] = {
+	{ AF_BRIDGE,	RTNLGRP_LINK },
+	{ END_OF_GROUP_LIST },
+};
+
+static struct nl_cache_ops rtnl_bridge_link_ops = {
+	.co_name		= "route/bridge_link",
+	.co_hdrsize		= sizeof(struct ifinfomsg),
+	.co_msgtypes		= {
+					{ RTM_NEWLINK, NL_ACT_NEW, "new" },
+					{ RTM_DELLINK, NL_ACT_DEL, "del" },
+					{ RTM_GETLINK, NL_ACT_GET, "get" },
+					{ RTM_SETLINK, NL_ACT_CHANGE, "set" },
+					END_OF_MSGTYPES_LIST,
+				  },
+	.co_protocol		= NETLINK_ROUTE,
+	.co_groups		= bridge_link_groups,
+	.co_request_update	= bridge_link_request_update,
+	.co_msg_parser		= bridge_link_msg_parser,
+	.co_event_filter	= bridge_link_event_filter,
+	.co_obj_ops		= &bridge_link_obj_ops,
+};
+
+static void __init bridge_link_init(void)
+{
+	nl_cache_mngt_register(&rtnl_bridge_link_ops);
+}
+
+static void __exit bridge_link_exit(void)
+{
+	nl_cache_mngt_unregister(&rtnl_bridge_link_ops);
+}
diff --git a/lib/route/bridge_link_obj.c b/lib/route/bridge_link_obj.c
new file mode 100644
index 0000000..54303dc
--- /dev/null
+++ b/lib/route/bridge_link_obj.c
@@ -0,0 +1,960 @@
+/*
+ * lib/route/bridge_link_obj.c	Bridge Link object
+ *
+ *	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) 2012 Cumulus Networks, Inc
+ */
+
+/**
+ * @ingroup rtnl
+ * @defgroup bridge_link_obj Bridge Link object (Interfaces)
+ *
+ * @details
+ * @route_doc{route_bridge_link_obj, Beidge Link Object Documentation}
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/hashtable.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/bridge_link.h>
+#include <netlink/route/link/api.h>
+
+/** @cond SKIP */
+#define BRIDGE_LINK_ATTR_MTU     0x0001
+#define BRIDGE_LINK_ATTR_LINK    0x0002
+#define BRIDGE_LINK_ATTR_MASTER  0x0004
+#define BRIDGE_LINK_ATTR_ADDR    0x0008
+#define BRIDGE_LINK_ATTR_FLAGS   0x0010
+#define BRIDGE_LINK_ATTR_IFNAME  0x0020
+#define BRIDGE_LINK_ATTR_IFINDEX 0x0040
+#define BRIDGE_LINK_ATTR_FAMILY  0x0080
+#define BRIDGE_LINK_ATTR_OPERSTATE 0x0100
+#define BRIDGE_LINK_ATTR_AF_DATA 0x0200
+
+/** @endcond */
+
+static struct rtnl_link_af_ops *af_lookup_and_alloc(struct rtnl_link *link,
+						    int family)
+{
+	struct rtnl_link_af_ops *af_ops;
+	void *data;
+
+	af_ops = rtnl_link_af_ops_lookup(family);
+	if (!af_ops)
+		return NULL;
+
+	if (!(data = rtnl_link_af_alloc(link, af_ops)))
+		return NULL;
+
+	return af_ops;
+}
+
+static int af_free(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+		    void *data, void *arg)
+{
+	if (ops->ao_free)
+		ops->ao_free(link, data);
+
+	rtnl_link_af_ops_put(ops);
+
+	return 0;
+}
+
+static int af_clone(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+		    void *data, void *arg)
+{
+	struct rtnl_link *dst = arg;
+
+	if (ops->ao_clone &&
+	    !(dst->l_af_data[ops->ao_family] = ops->ao_clone(dst, data)))
+		return -NLE_NOMEM;
+
+	return 0;
+}
+
+static int af_fill(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+		   void *data, void *arg)
+{
+	struct nl_msg *msg = arg;
+	struct nlattr *af_attr;
+	int err;
+
+	if (!ops->ao_fill_af)
+		return 0;
+
+	if (!(af_attr = nla_nest_start(msg, ops->ao_family)))
+		return -NLE_MSGSIZE;
+
+	if ((err = ops->ao_fill_af(link, arg, data)) < 0)
+		return err;
+
+	nla_nest_end(msg, af_attr);
+
+	return 0;
+}
+
+static int af_dump_line(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+			 void *data, void *arg)
+{
+	struct nl_dump_params *p = arg;
+
+	if (ops->ao_dump[NL_DUMP_LINE])
+		ops->ao_dump[NL_DUMP_LINE](link, p, data);
+
+	return 0;
+}
+
+static int af_dump_details(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+			   void *data, void *arg)
+{
+	struct nl_dump_params *p = arg;
+
+	if (ops->ao_dump[NL_DUMP_DETAILS])
+		ops->ao_dump[NL_DUMP_DETAILS](link, p, data);
+
+	return 0;
+}
+
+static int do_foreach_af(struct rtnl_link *link,
+			 int (*cb)(struct rtnl_link *,
+			 struct rtnl_link_af_ops *, void *, void *),
+			 void *arg)
+{
+	int i, err;
+
+	for (i = 0; i < AF_MAX; i++) {
+		if (link->l_af_data[i]) {
+			struct rtnl_link_af_ops *ops;
+
+			if (!(ops = rtnl_link_af_ops_lookup(i)))
+				BUG();
+
+			if ((err = cb(link, ops, link->l_af_data[i], arg)) < 0)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
+static void bridge_link_free_data(struct nl_object *c)
+{
+	struct rtnl_link *link = nl_object_priv(c);
+
+	if (link) {
+		nl_addr_put(link->l_addr);
+
+		do_foreach_af(link, af_free, NULL);
+	}
+}
+
+static int bridge_link_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct rtnl_link *dst = nl_object_priv(_dst);
+	struct rtnl_link *src = nl_object_priv(_src);
+	int err;
+
+	if (src->l_addr)
+		if (!(dst->l_addr = nl_addr_clone(src->l_addr)))
+			return -NLE_NOMEM;
+
+	if ((err = do_foreach_af(src, af_clone, dst)) < 0)
+		return err;
+
+	return 0;
+}
+
+static struct nla_policy link_policy[IFLA_MAX+1] = {
+	[IFLA_IFNAME]	= { .type = NLA_STRING,
+			    .maxlen = IFNAMSIZ },
+	[IFLA_MTU]	= { .type = NLA_U32 },
+	[IFLA_LINK]	= { .type = NLA_U32 },
+	[IFLA_MASTER]	= { .type = NLA_U32 },
+	[IFLA_OPERSTATE]= { .type = NLA_U8 },
+};
+
+int rtnl_bridge_link_parse(struct nlmsghdr *n, struct rtnl_link **result)
+{
+	struct rtnl_link *link;
+	struct ifinfomsg *ifi;
+	struct nlattr *tb[IFLA_MAX+1];
+	struct rtnl_link_af_ops *af_ops = NULL;
+	int err, family;
+
+	link = rtnl_bridge_link_alloc();
+	if (link == NULL)
+		return -NLE_NOMEM;
+
+	link->ce_msgtype = n->nlmsg_type;
+
+	if (!nlmsg_valid_hdr(n, sizeof(*ifi)))
+		return -NLE_MSG_TOOSHORT;
+
+	ifi = nlmsg_data(n);
+	link->l_family = family = ifi->ifi_family;
+	link->l_index = ifi->ifi_index;
+	link->l_flags = ifi->ifi_flags;
+	link->ce_mask = (BRIDGE_LINK_ATTR_IFNAME | BRIDGE_LINK_ATTR_FAMILY |
+			 BRIDGE_LINK_ATTR_IFINDEX | BRIDGE_LINK_ATTR_FLAGS);
+
+	if ((af_ops = af_lookup_and_alloc(link, family))) {
+		if (af_ops->ao_protinfo_policy) {
+			memcpy(&link_policy[IFLA_PROTINFO],
+			       af_ops->ao_protinfo_policy,
+			       sizeof(struct nla_policy));
+		}
+	}
+
+	err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, link_policy);
+	if (err < 0)
+		goto errout;
+
+	if (tb[IFLA_IFNAME] == NULL) {
+		err = -NLE_MISSING_ATTR;
+		goto errout;
+	}
+
+	nla_strlcpy(link->l_name, tb[IFLA_IFNAME], IFNAMSIZ);
+
+	if (tb[IFLA_MTU]) {
+		link->l_mtu = nla_get_u32(tb[IFLA_MTU]);
+		link->ce_mask |= BRIDGE_LINK_ATTR_MTU;
+	}
+
+	if (tb[IFLA_ADDRESS]) {
+		link->l_addr = nl_addr_alloc_attr(tb[IFLA_ADDRESS], AF_UNSPEC);
+		if (link->l_addr == NULL) {
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		nl_addr_set_family(link->l_addr,
+				   nl_addr_guess_family(link->l_addr));
+		link->ce_mask |= BRIDGE_LINK_ATTR_ADDR;
+	}
+
+	if (tb[IFLA_LINK]) {
+		link->l_link = nla_get_u32(tb[IFLA_LINK]);
+		link->ce_mask |= BRIDGE_LINK_ATTR_LINK;
+	}
+
+	if (tb[IFLA_MASTER]) {
+		link->l_master = nla_get_u32(tb[IFLA_MASTER]);
+		link->ce_mask |= BRIDGE_LINK_ATTR_MASTER;
+	}
+
+	if (tb[IFLA_OPERSTATE]) {
+		link->l_operstate = nla_get_u8(tb[IFLA_OPERSTATE]);
+		link->ce_mask |= BRIDGE_LINK_ATTR_OPERSTATE;
+	}
+
+	if (tb[IFLA_PROTINFO] && af_ops && af_ops->ao_parse_protinfo) {
+		err = af_ops->ao_parse_protinfo(link, tb[IFLA_PROTINFO],
+						link->l_af_data[link->l_family]);
+		if (err < 0)
+			goto errout;
+		link->ce_mask |= BRIDGE_LINK_ATTR_AF_DATA;
+	}
+
+	*result = link;
+	rtnl_link_af_ops_put(af_ops);
+	return 0;
+
+errout:
+	rtnl_link_af_ops_put(af_ops);
+	rtnl_bridge_link_put(link);
+
+	return err;
+}
+
+static void bridge_link_dump_line(struct nl_object *obj, struct nl_dump_params *p)
+{
+	char buf[128];
+	struct nl_cache *cache = dp_cache(obj);
+	struct rtnl_link *link = (struct rtnl_link *) obj;
+
+	nl_dump_line(p, "%s %s ", link->l_name,
+		     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
+
+	if (link->l_addr && !nl_addr_iszero(link->l_addr))
+		nl_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf)));
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_MASTER) {
+		struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
+		nl_dump(p, "master %s ", master ? master->l_name : "inv");
+		if (master)
+			rtnl_link_put(master);
+	}
+
+	rtnl_link_flags2str(link->l_flags, buf, sizeof(buf));
+	if (buf[0])
+		nl_dump(p, "<%s> ", buf);
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_LINK) {
+		struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
+		nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE");
+		if (ll)
+			rtnl_link_put(ll);
+	}
+
+	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_LINE])
+		link->l_info_ops->io_dump[NL_DUMP_LINE](link, p);
+
+	do_foreach_af(link, af_dump_line, p);
+
+	nl_dump(p, "\n");
+}
+
+static void bridge_link_dump_details(struct nl_object *obj, struct nl_dump_params *p)
+{
+	struct rtnl_link *link = (struct rtnl_link *) obj;
+	char buf[64];
+
+	bridge_link_dump_line(obj, p);
+
+	nl_dump_line(p, "    mtu %u ", link->l_mtu);
+	nl_dump(p, "txqlen %u weight %u ", link->l_txqlen, link->l_weight);
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_IFINDEX)
+		nl_dump(p, "index %u ", link->l_index);
+
+
+	nl_dump(p, "\n");
+
+	if ((link->ce_mask & BRIDGE_LINK_ATTR_OPERSTATE) &&
+	    link->l_operstate != IF_OPER_UNKNOWN) {
+		rtnl_link_operstate2str(link->l_operstate, buf, sizeof(buf));
+		nl_dump(p, "state %s ", buf);
+	}
+
+	do_foreach_af(link, af_dump_details, p);
+}
+
+static void bridge_link_keygen(struct nl_object *obj, uint32_t *hashkey,
+        uint32_t table_sz)
+{
+	struct link_hash_key {
+		uint32_t			    l_index;
+		/* since mac-addr can change we will not use the addr in the key */
+	};
+
+	struct link_hash_key *_key;
+	unsigned int _key_sz;
+	struct rtnl_link *link = (struct rtnl_link *) obj;
+
+	_key_sz = sizeof(struct link_hash_key);
+
+	_key = calloc(1 , _key_sz);
+
+	if (!_key) {
+	    NL_DBG(2, "Warning: calloc failed for %d bytes...\n", _key_sz);
+	    *hashkey = 0;
+	}
+
+	_key->l_index = link->l_index;
+
+	NL_DBG(5, "link %p buffer %p, size %d\n",
+	       link, _key, _key_sz);
+
+	*hashkey = nl_hash(_key, _key_sz, 0) % table_sz;
+
+	free(_key);
+
+	return;
+}
+
+static int bridge_link_compare(struct nl_object *_a, struct nl_object *_b,
+			       uint32_t attrs, int flags)
+{
+	struct rtnl_link *a = (struct rtnl_link *) _a;
+	struct rtnl_link *b = (struct rtnl_link *) _b;
+	int diff = 0;
+
+#define LINK_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, BRIDGE_LINK_ATTR_##ATTR, a, b, EXPR)
+
+	diff |= LINK_DIFF(IFINDEX,	a->l_index != b->l_index);
+	diff |= LINK_DIFF(MTU,		a->l_mtu != b->l_mtu);
+	diff |= LINK_DIFF(LINK,		a->l_link != b->l_link);
+	diff |= LINK_DIFF(MASTER,	a->l_master != b->l_master);
+	diff |= LINK_DIFF(FAMILY,	a->l_family != b->l_family);
+	diff |= LINK_DIFF(OPERSTATE,	a->l_operstate != b->l_operstate);
+	diff |= LINK_DIFF(IFNAME,	strcmp(a->l_name, b->l_name));
+	diff |= LINK_DIFF(ADDR,		nl_addr_cmp(a->l_addr, b->l_addr));
+
+	if (attrs & BRIDGE_LINK_ATTR_AF_DATA)
+		diff |= (rtnl_bridge_compare_af_data(a, b));
+
+	if (flags & LOOSE_COMPARISON)
+		diff |= LINK_DIFF(FLAGS,
+				  (a->l_flags ^ b->l_flags) & b->l_flag_mask);
+	else
+		diff |= LINK_DIFF(FLAGS, a->l_flags != b->l_flags);
+
+#undef LINK_DIFF
+
+	return diff;
+}
+
+static const struct trans_tbl link_attrs[] = {
+	__ADD(BRIDGE_LINK_ATTR_MTU, mtu)
+	__ADD(BRIDGE_LINK_ATTR_LINK, link)
+	__ADD(BRIDGE_LINK_ATTR_MASTER, master)
+	__ADD(BRIDGE_LINK_ATTR_ADDR, address)
+	__ADD(BRIDGE_LINK_ATTR_FLAGS, flags)
+	__ADD(BRIDGE_LINK_ATTR_IFNAME, name)
+	__ADD(BRIDGE_LINK_ATTR_IFINDEX, ifindex)
+	__ADD(BRIDGE_LINK_ATTR_FAMILY, family)
+	__ADD(BRIDGE_LINK_ATTR_OPERSTATE, operstate)
+};
+
+static char *bridge_link_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, link_attrs,
+			   ARRAY_SIZE(link_attrs));
+}
+
+/**
+ * @name Get / List
+ * @{
+ */
+
+/**
+ * Construct RTM_GETLINK netlink message
+ * @arg ifindex		Interface index
+ * @arg name		Name of link
+ * @arg result		Pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_link_get_kernel()
+ * with the exception that it will not send the message but return it in
+ * the provided return pointer instead.
+ *
+ * @see rtnl_link_get_kernel()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_bridge_link_build_get_request(int ifindex, const char *name,
+				       struct nl_msg **result)
+{
+	struct ifinfomsg ifi;
+	struct nl_msg *msg;
+
+	if (ifindex <= 0 && !name) {
+		APPBUG("ifindex or name must be specified");
+		return -NLE_MISSING_ATTR;
+	}
+
+	memset(&ifi, 0, sizeof(ifi));
+
+	if (!(msg = nlmsg_alloc_simple(RTM_GETLINK, 0)))
+		return -NLE_NOMEM;
+
+	if (ifindex > 0)
+		ifi.ifi_index = ifindex;
+
+	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (name)
+		NLA_PUT_STRING(msg, IFLA_IFNAME, name);
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/** @} */
+
+static int build_bridge_link_msg(int cmd, struct ifinfomsg *hdr,
+				 struct rtnl_link *link, int flags, struct nl_msg **result)
+{
+	struct nl_msg *msg;
+	struct nlattr *af_spec;
+
+	msg = nlmsg_alloc_simple(cmd, flags);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, hdr, sizeof(*hdr), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_ADDR)
+		NLA_PUT_ADDR(msg, IFLA_ADDRESS, link->l_addr);
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_MTU)
+		NLA_PUT_U32(msg, IFLA_MTU, link->l_mtu);
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_IFNAME)
+		NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name);
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_OPERSTATE)
+		NLA_PUT_U8(msg, IFLA_OPERSTATE, link->l_operstate);
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_LINK)
+		NLA_PUT_U32(msg, IFLA_LINK, link->l_link);
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_MASTER)
+		NLA_PUT_U32(msg, IFLA_MASTER, link->l_master);
+
+	if (do_foreach_af(link, af_fill, msg) < 0)
+		goto nla_put_failure;
+
+	nla_nest_end(msg, af_spec);
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/**
+ * @name Add / Modify
+ * @{
+ */
+
+/**
+ * Build a netlink message requesting the addition of new virtual link
+ * @arg link		new link to add
+ * @arg flags		additional netlink message flags
+ * @arg result		pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_link_add() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_link_add()
+ *
+ * @note This operation is not supported on all kernel versions.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_bridge_link_build_add_request(struct rtnl_link *link, int flags,
+				       struct nl_msg **result)
+{
+	struct ifinfomsg ifi = {
+		.ifi_family = link->l_family,
+		.ifi_index = link->l_index,
+		.ifi_flags = link->l_flags,
+	};
+
+	return build_bridge_link_msg(RTM_NEWLINK, &ifi, link, flags, result);
+}
+
+/**
+ * Build a netlink message requesting the modification of link
+ * @arg orig		original link to change
+ * @arg changes		link containing the changes to be made
+ * @arg flags		additional netlink message flags
+ * @arg result		pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_link_change() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_link_change()
+ *
+ * @note The resulting message will have message type set to RTM_NEWLINK
+ *       which may not work with older kernels. You may have to modify it
+ *       to RTM_SETLINK (does not allow changing link info attributes) to
+ *       have the change request work with older kernels.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_bridge_link_build_change_request(struct rtnl_link *orig,
+					  struct rtnl_link *changes, int flags,
+					  struct nl_msg **result)
+{
+	struct ifinfomsg ifi = {
+		.ifi_family = orig->l_family,
+		.ifi_index = orig->l_index,
+	};
+	int err;
+
+	if (changes->ce_mask & BRIDGE_LINK_ATTR_FLAGS) {
+		ifi.ifi_flags = orig->l_flags & ~changes->l_flag_mask;
+		ifi.ifi_flags |= changes->l_flags;
+	}
+
+	if (changes->l_family && changes->l_family != orig->l_family) {
+		APPBUG("link change: family is immutable");
+		return -NLE_IMMUTABLE;
+	}
+
+	/* Avoid unnecessary name change requests */
+	if (orig->ce_mask & BRIDGE_LINK_ATTR_IFINDEX &&
+	    orig->ce_mask & BRIDGE_LINK_ATTR_IFNAME &&
+	    changes->ce_mask & BRIDGE_LINK_ATTR_IFNAME &&
+	    !strcmp(orig->l_name, changes->l_name))
+		changes->ce_mask &= ~BRIDGE_LINK_ATTR_IFNAME;
+
+	if ((err = build_bridge_link_msg(RTM_NEWLINK, &ifi, changes, flags, result)) < 0)
+		goto errout;
+
+	return 0;
+
+errout:
+	return err;
+}
+
+/** @} */
+
+/**
+ * @name Delete
+ * @{
+ */
+
+/**
+ * Build a netlink message requesting the deletion of a link
+ * @arg link		Link to delete
+ * @arg result		Pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_link_delete() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_link_delete()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_bridge_link_build_delete_request(const struct rtnl_link *link,
+					  struct nl_msg **result)
+{
+	struct nl_msg *msg;
+	struct ifinfomsg ifi = {
+		.ifi_index = link->l_index,
+	};
+
+	if (!(link->ce_mask & (BRIDGE_LINK_ATTR_IFINDEX | BRIDGE_LINK_ATTR_IFNAME))) {
+		APPBUG("ifindex or name must be specified");
+		return -NLE_MISSING_ATTR;
+	}
+
+	if (!(msg = nlmsg_alloc_simple(RTM_DELLINK, 0)))
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (link->ce_mask & BRIDGE_LINK_ATTR_IFNAME)
+		NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name);
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/** @} */
+
+/**
+ * @name Link Object
+ * @{
+ */
+
+/**
+ * Allocate link object
+ *
+ * @see rtnl_link_put()
+ * @return New link object or NULL if allocation failed
+ */
+struct rtnl_link *rtnl_bridge_link_alloc(void)
+{
+	return (struct rtnl_link *) nl_object_alloc(&bridge_link_obj_ops);
+}
+
+/**
+ * Return a link object reference
+ *
+ * @copydetails nl_object_put()
+ */
+void rtnl_bridge_link_put(struct rtnl_link *link)
+{
+	nl_object_put((struct nl_object *) link);
+}
+
+/**
+ * Set name of link object
+ * @arg link		Link object
+ * @arg name		New name
+ *
+ * @note To change the name of a link in the kernel, set the interface
+ *       index to the link you wish to change, modify the link name using
+ *       this function and pass the link object to rtnl_link_change() or
+ *       rtnl_link_add().
+ *
+ * @route_doc{link_attr_name, Link Name}
+ * @see rtnl_link_get_name()
+ * @see rtnl_link_set_ifindex()
+ */
+void rtnl_bridge_link_set_name(struct rtnl_link *link, const char *name)
+{
+	strncpy(link->l_name, name, sizeof(link->l_name) - 1);
+	link->ce_mask |= BRIDGE_LINK_ATTR_IFNAME;
+}
+
+/**
+ * Return name of link object
+ * @arg link		Link object
+ *
+ * @route_doc{link_attr_name, Link Name}
+ * @see rtnl_link_set_name()
+ * @return Link name or NULL if name is not specified
+ */
+char *rtnl_bridge_link_get_name(struct rtnl_link *link)
+{
+	return link->ce_mask & BRIDGE_LINK_ATTR_IFNAME ? link->l_name : NULL;
+}
+
+/**
+ * Set flags of link object
+ * @arg link		Link object
+ * @arg flags		Flags
+ *
+ * @see rtnl_link_get_flags()
+ * @see rtnl_link_unset_flags()
+ */
+void rtnl_bridge_link_set_flags(struct rtnl_link *link, unsigned int flags)
+{
+	link->l_flag_mask |= flags;
+	link->l_flags |= flags;
+	link->ce_mask |= BRIDGE_LINK_ATTR_FLAGS;
+}
+
+/**
+ * Unset flags of link object
+ * @arg link		Link object
+ * @arg flags		Flags
+ *
+ * @see rtnl_link_set_flags()
+ * @see rtnl_link_get_flags()
+ */
+void rtnl_bridge_link_unset_flags(struct rtnl_link *link, unsigned int flags)
+{
+	link->l_flag_mask |= flags;
+	link->l_flags &= ~flags;
+	link->ce_mask |= BRIDGE_LINK_ATTR_FLAGS;
+}
+
+/**
+ * Return flags of link object
+ * @arg link		Link object
+ *
+ * @route_doc{link_attr_flags, Link Flags}
+ * @see rtnl_link_set_flags()
+ * @see rtnl_link_unset_flags()
+ * @return Link flags or 0 if none have been set.
+ */
+unsigned int rtnl_bridge_link_get_flags(struct rtnl_link *link)
+{
+	return link->l_flags;
+}
+
+/**
+ * Set address family of link object
+ *
+ * @see rtnl_link_get_family()
+ */
+void rtnl_bridge_link_set_family(struct rtnl_link *link, int family)
+{
+	link->l_family = family;
+	link->ce_mask |= BRIDGE_LINK_ATTR_FAMILY;
+}
+
+/**
+ * Return address family of link object
+ * @arg link		Link object
+ *
+ * @see rtnl_link_set_family()
+ * @return Address family or \c AF_UNSPEC if not specified.
+ */
+int rtnl_bridge_link_get_family(struct rtnl_link *link)
+{
+	return link->ce_mask & BRIDGE_LINK_ATTR_FAMILY ? link->l_family : AF_UNSPEC;
+}
+
+/**
+ * Set interface index of link object
+ * @arg link		Link object
+ * @arg ifindex		Interface index
+ *
+ * @route_doc{link_attr_ifindex, Interface Index}
+ * @see rtnl_link_get_ifindex()
+ */
+void rtnl_bridge_link_set_ifindex(struct rtnl_link *link, int ifindex)
+{
+	link->l_index = ifindex;
+	link->ce_mask |= BRIDGE_LINK_ATTR_IFINDEX;
+}
+
+
+/**
+ * Return interface index of link object
+ * @arg link		Link object
+ *
+ * @route_doc{link_attr_ifindex, Interface Index}
+ * @see rtnl_link_set_ifindex()
+ * @return Interface index or 0 if not set.
+ */
+int rtnl_bridge_link_get_ifindex(struct rtnl_link *link)
+{
+	return link->l_index;
+}
+
+void rtnl_bridge_link_set_link(struct rtnl_link *link, int ifindex)
+{
+	link->l_link = ifindex;
+	link->ce_mask |= BRIDGE_LINK_ATTR_LINK;
+}
+
+int rtnl_bridge_link_get_link(struct rtnl_link *link)
+{
+	return link->l_link;
+}
+
+/**
+ * Set master link of link object
+ * @arg link		Link object
+ * @arg ifindex		Interface index of master link
+ *
+ * @see rtnl_link_get_master()
+ */
+void rtnl_bridge_link_set_master(struct rtnl_link *link, int ifindex)
+{
+	link->l_master = ifindex;
+	link->ce_mask |= BRIDGE_LINK_ATTR_MASTER;
+}
+
+/**
+ * Return master link of link object
+ * @arg link		Link object
+ *
+ * @see rtnl_link_set_master()
+ * @return Interface index of master link or 0 if not specified
+ */
+int rtnl_bridge_link_get_master(struct rtnl_link *link)
+{
+	return link->l_master;
+}
+
+/**
+ * Set operational status of link object
+ * @arg link		Link object
+ * @arg status		New opertional status
+ *
+ * @route_doc{link_attr_operstate, Operational Status}}
+ * @see rtnl_link_get_operstate()
+ */
+void rtnl_bridge_link_set_operstate(struct rtnl_link *link, uint8_t status)
+{
+	link->l_operstate = status;
+	link->ce_mask |= BRIDGE_LINK_ATTR_OPERSTATE;
+}
+
+/**
+ * Return operational status of link object
+ * @arg link		Link object
+ *
+ * @route_doc{link_attr_operstate, Operational Status}
+ * @see rtnl_link_set_operstate()
+ * @return Opertional state or \c IF_OPER_UNKNOWN
+ */
+uint8_t rtnl_bridge_link_get_operstate(struct rtnl_link *link)
+{
+	return link->l_operstate;
+}
+
+/** @} */
+
+/**
+ * @name Utilities
+ * @{
+ */
+
+static const struct trans_tbl link_flags[] = {
+	__ADD(IFF_LOOPBACK, loopback)
+	__ADD(IFF_BROADCAST, broadcast)
+	__ADD(IFF_POINTOPOINT, pointopoint)
+	__ADD(IFF_MULTICAST, multicast)
+	__ADD(IFF_NOARP, noarp)
+	__ADD(IFF_ALLMULTI, allmulti)
+	__ADD(IFF_PROMISC, promisc)
+	__ADD(IFF_MASTER, master)
+	__ADD(IFF_SLAVE, slave)
+	__ADD(IFF_DEBUG, debug)
+	__ADD(IFF_DYNAMIC, dynamic)
+	__ADD(IFF_AUTOMEDIA, automedia)
+	__ADD(IFF_PORTSEL, portsel)
+	__ADD(IFF_NOTRAILERS, notrailers)
+	__ADD(IFF_UP, up)
+	__ADD(IFF_RUNNING, running)
+	__ADD(IFF_LOWER_UP, lowerup)
+	__ADD(IFF_DORMANT, dormant)
+	__ADD(IFF_ECHO, echo)
+};
+
+char *rtnl_bridge_link_flags2str(int flags, char *buf, size_t len)
+{
+	return __flags2str(flags, buf, len, link_flags,
+			   ARRAY_SIZE(link_flags));
+}
+
+int rtnl_bridge_link_str2flags(const char *name)
+{
+	return __str2flags(name, link_flags, ARRAY_SIZE(link_flags));
+}
+
+static const struct trans_tbl link_operstates[] = {
+	__ADD(IF_OPER_UNKNOWN, unknown)
+	__ADD(IF_OPER_NOTPRESENT, notpresent)
+	__ADD(IF_OPER_DOWN, down)
+	__ADD(IF_OPER_LOWERLAYERDOWN, lowerlayerdown)
+	__ADD(IF_OPER_TESTING, testing)
+	__ADD(IF_OPER_DORMANT, dormant)
+	__ADD(IF_OPER_UP, up)
+};
+
+char *rtnl_bridge_link_operstate2str(uint8_t st, char *buf, size_t len)
+{
+	return __type2str(st, buf, len, link_operstates,
+			  ARRAY_SIZE(link_operstates));
+}
+
+int rtnl_bridge_link_str2operstate(const char *name)
+{
+	return __str2type(name, link_operstates,
+			  ARRAY_SIZE(link_operstates));
+}
+
+/** @} */
+
+struct nl_object_ops bridge_link_obj_ops = {
+	.oo_name		= "route/bridge_link",
+	.oo_size		= sizeof(struct rtnl_link),
+	.oo_free_data		= bridge_link_free_data,
+	.oo_clone		= bridge_link_clone,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= bridge_link_dump_line,
+	    [NL_DUMP_DETAILS]	= bridge_link_dump_details,
+	},
+	.oo_compare		= bridge_link_compare,
+	.oo_keygen		= bridge_link_keygen,
+	.oo_attrs2str		= bridge_link_attrs2str,
+	.oo_id_attrs		= BRIDGE_LINK_ATTR_IFINDEX,
+};
+
+/** @} */
-- 
1.7.2.5




More information about the libnl mailing list