Link info interface and vlan support
Adds an external interface to implement link info types and implements the type vlan.
This commit is contained in:
parent
1952414737
commit
a7469ce758
9 changed files with 958 additions and 11 deletions
|
@ -76,6 +76,9 @@ enum
|
|||
#define IFLA_WEIGHT IFLA_WEIGHT
|
||||
IFLA_OPERSTATE,
|
||||
IFLA_LINKMODE,
|
||||
IFLA_LINKINFO,
|
||||
#define IFLA_LINKINFO IFLA_LINKINFO
|
||||
IFLA_NET_NS_PID,
|
||||
__IFLA_MAX
|
||||
};
|
||||
|
||||
|
@ -140,4 +143,49 @@ struct ifla_cacheinfo
|
|||
__u32 retrans_time;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
IFLA_INFO_UNSPEC,
|
||||
IFLA_INFO_KIND,
|
||||
IFLA_INFO_DATA,
|
||||
IFLA_INFO_XSTATS,
|
||||
__IFLA_INFO_MAX,
|
||||
};
|
||||
|
||||
#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1)
|
||||
|
||||
/* VLAN section */
|
||||
|
||||
enum
|
||||
{
|
||||
IFLA_VLAN_UNSPEC,
|
||||
IFLA_VLAN_ID,
|
||||
IFLA_VLAN_FLAGS,
|
||||
IFLA_VLAN_EGRESS_QOS,
|
||||
IFLA_VLAN_INGRESS_QOS,
|
||||
__IFLA_VLAN_MAX,
|
||||
};
|
||||
|
||||
#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
|
||||
|
||||
struct ifla_vlan_flags {
|
||||
__u32 flags;
|
||||
__u32 mask;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
IFLA_VLAN_QOS_UNSPEC,
|
||||
IFLA_VLAN_QOS_MAPPING,
|
||||
__IFLA_VLAN_QOS_MAX
|
||||
};
|
||||
|
||||
#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1)
|
||||
|
||||
struct ifla_vlan_qos_mapping
|
||||
{
|
||||
__u32 from;
|
||||
__u32 to;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_IF_LINK_H */
|
||||
|
|
|
@ -175,6 +175,9 @@ struct rtnl_link
|
|||
uint32_t l_flag_mask;
|
||||
uint8_t l_operstate;
|
||||
uint8_t l_linkmode;
|
||||
/* 2 byte hole */
|
||||
struct rtnl_link_info_ops *l_info_ops;
|
||||
void * l_info;
|
||||
};
|
||||
|
||||
struct rtnl_ncacheinfo
|
||||
|
|
|
@ -158,6 +158,10 @@ extern uint8_t rtnl_link_get_linkmode(struct rtnl_link *);
|
|||
|
||||
extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int);
|
||||
|
||||
extern int rtnl_link_set_info_type(struct rtnl_link *,
|
||||
const char *);
|
||||
extern char * rtnl_link_get_info_type(struct rtnl_link *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
71
include/netlink/route/link/info-api.h
Normal file
71
include/netlink/route/link/info-api.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* netlink/route/link/info-api.h Link Info 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-2008 Thomas Graf <tgraf@suug.ch>
|
||||
*/
|
||||
|
||||
#ifndef NETLINK_LINK_INFO_API_H_
|
||||
#define NETLINK_LINK_INFO_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. */
|
||||
int (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
|
||||
struct nl_dump_params *, int);
|
||||
|
||||
/** 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 *);
|
||||
|
||||
#endif
|
55
include/netlink/route/link/vlan.h
Normal file
55
include/netlink/route/link/vlan.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* netlink/route/link/vlan.h VLAN interface
|
||||
*
|
||||
* 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-2008 Thomas Graf <tgraf@suug.ch>
|
||||
*/
|
||||
|
||||
#ifndef NETLINK_LINK_VLAN_H_
|
||||
#define NETLINK_LINK_VLAN_H_
|
||||
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/route/link.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct vlan_map
|
||||
{
|
||||
uint32_t vm_from;
|
||||
uint32_t vm_to;
|
||||
};
|
||||
|
||||
#define VLAN_PRIO_MAX 7
|
||||
|
||||
extern char * rtnl_link_vlan_flags2str(int, char *, size_t);
|
||||
extern int rtnl_link_vlan_str2flags(const char *);
|
||||
|
||||
extern int rtnl_link_vlan_set_id(struct rtnl_link *, int);
|
||||
extern int rtnl_link_vlan_get_id(struct rtnl_link *);
|
||||
|
||||
extern int rtnl_link_vlan_set_flags(struct rtnl_link *,
|
||||
unsigned int);
|
||||
extern int rtnl_link_vlan_unset_flags(struct rtnl_link *,
|
||||
unsigned int);
|
||||
extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *);
|
||||
|
||||
extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *,
|
||||
int, uint32_t);
|
||||
extern uint32_t * rtnl_link_vlan_get_ingress_map(struct rtnl_link *);
|
||||
|
||||
extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *,
|
||||
uint32_t, int);
|
||||
extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *,
|
||||
int *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -21,6 +21,8 @@ CIN += $(wildcard route/*.c)
|
|||
CIN += $(wildcard route/sch/*.c)
|
||||
# Classifiers
|
||||
CIN += $(wildcard route/cls/*.c)
|
||||
# Link Info Modules
|
||||
CIN += $(wildcard route/link/*.c)
|
||||
# NETLINK_GENERIC
|
||||
CIN += $(wildcard genl/*.c)
|
||||
# fib lookup
|
||||
|
|
180
lib/route/link.c
180
lib/route/link.c
|
@ -123,6 +123,27 @@
|
|||
* // Don't forget to give back the link object ;->
|
||||
* rtnl_link_put(old);
|
||||
* @endcode
|
||||
*
|
||||
* @par 3) Link Type Specific Attributes
|
||||
* @code
|
||||
* // Some link types offer additional parameters and statistics specific
|
||||
* // to their type. F.e. a VLAN link can be configured like this:
|
||||
* //
|
||||
* // Allocate a new link and set the info type to "vlan". This is required
|
||||
* // to prepare the link to hold vlan specific attributes.
|
||||
* struct rtnl_link *request = rtnl_link_alloc();
|
||||
* rtnl_link_set_info_type(request, "vlan");
|
||||
*
|
||||
* // Now vlan specific attributes can be set:
|
||||
* rtnl_link_vlan_set_id(request, 10);
|
||||
* rtnl_link_vlan_set_ingress_map(request, 2, 8);
|
||||
*
|
||||
* // Of course the attributes can also be read, check the info type
|
||||
* // to make sure you are using the right access functions:
|
||||
* char *type = rtnl_link_get_info_type(link);
|
||||
* if (!strcmp(type, "vlan"))
|
||||
* int id = rtnl_link_vlan_get_id(link);
|
||||
* @endcode
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
@ -133,6 +154,7 @@
|
|||
#include <netlink/object.h>
|
||||
#include <netlink/route/rtnl.h>
|
||||
#include <netlink/route/link.h>
|
||||
#include <netlink/route/link/info-api.h>
|
||||
|
||||
/** @cond SKIP */
|
||||
#define LINK_ATTR_MTU 0x0001
|
||||
|
@ -153,16 +175,33 @@
|
|||
#define LINK_ATTR_CHANGE 0x8000
|
||||
#define LINK_ATTR_OPERSTATE 0x10000
|
||||
#define LINK_ATTR_LINKMODE 0x20000
|
||||
#define LINK_ATTR_LINKINFO 0x40000
|
||||
|
||||
static struct nl_cache_ops rtnl_link_ops;
|
||||
static struct nl_object_ops link_obj_ops;
|
||||
/** @endcond */
|
||||
|
||||
static void release_link_info(struct rtnl_link *link)
|
||||
{
|
||||
struct rtnl_link_info_ops *io = link->l_info_ops;
|
||||
|
||||
if (io != NULL) {
|
||||
io->io_refcnt--;
|
||||
io->io_free(link);
|
||||
link->l_info_ops = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void link_free_data(struct nl_object *c)
|
||||
{
|
||||
struct rtnl_link *link = nl_object_priv(c);
|
||||
|
||||
if (link) {
|
||||
struct rtnl_link_info_ops *io;
|
||||
|
||||
if ((io = link->l_info_ops) != NULL)
|
||||
release_link_info(link);
|
||||
|
||||
nl_addr_put(link->l_addr);
|
||||
nl_addr_put(link->l_bcast);
|
||||
}
|
||||
|
@ -172,6 +211,7 @@ static int 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)))
|
||||
|
@ -181,6 +221,12 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src)
|
|||
if (!(dst->l_bcast = nl_addr_clone(src->l_bcast)))
|
||||
goto errout;
|
||||
|
||||
if (src->l_info_ops && src->l_info_ops->io_clone) {
|
||||
err = src->l_info_ops->io_clone(dst, src);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
return nl_get_errno();
|
||||
|
@ -196,12 +242,19 @@ static struct nla_policy link_policy[IFLA_MAX+1] = {
|
|||
[IFLA_MASTER] = { .type = NLA_U32 },
|
||||
[IFLA_OPERSTATE]= { .type = NLA_U8 },
|
||||
[IFLA_LINKMODE] = { .type = NLA_U8 },
|
||||
[IFLA_LINKINFO] = { .type = NLA_NESTED },
|
||||
[IFLA_QDISC] = { .type = NLA_STRING,
|
||||
.maxlen = IFQDISCSIZ },
|
||||
[IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) },
|
||||
[IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) },
|
||||
};
|
||||
|
||||
static struct nla_policy link_info_policy[IFLA_INFO_MAX+1] = {
|
||||
[IFLA_INFO_KIND] = { .type = NLA_STRING },
|
||||
[IFLA_INFO_DATA] = { .type = NLA_NESTED },
|
||||
[IFLA_INFO_XSTATS] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
||||
struct nlmsghdr *n, struct nl_parser_param *pp)
|
||||
{
|
||||
|
@ -337,6 +390,34 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
|||
link->ce_mask |= LINK_ATTR_LINKMODE;
|
||||
}
|
||||
|
||||
if (tb[IFLA_LINKINFO]) {
|
||||
struct nlattr *li[IFLA_INFO_MAX+1];
|
||||
|
||||
err = nla_parse_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO],
|
||||
link_info_policy);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
||||
if (li[IFLA_INFO_KIND] &&
|
||||
(li[IFLA_INFO_DATA] || li[IFLA_INFO_XSTATS])) {
|
||||
struct rtnl_link_info_ops *ops;
|
||||
char *kind;
|
||||
|
||||
kind = nla_get_string(li[IFLA_INFO_KIND]);
|
||||
ops = rtnl_link_info_ops_lookup(kind);
|
||||
if (ops != NULL) {
|
||||
ops->io_refcnt++;
|
||||
link->l_info_ops = ops;
|
||||
err = ops->io_parse(link, li[IFLA_INFO_DATA],
|
||||
li[IFLA_INFO_XSTATS]);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
} else {
|
||||
/* XXX: Warn about unparsed info? */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = pp->pp_cb((struct nl_object *) link, pp);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
@ -360,16 +441,8 @@ static int link_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
|
|||
struct rtnl_link *link = (struct rtnl_link *) obj;
|
||||
int line = 1;
|
||||
|
||||
dp_dump(p, "%s ", link->l_name);
|
||||
|
||||
if (link->ce_mask & LINK_ATTR_LINK) {
|
||||
struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
|
||||
dp_dump(p, "@%s", ll ? ll->l_name : "NONE");
|
||||
if (ll)
|
||||
rtnl_link_put(ll);
|
||||
}
|
||||
|
||||
dp_dump(p, "%s ", nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
|
||||
dp_dump(p, "%s %s ", link->l_name,
|
||||
nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
|
||||
|
||||
if (link->l_addr && !nl_addr_iszero(link->l_addr))
|
||||
dp_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf)));
|
||||
|
@ -383,7 +456,17 @@ static int link_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
|
|||
|
||||
rtnl_link_flags2str(link->l_flags, buf, sizeof(buf));
|
||||
if (buf[0])
|
||||
dp_dump(p, "<%s>", buf);
|
||||
dp_dump(p, "<%s> ", buf);
|
||||
|
||||
if (link->ce_mask & LINK_ATTR_LINK) {
|
||||
struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
|
||||
dp_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_BRIEF])
|
||||
line = link->l_info_ops->io_dump[NL_DUMP_BRIEF](link, p, line);
|
||||
|
||||
dp_dump(p, "\n");
|
||||
|
||||
|
@ -430,6 +513,9 @@ static int link_dump_full(struct nl_object *obj, struct nl_dump_params *p)
|
|||
dp_dump(p, "mode %s\n",
|
||||
rtnl_link_mode2str(link->l_linkmode, buf, sizeof(buf)));
|
||||
|
||||
if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_FULL])
|
||||
line = link->l_info_ops->io_dump[NL_DUMP_FULL](link, p, line);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
@ -495,6 +581,9 @@ static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
|
|||
link->l_stats[RTNL_LINK_TX_WIN_ERR],
|
||||
link->l_stats[RTNL_LINK_TX_COLLISIONS]);
|
||||
|
||||
if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_STATS])
|
||||
line = link->l_info_ops->io_dump[NL_DUMP_STATS](link, p, line);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
@ -555,6 +644,12 @@ static int link_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
|
|||
dp_dump_line(p, line++, " </stats>\n");
|
||||
}
|
||||
|
||||
if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_XML]) {
|
||||
dp_dump_line(p, line++, " <info>\n");
|
||||
line = link->l_info_ops->io_dump[NL_DUMP_XML](link, p, line);
|
||||
dp_dump_line(p, line++, " </info>\n");
|
||||
}
|
||||
|
||||
dp_dump_line(p, line++, "</link>\n");
|
||||
|
||||
#if 0
|
||||
|
@ -630,6 +725,9 @@ static int link_dump_env(struct nl_object *obj, struct nl_dump_params *p)
|
|||
}
|
||||
}
|
||||
|
||||
if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_ENV])
|
||||
line = link->l_info_ops->io_dump[NL_DUMP_ENV](link, p, line);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
@ -915,6 +1013,21 @@ struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old,
|
|||
if (tmpl->ce_mask & LINK_ATTR_LINKMODE)
|
||||
NLA_PUT_U8(msg, IFLA_LINKMODE, tmpl->l_linkmode);
|
||||
|
||||
if ((tmpl->ce_mask & LINK_ATTR_LINKINFO) && tmpl->l_info_ops &&
|
||||
tmpl->l_info_ops->io_put_attrs) {
|
||||
struct nlattr *info;
|
||||
|
||||
if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
|
||||
goto nla_put_failure;
|
||||
|
||||
NLA_PUT_STRING(msg, IFLA_INFO_KIND, tmpl->l_info_ops->io_name);
|
||||
|
||||
if (tmpl->l_info_ops->io_put_attrs(msg, tmpl) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, info);
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
nla_put_failure:
|
||||
|
@ -1379,6 +1492,51 @@ uint64_t rtnl_link_get_stat(struct rtnl_link *link, int id)
|
|||
return link->l_stats[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the info type of a link
|
||||
* @arg link link object
|
||||
* @arg type info type
|
||||
*
|
||||
* Looks up the info type and prepares the link to store info type
|
||||
* specific attributes. If an info type has been assigned already
|
||||
* it will be released with all changes lost.
|
||||
*
|
||||
* @return 0 on success or a negative errror code.
|
||||
*/
|
||||
int rtnl_link_set_info_type(struct rtnl_link *link, const char *type)
|
||||
{
|
||||
struct rtnl_link_info_ops *io;
|
||||
int err;
|
||||
|
||||
if ((io = rtnl_link_info_ops_lookup(type)) == NULL)
|
||||
return nl_error(ENOENT, "No such link info type exists");
|
||||
|
||||
if (link->l_info_ops)
|
||||
release_link_info(link);
|
||||
|
||||
if ((err = io->io_alloc(link)) < 0)
|
||||
return err;
|
||||
|
||||
link->l_info_ops = io;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return info type of a link
|
||||
* @arg link link object
|
||||
*
|
||||
* @note The returned pointer is only valid as long as the link exists
|
||||
* @return Info type name or NULL if unknown.
|
||||
*/
|
||||
char *rtnl_link_get_info_type(struct rtnl_link *link)
|
||||
{
|
||||
if (link->l_info_ops)
|
||||
return link->l_info_ops->io_name;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
static struct nl_object_ops link_obj_ops = {
|
||||
|
|
98
lib/route/link/api.c
Normal file
98
lib/route/link/api.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* lib/route/link/api.c Link Info 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-2008 Thomas Graf <tgraf@suug.ch>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup link
|
||||
* @defgroup link_info Link Info API
|
||||
* @brief
|
||||
*
|
||||
* @par 1) Registering/Unregistering a new link info type
|
||||
* @code
|
||||
* static struct rtnl_link_info_ops vlan_info_ops = {
|
||||
* .io_name = "vlan",
|
||||
* .io_alloc = vlan_alloc,
|
||||
* .io_parse = vlan_parse,
|
||||
* .io_dump[NL_DUMP_BRIEF] = vlan_dump_brief,
|
||||
* .io_dump[NL_DUMP_FULL] = vlan_dump_full,
|
||||
* .io_free = vlan_free,
|
||||
* };
|
||||
*
|
||||
* static void __init vlan_init(void)
|
||||
* {
|
||||
* rtnl_link_register_info(&vlan_info_ops);
|
||||
* }
|
||||
*
|
||||
* static void __exit vlan_exit(void)
|
||||
* {
|
||||
* rtnl_link_unregister_info(&vlan_info_ops);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <netlink-local.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/utils.h>
|
||||
#include <netlink/route/link.h>
|
||||
#include <netlink/route/link/info-api.h>
|
||||
|
||||
static struct rtnl_link_info_ops *info_ops;
|
||||
|
||||
struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name)
|
||||
{
|
||||
struct rtnl_link_info_ops *ops;
|
||||
|
||||
for (ops = info_ops; ops; ops = ops->io_next)
|
||||
if (!strcmp(ops->io_name, name))
|
||||
return ops;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int rtnl_link_register_info(struct rtnl_link_info_ops *ops)
|
||||
{
|
||||
if (ops->io_name == NULL)
|
||||
return nl_error(EINVAL, "No name specified");
|
||||
|
||||
if (rtnl_link_info_ops_lookup(ops->io_name))
|
||||
return nl_error(EEXIST, "Link info operations already exist");
|
||||
|
||||
NL_DBG(1, "Registered link info operations %s\n", ops->io_name);
|
||||
|
||||
ops->io_next = info_ops;
|
||||
info_ops = ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops)
|
||||
{
|
||||
struct rtnl_link_info_ops *t, **tp;
|
||||
|
||||
for (tp = &info_ops; (t=*tp) != NULL; tp = &t->io_next)
|
||||
if (t == ops)
|
||||
break;
|
||||
|
||||
if (!t)
|
||||
return nl_error(ENOENT, "No such link info operations");
|
||||
|
||||
if (t->io_refcnt > 0)
|
||||
return nl_error(EBUSY, "Info operations in use");
|
||||
|
||||
NL_DBG(1, "Unregistered link info perations %s\n", ops->io_name);
|
||||
|
||||
*tp = t->io_next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
508
lib/route/link/vlan.c
Normal file
508
lib/route/link/vlan.c
Normal file
|
@ -0,0 +1,508 @@
|
|||
/*
|
||||
* lib/route/link/vlan.c VLAN Link Info
|
||||
*
|
||||
* 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-2007 Thomas Graf <tgraf@suug.ch>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup link_info
|
||||
* @defgroup vlan VLAN
|
||||
* @brief
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <netlink-local.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/attr.h>
|
||||
#include <netlink/utils.h>
|
||||
#include <netlink/object.h>
|
||||
#include <netlink/route/rtnl.h>
|
||||
#include <netlink/route/link/info-api.h>
|
||||
#include <netlink/route/link/vlan.h>
|
||||
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
/** @cond SKIP */
|
||||
#define VLAN_HAS_ID (1<<0)
|
||||
#define VLAN_HAS_FLAGS (1<<1)
|
||||
#define VLAN_HAS_INGRESS_QOS (1<<2)
|
||||
#define VLAN_HAS_EGRESS_QOS (1<<3)
|
||||
|
||||
struct vlan_info
|
||||
{
|
||||
uint16_t vi_vlan_id;
|
||||
uint32_t vi_flags;
|
||||
uint32_t vi_flags_mask;
|
||||
uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1];
|
||||
uint32_t vi_negress;
|
||||
uint32_t vi_egress_size;
|
||||
struct vlan_map * vi_egress_qos;
|
||||
uint32_t vi_mask;
|
||||
};
|
||||
/** @endcond */
|
||||
|
||||
static struct trans_tbl vlan_flags[] = {
|
||||
__ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
|
||||
};
|
||||
|
||||
char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
|
||||
{
|
||||
return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
|
||||
}
|
||||
|
||||
int rtnl_link_vlan_str2flags(const char *name)
|
||||
{
|
||||
return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
|
||||
}
|
||||
|
||||
static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = {
|
||||
[IFLA_VLAN_ID] = { .type = NLA_U16 },
|
||||
[IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) },
|
||||
[IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
|
||||
[IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static int vlan_alloc(struct rtnl_link *link)
|
||||
{
|
||||
struct vlan_info *vi;
|
||||
|
||||
if ((vi = calloc(1, sizeof(*vi))) == NULL)
|
||||
return nl_errno(ENOMEM);
|
||||
|
||||
link->l_info = vi;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
|
||||
struct nlattr *xstats)
|
||||
{
|
||||
struct nlattr *tb[IFLA_VLAN_MAX+1];
|
||||
struct vlan_info *vi;
|
||||
int err;
|
||||
|
||||
NL_DBG(3, "Parsing VLAN link info");
|
||||
|
||||
if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0)
|
||||
goto errout;
|
||||
|
||||
if ((err = vlan_alloc(link)) < 0)
|
||||
goto errout;
|
||||
|
||||
vi = link->l_info;
|
||||
|
||||
if (tb[IFLA_VLAN_ID]) {
|
||||
vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]);
|
||||
vi->vi_mask |= VLAN_HAS_ID;
|
||||
}
|
||||
|
||||
if (tb[IFLA_VLAN_FLAGS]) {
|
||||
struct ifla_vlan_flags flags;
|
||||
nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
|
||||
|
||||
vi->vi_flags = flags.flags;
|
||||
vi->vi_mask |= VLAN_HAS_FLAGS;
|
||||
}
|
||||
|
||||
if (tb[IFLA_VLAN_INGRESS_QOS]) {
|
||||
struct ifla_vlan_qos_mapping *map;
|
||||
struct nlattr *nla;
|
||||
int remaining;
|
||||
|
||||
memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos));
|
||||
|
||||
nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) {
|
||||
if (nla_len(nla) < sizeof(*map))
|
||||
return nl_error(EINVAL, "Malformed mapping");
|
||||
|
||||
map = nla_data(nla);
|
||||
if (map->from < 0 || map->from > VLAN_PRIO_MAX) {
|
||||
return nl_error(EINVAL, "VLAN prio %d out of "
|
||||
"range", map->from);
|
||||
}
|
||||
|
||||
vi->vi_ingress_qos[map->from] = map->to;
|
||||
}
|
||||
|
||||
vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
|
||||
}
|
||||
|
||||
if (tb[IFLA_VLAN_EGRESS_QOS]) {
|
||||
struct ifla_vlan_qos_mapping *map;
|
||||
struct nlattr *nla;
|
||||
int remaining, i = 0;
|
||||
|
||||
nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
|
||||
if (nla_len(nla) < sizeof(*map))
|
||||
return nl_error(EINVAL, "Malformed mapping");
|
||||
i++;
|
||||
}
|
||||
|
||||
/* align to have a little reserve */
|
||||
vi->vi_egress_size = (i + 32) & ~31;
|
||||
vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map));
|
||||
if (vi->vi_egress_qos == NULL)
|
||||
return nl_errno(ENOMEM);
|
||||
|
||||
i = 0;
|
||||
nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
|
||||
map = nla_data(nla);
|
||||
NL_DBG(4, "Assigning egress qos mapping %d\n", i);
|
||||
vi->vi_egress_qos[i].vm_from = map->from;
|
||||
vi->vi_egress_qos[i++].vm_to = map->to;
|
||||
}
|
||||
|
||||
vi->vi_negress = i;
|
||||
vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
errout:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void vlan_free(struct rtnl_link *link)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (vi) {
|
||||
free(vi->vi_egress_qos);
|
||||
vi->vi_egress_qos = NULL;
|
||||
}
|
||||
|
||||
free(vi);
|
||||
link->l_info = NULL;
|
||||
}
|
||||
|
||||
static int vlan_dump_brief(struct rtnl_link *link, struct nl_dump_params *p,
|
||||
int line)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
dp_dump(p, "vlan-id %d", vi->vi_vlan_id);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
static int vlan_dump_full(struct rtnl_link *link, struct nl_dump_params *p,
|
||||
int line)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
int i, printed;
|
||||
char buf[64];
|
||||
|
||||
rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
|
||||
dp_dump_line(p, line++, " vlan-info id %d <%s>\n",
|
||||
vi->vi_vlan_id, buf);
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
|
||||
dp_dump_line(p, line++,
|
||||
" ingress vlan prio -> qos/socket prio mapping:\n");
|
||||
for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
|
||||
if (vi->vi_ingress_qos[i]) {
|
||||
if (printed == 0) {
|
||||
dp_new_line(p, line);
|
||||
dp_dump(p, " ");
|
||||
}
|
||||
dp_dump(p, "%x -> %#08x, ",
|
||||
i, vi->vi_ingress_qos[i]);
|
||||
if (printed++ == 3) {
|
||||
dp_dump(p, "\n");
|
||||
printed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (printed > 0 && printed != 4)
|
||||
dp_dump(p, "\n");
|
||||
}
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
|
||||
dp_dump_line(p, line++,
|
||||
" egress qos/socket prio -> vlan prio mapping:\n");
|
||||
for (i = 0, printed = 0; i < vi->vi_negress; i++) {
|
||||
if (printed == 0) {
|
||||
dp_new_line(p, line);
|
||||
dp_dump(p, " ");
|
||||
}
|
||||
dp_dump(p, "%#08x -> %x, ",
|
||||
vi->vi_egress_qos[i].vm_from,
|
||||
vi->vi_egress_qos[i].vm_to);
|
||||
if (printed++ == 3) {
|
||||
dp_dump(p, "\n");
|
||||
printed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (printed > 0 && printed != 4)
|
||||
dp_dump(p, "\n");
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
|
||||
{
|
||||
struct vlan_info *vdst, *vsrc = src->l_info;
|
||||
int err;
|
||||
|
||||
dst->l_info = NULL;
|
||||
if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0)
|
||||
return err;
|
||||
vdst = dst->l_info;
|
||||
|
||||
vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
|
||||
sizeof(struct vlan_map));
|
||||
if (!vdst->vi_egress_qos)
|
||||
return nl_errno(ENOMEM);
|
||||
|
||||
memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
|
||||
vsrc->vi_egress_size * sizeof(struct vlan_map));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
struct nlattr *data;
|
||||
|
||||
if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
|
||||
return nl_errno(ENOBUFS);
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_ID)
|
||||
NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_FLAGS) {
|
||||
struct ifla_vlan_flags flags = {
|
||||
.flags = vi->vi_flags,
|
||||
.mask = vi->vi_flags_mask,
|
||||
};
|
||||
|
||||
NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
|
||||
}
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
|
||||
struct ifla_vlan_qos_mapping map;
|
||||
struct nlattr *qos;
|
||||
int i;
|
||||
|
||||
if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
|
||||
goto nla_put_failure;
|
||||
|
||||
for (i = 0; i <= VLAN_PRIO_MAX; i++) {
|
||||
if (vi->vi_ingress_qos[i]) {
|
||||
map.from = i;
|
||||
map.to = vi->vi_ingress_qos[i];
|
||||
|
||||
NLA_PUT(msg, i, sizeof(map), &map);
|
||||
}
|
||||
}
|
||||
|
||||
nla_nest_end(msg, qos);
|
||||
}
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
|
||||
struct ifla_vlan_qos_mapping map;
|
||||
struct nlattr *qos;
|
||||
int i;
|
||||
|
||||
if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
|
||||
goto nla_put_failure;
|
||||
|
||||
for (i = 0; i < vi->vi_negress; i++) {
|
||||
map.from = vi->vi_egress_qos[i].vm_from;
|
||||
map.to = vi->vi_egress_qos[i].vm_to;
|
||||
|
||||
NLA_PUT(msg, i, sizeof(map), &map);
|
||||
}
|
||||
|
||||
nla_nest_end(msg, qos);
|
||||
}
|
||||
|
||||
nla_nest_end(msg, data);
|
||||
|
||||
nla_put_failure:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rtnl_link_info_ops vlan_info_ops = {
|
||||
.io_name = "vlan",
|
||||
.io_alloc = vlan_alloc,
|
||||
.io_parse = vlan_parse,
|
||||
.io_dump[NL_DUMP_BRIEF] = vlan_dump_brief,
|
||||
.io_dump[NL_DUMP_FULL] = vlan_dump_full,
|
||||
.io_clone = vlan_clone,
|
||||
.io_free = vlan_free,
|
||||
};
|
||||
|
||||
int rtnl_link_vlan_set_id(struct rtnl_link *link, int id)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
|
||||
return nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
|
||||
vi->vi_vlan_id = id;
|
||||
vi->vi_mask |= VLAN_HAS_ID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtnl_link_vlan_get_id(struct rtnl_link *link)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
|
||||
return nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_ID)
|
||||
return vi->vi_vlan_id;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
|
||||
return nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
|
||||
vi->vi_flags_mask |= flags;
|
||||
vi->vi_flags |= flags;
|
||||
vi->vi_mask |= VLAN_HAS_FLAGS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
|
||||
return nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
|
||||
vi->vi_flags_mask |= flags;
|
||||
vi->vi_flags &= ~flags;
|
||||
vi->vi_mask |= VLAN_HAS_FLAGS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
|
||||
return nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
|
||||
return vi->vi_flags;
|
||||
}
|
||||
|
||||
int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
|
||||
uint32_t to)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
|
||||
return nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
|
||||
if (from < 0 || from > VLAN_PRIO_MAX)
|
||||
return nl_error(EINVAL, "Invalid vlan prio 0..%d",
|
||||
VLAN_PRIO_MAX);
|
||||
|
||||
vi->vi_ingress_qos[from] = to;
|
||||
vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) {
|
||||
nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
|
||||
return vi->vi_ingress_qos;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
|
||||
return nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
|
||||
if (to < 0 || to > VLAN_PRIO_MAX)
|
||||
return nl_error(EINVAL, "Invalid vlan prio 0..%d",
|
||||
VLAN_PRIO_MAX);
|
||||
|
||||
if (vi->vi_negress >= vi->vi_egress_size) {
|
||||
int new_size = vi->vi_egress_size + 32;
|
||||
void *ptr;
|
||||
|
||||
ptr = realloc(vi->vi_egress_qos, new_size);
|
||||
if (!ptr)
|
||||
return nl_errno(ENOMEM);
|
||||
|
||||
vi->vi_egress_qos = ptr;
|
||||
vi->vi_egress_size = new_size;
|
||||
}
|
||||
|
||||
vi->vi_egress_qos[vi->vi_negress].vm_from = from;
|
||||
vi->vi_egress_qos[vi->vi_negress].vm_to = to;
|
||||
vi->vi_negress++;
|
||||
vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
|
||||
int *negress)
|
||||
{
|
||||
struct vlan_info *vi = link->l_info;
|
||||
|
||||
if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) {
|
||||
nl_error(EOPNOTSUPP, "Not a VLAN link");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (negress == NULL) {
|
||||
nl_error(EINVAL, "Require pointer to store negress");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
|
||||
*negress = vi->vi_negress;
|
||||
return vi->vi_egress_qos;
|
||||
} else {
|
||||
*negress = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init vlan_init(void)
|
||||
{
|
||||
rtnl_link_register_info(&vlan_info_ops);
|
||||
}
|
||||
|
||||
static void __exit vlan_exit(void)
|
||||
{
|
||||
rtnl_link_unregister_info(&vlan_info_ops);
|
||||
}
|
||||
|
||||
/** @} */
|
Loading…
Add table
Reference in a new issue