diff options
Diffstat (limited to 'net/ipv6/raw.c')
-rw-r--r-- | net/ipv6/raw.c | 196 |
1 files changed, 139 insertions, 57 deletions
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index f6c0a42ac..b13ccd164 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.27 1999/07/02 11:26:40 davem Exp $ + * $Id: raw.c,v 1.29 1999/08/20 11:06:26 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,16 +36,19 @@ #include <net/ip6_route.h> #include <net/addrconf.h> #include <net/transp_v6.h> +#include <net/udp.h> +#include <net/inet_common.h> #include <net/rawv6.h> struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE]; +rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED; static void raw_v6_hash(struct sock *sk) { struct sock **skp = &raw_v6_htable[sk->num & (RAWV6_HTABLE_SIZE - 1)]; - SOCKHASH_LOCK_WRITE(); + write_lock_bh(&raw_v6_lock); if ((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; @@ -53,33 +56,34 @@ static void raw_v6_hash(struct sock *sk) sk->prot->inuse++; if(sk->prot->highestinuse < sk->prot->inuse) sk->prot->highestinuse = sk->prot->inuse; - SOCKHASH_UNLOCK_WRITE(); + sock_hold(sk); + write_unlock_bh(&raw_v6_lock); } static void raw_v6_unhash(struct sock *sk) { - SOCKHASH_LOCK_WRITE(); + write_lock_bh(&raw_v6_lock); if (sk->pprev) { if (sk->next) sk->next->pprev = sk->pprev; *sk->pprev = sk->next; sk->pprev = NULL; sk->prot->inuse--; + __sock_put(sk); } - SOCKHASH_UNLOCK_WRITE(); + write_unlock_bh(&raw_v6_lock); } /* Grumble... icmp and ip_input want to get at this... */ -struct sock *raw_v6_lookup(struct sock *sk, unsigned short num, - struct in6_addr *loc_addr, struct in6_addr *rmt_addr) +struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num, + struct in6_addr *loc_addr, struct in6_addr *rmt_addr) { struct sock *s = sk; int addr_type = ipv6_addr_type(loc_addr); for(s = sk; s; s = s->next) { - if((s->num == num) && - !(s->dead && (s->state == TCP_CLOSE))) { + if(s->num == num) { struct ipv6_pinfo *np = &s->net_pinfo.af_inet6; if (!ipv6_addr_any(&np->daddr) && @@ -88,56 +92,136 @@ struct sock *raw_v6_lookup(struct sock *sk, unsigned short num, if (!ipv6_addr_any(&np->rcv_saddr)) { if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0) - return(s); + break; if ((addr_type & IPV6_ADDR_MULTICAST) && inet6_mc_check(s, loc_addr)) - return (s); + break; continue; } - return(s); + break; + } + } + return s; +} + +/* + * 0 - deliver + * 1 - block + */ +static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb) +{ + struct icmp6hdr *icmph; + struct raw6_opt *opt; + + opt = &sk->tp_pinfo.tp_raw; + icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1); + return test_bit(icmph->icmp6_type, &opt->filter); +} + +/* + * demultiplex raw sockets. + * (should consider queueing the skb in the sock receive_queue + * without calling rawv6.c) + */ +struct sock * ipv6_raw_deliver(struct sk_buff *skb, + int nexthdr, unsigned long len) +{ + struct in6_addr *saddr; + struct in6_addr *daddr; + struct sock *sk, *sk2; + __u8 hash; + + saddr = &skb->nh.ipv6h->saddr; + daddr = saddr + 1; + + hash = nexthdr & (MAX_INET_PROTOS - 1); + + read_lock(&raw_v6_lock); + sk = raw_v6_htable[hash]; + + /* + * The first socket found will be delivered after + * delivery to transport protocols. + */ + + if (sk == NULL) + goto out; + + sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr); + + if (sk) { + sk2 = sk; + + while ((sk2 = __raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) { + struct sk_buff *buff; + + if (nexthdr == IPPROTO_ICMPV6 && + icmpv6_filter(sk2, skb)) + continue; + + buff = skb_clone(skb, GFP_ATOMIC); + if (buff) + rawv6_rcv(sk2, buff, len); } } - return NULL; + + if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb)) + sk = NULL; + +out: + if (sk) + sock_hold(sk); + read_unlock(&raw_v6_lock); + return sk; } + + /* This cleans up af_inet6 a bit. -DaveM */ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; __u32 v4addr = 0; int addr_type; + int err; - /* Check these errors. */ - if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6))) + if (addr_len < sizeof(struct sockaddr_in6)) return -EINVAL; addr_type = ipv6_addr_type(&addr->sin6_addr); - /* Check if the address belongs to the host. */ - if (addr_type == IPV6_ADDR_MAPPED) { - /* Raw sockets are IPv6 only */ + /* Raw sockets are IPv6 only */ + if (addr_type == IPV6_ADDR_MAPPED) return(-EADDRNOTAVAIL); - } else { - if (addr_type != IPV6_ADDR_ANY) { - /* ipv4 addr of the socket is invalid. Only the - * unpecified and mapped address have a v4 equivalent. - */ - v4addr = LOOPBACK4_IPV6; - if (!(addr_type & IPV6_ADDR_MULTICAST)) { - if (ipv6_chk_addr(&addr->sin6_addr, NULL, 0) == NULL) - return(-EADDRNOTAVAIL); - } + + lock_sock(sk); + + err = -EINVAL; + if (sk->state != TCP_CLOSE) + goto out; + + /* Check if the address belongs to the host. */ + if (addr_type != IPV6_ADDR_ANY) { + /* ipv4 addr of the socket is invalid. Only the + * unpecified and mapped address have a v4 equivalent. + */ + v4addr = LOOPBACK4_IPV6; + if (!(addr_type & IPV6_ADDR_MULTICAST)) { + err = -EADDRNOTAVAIL; + if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) + goto out; } } sk->rcv_saddr = v4addr; sk->saddr = v4addr; - memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr, - sizeof(struct in6_addr)); + ipv6_addr_copy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr); if (!(addr_type & IPV6_ADDR_MULTICAST)) - memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr, - sizeof(struct in6_addr)); - return 0; + ipv6_addr_copy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr); + err = 0; +out: + release_sock(sk); + return err; } void rawv6_err(struct sock *sk, struct sk_buff *skb, struct ipv6hdr *hdr, @@ -193,7 +277,7 @@ static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb) */ int rawv6_rcv(struct sock *sk, struct sk_buff *skb, unsigned long len) { - if (sk->ip_hdrincl) + if (sk->protinfo.af_inet.hdrincl) skb->h.raw = skb->nh.raw; rawv6_rcv_skb(sk, skb); @@ -341,9 +425,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) /* Mirror BSD error message compatibility */ if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; - - if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT)) - return(-EINVAL); + /* * Get and verify the address. */ @@ -590,15 +672,10 @@ 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); if (sk->num == IPPROTO_RAW) ip6_ra_control(sk, -1, NULL); - sk->dead = 1; - destroy_sock(sk); + + inet_sock_release(sk); } static int rawv6_init_sk(struct sock *sk) @@ -606,6 +683,9 @@ static int rawv6_init_sk(struct sock *sk) return(0); } +#define LINE_LEN 190 +#define LINE_FMT "%-190s\n" + static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i) { struct in6_addr *dest, *src; @@ -615,13 +695,13 @@ static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i) dest = &sp->net_pinfo.af_inet6.daddr; src = &sp->net_pinfo.af_inet6.rcv_saddr; - destp = ntohs(sp->dport); - srcp = ntohs(sp->sport); + destp = 0; + srcp = sp->num; timer_active = (sp->timer.prev != NULL) ? 2 : 0; timer_expires = (timer_active == 2 ? sp->timer.expires : jiffies); sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld %d %p", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, @@ -630,8 +710,9 @@ static void get_raw6_sock(struct sock *sp, char *tmpbuf, int i) sp->state, atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc), timer_active, timer_expires-jiffies, 0, - sp->socket->inode->i_uid, timer_active ? sp->timeout : 0, - sp->socket ? sp->socket->inode->i_ino : 0); + sp->socket->inode->i_uid, 0, + sp->socket ? sp->socket->inode->i_ino : 0, + atomic_read(&sp->refcnt), sp); } int raw6_get_info(char *buffer, char **start, off_t offset, int length, int dummy) @@ -639,10 +720,10 @@ int raw6_get_info(char *buffer, char **start, off_t offset, int length, int dumm int len = 0, num = 0, i; off_t pos = 0; off_t begin; - char tmpbuf[150]; + char tmpbuf[LINE_LEN+2]; - if (offset < 149) - len += sprintf(buffer, "%-148s\n", + if (offset < LINE_LEN+1) + len += sprintf(buffer, LINE_FMT, " sl " /* 6 */ "local_address " /* 38 */ "remote_address " /* 38 */ @@ -650,25 +731,25 @@ int raw6_get_info(char *buffer, char **start, off_t offset, int length, int dumm " uid timeout inode"); /* 21 */ /*----*/ /*144 */ - pos = 149; - SOCKHASH_LOCK_READ(); + pos = LINE_LEN+1; + read_lock(&raw_v6_lock); for (i = 0; i < RAWV6_HTABLE_SIZE; i++) { struct sock *sk; for (sk = raw_v6_htable[i]; sk; sk = sk->next, num++) { if (sk->family != PF_INET6) continue; - pos += 149; + pos += LINE_LEN+1; if (pos < offset) continue; get_raw6_sock(sk, tmpbuf, i); - len += sprintf(buffer+len, "%-148s\n", tmpbuf); + len += sprintf(buffer+len, LINE_FMT, tmpbuf); if(len >= length) goto out; } } out: - SOCKHASH_UNLOCK_READ(); + read_unlock(&raw_v6_lock); begin = len - (pos - offset); *start = buffer + begin; len -= begin; @@ -682,6 +763,7 @@ out: struct proto rawv6_prot = { rawv6_close, /* close */ udpv6_connect, /* connect */ + udp_disconnect, /* disconnect */ NULL, /* accept */ NULL, /* retransmit */ NULL, /* write_wakeup */ |