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)
13 files changed:
include/linux/in6.h
include/linux/ipv6.h
include/net/ipv6.h
include/net/transp_v6.h
net/ipv6/datagram.c
net/ipv6/exthdrs.c
net/ipv6/icmp.c
net/ipv6/ip6_flowlabel.c
net/ipv6/ip6_output.c
net/ipv6/ipv6_sockglue.c
net/ipv6/raw.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c

index dcf5720..bd32b79 100644 (file)
@@ -148,13 +148,13 @@ struct in6_flowlabel_req
  */
 
 #define IPV6_ADDRFORM          1
-#define IPV6_PKTINFO           2
-#define IPV6_HOPOPTS           3
-#define IPV6_DSTOPTS           4
-#define IPV6_RTHDR             5
-#define IPV6_PKTOPTIONS                6
+#define IPV6_2292PKTINFO       2
+#define IPV6_2292HOPOPTS       3
+#define IPV6_2292DSTOPTS       4
+#define IPV6_2292RTHDR         5
+#define IPV6_2292PKTOPTIONS    6
 #define IPV6_CHECKSUM          7
-#define IPV6_HOPLIMIT          8
+#define IPV6_2292HOPLIMIT      8
 #define IPV6_NEXTHOP           9
 #define IPV6_AUTHHDR           10      /* obsolete */
 #define IPV6_FLOWINFO          11
@@ -198,4 +198,28 @@ struct in6_flowlabel_req
  * MCAST_MSFILTER              48
  */
 
+/* RFC3542 advanced socket options (50-67) */
+#define IPV6_RECVPKTINFO       50
+#define IPV6_PKTINFO           51
+#if 0
+#define IPV6_RECVPATHMTU       52
+#define IPV6_PATHMTU           53
+#define IPV6_DONTFRAG          54
+#define IPV6_USE_MIN_MTU       55
+#endif
+#define IPV6_RECVHOPOPTS       56
+#define IPV6_HOPOPTS           57
+#if 0
+#define IPV6_RECVRTHDRDSTOPTS  58      /* Unused, see net/ipv6/datagram.c */
+#endif
+#define IPV6_RTHDRDSTOPTS      59
+#define IPV6_RECVRTHDR         60
+#define IPV6_RTHDR             61
+#define IPV6_RECVDSTOPTS       62
+#define IPV6_DSTOPTS           63
+#define IPV6_RECVHOPLIMIT      64
+#define IPV6_HOPLIMIT          65
+#define IPV6_RECVTCLASS                66
+#define IPV6_TCLASS            67
+
 #endif
index 3c7dbc6..6c5f7b3 100644 (file)
@@ -189,6 +189,7 @@ struct inet6_skb_parm {
        __u16                   dst0;
        __u16                   srcrt;
        __u16                   dst1;
+       __u16                   lastopt;
 };
 
 #define IP6CB(skb)     ((struct inet6_skb_parm*)((skb)->cb))
@@ -234,14 +235,20 @@ struct ipv6_pinfo {
        /* pktoption flags */
        union {
                struct {
-                       __u8    srcrt:2,
+                       __u16   srcrt:2,
+                               osrcrt:2,
                                rxinfo:1,
+                               rxoinfo:1,
                                rxhlim:1,
+                               rxohlim:1,
                                hopopts:1,
+                               ohopopts:1,
                                dstopts:1,
-                                rxflow:1;
+                               odstopts:1,
+                                rxflow:1,
+                               rxtclass:1;
                } bits;
-               __u           all;
+               __u16           all;
        } rxopt;
 
        /* sockopt flags */
@@ -250,6 +257,7 @@ struct ipv6_pinfo {
                                sndflow:1,
                                pmtudisc:2,
                                ipv6only:1;
+       __u8                    tclass;
 
        __u32                   dst_cookie;
 
@@ -263,6 +271,7 @@ struct ipv6_pinfo {
                struct ipv6_txoptions *opt;
                struct rt6_info *rt;
                int hop_limit;
+               int tclass;
        } cork;
 };
 
index 3203eaf..65ec866 100644 (file)
@@ -233,6 +233,10 @@ extern int                         ip6_ra_control(struct sock *sk, int sel,
 extern int                     ipv6_parse_hopopts(struct sk_buff *skb, int);
 
 extern struct ipv6_txoptions *  ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
+extern struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
+                                                  int newtype,
+                                                  struct ipv6_opt_hdr __user *newopt,
+                                                  int newoptlen);
 
 extern int ip6_frag_nqueues;
 extern atomic_t ip6_frag_mem;
@@ -373,6 +377,7 @@ extern int                  ip6_append_data(struct sock *sk,
                                                int length,
                                                int transhdrlen,
                                                int hlimit,
+                                               int tclass,
                                                struct ipv6_txoptions *opt,
                                                struct flowi *fl,
                                                struct rt6_info *rt,
index 8b075ab..4e86f2d 100644 (file)
@@ -37,7 +37,7 @@ extern int                    datagram_recv_ctl(struct sock *sk,
 extern int                     datagram_send_ctl(struct msghdr *msg,
                                                  struct flowi *fl,
                                                  struct ipv6_txoptions *opt,
-                                                 int *hlimit);
+                                                 int *hlimit, int *tclass);
 
 #define                LOOPBACK4_IPV6          __constant_htonl(0x7f000006)
 
index 01468fa..157cec6 100644 (file)
@@ -390,32 +390,101 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
                put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
        }
 
+       if (np->rxopt.bits.rxtclass) {
+               int tclass = (ntohl(*(u32 *)skb->nh.ipv6h) >> 20) & 0xff;
+               put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
+       }
+
        if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) {
                u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
                put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
        }
+
+       /* HbH is allowed only once */
        if (np->rxopt.bits.hopopts && opt->hop) {
                u8 *ptr = skb->nh.raw + opt->hop;
                put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
        }
-       if (np->rxopt.bits.dstopts && opt->dst0) {
+
+       if (opt->lastopt &&
+           (np->rxopt.bits.dstopts || np->rxopt.bits.srcrt)) {
+               /*
+                * Silly enough, but we need to reparse in order to
+                * report extension headers (except for HbH)
+                * in order.
+                *
+                * Also note that IPV6_RECVRTHDRDSTOPTS is NOT 
+                * (and WILL NOT be) defined because
+                * IPV6_RECVDSTOPTS is more generic. --yoshfuji
+                */
+               unsigned int off = sizeof(struct ipv6hdr);
+               u8 nexthdr = skb->nh.ipv6h->nexthdr;
+
+               while (off <= opt->lastopt) {
+                       unsigned len;
+                       u8 *ptr = skb->nh.raw + off;
+
+                       switch(nexthdr) {
+                       case IPPROTO_DSTOPTS:
+                               nexthdr = ptr[0];
+                               len = (ptr[1] + 1) << 3;
+                               if (np->rxopt.bits.dstopts)
+                                       put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
+                               break;
+                       case IPPROTO_ROUTING:
+                               nexthdr = ptr[0];
+                               len = (ptr[1] + 1) << 3;
+                               if (np->rxopt.bits.srcrt)
+                                       put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
+                               break;
+                       case IPPROTO_AH:
+                               nexthdr = ptr[0];
+                               len = (ptr[1] + 1) << 2;
+                               break;
+                       default:
+                               nexthdr = ptr[0];
+                               len = (ptr[1] + 1) << 3;
+                               break;
+                       }
+
+                       off += len;
+               }
+       }
+
+       /* socket options in old style */
+       if (np->rxopt.bits.rxoinfo) {
+               struct in6_pktinfo src_info;
+
+               src_info.ipi6_ifindex = opt->iif;
+               ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
+               put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
+       }
+       if (np->rxopt.bits.rxohlim) {
+               int hlim = skb->nh.ipv6h->hop_limit;
+               put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
+       }
+       if (np->rxopt.bits.ohopopts && opt->hop) {
+               u8 *ptr = skb->nh.raw + opt->hop;
+               put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
+       }
+       if (np->rxopt.bits.odstopts && opt->dst0) {
                u8 *ptr = skb->nh.raw + opt->dst0;
-               put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
+               put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
        }
-       if (np->rxopt.bits.srcrt && opt->srcrt) {
+       if (np->rxopt.bits.osrcrt && opt->srcrt) {
                struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
-               put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
+               put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
        }
-       if (np->rxopt.bits.dstopts && opt->dst1) {
+       if (np->rxopt.bits.odstopts && opt->dst1) {
                u8 *ptr = skb->nh.raw + opt->dst1;
-               put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
+               put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
        }
        return 0;
 }
 
 int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
                      struct ipv6_txoptions *opt,
-                     int *hlimit)
+                     int *hlimit, int *tclass)
 {
        struct in6_pktinfo *src_info;
        struct cmsghdr *cmsg;
@@ -438,6 +507,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
 
                switch (cmsg->cmsg_type) {
                case IPV6_PKTINFO:
+               case IPV6_2292PKTINFO:
                        if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
                                err = -EINVAL;
                                goto exit_f;
@@ -492,6 +562,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
                        fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
                        break;
 
+               case IPV6_2292HOPOPTS:
                case IPV6_HOPOPTS:
                         if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
                                err = -EINVAL;
@@ -512,7 +583,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
                        opt->hopopt = hdr;
                        break;
 
-               case IPV6_DSTOPTS:
+               case IPV6_2292DSTOPTS:
                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
                                err = -EINVAL;
                                goto exit_f;
@@ -536,6 +607,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
                        opt->dst1opt = hdr;
                        break;
 
+               case IPV6_DSTOPTS:
+               case IPV6_RTHDRDSTOPTS:
+                       if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
+                               err = -EINVAL;
+                               goto exit_f;
+                       }
+
+                       hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
+                       len = ((hdr->hdrlen + 1) << 3);
+                       if (cmsg->cmsg_len < CMSG_LEN(len)) {
+                               err = -EINVAL;
+                               goto exit_f;
+                       }
+                       if (!capable(CAP_NET_RAW)) {
+                               err = -EPERM;
+                               goto exit_f;
+                       }
+                       if (cmsg->cmsg_type == IPV6_DSTOPTS) {
+                               opt->opt_flen += len;
+                               opt->dst1opt = hdr;
+                       } else {
+                               opt->opt_nflen += len;
+                               opt->dst0opt = hdr;
+                       }
+                       break;
+
+               case IPV6_2292RTHDR:
                case IPV6_RTHDR:
                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
                                err = -EINVAL;
@@ -568,7 +666,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
                        opt->opt_nflen += len;
                        opt->srcrt = rthdr;
 
-                       if (opt->dst1opt) {
+                       if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
                                int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
 
                                opt->opt_nflen += dsthdrlen;
@@ -579,6 +677,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
 
                        break;
 
+               case IPV6_2292HOPLIMIT:
                case IPV6_HOPLIMIT:
                        if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
                                err = -EINVAL;
@@ -588,6 +687,24 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
                        *hlimit = *(int *)CMSG_DATA(cmsg);
                        break;
 
+               case IPV6_TCLASS:
+                   {
+                       int tc;
+
+                       err = -EINVAL;
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
+                               goto exit_f;
+                       }
+
+                       tc = *(int *)CMSG_DATA(cmsg);
+                       if (tc < 0 || tc > 0xff)
+                               goto exit_f;
+
+                       err = 0;
+                       *tclass = tc;
+
+                       break;
+                   }
                default:
                        LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n",
                                       cmsg->cmsg_type);
index 3b9fa90..4712272 100644 (file)
@@ -164,6 +164,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
                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 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
 
 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;
@@ -538,10 +540,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
                          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 +579,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
        }
        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);
+}
+
index fa8f1bb..34e99c5 100644 (file)
@@ -287,7 +287,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
        int iif = 0;
        int addr_type = 0;
        int len;
-       int hlimit;
+       int hlimit, tclass;
        int err = 0;
 
        if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail)
@@ -385,6 +385,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
        if (hlimit < 0)
                hlimit = ipv6_get_hoplimit(dst->dev);
 
+       tclass = np->cork.tclass;
+       if (tclass < 0)
+               tclass = 0;
+
        msg.skb = skb;
        msg.offset = skb->nh.raw - skb->data;
 
@@ -400,7 +404,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
        err = ip6_append_data(sk, icmpv6_getfrag, &msg,
                              len + sizeof(struct icmp6hdr),
                              sizeof(struct icmp6hdr),
-                             hlimit, NULL, &fl, (struct rt6_info*)dst,
+                             hlimit, tclass, NULL, &fl, (struct rt6_info*)dst,
                              MSG_DONTWAIT);
        if (err) {
                ip6_flush_pending_frames(sk);
@@ -434,6 +438,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        struct dst_entry *dst;
        int err = 0;
        int hlimit;
+       int tclass;
 
        saddr = &skb->nh.ipv6h->daddr;
 
@@ -475,13 +480,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        if (hlimit < 0)
                hlimit = ipv6_get_hoplimit(dst->dev);
 
+       tclass = np->cork.tclass;
+       if (tclass < 0)
+               tclass = 0;
+
        idev = in6_dev_get(skb->dev);
 
        msg.skb = skb;
        msg.offset = 0;
 
        err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
-                               sizeof(struct icmp6hdr), hlimit, NULL, &fl,
+                               sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl,
                                (struct rt6_info*)dst, MSG_DONTWAIT);
 
        if (err) {
index b6c73da..a7db762 100644 (file)
@@ -225,16 +225,20 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
                                         struct ip6_flowlabel * fl,
                                         struct ipv6_txoptions * fopt)
 {
-       struct ipv6_txoptions * fl_opt = fl->opt;
+       struct ipv6_txoptions * fl_opt = fl ? fl->opt : NULL;
 
-       if (fopt == NULL || fopt->opt_flen == 0)
-               return fl_opt;
+       if (fopt == NULL || fopt->opt_flen == 0) {
+               if (!fl_opt || !fl_opt->dst0opt || fl_opt->srcrt)
+                       return fl_opt;
+       }
 
        if (fl_opt != NULL) {
                opt_space->hopopt = fl_opt->hopopt;
-               opt_space->dst0opt = fl_opt->dst0opt;
+               opt_space->dst0opt = fl_opt->srcrt ? fl_opt->dst0opt : NULL;
                opt_space->srcrt = fl_opt->srcrt;
                opt_space->opt_nflen = fl_opt->opt_nflen;
+               if (fl_opt->dst0opt && !fl_opt->srcrt)
+                       opt_space->opt_nflen -= ipv6_optlen(fl_opt->dst0opt);
        } else {
                if (fopt->opt_nflen == 0)
                        return fopt;
@@ -310,7 +314,7 @@ fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *
                msg.msg_control = (void*)(fl->opt+1);
                flowi.oif = 0;
 
-               err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk);
+               err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk, &junk);
                if (err)
                        goto done;
                err = -EINVAL;
index 01ef94f..2f589f2 100644 (file)
@@ -166,7 +166,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        struct ipv6hdr *hdr;
        u8  proto = fl->proto;
        int seg_len = skb->len;
-       int hlimit;
+       int hlimit, tclass;
        u32 mtu;
 
        if (opt) {
@@ -202,7 +202,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
         *      Fill in the IPv6 header
         */
 
-       *(u32*)hdr = htonl(0x60000000) | fl->fl6_flowlabel;
        hlimit = -1;
        if (np)
                hlimit = np->hop_limit;
@@ -211,6 +210,14 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        if (hlimit < 0)
                hlimit = ipv6_get_hoplimit(dst->dev);
 
+       tclass = -1;
+       if (np)
+               tclass = np->tclass;
+       if (tclass < 0)
+               tclass = 0;
+
+       *(u32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
+
        hdr->payload_len = htons(seg_len);
        hdr->nexthdr = proto;
        hdr->hop_limit = hlimit;
@@ -762,10 +769,11 @@ out_err_release:
        return err;
 }
 
-int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb),
-                   void *from, int length, int transhdrlen,
-                   int hlimit, struct ipv6_txoptions *opt, struct flowi *fl, struct rt6_info *rt,
-                   unsigned int flags)
+int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
+       int offset, int len, int odd, struct sk_buff *skb),
+       void *from, int length, int transhdrlen,
+       int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,
+       struct rt6_info *rt, unsigned int flags)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -803,6 +811,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
                np->cork.rt = rt;
                inet->cork.fl = *fl;
                np->cork.hop_limit = hlimit;
+               np->cork.tclass = tclass;
                inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
                if (dst_allfrag(rt->u.dst.path))
                        inet->cork.flags |= IPCORK_ALLFRAG;
@@ -1084,7 +1093,8 @@ int ip6_push_pending_frames(struct sock *sk)
 
        skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr));
        
-       *(u32*)hdr = fl->fl6_flowlabel | htonl(0x60000000);
+       *(u32*)hdr = fl->fl6_flowlabel |
+                    htonl(0x60000000 | ((int)np->cork.tclass << 20));
 
        if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN)
                hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
index 76466af..8567873 100644 (file)
@@ -210,39 +210,139 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
                retv = 0;
                break;
 
-       case IPV6_PKTINFO:
+       case IPV6_RECVPKTINFO:
                np->rxopt.bits.rxinfo = valbool;
                retv = 0;
                break;
+               
+       case IPV6_2292PKTINFO:
+               np->rxopt.bits.rxoinfo = valbool;
+               retv = 0;
+               break;
 
-       case IPV6_HOPLIMIT:
+       case IPV6_RECVHOPLIMIT:
                np->rxopt.bits.rxhlim = valbool;
                retv = 0;
                break;
 
-       case IPV6_RTHDR:
+       case IPV6_2292HOPLIMIT:
+               np->rxopt.bits.rxohlim = valbool;
+               retv = 0;
+               break;
+
+       case IPV6_RECVRTHDR:
                if (val < 0 || val > 2)
                        goto e_inval;
                np->rxopt.bits.srcrt = val;
                retv = 0;
                break;
 
-       case IPV6_HOPOPTS:
+       case IPV6_2292RTHDR:
+               if (val < 0 || val > 2)
+                       goto e_inval;
+               np->rxopt.bits.osrcrt = val;
+               retv = 0;
+               break;
+
+       case IPV6_RECVHOPOPTS:
                np->rxopt.bits.hopopts = valbool;
                retv = 0;
                break;
 
-       case IPV6_DSTOPTS:
+       case IPV6_2292HOPOPTS:
+               np->rxopt.bits.ohopopts = valbool;
+               retv = 0;
+               break;
+
+       case IPV6_RECVDSTOPTS:
                np->rxopt.bits.dstopts = valbool;
                retv = 0;
                break;
 
+       case IPV6_2292DSTOPTS:
+               np->rxopt.bits.odstopts = valbool;
+               retv = 0;
+               break;
+
+       case IPV6_TCLASS:
+               if (val < 0 || val > 0xff)
+                       goto e_inval;
+               np->tclass = val;
+               retv = 0;
+               break;
+               
+       case IPV6_RECVTCLASS:
+               np->rxopt.bits.rxtclass = valbool;
+               retv = 0;
+               break;
+
        case IPV6_FLOWINFO:
                np->rxopt.bits.rxflow = valbool;
                retv = 0;
                break;
 
-       case IPV6_PKTOPTIONS:
+       case IPV6_HOPOPTS:
+       case IPV6_RTHDRDSTOPTS:
+       case IPV6_RTHDR:
+       case IPV6_DSTOPTS:
+       {
+               struct ipv6_txoptions *opt;
+               if (optlen == 0)
+                       optval = 0;
+
+               /* hop-by-hop / destination options are privileged option */
+               retv = -EPERM;
+               if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))
+                       break;
+
+               retv = -EINVAL;
+               if (optlen & 0x7 || optlen > 8 * 255)
+                       break;
+
+               opt = ipv6_renew_options(sk, np->opt, optname,
+                                        (struct ipv6_opt_hdr __user *)optval,
+                                        optlen);
+               if (IS_ERR(opt)) {
+                       retv = PTR_ERR(opt);
+                       break;
+               }
+
+               /* routing header option needs extra check */
+               if (optname == IPV6_RTHDR && opt->srcrt) {
+                       struct ipv6_rt_hdr *rthdr = opt->srcrt;
+                       if (rthdr->type)
+                               goto sticky_done;
+                       if ((rthdr->hdrlen & 1) ||
+                           (rthdr->hdrlen >> 1) != rthdr->segments_left)
+                               goto sticky_done;
+               }
+
+               retv = 0;
+               if (sk->sk_type == SOCK_STREAM) {
+                       if (opt) {
+                               struct tcp_sock *tp = tcp_sk(sk);
+                               if (!((1 << sk->sk_state) &
+                                     (TCPF_LISTEN | TCPF_CLOSE))
+                                   && inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
+                                       tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
+                                       tcp_sync_mss(sk, tp->pmtu_cookie);
+                               }
+                       }
+                       opt = xchg(&np->opt, opt);
+                       sk_dst_reset(sk);
+               } else {
+                       write_lock(&sk->sk_dst_lock);
+                       opt = xchg(&np->opt, opt);
+                       write_unlock(&sk->sk_dst_lock);
+                       sk_dst_reset(sk);
+               }
+sticky_done:
+               if (opt)
+                       sock_kfree_s(sk, opt, opt->tot_len);
+               break;
+       }
+
+       case IPV6_2292PKTOPTIONS:
        {
                struct ipv6_txoptions *opt = NULL;
                struct msghdr msg;
@@ -276,7 +376,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
                msg.msg_controllen = optlen;
                msg.msg_control = (void*)(opt+1);
 
-               retv = datagram_send_ctl(&msg, &fl, opt, &junk);
+               retv = datagram_send_ctl(&msg, &fl, opt, &junk, &junk);
                if (retv)
                        goto done;
 update:
@@ -529,6 +629,17 @@ e_inval:
        return -EINVAL;
 }
 
+int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_opt_hdr *hdr,
+                          char __user *optval, int len)
+{
+       if (!hdr)
+               return 0;
+       len = min_t(int, len, ipv6_optlen(hdr));
+       if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
+               return -EFAULT;
+       return len;
+}
+
 int ipv6_getsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, int __user *optlen)
 {
@@ -567,7 +678,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
                return err;
        }
 
-       case IPV6_PKTOPTIONS:
+       case IPV6_2292PKTOPTIONS:
        {
                struct msghdr msg;
                struct sk_buff *skb;
@@ -601,6 +712,16 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
                                int hlim = np->mcast_hops;
                                put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
                        }
+                       if (np->rxopt.bits.rxoinfo) {
+                               struct in6_pktinfo src_info;
+                               src_info.ipi6_ifindex = np->mcast_oif;
+                               ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
+                               put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
+                       }
+                       if (np->rxopt.bits.rxohlim) {
+                               int hlim = np->mcast_hops;
+                               put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
+                       }
                }
                len -= msg.msg_controllen;
                return put_user(len, optlen);
@@ -625,26 +746,67 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
                val = np->ipv6only;
                break;
 
-       case IPV6_PKTINFO:
+       case IPV6_RECVPKTINFO:
                val = np->rxopt.bits.rxinfo;
                break;
 
-       case IPV6_HOPLIMIT:
+       case IPV6_2292PKTINFO:
+               val = np->rxopt.bits.rxoinfo;
+               break;
+
+       case IPV6_RECVHOPLIMIT:
                val = np->rxopt.bits.rxhlim;
                break;
 
-       case IPV6_RTHDR:
+       case IPV6_2292HOPLIMIT:
+               val = np->rxopt.bits.rxohlim;
+               break;
+
+       case IPV6_RECVRTHDR:
                val = np->rxopt.bits.srcrt;
                break;
 
+       case IPV6_2292RTHDR:
+               val = np->rxopt.bits.osrcrt;
+               break;
+
        case IPV6_HOPOPTS:
+       case IPV6_RTHDRDSTOPTS:
+       case IPV6_RTHDR:
+       case IPV6_DSTOPTS:
+       {
+
+               lock_sock(sk);
+               len = ipv6_getsockopt_sticky(sk, np->opt->hopopt,
+                                            optval, len);
+               release_sock(sk);
+               return put_user(len, optlen);
+       }
+
+       case IPV6_RECVHOPOPTS:
                val = np->rxopt.bits.hopopts;
                break;
 
-       case IPV6_DSTOPTS:
+       case IPV6_2292HOPOPTS:
+               val = np->rxopt.bits.ohopopts;
+               break;
+
+       case IPV6_RECVDSTOPTS:
                val = np->rxopt.bits.dstopts;
                break;
 
+       case IPV6_2292DSTOPTS:
+               val = np->rxopt.bits.odstopts;
+               break;
+
+       case IPV6_TCLASS:
+               val = np->tclass;
+               break;
+
+       case IPV6_RECVTCLASS:
+               val = np->rxopt.bits.rxtclass;
+               break;
+
        case IPV6_FLOWINFO:
                val = np->rxopt.bits.rxflow;
                break;
index ed3a76b..2ad3789 100644 (file)
@@ -655,6 +655,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
        struct flowi fl;
        int addr_len = msg->msg_namelen;
        int hlimit = -1;
+       int tclass = -1;
        u16 proto;
        int err;
 
@@ -740,7 +741,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;
@@ -755,8 +756,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
        }
        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 = proto;
        rawv6_probe_proto_opt(&fl, msg);
@@ -798,6 +798,12 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                        hlimit = ipv6_get_hoplimit(dst->dev);
        }
 
+       if (tclass < 0) {
+               tclass = np->cork.tclass;
+               if (tclass < 0)
+                       tclass = 0;
+       }
+
        if (msg->msg_flags&MSG_CONFIRM)
                goto do_confirm;
 
@@ -806,8 +812,9 @@ 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);
index 794734f..246414b 100644 (file)
@@ -849,7 +849,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
        if (dst == NULL) {
                opt = np->opt;
                if (opt == NULL &&
-                   np->rxopt.bits.srcrt == 2 &&
+                   np->rxopt.bits.osrcrt == 2 &&
                    treq->pktopts) {
                        struct sk_buff *pktopts = treq->pktopts;
                        struct inet6_skb_parm *rxopt = IP6CB(pktopts);
@@ -915,11 +915,10 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
        struct inet6_skb_parm *opt = IP6CB(skb);
 
        if (np->rxopt.all) {
-               if ((opt->hop && np->rxopt.bits.hopopts) ||
-                   ((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) &&
-                    np->rxopt.bits.rxflow) ||
-                   (opt->srcrt && np->rxopt.bits.srcrt) ||
-                   ((opt->dst1 || opt->dst0) && np->rxopt.bits.dstopts))
+               if ((opt->hop && (np->rxopt.bits.hopopts || np->rxopt.bits.ohopopts)) ||
+                   ((IPV6_FLOWINFO_MASK & *(u32*)skb->nh.raw) && np->rxopt.bits.rxflow) ||
+                   (opt->srcrt && (np->rxopt.bits.srcrt || np->rxopt.bits.osrcrt)) ||
+                   ((opt->dst1 || opt->dst0) && (np->rxopt.bits.dstopts || np->rxopt.bits.odstopts)))
                        return 1;
        }
        return 0;
@@ -1190,8 +1189,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        TCP_ECN_create_request(req, skb->h.th);
        treq->pktopts = NULL;
        if (ipv6_opt_accepted(sk, skb) ||
-           np->rxopt.bits.rxinfo ||
-           np->rxopt.bits.rxhlim) {
+           np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+           np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
                atomic_inc(&skb->users);
                treq->pktopts = skb;
        }
@@ -1288,7 +1287,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        if (sk_acceptq_is_full(sk))
                goto out_overflow;
 
-       if (np->rxopt.bits.srcrt == 2 &&
+       if (np->rxopt.bits.osrcrt == 2 &&
            opt == NULL && treq->pktopts) {
                struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts);
                if (rxopt->srcrt)
@@ -1544,9 +1543,9 @@ ipv6_pktoptions:
        tp = tcp_sk(sk);
        if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt &&
            !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) {
-               if (np->rxopt.bits.rxinfo)
+               if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
                        np->mcast_oif = inet6_iif(opt_skb);
-               if (np->rxopt.bits.rxhlim)
+               if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
                        np->mcast_hops = opt_skb->nh.ipv6h->hop_limit;
                if (ipv6_opt_accepted(sk, opt_skb)) {
                        skb_set_owner_r(opt_skb, sk);
index 7cbcaa3..f5ae148 100644 (file)
@@ -637,6 +637,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
        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 @@ 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;
@@ -773,8 +774,7 @@ do_udp_sendmsg:
        }
        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);
@@ -815,6 +815,12 @@ do_udp_sendmsg:
                        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:
@@ -834,9 +840,10 @@ 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)