[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