vserver 1.9.3
[linux-2.6.git] / net / ipv6 / raw.c
index 0319896..c73d720 100644 (file)
@@ -555,12 +555,58 @@ error:
        IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
        return err; 
 }
+
+static void rawv6_probe_proto_opt(struct flowi *fl, struct msghdr *msg)
+{
+       struct iovec *iov;
+       u8 __user *type = NULL;
+       u8 __user *code = NULL;
+       int probed = 0;
+       int i;
+
+       if (!msg->msg_iov)
+               return;
+
+       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) {
+                               get_user(fl->fl_icmp_type, type);
+                               __get_user(fl->fl_icmp_code, code);
+                               probed = 1;
+                       }
+                       break;
+               default:
+                       probed = 1;
+                       break;
+               }
+               if (probed)
+                       break;
+       }
+}
+
 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 in6_addr *daddr, *final_p = NULL, final;
        struct inet_opt *inet = inet_sk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct raw6_opt *raw_opt = raw6_sk(sk);
@@ -674,6 +720,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                opt = fl6_merge_options(&opt_space, flowlabel, opt);
 
        fl.proto = proto;
+       rawv6_probe_proto_opt(&fl, msg);
        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);
@@ -681,7 +729,9 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
        /* 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))
@@ -690,6 +740,13 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
        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) {
+               dst_release(dst);
+               goto out;
+       }
 
        if (hlimit < 0) {
                if (ipv6_addr_is_multicast(&fl.fl6_dst))
@@ -932,6 +989,7 @@ struct proto rawv6_prot = {
        .backlog_rcv =  rawv6_rcv_skb,
        .hash =         raw_v6_hash,
        .unhash =       raw_v6_unhash,
+       .slab_obj_size = sizeof(struct raw6_sock),
 };
 
 #ifdef CONFIG_PROC_FS