Merge git://git.skbuff.net/gitroot/yoshfuji/linux-2.6-git-rfc3542
authorDavid S. Miller <davem@sunset.davemloft.net>
Thu, 8 Sep 2005 19:59:43 +0000 (12:59 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Thu, 8 Sep 2005 19:59:43 +0000 (12:59 -0700)
1  2 
net/ipv6/exthdrs.c
net/ipv6/udp.c

diff --combined net/ipv6/exthdrs.c
index 3b9fa900a4bf73d40be0f86d56c10d160d64ccf6,ffcda45e2c1e3105ddc53a67a596980465fc6897..47122728212ab570b5eef20d45117bd33419fbc0
@@@ -164,6 -164,7 +164,7 @@@ static int ipv6_destopt_rcv(struct sk_b
                return -1;
        }
  
+       opt->lastopt = skb->h.raw - skb->nh.raw;
        opt->dst1 = skb->h.raw - skb->nh.raw;
  
        if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
@@@ -243,6 -244,7 +244,7 @@@ static int ipv6_rthdr_rcv(struct sk_buf
  
  looped_back:
        if (hdr->segments_left == 0) {
+               opt->lastopt = skb->h.raw - skb->nh.raw;
                opt->srcrt = skb->h.raw - skb->nh.raw;
                skb->h.raw += (hdr->hdrlen + 1) << 3;
                opt->dst0 = opt->dst1;
@@@ -459,10 -461,11 +461,10 @@@ static int ipv6_hop_jumbo(struct sk_buf
                IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
                goto drop;
        }
 -      if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
 -              __pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
 -              if (skb->ip_summed == CHECKSUM_HW)
 -                      skb->ip_summed = CHECKSUM_NONE;
 -      }
 +
 +      if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
 +              goto drop;
 +
        return 1;
  
  drop:
@@@ -538,10 -541,15 +540,15 @@@ void ipv6_push_nfrag_opts(struct sk_buf
                          u8 *proto,
                          struct in6_addr **daddr)
  {
-       if (opt->srcrt)
+       if (opt->srcrt) {
                ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
-       if (opt->dst0opt)
-               ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
+               /*
+                * IPV6_RTHDRDSTOPTS is ignored
+                * unless IPV6_RTHDR is set (RFC3542).
+                */
+               if (opt->dst0opt)
+                       ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
+       }
        if (opt->hopopt)
                ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
  }
@@@ -572,3 -580,97 +579,97 @@@ ipv6_dup_options(struct sock *sk, struc
        }
        return opt2;
  }
+ static int ipv6_renew_option(void *ohdr,
+                            struct ipv6_opt_hdr __user *newopt, int newoptlen,
+                            int inherit,
+                            struct ipv6_opt_hdr **hdr,
+                            char **p)
+ {
+       if (inherit) {
+               if (ohdr) {
+                       memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
+                       *hdr = (struct ipv6_opt_hdr *)*p;
+                       *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
+               }
+       } else {
+               if (newopt) {
+                       if (copy_from_user(*p, newopt, newoptlen))
+                               return -EFAULT;
+                       *hdr = (struct ipv6_opt_hdr *)*p;
+                       if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
+                               return -EINVAL;
+                       *p += CMSG_ALIGN(newoptlen);
+               }
+       }
+       return 0;
+ }
+ struct ipv6_txoptions *
+ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
+                  int newtype,
+                  struct ipv6_opt_hdr __user *newopt, int newoptlen)
+ {
+       int tot_len = 0;
+       char *p;
+       struct ipv6_txoptions *opt2;
+       int err;
+       if (newtype != IPV6_HOPOPTS && opt->hopopt)
+               tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
+       if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
+               tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
+       if (newtype != IPV6_RTHDR && opt->srcrt)
+               tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
+       if (newtype != IPV6_DSTOPTS && opt->dst1opt)
+               tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
+       if (newopt && newoptlen)
+               tot_len += CMSG_ALIGN(newoptlen);
+       if (!tot_len)
+               return NULL;
+       opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
+       if (!opt2)
+               return ERR_PTR(-ENOBUFS);
+       memset(opt2, 0, tot_len);
+       opt2->tot_len = tot_len;
+       p = (char *)(opt2 + 1);
+       err = ipv6_renew_option(opt->hopopt, newopt, newoptlen,
+                               newtype != IPV6_HOPOPTS,
+                               &opt2->hopopt, &p);
+       if (err)
+               goto out;
+       err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen,
+                               newtype != IPV6_RTHDRDSTOPTS,
+                               &opt2->dst0opt, &p);
+       if (err)
+               goto out;
+       err = ipv6_renew_option(opt->srcrt, newopt, newoptlen,
+                               newtype != IPV6_RTHDR,
+                               (struct ipv6_opt_hdr **)opt2->srcrt, &p);
+       if (err)
+               goto out;
+       err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen,
+                               newtype != IPV6_DSTOPTS,
+                               &opt2->dst1opt, &p);
+       if (err)
+               goto out;
+       opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
+                         (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
+                         (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
+       opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
+       return opt2;
+ out:
+       sock_kfree_s(sk, p, tot_len);
+       return ERR_PTR(err);
+ }
diff --combined net/ipv6/udp.c
index 7cbcaa30cf5e82af47fb8836c321227e0363e9bd,dbd18a9d166995a3ce719291fe5cb4385bbc4dd6..f5ae14810a7041d011f0c17d89e0e41c9ffdec8f
@@@ -483,7 -483,7 +483,7 @@@ static int udpv6_rcv(struct sk_buff **p
        }
  
        if (ulen < skb->len) {
 -              if (__pskb_trim(skb, ulen))
 +              if (pskb_trim_rcsum(skb, ulen))
                        goto discard;
                saddr = &skb->nh.ipv6h->saddr;
                daddr = &skb->nh.ipv6h->daddr;
@@@ -637,6 -637,7 +637,7 @@@ static int udpv6_sendmsg(struct kiocb *
        int addr_len = msg->msg_namelen;
        int ulen = len;
        int hlimit = -1;
+       int tclass = -1;
        int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
        int err;
  
@@@ -758,7 -759,7 +759,7 @@@ do_udp_sendmsg
                memset(opt, 0, sizeof(struct ipv6_txoptions));
                opt->tot_len = sizeof(*opt);
  
-               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;
        }
        if (opt == NULL)
                opt = np->opt;
-       if (flowlabel)
-               opt = fl6_merge_options(&opt_space, flowlabel, opt);
+       opt = fl6_merge_options(&opt_space, flowlabel, opt);
  
        fl->proto = IPPROTO_UDP;
        ipv6_addr_copy(&fl->fl6_dst, daddr);
                        hlimit = ipv6_get_hoplimit(dst->dev);
        }
  
+       if (tclass < 0) {
+               tclass = np->tclass;
+               if (tclass < 0)
+                       tclass = 0;
+       }
        if (msg->msg_flags&MSG_CONFIRM)
                goto do_confirm;
  back_from_confirm:
  
  do_append_data:
        up->len += ulen;
-       err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, sizeof(struct udphdr),
-                             hlimit, opt, fl, (struct rt6_info*)dst,
-                             corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
+       err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
+               sizeof(struct udphdr), hlimit, tclass, opt, fl,
+               (struct rt6_info*)dst,
+               corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
        if (err)
                udp_v6_flush_pending_frames(sk);
        else if (!corkreq)