diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/addrconf.c | 22 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 4 | ||||
-rw-r--r-- | net/ipv6/exthdrs.c | 4 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 4 | ||||
-rw-r--r-- | net/ipv6/ip6_fw.c | 5 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 21 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 98 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 52 | ||||
-rw-r--r-- | net/ipv6/proc.c | 15 | ||||
-rw-r--r-- | net/ipv6/raw.c | 27 | ||||
-rw-r--r-- | net/ipv6/reassembly.c | 5 | ||||
-rw-r--r-- | net/ipv6/route.c | 15 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 128 | ||||
-rw-r--r-- | net/ipv6/udp.c | 34 |
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, |