summaryrefslogtreecommitdiff
path: root/lib/route
diff options
context:
space:
mode:
authorThomas Graf <tgraf@suug.ch>2010-11-16 12:41:43 +0100
committerThomas Graf <tgraf@suug.ch>2010-11-16 12:41:43 +0100
commit53015f83811bd56d4b66331656633ad39051babf (patch)
tree9012ce9a769af8b977e98baa25b8b981cafbdba2 /lib/route
parent2e3ca4db0cbca7974888e7d3e7d84ba8fbbcf639 (diff)
downloadlibnl-53015f83811bd56d4b66331656633ad39051babf.tar.gz
link: AF_INET link module
Note: The code for this is not upstream yet. Extends the link api to allow address family modules to fill a link message and implements a AF_INET address family link module which uses the new interface.
Diffstat (limited to 'lib/route')
-rw-r--r--lib/route/link.c34
-rw-r--r--lib/route/link/api.c58
-rw-r--r--lib/route/link/inet.c228
3 files changed, 313 insertions, 7 deletions
diff --git a/lib/route/link.c b/lib/route/link.c
index 60e1600..868a89a 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -187,18 +187,14 @@ static struct rtnl_link_af_ops *af_lookup_and_alloc(struct rtnl_link *link,
int family)
{
struct rtnl_link_af_ops *af_ops;
+ void *data;
af_ops = rtnl_link_af_ops_lookup(family);
if (!af_ops)
return NULL;
- if (!link->l_af_data[family] && af_ops->ao_alloc) {
- link->l_af_data[family] = af_ops->ao_alloc(link);
- if (!link->l_af_data[family]) {
- rtnl_link_af_ops_put(af_ops);
- return NULL;
- }
- }
+ if (!(data = rtnl_link_af_data(link, af_ops)))
+ return NULL;
return af_ops;
}
@@ -226,6 +222,27 @@ static int af_clone(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
return 0;
}
+static int af_fill(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+ void *data, void *arg)
+{
+ struct nl_msg *msg = arg;
+ struct nlattr *af_attr;
+ int err;
+
+ if (!ops->ao_fill_af)
+ return 0;
+
+ if (!(af_attr = nla_nest_start(msg, ops->ao_family)))
+ return -NLE_MSGSIZE;
+
+ if ((err = ops->ao_fill_af(link, arg, data)) < 0)
+ return err;
+
+ nla_nest_end(msg, af_attr);
+
+ return 0;
+}
+
static int af_dump_line(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
void *data, void *arg)
{
@@ -1096,6 +1113,9 @@ int rtnl_link_build_change_request(struct rtnl_link *old,
nla_nest_end(msg, info);
}
+ if (do_foreach_af(tmpl, af_fill, msg) < 0)
+ goto nla_put_failure;
+
*result = msg;
return 0;
diff --git a/lib/route/link/api.c b/lib/route/link/api.c
index b1608e3..f7907a7 100644
--- a/lib/route/link/api.c
+++ b/lib/route/link/api.c
@@ -191,6 +191,64 @@ void rtnl_link_af_ops_put(struct rtnl_link_af_ops *ops)
}
/**
+ * Allocate and return data buffer for link address family modules
+ * @arg link Link object
+ * @arg ops Address family operations
+ *
+ * This function must be called by link address family modules in all
+ * cases where the API does not provide the data buffer as argument
+ * already. This typically includes set functions the module provides.
+ * Calling this function is strictly required to ensure proper allocation
+ * of the buffer upon first use. Link objects will NOT proactively
+ * allocate a data buffer for each registered link address family.
+ *
+ * @return Pointer to data buffer or NULL on error.
+ */
+void *rtnl_link_af_alloc(struct rtnl_link *link,
+ const struct rtnl_link_af_ops *ops)
+{
+ int family;
+
+ if (!link || !ops)
+ BUG();
+
+ family = ops->ao_family;
+
+ if (!link->l_af_data[family]) {
+ if (!ops->ao_alloc)
+ BUG();
+
+ link->l_af_data[family] = ops->ao_alloc(link);
+ if (!link->l_af_data[family])
+ return NULL;
+ }
+
+ return link->l_af_data[family];
+}
+
+/**
+ * Return data buffer for link address family modules
+ * @arg link Link object
+ * @arg ops Address family operations
+ *
+ * This function returns a pointer to the data buffer for the specified link
+ * address family module or NULL if the buffer was not allocated yet. This
+ * function is typically used by get functions of modules which are not
+ * interested in having the data buffer allocated if no values have been set
+ * yet.
+ *
+ * @return Pointer to data buffer or NULL on error.
+ */
+void *rtnl_link_af_data(const struct rtnl_link *link,
+ const struct rtnl_link_af_ops *ops)
+{
+ if (!link || !ops)
+ BUG();
+
+ return link->l_af_data[ops->ao_family];
+}
+
+/**
* Register operations for a link address family
* @arg ops Address family operations
*
diff --git a/lib/route/link/inet.c b/lib/route/link/inet.c
new file mode 100644
index 0000000..5736b32
--- /dev/null
+++ b/lib/route/link/inet.c
@@ -0,0 +1,228 @@
+/*
+ * lib/route/link/inet.c AF_INET link operations
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link/api.h>
+
+struct inet_data
+{
+ uint8_t i_confset[IPV4_DEVCONF_MAX];
+ uint32_t i_conf[IPV4_DEVCONF_MAX];
+};
+
+static void *inet_alloc(struct rtnl_link *link)
+{
+ return calloc(1, sizeof(struct inet_data));
+}
+
+static void *inet_clone(struct rtnl_link *link, void *data)
+{
+ struct inet_data *id;
+
+ if ((id = inet_alloc(link)))
+ memcpy(id, data, sizeof(*id));
+
+ return id;
+}
+
+static void inet_free(struct rtnl_link *link, void *data)
+{
+ free(data);
+}
+
+static struct nla_policy inet_policy[IFLA_INET6_MAX+1] = {
+ [IFLA_INET_CONF] = { .minlen = IPV4_DEVCONF_MAX * 4 },
+};
+
+static int inet_parse_af(struct rtnl_link *link, struct nlattr *attr, void *data)
+{
+ struct inet_data *id = data;
+ struct nlattr *tb[IFLA_INET_MAX+1];
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_INET_MAX, attr, inet_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[IFLA_INET_CONF])
+ nla_memcpy(&id->i_conf, tb[IFLA_INET_CONF], sizeof(id->i_conf));
+
+ return 0;
+}
+
+static int inet_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data)
+{
+ struct inet_data *id = data;
+ struct nlattr *nla;
+ int i;
+
+ if (!(nla = nla_nest_start(msg, IFLA_INET_CONF)))
+ return -NLE_MSGSIZE;
+
+ for (i = 0; i < IPV4_DEVCONF_MAX; i++)
+ if (id->i_confset[i])
+ NLA_PUT_U32(msg, i+1, id->i_conf[i]);
+
+ nla_nest_end(msg, nla);
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static struct trans_tbl inet_devconf[] = {
+ __ADD(IPV4_DEVCONF_FORWARDING, forwarding)
+ __ADD(IPV4_DEVCONF_MC_FORWARDING, mc_forwarding)
+ __ADD(IPV4_DEVCONF_PROXY_ARP, proxy_arp)
+ __ADD(IPV4_DEVCONF_ACCEPT_REDIRECTS, accept_redirects)
+ __ADD(IPV4_DEVCONF_SECURE_REDIRECTS, secure_redirects)
+ __ADD(IPV4_DEVCONF_SEND_REDIRECTS, send_redirects)
+ __ADD(IPV4_DEVCONF_SHARED_MEDIA, shared_media)
+ __ADD(IPV4_DEVCONF_RP_FILTER, rp_filter)
+ __ADD(IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route)
+ __ADD(IPV4_DEVCONF_BOOTP_RELAY, bootp_relay)
+ __ADD(IPV4_DEVCONF_LOG_MARTIANS, log_martians)
+ __ADD(IPV4_DEVCONF_TAG, tag)
+ __ADD(IPV4_DEVCONF_ARPFILTER, arpfilter)
+ __ADD(IPV4_DEVCONF_MEDIUM_ID, medium_id)
+ __ADD(IPV4_DEVCONF_NOXFRM, noxfrm)
+ __ADD(IPV4_DEVCONF_NOPOLICY, nopolicy)
+ __ADD(IPV4_DEVCONF_FORCE_IGMP_VERSION, force_igmp_version)
+ __ADD(IPV4_DEVCONF_ARP_ANNOUNCE, arp_announce)
+ __ADD(IPV4_DEVCONF_ARP_IGNORE, arp_ignore)
+ __ADD(IPV4_DEVCONF_PROMOTE_SECONDARIES, promote_secondaries)
+ __ADD(IPV4_DEVCONF_ARP_ACCEPT, arp_accept)
+ __ADD(IPV4_DEVCONF_ARP_NOTIFY, arp_notify)
+ __ADD(IPV4_DEVCONF_ACCEPT_LOCAL, accept_local)
+ __ADD(IPV4_DEVCONF_SRC_VMARK, src_vmark)
+ __ADD(IPV4_DEVCONF_PROXY_ARP_PVLAN, proxy_arp_pvlan)
+};
+
+const char *rtnl_link_inet_devconf2str(int type, char *buf, size_t len)
+{
+ return __type2str(type, buf, len, inet_devconf,
+ ARRAY_SIZE(inet_devconf));
+}
+
+unsigned int rtnl_link_inet_str2devconf(const char *name)
+{
+ return __str2type(name, inet_devconf, ARRAY_SIZE(inet_devconf));
+}
+
+static void inet_dump_details(struct rtnl_link *link,
+ struct nl_dump_params *p, void *data)
+{
+ struct inet_data *id = data;
+ char buf[64];
+ int i, n = 0;
+
+ nl_dump_line(p, " ipv4 devconf:\n");
+ nl_dump_line(p, " ");
+
+ for (i = 0; i < IPV4_DEVCONF_MAX; i++) {
+ nl_dump_line(p, "%s %u",
+ rtnl_link_inet_devconf2str(i+1, buf, sizeof(buf)),
+ id->i_conf[i]);
+
+ if (++n == 4) {
+ nl_dump(p, "\n");
+ nl_dump_line(p, " ");
+ n = 0;
+ } else
+ nl_dump(p, " ");
+ }
+
+ if (n != 0)
+ nl_dump(p, "\n");
+}
+
+static struct rtnl_link_af_ops inet_ops = {
+ .ao_family = AF_INET,
+ .ao_alloc = &inet_alloc,
+ .ao_clone = &inet_clone,
+ .ao_free = &inet_free,
+ .ao_parse_af = &inet_parse_af,
+ .ao_fill_af = &inet_fill_af,
+ .ao_dump[NL_DUMP_DETAILS] = &inet_dump_details,
+};
+
+/**
+ * Get value of a ipv4 link configuration setting
+ * @arg link Link object
+ * @arg cfgid Configuration identifier
+ * @arg res Result pointer
+ *
+ * Stores the value of the specified configuration setting in the provided
+ * result pointer.
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX
+ * @return -NLE_NOATTR configuration setting not available
+ */
+int rtnl_link_inet_get_conf(struct rtnl_link *link, const unsigned int cfgid,
+ uint32_t *res)
+{
+ struct inet_data *id;
+
+ if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX)
+ return -NLE_RANGE;
+
+ if (!(id = rtnl_link_af_alloc(link, &inet_ops)))
+ return -NLE_NOATTR;
+
+ *res = id->i_conf[cfgid];
+
+ return 0;
+}
+
+/**
+ * Change value of a ipv4 link configuration setting
+ * @arg link Link object
+ * @arg cfgid Configuration identifier
+ * @arg value New value
+ *
+ * Changes the value in the per link ipv4 configuration array.
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX
+ * @return -NLE_NOMEM memory allocation failed
+ */
+int rtnl_link_inet_set_conf(struct rtnl_link *link, const unsigned int cfgid,
+ uint32_t value)
+{
+ struct inet_data *id;
+
+ if (!(id = rtnl_link_af_alloc(link, &inet_ops)))
+ return -NLE_NOMEM;
+
+ if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX)
+ return -NLE_RANGE;
+
+ id->i_confset[cfgid - 1] = 1;
+ id->i_conf[cfgid - 1] = value;
+
+ return 0;
+}
+
+
+static void __init inet_init(void)
+{
+ rtnl_link_af_register(&inet_ops);
+}
+
+static void __exit inet_exit(void)
+{
+ rtnl_link_af_unregister(&inet_ops);
+}