patch-2.1.79 linux/net/ipv6/addrconf.c
Next file: linux/net/ipv6/af_inet6.c
Previous file: linux/net/ipv6/Config.in
Back to the patch index
Back to the overall index
- Lines: 1124
- Date:
Mon Jan 12 15:28:28 1998
- Orig file:
v2.1.78/linux/net/ipv6/addrconf.c
- Orig date:
Fri Dec 19 15:53:06 1997
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: addrconf.c,v 1.30 1997/12/09 17:12:47 freitag Exp $
+ * $Id: addrconf.c,v 1.32 1997/12/27 20:41:18 kuznet Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -35,6 +35,9 @@
#include <linux/route.h>
#include <linux/inetdevice.h>
#include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
#include <linux/proc_fs.h>
#include <net/sock.h>
@@ -47,6 +50,7 @@
#include <net/addrconf.h>
#include <net/ip.h>
#include <linux/if_tunnel.h>
+#include <linux/rtnetlink.h>
#include <asm/uaccess.h>
@@ -62,17 +66,12 @@
/*
* Configured unicast address list
*/
-struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
-
-/*
- * Hash list of configured multicast addresses
- */
-struct ifmcaddr6 *inet6_mcast_lst[IN6_ADDR_HSIZE];
+static struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
/*
* AF_INET6 device list
*/
-struct inet6_dev *inet6_dev_lst[IN6_ADDR_HSIZE];
+static struct inet6_dev *inet6_dev_lst[IN6_ADDR_HSIZE];
static atomic_t addr_list_lock = ATOMIC_INIT(0);
@@ -83,12 +82,13 @@
0, 0, addrconf_verify
};
-static int addrconf_ifdown(struct device *dev);
+static int addrconf_ifdown(struct device *dev, int how);
static void addrconf_dad_start(struct inet6_ifaddr *ifp);
static void addrconf_dad_timer(unsigned long data);
static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
static void addrconf_rs_timer(unsigned long data);
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
int ipv6_addr_type(struct in6_addr *addr)
{
@@ -157,6 +157,14 @@
memset(ndev, 0, sizeof(struct inet6_dev));
ndev->dev = dev;
+ ndev->nd_parms = neigh_parms_alloc(&nd_tbl);
+ if (ndev->nd_parms == NULL)
+ ndev->nd_parms = &nd_tbl.parms;
+#ifdef CONFIG_SYSCTL
+ else
+ ndev->nd_parms->sysctl_table =
+ neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+#endif
hash = ipv6_devindex_hash(dev->ifindex);
bptr = &inet6_dev_lst[hash];
iter = *bptr;
@@ -165,10 +173,25 @@
bptr = &iter->next;
*bptr = ndev;
+
}
return ndev;
}
+static struct inet6_dev * ipv6_find_idev(struct device *dev)
+{
+ struct inet6_dev *idev;
+
+ if ((idev = ipv6_get_idev(dev)) == NULL) {
+ idev = ipv6_add_dev(dev);
+ if (idev == NULL)
+ return NULL;
+ }
+ if (dev->flags&IFF_UP)
+ ipv6_mc_up(idev);
+ return idev;
+}
+
void addrconf_forwarding_on(void)
{
struct inet6_dev *idev;
@@ -180,18 +203,7 @@
printk(KERN_DEBUG "dev %s\n", idev->dev->name);
#endif
- if (idev->dev->type == ARPHRD_ETHER) {
- struct in6_addr maddr;
-
-#if ACONF_DEBUG >= 2
- printk(KERN_DEBUG "joining all-routers\n");
-#endif
- idev->router = 1;
-
- /* Wrong. It is user level function. */
- ipv6_addr_all_routers(&maddr);
- ipv6_dev_mc_inc(idev->dev, &maddr);
- }
+ idev->router = 1;
}
}
}
@@ -244,11 +256,13 @@
return ifa;
}
-void ipv6_del_addr(struct inet6_ifaddr *ifp)
+static void ipv6_del_addr(struct inet6_ifaddr *ifp)
{
struct inet6_ifaddr *iter, **back;
int hash;
+ ipv6_ifa_notify(RTM_DELADDR, ifp);
+
if (atomic_read(&addr_list_lock)) {
ifp->flags |= ADDR_INVALID;
return;
@@ -399,33 +413,75 @@
* to the host.
*/
-struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr)
+struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr, struct device *dev, int nd)
{
struct inet6_ifaddr * ifp;
u8 hash;
+ unsigned flags = 0;
+
+ if (!nd)
+ flags |= DAD_STATUS|ADDR_INVALID;
atomic_inc(&addr_list_lock);
hash = ipv6_addr_hash(addr);
for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
- if (ipv6_addr_cmp(&ifp->addr, addr) == 0)
- break;
+ if (ipv6_addr_cmp(&ifp->addr, addr) == 0 && !(ifp->flags&flags)) {
+ if (dev == NULL || ifp->idev->dev == dev ||
+ !(ifp->scope&(IFA_LINK|IFA_HOST)))
+ break;
+ }
}
atomic_dec(&addr_list_lock);
- return ifp;
+ return ifp;
}
+void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+{
+ printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
+ del_timer(&ifp->timer);
+ ipv6_del_addr(ifp);
+}
+
+
/* Join to solicited addr multicast group. */
static void addrconf_join_solict(struct device *dev, struct in6_addr *addr)
{
struct in6_addr maddr;
- addrconf_addr_solict_mult(addr, &maddr);
+ if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+ return;
+
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(addr, &maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(addr, &maddr);
ipv6_dev_mc_inc(dev, &maddr);
+#endif
}
+static void addrconf_leave_solict(struct device *dev, struct in6_addr *addr)
+{
+ struct in6_addr maddr;
+
+ if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+ return;
+
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(addr, &maddr);
+ ipv6_dev_mc_dec(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(addr, &maddr);
+ ipv6_dev_mc_dec(dev, &maddr);
+#endif
+}
+
+
#ifdef CONFIG_IPV6_EUI64
static int ipv6_generate_eui64(u8 *eui, struct device *dev)
{
@@ -462,6 +518,7 @@
rtmsg.rtmsg_ifindex = dev->ifindex;
rtmsg.rtmsg_info = info;
rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
/* Prevent useless cloning on PtP SIT.
This thing is done here expecting that the whole
@@ -469,12 +526,8 @@
*/
if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
- rtmsg.rtmsg_type = RTMSG_NEWROUTE;
ip6_route_add(&rtmsg, &err);
-
- if (err)
- printk(KERN_DEBUG "IPv6: error %d adding prefix route\n", err);
}
/* Create "default" multicast route to the interface */
@@ -482,7 +535,6 @@
static void addrconf_add_mroute(struct device *dev)
{
struct in6_rtmsg rtmsg;
- struct rt6_info *rt;
int err;
memset(&rtmsg, 0, sizeof(rtmsg));
@@ -493,25 +545,12 @@
rtmsg.rtmsg_ifindex = dev->ifindex;
rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-
- rt = ip6_route_add(&rtmsg, &err);
-
- /*
- * Pedro makes interesting thing here, he attached
- * fake nexthop to multicast route.
- * It is trick to avoid cloning, ugly, but efficient. --ANK
- */
-
- if (err)
- printk(KERN_DEBUG "IPv6: error %d adding mroute\n", err);
- else
- rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+ ip6_route_add(&rtmsg, &err);
}
static void sit_route_add(struct device *dev)
{
struct in6_rtmsg rtmsg;
- struct rt6_info *rt;
int err;
memset(&rtmsg, 0, sizeof(rtmsg));
@@ -521,19 +560,10 @@
/* prefix length - 96 bytes "::d.d.d.d" */
rtmsg.rtmsg_dst_len = 96;
- rtmsg.rtmsg_flags = RTF_UP;
+ rtmsg.rtmsg_flags = RTF_UP|RTF_NONEXTHOP;
rtmsg.rtmsg_ifindex = dev->ifindex;
- rt = ip6_route_add(&rtmsg, &err);
-
- /* See comment in addrconf_add_mroute.
- * It is the same trick, but to avoid cloning for direct
- * sit routes i.e. IPv4 comaptible destinations.
- */
- if (err)
- printk(KERN_DEBUG "sit_route_add: error %d in route_add\n", err);
- else
- rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+ ip6_route_add(&rtmsg, &err);
}
static void addrconf_add_lroute(struct device *dev)
@@ -546,24 +576,16 @@
static struct inet6_dev *addrconf_add_dev(struct device *dev)
{
- struct in6_addr maddr;
struct inet6_dev *idev;
- if ((idev = ipv6_get_idev(dev)) == NULL) {
- idev = ipv6_add_dev(dev);
- if (idev == NULL)
- return NULL;
- }
+ if ((idev = ipv6_find_idev(dev)) == NULL)
+ return NULL;
/* Add default multicast route */
addrconf_add_mroute(dev);
/* Add link local route */
addrconf_add_lroute(dev);
-
- /* Join to all nodes multicast group. */
- ipv6_addr_all_nodes(&maddr);
- ipv6_dev_mc_inc(dev, &maddr);
return idev;
}
@@ -613,9 +635,15 @@
* 2) Configure prefixes with the auto flag set
*/
- rt_expires = jiffies + valid_lft * HZ;
- if (rt_expires < jiffies)
- rt_expires = ~0;
+ /* Avoid arithemtic overflow. Really, we could
+ save rt_expires in seconds, likely valid_lft,
+ but it would require division in fib gc, that it
+ not good.
+ */
+ if (valid_lft >= 0x7FFFFFFF/HZ)
+ rt_expires = 0;
+ else
+ rt_expires = jiffies + valid_lft * HZ;
rt = rt6_lookup(&pinfo->prefix, NULL, dev, RTF_LINKRT);
@@ -660,9 +688,9 @@
return;
ok:
- ifp = ipv6_chk_addr(&addr);
+ ifp = ipv6_chk_addr(&addr, dev, 1);
- if (ifp == NULL && valid_lft) {
+ if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) {
struct inet6_dev *in6_dev = ipv6_get_idev(dev);
if (in6_dev == NULL) {
@@ -670,8 +698,8 @@
return;
}
- ifp = ipv6_add_addr(in6_dev, &addr,
- addr_type & IPV6_ADDR_SCOPE_MASK);
+ if (ifp == NULL)
+ ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK);
if (ifp == NULL)
return;
@@ -687,9 +715,14 @@
}
if (ifp) {
+ int event = 0;
ifp->valid_lft = valid_lft;
ifp->prefered_lft = prefered_lft;
ifp->tstamp = jiffies;
+ if (ifp->flags & ADDR_INVALID)
+ event = RTM_NEWADDR;
+ ifp->flags &= ~(ADDR_DEPRECATED|ADDR_INVALID);
+ ipv6_ifa_notify(event, ifp);
}
}
}
@@ -705,25 +738,26 @@
struct device *dev;
int err = -EINVAL;
- if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) {
- err = -EFAULT;
+ rtnl_lock();
+
+ err = -EFAULT;
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
goto err_exit;
- }
dev = dev_get_by_index(ireq.ifr6_ifindex);
- if (dev == NULL) {
- err = -ENODEV;
+ err = -ENODEV;
+ if (dev == NULL)
goto err_exit;
- }
if (dev->type == ARPHRD_SIT) {
struct ifreq ifr;
mm_segment_t oldfs;
struct ip_tunnel_parm p;
+ err = -EADDRNOTAVAIL;
if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
- return -EADDRNOTAVAIL;
+ goto err_exit;
memset(&p, 0, sizeof(p));
p.iph.daddr = ireq.ifr6_addr.s6_addr32[3];
@@ -747,27 +781,21 @@
}
err_exit:
+ rtnl_unlock();
return err;
}
/*
* Manual configuration of address on an interface
*/
-int addrconf_add_ifaddr(void *arg)
+static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen)
{
- struct inet6_dev *idev;
- struct in6_ifreq ireq;
struct inet6_ifaddr *ifp;
+ struct inet6_dev *idev;
struct device *dev;
int scope;
- if (!suser())
- return -EPERM;
-
- if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
- return -EFAULT;
-
- if ((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
+ if ((dev = dev_get_by_index(ifindex)) == NULL)
return -ENODEV;
if (!(dev->flags&IFF_UP))
@@ -776,49 +804,83 @@
if ((idev = addrconf_add_dev(dev)) == NULL)
return -ENOBUFS;
- scope = ipv6_addr_scope(&ireq.ifr6_addr);
+ scope = ipv6_addr_scope(pfx);
- if((ifp = ipv6_add_addr(idev, &ireq.ifr6_addr, scope)) == NULL)
+ if ((ifp = ipv6_add_addr(idev, pfx, scope)) == NULL)
return -ENOMEM;
- ifp->prefix_len = ireq.ifr6_prefixlen;
+ ifp->prefix_len = plen;
ifp->flags |= ADDR_PERMANENT;
addrconf_dad_start(ifp);
return 0;
}
-int addrconf_del_ifaddr(void *arg)
+static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
{
- struct in6_ifreq ireq;
struct inet6_ifaddr *ifp;
+ struct inet6_dev *idev;
struct device *dev;
int scope;
- struct inet6_dev *idev;
- if (!suser())
- return -EPERM;
-
- if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
- return -EFAULT;
-
- if ((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
+ if ((dev = dev_get_by_index(ifindex)) == NULL)
return -ENODEV;
if ((idev = ipv6_get_idev(dev)) == NULL)
return -ENXIO;
- scope = ipv6_addr_scope(&ireq.ifr6_addr);
+ scope = ipv6_addr_scope(pfx);
- for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
- if (ifp->scope == scope &&
- (!memcmp(&ireq.ifr6_addr, &ifp->addr, sizeof(struct in6_addr)))) {
+ for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->scope == scope && ifp->prefix_len == plen &&
+ (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) {
ipv6_del_addr(ifp);
- break;
+
+ /* If the last address is deleted administratively,
+ disable IPv6 on this interface.
+ */
+
+ if (idev->addr_list == NULL)
+ addrconf_ifdown(idev->dev, 1);
+ return 0;
}
}
+ return -EADDRNOTAVAIL;
+}
- return 0;
+
+int addrconf_add_ifaddr(void *arg)
+{
+ struct in6_ifreq ireq;
+ int err;
+
+ if (!suser())
+ return -EPERM;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ return -EFAULT;
+
+ rtnl_lock();
+ err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+ rtnl_unlock();
+ return err;
+}
+
+int addrconf_del_ifaddr(void *arg)
+{
+ struct in6_ifreq ireq;
+ int err;
+
+ if (!suser())
+ return -EPERM;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ return -EFAULT;
+
+ rtnl_lock();
+ err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+ rtnl_unlock();
+ return err;
}
static void sit_add_v4_addrs(struct inet6_dev *idev)
@@ -843,7 +905,7 @@
if (ifp) {
ifp->flags |= ADDR_PERMANENT;
ifp->prefix_len = 128;
- ip6_rt_addr_add(&ifp->addr, idev->dev);
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
}
return;
}
@@ -876,7 +938,7 @@
else
ifp->prefix_len = 96;
ifp->flags |= ADDR_PERMANENT;
- ip6_rt_addr_add(&ifp->addr, dev);
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
}
}
}
@@ -887,16 +949,13 @@
struct in6_addr addr;
struct inet6_dev *idev;
struct inet6_ifaddr * ifp;
- int err;
/* ::1 */
memset(&addr, 0, sizeof(struct in6_addr));
addr.s6_addr[15] = 1;
- idev = ipv6_add_dev(dev);
-
- if (idev == NULL) {
+ if ((idev = ipv6_find_idev(dev)) == NULL) {
printk(KERN_DEBUG "init loopback: add_dev failed\n");
return;
}
@@ -909,10 +968,9 @@
}
ifp->flags |= ADDR_PERMANENT;
+ ifp->prefix_len = 128;
- err = ip6_rt_addr_add(&addr, dev);
- if (err)
- printk(KERN_DEBUG "init_loopback: error in route_add\n");
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
}
static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
@@ -932,7 +990,6 @@
static void addrconf_dev_config(struct device *dev)
{
struct in6_addr addr;
- struct in6_addr maddr;
struct inet6_dev * idev;
if (dev->type != ARPHRD_ETHER) {
@@ -965,16 +1022,8 @@
addrconf_add_linklocal(idev, &addr);
#endif
- if (ipv6_config.forwarding) {
+ if (ipv6_config.forwarding)
idev->router = 1;
-
- /* It is wrong.
- It is routing daemon or radvd that must make it,
- rather than kernel.
- */
- ipv6_addr_all_routers(&maddr);
- ipv6_dev_mc_inc(dev, &maddr);
- }
}
static void addrconf_sit_config(struct device *dev)
@@ -987,8 +1036,7 @@
* our v4 addrs in the tunnel
*/
- idev = ipv6_add_dev(dev);
- if (idev == NULL) {
+ if ((idev = ipv6_find_idev(dev)) == NULL) {
printk(KERN_DEBUG "init sit: add_dev failed\n");
return;
}
@@ -1026,19 +1074,20 @@
break;
};
+#ifdef CONFIG_IPV6_NETLINK
rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
+#endif
break;
case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
/*
- * Remove all addresses from this interface
- * and take the interface out of the list.
+ * Remove all addresses from this interface.
*/
- if (addrconf_ifdown(dev) == 0) {
-#if 0
- rt6_ifdown(dev);
-#endif
+ if (addrconf_ifdown(dev, event == NETDEV_UNREGISTER) == 0) {
+#ifdef CONFIG_IPV6_NETLINK
rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
+#endif
}
break;
@@ -1047,57 +1096,65 @@
return NOTIFY_OK;
}
-static int addrconf_ifdown(struct device *dev)
+static int addrconf_ifdown(struct device *dev, int how)
{
struct inet6_dev *idev, **bidev;
struct inet6_ifaddr *ifa, **bifa;
int i, hash;
- start_bh_atomic();
+ rt6_ifdown(dev);
+ neigh_ifdown(&nd_tbl, dev);
- hash = ipv6_devindex_hash(dev->ifindex);
- bidev = &inet6_dev_lst[hash];
+ idev = ipv6_get_idev(dev);
+ if (idev == NULL)
+ return -ENODEV;
- for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
- if (idev->dev == dev) {
- *bidev = idev->next;
- break;
- }
- bidev = &idev->next;
- }
+ start_bh_atomic();
- if (idev == NULL) {
- end_bh_atomic();
+ /* Discard multicast list */
- printk(KERN_DEBUG "addrconf_ifdown: invalid device %p\n",dev);
- return -ENODEV;
- }
+ if (how == 1)
+ ipv6_mc_destroy_dev(idev);
+ else
+ ipv6_mc_down(idev);
- /*
- * FIXME: clear multicast group membership
- */
+ /* Discard address list */
+
+ idev->addr_list = NULL;
/*
- * clean addr_list
+ * Clean addresses hash table
*/
for (i=0; i<16; i++) {
bifa = &inet6_addr_lst[i];
- for (ifa=inet6_addr_lst[i]; ifa; ) {
+ while ((ifa = *bifa) != NULL) {
if (ifa->idev == idev) {
*bifa = ifa->lst_next;
del_timer(&ifa->timer);
+ ipv6_ifa_notify(RTM_DELADDR, ifa);
kfree(ifa);
- ifa = *bifa;
continue;
}
bifa = &ifa->lst_next;
- ifa = *bifa;
}
}
- kfree(idev);
+ /* Delete device from device hash table (if unregistered) */
+
+ if (how == 1) {
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (bidev = &inet6_dev_lst[hash]; (idev=*bidev) != NULL; bidev = &idev->next) {
+ if (idev->dev == dev) {
+ *bidev = idev->next;
+ neigh_parms_release(&nd_tbl, idev->nd_parms);
+ kfree(idev);
+ break;
+ }
+ }
+ }
end_bh_atomic();
return 0;
}
@@ -1123,12 +1180,9 @@
if (ifp->probes++ <= ipv6_config.rtr_solicits) {
struct in6_addr all_routers;
- ipv6_addr_set(&all_routers,
- __constant_htonl(0xff020000U), 0, 0,
- __constant_htonl(0x2U));
+ ipv6_addr_all_routers(&all_routers);
- ndisc_send_rs(ifp->idev->dev, &ifp->addr,
- &all_routers);
+ ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
ifp->timer.function = addrconf_rs_timer;
ifp->timer.expires = (jiffies +
@@ -1158,7 +1212,6 @@
*/
static void addrconf_dad_start(struct inet6_ifaddr *ifp)
{
- static int rand_seed = 1;
struct device *dev;
unsigned long rand_num;
@@ -1177,15 +1230,12 @@
return;
}
- if (rand_seed) {
- rand_seed = 0;
- nd_rand_seed = ifp->addr.s6_addr32[3];
- }
+ net_srandom(ifp->addr.s6_addr32[3]);
ifp->probes = ipv6_config.dad_transmits;
ifp->flags |= DAD_INCOMPLETE;
- rand_num = ipv6_random() % ipv6_config.rtr_solicit_delay;
+ rand_num = net_random() % ipv6_config.rtr_solicit_delay;
ifp->timer.function = addrconf_dad_timer;
ifp->timer.expires = jiffies + rand_num;
@@ -1215,9 +1265,14 @@
/* send a neighbour solicitation for our addr */
memset(&unspec, 0, sizeof(unspec));
- addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
-
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(&ifp->addr, &mcaddr);
+ ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+#endif
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(&ifp->addr, &mcaddr);
ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+#endif
ifp->timer.expires = jiffies + ipv6_config.rtr_solicit_interval;
add_timer(&ifp->timer);
@@ -1231,7 +1286,7 @@
* Configure the address for reception. Now it is valid.
*/
- ip6_rt_addr_add(&ifp->addr, dev);
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
/* If added prefix is link local and forwarding is off,
start sending router solicitations.
@@ -1242,9 +1297,7 @@
(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
struct in6_addr all_routers;
- ipv6_addr_set(&all_routers,
- __constant_htonl(0xff020000U), 0, 0,
- __constant_htonl(0x2U));
+ ipv6_addr_all_routers(&all_routers);
/*
* If a host as already performed a random delay
@@ -1319,31 +1372,221 @@
for (i=0; i < IN6_ADDR_HSIZE; i++) {
for (ifp=inet6_addr_lst[i]; ifp;) {
+ if (ifp->flags & ADDR_INVALID) {
+ struct inet6_ifaddr *bp = ifp;
+ ifp= ifp->lst_next;
+ ipv6_del_addr(bp);
+ continue;
+ }
if (!(ifp->flags & ADDR_PERMANENT)) {
struct inet6_ifaddr *bp;
unsigned long age;
age = (now - ifp->tstamp) / HZ;
- if (age > ifp->prefered_lft)
- ifp->flags |= ADDR_DEPRECATED;
-
bp = ifp;
- ifp=ifp->lst_next;
+ ifp= ifp->lst_next;
if (age > bp->valid_lft)
ipv6_del_addr(bp);
+ else if (age > bp->prefered_lft) {
+ bp->flags |= ADDR_DEPRECATED;
+ ipv6_ifa_notify(0, bp);
+ }
continue;
}
- ifp=ifp->lst_next;
+ ifp = ifp->lst_next;
}
}
addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
- add_timer(&addr_chk_timer);
+ add_timer(&addr_chk_timer);
+}
+
+#ifdef CONFIG_RTNETLINK
+static int
+inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct kern_ifa *k_ifa = arg;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct in6_addr *pfx;
+
+ pfx = NULL;
+ if (k_ifa->ifa_address)
+ pfx = k_ifa->ifa_address;
+ if (k_ifa->ifa_local) {
+ if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+ return -EINVAL;
+ pfx = k_ifa->ifa_local;
+ }
+
+ return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+}
+
+static int
+inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct kern_ifa *k_ifa = arg;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct in6_addr *pfx;
+
+ pfx = NULL;
+ if (k_ifa->ifa_address)
+ pfx = k_ifa->ifa_address;
+ if (k_ifa->ifa_local) {
+ if (pfx && memcmp(pfx, k_ifa->ifa_local, sizeof(*pfx)))
+ return -EINVAL;
+ pfx = k_ifa->ifa_local;
+ }
+
+ return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+}
+
+static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
+ pid_t pid, u32 seq, int event)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+ struct ifa_cacheinfo ci;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm));
+ ifm = NLMSG_DATA(nlh);
+ ifm->ifa_family = AF_INET6;
+ ifm->ifa_prefixlen = ifa->prefix_len;
+ ifm->ifa_flags = ifa->flags & ~ADDR_INVALID;
+ ifm->ifa_scope = RT_SCOPE_UNIVERSE;
+ if (ifa->scope&IFA_HOST)
+ ifm->ifa_scope = RT_SCOPE_HOST;
+ else if (ifa->scope&IFA_LINK)
+ ifm->ifa_scope = RT_SCOPE_LINK;
+ else if (ifa->scope&IFA_SITE)
+ ifm->ifa_scope = RT_SCOPE_SITE;
+ ifm->ifa_index = ifa->idev->dev->ifindex;
+ RTA_PUT(skb, IFA_ADDRESS, 16, &ifa->addr);
+ if (!(ifa->flags&IFA_F_PERMANENT)) {
+ ci.ifa_prefered = ifa->prefered_lft;
+ ci.ifa_valid = ifa->valid_lft;
+ if (ci.ifa_prefered != 0xFFFFFFFF) {
+ long tval = (jiffies - ifa->tstamp)/HZ;
+ ci.ifa_prefered -= tval;
+ if (ci.ifa_valid != 0xFFFFFFFF)
+ ci.ifa_valid -= tval;
+ }
+ RTA_PUT(skb, IFA_CACHEINFO, sizeof(ci), &ci);
+ }
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_put(skb, b - skb->tail);
+ return -1;
+}
+
+static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx, ip_idx;
+ int s_idx, s_ip_idx;
+ struct inet6_ifaddr *ifa;
+
+ s_idx = cb->args[0];
+ s_ip_idx = ip_idx = cb->args[1];
+
+ for (idx=0; idx < IN6_ADDR_HSIZE; idx++) {
+ if (idx < s_idx)
+ continue;
+ if (idx > s_idx)
+ s_ip_idx = 0;
+ start_bh_atomic();
+ for (ifa=inet6_addr_lst[idx], ip_idx = 0; ifa;
+ ifa = ifa->lst_next, ip_idx++) {
+ if (ip_idx < s_ip_idx)
+ continue;
+ if (inet6_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0) {
+ end_bh_atomic();
+ goto done;
+ }
+ }
+ end_bh_atomic();
+ }
+done:
+ cb->args[0] = idx;
+ cb->args[1] = ip_idx;
+
+ return skb->len;
}
+static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, ENOBUFS);
+ return;
+ }
+ if (inet6_fill_ifaddr(skb, ifa, 0, 0, event) < 0) {
+ kfree_skb(skb, 0);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_IFADDR;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFADDR, GFP_ATOMIC);
+}
+
+static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
+{
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, rtnetlink_dump_ifinfo, },
+ { NULL, NULL, },
+
+ { inet6_rtm_newaddr, NULL, },
+ { inet6_rtm_deladdr, NULL, },
+ { NULL, inet6_dump_ifaddr, },
+ { NULL, NULL, },
+
+ { inet6_rtm_newroute, NULL, },
+ { inet6_rtm_delroute, NULL, },
+ { NULL, inet6_dump_fib, },
+ { NULL, NULL, },
+
+ { neigh_add, NULL, },
+ { neigh_delete, NULL, },
+ { NULL, neigh_dump_info, },
+ { NULL, NULL, },
+
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+};
+#endif
+
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+{
+#ifdef CONFIG_RTNETLINK
+ inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
+#endif
+ switch (event) {
+ case RTM_NEWADDR:
+ ip6_rt_addr_add(&ifp->addr, ifp->idev->dev);
+ break;
+ case RTM_DELADDR:
+ start_bh_atomic();
+ addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
+ if (ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 0) == NULL)
+ ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
+ end_bh_atomic();
+ break;
+ }
+}
+
+
/*
* Init / cleanup code
*/
@@ -1352,19 +1595,7 @@
{
#ifdef MODULE
struct device *dev;
-#endif
-
- /*
- * init address and device hash lists
- */
-
- memset(inet6_addr_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_ifaddr *));
-
- memset(inet6_mcast_lst, 0, IN6_ADDR_HSIZE * sizeof(struct ifmcaddr6 *));
- memset(inet6_dev_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_dev *));
-
-#ifdef MODULE
/* This takes sense only during module load. */
for (dev = dev_base; dev; dev = dev->next) {
@@ -1390,6 +1621,9 @@
addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
add_timer(&addr_chk_timer);
+#ifdef CONFIG_RTNETLINK
+ rtnetlink_links[AF_INET6] = inet6_rtnetlink_table;
+#endif
}
#ifdef MODULE
@@ -1399,6 +1633,11 @@
struct inet6_ifaddr *ifa;
int i;
+#ifdef CONFIG_RTNETLINK
+ rtnetlink_links[AF_INET6] = NULL;
+#endif
+ start_bh_atomic();
+
del_timer(&addr_chk_timer);
/*
@@ -1409,7 +1648,7 @@
struct inet6_dev *next;
for (idev = inet6_dev_lst[i]; idev; idev = next) {
next = idev->next;
- addrconf_ifdown(idev->dev);
+ addrconf_ifdown(idev->dev, 1);
}
}
@@ -1423,9 +1662,13 @@
bifa = ifa;
ifa = ifa->lst_next;
- kfree(bifa);
+ printk(KERN_DEBUG "bug: IPv6 address leakage detected: ifa=%p\n", bifa);
+ /* Do not free it; something is wrong.
+ Now we can investigate it with debugger.
+ */
}
}
+ end_bh_atomic();
#ifdef CONFIG_PROC_FS
proc_net_unregister(iface_proc_entry.low_ino);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov