X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv6%2Fraw.c;h=9479fbd223c7afe4d539775089f6ee8e867a7afb;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=c73d720114fa9721608b68d02b51f7b0008aa1c6;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index c73d72011..9479fbd22 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,10 @@ #include #include #include +#include +#ifdef CONFIG_IPV6_MIP6 +#include +#endif #include #include @@ -56,7 +61,7 @@ #include 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) { @@ -80,7 +85,8 @@ static void raw_v6_unhash(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); @@ -90,11 +96,14 @@ struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num, 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)) @@ -115,10 +124,10 @@ found: 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; @@ -136,11 +145,12 @@ static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb) * * 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; @@ -159,29 +169,58 @@ void ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) 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; @@ -251,9 +290,9 @@ out: 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; @@ -286,13 +325,10 @@ void rawv6_err(struct sock *sk, struct sk_buff *skb, static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb) { if ((raw6_sk(sk)->checksum || 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; + skb_checksum_complete(skb)) { + /* FIXME: increment a raw6 drops counter here */ + kfree_skb(skb); + return 0; } /* Charge it to the socket. */ @@ -314,42 +350,36 @@ static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb) */ 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); @@ -394,7 +424,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, 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 { @@ -408,6 +438,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, /* 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; @@ -430,44 +461,38 @@ out: 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; - u32 tmp_csum; + int offset; + int len; + int total_len; + __wsum tmp_csum; + __sum16 csum; - if (!opt->checksum) + 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; } @@ -478,23 +503,45 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct r */ tmp_csum = skb->csum; } else { + 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; } + + skb = csum_skb; } + 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); + 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); - *csum = csum_ipv6_magic(&fl->fl6_src, - &fl->fl6_dst, - 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(); - if (*csum == 0) - *csum = -1; send: err = ip6_push_pending_frames(sk); out: @@ -505,7 +552,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length, 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; @@ -538,11 +585,11 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length, if (err) goto error_fault; - IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); + 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: @@ -552,20 +599,23 @@ error_fault: err = -EFAULT; kfree_skb(skb); error: - IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS); + IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); return err; } -static void rawv6_probe_proto_opt(struct flowi *fl, struct msghdr *msg) +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; + return 0; for (i = 0; i < msg->msg_iovlen; i++) { iov = &msg->msg_iov[i]; @@ -587,11 +637,27 @@ static void rawv6_probe_proto_opt(struct flowi *fl, struct msghdr *msg) code = iov->iov_base; if (type && code) { - get_user(fl->fl_icmp_type, type); - __get_user(fl->fl_icmp_code, 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; @@ -599,6 +665,7 @@ static void rawv6_probe_proto_opt(struct flowi *fl, struct msghdr *msg) if (probed) break; } + return 0; } static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, @@ -607,22 +674,23 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct ipv6_txoptions opt_space; struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name; struct in6_addr *daddr, *final_p = NULL, final; - struct inet_opt *inet = inet_sk(sk); + 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 */ @@ -639,7 +707,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, 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); @@ -668,7 +736,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, * 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) && @@ -701,7 +769,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, 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; @@ -718,9 +786,12 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, opt = np->opt; if (flowlabel) opt = fl6_merge_options(&opt_space, flowlabel, opt); + opt = ipv6_fixup_options(&opt_space, opt); fl.proto = proto; - rawv6_probe_proto_opt(&fl, msg); + 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)) @@ -736,6 +807,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, 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) @@ -743,10 +815,8 @@ 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, 0)) < 0) { - dst_release(dst); + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) goto out; - } if (hlimit < 0) { if (ipv6_addr_is_multicast(&fl.fl6_dst)) @@ -755,6 +825,14 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, 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) @@ -765,22 +843,19 @@ back_from_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)) - err = rawv6_push_pending_frames(sk, &fl, raw_opt, len); + 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; @@ -835,29 +910,12 @@ static int rawv6_geticmpfilter(struct sock *sk, int level, int optname, } -static int rawv6_setsockopt(struct sock *sk, int level, int optname, +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 __user *)optval)) return -EFAULT; @@ -868,10 +926,10 @@ static int rawv6_setsockopt(struct sock *sk, int level, int 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; @@ -882,12 +940,9 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname, } } -static int rawv6_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *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; @@ -895,25 +950,55 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname, 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: @@ -929,6 +1014,50 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname, 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) { @@ -942,11 +1071,11 @@ static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg) 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); + spin_unlock_bh(&sk->sk_receive_queue.lock); return put_user(amount, (int __user *)arg); } @@ -966,30 +1095,35 @@ static void rawv6_close(struct sock *sk, long timeout) 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 = 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, - .slab_obj_size = sizeof(struct raw6_sock), + .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 @@ -1115,7 +1249,7 @@ static int raw6_seq_open(struct inode *inode, struct file *file) { 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); @@ -1123,7 +1257,6 @@ static int raw6_seq_open(struct inode *inode, struct file *file) goto out_kfree; seq = file->private_data; seq->private = s; - memset(s, 0, sizeof(*s)); out: return rc; out_kfree: