diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 292d1cd..b60ff97 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -147,6 +147,14 @@ enum { #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) #endif +enum { + IFLA_INET_UNSPEC, + IFLA_INET_CONF, + __IFLA_INET_MAX, +}; + +#define IFLA_INET_MAX (__IFLA_INET_MAX - 1) + /* ifi_flags. IFF_* flags. diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h new file mode 100644 index 0000000..fd4d2df --- /dev/null +++ b/include/linux/inetdevice.h @@ -0,0 +1,36 @@ +#ifndef _LINUX_INETDEVICE_H +#define _LINUX_INETDEVICE_H + +enum +{ + IPV4_DEVCONF_FORWARDING=1, + IPV4_DEVCONF_MC_FORWARDING, + IPV4_DEVCONF_PROXY_ARP, + IPV4_DEVCONF_ACCEPT_REDIRECTS, + IPV4_DEVCONF_SECURE_REDIRECTS, + IPV4_DEVCONF_SEND_REDIRECTS, + IPV4_DEVCONF_SHARED_MEDIA, + IPV4_DEVCONF_RP_FILTER, + IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, + IPV4_DEVCONF_BOOTP_RELAY, + IPV4_DEVCONF_LOG_MARTIANS, + IPV4_DEVCONF_TAG, + IPV4_DEVCONF_ARPFILTER, + IPV4_DEVCONF_MEDIUM_ID, + IPV4_DEVCONF_NOXFRM, + IPV4_DEVCONF_NOPOLICY, + IPV4_DEVCONF_FORCE_IGMP_VERSION, + IPV4_DEVCONF_ARP_ANNOUNCE, + IPV4_DEVCONF_ARP_IGNORE, + IPV4_DEVCONF_PROMOTE_SECONDARIES, + IPV4_DEVCONF_ARP_ACCEPT, + IPV4_DEVCONF_ARP_NOTIFY, + IPV4_DEVCONF_ACCEPT_LOCAL, + IPV4_DEVCONF_SRC_VMARK, + IPV4_DEVCONF_PROXY_ARP_PVLAN, + __IPV4_DEVCONF_MAX +}; + +#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1) + +#endif /* _LINUX_INETDEVICE_H */ diff --git a/include/netlink-local.h b/include/netlink-local.h index 59f1d61..fbcf961 100644 --- a/include/netlink-local.h +++ b/include/netlink-local.h @@ -48,7 +48,7 @@ #include #include #include - +#include #include #include diff --git a/include/netlink/route/link/api.h b/include/netlink/route/link/api.h index 2280b4a..960d3f1 100644 --- a/include/netlink/route/link/api.h +++ b/include/netlink/route/link/api.h @@ -110,6 +110,11 @@ struct rtnl_link_af_ops int (*ao_parse_af)(struct rtnl_link *, struct nlattr *, void *); + /** Called if a link message is sent to the kernel. Must append the + * link address family specific attributes to the message. */ + int (*ao_fill_af)(struct rtnl_link *, + struct nl_msg *msg, void *); + /** Dump address family specific link attributes */ void (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *, struct nl_dump_params *, @@ -118,6 +123,10 @@ struct rtnl_link_af_ops extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int); extern void rtnl_link_af_ops_put(struct rtnl_link_af_ops *); +extern void * rtnl_link_af_alloc(struct rtnl_link *, + const struct rtnl_link_af_ops *); +extern void * rtnl_link_af_data(const struct rtnl_link *, + const struct rtnl_link_af_ops *); extern int rtnl_link_af_register(struct rtnl_link_af_ops *); extern int rtnl_link_af_unregister(struct rtnl_link_af_ops *); diff --git a/include/netlink/route/link/inet.h b/include/netlink/route/link/inet.h new file mode 100644 index 0000000..9feff37 --- /dev/null +++ b/include/netlink/route/link/inet.h @@ -0,0 +1,29 @@ +/* + * netlink/route/link/inet.h INET Link Module + * + * 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 + */ + +#ifndef NETLINK_LINK_INET_H_ +#define NETLINK_LINK_INET_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char * rtnl_link_inet_devconf2str(int, char *, size_t); +extern unsigned int rtnl_link_inet_str2devconf(const char *); + +extern int rtnl_link_inet_get_conf(struct rtnl_link *, + const unsigned int, uint32_t *); +extern int rtnl_link_inet_set_conf(struct rtnl_link *, + const unsigned int, uint32_t); + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 4aafb3b..3cfe80b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -59,7 +59,7 @@ libnl_route_la_SOURCES = \ route/cls/ematch/meta.c \ \ route/link/api.c route/link/vlan.c \ - route/link/bridge.c route/link/inet6.c \ + route/link/bridge.c route/link/inet6.c route/link/inet.c \ \ route/sch/blackhole.c route/sch/cbq.c route/sch/dsmark.c \ route/sch/fifo.c route/sch/htb.c route/sch/netem.c route/sch/prio.c \ 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 @@ -190,6 +190,64 @@ void rtnl_link_af_ops_put(struct rtnl_link_af_ops *ops) ops->ao_refcnt--; } +/** + * 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 + */ + +#include +#include +#include +#include +#include + +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); +}