summaryrefslogtreecommitdiffstats
path: root/net/ipv6/raw.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/raw.c')
-rw-r--r--net/ipv6/raw.c196
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 */