rtnl/ipv6: use netconf msg to advertise forwarding status
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Thu, 25 Oct 2012 22:28:50 +0000 (22:28 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 28 Oct 2012 23:05:00 +0000 (19:05 -0400)
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/rtnetlink.h
net/ipv6/addrconf.c

index 0043b413b8bc5bbb0d71e5c7e6fa84497fd8ec7d..a4d75ea868ed638b0cf6c1bf689811aaa2268657 100644 (file)
@@ -592,6 +592,8 @@ enum rtnetlink_groups {
 #define RTNLGRP_PHONET_ROUTE   RTNLGRP_PHONET_ROUTE
        RTNLGRP_DCB,
 #define RTNLGRP_DCB            RTNLGRP_DCB
+       RTNLGRP_IPV6_NETCONF,
+#define RTNLGRP_IPV6_NETCONF   RTNLGRP_IPV6_NETCONF
        __RTNLGRP_MAX
 };
 #define RTNLGRP_MAX    (__RTNLGRP_MAX - 1)
index 0424e4e27414d3bd6c79f5094d0d3907e62b02f5..0c57a8f6771510d078d9e9f288966b458291a0bc 100644 (file)
@@ -81,6 +81,7 @@
 #include <net/pkt_sched.h>
 #include <linux/if_tunnel.h>
 #include <linux/rtnetlink.h>
+#include <linux/netconf.h>
 
 #ifdef CONFIG_IPV6_PRIVACY
 #include <linux/random.h>
@@ -460,6 +461,72 @@ static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
        return idev;
 }
 
+static int inet6_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 inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
+                                     struct ipv6_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_INET6;
+
+       if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
+               goto nla_put_failure;
+
+       if (type == NETCONFA_FORWARDING &&
+           nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0)
+               goto nla_put_failure;
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
+}
+
+static void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex,
+                                        struct ipv6_devconf *devconf)
+{
+       struct sk_buff *skb;
+       int err = -ENOBUFS;
+
+       skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_ATOMIC);
+       if (skb == NULL)
+               goto errout;
+
+       err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
+                                        RTM_NEWNETCONF, 0, type);
+       if (err < 0) {
+               /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
+               WARN_ON(err == -EMSGSIZE);
+               kfree_skb(skb);
+               goto errout;
+       }
+       rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_ATOMIC);
+       return;
+errout:
+       if (err < 0)
+               rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err);
+}
+
 #ifdef CONFIG_SYSCTL
 static void dev_forward_change(struct inet6_dev *idev)
 {
@@ -486,6 +553,8 @@ static void dev_forward_change(struct inet6_dev *idev)
                else
                        addrconf_leave_anycast(ifa);
        }
+       inet6_netconf_notify_devconf(dev_net(dev), NETCONFA_FORWARDING,
+                                    dev->ifindex, &idev->cnf);
 }
 
 
@@ -518,6 +587,10 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
        *p = newf;
 
        if (p == &net->ipv6.devconf_dflt->forwarding) {
+               if ((!newf) ^ (!old))
+                       inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+                                                    NETCONFA_IFINDEX_DEFAULT,
+                                                    net->ipv6.devconf_dflt);
                rtnl_unlock();
                return 0;
        }
@@ -525,6 +598,10 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
        if (p == &net->ipv6.devconf_all->forwarding) {
                net->ipv6.devconf_dflt->forwarding = newf;
                addrconf_forward_change(net, newf);
+               if ((!newf) ^ (!old))
+                       inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+                                                    NETCONFA_IFINDEX_ALL,
+                                                    net->ipv6.devconf_all);
        } else if ((!newf) ^ (!old))
                dev_forward_change((struct inet6_dev *)table->extra1);
        rtnl_unlock();