X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=net%2Fipv6%2Fraw.c;h=1f8f6275a7e4f08018a2d6715b32f17115b98ed7;hb=d9bc125caf592b7d081021f32ce5b717efdf70c8;hp=15b862d8acabc621edde195034cb2b9e72737bfd;hpb=c9802cd9574a80444e689c7525627b40d7dc3a06;p=linux-2.6-omap-h63xx.git diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 15b862d8aca..1f8f6275a7e 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1,9 +1,9 @@ /* * RAW sockets for IPv6 - * Linux INET6 implementation + * Linux INET6 implementation * * Authors: - * Pedro Roque + * Pedro Roque * * Adapted from linux/net/ipv4/raw.c * @@ -11,7 +11,7 @@ * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support - * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance) + * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance) * Kazunori MIYAZAWA @USAGI: change process style to use ip6_append_data * * This program is free software; you can redistribute it and/or @@ -50,6 +50,9 @@ #include #include #include +#ifdef CONFIG_IPV6_MIP6 +#include +#endif #include #include @@ -68,12 +71,12 @@ static void raw_v6_hash(struct sock *sk) write_lock_bh(&raw_v6_lock); sk_add_node(sk, list); sock_prot_inc_use(sk->sk_prot); - write_unlock_bh(&raw_v6_lock); + write_unlock_bh(&raw_v6_lock); } static void raw_v6_unhash(struct sock *sk) { - write_lock_bh(&raw_v6_lock); + 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); @@ -169,8 +172,32 @@ int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr, IP6CB(skb)->iif); while (sk) { + int filtered; + delivered = 1; - if (nexthdr != IPPROTO_ICMPV6 || !icmpv6_filter(sk, skb)) { + 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! */ @@ -193,7 +220,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 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; @@ -223,7 +250,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) */ sk->sk_bound_dev_if = addr->sin6_scope_id; } - + /* Binding to link-local address requires an interface */ if (!sk->sk_bound_dev_if) goto out; @@ -234,7 +261,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) goto out; } } - + /* ipv4 addr of the socket is invalid. Only the * unspecified and mapped address have a v4 equivalent. */ @@ -263,7 +290,7 @@ 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_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); @@ -297,7 +324,7 @@ 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) && + if ((raw6_sk(sk)->checksum || sk->sk_filter) && skb_checksum_complete(skb)) { /* FIXME: increment a raw6 drops counter here */ kfree_skb(skb); @@ -315,10 +342,10 @@ static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb) } /* - * This is next to useless... + * This is next to useless... * if we demultiplex in network layer we don't need the extra call - * just to queue the skb... - * maybe we could have the network decide upon a hint if it + * just to queue the skb... + * maybe we could have the network decide upon a hint if it * should call raw_rcv for demultiplexing */ int rawv6_rcv(struct sock *sk, struct sk_buff *skb) @@ -326,26 +353,26 @@ int rawv6_rcv(struct sock *sk, struct sk_buff *skb) 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 (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) { + kfree_skb(skb); + return NET_RX_DROP; + } if (!rp->checksum) 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); + 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 (skb->ip_summed != CHECKSUM_UNNECESSARY) - skb->csum = ~csum_ipv6_magic(&skb->nh.ipv6h->saddr, + skb->csum = ~csum_unfold(csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr, - skb->len, inet->num, 0); + skb->len, inet->num, 0)); if (inet->hdrincl) { if (skb_checksum_complete(skb)) { @@ -377,8 +404,8 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, if (flags & MSG_OOB) return -EOPNOTSUPP; - - if (addr_len) + + if (addr_len) *addr_len=sizeof(*sin6); if (flags & MSG_ERRQUEUE) @@ -389,10 +416,10 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, goto out; copied = skb->len; - if (copied > len) { - copied = len; - msg->msg_flags |= MSG_TRUNC; - } + if (copied > len) { + copied = len; + msg->msg_flags |= MSG_TRUNC; + } if (skb->ip_summed==CHECKSUM_UNNECESSARY) { err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); @@ -452,8 +479,8 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, int offset; int len; int total_len; - u32 tmp_csum; - u16 csum; + __wsum tmp_csum; + __sum16 csum; if (!rp->checksum) goto send; @@ -503,16 +530,15 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl, /* in case cksum was not initialized */ if (unlikely(csum)) - tmp_csum = csum_sub(tmp_csum, csum); + tmp_csum = csum_sub(tmp_csum, csum_unfold(csum)); - tmp_csum = csum_ipv6_magic(&fl->fl6_src, + csum = csum_ipv6_magic(&fl->fl6_src, &fl->fl6_dst, total_len, fl->proto, tmp_csum); - if (tmp_csum == 0) - tmp_csum = -1; + if (csum == 0 && fl->proto == IPPROTO_UDP) + csum = CSUM_MANGLED_0; - csum = tmp_csum; if (skb_store_bits(skb, offset, &csum, 2)) BUG(); @@ -523,7 +549,7 @@ out: } static int rawv6_send_hdrinc(struct sock *sk, void *from, int length, - struct flowi *fl, struct rt6_info *rt, + struct flowi *fl, struct rt6_info *rt, unsigned int flags) { struct ipv6_pinfo *np = inet6_sk(sk); @@ -544,7 +570,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length, skb = sock_alloc_send_skb(sk, length+hh_len+15, flags&MSG_DONTWAIT, &err); if (skb == NULL) - goto error; + goto error; skb_reserve(skb, hh_len); skb->priority = sk->sk_priority; @@ -559,7 +585,7 @@ 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) @@ -573,20 +599,23 @@ error_fault: err = -EFAULT; kfree_skb(skb); error: - IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS); - return err; + 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]; @@ -608,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; @@ -620,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, @@ -648,19 +694,19 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, return -EMSGSIZE; /* Mirror BSD error message compatibility */ - if (msg->msg_flags & MSG_OOB) + if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; /* - * Get and verify the address. + * Get and verify the address. */ memset(&fl, 0, sizeof(fl)); if (sin6) { - if (addr_len < SIN6_LEN_RFC2133) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; - if (sin6->sin6_family && sin6->sin6_family != AF_INET6) + if (sin6->sin6_family && sin6->sin6_family != AF_INET6) return(-EAFNOSUPPORT); /* port is the proto value [0..255] carried in nexthdr */ @@ -698,17 +744,17 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) fl.oif = sin6->sin6_scope_id; } else { - if (sk->sk_state != TCP_ESTABLISHED) + if (sk->sk_state != TCP_ESTABLISHED) return -EDESTADDRREQ; - + proto = inet->num; daddr = &np->daddr; fl.fl6_flowlabel = np->flow_label; } if (ipv6_addr_any(daddr)) { - /* - * unspecified destination address + /* + * unspecified destination address * treated as error... is this correct ? */ fl6_sock_release(flowlabel); @@ -743,8 +789,10 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, 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)) ipv6_addr_copy(&fl.fl6_src, &np->saddr); @@ -759,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) @@ -766,7 +815,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, 0)) < 0) + if ((err = xfrm_lookup(&dst, &fl, sk, 1)) < 0) goto out; if (hlimit < 0) { @@ -805,8 +854,9 @@ back_from_confirm: } done: dst_release(dst); - release_sock(sk); -out: + if (!inet->hdrincl) + release_sock(sk); +out: fl6_sock_release(flowlabel); return err<0?err:len; do_confirm: @@ -817,7 +867,7 @@ do_confirm: goto done; } -static int rawv6_seticmpfilter(struct sock *sk, int level, int optname, +static int rawv6_seticmpfilter(struct sock *sk, int level, int optname, char __user *optval, int optlen) { switch (optname) { @@ -834,7 +884,7 @@ static int rawv6_seticmpfilter(struct sock *sk, int level, int optname, return 0; } -static int rawv6_geticmpfilter(struct sock *sk, int level, int optname, +static int rawv6_geticmpfilter(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { int len; @@ -866,7 +916,7 @@ static int do_rawv6_setsockopt(struct sock *sk, int level, int optname, struct raw6_sock *rp = raw6_sk(sk); int val; - if (get_user(val, (int __user *)optval)) + if (get_user(val, (int __user *)optval)) return -EFAULT; switch (optname) { @@ -1044,10 +1094,19 @@ 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_sock *rp = raw6_sk(sk); + struct raw6_sock *rp = raw6_sk(sk); + + switch (inet_sk(sk)->num) { + case IPPROTO_ICMPV6: rp->checksum = 1; rp->offset = 2; + break; + case IPPROTO_MH: + rp->checksum = 1; + rp->offset = 4; + break; + default: + break; } return(0); } @@ -1165,7 +1224,7 @@ static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) src->s6_addr32[2], src->s6_addr32[3], srcp, dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp, - sp->sk_state, + sp->sk_state, atomic_read(&sp->sk_wmem_alloc), atomic_read(&sp->sk_rmem_alloc), 0, 0L, 0, @@ -1214,7 +1273,7 @@ out_kfree: goto out; } -static struct file_operations raw6_seq_fops = { +static const struct file_operations raw6_seq_fops = { .owner = THIS_MODULE, .open = raw6_seq_open, .read = seq_read,