diff options
Diffstat (limited to 'net/ipv4/raw.c')
-rw-r--r-- | net/ipv4/raw.c | 94 |
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: |