aboutsummaryrefslogtreecommitdiff
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/Kconfig79
-rw-r--r--net/ipv6/Makefile25
-rw-r--r--net/ipv6/addrconf.c3615
-rw-r--r--net/ipv6/af_inet6.c867
-rw-r--r--net/ipv6/ah6.c478
-rw-r--r--net/ipv6/anycast.c594
-rw-r--r--net/ipv6/datagram.c600
-rw-r--r--net/ipv6/esp6.c424
-rw-r--r--net/ipv6/exthdrs.c575
-rw-r--r--net/ipv6/exthdrs_core.c109
-rw-r--r--net/ipv6/icmp.c822
-rw-r--r--net/ipv6/ip6_fib.c1225
-rw-r--r--net/ipv6/ip6_flowlabel.c706
-rw-r--r--net/ipv6/ip6_input.c269
-rw-r--r--net/ipv6/ip6_output.c1197
-rw-r--r--net/ipv6/ip6_tunnel.c1163
-rw-r--r--net/ipv6/ipcomp6.c524
-rw-r--r--net/ipv6/ipv6_sockglue.c704
-rw-r--r--net/ipv6/ipv6_syms.c41
-rw-r--r--net/ipv6/mcast.c2499
-rw-r--r--net/ipv6/ndisc.c1690
-rw-r--r--net/ipv6/netfilter/Kconfig242
-rw-r--r--net/ipv6/netfilter/Makefile26
-rw-r--r--net/ipv6/netfilter/ip6_queue.c741
-rw-r--r--net/ipv6/netfilter/ip6_tables.c1970
-rw-r--r--net/ipv6/netfilter/ip6t_LOG.c509
-rw-r--r--net/ipv6/netfilter/ip6t_MARK.c78
-rw-r--r--net/ipv6/netfilter/ip6t_ah.c208
-rw-r--r--net/ipv6/netfilter/ip6t_dst.c298
-rw-r--r--net/ipv6/netfilter/ip6t_esp.c181
-rw-r--r--net/ipv6/netfilter/ip6t_eui64.c101
-rw-r--r--net/ipv6/netfilter/ip6t_frag.c229
-rw-r--r--net/ipv6/netfilter/ip6t_hbh.c298
-rw-r--r--net/ipv6/netfilter/ip6t_hl.c80
-rw-r--r--net/ipv6/netfilter/ip6t_ipv6header.c167
-rw-r--r--net/ipv6/netfilter/ip6t_length.c66
-rw-r--r--net/ipv6/netfilter/ip6t_limit.c147
-rw-r--r--net/ipv6/netfilter/ip6t_mac.c80
-rw-r--r--net/ipv6/netfilter/ip6t_mark.c66
-rw-r--r--net/ipv6/netfilter/ip6t_multiport.c125
-rw-r--r--net/ipv6/netfilter/ip6t_owner.c174
-rw-r--r--net/ipv6/netfilter/ip6t_physdev.c135
-rw-r--r--net/ipv6/netfilter/ip6t_rt.c301
-rw-r--r--net/ipv6/netfilter/ip6table_filter.c214
-rw-r--r--net/ipv6/netfilter/ip6table_mangle.c287
-rw-r--r--net/ipv6/netfilter/ip6table_raw.c182
-rw-r--r--net/ipv6/proc.c303
-rw-r--r--net/ipv6/protocol.c86
-rw-r--r--net/ipv6/raw.c1157
-rw-r--r--net/ipv6/reassembly.c771
-rw-r--r--net/ipv6/route.c2131
-rw-r--r--net/ipv6/sit.c833
-rw-r--r--net/ipv6/sysctl_net_ipv6.c125
-rw-r--r--net/ipv6/tcp_ipv6.c2265
-rw-r--r--net/ipv6/udp.c1075
-rw-r--r--net/ipv6/xfrm6_input.c150
-rw-r--r--net/ipv6/xfrm6_output.c143
-rw-r--r--net/ipv6/xfrm6_policy.c295
-rw-r--r--net/ipv6/xfrm6_state.c136
-rw-r--r--net/ipv6/xfrm6_tunnel.c543
60 files changed, 35124 insertions, 0 deletions
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
new file mode 100644
index 00000000000..e66ca9381cf
--- /dev/null
+++ b/net/ipv6/Kconfig
@@ -0,0 +1,79 @@
+#
+# IPv6 configuration
+#
+config IPV6_PRIVACY
+ bool "IPv6: Privacy Extensions (RFC 3041) support"
+ depends on IPV6
+ ---help---
+ Privacy Extensions for Stateless Address Autoconfiguration in IPv6
+ support. With this option, additional periodically-alter
+ pseudo-random global-scope unicast address(es) will assigned to
+ your interface(s).
+
+ By default, kernel do not generate temporary addresses.
+ To use temporary addresses, do
+
+ echo 2 >/proc/sys/net/ipv6/conf/all/use_tempaddr
+
+ See <file:Documentation/networking/ip-sysctl.txt> for details.
+
+config INET6_AH
+ tristate "IPv6: AH transformation"
+ depends on IPV6
+ select XFRM
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_MD5
+ select CRYPTO_SHA1
+ ---help---
+ Support for IPsec AH.
+
+ If unsure, say Y.
+
+config INET6_ESP
+ tristate "IPv6: ESP transformation"
+ depends on IPV6
+ select XFRM
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_MD5
+ select CRYPTO_SHA1
+ select CRYPTO_DES
+ ---help---
+ Support for IPsec ESP.
+
+ If unsure, say Y.
+
+config INET6_IPCOMP
+ tristate "IPv6: IPComp transformation"
+ depends on IPV6
+ select XFRM
+ select INET6_TUNNEL
+ select CRYPTO
+ select CRYPTO_DEFLATE
+ ---help---
+ Support for IP Payload Compression Protocol (IPComp) (RFC3173),
+ typically needed for IPsec.
+
+ If unsure, say Y.
+
+config INET6_TUNNEL
+ tristate "IPv6: tunnel transformation"
+ depends on IPV6
+ select XFRM
+ ---help---
+ Support for generic IPv6-in-IPv6 tunnel transformation, which is
+ required by the IPv6-in-IPv6 tunneling module as well as tunnel mode
+ IPComp.
+
+ If unsure, say Y.
+
+config IPV6_TUNNEL
+ tristate "IPv6: IPv6-in-IPv6 tunnel"
+ depends on IPV6
+ select INET6_TUNNEL
+ ---help---
+ Support for IPv6-in-IPv6 tunnels described in RFC 2473.
+
+ If unsure, say N.
+
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
new file mode 100644
index 00000000000..b39e0494059
--- /dev/null
+++ b/net/ipv6/Makefile
@@ -0,0 +1,25 @@
+#
+# Makefile for the Linux TCP/IP (INET6) layer.
+#
+
+obj-$(CONFIG_IPV6) += ipv6.o
+
+ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
+ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
+ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
+ ip6_flowlabel.o ipv6_syms.o
+
+ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
+ xfrm6_output.o
+ipv6-objs += $(ipv6-y)
+
+obj-$(CONFIG_INET6_AH) += ah6.o
+obj-$(CONFIG_INET6_ESP) += esp6.o
+obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o
+obj-$(CONFIG_INET6_TUNNEL) += xfrm6_tunnel.o
+obj-$(CONFIG_NETFILTER) += netfilter/
+
+obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
+
+obj-y += exthdrs_core.o
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
new file mode 100644
index 00000000000..5ffde14ddc0
--- /dev/null
+++ b/net/ipv6/addrconf.c
@@ -0,0 +1,3615 @@
+/*
+ * IPv6 Address [auto]configuration
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *
+ * $Id: addrconf.c,v 1.69 2001/10/31 21:55:54 davem Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Changes:
+ *
+ * Janos Farkas : delete timer on ifdown
+ * <chexum@bankinf.banki.hu>
+ * Andi Kleen : kill double kfree on module
+ * unload.
+ * Maciej W. Rozycki : FDDI support
+ * sekiya@USAGI : Don't send too many RS
+ * packets.
+ * yoshfuji@USAGI : Fixed interval between DAD
+ * packets.
+ * YOSHIFUJI Hideaki @USAGI : improved accuracy of
+ * address validation timer.
+ * YOSHIFUJI Hideaki @USAGI : Privacy Extensions (RFC3041)
+ * support.
+ * Yuji SEKIYA @USAGI : Don't assign a same IPv6
+ * address on a same interface.
+ * YOSHIFUJI Hideaki @USAGI : ARCnet support
+ * YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to
+ * seq_file.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_arcnet.h>
+#include <linux/if_infiniband.h>
+#include <linux/route.h>
+#include <linux/inetdevice.h>
+#include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+#include <linux/delay.h>
+#include <linux/notifier.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/tcp.h>
+#include <net/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/rtnetlink.h>
+
+#ifdef CONFIG_IPV6_PRIVACY
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#endif
+
+#include <asm/uaccess.h>
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+/* Set to 3 to get tracing... */
+#define ACONF_DEBUG 2
+
+#if ACONF_DEBUG >= 3
+#define ADBG(x) printk x
+#else
+#define ADBG(x)
+#endif
+
+#define INFINITY_LIFE_TIME 0xFFFFFFFF
+#define TIME_DELTA(a,b) ((unsigned long)((long)(a) - (long)(b)))
+
+#ifdef CONFIG_SYSCTL
+static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p);
+static void addrconf_sysctl_unregister(struct ipv6_devconf *p);
+#endif
+
+#ifdef CONFIG_IPV6_PRIVACY
+static int __ipv6_regen_rndid(struct inet6_dev *idev);
+static int __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
+static void ipv6_regen_rndid(unsigned long data);
+
+static int desync_factor = MAX_DESYNC_FACTOR * HZ;
+static struct crypto_tfm *md5_tfm;
+static DEFINE_SPINLOCK(md5_tfm_lock);
+#endif
+
+static int ipv6_count_addresses(struct inet6_dev *idev);
+
+/*
+ * Configured unicast address hash table
+ */
+static struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
+static DEFINE_RWLOCK(addrconf_hash_lock);
+
+/* Protects inet6 devices */
+DEFINE_RWLOCK(addrconf_lock);
+
+static void addrconf_verify(unsigned long);
+
+static struct timer_list addr_chk_timer =
+ TIMER_INITIALIZER(addrconf_verify, 0, 0);
+static DEFINE_SPINLOCK(addrconf_verify_lock);
+
+static void addrconf_join_anycast(struct inet6_ifaddr *ifp);
+static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);
+
+static int addrconf_ifdown(struct net_device *dev, int how);
+
+static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags);
+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);
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
+
+static void inet6_prefix_notify(int event, struct inet6_dev *idev,
+ struct prefix_info *pinfo);
+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 = {
+ .forwarding = 0,
+ .hop_limit = IPV6_DEFAULT_HOPLIMIT,
+ .mtu6 = IPV6_MIN_MTU,
+ .accept_ra = 1,
+ .accept_redirects = 1,
+ .autoconf = 1,
+ .force_mld_version = 0,
+ .dad_transmits = 1,
+ .rtr_solicits = MAX_RTR_SOLICITATIONS,
+ .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
+#ifdef CONFIG_IPV6_PRIVACY
+ .use_tempaddr = 0,
+ .temp_valid_lft = TEMP_VALID_LIFETIME,
+ .temp_prefered_lft = TEMP_PREFERRED_LIFETIME,
+ .regen_max_retry = REGEN_MAX_RETRY,
+ .max_desync_factor = MAX_DESYNC_FACTOR,
+#endif
+ .max_addresses = IPV6_MAX_ADDRESSES,
+};
+
+static struct ipv6_devconf ipv6_devconf_dflt = {
+ .forwarding = 0,
+ .hop_limit = IPV6_DEFAULT_HOPLIMIT,
+ .mtu6 = IPV6_MIN_MTU,
+ .accept_ra = 1,
+ .accept_redirects = 1,
+ .autoconf = 1,
+ .dad_transmits = 1,
+ .rtr_solicits = MAX_RTR_SOLICITATIONS,
+ .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
+#ifdef CONFIG_IPV6_PRIVACY
+ .use_tempaddr = 0,
+ .temp_valid_lft = TEMP_VALID_LIFETIME,
+ .temp_prefered_lft = TEMP_PREFERRED_LIFETIME,
+ .regen_max_retry = REGEN_MAX_RETRY,
+ .max_desync_factor = MAX_DESYNC_FACTOR,
+#endif
+ .max_addresses = IPV6_MAX_ADDRESSES,
+};
+
+/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
+#if 0
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+#endif
+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];
+
+ if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) {
+ type = IPV6_ADDR_MULTICAST;
+
+ switch((st & htonl(0x00FF0000))) {
+ case __constant_htonl(0x00010000):
+ type |= IPV6_ADDR_LOOPBACK;
+ break;
+
+ case __constant_htonl(0x00020000):
+ type |= IPV6_ADDR_LINKLOCAL;
+ break;
+
+ case __constant_htonl(0x00050000):
+ type |= IPV6_ADDR_SITELOCAL;
+ break;
+ };
+ return type;
+ }
+
+ 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 | type);
+
+ if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000))
+ 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)
+ return IPV6_ADDR_ANY;
+
+ if (addr->s6_addr32[3] == htonl(0x00000001))
+ return (IPV6_ADDR_LOOPBACK | type);
+
+ return (IPV6_ADDR_COMPATv4 | type);
+ }
+
+ if (addr->s6_addr32[2] == htonl(0x0000ffff))
+ return IPV6_ADDR_MAPPED;
+ }
+
+ 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)
+{
+ if (del_timer(&ifp->timer))
+ __in6_ifa_put(ifp);
+}
+
+enum addrconf_timer_t
+{
+ AC_NONE,
+ AC_DAD,
+ AC_RS,
+};
+
+static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
+ enum addrconf_timer_t what,
+ unsigned long when)
+{
+ if (!del_timer(&ifp->timer))
+ in6_ifa_hold(ifp);
+
+ switch (what) {
+ case AC_DAD:
+ ifp->timer.function = addrconf_dad_timer;
+ break;
+ case AC_RS:
+ ifp->timer.function = addrconf_rs_timer;
+ break;
+ default:;
+ }
+ ifp->timer.expires = jiffies + when;
+ add_timer(&ifp->timer);
+}
+
+/* Nobody refers to this device, we may destroy it. */
+
+void in6_dev_finish_destroy(struct inet6_dev *idev)
+{
+ struct net_device *dev = idev->dev;
+ BUG_TRAP(idev->addr_list==NULL);
+ BUG_TRAP(idev->mc_list==NULL);
+#ifdef NET_REFCNT_DEBUG
+ printk(KERN_DEBUG "in6_dev_finish_destroy: %s\n", dev ? dev->name : "NIL");
+#endif
+ dev_put(dev);
+ if (!idev->dead) {
+ printk("Freeing alive inet6 device %p\n", idev);
+ return;
+ }
+ snmp6_free_dev(idev);
+ kfree(idev);
+}
+
+static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
+{
+ struct inet6_dev *ndev;
+
+ ASSERT_RTNL();
+
+ if (dev->mtu < IPV6_MIN_MTU)
+ return NULL;
+
+ ndev = kmalloc(sizeof(struct inet6_dev), GFP_KERNEL);
+
+ if (ndev) {
+ memset(ndev, 0, sizeof(struct inet6_dev));
+
+ rwlock_init(&ndev->lock);
+ ndev->dev = dev;
+ memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));
+ ndev->cnf.mtu6 = dev->mtu;
+ ndev->cnf.sysctl = NULL;
+ ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
+ if (ndev->nd_parms == NULL) {
+ kfree(ndev);
+ return NULL;
+ }
+ /* We refer to the device */
+ dev_hold(dev);
+
+ if (snmp6_alloc_dev(ndev) < 0) {
+ ADBG((KERN_WARNING
+ "%s(): cannot allocate memory for statistics; dev=%s.\n",
+ __FUNCTION__, dev->name));
+ neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ ndev->dead = 1;
+ in6_dev_finish_destroy(ndev);
+ return NULL;
+ }
+
+ if (snmp6_register_dev(ndev) < 0) {
+ ADBG((KERN_WARNING
+ "%s(): cannot create /proc/net/dev_snmp6/%s\n",
+ __FUNCTION__, dev->name));
+ neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ ndev->dead = 1;
+ in6_dev_finish_destroy(ndev);
+ return NULL;
+ }
+
+ /* One reference from device. We must do this before
+ * we invoke __ipv6_regen_rndid().
+ */
+ in6_dev_hold(ndev);
+
+#ifdef CONFIG_IPV6_PRIVACY
+ get_random_bytes(ndev->rndid, sizeof(ndev->rndid));
+ get_random_bytes(ndev->entropy, sizeof(ndev->entropy));
+ init_timer(&ndev->regen_timer);
+ ndev->regen_timer.function = ipv6_regen_rndid;
+ ndev->regen_timer.data = (unsigned long) ndev;
+ if ((dev->flags&IFF_LOOPBACK) ||
+ dev->type == ARPHRD_TUNNEL ||
+ dev->type == ARPHRD_SIT) {
+ printk(KERN_INFO
+ "Disabled Privacy Extensions on device %p(%s)\n",
+ dev, dev->name);
+ ndev->cnf.use_tempaddr = -1;
+ } else {
+ in6_dev_hold(ndev);
+ ipv6_regen_rndid((unsigned long) ndev);
+ }
+#endif
+
+ write_lock_bh(&addrconf_lock);
+ dev->ip6_ptr = ndev;
+ write_unlock_bh(&addrconf_lock);
+
+ ipv6_mc_init_dev(ndev);
+ ndev->tstamp = jiffies;
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6,
+ NET_IPV6_NEIGH, "ipv6",
+ &ndisc_ifinfo_sysctl_change,
+ NULL);
+ addrconf_sysctl_register(ndev, &ndev->cnf);
+#endif
+ }
+ return ndev;
+}
+
+static struct inet6_dev * ipv6_find_idev(struct net_device *dev)
+{
+ struct inet6_dev *idev;
+
+ ASSERT_RTNL();
+
+ if ((idev = __in6_dev_get(dev)) == NULL) {
+ if ((idev = ipv6_add_dev(dev)) == NULL)
+ return NULL;
+ }
+ if (dev->flags&IFF_UP)
+ ipv6_mc_up(idev);
+ return idev;
+}
+
+#ifdef CONFIG_SYSCTL
+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) {
+ if (idev->cnf.forwarding)
+ addrconf_join_anycast(ifa);
+ else
+ addrconf_leave_anycast(ifa);
+ }
+}
+
+
+static void addrconf_forward_change(void)
+{
+ struct net_device *dev;
+ struct inet6_dev *idev;
+
+ read_lock(&dev_base_lock);
+ for (dev=dev_base; dev; dev=dev->next) {
+ read_lock(&addrconf_lock);
+ idev = __in6_dev_get(dev);
+ if (idev) {
+ int changed = (!idev->cnf.forwarding) ^ (!ipv6_devconf.forwarding);
+ idev->cnf.forwarding = ipv6_devconf.forwarding;
+ if (changed)
+ dev_forward_change(idev);
+ }
+ read_unlock(&addrconf_lock);
+ }
+ read_unlock(&dev_base_lock);
+}
+#endif
+
+/* Nobody refers to this ifaddr, destroy it */
+
+void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
+{
+ BUG_TRAP(ifp->if_next==NULL);
+ BUG_TRAP(ifp->lst_next==NULL);
+#ifdef NET_REFCNT_DEBUG
+ printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");
+#endif
+
+ in6_dev_put(ifp->idev);
+
+ if (del_timer(&ifp->timer))
+ printk("Timer is still running, when freeing ifa=%p\n", ifp);
+
+ if (!ifp->dead) {
+ printk("Freeing alive inet6 address %p\n", ifp);
+ return;
+ }
+ dst_release(&ifp->rt->u.dst);
+
+ kfree(ifp);
+}
+
+/* On success it returns ifp with increased reference count */
+
+static struct inet6_ifaddr *
+ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
+ int scope, unsigned flags)
+{
+ struct inet6_ifaddr *ifa = NULL;
+ struct rt6_info *rt;
+ int hash;
+ int err = 0;
+
+ read_lock_bh(&addrconf_lock);
+ if (idev->dead) {
+ err = -ENODEV; /*XXX*/
+ goto out2;
+ }
+
+ write_lock(&addrconf_hash_lock);
+
+ /* Ignore adding duplicate addresses on an interface */
+ if (ipv6_chk_same_addr(addr, idev->dev)) {
+ ADBG(("ipv6_add_addr: already assigned\n"));
+ err = -EEXIST;
+ goto out;
+ }
+
+ ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
+
+ if (ifa == NULL) {
+ ADBG(("ipv6_add_addr: malloc failed\n"));
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ rt = addrconf_dst_alloc(idev, addr, 0);
+ if (IS_ERR(rt)) {
+ err = PTR_ERR(rt);
+ goto out;
+ }
+
+ memset(ifa, 0, sizeof(struct inet6_ifaddr));
+ ipv6_addr_copy(&ifa->addr, addr);
+
+ spin_lock_init(&ifa->lock);
+ init_timer(&ifa->timer);
+ ifa->timer.data = (unsigned long) ifa;
+ ifa->scope = scope;
+ ifa->prefix_len = pfxlen;
+ ifa->flags = flags | IFA_F_TENTATIVE;
+ ifa->cstamp = ifa->tstamp = jiffies;
+
+ ifa->idev = idev;
+ in6_dev_hold(idev);
+ /* For caller */
+ in6_ifa_hold(ifa);
+
+ /* Add to big hash table */
+ hash = ipv6_addr_hash(addr);
+
+ ifa->lst_next = inet6_addr_lst[hash];
+ inet6_addr_lst[hash] = ifa;
+ in6_ifa_hold(ifa);
+ write_unlock(&addrconf_hash_lock);
+
+ write_lock(&idev->lock);
+ /* Add to inet6_dev unicast addr list. */
+ ifa->if_next = idev->addr_list;
+ idev->addr_list = ifa;
+
+#ifdef CONFIG_IPV6_PRIVACY
+ if (ifa->flags&IFA_F_TEMPORARY) {
+ ifa->tmp_next = idev->tempaddr_list;
+ idev->tempaddr_list = ifa;
+ in6_ifa_hold(ifa);
+ }
+#endif
+
+ ifa->rt = rt;
+
+ in6_ifa_hold(ifa);
+ write_unlock(&idev->lock);
+out2:
+ read_unlock_bh(&addrconf_lock);
+
+ if (unlikely(err == 0))
+ notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
+ else {
+ kfree(ifa);
+ ifa = ERR_PTR(err);
+ }
+
+ return ifa;
+out:
+ write_unlock(&addrconf_hash_lock);
+ goto out2;
+}
+
+/* This function wants to get referenced ifp and releases it before return */
+
+static void ipv6_del_addr(struct inet6_ifaddr *ifp)
+{
+ struct inet6_ifaddr *ifa, **ifap;
+ struct inet6_dev *idev = ifp->idev;
+ int hash;
+ int deleted = 0, onlink = 0;
+ unsigned long expires = jiffies;
+
+ hash = ipv6_addr_hash(&ifp->addr);
+
+ ifp->dead = 1;
+
+ write_lock_bh(&addrconf_hash_lock);
+ for (ifap = &inet6_addr_lst[hash]; (ifa=*ifap) != NULL;
+ ifap = &ifa->lst_next) {
+ if (ifa == ifp) {
+ *ifap = ifa->lst_next;
+ __in6_ifa_put(ifp);
+ ifa->lst_next = NULL;
+ break;
+ }
+ }
+ write_unlock_bh(&addrconf_hash_lock);
+
+ write_lock_bh(&idev->lock);
+#ifdef CONFIG_IPV6_PRIVACY
+ if (ifp->flags&IFA_F_TEMPORARY) {
+ for (ifap = &idev->tempaddr_li