summaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/addrconf.c22
-rw-r--r--net/ipv6/af_inet6.c4
-rw-r--r--net/ipv6/exthdrs.c4
-rw-r--r--net/ipv6/icmp.c4
-rw-r--r--net/ipv6/ip6_fw.c5
-rw-r--r--net/ipv6/ip6_output.c21
-rw-r--r--net/ipv6/mcast.c98
-rw-r--r--net/ipv6/ndisc.c52
-rw-r--r--net/ipv6/proc.c15
-rw-r--r--net/ipv6/raw.c27
-rw-r--r--net/ipv6/reassembly.c5
-rw-r--r--net/ipv6/route.c15
-rw-r--r--net/ipv6/tcp_ipv6.c128
-rw-r--r--net/ipv6/udp.c34
14 files changed, 264 insertions, 170 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f34975076..9f71f7cda 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: addrconf.c,v 1.48 1999/03/25 10:04:43 davem Exp $
+ * $Id: addrconf.c,v 1.50 1999/06/09 10:11:09 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -100,9 +100,7 @@ static struct timer_list addr_chk_timer = {
1. The result of inet6_add_addr() is used only inside lock
or from bh_atomic context.
- 2. inet6_get_lladdr() is used only from bh protected context.
-
- 3. The result of ipv6_chk_addr() is not used outside of bh protected context.
+ 2. The result of ipv6_chk_addr() is not used outside of bh protected context.
*/
static __inline__ void addrconf_lock(void)
@@ -463,7 +461,7 @@ out:
return err;
}
-struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
+int ipv6_get_lladdr(struct device *dev, struct in6_addr *addr)
{
struct inet6_ifaddr *ifp = NULL;
struct inet6_dev *idev;
@@ -471,12 +469,15 @@ struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
if ((idev = ipv6_get_idev(dev)) != NULL) {
addrconf_lock();
for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
- if (ifp->scope == IFA_LINK)
- break;
+ if (ifp->scope == IFA_LINK) {
+ ipv6_addr_copy(addr, &ifp->addr);
+ addrconf_unlock();
+ return 0;
+ }
}
addrconf_unlock();
}
- return ifp;
+ return -EADDRNOTAVAIL;
}
/*
@@ -982,6 +983,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
return;
}
+ read_lock(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
if (dev->ip_ptr && (dev->flags & IFF_UP)) {
struct in_device * in_dev = dev->ip_ptr;
@@ -1014,6 +1016,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
}
}
}
+ read_unlock(&dev_base_lock);
}
static void init_loopback(struct device *dev)
@@ -1842,7 +1845,7 @@ __initfunc(void addrconf_init(void))
struct device *dev;
/* This takes sense only during module load. */
-
+ read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
if (!(dev->flags&IFF_UP))
continue;
@@ -1858,6 +1861,7 @@ __initfunc(void addrconf_init(void))
/* Ignore all other */
}
}
+ read_unlock(&dev_base_lock);
#endif
#ifdef CONFIG_PROC_FS
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 36ab229ed..f7f50df86 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/af_inet.c
*
- * $Id: af_inet6.c,v 1.43 1999/04/22 10:07:39 davem Exp $
+ * $Id: af_inet6.c,v 1.44 1999/06/09 08:29:29 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -103,7 +103,7 @@ static int inet6_create(struct socket *sock, int protocol)
if (protocol && protocol != IPPROTO_UDP)
goto free_and_noproto;
protocol = IPPROTO_UDP;
- sk->no_check = UDP_NO_CHECK;
+ sk->no_check = UDP_CSUM_DEFAULT;
prot=&udpv6_prot;
sock->ops = &inet6_dgram_ops;
} else if(sock->type == SOCK_RAW) {
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 8a4f85b6c..5fb915390 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -7,7 +7,7 @@
* Andi Kleen <ak@muc.de>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*
- * $Id: exthdrs.c,v 1.8 1998/10/03 09:38:27 davem Exp $
+ * $Id: exthdrs.c,v 1.9 1999/05/17 23:47:35 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -369,7 +369,7 @@ ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
Certainly, it is possible only for udp and raw sockets, but not for tcp.
AUTH header has 4byte granular length, which kills all the idea
- behind AUTOMATIC 64bit alignment of IPv6. Now we will loose
+ behind AUTOMATIC 64bit alignment of IPv6. Now we will lose
cpu ticks, checking that sender did not something stupid
and opt->hdrlen is even. Shit! --ANK (980730)
*/
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 3760be8eb..1abc87541 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: icmp.c,v 1.21 1999/03/21 05:22:51 davem Exp $
+ * $Id: icmp.c,v 1.22 1999/05/19 22:06:39 davem Exp $
*
* Based on net/ipv4/icmp.c
*
@@ -315,6 +315,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
fl.nl_u.ip6_u.daddr = &hdr->saddr;
fl.nl_u.ip6_u.saddr = saddr;
fl.oif = iif;
+ fl.fl6_flowlabel = 0;
fl.uli_u.icmpt.type = type;
fl.uli_u.icmpt.code = code;
@@ -388,6 +389,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl.nl_u.ip6_u.daddr = &hdr->saddr;
fl.nl_u.ip6_u.saddr = saddr;
fl.oif = skb->dev->ifindex;
+ fl.fl6_flowlabel = 0;
fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
fl.uli_u.icmpt.code = 0;
diff --git a/net/ipv6/ip6_fw.c b/net/ipv6/ip6_fw.c
index c19a561e9..a6263d41c 100644
--- a/net/ipv6/ip6_fw.c
+++ b/net/ipv6/ip6_fw.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_fw.c,v 1.10 1998/08/26 12:04:57 davem Exp $
+ * $Id: ip6_fw.c,v 1.12 1999/06/09 08:29:32 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -16,6 +16,7 @@
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/net.h>
@@ -382,7 +383,7 @@ __initfunc(void ip6_fw_init(void))
}
#ifdef MODULE
-void module_cleanup(void)
+void cleanup_module(void)
{
#ifdef CONFIG_NETLINK
netlink_detach(NETLINK_IP6_FW);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 26ec51c4d..9a635f882 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_output.c,v 1.17 1999/04/22 10:07:42 davem Exp $
+ * $Id: ip6_output.c,v 1.20 1999/06/09 10:11:12 davem Exp $
*
* Based on linux/net/ipv4/ip_output.c
*
@@ -20,11 +20,13 @@
* route changes now work.
* ip6_forward does not confuse sniffers.
* etc.
- *
+ *
+ * H. von Brand : Added missing #include <linux/string.h>
*/
#include <linux/errno.h>
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/netdevice.h>
@@ -73,19 +75,10 @@ int ip6_output(struct sk_buff *skb)
}
if (hh) {
-#ifdef __alpha__
- /* Alpha has disguisting memcpy. Help it. */
- u64 *aligned_hdr = (u64*)(skb->data - 16);
- u64 *aligned_hdr0 = hh->hh_data;
- read_lock_irq(&hh->hh_lock);
- aligned_hdr[0] = aligned_hdr0[0];
- aligned_hdr[1] = aligned_hdr0[1];
-#else
- read_lock_irq(&hh->hh_lock);
+ read_lock_bh(&hh->hh_lock);
memcpy(skb->data - 16, hh->hh_data, 16);
-#endif
- read_unlock_irq(&hh->hh_lock);
- skb_push(skb, dev->hard_header_len);
+ read_unlock_bh(&hh->hh_lock);
+ skb_push(skb, hh->hh_len);
return hh->hh_output(skb);
} else if (dst->neighbour)
return dst->neighbour->output(skb);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 939d268da..cedf9e691 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: mcast.c,v 1.19 1999/03/25 10:04:50 davem Exp $
+ * $Id: mcast.c,v 1.23 1999/06/09 10:11:14 davem Exp $
*
* Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
*
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/sched.h>
@@ -52,6 +53,11 @@
#define MDBG(x)
#endif
+/* Big mc list lock for all the devices */
+static rwlock_t ipv6_mc_lock = RW_LOCK_UNLOCKED;
+/* Big mc list lock for all the sockets */
+static rwlock_t ipv6_sk_mc_lock = RW_LOCK_UNLOCKED;
+
static struct socket *igmp6_socket;
static void igmp6_join_group(struct ifmcaddr6 *ma);
@@ -114,8 +120,10 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
return err;
}
+ write_lock_bh(&ipv6_sk_mc_lock);
mc_lst->next = np->ipv6_mc_list;
np->ipv6_mc_list = mc_lst;
+ write_unlock_bh(&ipv6_sk_mc_lock);
return 0;
}
@@ -128,13 +136,14 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct ipv6_mc_socklist *mc_lst, **lnk;
+ write_lock_bh(&ipv6_sk_mc_lock);
for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
if (mc_lst->ifindex == ifindex &&
ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
struct device *dev;
*lnk = mc_lst->next;
- synchronize_bh();
+ write_unlock_bh(&ipv6_sk_mc_lock);
if ((dev = dev_get_by_index(ifindex)) != NULL)
ipv6_dev_mc_dec(dev, &mc_lst->addr);
@@ -142,6 +151,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
return 0;
}
}
+ write_unlock_bh(&ipv6_sk_mc_lock);
return -ENOENT;
}
@@ -151,15 +161,38 @@ void ipv6_sock_mc_close(struct sock *sk)
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct ipv6_mc_socklist *mc_lst;
+ write_lock_bh(&ipv6_sk_mc_lock);
while ((mc_lst = np->ipv6_mc_list) != NULL) {
- struct device *dev = dev_get_by_index(mc_lst->ifindex);
+ struct device *dev;
+
+ np->ipv6_mc_list = mc_lst->next;
+ write_unlock_bh(&ipv6_sk_mc_lock);
+ dev = dev_get_by_index(mc_lst->ifindex);
if (dev)
ipv6_dev_mc_dec(dev, &mc_lst->addr);
- np->ipv6_mc_list = mc_lst->next;
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+
+ write_lock_bh(&ipv6_sk_mc_lock);
+ }
+ write_unlock_bh(&ipv6_sk_mc_lock);
+}
+
+int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+ struct ipv6_mc_socklist *mc;
+
+ read_lock(&ipv6_sk_mc_lock);
+ for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+ if (ipv6_addr_cmp(&mc->addr, addr) == 0) {
+ read_unlock(&ipv6_sk_mc_lock);
+ return 1;
+ }
}
+ read_unlock(&ipv6_sk_mc_lock);
+
+ return 0;
}
static int igmp6_group_added(struct ifmcaddr6 *mc)
@@ -209,9 +242,11 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
hash = ipv6_addr_hash(addr);
+ write_lock_bh(&ipv6_mc_lock);
for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) {
atomic_inc(&mc->mca_users);
+ write_unlock_bh(&ipv6_mc_lock);
return 0;
}
}
@@ -222,8 +257,10 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
- if (mc == NULL)
+ if (mc == NULL) {
+ write_unlock_bh(&ipv6_mc_lock);
return -ENOMEM;
+ }
memset(mc, 0, sizeof(struct ifmcaddr6));
mc->mca_timer.function = igmp6_timer_handler;
@@ -241,6 +278,8 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
igmp6_group_added(mc);
+ write_unlock_bh(&ipv6_mc_lock);
+
return 0;
}
@@ -256,7 +295,6 @@ static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma)
for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
if (iter == ma) {
*lnk = iter->if_next;
- synchronize_bh();
return;
}
}
@@ -273,20 +311,22 @@ int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
hash = ipv6_addr_hash(addr);
+ write_lock_bh(&ipv6_mc_lock);
for (lnk = &inet6_mcast_lst[hash]; (ma=*lnk) != NULL; lnk = &ma->next) {
if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) {
if (atomic_dec_and_test(&ma->mca_users)) {
igmp6_group_dropped(ma);
*lnk = ma->next;
- synchronize_bh();
ipv6_mca_remove(dev, ma);
kfree(ma);
}
+ write_unlock_bh(&ipv6_mc_lock);
return 0;
}
}
+ write_unlock_bh(&ipv6_mc_lock);
return -ENOENT;
}
@@ -301,10 +341,14 @@ int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
hash = ipv6_addr_hash(addr);
+ read_lock_bh(&ipv6_mc_lock);
for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
- if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
+ if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0) {
+ read_unlock_bh(&ipv6_mc_lock);
return 1;
+ }
}
+ read_unlock_bh(&ipv6_mc_lock);
return 0;
}
@@ -363,11 +407,14 @@ int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
if (idev == NULL)
return 0;
+ read_lock(&ipv6_mc_lock);
for (ma = idev->mc_list; ma; ma=ma->if_next)
igmp6_group_queried(ma, resptime);
+ read_unlock(&ipv6_mc_lock);
} else {
int hash = ipv6_addr_hash(addrp);
+ read_lock(&ipv6_mc_lock);
for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
if (ma->dev == skb->dev &&
ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
@@ -375,6 +422,7 @@ int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
break;
}
}
+ read_unlock(&ipv6_mc_lock);
}
return 0;
@@ -409,6 +457,7 @@ int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
hash = ipv6_addr_hash(addrp);
+ read_lock(&ipv6_mc_lock);
for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
if (ma->mca_flags & MAF_TIMER_RUNNING) {
@@ -420,6 +469,7 @@ int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
break;
}
}
+ read_unlock(&ipv6_mc_lock);
return 0;
}
@@ -429,9 +479,9 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
struct sock *sk = igmp6_socket->sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
- struct inet6_ifaddr *ifp;
struct in6_addr *snd_addr;
struct in6_addr *addrp;
+ struct in6_addr addr_buf;
struct in6_addr all_routers;
int err, len, payload_len, full_len;
u8 ra[8] = { IPPROTO_ICMPV6, 0,
@@ -460,9 +510,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len);
}
- ifp = ipv6_get_lladdr(dev);
-
- if (ifp == NULL) {
+ if (ipv6_get_lladdr(dev, &addr_buf)) {
#if MCAST_DEBUG >= 1
printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
dev->name);
@@ -470,7 +518,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
return;
}
- ip6_nd_hdr(sk, skb, dev, &ifp->addr, snd_addr, NEXTHDR_HOP, payload_len);
+ ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len);
memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
@@ -481,7 +529,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
ipv6_addr_copy(addrp, addr);
- hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, snd_addr, len,
+ hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len,
IPPROTO_ICMPV6,
csum_partial((__u8 *) hdr, len, 0));
@@ -503,7 +551,6 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)))
return;
- start_bh_atomic();
igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
delay = net_random() % IGMP6_UNSOLICITED_IVAL;
@@ -514,7 +561,6 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
add_timer(&ma->mca_timer);
ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
- end_bh_atomic();
}
static void igmp6_leave_group(struct ifmcaddr6 *ma)
@@ -526,22 +572,22 @@ static void igmp6_leave_group(struct ifmcaddr6 *ma)
if ((addr_type & IPV6_ADDR_LINKLOCAL))
return;
- start_bh_atomic();
if (ma->mca_flags & MAF_LAST_REPORTER)
igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
if (ma->mca_flags & MAF_TIMER_RUNNING)
del_timer(&ma->mca_timer);
- end_bh_atomic();
}
void igmp6_timer_handler(unsigned long data)
{
struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
+ read_lock(&ipv6_mc_lock);
ma->mca_flags |= MAF_LAST_REPORTER;
igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
ma->mca_flags &= ~MAF_TIMER_RUNNING;
+ read_unlock(&ipv6_mc_lock);
}
/* Device going down */
@@ -553,8 +599,10 @@ void ipv6_mc_down(struct inet6_dev *idev)
/* Withdraw multicast list */
+ read_lock_bh(&ipv6_mc_lock);
for (i = idev->mc_list; i; i=i->if_next)
igmp6_group_dropped(i);
+ read_unlock_bh(&ipv6_mc_lock);
/* Delete all-nodes address. */
@@ -576,8 +624,10 @@ void ipv6_mc_up(struct inet6_dev *idev)
/* Install multicast list, except for all-nodes (already installed) */
+ read_lock(&ipv6_mc_lock);
for (i = idev->mc_list; i; i=i->if_next)
igmp6_group_added(i);
+ read_unlock(&ipv6_mc_lock);
}
/*
@@ -589,6 +639,7 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
int hash;
struct ifmcaddr6 *i, **lnk;
+ write_lock_bh(&ipv6_mc_lock);
while ((i = idev->mc_list) != NULL) {
idev->mc_list = i->if_next;
@@ -597,13 +648,13 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) {
if (*lnk == i) {
*lnk = i->next;
- synchronize_bh();
break;
}
}
igmp6_group_dropped(i);
kfree(i);
}
+ write_unlock_bh(&ipv6_mc_lock);
}
#ifdef CONFIG_PROC_FS
@@ -615,12 +666,14 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset,
int len=0;
struct device *dev;
+ read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
struct inet6_dev *idev;
if ((idev = ipv6_get_idev(dev)) == NULL)
continue;
+ read_lock_bh(&ipv6_mc_lock);
for (im = idev->mc_list; im; im = im->if_next) {
int i;
@@ -640,13 +693,18 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset,
len=0;
begin=pos;
}
- if (pos > offset+length)
+ if (pos > offset+length) {
+ read_unlock_bh(&ipv6_mc_lock);
goto done;
+ }
}
+ read_unlock_bh(&ipv6_mc_lock);
}
*eof = 1;
done:
+ read_unlock(&dev_base_lock);
+
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index bb5e08373..d0613056a 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -268,14 +268,21 @@ ndisc_build_ll_hdr(struct sk_buff *skb, struct device *dev,
ndisc_mc_map(daddr, ha, dev, 1);
h_dest = ha;
} else if (neigh) {
- h_dest = neigh->ha;
+ read_lock_bh(&neigh->lock);
+ if (neigh->nud_state&NUD_VALID) {
+ memcpy(ha, neigh->ha, dev->addr_len);
+ h_dest = ha;
+ }
+ read_unlock_bh(&neigh->lock);
} else {
neigh = neigh_lookup(&nd_tbl, daddr, dev);
if (neigh) {
+ read_lock_bh(&neigh->lock);
if (neigh->nud_state&NUD_VALID) {
memcpy(ha, neigh->ha, dev->addr_len);
h_dest = ha;
}
+ read_unlock_bh(&neigh->lock);
neigh_release(neigh);
}
}
@@ -362,6 +369,7 @@ void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
+ struct in6_addr addr_buf;
int len;
int err;
@@ -377,13 +385,8 @@ void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
}
if (saddr == NULL) {
- struct inet6_ifaddr *ifa;
-
- /* use link local address */
- ifa = ipv6_get_lladdr(dev);
-
- if (ifa)
- saddr = &ifa->addr;
+ if (!ipv6_get_lladdr(dev, &addr_buf))
+ saddr = &addr_buf;
}
if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
@@ -501,13 +504,15 @@ static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
kfree_skb(skb);
}
+/* Called with locked neigh: either read or both */
+
static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
{
struct in6_addr *saddr = NULL;
struct in6_addr mcaddr;
struct device *dev = neigh->dev;
struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
- int probes = neigh->probes;
+ int probes = atomic_read(&neigh->probes);
if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 0))
saddr = &skb->nh.ipv6h->saddr;
@@ -774,8 +779,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
struct sock *sk = ndisc_socket->sk;
int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
struct sk_buff *buff;
- struct inet6_ifaddr *ifp;
struct icmp6hdr *icmph;
+ struct in6_addr saddr_buf;
struct in6_addr *addrp;
struct device *dev;
struct rt6_info *rt;
@@ -817,12 +822,10 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
rd_len &= ~0x7;
len += rd_len;
- ifp = ipv6_get_lladdr(dev);
-
- if (ifp == NULL) {
- ND_PRINTK1("redirect: no link_local addr for dev\n");
- return;
- }
+ if (ipv6_get_lladdr(dev, &saddr_buf)) {
+ ND_PRINTK1("redirect: no link_local addr for dev\n");
+ return;
+ }
buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
0, 0, &err);
@@ -838,7 +841,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
return;
}
- ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+ ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr,
IPPROTO_ICMPV6, len);
icmph = (struct icmp6hdr *) skb_put(buff, len);
@@ -875,7 +878,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
memcpy(opt, skb->nh.ipv6h, rd_len - 8);
- icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+ icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &skb->nh.ipv6h->saddr,
len, IPPROTO_ICMPV6,
csum_partial((u8 *) icmph, len, 0));
@@ -1034,7 +1037,7 @@ int ndisc_rcv(struct sk_buff *skb, unsigned long len)
ifp->idev->dev->name);
return 0;
}
- neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 0);
+ neigh = neigh_lookup(&nd_tbl, &msg->target, skb->dev);
if (neigh) {
if (neigh->flags & NTF_ROUTER) {
@@ -1083,11 +1086,10 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum
unsigned long now = jiffies;
int i;
- neigh_table_lock(&nd_tbl);
-
for (i = 0; i <= NEIGH_HASHMASK; i++) {
struct neighbour *neigh;
+ read_lock_bh(&nd_tbl.lock);
for (neigh = nd_tbl.hash_buckets[i]; neigh; neigh = neigh->next) {
int j;
@@ -1097,6 +1099,7 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum
size += 2;
}
+ read_lock(&neigh->lock);
size += sprintf(buffer+len+size,
" %02x %02x %02x %02x %08lx %08lx %08x %04x %04x %04x %8s ", i,
128,
@@ -1118,19 +1121,22 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum
} else {
size += sprintf(buffer+len+size, "000000000000");
}
+ read_unlock(&neigh->lock);
size += sprintf(buffer+len+size, "\n");
len += size;
pos += size;
if (pos <= offset)
len=0;
- if (pos >= offset+length)
+ if (pos >= offset+length) {
+ read_unlock_bh(&nd_tbl.lock);
goto done;
+ }
}
+ read_unlock_bh(&nd_tbl.lock);
}
done:
- neigh_table_unlock(&nd_tbl);
*start = buffer+len-(pos-offset); /* Start of wanted data */
len = pos-offset; /* Start slop */
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index 31f6a2f55..b83bdc34b 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -7,7 +7,7 @@
* PROC file system. This is very similar to the IPv4 version,
* except it reports the sockets in the INET6 address family.
*
- * Version: $Id: proc.c,v 1.9 1998/08/26 12:05:11 davem Exp $
+ * Version: $Id: proc.c,v 1.10 1999/05/27 00:38:14 davem Exp $
*
* Authors: David S. Miller (davem@caip.rutgers.edu)
*
@@ -52,7 +52,7 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
/*144 */
pos = 149;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
sp = pro->sklist_next;
while(sp != (struct sock *)pro) {
struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp;
@@ -72,6 +72,7 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
}
destp = ntohs(sp->dport);
srcp = ntohs(sp->sport);
+
if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
extern int tcp_tw_death_row_slot;
int slot_dist;
@@ -85,10 +86,8 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
slot_dist = tcp_tw_death_row_slot - slot_dist;
timer_expires = jiffies + (slot_dist * TCP_TWKILL_PERIOD);
} else {
- timer_active1 = del_timer(&tp->retransmit_timer);
- timer_active2 = del_timer(&sp->timer);
- if(!timer_active1) tp->retransmit_timer.expires = 0;
- if(!timer_active2) sp->timer.expires = 0;
+ timer_active1 = tp->retransmit_timer.prev != NULL;
+ timer_active2 = sp->timer.prev != NULL;
timer_active = 0;
timer_expires = (unsigned) -1;
}
@@ -128,8 +127,6 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
((!tw_bucket && sp->socket) ?
sp->socket->inode->i_ino : 0));
- if(timer_active1) add_timer(&tp->retransmit_timer);
- if(timer_active2) add_timer(&sp->timer);
len += sprintf(buffer+len, "%-148s\n", tmpbuf);
if(len >= length)
break;
@@ -137,7 +134,7 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta
sp = sp->sklist_next;
i++;
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
begin = len - (pos - offset);
*start = buffer + begin;
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index f82ac33db..70394dc03 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/raw.c
*
- * $Id: raw.c,v 1.24 1999/04/22 10:07:45 davem Exp $
+ * $Id: raw.c,v 1.26 1999/06/09 10:11:18 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -50,11 +50,11 @@ static void raw_v6_hash(struct sock *sk)
num &= (RAWV6_HTABLE_SIZE - 1);
skp = &raw_v6_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
sk->next = *skp;
*skp = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v6_unhash(struct sock *sk)
@@ -65,7 +65,7 @@ static void raw_v6_unhash(struct sock *sk)
num &= (RAWV6_HTABLE_SIZE - 1);
skp = &raw_v6_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
@@ -73,7 +73,7 @@ static void raw_v6_unhash(struct sock *sk)
}
skp = &((*skp)->next);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v6_rehash(struct sock *sk)
@@ -85,7 +85,7 @@ static void raw_v6_rehash(struct sock *sk)
num &= (RAWV6_HTABLE_SIZE - 1);
skp = &raw_v6_htable[oldnum];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
@@ -96,20 +96,9 @@ static void raw_v6_rehash(struct sock *sk)
sk->next = raw_v6_htable[num];
raw_v6_htable[num] = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
-static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
- struct ipv6_mc_socklist *mc;
-
- for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
- if (ipv6_addr_cmp(&mc->addr, addr) == 0)
- return 1;
- }
-
- return 0;
-}
/* Grumble... icmp and ip_input want to get at this... */
struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
@@ -631,6 +620,8 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
static void rawv6_close(struct sock *sk, long timeout)
{
+ bh_lock_sock(sk);
+
/* See for explanation: raw_close in ipv4/raw.c */
sk->state = TCP_CLOSE;
raw_v6_unhash(sk);
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index e455b0533..74cf4571b 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: reassembly.c,v 1.11 1998/08/26 12:05:16 davem Exp $
+ * $Id: reassembly.c,v 1.13 1999/06/09 08:29:40 davem Exp $
*
* Based on: net/ipv4/ip_fragment.c
*
@@ -19,9 +19,12 @@
* Fixes:
* Andi Kleen Make it work with multiple hosts.
* More RFC compliance.
+ *
+ * Horst von Brand Add missing #include <linux/string.h>
*/
#include <linux/errno.h>
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/sched.h>
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 04b49d843..5f8ff914b 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: route.c,v 1.35 1999/03/21 05:22:57 davem Exp $
+ * $Id: route.c,v 1.36 1999/06/09 10:11:21 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -1607,7 +1607,7 @@ static int fib6_dump_node(struct fib6_walker_t *w)
return 0;
}
-static int fib6_dump_done(struct netlink_callback *cb)
+static void fib6_dump_end(struct netlink_callback *cb)
{
struct fib6_walker_t *w = (void*)cb->args[0];
@@ -1622,6 +1622,11 @@ static int fib6_dump_done(struct netlink_callback *cb)
cb->done = (void*)cb->args[1];
cb->args[1] = 0;
}
+}
+
+static int fib6_dump_done(struct netlink_callback *cb)
+{
+ fib6_dump_end(cb);
return cb->done(cb);
}
@@ -1668,11 +1673,15 @@ int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
if (res <= 0 && skb->len == 0)
RT6_TRACE("%p>dump end\n", w);
#endif
+ res = res < 0 ? res : skb->len;
/* res < 0 is an error. (really, impossible)
res == 0 means that dump is complete, but skb still can contain data.
res > 0 dump is not complete, but frame is full.
*/
- return res < 0 ? res : skb->len;
+ /* Destroy walker, if dump of this table is complete. */
+ if (res <= 0)
+ fib6_dump_end(cb);
+ return res;
}
int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f1ef74de8..2164e245e 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.104 1999/04/24 00:27:25 davem Exp $
+ * $Id: tcp_ipv6.c,v 1.108 1999/06/09 08:29:43 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
@@ -67,7 +67,7 @@ static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
int hashent = (lport ^ fport);
hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
- return (hashent & ((TCP_HTABLE_SIZE/2) - 1));
+ return (hashent & ((tcp_ehash_size >> 1) - 1));
}
static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
@@ -89,8 +89,8 @@ static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
struct tcp_bind_bucket *tb;
int result = 0;
- SOCKHASH_LOCK();
- for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ SOCKHASH_LOCK_WRITE();
+ for(tb = tcp_bhash[tcp_bhashfn(snum)];
(tb && (tb->port != snum));
tb = tb->next)
;
@@ -156,7 +156,7 @@ static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
}
}
go_like_smoke:
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
return result;
}
@@ -172,20 +172,20 @@ static void tcp_v6_hash(struct sock *sk)
if(sk->state != TCP_CLOSE) {
struct sock **skp;
- SOCKHASH_LOCK();
- skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+ SOCKHASH_LOCK_WRITE();
+ skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
*skp = sk;
sk->pprev = skp;
tcp_sk_bindify(sk);
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
}
static void tcp_v6_unhash(struct sock *sk)
{
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
if(sk->pprev) {
if(sk->next)
sk->next->pprev = sk->pprev;
@@ -194,14 +194,14 @@ static void tcp_v6_unhash(struct sock *sk)
tcp_sk_unbindify(sk);
tcp_reg_zap(sk);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void tcp_v6_rehash(struct sock *sk)
{
unsigned char state;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
state = sk->state;
if(sk->pprev != NULL) {
if(sk->next)
@@ -216,7 +216,7 @@ static void tcp_v6_rehash(struct sock *sk)
if(state == TCP_LISTEN)
skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
else
- skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+ skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
@@ -225,7 +225,7 @@ static void tcp_v6_rehash(struct sock *sk)
if(state == TCP_LISTEN)
tcp_sk_bindify(sk);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif)
@@ -264,10 +264,10 @@ static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned shor
/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
- * It is assumed that this code only gets called from within NET_BH.
+ *
+ * The sockhash lock must be held as a reader here.
*/
-static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
- struct in6_addr *saddr, u16 sport,
+static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport,
struct in6_addr *daddr, u16 dport,
int dif)
{
@@ -285,7 +285,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
* have wildcards anyways.
*/
hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
- for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
+ for(sk = tcp_ehash[hash]; sk; sk = sk->next) {
/* For IPV6 do the cheaper port and family tests first. */
if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) {
if (sk->state == TCP_ESTABLISHED)
@@ -294,7 +294,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
}
}
/* Must check for a TIME_WAIT'er before going to listener hash. */
- for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
+ for(sk = tcp_ehash[hash+(tcp_ehash_size >> 1)]; sk; sk = sk->next) {
if(*((__u32 *)&(sk->dport)) == ports &&
sk->family == PF_INET6) {
struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
@@ -309,7 +309,13 @@ hit:
return sk;
}
-#define tcp_v6_lookup(sa, sp, da, dp, dif) __tcp_v6_lookup((0),(sa),(sp),(da),(dp),(dif))
+#define tcp_v6_lookup(sa, sp, da, dp, dif) \
+({ struct sock *___sk; \
+ SOCKHASH_LOCK_READ(); \
+ ___sk = __tcp_v6_lookup((sa),(sp),(da),(dp),(dif)); \
+ SOCKHASH_UNLOCK_READ(); \
+ ___sk; \
+})
static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
struct in6_addr *saddr,
@@ -344,24 +350,26 @@ static int tcp_v6_unique_address(struct sock *sk)
int retval = 1;
/* Freeze the hash while we snoop around. */
- SOCKHASH_LOCK();
- tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ SOCKHASH_LOCK_READ();
+ tb = tcp_bhash[tcp_bhashfn(snum)];
for(; tb; tb = tb->next) {
if(tb->port == snum && tb->owners != NULL) {
/* Almost certainly the re-use port case, search the real hashes
* so it actually scales. (we hope that all ipv6 ftp servers will
* use passive ftp, I just cover this case for completeness)
*/
- sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr,
+ sk = __tcp_v6_lookup(&sk->net_pinfo.af_inet6.daddr,
sk->dport,
&sk->net_pinfo.af_inet6.rcv_saddr, snum,
sk->bound_dev_if);
+ SOCKHASH_UNLOCK_READ();
+
if((sk != NULL) && (sk->state != TCP_LISTEN))
retval = 0;
- break;
+ return retval;
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return retval;
}
@@ -551,7 +559,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
failure:
dst_release(xchg(&sk->dst_cache, NULL));
- memcpy(&np->daddr, 0, sizeof(struct in6_addr));
+ memset(&np->daddr, 0, sizeof(struct in6_addr));
sk->daddr = 0;
return err;
}
@@ -628,11 +636,14 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
if (type == ICMPV6_PKT_TOOBIG) {
struct dst_entry *dst = NULL;
- if (atomic_read(&sk->sock_readers))
+ if (sk->state == TCP_LISTEN)
return;
- if (sk->state == TCP_LISTEN)
+ bh_lock_sock(sk);
+ if(sk->lock.users) {
+ bh_unlock_sock(sk);
return;
+ }
/* icmp should have updated the destination cache entry */
if (sk->dst_cache)
@@ -664,7 +675,7 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
tcp_simple_retransmit(sk);
} /* else let the usual retransmit timer handle it */
dst_release(dst);
- return;
+ bh_unlock_sock(sk);
}
icmpv6_err_convert(type, code, &err);
@@ -674,11 +685,13 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
struct open_request *req, *prev;
struct ipv6hdr hd;
case TCP_LISTEN:
- if (atomic_read(&sk->sock_readers)) {
+ bh_lock_sock(sk);
+ if (sk->lock.users) {
net_statistics.LockDroppedIcmps++;
/* If too many ICMPs get dropped on busy
* servers this needs to be solved differently.
*/
+ bh_unlock_sock(sk);
return;
}
@@ -686,20 +699,22 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
ipv6_addr_copy(&hd.saddr, saddr);
ipv6_addr_copy(&hd.daddr, daddr);
req = tcp_v6_search_req(tp, &hd, th, tcp_v6_iif(skb), &prev);
- if (!req)
- return;
- if (seq != req->snt_isn) {
+ if (!req || (seq != req->snt_isn)) {
net_statistics.OutOfWindowIcmps++;
+ bh_unlock_sock(sk);
return;
}
if (req->sk) {
+ bh_unlock_sock(sk);
sk = req->sk; /* report error in accept */
} else {
tp->syn_backlog--;
tcp_synq_unlink(tp, req, prev);
req->class->destructor(req);
tcp_openreq_free(req);
+ bh_unlock_sock(sk);
}
+
/* FALL THROUGH */
case TCP_SYN_SENT:
case TCP_SYN_RECV: /* Cannot happen */
@@ -1210,12 +1225,20 @@ static inline struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb)
return sk;
}
+/* The socket must have it's spinlock held when we get
+ * here.
+ *
+ * We have a potential double-lock case here, so even when
+ * doing backlog processing we use the BH locking scheme.
+ * This is because we cannot sleep with the original spinlock
+ * held.
+ */
static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
{
#ifdef CONFIG_FILTER
struct sk_filter *filter;
#endif
- int users = 0;
+ int users = 0, need_unlock = 0;
/* Imagine: socket is IPv6. IPv4 packet arrives,
goes to IPv4 receive handler and backlogged.
@@ -1286,19 +1309,24 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
* otherwise we just shortcircuit this and continue with
* the new socket..
*/
- if (atomic_read(&nsk->sock_readers)) {
- skb_orphan(skb);
- __skb_queue_tail(&nsk->back_log, skb);
- return 0;
+ if(nsk != sk) {
+ bh_lock_sock(nsk);
+ if (nsk->lock.users) {
+ skb_orphan(skb);
+ sk_add_backlog(nsk, skb);
+ bh_unlock_sock(nsk);
+ return 0;
+ }
+ need_unlock = 1;
+ sk = nsk;
}
- sk = nsk;
}
if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
goto reset;
if (users)
goto ipv6_pktoptions;
- return 0;
+ goto out_maybe_unlock;
reset:
tcp_v6_send_reset(skb);
@@ -1306,7 +1334,7 @@ discard:
if (users)
kfree_skb(skb);
kfree_skb(skb);
- return 0;
+ goto out_maybe_unlock;
ipv6_pktoptions:
/* Do you ask, what is it?
@@ -1335,6 +1363,9 @@ ipv6_pktoptions:
if (skb)
kfree_skb(skb);
+out_maybe_unlock:
+ if (need_unlock)
+ bh_unlock_sock(sk);
return 0;
}
@@ -1344,6 +1375,7 @@ int tcp_v6_rcv(struct sk_buff *skb, unsigned long len)
struct sock *sk;
struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ int ret;
th = skb->h.th;
@@ -1383,7 +1415,9 @@ int tcp_v6_rcv(struct sk_buff *skb, unsigned long len)
/* CHECKSUM_UNNECESSARY */
};
- sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+ SOCKHASH_LOCK_READ_BH();
+ sk = __tcp_v6_lookup(saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+ SOCKHASH_UNLOCK_READ_BH();
if (!sk)
goto no_tcp_socket;
@@ -1396,11 +1430,15 @@ int tcp_v6_rcv(struct sk_buff *skb, unsigned long len)
if(sk->state == TCP_TIME_WAIT)
goto do_time_wait;
- if (!atomic_read(&sk->sock_readers))
- return tcp_v6_do_rcv(sk, skb);
+ bh_lock_sock(sk);
+ ret = 0;
+ if (!sk->lock.users)
+ ret = tcp_v6_do_rcv(sk, skb);
+ else
+ sk_add_backlog(sk, skb);
+ bh_unlock_sock(sk);
- __skb_queue_tail(&sk->back_log, skb);
- return(0);
+ return ret;
no_tcp_socket:
tcp_v6_send_reset(skb);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 5b4d55f9e..da020d8fb 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -7,7 +7,7 @@
*
* Based on linux/ipv4/udp.c
*
- * $Id: udp.c,v 1.40 1999/05/08 20:00:32 davem Exp $
+ * $Id: udp.c,v 1.42 1999/06/09 10:11:24 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -55,7 +55,7 @@ static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
int retval = 0, sk_reuse = sk->reuse;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
if((sk2->num == snum) && (sk2 != sk)) {
unsigned char state = sk2->state;
@@ -86,7 +86,7 @@ static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
}
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return retval;
}
@@ -98,11 +98,11 @@ static void udp_v6_hash(struct sock *sk)
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
sk->next = *skp;
*skp = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void udp_v6_unhash(struct sock *sk)
@@ -113,7 +113,7 @@ static void udp_v6_unhash(struct sock *sk)
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
@@ -121,7 +121,7 @@ static void udp_v6_unhash(struct sock *sk)
}
skp = &((*skp)->next);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void udp_v6_rehash(struct sock *sk)
@@ -133,7 +133,7 @@ static void udp_v6_rehash(struct sock *sk)
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[oldnum];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
@@ -144,7 +144,7 @@ static void udp_v6_rehash(struct sock *sk)
sk->next = udp_hash[num];
udp_hash[num] = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
@@ -154,6 +154,7 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
unsigned short hnum = ntohs(dport);
int badness = -1;
+ SOCKHASH_LOCK_READ();
for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
if((sk->num == hnum) &&
(sk->family == PF_INET6) &&
@@ -189,6 +190,7 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
}
}
}
+ SOCKHASH_UNLOCK_READ();
return result;
}
@@ -331,6 +333,8 @@ ipv4_connected:
static void udpv6_close(struct sock *sk, long timeout)
{
+ bh_lock_sock(sk);
+
/* See for explanation: raw_close in ipv4/raw.c */
sk->state = TCP_CLOSE;
udp_v6_unhash(sk);
@@ -498,18 +502,6 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
return 0;
}
-static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
- struct ipv6_mc_socklist *mc;
-
- for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
- if (ipv6_addr_cmp(&mc->addr, addr) == 0)
- return 1;
- }
-
- return 0;
-}
-
static struct sock *udp_v6_mcast_next(struct sock *sk,
u16 loc_port, struct in6_addr *loc_addr,
u16 rmt_port, struct in6_addr *rmt_addr,