rtnl/ipv4: use netconf msg to advertise forwarding status

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Nicolas Dichtel 2012-10-25 22:28:52 +00:00 committed by David S. Miller
parent 76f8f6cb76
commit edc9e74893
2 changed files with 91 additions and 4 deletions

View File

@ -592,6 +592,8 @@ enum rtnetlink_groups {
#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE #define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
RTNLGRP_DCB, RTNLGRP_DCB,
#define RTNLGRP_DCB RTNLGRP_DCB #define RTNLGRP_DCB RTNLGRP_DCB
RTNLGRP_IPV4_NETCONF,
#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF
RTNLGRP_IPV6_NETCONF, RTNLGRP_IPV6_NETCONF,
#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF #define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF
__RTNLGRP_MAX __RTNLGRP_MAX

View File

@ -55,6 +55,7 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#endif #endif
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/netconf.h>
#include <net/arp.h> #include <net/arp.h>
#include <net/ip.h> #include <net/ip.h>
@ -1442,6 +1443,73 @@ static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
return 0; return 0;
} }
static int inet_netconf_msgsize_devconf(int type)
{
int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
+ nla_total_size(4); /* NETCONFA_IFINDEX */
if (type == NETCONFA_FORWARDING)
size += nla_total_size(4);
return size;
}
static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
struct ipv4_devconf *devconf, u32 portid,
u32 seq, int event, unsigned int flags,
int type)
{
struct nlmsghdr *nlh;
struct netconfmsg *ncm;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
flags);
if (nlh == NULL)
return -EMSGSIZE;
ncm = nlmsg_data(nlh);
ncm->ncm_family = AF_INET;
if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
goto nla_put_failure;
if (type == NETCONFA_FORWARDING &&
nla_put_s32(skb, NETCONFA_FORWARDING,
IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
goto nla_put_failure;
return nlmsg_end(skb, nlh);
nla_put_failure:
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
}
static void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
struct ipv4_devconf *devconf)
{
struct sk_buff *skb;
int err = -ENOBUFS;
skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
if (skb == NULL)
goto errout;
err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
RTM_NEWNETCONF, 0, type);
if (err < 0) {
/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
WARN_ON(err == -EMSGSIZE);
kfree_skb(skb);
goto errout;
}
rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC);
return;
errout:
if (err < 0)
rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
}
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
static void devinet_copy_dflt_conf(struct net *net, int i) static void devinet_copy_dflt_conf(struct net *net, int i)
@ -1467,6 +1535,12 @@ static void inet_forward_change(struct net *net)
IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
IPV4_DEVCONF_DFLT(net, FORWARDING) = on; IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
NETCONFA_IFINDEX_ALL,
net->ipv4.devconf_all);
inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
NETCONFA_IFINDEX_DEFAULT,
net->ipv4.devconf_dflt);
for_each_netdev(net, dev) { for_each_netdev(net, dev) {
struct in_device *in_dev; struct in_device *in_dev;
@ -1474,8 +1548,11 @@ static void inet_forward_change(struct net *net)
dev_disable_lro(dev); dev_disable_lro(dev);
rcu_read_lock(); rcu_read_lock();
in_dev = __in_dev_get_rcu(dev); in_dev = __in_dev_get_rcu(dev);
if (in_dev) if (in_dev) {
IN_DEV_CONF_SET(in_dev, FORWARDING, on); IN_DEV_CONF_SET(in_dev, FORWARDING, on);
inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
dev->ifindex, &in_dev->cnf);
}
rcu_read_unlock(); rcu_read_unlock();
} }
} }
@ -1527,15 +1604,23 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
} }
if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
inet_forward_change(net); inet_forward_change(net);
} else if (*valp) { } else {
struct ipv4_devconf *cnf = ctl->extra1; struct ipv4_devconf *cnf = ctl->extra1;
struct in_device *idev = struct in_device *idev =
container_of(cnf, struct in_device, cnf); container_of(cnf, struct in_device, cnf);
dev_disable_lro(idev->dev); if (*valp)
dev_disable_lro(idev->dev);
inet_netconf_notify_devconf(net,
NETCONFA_FORWARDING,
idev->dev->ifindex,
cnf);
} }
rtnl_unlock(); rtnl_unlock();
rt_cache_flush(net); rt_cache_flush(net);
} } else
inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
NETCONFA_IFINDEX_DEFAULT,
net->ipv4.devconf_dflt);
} }
return ret; return ret;