patch-2.4.21 linux-2.4.21/net/ipv6/addrconf.c
Next file: linux-2.4.21/net/ipv6/af_inet6.c
Previous file: linux-2.4.21/net/ipv6/Makefile
Back to the patch index
Back to the overall index
- Lines: 571
- Date:
2003-06-13 07:51:39.000000000 -0700
- Orig file:
linux-2.4.20/net/ipv6/addrconf.c
- Orig date:
2002-11-28 15:53:15.000000000 -0800
diff -urN linux-2.4.20/net/ipv6/addrconf.c linux-2.4.21/net/ipv6/addrconf.c
@@ -28,6 +28,8 @@
* packets.
* YOSHIFUJI Hideaki @USAGI : improved accuracy of
* address validation timer.
+ * Yuji SEKIYA @USAGI : Don't assign a same IPv6
+ * address on a same interface.
*/
#include <linux/config.h>
@@ -105,6 +107,8 @@
static void addrconf_rs_timer(unsigned long data);
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
+static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev);
+
static struct notifier_block *inet6addr_chain;
struct ipv6_devconf ipv6_devconf =
@@ -135,21 +139,19 @@
MAX_RTR_SOLICITATION_DELAY, /* rtr solicit delay */
};
-int ipv6_addr_type(struct in6_addr *addr)
+/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+
+int ipv6_addr_type(const struct in6_addr *addr)
{
+ int type;
u32 st;
st = addr->s6_addr32[0];
- /* Consider all addresses with the first three bits different of
- 000 and 111 as unicasts.
- */
- if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
- (st & htonl(0xE0000000)) != htonl(0xE0000000))
- return IPV6_ADDR_UNICAST;
-
if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) {
- int type = IPV6_ADDR_MULTICAST;
+ type = IPV6_ADDR_MULTICAST;
switch((st & htonl(0x00FF0000))) {
case __constant_htonl(0x00010000):
@@ -166,29 +168,53 @@
};
return type;
}
+ /* check for reserved anycast addresses */
+
+ if ((st & htonl(0xE0000000)) &&
+ ((addr->s6_addr32[2] == htonl(0xFDFFFFFF) &&
+ (addr->s6_addr32[3] | htonl(0x7F)) == (u32)~0) ||
+ (addr->s6_addr32[2] == 0 && addr->s6_addr32[3] == 0)))
+ type = IPV6_ADDR_ANYCAST;
+ else
+ type = IPV6_ADDR_UNICAST;
+
+ /* Consider all addresses with the first three bits different of
+ 000 and 111 as finished.
+ */
+ if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
+ (st & htonl(0xE0000000)) != htonl(0xE0000000))
+ return type;
if ((st & htonl(0xFFC00000)) == htonl(0xFE800000))
- return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);
+ return (IPV6_ADDR_LINKLOCAL | type);
if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000))
- return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);
+ return (IPV6_ADDR_SITELOCAL | type);
if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
if (addr->s6_addr32[2] == 0) {
- if (addr->s6_addr32[3] == 0)
+ if (addr->in6_u.u6_addr32[3] == 0)
return IPV6_ADDR_ANY;
if (addr->s6_addr32[3] == htonl(0x00000001))
- return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);
+ return (IPV6_ADDR_LOOPBACK | type);
- return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);
+ return (IPV6_ADDR_COMPATv4 | type);
}
if (addr->s6_addr32[2] == htonl(0x0000ffff))
return IPV6_ADDR_MAPPED;
}
- return IPV6_ADDR_RESERVED;
+ st &= htonl(0xFF000000);
+ if (st == 0)
+ return IPV6_ADDR_RESERVED;
+ st &= htonl(0xFE000000);
+ if (st == htonl(0x02000000))
+ return IPV6_ADDR_RESERVED; /* for NSAP */
+ if (st == htonl(0x04000000))
+ return IPV6_ADDR_RESERVED; /* for IPX */
+ return type;
}
static void addrconf_del_timer(struct inet6_ifaddr *ifp)
@@ -224,7 +250,6 @@
add_timer(&ifp->timer);
}
-
/* Nobody refers to this device, we may destroy it. */
void in6_dev_finish_destroy(struct inet6_dev *idev)
@@ -303,24 +328,90 @@
return idev;
}
+void ipv6_addr_prefix(struct in6_addr *prefix,
+ struct in6_addr *addr, int prefix_len)
+{
+ unsigned long mask;
+ int ncopy, nbits;
+
+ memset(prefix, 0, sizeof(*prefix));
+
+ if (prefix_len <= 0)
+ return;
+ if (prefix_len > 128)
+ prefix_len = 128;
+
+ ncopy = prefix_len / 32;
+ switch (ncopy) {
+ case 4: prefix->s6_addr32[3] = addr->s6_addr32[3];
+ case 3: prefix->s6_addr32[2] = addr->s6_addr32[2];
+ case 2: prefix->s6_addr32[1] = addr->s6_addr32[1];
+ case 1: prefix->s6_addr32[0] = addr->s6_addr32[0];
+ case 0: break;
+ }
+ nbits = prefix_len % 32;
+ if (nbits == 0)
+ return;
+
+ mask = ~((1 << (32 - nbits)) - 1);
+ mask = htonl(mask);
+
+ prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask;
+}
+
+
+static void dev_forward_change(struct inet6_dev *idev)
+{
+ struct net_device *dev;
+ struct inet6_ifaddr *ifa;
+ struct in6_addr addr;
+
+ if (!idev)
+ return;
+ dev = idev->dev;
+ if (dev && (dev->flags & IFF_MULTICAST)) {
+ ipv6_addr_all_routers(&addr);
+
+ if (idev->cnf.forwarding)
+ ipv6_dev_mc_inc(dev, &addr);
+ else
+ ipv6_dev_mc_dec(dev, &addr);
+ }
+ for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) {
+ ipv6_addr_prefix(&addr, &ifa->addr, ifa->prefix_len);
+ if (ipv6_addr_any(&addr))
+ continue;
+ if (idev->cnf.forwarding)
+ ipv6_dev_ac_inc(idev->dev, &addr);
+ else
+ ipv6_dev_ac_dec(idev->dev, &addr);
+ }
+}
+
+
static void addrconf_forward_change(struct inet6_dev *idev)
{
struct net_device *dev;
- if (idev)
+ if (idev) {
+ dev_forward_change(idev);
return;
+ }
read_lock(&dev_base_lock);
for (dev=dev_base; dev; dev=dev->next) {
read_lock(&addrconf_lock);
idev = __in6_dev_get(dev);
- if (idev)
+ if (idev) {
idev->cnf.forwarding = ipv6_devconf.forwarding;
+ dev_forward_change(idev);
+ }
read_unlock(&addrconf_lock);
}
read_unlock(&dev_base_lock);
}
+
/* Nobody refers to this ifaddr, destroy it */
void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
@@ -347,17 +438,28 @@
/* On success it returns ifp with increased reference count */
static struct inet6_ifaddr *
-ipv6_add_addr(struct inet6_dev *idev, struct in6_addr *addr, int pfxlen,
+ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
int scope, unsigned flags)
{
struct inet6_ifaddr *ifa;
int hash;
+ static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+
+ spin_lock_bh(&lock);
+
+ /* Ignore adding duplicate addresses on an interface */
+ if (ipv6_chk_same_addr(addr, idev->dev)) {
+ spin_unlock_bh(&lock);
+ ADBG(("ipv6_add_addr: already assigned\n"));
+ return ERR_PTR(-EEXIST);
+ }
ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
if (ifa == NULL) {
+ spin_unlock_bh(&lock);
ADBG(("ipv6_add_addr: malloc failed\n"));
- return NULL;
+ return ERR_PTR(-ENOBUFS);
}
memset(ifa, 0, sizeof(struct inet6_ifaddr));
@@ -373,8 +475,9 @@
read_lock(&addrconf_lock);
if (idev->dead) {
read_unlock(&addrconf_lock);
+ spin_unlock_bh(&lock);
kfree(ifa);
- return NULL;
+ return ERR_PTR(-ENODEV); /*XXX*/
}
inet6_ifa_count++;
@@ -399,6 +502,7 @@
in6_ifa_hold(ifa);
write_unlock_bh(&idev->lock);
read_unlock(&addrconf_lock);
+ spin_unlock_bh(&lock);
notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifa);
@@ -458,29 +562,20 @@
* an address of the attached interface
* iii) don't use deprecated addresses
*/
-int ipv6_get_saddr(struct dst_entry *dst,
- struct in6_addr *daddr, struct in6_addr *saddr)
+int ipv6_dev_get_saddr(struct net_device *dev,
+ struct in6_addr *daddr, struct in6_addr *saddr, int onlink)
{
- int scope;
struct inet6_ifaddr *ifp = NULL;
struct inet6_ifaddr *match = NULL;
- struct net_device *dev = NULL;
struct inet6_dev *idev;
- struct rt6_info *rt;
+ int scope;
int err;
- rt = (struct rt6_info *) dst;
- if (rt)
- dev = rt->rt6i_dev;
- scope = ipv6_addr_scope(daddr);
- if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
- /*
- * route for the "all destinations on link" rule
- * when no routers are present
- */
+ if (!onlink)
+ scope = ipv6_addr_scope(daddr);
+ else
scope = IFA_LINK;
- }
/*
* known dev
@@ -568,6 +663,24 @@
return err;
}
+
+int ipv6_get_saddr(struct dst_entry *dst,
+ struct in6_addr *daddr, struct in6_addr *saddr)
+{
+ struct rt6_info *rt;
+ struct net_device *dev = NULL;
+ int onlink;
+
+ rt = (struct rt6_info *) dst;
+ if (rt)
+ dev = rt->rt6i_dev;
+
+ onlink = (rt && (rt->rt6i_flags & RTF_ALLONLINK));
+
+ return ipv6_dev_get_saddr(dev, daddr, saddr, onlink);
+}
+
+
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
{
struct inet6_dev *idev;
@@ -621,6 +734,23 @@
return ifp != NULL;
}
+static
+int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev)
+{
+ struct inet6_ifaddr * ifp;
+ u8 hash = ipv6_addr_hash(addr);
+
+ read_lock_bh(&addrconf_hash_lock);
+ for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+ if (ipv6_addr_cmp(&ifp->addr, addr) == 0) {
+ if (dev == NULL || ifp->idev->dev == dev)
+ break;
+ }
+ }
+ read_unlock_bh(&addrconf_hash_lock);
+ return ifp != NULL;
+}
+
struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *dev)
{
struct inet6_ifaddr * ifp;
@@ -660,7 +790,7 @@
/* Join to solicited addr multicast group. */
-static void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr)
+void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr)
{
struct in6_addr maddr;
@@ -671,7 +801,7 @@
ipv6_dev_mc_inc(dev, &maddr);
}
-static void addrconf_leave_solict(struct net_device *dev, struct in6_addr *addr)
+void addrconf_leave_solict(struct net_device *dev, struct in6_addr *addr)
{
struct in6_addr maddr;
@@ -744,7 +874,7 @@
if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
- ip6_route_add(&rtmsg);
+ ip6_route_add(&rtmsg, NULL);
}
/* Create "default" multicast route to the interface */
@@ -761,7 +891,7 @@
rtmsg.rtmsg_ifindex = dev->ifindex;
rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
rtmsg.rtmsg_type = RTMSG_NEWROUTE;
- ip6_route_add(&rtmsg);
+ ip6_route_add(&rtmsg, NULL);
}
static void sit_route_add(struct net_device *dev)
@@ -778,7 +908,7 @@
rtmsg.rtmsg_flags = RTF_UP|RTF_NONEXTHOP;
rtmsg.rtmsg_ifindex = dev->ifindex;
- ip6_route_add(&rtmsg);
+ ip6_route_add(&rtmsg, NULL);
}
static void addrconf_add_lroute(struct net_device *dev)
@@ -786,7 +916,7 @@
struct in6_addr addr;
ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
- addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF);
+ addrconf_prefix_route(&addr, 64, dev, 0, RTF_ADDRCONF);
}
static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
@@ -870,7 +1000,7 @@
if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
if (rt->rt6i_flags&RTF_EXPIRES) {
if (pinfo->onlink == 0 || valid_lft == 0) {
- ip6_del_rt(rt);
+ ip6_del_rt(rt, NULL);
rt = NULL;
} else {
rt->rt6i_expires = rt_expires;
@@ -919,7 +1049,7 @@
ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
addr_type&IPV6_ADDR_SCOPE_MASK, 0);
- if (ifp == NULL) {
+ if (IS_ERR(ifp)) {
in6_dev_put(in6_dev);
return;
}
@@ -947,6 +1077,7 @@
ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ?
0 : RTM_NEWADDR, ifp);
in6_ifa_put(ifp);
+ addrconf_verify(0);
}
}
in6_dev_put(in6_dev);
@@ -1033,13 +1164,14 @@
scope = ipv6_addr_scope(pfx);
- if ((ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT)) != NULL) {
+ ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT);
+ if (!IS_ERR(ifp)) {
addrconf_dad_start(ifp);
in6_ifa_put(ifp);
return 0;
}
- return -ENOBUFS;
+ return PTR_ERR(ifp);
}
static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
@@ -1131,7 +1263,7 @@
if (addr.s6_addr32[3]) {
ifp = ipv6_add_addr(idev, &addr, 128, scope, IFA_F_PERMANENT);
- if (ifp) {
+ if (!IS_ERR(ifp)) {
spin_lock_bh(&ifp->lock);
ifp->flags &= ~IFA_F_TENTATIVE;
spin_unlock_bh(&ifp->lock);
@@ -1161,13 +1293,13 @@
flag |= IFA_HOST;
}
if (idev->dev->flags&IFF_POINTOPOINT)
- plen = 10;
+ plen = 64;
else
plen = 96;
ifp = ipv6_add_addr(idev, &addr, plen, flag,
IFA_F_PERMANENT);
- if (ifp) {
+ if (!IS_ERR(ifp)) {
spin_lock_bh(&ifp->lock);
ifp->flags &= ~IFA_F_TENTATIVE;
spin_unlock_bh(&ifp->lock);
@@ -1181,7 +1313,6 @@
static void init_loopback(struct net_device *dev)
{
- struct in6_addr addr;
struct inet6_dev *idev;
struct inet6_ifaddr * ifp;
@@ -1189,16 +1320,13 @@
ASSERT_RTNL();
- memset(&addr, 0, sizeof(struct in6_addr));
- addr.s6_addr32[3] = htonl(0x00000001);
-
if ((idev = ipv6_find_idev(dev)) == NULL) {
printk(KERN_DEBUG "init loopback: add_dev failed\n");
return;
}
- ifp = ipv6_add_addr(idev, &addr, 128, IFA_HOST, IFA_F_PERMANENT);
- if (ifp) {
+ ifp = ipv6_add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFA_F_PERMANENT);
+ if (!IS_ERR(ifp)) {
spin_lock_bh(&ifp->lock);
ifp->flags &= ~IFA_F_TENTATIVE;
spin_unlock_bh(&ifp->lock);
@@ -1211,8 +1339,8 @@
{
struct inet6_ifaddr * ifp;
- ifp = ipv6_add_addr(idev, addr, 10, IFA_LINK, IFA_F_PERMANENT);
- if (ifp) {
+ ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+ if (!IS_ERR(ifp)) {
addrconf_dad_start(ifp);
in6_ifa_put(ifp);
}
@@ -1455,7 +1583,7 @@
rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
- ip6_route_add(&rtmsg);
+ ip6_route_add(&rtmsg, NULL);
}
out:
@@ -1564,6 +1692,14 @@
addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
spin_unlock_bh(&ifp->lock);
}
+
+ if (ifp->idev->cnf.forwarding) {
+ struct in6_addr addr;
+
+ ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
+ if (!ipv6_addr_any(&addr))
+ ipv6_dev_ac_inc(ifp->idev->dev, &addr);
+ }
}
#ifdef CONFIG_PROC_FS
@@ -1863,6 +1999,13 @@
break;
case RTM_DELADDR:
addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
+ if (ifp->idev->cnf.forwarding) {
+ struct in6_addr addr;
+
+ ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
+ if (!ipv6_addr_any(&addr))
+ ipv6_dev_ac_dec(ifp->idev->dev, &addr);
+ }
if (!ipv6_chk_addr(&ifp->addr, NULL))
ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
break;
@@ -1885,11 +2028,7 @@
struct inet6_dev *idev = NULL;
if (valp != &ipv6_devconf.forwarding) {
- struct net_device *dev = dev_get_by_index(ctl->ctl_name);
- if (dev) {
- idev = in6_dev_get(dev);
- dev_put(dev);
- }
+ idev = (struct inet6_dev *)ctl->extra1;
if (idev == NULL)
return ret;
} else
@@ -1899,8 +2038,6 @@
if (*valp)
rt6_purge_dflt_routers(0);
- if (idev)
- in6_dev_put(idev);
}
return ret;
@@ -1977,6 +2114,7 @@
for (i=0; i<sizeof(t->addrconf_vars)/sizeof(t->addrconf_vars[0])-1; i++) {
t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
t->addrconf_vars[i].de = NULL;
+ t->addrconf_vars[i].extra1 = idev; /* embedded; no ref */
}
if (dev) {
t->addrconf_dev[0].procname = dev->name;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)