link: API for address family specific link data

Introduces a new API to handle address familiy specific link data such as
IFLA_PROTINFO. It provides entry hooks for parsing IFLA_PROTINFO attributes
and allows to include the parsed data when a link object is dumped.
This commit is contained in:
Thomas Graf 2010-11-11 15:50:49 +01:00
parent fd857eeb9f
commit 3fa6a6b410
6 changed files with 358 additions and 92 deletions

View file

@ -150,31 +150,31 @@ struct rtnl_link
{
NLHDR_COMMON
char l_name[IFNAMSIZ];
uint32_t l_family;
uint32_t l_arptype;
uint32_t l_index;
uint32_t l_flags;
uint32_t l_change;
uint32_t l_mtu;
uint32_t l_link;
uint32_t l_txqlen;
uint32_t l_weight;
uint32_t l_master;
struct nl_addr *l_addr;
struct nl_addr *l_bcast;
char l_qdisc[IFQDISCSIZ];
struct rtnl_link_map l_map;
uint64_t l_stats[RTNL_LINK_STATS_MAX+1];
uint32_t l_flag_mask;
uint32_t l_num_vf;
uint8_t l_operstate;
uint8_t l_linkmode;
char l_name[IFNAMSIZ];
uint32_t l_family;
uint32_t l_arptype;
uint32_t l_index;
uint32_t l_flags;
uint32_t l_change;
uint32_t l_mtu;
uint32_t l_link;
uint32_t l_txqlen;
uint32_t l_weight;
uint32_t l_master;
struct nl_addr * l_addr;
struct nl_addr * l_bcast;
char l_qdisc[IFQDISCSIZ];
struct rtnl_link_map l_map;
uint64_t l_stats[RTNL_LINK_STATS_MAX+1];
uint32_t l_flag_mask;
uint32_t l_num_vf;
uint8_t l_operstate;
uint8_t l_linkmode;
/* 2 byte hole */
struct rtnl_link_info_ops *l_info_ops;
void * l_info;
char * l_ifalias;
struct rtnl_link_info_ops * l_info_ops;
void * l_af_data[AF_MAX];
void * l_info;
char * l_ifalias;
};
struct rtnl_ncacheinfo

View file

@ -0,0 +1,115 @@
/*
* netlink/route/link/api.h Link Modules API
*
* 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) 2003-2010 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_LINK_API_H_
#define NETLINK_LINK_API_H_
#include <netlink/netlink.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @ingroup link_info
*
* Link info operations
*/
struct rtnl_link_info_ops
{
/** Name of operations, must match name on kernel side */
char * io_name;
/** Reference count (internal, do not use) */
int io_refcnt;
/** Called to assign an info type to a link.
* Has to allocate enough resources to hold attributes. Can
* use link->l_info to store a pointer. */
int (*io_alloc)(struct rtnl_link *);
/** Called to parse the link info attribute.
* Must parse the attribute and assign all values to the link.
*/
int (*io_parse)(struct rtnl_link *,
struct nlattr *,
struct nlattr *);
/** Called when the link object is dumped.
* Must dump the info type specific attributes. */
void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
struct nl_dump_params *);
/** Called when a link object is cloned.
* Must clone all info type specific attributes. */
int (*io_clone)(struct rtnl_link *, struct rtnl_link *);
/** Called when construction a link netlink message.
* Must append all info type specific attributes to the message. */
int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *);
/** Called to release all resources previously allocated
* in either io_alloc() or io_parse(). */
void (*io_free)(struct rtnl_link *);
struct rtnl_link_info_ops * io_next;
};
extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *);
extern int rtnl_link_register_info(struct rtnl_link_info_ops *);
extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *);
struct rtnl_link_af_ops
{
/** The address family this operations set implements */
const unsigned int ao_family;
/** Number of users of this operations, DO NOT MODIFY. */
int ao_refcnt;
/** Validation policy for IFLA_PROTINFO attribute. This pointer
* can be set to a nla_policy structure describing the minimal
* requirements the attribute must meet. Failure of meeting these
* requirements will result in a parsing error. */
const struct nla_policy *ao_protinfo_policy;
/** Called after address family has been assigned to link. Must
* allocate data buffer to hold address family specific data and
* store it in link->l_af_data. */
void * (*ao_alloc)(struct rtnl_link *);
/** Called when the link is cloned, must allocate a clone of the
* address family specific buffer and return it. */
void * (*ao_clone)(struct rtnl_link *, void *);
/** Called when the link gets freed. Must free all allocated data */
void (*ao_free)(struct rtnl_link *, void *);
/** Called if a IFLA_PROTINFO attribute needs to be parsed. Typically
* stores the parsed data in the address family specific buffer. */
int (*ao_parse_protinfo)(struct rtnl_link *,
struct nlattr *, void *);
/** Dump address family specific link attributes */
void (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
struct nl_dump_params *,
void *);
};
extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int);
extern void rtnl_link_af_ops_put(struct rtnl_link_af_ops *);
extern int rtnl_link_af_register(struct rtnl_link_af_ops *);
extern int rtnl_link_af_unregister(struct rtnl_link_af_ops *);
#endif

View file

@ -6,66 +6,15 @@
* 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>
*/
#ifndef NETLINK_LINK_INFO_API_H_
#define NETLINK_LINK_INFO_API_H_
#include <netlink/netlink.h>
#warning "<netlink/route/link/info-api.h> is obsolete and may be removed in the future."
#warning "include <netlink/route/link/api.h> instead.
#ifdef __cplusplus
extern "C" {
#endif
/**
* @ingroup link_info
*
* Link info operations
*/
struct rtnl_link_info_ops
{
/** Name of operations, must match name on kernel side */
char * io_name;
/** Reference count (internal, do not use) */
int io_refcnt;
/** Called to assign an info type to a link.
* Has to allocate enough resources to hold attributes. Can
* use link->l_info to store a pointer. */
int (*io_alloc)(struct rtnl_link *);
/** Called to parse the link info attribute.
* Must parse the attribute and assign all values to the link.
*/
int (*io_parse)(struct rtnl_link *,
struct nlattr *,
struct nlattr *);
/** Called when the link object is dumped.
* Must dump the info type specific attributes. */
void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
struct nl_dump_params *);
/** Called when a link object is cloned.
* Must clone all info type specific attributes. */
int (*io_clone)(struct rtnl_link *, struct rtnl_link *);
/** Called when construction a link netlink message.
* Must append all info type specific attributes to the message. */
int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *);
/** Called to release all resources previously allocated
* in either io_alloc() or io_parse(). */
void (*io_free)(struct rtnl_link *);
struct rtnl_link_info_ops * io_next;
};
extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *);
extern int rtnl_link_register_info(struct rtnl_link_info_ops *);
extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *);
#include <netlink/route/link/api.h>
#endif

View file

@ -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

View file

@ -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;
}
/** @} */

View file

@ -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>