dect
/
libnl
Archived
13
0
Fork 0

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:
Thomas Graf 2008-01-08 15:00:46 +01:00
parent 1952414737
commit a7469ce758
9 changed files with 958 additions and 11 deletions

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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