summaryrefslogtreecommitdiffstats
path: root/net/ipv6/icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/icmp.c')
-rw-r--r--net/ipv6/icmp.c79
1 files changed, 63 insertions, 16 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 1abc87541..211db2544 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.22 1999/05/19 22:06:39 davem Exp $
+ * $Id: icmp.c,v 1.24 1999/08/20 11:06:18 davem Exp $
*
* Based on net/ipv4/icmp.c
*
@@ -88,6 +88,42 @@ struct icmpv6_msg {
};
+static int icmpv6_xmit_holder = -1;
+
+static int icmpv6_xmit_lock_bh(void)
+{
+ if (!spin_trylock(&icmpv6_socket->sk->lock.slock)) {
+ if (icmpv6_xmit_holder == smp_processor_id())
+ return -EAGAIN;
+ spin_lock(&icmpv6_socket->sk->lock.slock);
+ }
+ icmpv6_xmit_holder = smp_processor_id();
+ return 0;
+}
+
+static __inline__ int icmpv6_xmit_lock(void)
+{
+ int ret;
+ local_bh_disable();
+ ret = icmpv6_xmit_lock_bh();
+ if (ret)
+ local_bh_enable();
+ return ret;
+}
+
+static void icmpv6_xmit_unlock_bh(void)
+{
+ icmpv6_xmit_holder = -1;
+ spin_unlock(&icmpv6_socket->sk->lock.slock);
+}
+
+static __inline__ void icmpv6_xmit_unlock(void)
+{
+ icmpv6_xmit_unlock_bh();
+ local_bh_enable();
+}
+
+
/*
* getfrag callback
@@ -237,7 +273,7 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
*/
void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
- struct device *dev)
+ struct net_device *dev)
{
struct ipv6hdr *hdr = skb->nh.ipv6h;
struct sock *sk = icmpv6_socket->sk;
@@ -267,7 +303,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
addr_type = ipv6_addr_type(&hdr->daddr);
- if (ipv6_chk_addr(&hdr->daddr, skb->dev, 0))
+ if (ipv6_chk_addr(&hdr->daddr, skb->dev))
saddr = &hdr->daddr;
/*
@@ -319,8 +355,11 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
fl.uli_u.icmpt.type = type;
fl.uli_u.icmpt.code = code;
- if (!icmpv6_xrlim_allow(sk, type, &fl))
- return;
+ if (icmpv6_xmit_lock())
+ return;
+
+ if (!icmpv6_xrlim_allow(sk, type, &fl))
+ goto out;
/*
* ok. kick it. checksum will be provided by the
@@ -341,7 +380,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
if (len < 0) {
printk(KERN_DEBUG "icmp: len problem\n");
- return;
+ goto out;
}
msg.len = len;
@@ -351,6 +390,8 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
(&icmpv6_statistics.Icmp6OutDestUnreachs)[type-1]++;
icmpv6_statistics.Icmp6OutMsgs++;
+out:
+ icmpv6_xmit_unlock();
}
static void icmpv6_echo_reply(struct sk_buff *skb)
@@ -393,10 +434,15 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
fl.uli_u.icmpt.code = 0;
+ if (icmpv6_xmit_lock_bh())
+ return;
+
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
MSG_DONTWAIT);
icmpv6_statistics.Icmp6OutEchoReplies++;
icmpv6_statistics.Icmp6OutMsgs++;
+
+ icmpv6_xmit_unlock_bh();
}
static void icmpv6_notify(struct sk_buff *skb,
@@ -431,6 +477,7 @@ static void icmpv6_notify(struct sk_buff *skb,
hash = nexthdr & (MAX_INET_PROTOS - 1);
+ read_lock(&inet6_protocol_lock);
for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
ipprot != NULL;
ipprot=(struct inet6_protocol *)ipprot->next) {
@@ -440,16 +487,16 @@ static void icmpv6_notify(struct sk_buff *skb,
if (ipprot->err_handler)
ipprot->err_handler(skb, hdr, NULL, type, code, pb, info);
}
+ read_unlock(&inet6_protocol_lock);
- sk = raw_v6_htable[hash];
-
- if (sk == NULL)
- return;
-
- while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
- rawv6_err(sk, skb, hdr, NULL, type, code, pb, info);
- sk = sk->next;
+ read_lock(&raw_v6_lock);
+ if ((sk = raw_v6_htable[hash]) != NULL) {
+ while((sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
+ rawv6_err(sk, skb, hdr, NULL, type, code, pb, info);
+ sk = sk->next;
+ }
}
+ read_unlock(&raw_v6_lock);
}
/*
@@ -458,7 +505,7 @@ static void icmpv6_notify(struct sk_buff *skb,
int icmpv6_rcv(struct sk_buff *skb, unsigned long len)
{
- struct device *dev = skb->dev;
+ struct net_device *dev = skb->dev;
struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
struct ipv6hdr *orig_hdr;
@@ -615,7 +662,7 @@ int __init icmpv6_init(struct net_proto_family *ops)
sk = icmpv6_socket->sk;
sk->allocation = GFP_ATOMIC;
- sk->num = 256; /* Don't receive any data */
+ sk->prot->unhash(sk);
inet6_add_protocol(&icmpv6_protocol);