summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Graf <tgr@deb.localdomain>2008-01-08 15:00:46 +0100
committerThomas Graf <tgr@deb.localdomain>2008-01-08 15:00:46 +0100
commita7469ce758fac3631df6ce72eb3f89150070e7f8 (patch)
tree4e429eaac1902755cdc36ed680cdb1126a0c2d61
parent195241473723527e2f30ac120087f106c607ce3e (diff)
downloadlibnl-a7469ce758fac3631df6ce72eb3f89150070e7f8.tar.gz
Link info interface and vlan support
Adds an external interface to implement link info types and implements the type vlan.
-rw-r--r--include/linux/if_link.h48
-rw-r--r--include/netlink-types.h3
-rw-r--r--include/netlink/route/link.h4
-rw-r--r--include/netlink/route/link/info-api.h71
-rw-r--r--include/netlink/route/link/vlan.h55
-rw-r--r--lib/Makefile2
-rw-r--r--lib/route/link.c180
-rw-r--r--lib/route/link/api.c98
-rw-r--r--lib/route/link/vlan.c508
9 files changed, 958 insertions, 11 deletions
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 604c243..84c3492 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -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 */
diff --git a/include/netlink-types.h b/include/netlink-types.h
index 5a23450..f273da3 100644
--- a/include/netlink-types.h
+++ b/include/netlink-types.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
diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h
index 78f2b0b..caaa792 100644
--- a/include/netlink/route/link.h
+++ b/include/netlink/route/link.h
@@ -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
diff --git a/include/netlink/route/link/info-api.h b/include/netlink/route/link/info-api.h
new file mode 100644
index 0000000..2ccce9d
--- /dev/null
+++ b/include/netlink/route/link/info-api.h
@@ -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
diff --git a/include/netlink/route/link/vlan.h b/include/netlink/route/link/vlan.h
new file mode 100644
index 0000000..a3ad76d
--- /dev/null
+++ b/include/netlink/route/link/vlan.h
@@ -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
diff --git a/lib/Makefile b/lib/Makefile
index a654242..0bf8af7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
diff --git a/lib/route/link.c b/lib/route/link.c
index 2fec334..ab89c24 100644
--- a/lib/route/link.c
+++ b/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 = {
diff --git a/lib/route/link/api.c b/lib/route/link/api.c
new file mode 100644
index 0000000..afe00b1
--- /dev/null
+++ b/lib/route/link/api.c
@@ -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;
+}
+
+/** @} */
+
diff --git a/lib/route/link/vlan.c b/lib/route/link/vlan.c
new file mode 100644
index 0000000..5ab9a20
--- /dev/null
+++ b/lib/route/link/vlan.c
@@ -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);
+}
+
+/** @} */