]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/addrconf.c
rtnl/ipv6: use netconf msg to advertise forwarding status
[~andy/linux] / net / ipv6 / addrconf.c
index 480e68422efb3c0f3ff7267ac89b8fe2b0d42fee..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();
@@ -1769,14 +1846,6 @@ static void sit_route_add(struct net_device *dev)
 }
 #endif
 
-static void addrconf_add_lroute(struct net_device *dev)
-{
-       struct in6_addr addr;
-
-       ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
-       addrconf_prefix_route(&addr, 64, dev, 0, 0);
-}
-
 static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
 {
        struct inet6_dev *idev;
@@ -1794,8 +1863,6 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
        if (!(dev->flags & IFF_LOOPBACK))
                addrconf_add_mroute(dev);
 
-       /* Add link local route */
-       addrconf_add_lroute(dev);
        return idev;
 }
 
@@ -2474,10 +2541,9 @@ static void addrconf_sit_config(struct net_device *dev)
 
        sit_add_v4_addrs(idev);
 
-       if (dev->flags&IFF_POINTOPOINT) {
+       if (dev->flags&IFF_POINTOPOINT)
                addrconf_add_mroute(dev);
-               addrconf_add_lroute(dev);
-       } else
+       else
                sit_route_add(dev);
 }
 #endif
@@ -3075,14 +3141,15 @@ static struct inet6_ifaddr *if6_get_first(struct seq_file *seq, loff_t pos)
                struct hlist_node *n;
                hlist_for_each_entry_rcu_bh(ifa, n, &inet6_addr_lst[state->bucket],
                                         addr_lst) {
+                       if (!net_eq(dev_net(ifa->idev->dev), net))
+                               continue;
                        /* sync with offset */
                        if (p < state->offset) {
                                p++;
                                continue;
                        }
                        state->offset++;
-                       if (net_eq(dev_net(ifa->idev->dev), net))
-                               return ifa;
+                       return ifa;
                }
 
                /* prepare for next bucket */
@@ -3100,18 +3167,20 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq,
        struct hlist_node *n = &ifa->addr_lst;
 
        hlist_for_each_entry_continue_rcu_bh(ifa, n, addr_lst) {
+               if (!net_eq(dev_net(ifa->idev->dev), net))
+                       continue;
                state->offset++;
-               if (net_eq(dev_net(ifa->idev->dev), net))
-                       return ifa;
+               return ifa;
        }
 
        while (++state->bucket < IN6_ADDR_HSIZE) {
                state->offset = 0;
                hlist_for_each_entry_rcu_bh(ifa, n,
                                     &inet6_addr_lst[state->bucket], addr_lst) {
+                       if (!net_eq(dev_net(ifa->idev->dev), net))
+                               continue;
                        state->offset++;
-                       if (net_eq(dev_net(ifa->idev->dev), net))
-                               return ifa;
+                       return ifa;
                }
        }