#include <linux/icmpv6.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/skbuff.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
+#include <net/ip.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <net/ndisc.h>
#include <net/protocol.h>
#include <net/ip6_route.h>
+#include <net/ip6_checksum.h>
#include <net/addrconf.h>
#include <net/transp_v6.h>
#include <net/udp.h>
#include <net/inet_common.h>
+#include <net/tcp_states.h>
+#ifdef CONFIG_IPV6_MIP6
+#include <net/mip6.h>
+#endif
#include <net/rawv6.h>
#include <net/xfrm.h>
#include <linux/seq_file.h>
struct hlist_head raw_v6_htable[RAWV6_HTABLE_SIZE];
-rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(raw_v6_lock);
static void raw_v6_hash(struct sock *sk)
{
/* 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 in6_addr *loc_addr, struct in6_addr *rmt_addr,
+ int dif)
{
struct hlist_node *node;
int is_multicast = ipv6_addr_is_multicast(loc_addr);
struct ipv6_pinfo *np = inet6_sk(sk);
if (!ipv6_addr_any(&np->daddr) &&
- ipv6_addr_cmp(&np->daddr, rmt_addr))
+ !ipv6_addr_equal(&np->daddr, rmt_addr))
+ continue;
+
+ if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
continue;
if (!ipv6_addr_any(&np->rcv_saddr)) {
- if (!ipv6_addr_cmp(&np->rcv_saddr, loc_addr))
+ if (ipv6_addr_equal(&np->rcv_saddr, loc_addr))
goto found;
if (is_multicast &&
inet6_mc_check(sk, loc_addr, rmt_addr))
static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
{
struct icmp6hdr *icmph;
- struct raw6_opt *opt = raw6_sk(sk);
+ struct raw6_sock *rp = raw6_sk(sk);
if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) {
- __u32 *data = &opt->filter.data[0];
+ __u32 *data = &rp->filter.data[0];
int bit_nr;
icmph = (struct icmp6hdr *) skb->data;
*
* Caller owns SKB so we must make clones.
*/
-void ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
+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;
saddr = &skb->nh.ipv6h->saddr;
if (sk == NULL)
goto out;
- sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr);
+ sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr, IP6CB(skb)->iif);
while (sk) {
- if (nexthdr != IPPROTO_ICMPV6 || !icmpv6_filter(sk, skb)) {
+ int filtered;
+
+ delivered = 1;
+ switch (nexthdr) {
+ case IPPROTO_ICMPV6:
+ filtered = icmpv6_filter(sk, skb);
+ break;
+#ifdef CONFIG_IPV6_MIP6
+ case IPPROTO_MH:
+ /* XXX: To validate MH only once for each packet,
+ * this is placed here. It should be after checking
+ * xfrm policy, however it doesn't. The checking xfrm
+ * policy is placed in rawv6_rcv() because it is
+ * required for each socket.
+ */
+ filtered = mip6_mh_filter(sk, skb);
+ break;
+#endif
+ default:
+ filtered = 0;
+ break;
+ }
+
+ if (filtered < 0)
+ break;
+ if (filtered == 0) {
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
/* Not releasing hash table! */
- if (clone)
+ if (clone) {
+ nf_reset(clone);
rawv6_rcv(sk, clone);
+ }
}
- sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr);
+ sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr,
+ IP6CB(skb)->iif);
}
out:
read_unlock(&raw_v6_lock);
+ return delivered;
}
/* This cleans up af_inet6 a bit. -DaveM */
static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
- __u32 v4addr = 0;
+ __be32 v4addr = 0;
int addr_type;
int err;
void rawv6_err(struct sock *sk, struct sk_buff *skb,
struct inet6_skb_parm *opt,
- int type, int code, int offset, u32 info)
+ int type, int code, int offset, __be32 info)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
int err;
int harderr;
static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
{
- if (sk->sk_filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
- if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
- /* FIXME: increment a raw6 drops counter here */
- kfree_skb(skb);
- return 0;
- }
- skb->ip_summed = CHECKSUM_UNNECESSARY;
+ if ((raw6_sk(sk)->checksum || sk->sk_filter) &&
+ skb_checksum_complete(skb)) {
+ /* FIXME: increment a raw6 drops counter here */
+ kfree_skb(skb);
+ return 0;
}
/* Charge it to the socket. */
*/
int rawv6_rcv(struct sock *sk, struct sk_buff *skb)
{
- struct inet_opt *inet = inet_sk(sk);
- struct raw6_opt *raw_opt = raw6_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+ struct raw6_sock *rp = raw6_sk(sk);
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return NET_RX_DROP;
}
- if (!raw_opt->checksum)
+ if (!rp->checksum)
skb->ip_summed = CHECKSUM_UNNECESSARY;
- if (skb->ip_summed != CHECKSUM_UNNECESSARY) {
- if (skb->ip_summed == CHECKSUM_HW) {
+ if (skb->ip_summed == CHECKSUM_COMPLETE) {
+ skb_postpull_rcsum(skb, skb->nh.raw,
+ skb->h.raw - skb->nh.raw);
+ if (!csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+ &skb->nh.ipv6h->daddr,
+ skb->len, inet->num, skb->csum))
skb->ip_summed = CHECKSUM_UNNECESSARY;
- if (csum_ipv6_magic(&skb->nh.ipv6h->saddr,
- &skb->nh.ipv6h->daddr,
- skb->len, inet->num, skb->csum)) {
- LIMIT_NETDEBUG(
- printk(KERN_DEBUG "raw v6 hw csum failure.\n"));
- skb->ip_summed = CHECKSUM_NONE;
- }
- }
- if (skb->ip_summed == CHECKSUM_NONE)
- skb->csum = ~csum_ipv6_magic(&skb->nh.ipv6h->saddr,
- &skb->nh.ipv6h->daddr,
- skb->len, inet->num, 0);
}
+ if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+ skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+ &skb->nh.ipv6h->daddr,
+ skb->len, inet->num, 0));
if (inet->hdrincl) {
- if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
- (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
+ if (skb_checksum_complete(skb)) {
/* FIXME: increment a raw6 drops counter here */
kfree_skb(skb);
return 0;
}
- skb->ip_summed = CHECKSUM_UNNECESSARY;
}
rawv6_rcv_skb(sk, skb);
if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
} else if (msg->msg_flags&MSG_TRUNC) {
- if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
+ if (__skb_checksum_complete(skb))
goto csum_copy_err;
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
} else {
/* Copy the address. */
if (sin6) {
sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
sin6->sin6_flowinfo = 0;
sin6->sin6_scope_id = 0;
- if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
- struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
- sin6->sin6_scope_id = opt->iif;
- }
+ if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin6->sin6_scope_id = IP6CB(skb)->iif;
}
sock_recv_timestamp(msg, sk, skb);
if (np->rxopt.all)
datagram_recv_ctl(sk, msg, skb);
+
err = copied;
+ if (flags & MSG_TRUNC)
+ err = skb->len;
out_free:
skb_free_datagram(sk, skb);
return err;
csum_copy_err:
- /* Clear queue. */
- if (flags&MSG_PEEK) {
- int clear = 0;
- spin_lock_irq(&sk->sk_receive_queue.lock);
- if (skb == skb_peek(&sk->sk_receive_queue)) {
- __skb_unlink(skb, &sk->sk_receive_queue);
- clear = 1;
- }
- spin_unlock_irq(&sk->sk_receive_queue.lock);
- if (clear)
- kfree_skb(skb);
- }
+ skb_kill_datagram(sk, skb, flags);
/* Error for blocking case is chosen to masquerade
as some normal condition.
*/
err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
/* FIXME: increment a raw6 drops counter here */
- goto out_free;
+ goto out;
}
-static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct raw6_opt *opt, int len)
+static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
+ struct raw6_sock *rp)
{
struct sk_buff *skb;
int err = 0;
- u16 *csum;
+ int offset;
+ int len;
+ int total_len;
+ __wsum tmp_csum;
+ __sum16 csum;
+
+ if (!rp->checksum)
+ goto send;
if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
goto out;
- if (opt->offset + 1 < len)
- csum = (u16 *)(skb->h.raw + opt->offset);
- else {
+ offset = rp->offset;
+ total_len = inet_sk(sk)->cork.length - (skb->nh.raw - skb->data);
+ if (offset >= total_len - 1) {
err = -EINVAL;
+ ip6_flush_pending_frames(sk);
goto out;
}
+ /* should be check HW csum miyazawa */
if (skb_queue_len(&sk->sk_write_queue) == 1) {
/*
* Only one fragment on the socket.
*/
- /* should be check HW csum miyazawa */
- *csum = csum_ipv6_magic(&fl->fl6_src,
- &fl->fl6_dst,
- len, fl->proto, skb->csum);
+ tmp_csum = skb->csum;
} else {
- u32 tmp_csum = 0;
+ struct sk_buff *csum_skb = NULL;
+ tmp_csum = 0;
skb_queue_walk(&sk->sk_write_queue, skb) {
tmp_csum = csum_add(tmp_csum, skb->csum);
+
+ if (csum_skb)
+ continue;
+
+ len = skb->len - (skb->h.raw - skb->data);
+ if (offset >= len) {
+ offset -= len;
+ continue;
+ }
+
+ csum_skb = skb;
}
- tmp_csum = csum_ipv6_magic(&fl->fl6_src,
- &fl->fl6_dst,
- len, fl->proto, tmp_csum);
- *csum = tmp_csum;
+ skb = csum_skb;
}
- if (*csum == 0)
- *csum = -1;
- ip6_push_pending_frames(sk);
+
+ offset += skb->h.raw - skb->data;
+ if (skb_copy_bits(skb, offset, &csum, 2))
+ BUG();
+
+ /* in case cksum was not initialized */
+ if (unlikely(csum))
+ tmp_csum = csum_sub(tmp_csum, csum_unfold(csum));
+
+ csum = csum_ipv6_magic(&fl->fl6_src,
+ &fl->fl6_dst,
+ total_len, fl->proto, tmp_csum);
+
+ if (csum == 0 && fl->proto == IPPROTO_UDP)
+ csum = CSUM_MANGLED_0;
+
+ if (skb_store_bits(skb, offset, &csum, 2))
+ BUG();
+
+send:
+ err = ip6_push_pending_frames(sk);
out:
return err;
}
struct flowi *fl, struct rt6_info *rt,
unsigned int flags)
{
- struct inet_opt *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6hdr *iph;
struct sk_buff *skb;
unsigned int hh_len;
if (err)
goto error_fault;
- IP6_INC_STATS(Ip6OutRequests);
+ IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
- err = inet->recverr ? net_xmit_errno(err) : 0;
+ err = np->recverr ? net_xmit_errno(err) : 0;
if (err)
goto error;
out:
err = -EFAULT;
kfree_skb(skb);
error:
- IP6_INC_STATS(Ip6OutDiscards);
+ IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
return err;
}
+
+static int rawv6_probe_proto_opt(struct flowi *fl, struct msghdr *msg)
+{
+ struct iovec *iov;
+ u8 __user *type = NULL;
+ u8 __user *code = NULL;
+#ifdef CONFIG_IPV6_MIP6
+ u8 len = 0;
+#endif
+ int probed = 0;
+ int i;
+
+ if (!msg->msg_iov)
+ return 0;
+
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ iov = &msg->msg_iov[i];
+ if (!iov)
+ continue;
+
+ switch (fl->proto) {
+ case IPPROTO_ICMPV6:
+ /* check if one-byte field is readable or not. */
+ if (iov->iov_base && iov->iov_len < 1)
+ break;
+
+ if (!type) {
+ type = iov->iov_base;
+ /* check if code field is readable or not. */
+ if (iov->iov_len > 1)
+ code = type + 1;
+ } else if (!code)
+ code = iov->iov_base;
+
+ if (type && code) {
+ if (get_user(fl->fl_icmp_type, type) ||
+ get_user(fl->fl_icmp_code, code))
+ return -EFAULT;
+ probed = 1;
+ }
+ break;
+#ifdef CONFIG_IPV6_MIP6
+ case IPPROTO_MH:
+ if (iov->iov_base && iov->iov_len < 1)
+ break;
+ /* check if type field is readable or not. */
+ if (iov->iov_len > 2 - len) {
+ u8 __user *p = iov->iov_base;
+ if (get_user(fl->fl_mh_type, &p[2 - len]))
+ return -EFAULT;
+ probed = 1;
+ } else
+ len += iov->iov_len;
+
+ break;
+#endif
+ default:
+ probed = 1;
+ break;
+ }
+ if (probed)
+ break;
+ }
+ return 0;
+}
+
static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len)
{
struct ipv6_txoptions opt_space;
struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
- struct in6_addr *daddr;
- struct inet_opt *inet = inet_sk(sk);
+ struct in6_addr *daddr, *final_p = NULL, final;
+ struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
- struct raw6_opt *raw_opt = raw6_sk(sk);
+ struct raw6_sock *rp = raw6_sk(sk);
struct ipv6_txoptions *opt = NULL;
struct ip6_flowlabel *flowlabel = NULL;
struct dst_entry *dst = NULL;
struct flowi fl;
int addr_len = msg->msg_namelen;
int hlimit = -1;
+ int tclass = -1;
u16 proto;
int err;
/* Rough check on arithmetic overflow,
- better check is made in ip6_build_xmit
+ better check is made in ip6_append_data().
*/
- if (len < 0)
+ if (len > INT_MAX)
return -EMSGSIZE;
/* Mirror BSD error message compatibility */
return -EINVAL;
if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
- return(-EINVAL);
+ return(-EAFNOSUPPORT);
/* port is the proto value [0..255] carried in nexthdr */
proto = ntohs(sin6->sin6_port);
* sk->sk_dst_cache.
*/
if (sk->sk_state == TCP_ESTABLISHED &&
- !ipv6_addr_cmp(daddr, &np->daddr))
+ ipv6_addr_equal(daddr, &np->daddr))
daddr = &np->daddr;
if (addr_len >= sizeof(struct sockaddr_in6) &&
memset(opt, 0, sizeof(struct ipv6_txoptions));
opt->tot_len = sizeof(struct ipv6_txoptions);
- err = datagram_send_ctl(msg, &fl, opt, &hlimit);
+ err = datagram_send_ctl(msg, &fl, opt, &hlimit, &tclass);
if (err < 0) {
fl6_sock_release(flowlabel);
return err;
opt = np->opt;
if (flowlabel)
opt = fl6_merge_options(&opt_space, flowlabel, opt);
+ opt = ipv6_fixup_options(&opt_space, opt);
fl.proto = proto;
+ err = rawv6_probe_proto_opt(&fl, msg);
+ if (err)
+ goto out;
+
ipv6_addr_copy(&fl.fl6_dst, daddr);
if (ipv6_addr_any(&fl.fl6_src) && !ipv6_addr_any(&np->saddr))
ipv6_addr_copy(&fl.fl6_src, &np->saddr);
/* merge ip6_build_xmit from ip6_output */
if (opt && opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
}
if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))
fl.oif = np->mcast_oif;
+ security_sk_classify_flow(sk, &fl);
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto out;
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto out;
if (hlimit < 0) {
if (ipv6_addr_is_multicast(&fl.fl6_dst))
hlimit = np->hop_limit;
if (hlimit < 0)
hlimit = dst_metric(dst, RTAX_HOPLIMIT);
+ if (hlimit < 0)
+ hlimit = ipv6_get_hoplimit(dst->dev);
+ }
+
+ if (tclass < 0) {
+ tclass = np->tclass;
+ if (tclass < 0)
+ tclass = 0;
}
if (msg->msg_flags&MSG_CONFIRM)
err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl, (struct rt6_info*)dst, msg->msg_flags);
} else {
lock_sock(sk);
- err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0,
- hlimit, opt, &fl, (struct rt6_info*)dst, msg->msg_flags);
+ err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
+ len, 0, hlimit, tclass, opt, &fl, (struct rt6_info*)dst,
+ msg->msg_flags);
if (err)
ip6_flush_pending_frames(sk);
- else if (!(msg->msg_flags & MSG_MORE)) {
- if (raw_opt->checksum) {
- err = rawv6_push_pending_frames(sk, &fl, raw_opt, len);
- } else {
- err = ip6_push_pending_frames(sk);
- }
- }
+ else if (!(msg->msg_flags & MSG_MORE))
+ err = rawv6_push_pending_frames(sk, &fl, rp);
}
done:
- ip6_dst_store(sk, dst,
- !ipv6_addr_cmp(&fl.fl6_dst, &np->daddr) ?
- &np->daddr : NULL);
- if (err > 0)
- err = np->recverr ? net_xmit_errno(err) : 0;
-
- release_sock(sk);
+ dst_release(dst);
+ if (!inet->hdrincl)
+ release_sock(sk);
out:
fl6_sock_release(flowlabel);
return err<0?err:len;
}
static int rawv6_seticmpfilter(struct sock *sk, int level, int optname,
- char *optval, int optlen)
+ char __user *optval, int optlen)
{
switch (optname) {
case ICMPV6_FILTER:
}
static int rawv6_geticmpfilter(struct sock *sk, int level, int optname,
- char *optval, int *optlen)
+ char __user *optval, int __user *optlen)
{
int len;
}
-static int rawv6_setsockopt(struct sock *sk, int level, int optname,
- char *optval, int optlen)
+static int do_rawv6_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen)
{
- struct raw6_opt *opt = raw6_sk(sk);
+ struct raw6_sock *rp = raw6_sk(sk);
int val;
- switch(level) {
- case SOL_RAW:
- break;
-
- case SOL_ICMPV6:
- if (inet_sk(sk)->num != IPPROTO_ICMPV6)
- return -EOPNOTSUPP;
- return rawv6_seticmpfilter(sk, level, optname, optval,
- optlen);
- case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
- break;
- default:
- return ipv6_setsockopt(sk, level, optname, optval,
- optlen);
- };
-
- if (get_user(val, (int *)optval))
+ if (get_user(val, (int __user *)optval))
return -EFAULT;
switch (optname) {
if (val > 0 && (val&1))
return(-EINVAL);
if (val < 0) {
- opt->checksum = 0;
+ rp->checksum = 0;
} else {
- opt->checksum = 1;
- opt->offset = val;
+ rp->checksum = 1;
+ rp->offset = val;
}
return 0;
}
}
-static int rawv6_getsockopt(struct sock *sk, int level, int optname,
- char *optval, int *optlen)
+static int rawv6_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen)
{
- struct raw6_opt *opt = raw6_sk(sk);
- int val, len;
-
switch(level) {
case SOL_RAW:
break;
case SOL_ICMPV6:
if (inet_sk(sk)->num != IPPROTO_ICMPV6)
return -EOPNOTSUPP;
- return rawv6_geticmpfilter(sk, level, optname, optval,
+ return rawv6_seticmpfilter(sk, level, optname, optval,
optlen);
case SOL_IPV6:
if (optname == IPV6_CHECKSUM)
break;
default:
- return ipv6_getsockopt(sk, level, optname, optval,
+ return ipv6_setsockopt(sk, level, optname, optval,
optlen);
};
+ return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
+}
+
+#ifdef CONFIG_COMPAT
+static int compat_rawv6_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen)
+{
+ switch (level) {
+ case SOL_RAW:
+ break;
+ case SOL_ICMPV6:
+ if (inet_sk(sk)->num != IPPROTO_ICMPV6)
+ return -EOPNOTSUPP;
+ return rawv6_seticmpfilter(sk, level, optname, optval, optlen);
+ case SOL_IPV6:
+ if (optname == IPV6_CHECKSUM)
+ break;
+ default:
+ return compat_ipv6_setsockopt(sk, level, optname,
+ optval, optlen);
+ };
+ return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
+}
+#endif
+
+static int do_rawv6_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct raw6_sock *rp = raw6_sk(sk);
+ int val, len;
if (get_user(len,optlen))
return -EFAULT;
switch (optname) {
case IPV6_CHECKSUM:
- if (opt->checksum == 0)
+ if (rp->checksum == 0)
val = -1;
else
- val = opt->offset;
+ val = rp->offset;
break;
default:
return 0;
}
+static int rawv6_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ switch(level) {
+ case SOL_RAW:
+ break;
+
+ case SOL_ICMPV6:
+ if (inet_sk(sk)->num != IPPROTO_ICMPV6)
+ return -EOPNOTSUPP;
+ return rawv6_geticmpfilter(sk, level, optname, optval,
+ optlen);
+ case SOL_IPV6:
+ if (optname == IPV6_CHECKSUM)
+ break;
+ default:
+ return ipv6_getsockopt(sk, level, optname, optval,
+ optlen);
+ };
+ return do_rawv6_getsockopt(sk, level, optname, optval, optlen);
+}
+
+#ifdef CONFIG_COMPAT
+static int compat_rawv6_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ switch (level) {
+ case SOL_RAW:
+ break;
+ case SOL_ICMPV6:
+ if (inet_sk(sk)->num != IPPROTO_ICMPV6)
+ return -EOPNOTSUPP;
+ return rawv6_geticmpfilter(sk, level, optname, optval, optlen);
+ case SOL_IPV6:
+ if (optname == IPV6_CHECKSUM)
+ break;
+ default:
+ return compat_ipv6_getsockopt(sk, level, optname,
+ optval, optlen);
+ };
+ return do_rawv6_getsockopt(sk, level, optname, optval, optlen);
+}
+#endif
+
static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
switch(cmd) {
case SIOCOUTQ:
{
int amount = atomic_read(&sk->sk_wmem_alloc);
- return put_user(amount, (int *)arg);
+ return put_user(amount, (int __user *)arg);
}
case SIOCINQ:
{
struct sk_buff *skb;
int amount = 0;
- spin_lock_irq(&sk->sk_receive_queue.lock);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
skb = skb_peek(&sk->sk_receive_queue);
if (skb != NULL)
amount = skb->tail - skb->h.raw;
- spin_unlock_irq(&sk->sk_receive_queue.lock);
- return put_user(amount, (int *)arg);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ return put_user(amount, (int __user *)arg);
}
default:
if (inet_sk(sk)->num == IPPROTO_RAW)
ip6_ra_control(sk, -1, NULL);
- inet_sock_release(sk);
+ sk_common_release(sk);
}
static int rawv6_init_sk(struct sock *sk)
{
if (inet_sk(sk)->num == IPPROTO_ICMPV6) {
- struct raw6_opt *opt = raw6_sk(sk);
- opt->checksum = 1;
- opt->offset = 2;
+ struct raw6_sock *rp = raw6_sk(sk);
+ rp->checksum = 1;
+ rp->offset = 2;
}
return(0);
}
struct proto rawv6_prot = {
- .name = "RAW",
- .close = rawv6_close,
- .connect = udpv6_connect,
- .disconnect = udp_disconnect,
- .ioctl = rawv6_ioctl,
- .init = rawv6_init_sk,
- .destroy = inet6_destroy_sock,
- .setsockopt = rawv6_setsockopt,
- .getsockopt = rawv6_getsockopt,
- .sendmsg = rawv6_sendmsg,
- .recvmsg = rawv6_recvmsg,
- .bind = rawv6_bind,
- .backlog_rcv = rawv6_rcv_skb,
- .hash = raw_v6_hash,
- .unhash = raw_v6_unhash,
+ .name = "RAWv6",
+ .owner = THIS_MODULE,
+ .close = rawv6_close,
+ .connect = ip6_datagram_connect,
+ .disconnect = udp_disconnect,
+ .ioctl = rawv6_ioctl,
+ .init = rawv6_init_sk,
+ .destroy = inet6_destroy_sock,
+ .setsockopt = rawv6_setsockopt,
+ .getsockopt = rawv6_getsockopt,
+ .sendmsg = rawv6_sendmsg,
+ .recvmsg = rawv6_recvmsg,
+ .bind = rawv6_bind,
+ .backlog_rcv = rawv6_rcv_skb,
+ .hash = raw_v6_hash,
+ .unhash = raw_v6_unhash,
+ .obj_size = sizeof(struct raw6_sock),
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_rawv6_setsockopt,
+ .compat_getsockopt = compat_rawv6_getsockopt,
+#endif
};
#ifdef CONFIG_PROC_FS
{
struct seq_file *seq;
int rc = -ENOMEM;
- struct raw6_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ struct raw6_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
goto out;
rc = seq_open(file, &raw6_seq_ops);
goto out_kfree;
seq = file->private_data;
seq->private = s;
- memset(s, 0, sizeof(*s));
out:
return rc;
out_kfree: