diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/route/link.c | 130 | ||||
-rw-r--r-- | lib/route/link/api.c | 94 | ||||
-rw-r--r-- | lib/route/link/vlan.c | 4 |
3 files changed, 215 insertions, 13 deletions
diff --git a/lib/route/link.c b/lib/route/link.c index 6aa3c77..2082d6e 100644 --- a/lib/route/link.c +++ b/lib/route/link.c @@ -154,7 +154,7 @@ #include <netlink/object.h> #include <netlink/route/rtnl.h> #include <netlink/route/link.h> -#include <netlink/route/link/info-api.h> +#include <netlink/route/link/api.h> /** @cond SKIP */ #define LINK_ATTR_MTU 0x0001 @@ -183,6 +183,84 @@ static struct nl_cache_ops rtnl_link_ops; static struct nl_object_ops link_obj_ops; /** @endcond */ +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(link, data))) + return -NLE_NOMEM; + + 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 af_dump_stats(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_STATS]) + ops->ao_dump[NL_DUMP_STATS](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 release_link_info(struct rtnl_link *link) { struct rtnl_link_info_ops *io = link->l_info_ops; @@ -208,6 +286,8 @@ static void link_free_data(struct nl_object *c) nl_addr_put(link->l_bcast); free(link->l_ifalias); + + do_foreach_af(link, af_free, NULL); } } @@ -235,6 +315,9 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src) return err; } + if ((err = do_foreach_af(src, af_clone, dst)) < 0) + return err; + return 0; } @@ -270,6 +353,7 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct rtnl_link *link; struct ifinfomsg *ifi; struct nlattr *tb[IFLA_MAX+1]; + struct rtnl_link_af_ops *af_ops = NULL; int err; link = rtnl_link_alloc(); @@ -280,6 +364,27 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, link->ce_msgtype = n->nlmsg_type; + if (!nlmsg_valid_hdr(n, sizeof(*ifi))) + return -NLE_MSG_TOOSHORT; + + ifi = nlmsg_data(n); + link->l_family = ifi->ifi_family; + link->l_arptype = ifi->ifi_type; + link->l_index = ifi->ifi_index; + link->l_flags = ifi->ifi_flags; + link->l_change = ifi->ifi_change; + link->ce_mask = (LINK_ATTR_IFNAME | LINK_ATTR_FAMILY | + LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX | + LINK_ATTR_FLAGS | LINK_ATTR_CHANGE); + + if ((af_ops = rtnl_link_af_ops_lookup(ifi->ifi_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; @@ -291,15 +396,6 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, nla_strlcpy(link->l_name, tb[IFLA_IFNAME], IFNAMSIZ); - ifi = nlmsg_data(n); - link->l_family = ifi->ifi_family; - link->l_arptype = ifi->ifi_type; - link->l_index = ifi->ifi_index; - link->l_flags = ifi->ifi_flags; - link->l_change = ifi->ifi_change; - link->ce_mask = (LINK_ATTR_IFNAME | LINK_ATTR_FAMILY | - LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX | - LINK_ATTR_FLAGS | LINK_ATTR_CHANGE); if (tb[IFLA_STATS]) { struct rtnl_link_stats *st = nla_data(tb[IFLA_STATS]); @@ -478,8 +574,16 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } } + 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; + } + err = pp->pp_cb((struct nl_object *) link, pp); errout: + rtnl_link_af_ops_put(af_ops); rtnl_link_put(link); return err; } @@ -522,6 +626,8 @@ static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p) 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"); } @@ -570,6 +676,8 @@ static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p) if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS]) link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p); + + do_foreach_af(link, af_dump_details, p); } static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) @@ -633,6 +741,8 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_STATS]) link->l_info_ops->io_dump[NL_DUMP_STATS](link, p); + + do_foreach_af(link, af_dump_stats, p); } #if 0 diff --git a/lib/route/link/api.c b/lib/route/link/api.c index a0e7679..2a8dd1d 100644 --- a/lib/route/link/api.c +++ b/lib/route/link/api.c @@ -43,7 +43,7 @@ #include <netlink/netlink.h> #include <netlink/utils.h> #include <netlink/route/link.h> -#include <netlink/route/link/info-api.h> +#include <netlink/route/link/api.h> static struct rtnl_link_info_ops *info_ops; @@ -94,5 +94,97 @@ int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops) return 0; } +static struct rtnl_link_af_ops *af_ops[AF_MAX]; + +/** + * Return operations of a specific link address family + * @arg family Address family + * + * @note The returned pointer must be given back using rtnl_link_af_ops_put() + * + * @return Pointer to operations or NULL if unavailable. + */ +struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(const unsigned int family) +{ + if (family == AF_UNSPEC || family >= AF_MAX) + return NULL; + + if (af_ops[family]) + af_ops[family]->ao_refcnt++; + + return af_ops[family]; +} + +/** + * Give back reference to a set of operations. + * @arg ops Address family operations. + */ +void rtnl_link_af_ops_put(struct rtnl_link_af_ops *ops) +{ + ops->ao_refcnt--; +} + +/** + * Register operations for a link address family + * @arg ops Address family operations + * + * This function must be called by modules implementing a specific link + * address family. It will make the operations implemented by the module + * available for everyone else. + * + * @return 0 on success or a negative error code. + * @return -NLE_INVAL Address family is out of range (0..AF_MAX) + * @return -NLE_EXIST Operations for address family already registered. + */ +int rtnl_link_af_register(struct rtnl_link_af_ops *ops) +{ + if (ops->ao_family == AF_UNSPEC || ops->ao_family >= AF_MAX) + return -NLE_INVAL; + + if (af_ops[ops->ao_family]) + return -NLE_EXIST; + + ops->ao_refcnt = 0; + af_ops[ops->ao_family] = ops; + + NL_DBG(1, "Registered link address family operations %u\n", + ops->ao_family); + + return 0; +} + +/** + * Unregister operations for a link address family + * @arg ops Address family operations + * + * This function must be called if a module implementing a specific link + * address family is unloaded or becomes unavailable. It must provide a + * set of operations which have previously been registered using + * rtnl_link_af_register(). + * + * @return 0 on success or a negative error code + * @return -NLE_INVAL ops is NULL + * @return -NLE_OBJ_NOTFOUND Address family operations not registered. + * @return -NLE_BUSY Address family operations still in use. + */ +int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops) +{ + if (!ops) + return -NLE_INVAL; + + if (!af_ops[ops->ao_family]) + return -NLE_OBJ_NOTFOUND; + + if (ops->ao_refcnt > 0) + return -NLE_BUSY; + + af_ops[ops->ao_family] = NULL; + + NL_DBG(1, "Unregistered link address family operations %u\n", + ops->ao_family); + + return 0; +} + /** @} */ diff --git a/lib/route/link/vlan.c b/lib/route/link/vlan.c index c466afe..a76cb83 100644 --- a/lib/route/link/vlan.c +++ b/lib/route/link/vlan.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch> */ /** @@ -23,7 +23,7 @@ #include <netlink/utils.h> #include <netlink/object.h> #include <netlink/route/rtnl.h> -#include <netlink/route/link/info-api.h> +#include <netlink/route/link/api.h> #include <netlink/route/link/vlan.h> #include <linux/if_vlan.h> |