#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);
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;
*
* 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
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;
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)
{
v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) {
err = -EADDRNOTAVAIL;
- if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) {
+ if (!ipv6_chk_addr(&init_net, &addr->sin6_addr,
+ dev, 0)) {
if (dev)
dev_put(dev);
goto 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)
{
}
}
+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) &&
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)
};
#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);
"st tx_queue rx_queue tr tm->when retrnsmt"
" 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 = {
.open = raw6_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
int __init raw6_proc_init(void)
proc_net_remove(&init_net, "raw6");
}
#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);
+}