summaryrefslogtreecommitdiff
path: root/net/ipv6/raw.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/raw.c')
-rw-r--r--net/ipv6/raw.c264
1 files changed, 149 insertions, 115 deletions
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 807260d03586..4d880551fe6a 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -54,39 +54,31 @@
#include <net/mip6.h>
#endif
+#include <net/raw.h>
#include <net/rawv6.h>
#include <net/xfrm.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-struct hlist_head raw_v6_htable[RAWV6_HTABLE_SIZE];
-DEFINE_RWLOCK(raw_v6_lock);
+static struct raw_hashinfo raw_v6_hashinfo = {
+ .lock = __RW_LOCK_UNLOCKED(),
+};
static void raw_v6_hash(struct sock *sk)
{
- struct hlist_head *list = &raw_v6_htable[inet_sk(sk)->num &
- (RAWV6_HTABLE_SIZE - 1)];
-
- write_lock_bh(&raw_v6_lock);
- sk_add_node(sk, list);
- sock_prot_inc_use(sk->sk_prot);
- write_unlock_bh(&raw_v6_lock);
+ raw_hash_sk(sk, &raw_v6_hashinfo);
}
static void raw_v6_unhash(struct sock *sk)
{
- write_lock_bh(&raw_v6_lock);
- if (sk_del_node_init(sk))
- sock_prot_dec_use(sk->sk_prot);
- write_unlock_bh(&raw_v6_lock);
+ raw_unhash_sk(sk, &raw_v6_hashinfo);
}
-/* 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,
- int dif)
+static struct sock *__raw_v6_lookup(struct net *net, struct sock *sk,
+ unsigned short num, struct in6_addr *loc_addr,
+ struct in6_addr *rmt_addr, int dif)
{
struct hlist_node *node;
int is_multicast = ipv6_addr_is_multicast(loc_addr);
@@ -95,6 +87,9 @@ struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
if (inet_sk(sk)->num == num) {
struct ipv6_pinfo *np = inet6_sk(sk);
+ if (sk->sk_net != net)
+ continue;
+
if (!ipv6_addr_any(&np->daddr) &&
!ipv6_addr_equal(&np->daddr, rmt_addr))
continue;
@@ -167,21 +162,22 @@ EXPORT_SYMBOL(rawv6_mh_filter_unregister);
*
* Caller owns SKB so we must make clones.
*/
-int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
+static int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
{
struct in6_addr *saddr;
struct in6_addr *daddr;
struct sock *sk;
int delivered = 0;
__u8 hash;
+ struct net *net;
saddr = &ipv6_hdr(skb)->saddr;
daddr = saddr + 1;
hash = nexthdr & (MAX_INET_PROTOS - 1);
- read_lock(&raw_v6_lock);
- sk = sk_head(&raw_v6_htable[hash]);
+ read_lock(&raw_v6_hashinfo.lock);
+ sk = sk_head(&raw_v6_hashinfo.ht[hash]);
/*
* The first socket found will be delivered after
@@ -191,7 +187,8 @@ int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
if (sk == NULL)
goto out;
- sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr, IP6CB(skb)->iif);
+ net = skb->dev->nd_net;
+ sk = __raw_v6_lookup(net, sk, nexthdr, daddr, saddr, IP6CB(skb)->iif);
while (sk) {
int filtered;
@@ -234,14 +231,25 @@ int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
rawv6_rcv(sk, clone);
}
}
- sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr,
+ sk = __raw_v6_lookup(net, sk_next(sk), nexthdr, daddr, saddr,
IP6CB(skb)->iif);
}
out:
- read_unlock(&raw_v6_lock);
+ read_unlock(&raw_v6_hashinfo.lock);
return delivered;
}
+int raw6_local_deliver(struct sk_buff *skb, int nexthdr)
+{
+ struct sock *raw_sk;
+
+ raw_sk = sk_head(&raw_v6_hashinfo.ht[nexthdr & (MAX_INET_PROTOS - 1)]);
+ if (raw_sk && !ipv6_raw_deliver(skb, nexthdr))
+ raw_sk = NULL;
+
+ return raw_sk != NULL;
+}
+
/* This cleans up af_inet6 a bit. -DaveM */
static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
@@ -283,7 +291,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (!sk->sk_bound_dev_if)
goto out;
- dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
+ dev = dev_get_by_index(sk->sk_net, sk->sk_bound_dev_if);
if (!dev) {
err = -ENODEV;
goto out;
@@ -296,7 +304,8 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) {
err = -EADDRNOTAVAIL;
- if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) {
+ if (!ipv6_chk_addr(sk->sk_net, &addr->sin6_addr,
+ dev, 0)) {
if (dev)
dev_put(dev);
goto out;
@@ -316,7 +325,7 @@ out:
return err;
}
-void rawv6_err(struct sock *sk, struct sk_buff *skb,
+static void rawv6_err(struct sock *sk, struct sk_buff *skb,
struct inet6_skb_parm *opt,
int type, int code, int offset, __be32 info)
{
@@ -350,18 +359,45 @@ void rawv6_err(struct sock *sk, struct sk_buff *skb,
}
}
+void raw6_icmp_error(struct sk_buff *skb, int nexthdr,
+ int type, int code, int inner_offset, __be32 info)
+{
+ struct sock *sk;
+ int hash;
+ struct in6_addr *saddr, *daddr;
+ struct net *net;
+
+ hash = nexthdr & (RAW_HTABLE_SIZE - 1);
+
+ read_lock(&raw_v6_hashinfo.lock);
+ sk = sk_head(&raw_v6_hashinfo.ht[hash]);
+ if (sk != NULL) {
+ saddr = &ipv6_hdr(skb)->saddr;
+ daddr = &ipv6_hdr(skb)->daddr;
+ net = skb->dev->nd_net;
+
+ while ((sk = __raw_v6_lookup(net, sk, nexthdr, saddr, daddr,
+ IP6CB(skb)->iif))) {
+ rawv6_err(sk, skb, NULL, type, code,
+ inner_offset, info);
+ sk = sk_next(sk);
+ }
+ }
+ read_unlock(&raw_v6_hashinfo.lock);
+}
+
static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
{
if ((raw6_sk(sk)->checksum || sk->sk_filter) &&
skb_checksum_complete(skb)) {
- /* FIXME: increment a raw6 drops counter here */
+ atomic_inc(&sk->sk_drops);
kfree_skb(skb);
return 0;
}
/* Charge it to the socket. */
if (sock_queue_rcv_skb(sk,skb)<0) {
- /* FIXME: increment a raw6 drops counter here */
+ atomic_inc(&sk->sk_drops);
kfree_skb(skb);
return 0;
}
@@ -382,6 +418,7 @@ int rawv6_rcv(struct sock *sk, struct sk_buff *skb)
struct raw6_sock *rp = raw6_sk(sk);
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {
+ atomic_inc(&sk->sk_drops);
kfree_skb(skb);
return NET_RX_DROP;
}
@@ -405,7 +442,7 @@ int rawv6_rcv(struct sock *sk, struct sk_buff *skb)
if (inet->hdrincl) {
if (skb_checksum_complete(skb)) {
- /* FIXME: increment a raw6 drops counter here */
+ atomic_inc(&sk->sk_drops);
kfree_skb(skb);
return 0;
}
@@ -496,7 +533,7 @@ csum_copy_err:
as some normal condition.
*/
err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
- /* FIXME: increment a raw6 drops counter here */
+ atomic_inc(&sk->sk_drops);
goto out;
}
@@ -618,7 +655,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
goto error_fault;
IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
- err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+ err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0;
@@ -843,7 +880,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = __xfrm_lookup(&dst, &fl, sk, 1)) < 0) {
+ if ((err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT)) < 0) {
if (err == -EREMOTE)
err = ip6_dst_blackhole(sk, &dst, &fl);
if (err < 0)
@@ -1172,76 +1209,6 @@ struct proto rawv6_prot = {
};
#ifdef CONFIG_PROC_FS
-struct raw6_iter_state {
- int bucket;
-};
-
-#define raw6_seq_private(seq) ((struct raw6_iter_state *)(seq)->private)
-
-static struct sock *raw6_get_first(struct seq_file *seq)
-{
- struct sock *sk;
- struct hlist_node *node;
- struct raw6_iter_state* state = raw6_seq_private(seq);
-
- for (state->bucket = 0; state->bucket < RAWV6_HTABLE_SIZE; ++state->bucket)
- sk_for_each(sk, node, &raw_v6_htable[state->bucket])
- if (sk->sk_family == PF_INET6)
- goto out;
- sk = NULL;
-out:
- return sk;
-}
-
-static struct sock *raw6_get_next(struct seq_file *seq, struct sock *sk)
-{
- struct raw6_iter_state* state = raw6_seq_private(seq);
-
- do {
- sk = sk_next(sk);
-try_again:
- ;
- } while (sk && sk->sk_family != PF_INET6);
-
- if (!sk && ++state->bucket < RAWV6_HTABLE_SIZE) {
- sk = sk_head(&raw_v6_htable[state->bucket]);
- goto try_again;
- }
- return sk;
-}
-
-static struct sock *raw6_get_idx(struct seq_file *seq, loff_t pos)
-{
- struct sock *sk = raw6_get_first(seq);
- if (sk)
- while (pos && (sk = raw6_get_next(seq, sk)) != NULL)
- --pos;
- return pos ? NULL : sk;
-}
-
-static void *raw6_seq_start(struct seq_file *seq, loff_t *pos)
-{
- read_lock(&raw_v6_lock);
- return *pos ? raw6_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
-}
-
-static void *raw6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- struct sock *sk;
-
- if (v == SEQ_START_TOKEN)
- sk = raw6_get_first(seq);
- else
- sk = raw6_get_next(seq, v);
- ++*pos;
- return sk;
-}
-
-static void raw6_seq_stop(struct seq_file *seq, void *v)
-{
- read_unlock(&raw_v6_lock);
-}
-
static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
{
struct ipv6_pinfo *np = inet6_sk(sp);
@@ -1254,7 +1221,7 @@ static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
srcp = inet_sk(sp)->num;
seq_printf(seq,
"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p\n",
+ "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n",
i,
src->s6_addr32[0], src->s6_addr32[1],
src->s6_addr32[2], src->s6_addr32[3], srcp,
@@ -1266,7 +1233,7 @@ static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
0, 0L, 0,
sock_i_uid(sp), 0,
sock_i_ino(sp),
- atomic_read(&sp->sk_refcnt), sp);
+ atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));
}
static int raw6_seq_show(struct seq_file *seq, void *v)
@@ -1277,23 +1244,22 @@ static int raw6_seq_show(struct seq_file *seq, void *v)
"local_address "
"remote_address "
"st tx_queue rx_queue tr tm->when retrnsmt"
- " uid timeout inode\n");
+ " uid timeout inode drops\n");
else
- raw6_sock_seq_show(seq, v, raw6_seq_private(seq)->bucket);
+ raw6_sock_seq_show(seq, v, raw_seq_private(seq)->bucket);
return 0;
}
static const struct seq_operations raw6_seq_ops = {
- .start = raw6_seq_start,
- .next = raw6_seq_next,
- .stop = raw6_seq_stop,
+ .start = raw_seq_start,
+ .next = raw_seq_next,
+ .stop = raw_seq_stop,
.show = raw6_seq_show,
};
static int raw6_seq_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &raw6_seq_ops,
- sizeof(struct raw6_iter_state));
+ return raw_seq_open(inode, file, &raw_v6_hashinfo, PF_INET6);
}
static const struct file_operations raw6_seq_fops = {
@@ -1301,18 +1267,86 @@ static const struct file_operations raw6_seq_fops = {
.open = raw6_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
-int __init raw6_proc_init(void)
+static int raw6_init_net(struct net *net)
{
- if (!proc_net_fops_create(&init_net, "raw6", S_IRUGO, &raw6_seq_fops))
+ if (!proc_net_fops_create(net, "raw6", S_IRUGO, &raw6_seq_fops))
return -ENOMEM;
+
return 0;
}
+static void raw6_exit_net(struct net *net)
+{
+ proc_net_remove(net, "raw6");
+}
+
+static struct pernet_operations raw6_net_ops = {
+ .init = raw6_init_net,
+ .exit = raw6_exit_net,
+};
+
+int __init raw6_proc_init(void)
+{
+ return register_pernet_subsys(&raw6_net_ops);
+}
+
void raw6_proc_exit(void)
{
- proc_net_remove(&init_net, "raw6");
+ unregister_pernet_subsys(&raw6_net_ops);
}
#endif /* CONFIG_PROC_FS */
+
+/* Same as inet6_dgram_ops, sans udp_poll. */
+static const struct proto_ops inet6_sockraw_ops = {
+ .family = PF_INET6,
+ .owner = THIS_MODULE,
+ .release = inet6_release,
+ .bind = inet6_bind,
+ .connect = inet_dgram_connect, /* ok */
+ .socketpair = sock_no_socketpair, /* a do nothing */
+ .accept = sock_no_accept, /* a do nothing */
+ .getname = inet6_getname,
+ .poll = datagram_poll, /* ok */
+ .ioctl = inet6_ioctl, /* must change */
+ .listen = sock_no_listen, /* ok */
+ .shutdown = inet_shutdown, /* ok */
+ .setsockopt = sock_common_setsockopt, /* ok */
+ .getsockopt = sock_common_getsockopt, /* ok */
+ .sendmsg = inet_sendmsg, /* ok */
+ .recvmsg = sock_common_recvmsg, /* ok */
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_sock_common_setsockopt,
+ .compat_getsockopt = compat_sock_common_getsockopt,
+#endif
+};
+
+static struct inet_protosw rawv6_protosw = {
+ .type = SOCK_RAW,
+ .protocol = IPPROTO_IP, /* wild card */
+ .prot = &rawv6_prot,
+ .ops = &inet6_sockraw_ops,
+ .capability = CAP_NET_RAW,
+ .no_check = UDP_CSUM_DEFAULT,
+ .flags = INET_PROTOSW_REUSE,
+};
+
+int __init rawv6_init(void)
+{
+ int ret;
+
+ ret = inet6_register_protosw(&rawv6_protosw);
+ if (ret)
+ goto out;
+out:
+ return ret;
+}
+
+void rawv6_exit(void)
+{
+ inet6_unregister_protosw(&rawv6_protosw);
+}