summaryrefslogtreecommitdiffstats
path: root/net/ipv4/raw.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/raw.c')
-rw-r--r--net/ipv4/raw.c94
1 files changed, 82 insertions, 12 deletions
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index fc6b1f2ee..dd2e7555e 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -5,7 +5,7 @@
*
* RAW - implementation of IP "raw" sockets.
*
- * Version: $Id: raw.c,v 1.39 1998/11/08 11:17:04 davem Exp $
+ * Version: $Id: raw.c,v 1.41 1999/05/30 01:16:19 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -75,11 +75,11 @@ static void raw_v4_hash(struct sock *sk)
num &= (RAWV4_HTABLE_SIZE - 1);
skp = &raw_v4_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
sk->next = *skp;
*skp = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v4_unhash(struct sock *sk)
@@ -90,7 +90,7 @@ static void raw_v4_unhash(struct sock *sk)
num &= (RAWV4_HTABLE_SIZE - 1);
skp = &raw_v4_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
@@ -98,7 +98,7 @@ static void raw_v4_unhash(struct sock *sk)
}
skp = &((*skp)->next);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v4_rehash(struct sock *sk)
@@ -110,7 +110,7 @@ static void raw_v4_rehash(struct sock *sk)
num &= (RAWV4_HTABLE_SIZE - 1);
skp = &raw_v4_htable[oldnum];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
@@ -121,16 +121,15 @@ static void raw_v4_rehash(struct sock *sk)
sk->next = raw_v4_htable[num];
raw_v4_htable[num] = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
-/* Grumble... icmp and ip_input want to get at this... */
-struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
- unsigned long raddr, unsigned long laddr, int dif)
+static __inline__ struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num,
+ unsigned long raddr, unsigned long laddr,
+ int dif)
{
struct sock *s = sk;
- SOCKHASH_LOCK();
for(s = sk; s; s = s->next) {
if((s->num == num) &&
!(s->dead && (s->state == TCP_CLOSE)) &&
@@ -139,10 +138,79 @@ struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
!(s->bound_dev_if && s->bound_dev_if != dif))
break; /* gotcha */
}
- SOCKHASH_UNLOCK();
return s;
}
+struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+ unsigned long raddr, unsigned long laddr,
+ int dif)
+{
+ SOCKHASH_LOCK_READ();
+ sk = __raw_v4_lookup(sk, num, raddr, laddr, dif);
+ SOCKHASH_UNLOCK_READ();
+
+ return sk;
+}
+
+/*
+ * 0 - deliver
+ * 1 - block
+ */
+static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
+{
+ int type;
+
+ type = skb->h.icmph->type;
+ if (type < 32)
+ return test_bit(type, &sk->tp_pinfo.tp_raw4.filter);
+
+ /* Do not block unknown ICMP types */
+ return 0;
+}
+
+/* IP input processing comes here for RAW socket delivery.
+ * This is fun as to avoid copies we want to make no surplus
+ * copies.
+ *
+ * RFC 1122: SHOULD pass TOS value up to the transport layer.
+ * -> It does. And not only TOS, but all IP header.
+ */
+struct sock *raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash)
+{
+ struct sock *sk;
+
+ SOCKHASH_LOCK_READ_BH();
+ if ((sk = raw_v4_htable[hash]) == NULL)
+ goto out;
+ sk = __raw_v4_lookup(sk, iph->protocol,
+ iph->saddr, iph->daddr,
+ skb->dev->ifindex);
+ while(sk != NULL) {
+ struct sock *sknext = __raw_v4_lookup(sk->next, iph->protocol,
+ iph->saddr, iph->daddr,
+ skb->dev->ifindex);
+
+ if (iph->protocol != IPPROTO_ICMP ||
+ ! icmp_filter(sk, skb)) {
+ struct sk_buff *clone;
+
+ if(sknext == NULL)
+ break;
+ clone = skb_clone(skb, GFP_ATOMIC);
+ if(clone) {
+ SOCKHASH_UNLOCK_READ_BH();
+ raw_rcv(sk, clone);
+ SOCKHASH_LOCK_READ_BH();
+ }
+ }
+ sk = sknext;
+ }
+out:
+ SOCKHASH_UNLOCK_READ_BH();
+
+ return sk;
+}
+
void raw_err (struct sock *sk, struct sk_buff *skb)
{
int type = skb->h.icmph->type;
@@ -402,6 +470,8 @@ done:
static void raw_close(struct sock *sk, long timeout)
{
+ bh_lock_sock(sk);
+
/* Observation: when raw_close is called, processes have
no access to socket anymore. But net still has.
Step one, detach it from networking: