aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2005-09-08 15:59:43 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2005-09-08 15:59:43 -0400
commit2e66fc41169c90d93b7811caf7e7822de6aa2259 (patch)
tree014db698b03f898c560919e56fbdae8ca6ad3dd5
parent42ca89c18b75e1c4c3b02aa5589ad3aa916909a8 (diff)
parent41a1f8ea4fbfcdc4232f023732584aae2220de31 (diff)
Merge git://git.skbuff.net/gitroot/yoshfuji/linux-2.6-git-rfc3542
-rw-r--r--include/linux/in6.h36
-rw-r--r--include/linux/ipv6.h15
-rw-r--r--include/net/ipv6.h5
-rw-r--r--include/net/transp_v6.h2
-rw-r--r--net/ipv6/datagram.c135
-rw-r--r--net/ipv6/exthdrs.c107
-rw-r--r--net/ipv6/icmp.c15
-rw-r--r--net/ipv6/ip6_flowlabel.c14
-rw-r--r--net/ipv6/ip6_output.c24
-rw-r--r--net/ipv6/ipv6_sockglue.c186
-rw-r--r--net/ipv6/raw.c17
-rw-r--r--net/ipv6/tcp_ipv6.c21
-rw-r--r--net/ipv6/udp.c19
13 files changed, 525 insertions, 71 deletions
diff --git a/include/linux/in6.h b/include/linux/in6.h
index dcf5720ffcbb..bd32b79d6295 100644
--- a/include/linux/in6.h
+++ b/include/linux/in6.h
@@ -148,13 +148,13 @@ struct in6_flowlabel_req
148 */ 148 */
149 149
150#define IPV6_ADDRFORM 1 150#define IPV6_ADDRFORM 1
151#define IPV6_PKTINFO 2 151#define IPV6_2292PKTINFO 2
152#define IPV6_HOPOPTS 3 152#define IPV6_2292HOPOPTS 3
153#define IPV6_DSTOPTS 4 153#define IPV6_2292DSTOPTS 4
154#define IPV6_RTHDR 5 154#define IPV6_2292RTHDR 5
155#define IPV6_PKTOPTIONS 6 155#define IPV6_2292PKTOPTIONS 6
156#define IPV6_CHECKSUM 7 156#define IPV6_CHECKSUM 7
157#define IPV6_HOPLIMIT 8 157#define IPV6_2292HOPLIMIT 8
158#define IPV6_NEXTHOP 9 158#define IPV6_NEXTHOP 9
159#define IPV6_AUTHHDR 10 /* obsolete */ 159#define IPV6_AUTHHDR 10 /* obsolete */
160#define IPV6_FLOWINFO 11 160#define IPV6_FLOWINFO 11
@@ -198,4 +198,28 @@ struct in6_flowlabel_req
198 * MCAST_MSFILTER 48 198 * MCAST_MSFILTER 48
199 */ 199 */
200 200
201/* RFC3542 advanced socket options (50-67) */
202#define IPV6_RECVPKTINFO 50
203#define IPV6_PKTINFO 51
204#if 0
205#define IPV6_RECVPATHMTU 52
206#define IPV6_PATHMTU 53
207#define IPV6_DONTFRAG 54
208#define IPV6_USE_MIN_MTU 55
209#endif
210#define IPV6_RECVHOPOPTS 56
211#define IPV6_HOPOPTS 57
212#if 0
213#define IPV6_RECVRTHDRDSTOPTS 58 /* Unused, see net/ipv6/datagram.c */
214#endif
215#define IPV6_RTHDRDSTOPTS 59
216#define IPV6_RECVRTHDR 60
217#define IPV6_RTHDR 61
218#define IPV6_RECVDSTOPTS 62
219#define IPV6_DSTOPTS 63
220#define IPV6_RECVHOPLIMIT 64
221#define IPV6_HOPLIMIT 65
222#define IPV6_RECVTCLASS 66
223#define IPV6_TCLASS 67
224
201#endif 225#endif
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 3c7dbc6a0a70..6c5f7b39a4b0 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -189,6 +189,7 @@ struct inet6_skb_parm {
189 __u16 dst0; 189 __u16 dst0;
190 __u16 srcrt; 190 __u16 srcrt;
191 __u16 dst1; 191 __u16 dst1;
192 __u16 lastopt;
192}; 193};
193 194
194#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) 195#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
@@ -234,14 +235,20 @@ struct ipv6_pinfo {
234 /* pktoption flags */ 235 /* pktoption flags */
235 union { 236 union {
236 struct { 237 struct {
237 __u8 srcrt:2, 238 __u16 srcrt:2,
239 osrcrt:2,
238 rxinfo:1, 240 rxinfo:1,
241 rxoinfo:1,
239 rxhlim:1, 242 rxhlim:1,
243 rxohlim:1,
240 hopopts:1, 244 hopopts:1,
245 ohopopts:1,
241 dstopts:1, 246 dstopts:1,
242 rxflow:1; 247 odstopts:1,
248 rxflow:1,
249 rxtclass:1;
243 } bits; 250 } bits;
244 __u8 all; 251 __u16 all;
245 } rxopt; 252 } rxopt;
246 253
247 /* sockopt flags */ 254 /* sockopt flags */
@@ -250,6 +257,7 @@ struct ipv6_pinfo {
250 sndflow:1, 257 sndflow:1,
251 pmtudisc:2, 258 pmtudisc:2,
252 ipv6only:1; 259 ipv6only:1;
260 __u8 tclass;
253 261
254 __u32 dst_cookie; 262 __u32 dst_cookie;
255 263
@@ -263,6 +271,7 @@ struct ipv6_pinfo {
263 struct ipv6_txoptions *opt; 271 struct ipv6_txoptions *opt;
264 struct rt6_info *rt; 272 struct rt6_info *rt;
265 int hop_limit; 273 int hop_limit;
274 int tclass;
266 } cork; 275 } cork;
267}; 276};
268 277
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 3203eaff4bd4..65ec86678a08 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -233,6 +233,10 @@ extern int ip6_ra_control(struct sock *sk, int sel,
233extern int ipv6_parse_hopopts(struct sk_buff *skb, int); 233extern int ipv6_parse_hopopts(struct sk_buff *skb, int);
234 234
235extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt); 235extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
236extern struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
237 int newtype,
238 struct ipv6_opt_hdr __user *newopt,
239 int newoptlen);
236 240
237extern int ip6_frag_nqueues; 241extern int ip6_frag_nqueues;
238extern atomic_t ip6_frag_mem; 242extern atomic_t ip6_frag_mem;
@@ -373,6 +377,7 @@ extern int ip6_append_data(struct sock *sk,
373 int length, 377 int length,
374 int transhdrlen, 378 int transhdrlen,
375 int hlimit, 379 int hlimit,
380 int tclass,
376 struct ipv6_txoptions *opt, 381 struct ipv6_txoptions *opt,
377 struct flowi *fl, 382 struct flowi *fl,
378 struct rt6_info *rt, 383 struct rt6_info *rt,
diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h
index 8b075ab7a26c..4e86f2de6638 100644
--- a/include/net/transp_v6.h
+++ b/include/net/transp_v6.h
@@ -37,7 +37,7 @@ extern int datagram_recv_ctl(struct sock *sk,
37extern int datagram_send_ctl(struct msghdr *msg, 37extern int datagram_send_ctl(struct msghdr *msg,
38 struct flowi *fl, 38 struct flowi *fl,
39 struct ipv6_txoptions *opt, 39 struct ipv6_txoptions *opt,
40 int *hlimit); 40 int *hlimit, int *tclass);
41 41
42#define LOOPBACK4_IPV6 __constant_htonl(0x7f000006) 42#define LOOPBACK4_IPV6 __constant_htonl(0x7f000006)
43 43
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 01468fab3d3d..157cec648032 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -390,32 +390,101 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
390 put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); 390 put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
391 } 391 }
392 392
393 if (np->rxopt.bits.rxtclass) {
394 int tclass = (ntohl(*(u32 *)skb->nh.ipv6h) >> 20) & 0xff;
395 put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
396 }
397
393 if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) { 398 if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) {
394 u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK; 399 u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
395 put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); 400 put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
396 } 401 }
402
403 /* HbH is allowed only once */
397 if (np->rxopt.bits.hopopts && opt->hop) { 404 if (np->rxopt.bits.hopopts && opt->hop) {
398 u8 *ptr = skb->nh.raw + opt->hop; 405 u8 *ptr = skb->nh.raw + opt->hop;
399 put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); 406 put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
400 } 407 }
401 if (np->rxopt.bits.dstopts && opt->dst0) { 408
409 if (opt->lastopt &&
410 (np->rxopt.bits.dstopts || np->rxopt.bits.srcrt)) {
411 /*
412 * Silly enough, but we need to reparse in order to
413 * report extension headers (except for HbH)
414 * in order.
415 *
416 * Also note that IPV6_RECVRTHDRDSTOPTS is NOT
417 * (and WILL NOT be) defined because
418 * IPV6_RECVDSTOPTS is more generic. --yoshfuji
419 */
420 unsigned int off = sizeof(struct ipv6hdr);
421 u8 nexthdr = skb->nh.ipv6h->nexthdr;
422
423 while (off <= opt->lastopt) {
424 unsigned len;
425 u8 *ptr = skb->nh.raw + off;
426
427 switch(nexthdr) {
428 case IPPROTO_DSTOPTS:
429 nexthdr = ptr[0];
430 len = (ptr[1] + 1) << 3;
431 if (np->rxopt.bits.dstopts)
432 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
433 break;
434 case IPPROTO_ROUTING:
435 nexthdr = ptr[0];
436 len = (ptr[1] + 1) << 3;
437 if (np->rxopt.bits.srcrt)
438 put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
439 break;
440 case IPPROTO_AH:
441 nexthdr = ptr[0];
442 len = (ptr[1] + 1) << 2;
443 break;
444 default:
445 nexthdr = ptr[0];
446 len = (ptr[1] + 1) << 3;
447 break;
448 }
449
450 off += len;
451 }
452 }
453
454 /* socket options in old style */
455 if (np->rxopt.bits.rxoinfo) {
456 struct in6_pktinfo src_info;
457
458 src_info.ipi6_ifindex = opt->iif;
459 ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
460 put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
461 }
462 if (np->rxopt.bits.rxohlim) {
463 int hlim = skb->nh.ipv6h->hop_limit;
464 put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
465 }
466 if (np->rxopt.bits.ohopopts && opt->hop) {
467 u8 *ptr = skb->nh.raw + opt->hop;
468 put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
469 }
470 if (np->rxopt.bits.odstopts && opt->dst0) {
402 u8 *ptr = skb->nh.raw + opt->dst0; 471 u8 *ptr = skb->nh.raw + opt->dst0;
403 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); 472 put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
404 } 473 }
405 if (np->rxopt.bits.srcrt && opt->srcrt) { 474 if (np->rxopt.bits.osrcrt && opt->srcrt) {
406 struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); 475 struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
407 put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr); 476 put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
408 } 477 }
409 if (np->rxopt.bits.dstopts && opt->dst1) { 478 if (np->rxopt.bits.odstopts && opt->dst1) {
410 u8 *ptr = skb->nh.raw + opt->dst1; 479 u8 *ptr = skb->nh.raw + opt->dst1;
411 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); 480 put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
412 } 481 }
413 return 0; 482 return 0;
414} 483}
415 484
416int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, 485int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
417 struct ipv6_txoptions *opt, 486 struct ipv6_txoptions *opt,
418 int *hlimit) 487 int *hlimit, int *tclass)
419{ 488{
420 struct in6_pktinfo *src_info; 489 struct in6_pktinfo *src_info;
421 struct cmsghdr *cmsg; 490 struct cmsghdr *cmsg;
@@ -438,6 +507,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
438 507
439 switch (cmsg->cmsg_type) { 508 switch (cmsg->cmsg_type) {
440 case IPV6_PKTINFO: 509 case IPV6_PKTINFO:
510 case IPV6_2292PKTINFO:
441 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) { 511 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
442 err = -EINVAL; 512 err = -EINVAL;
443 goto exit_f; 513 goto exit_f;
@@ -492,6 +562,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
492 fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg); 562 fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
493 break; 563 break;
494 564
565 case IPV6_2292HOPOPTS:
495 case IPV6_HOPOPTS: 566 case IPV6_HOPOPTS:
496 if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { 567 if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
497 err = -EINVAL; 568 err = -EINVAL;
@@ -512,7 +583,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
512 opt->hopopt = hdr; 583 opt->hopopt = hdr;
513 break; 584 break;
514 585
515 case IPV6_DSTOPTS: 586 case IPV6_2292DSTOPTS:
516 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { 587 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
517 err = -EINVAL; 588 err = -EINVAL;
518 goto exit_f; 589 goto exit_f;
@@ -536,6 +607,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
536 opt->dst1opt = hdr; 607 opt->dst1opt = hdr;
537 break; 608 break;
538 609
610 case IPV6_DSTOPTS:
611 case IPV6_RTHDRDSTOPTS:
612 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
613 err = -EINVAL;
614 goto exit_f;
615 }
616
617 hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
618 len = ((hdr->hdrlen + 1) << 3);
619 if (cmsg->cmsg_len < CMSG_LEN(len)) {
620 err = -EINVAL;
621 goto exit_f;
622 }
623 if (!capable(CAP_NET_RAW)) {
624 err = -EPERM;
625 goto exit_f;
626 }
627 if (cmsg->cmsg_type == IPV6_DSTOPTS) {
628 opt->opt_flen += len;
629 opt->dst1opt = hdr;
630 } else {
631 opt->opt_nflen += len;
632 opt->dst0opt = hdr;
633 }
634 break;
635
636 case IPV6_2292RTHDR:
539 case IPV6_RTHDR: 637 case IPV6_RTHDR:
540 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) { 638 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
541 err = -EINVAL; 639 err = -EINVAL;
@@ -568,7 +666,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
568 opt->opt_nflen += len; 666 opt->opt_nflen += len;
569 opt->srcrt = rthdr; 667 opt->srcrt = rthdr;
570 668
571 if (opt->dst1opt) { 669 if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
572 int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3); 670 int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
573 671
574 opt->opt_nflen += dsthdrlen; 672 opt->opt_nflen += dsthdrlen;
@@ -579,6 +677,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
579 677
580 break; 678 break;
581 679
680 case IPV6_2292HOPLIMIT:
582 case IPV6_HOPLIMIT: 681 case IPV6_HOPLIMIT:
583 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { 682 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
584 err = -EINVAL; 683 err = -EINVAL;
@@ -588,6 +687,24 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
588 *hlimit = *(int *)CMSG_DATA(cmsg); 687 *hlimit = *(int *)CMSG_DATA(cmsg);
589 break; 688 break;
590 689
690 case IPV6_TCLASS:
691 {
692 int tc;
693
694 err = -EINVAL;
695 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
696 goto exit_f;
697 }
698
699 tc = *(int *)CMSG_DATA(cmsg);
700 if (tc < 0 || tc > 0xff)
701 goto exit_f;
702
703 err = 0;
704 *tclass = tc;
705
706 break;
707 }
591 default: 708 default:
592 LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n", 709 LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n",
593 cmsg->cmsg_type); 710 cmsg->cmsg_type);
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 3b9fa900a4bf..47122728212a 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -164,6 +164,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
164 return -1; 164 return -1;
165 } 165 }
166 166
167 opt->lastopt = skb->h.raw - skb->nh.raw;
167 opt->dst1 = skb->h.raw - skb->nh.raw; 168 opt->dst1 = skb->h.raw - skb->nh.raw;
168 169
169 if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { 170 if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
@@ -243,6 +244,7 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
243 244
244looped_back: 245looped_back:
245 if (hdr->segments_left == 0) { 246 if (hdr->segments_left == 0) {
247 opt->lastopt = skb->h.raw - skb->nh.raw;
246 opt->srcrt = skb->h.raw - skb->nh.raw; 248 opt->srcrt = skb->h.raw - skb->nh.raw;
247 skb->h.raw += (hdr->hdrlen + 1) << 3; 249 skb->h.raw += (hdr->hdrlen + 1) << 3;
248 opt->dst0 = opt->dst1; 250 opt->dst0 = opt->dst1;
@@ -538,10 +540,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
538 u8 *proto, 540 u8 *proto,
539 struct in6_addr **daddr) 541 struct in6_addr **daddr)
540{ 542{
541 if (opt->srcrt) 543 if (opt->srcrt) {
542 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); 544 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
543 if (opt->dst0opt) 545 /*
544 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); 546 * IPV6_RTHDRDSTOPTS is ignored
547 * unless IPV6_RTHDR is set (RFC3542).
548 */
549 if (opt->dst0opt)
550 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
551 }
545 if (opt->hopopt) 552 if (opt->hopopt)
546 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); 553 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
547} 554}
@@ -572,3 +579,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
572 } 579 }
573 return opt2; 580 return opt2;
574} 581}
582
583static int ipv6_renew_option(void *ohdr,
584 struct ipv6_opt_hdr __user *newopt, int newoptlen,
585 int inherit,
586 struct ipv6_opt_hdr **hdr,
587 char **p)
588{
589 if (inherit) {
590 if (ohdr) {
591 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
592 *hdr = (struct ipv6_opt_hdr *)*p;
593 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
594 }
595 } else {
596 if (newopt) {
597 if (copy_from_user(*p, newopt, newoptlen))
598 return -EFAULT;
599 *hdr = (struct ipv6_opt_hdr *)*p;
600 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
601 return -EINVAL;
602 *p += CMSG_ALIGN(newoptlen);
603 }
604 }
605 return 0;
606}
607
608struct ipv6_txoptions *
609ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
610 int newtype,
611 struct ipv6_opt_hdr __user *newopt, int newoptlen)
612{
613 int tot_len = 0;
614 char *p;
615 struct ipv6_txoptions *opt2;
616 int err;
617
618 if (newtype != IPV6_HOPOPTS && opt->hopopt)
619 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
620 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
621 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
622 if (newtype != IPV6_RTHDR && opt->srcrt)
623 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
624 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
625 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
626 if (newopt && newoptlen)
627 tot_len += CMSG_ALIGN(newoptlen);
628
629 if (!tot_len)
630 return NULL;
631
632 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
633 if (!opt2)
634 return ERR_PTR(-ENOBUFS);
635
636 memset(opt2, 0, tot_len);
637
638 opt2->tot_len = tot_len;
639 p = (char *)(opt2 + 1);
640
641 err = ipv6_renew_option(opt->hopopt, newopt, newoptlen,
642 newtype != IPV6_HOPOPTS,
643 &opt2->hopopt, &p);
644 if (err)
645 goto out;
646
647 err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen,
648 newtype != IPV6_RTHDRDSTOPTS,
649 &opt2->dst0opt, &p);
650 if (err)
651 goto out;
652
653 err = ipv6_renew_option(opt->srcrt, newopt, newoptlen,
654 newtype != IPV6_RTHDR,
655 (struct ipv6_opt_hdr **)opt2->srcrt, &p);
656 if (err)
657 goto out;
658
659 err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen,
660 newtype != IPV6_DSTOPTS,
661 &opt2->dst1opt, &p);
662 if (err)
663 goto out;
664
665 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
666 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
667 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
668 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
669
670 return opt2;
671out:
672 sock_kfree_s(sk, p, tot_len);
673 return ERR_PTR(err);
674}
675
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index fa8f1bb0aa52..34e99c55e856 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -287,7 +287,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
287 int iif = 0; 287 int iif = 0;
288 int addr_type = 0; 288 int addr_type = 0;
289 int len; 289 int len;
290 int hlimit; 290 int hlimit, tclass;
291 int err = 0; 291 int err = 0;
292 292
293 if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail) 293 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,
385 if (hlimit < 0) 385 if (hlimit < 0)
386 hlimit = ipv6_get_hoplimit(dst->dev); 386 hlimit = ipv6_get_hoplimit(dst->dev);
387 387
388 tclass = np->cork.tclass;
389 if (tclass < 0)
390 tclass = 0;
391
388 msg.skb = skb; 392 msg.skb = skb;
389 msg.offset = skb->nh.raw - skb->data; 393 msg.offset = skb->nh.raw - skb->data;
390 394
@@ -400,7 +404,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
400 err = ip6_append_data(sk, icmpv6_getfrag, &msg, 404 err = ip6_append_data(sk, icmpv6_getfrag, &msg,
401 len + sizeof(struct icmp6hdr), 405 len + sizeof(struct icmp6hdr),
402 sizeof(struct icmp6hdr), 406 sizeof(struct icmp6hdr),
403 hlimit, NULL, &fl, (struct rt6_info*)dst, 407 hlimit, tclass, NULL, &fl, (struct rt6_info*)dst,
404 MSG_DONTWAIT); 408 MSG_DONTWAIT);
405 if (err) { 409 if (err) {
406 ip6_flush_pending_frames(sk); 410 ip6_flush_pending_frames(sk);
@@ -434,6 +438,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
434 struct dst_entry *dst; 438 struct dst_entry *dst;
435 int err = 0; 439 int err = 0;
436 int hlimit; 440 int hlimit;
441 int tclass;
437 442
438 saddr = &skb->nh.ipv6h->daddr; 443 saddr = &skb->nh.ipv6h->daddr;
439 444
@@ -475,13 +480,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
475 if (hlimit < 0) 480 if (hlimit < 0)
476 hlimit = ipv6_get_hoplimit(dst->dev); 481 hlimit = ipv6_get_hoplimit(dst->dev);
477 482
483 tclass = np->cork.tclass;
484 if (tclass < 0)
485 tclass = 0;
486
478 idev = in6_dev_get(skb->dev); 487 idev = in6_dev_get(skb->dev);
479 488
480 msg.skb = skb; 489 msg.skb = skb;
481 msg.offset = 0; 490 msg.offset = 0;
482 491
483 err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), 492 err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
484 sizeof(struct icmp6hdr), hlimit, NULL, &fl, 493 sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl,
485 (struct rt6_info*)dst, MSG_DONTWAIT); 494 (struct rt6_info*)dst, MSG_DONTWAIT);
486 495
487 if (err) { 496 if (err) {
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index b6c73da5ff35..a7db762de14a 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -225,16 +225,20 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
225 struct ip6_flowlabel * fl, 225 struct ip6_flowlabel * fl,
226 struct ipv6_txoptions * fopt) 226 struct ipv6_txoptions * fopt)
227{ 227{
228 struct ipv6_txoptions * fl_opt = fl->opt; 228 struct ipv6_txoptions * fl_opt = fl ? fl->opt : NULL;
229 229
230 if (fopt == NULL || fopt->opt_flen == 0) 230 if (fopt == NULL || fopt->opt_flen == 0) {
231 return fl_opt; 231 if (!fl_opt || !fl_opt->dst0opt || fl_opt->srcrt)
232 return fl_opt;
233 }
232 234
233 if (fl_opt != NULL) { 235 if (fl_opt != NULL) {
234 opt_space->hopopt = fl_opt->hopopt; 236 opt_space->hopopt = fl_opt->hopopt;
235 opt_space->dst0opt = fl_opt->dst0opt; 237 opt_space->dst0opt = fl_opt->srcrt ? fl_opt->dst0opt : NULL;
236 opt_space->srcrt = fl_opt->srcrt; 238 opt_space->srcrt = fl_opt->srcrt;
237 opt_space->opt_nflen = fl_opt->opt_nflen; 239 opt_space->opt_nflen = fl_opt->opt_nflen;
240 if (fl_opt->dst0opt && !fl_opt->srcrt)
241 opt_space->opt_nflen -= ipv6_optlen(fl_opt->dst0opt);
238 } else { 242 } else {
239 if (fopt->opt_nflen == 0) 243 if (fopt->opt_nflen == 0)
240 return fopt; 244 return fopt;
@@ -310,7 +314,7 @@ fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *
310 msg.msg_control = (void*)(fl->opt+1); 314 msg.msg_control = (void*)(fl->opt+1);
311 flowi.oif = 0; 315 flowi.oif = 0;
312 316
313 err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk); 317 err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk, &junk);
314 if (err) 318 if (err)
315 goto done; 319 goto done;
316 err = -EINVAL; 320 err = -EINVAL;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 01ef94f7c7f1..2f589f24c093 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -166,7 +166,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
166 struct ipv6hdr *hdr; 166 struct ipv6hdr *hdr;
167 u8 proto = fl->proto; 167 u8 proto = fl->proto;
168 int seg_len = skb->len; 168 int seg_len = skb->len;
169 int hlimit; 169 int hlimit, tclass;
170 u32 mtu; 170 u32 mtu;
171 171
172 if (opt) { 172 if (opt) {
@@ -202,7 +202,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
202 * Fill in the IPv6 header 202 * Fill in the IPv6 header
203 */ 203 */
204 204
205 *(u32*)hdr = htonl(0x60000000) | fl->fl6_flowlabel;
206 hlimit = -1; 205 hlimit = -1;
207 if (np) 206 if (np)
208 hlimit = np->hop_limit; 207 hlimit = np->hop_limit;
@@ -211,6 +210,14 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
211 if (hlimit < 0) 210 if (hlimit < 0)
212 hlimit = ipv6_get_hoplimit(dst->dev); 211 hlimit = ipv6_get_hoplimit(dst->dev);
213 212
213 tclass = -1;
214 if (np)
215 tclass = np->tclass;
216 if (tclass < 0)
217 tclass = 0;
218
219 *(u32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
220
214 hdr->payload_len = htons(seg_len); 221 hdr->payload_len = htons(seg_len);
215 hdr->nexthdr = proto; 222 hdr->nexthdr = proto;
216 hdr->hop_limit = hlimit; 223 hdr->hop_limit = hlimit;
@@ -762,10 +769,11 @@ out_err_release:
762 return err; 769 return err;
763} 770}
764 771
765int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), 772int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
766 void *from, int length, int transhdrlen, 773 int offset, int len, int odd, struct sk_buff *skb),
767 int hlimit, struct ipv6_txoptions *opt, struct flowi *fl, struct rt6_info *rt, 774 void *from, int length, int transhdrlen,
768 unsigned int flags) 775 int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,
776 struct rt6_info *rt, unsigned int flags)
769{ 777{
770 struct inet_sock *inet = inet_sk(sk); 778 struct inet_sock *inet = inet_sk(sk);
771 struct ipv6_pinfo *np = inet6_sk(sk); 779 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
803 np->cork.rt = rt; 811 np->cork.rt = rt;
804 inet->cork.fl = *fl; 812 inet->cork.fl = *fl;
805 np->cork.hop_limit = hlimit; 813 np->cork.hop_limit = hlimit;
814 np->cork.tclass = tclass;
806 inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path); 815 inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
807 if (dst_allfrag(rt->u.dst.path)) 816 if (dst_allfrag(rt->u.dst.path))
808 inet->cork.flags |= IPCORK_ALLFRAG; 817 inet->cork.flags |= IPCORK_ALLFRAG;
@@ -1084,7 +1093,8 @@ int ip6_push_pending_frames(struct sock *sk)
1084 1093
1085 skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr)); 1094 skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr));
1086 1095
1087 *(u32*)hdr = fl->fl6_flowlabel | htonl(0x60000000); 1096 *(u32*)hdr = fl->fl6_flowlabel |
1097 htonl(0x60000000 | ((int)np->cork.tclass << 20));
1088 1098
1089 if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) 1099 if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN)
1090 hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 1100 hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 76466af8331e..8567873d0dd8 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -210,39 +210,139 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
210 retv = 0; 210 retv = 0;
211 break; 211 break;
212 212
213 case IPV6_PKTINFO: 213 case IPV6_RECVPKTINFO:
214 np->rxopt.bits.rxinfo = valbool; 214 np->rxopt.bits.rxinfo = valbool;
215 retv = 0; 215 retv = 0;
216 break; 216 break;
217
218 case IPV6_2292PKTINFO:
219 np->rxopt.bits.rxoinfo = valbool;
220 retv = 0;
221 break;
217 222
218 case IPV6_HOPLIMIT: 223 case IPV6_RECVHOPLIMIT:
219 np->rxopt.bits.rxhlim = valbool; 224 np->rxopt.bits.rxhlim = valbool;
220 retv = 0; 225 retv = 0;
221 break; 226 break;
222 227
223 case IPV6_RTHDR: 228 case IPV6_2292HOPLIMIT:
229 np->rxopt.bits.rxohlim = valbool;
230 retv = 0;
231 break;
232
233 case IPV6_RECVRTHDR:
224 if (val < 0 || val > 2) 234 if (val < 0 || val > 2)
225 goto e_inval; 235 goto e_inval;
226 np->rxopt.bits.srcrt = val; 236 np->rxopt.bits.srcrt = val;
227 retv = 0; 237 retv = 0;
228 break; 238 break;
229 239
230 case IPV6_HOPOPTS: 240 case IPV6_2292RTHDR:
241 if (val < 0 || val > 2)
242 goto e_inval;
243 np->rxopt.bits.osrcrt = val;
244 retv = 0;
245 break;
246
247 case IPV6_RECVHOPOPTS:
231 np->rxopt.bits.hopopts = valbool; 248 np->rxopt.bits.hopopts = valbool;
232 retv = 0; 249 retv = 0;
233 break; 250 break;
234 251
235 case IPV6_DSTOPTS: 252 case IPV6_2292HOPOPTS:
253 np->rxopt.bits.ohopopts = valbool;
254 retv = 0;
255 break;
256
257 case IPV6_RECVDSTOPTS:
236 np->rxopt.bits.dstopts = valbool; 258 np->rxopt.bits.dstopts = valbool;
237 retv = 0; 259 retv = 0;
238 break; 260 break;
239 261
262 case IPV6_2292DSTOPTS:
263 np->rxopt.bits.odstopts = valbool;
264 retv = 0;
265 break;
266
267 case IPV6_TCLASS:
268 if (val < 0 || val > 0xff)
269 goto e_inval;
270 np->tclass = val;
271 retv = 0;
272 break;
273
274 case IPV6_RECVTCLASS:
275 np->rxopt.bits.rxtclass = valbool;
276 retv = 0;
277 break;
278
240 case IPV6_FLOWINFO: 279 case IPV6_FLOWINFO:
241 np->rxopt.bits.rxflow = valbool; 280 np->rxopt.bits.rxflow = valbool;
242 retv = 0; 281 retv = 0;
243 break; 282 break;
244 283
245 case IPV6_PKTOPTIONS: 284 case IPV6_HOPOPTS:
285 case IPV6_RTHDRDSTOPTS:
286 case IPV6_RTHDR:
287 case IPV6_DSTOPTS:
288 {
289 struct ipv6_txoptions *opt;
290 if (optlen == 0)
291 optval = 0;
292
293 /* hop-by-hop / destination options are privileged option */
294 retv = -EPERM;
295 if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))
296 break;
297
298 retv = -EINVAL;
299 if (optlen & 0x7 || optlen > 8 * 255)
300 break;
301
302 opt = ipv6_renew_options(sk, np->opt, optname,
303 (struct ipv6_opt_hdr __user *)optval,
304 optlen);
305 if (IS_ERR(opt)) {
306 retv = PTR_ERR(opt);
307 break;
308 }
309
310 /* routing header option needs extra check */
311 if (optname == IPV6_RTHDR && opt->srcrt) {
312 struct ipv6_rt_hdr *rthdr = opt->srcrt;
313 if (rthdr->type)
314 goto sticky_done;
315 if ((rthdr->hdrlen & 1) ||
316 (rthdr->hdrlen >> 1) != rthdr->segments_left)
317 goto sticky_done;
318 }
319
320 retv = 0;
321 if (sk->sk_type == SOCK_STREAM) {
322 if (opt) {
323 struct tcp_sock *tp = tcp_sk(sk);
324 if (!((1 << sk->sk_state) &
325 (TCPF_LISTEN | TCPF_CLOSE))
326 && inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
327 tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
328 tcp_sync_mss(sk, tp->pmtu_cookie);
329 }
330 }
331 opt = xchg(&np->opt, opt);
332 sk_dst_reset(sk);
333 } else {
334 write_lock(&sk->sk_dst_lock);
335 opt = xchg(&np->opt, opt);
336 write_unlock(&sk->sk_dst_lock);
337 sk_dst_reset(sk);
338 }
339sticky_done:
340 if (opt)
341 sock_kfree_s(sk, opt, opt->tot_len);
342 break;
343 }
344
345 case IPV6_2292PKTOPTIONS:
246 { 346 {
247 struct ipv6_txoptions *opt = NULL; 347 struct ipv6_txoptions *opt = NULL;
248 struct msghdr msg; 348 struct msghdr msg;
@@ -276,7 +376,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
276 msg.msg_controllen = optlen; 376 msg.msg_controllen = optlen;
277 msg.msg_control = (void*)(opt+1); 377 msg.msg_control = (void*)(opt+1);
278 378
279 retv = datagram_send_ctl(&msg, &fl, opt, &junk); 379 retv = datagram_send_ctl(&msg, &fl, opt, &junk, &junk);
280 if (retv) 380 if (retv)
281 goto done; 381 goto done;
282update: 382update:
@@ -529,6 +629,17 @@ e_inval:
529 return -EINVAL; 629 return -EINVAL;
530} 630}
531 631
632int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_opt_hdr *hdr,
633 char __user *optval, int len)
634{
635 if (!hdr)
636 return 0;
637 len = min_t(int, len, ipv6_optlen(hdr));
638 if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
639 return -EFAULT;
640 return len;
641}
642
532int ipv6_getsockopt(struct sock *sk, int level, int optname, 643int ipv6_getsockopt(struct sock *sk, int level, int optname,
533 char __user *optval, int __user *optlen) 644 char __user *optval, int __user *optlen)
534{ 645{
@@ -567,7 +678,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
567 return err; 678 return err;
568 } 679 }
569 680
570 case IPV6_PKTOPTIONS: 681 case IPV6_2292PKTOPTIONS:
571 { 682 {
572 struct msghdr msg; 683 struct msghdr msg;
573 struct sk_buff *skb; 684 struct sk_buff *skb;
@@ -601,6 +712,16 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
601 int hlim = np->mcast_hops; 712 int hlim = np->mcast_hops;
602 put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); 713 put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
603 } 714 }
715 if (np->rxopt.bits.rxoinfo) {
716 struct in6_pktinfo src_info;
717 src_info.ipi6_ifindex = np->mcast_oif;
718 ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
719 put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
720 }
721 if (np->rxopt.bits.rxohlim) {
722 int hlim = np->mcast_hops;
723 put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
724 }
604 } 725 }
605 len -= msg.msg_controllen; 726 len -= msg.msg_controllen;
606 return put_user(len, optlen); 727 return put_user(len, optlen);
@@ -625,26 +746,67 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
625 val = np->ipv6only; 746 val = np->ipv6only;
626 break; 747 break;
627 748
628 case IPV6_PKTINFO: 749 case IPV6_RECVPKTINFO:
629 val = np->rxopt.bits.rxinfo; 750 val = np->rxopt.bits.rxinfo;
630 break; 751 break;
631 752
632 case IPV6_HOPLIMIT: 753 case IPV6_2292PKTINFO:
754 val = np->rxopt.bits.rxoinfo;
755 break;
756
757 case IPV6_RECVHOPLIMIT:
633 val = np->rxopt.bits.rxhlim; 758 val = np->rxopt.bits.rxhlim;
634 break; 759 break;
635 760
636 case IPV6_RTHDR: 761 case IPV6_2292HOPLIMIT:
762 val = np->rxopt.bits.rxohlim;
763 break;
764
765 case IPV6_RECVRTHDR:
637 val = np->rxopt.bits.srcrt; 766 val = np->rxopt.bits.srcrt;
638 break; 767 break;
639 768
769 case IPV6_2292RTHDR:
770 val = np->rxopt.bits.osrcrt;
771 break;
772
640 case IPV6_HOPOPTS: 773 case IPV6_HOPOPTS:
774 case IPV6_RTHDRDSTOPTS:
775 case IPV6_RTHDR:
776 case IPV6_DSTOPTS:
777 {
778
779 lock_sock(sk);
780 len = ipv6_getsockopt_sticky(sk, np->opt->hopopt,
781 optval, len);
782 release_sock(sk);
783 return put_user(len, optlen);
784 }
785
786 case IPV6_RECVHOPOPTS:
641 val = np->rxopt.bits.hopopts; 787 val = np->rxopt.bits.hopopts;
642 break; 788 break;
643 789
644 case IPV6_DSTOPTS: 790 case IPV6_2292HOPOPTS:
791 val = np->rxopt.bits.ohopopts;
792 break;
793
794 case IPV6_RECVDSTOPTS:
645 val = np->rxopt.bits.dstopts; 795 val = np->rxopt.bits.dstopts;
646 break; 796 break;
647 797
798 case IPV6_2292DSTOPTS:
799 val = np->rxopt.bits.odstopts;
800 break;
801
802 case IPV6_TCLASS:
803 val = np->tclass;
804 break;
805
806 case IPV6_RECVTCLASS:
807 val = np->rxopt.bits.rxtclass;
808 break;
809
648 case IPV6_FLOWINFO: 810 case IPV6_FLOWINFO:
649 val = np->rxopt.bits.rxflow; 811 val = np->rxopt.bits.rxflow;
650 break; 812 break;
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index ed3a76b30fd9..2ad37893334a 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -655,6 +655,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
655 struct flowi fl; 655 struct flowi fl;
656 int addr_len = msg->msg_namelen; 656 int addr_len = msg->msg_namelen;
657 int hlimit = -1; 657 int hlimit = -1;
658 int tclass = -1;
658 u16 proto; 659 u16 proto;
659 int err; 660 int err;
660 661
@@ -740,7 +741,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
740 memset(opt, 0, sizeof(struct ipv6_txoptions)); 741 memset(opt, 0, sizeof(struct ipv6_txoptions));
741 opt->tot_len = sizeof(struct ipv6_txoptions); 742 opt->tot_len = sizeof(struct ipv6_txoptions);
742 743
743 err = datagram_send_ctl(msg, &fl, opt, &hlimit); 744 err = datagram_send_ctl(msg, &fl, opt, &hlimit, &tclass);
744 if (err < 0) { 745 if (err < 0) {
745 fl6_sock_release(flowlabel); 746 fl6_sock_release(flowlabel);
746 return err; 747 return err;
@@ -755,8 +756,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
755 } 756 }
756 if (opt == NULL) 757 if (opt == NULL)
757 opt = np->opt; 758 opt = np->opt;
758 if (flowlabel) 759 opt = fl6_merge_options(&opt_space, flowlabel, opt);
759 opt = fl6_merge_options(&opt_space, flowlabel, opt);
760 760
761 fl.proto = proto; 761 fl.proto = proto;
762 rawv6_probe_proto_opt(&fl, msg); 762 rawv6_probe_proto_opt(&fl, msg);
@@ -798,6 +798,12 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
798 hlimit = ipv6_get_hoplimit(dst->dev); 798 hlimit = ipv6_get_hoplimit(dst->dev);
799 } 799 }
800 800
801 if (tclass < 0) {
802 tclass = np->cork.tclass;
803 if (tclass < 0)
804 tclass = 0;
805 }
806
801 if (msg->msg_flags&MSG_CONFIRM) 807 if (msg->msg_flags&MSG_CONFIRM)
802 goto do_confirm; 808 goto do_confirm;
803 809
@@ -806,8 +812,9 @@ back_from_confirm:
806 err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl, (struct rt6_info*)dst, msg->msg_flags); 812 err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl, (struct rt6_info*)dst, msg->msg_flags);
807 } else { 813 } else {
808 lock_sock(sk); 814 lock_sock(sk);
809 err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0, 815 err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
810 hlimit, opt, &fl, (struct rt6_info*)dst, msg->msg_flags); 816 len, 0, hlimit, tclass, opt, &fl, (struct rt6_info*)dst,
817 msg->msg_flags);
811 818
812 if (err) 819 if (err)
813 ip6_flush_pending_frames(sk); 820 ip6_flush_pending_frames(sk);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 794734f1d230..246414b27d0e 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -849,7 +849,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
849 if (dst == NULL) { 849 if (dst == NULL) {
850 opt = np->opt; 850 opt = np->opt;
851 if (opt == NULL && 851 if (opt == NULL &&
852 np->rxopt.bits.srcrt == 2 && 852 np->rxopt.bits.osrcrt == 2 &&
853 treq->pktopts) { 853 treq->pktopts) {
854 struct sk_buff *pktopts = treq->pktopts; 854 struct sk_buff *pktopts = treq->pktopts;
855 struct inet6_skb_parm *rxopt = IP6CB(pktopts); 855 struct inet6_skb_parm *rxopt = IP6CB(pktopts);
@@ -915,11 +915,10 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
915 struct inet6_skb_parm *opt = IP6CB(skb); 915 struct inet6_skb_parm *opt = IP6CB(skb);
916 916
917 if (np->rxopt.all) { 917 if (np->rxopt.all) {
918 if ((opt->hop && np->rxopt.bits.hopopts) || 918 if ((opt->hop && (np->rxopt.bits.hopopts || np->rxopt.bits.ohopopts)) ||
919 ((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) && 919 ((IPV6_FLOWINFO_MASK & *(u32*)skb->nh.raw) && np->rxopt.bits.rxflow) ||
920 np->rxopt.bits.rxflow) || 920 (opt->srcrt && (np->rxopt.bits.srcrt || np->rxopt.bits.osrcrt)) ||
921 (opt->srcrt && np->rxopt.bits.srcrt) || 921 ((opt->dst1 || opt->dst0) && (np->rxopt.bits.dstopts || np->rxopt.bits.odstopts)))
922 ((opt->dst1 || opt->dst0) && np->rxopt.bits.dstopts))
923 return 1; 922 return 1;
924 } 923 }
925 return 0; 924 return 0;
@@ -1190,8 +1189,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
1190 TCP_ECN_create_request(req, skb->h.th); 1189 TCP_ECN_create_request(req, skb->h.th);
1191 treq->pktopts = NULL; 1190 treq->pktopts = NULL;
1192 if (ipv6_opt_accepted(sk, skb) || 1191 if (ipv6_opt_accepted(sk, skb) ||
1193 np->rxopt.bits.rxinfo || 1192 np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
1194 np->rxopt.bits.rxhlim) { 1193 np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
1195 atomic_inc(&skb->users); 1194 atomic_inc(&skb->users);
1196 treq->pktopts = skb; 1195 treq->pktopts = skb;
1197 } 1196 }
@@ -1288,7 +1287,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
1288 if (sk_acceptq_is_full(sk)) 1287 if (sk_acceptq_is_full(sk))
1289 goto out_overflow; 1288 goto out_overflow;
1290 1289
1291 if (np->rxopt.bits.srcrt == 2 && 1290 if (np->rxopt.bits.osrcrt == 2 &&
1292 opt == NULL && treq->pktopts) { 1291 opt == NULL && treq->pktopts) {
1293 struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts); 1292 struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts);
1294 if (rxopt->srcrt) 1293 if (rxopt->srcrt)
@@ -1544,9 +1543,9 @@ ipv6_pktoptions:
1544 tp = tcp_sk(sk); 1543 tp = tcp_sk(sk);
1545 if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt && 1544 if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt &&
1546 !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) { 1545 !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) {
1547 if (np->rxopt.bits.rxinfo) 1546 if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
1548 np->mcast_oif = inet6_iif(opt_skb); 1547 np->mcast_oif = inet6_iif(opt_skb);
1549 if (np->rxopt.bits.rxhlim) 1548 if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
1550 np->mcast_hops = opt_skb->nh.ipv6h->hop_limit; 1549 np->mcast_hops = opt_skb->nh.ipv6h->hop_limit;
1551 if (ipv6_opt_accepted(sk, opt_skb)) { 1550 if (ipv6_opt_accepted(sk, opt_skb)) {
1552 skb_set_owner_r(opt_skb, sk); 1551 skb_set_owner_r(opt_skb, sk);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 7cbcaa30cf5e..f5ae14810a70 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -637,6 +637,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
637 int addr_len = msg->msg_namelen; 637 int addr_len = msg->msg_namelen;
638 int ulen = len; 638 int ulen = len;
639 int hlimit = -1; 639 int hlimit = -1;
640 int tclass = -1;
640 int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; 641 int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
641 int err; 642 int err;
642 643
@@ -758,7 +759,7 @@ do_udp_sendmsg:
758 memset(opt, 0, sizeof(struct ipv6_txoptions)); 759 memset(opt, 0, sizeof(struct ipv6_txoptions));
759 opt->tot_len = sizeof(*opt); 760 opt->tot_len = sizeof(*opt);
760 761
761 err = datagram_send_ctl(msg, fl, opt, &hlimit); 762 err = datagram_send_ctl(msg, fl, opt, &hlimit, &tclass);
762 if (err < 0) { 763 if (err < 0) {
763 fl6_sock_release(flowlabel); 764 fl6_sock_release(flowlabel);
764 return err; 765 return err;
@@ -773,8 +774,7 @@ do_udp_sendmsg:
773 } 774 }
774 if (opt == NULL) 775 if (opt == NULL)
775 opt = np->opt; 776 opt = np->opt;
776 if (flowlabel) 777 opt = fl6_merge_options(&opt_space, flowlabel, opt);
777 opt = fl6_merge_options(&opt_space, flowlabel, opt);
778 778
779 fl->proto = IPPROTO_UDP; 779 fl->proto = IPPROTO_UDP;
780 ipv6_addr_copy(&fl->fl6_dst, daddr); 780 ipv6_addr_copy(&fl->fl6_dst, daddr);
@@ -815,6 +815,12 @@ do_udp_sendmsg:
815 hlimit = ipv6_get_hoplimit(dst->dev); 815 hlimit = ipv6_get_hoplimit(dst->dev);
816 } 816 }
817 817
818 if (tclass < 0) {
819 tclass = np->tclass;
820 if (tclass < 0)
821 tclass = 0;
822 }
823
818 if (msg->msg_flags&MSG_CONFIRM) 824 if (msg->msg_flags&MSG_CONFIRM)
819 goto do_confirm; 825 goto do_confirm;
820back_from_confirm: 826back_from_confirm:
@@ -834,9 +840,10 @@ back_from_confirm:
834 840
835do_append_data: 841do_append_data:
836 up->len += ulen; 842 up->len += ulen;
837 err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, sizeof(struct udphdr), 843 err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
838 hlimit, opt, fl, (struct rt6_info*)dst, 844 sizeof(struct udphdr), hlimit, tclass, opt, fl,
839 corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); 845 (struct rt6_info*)dst,
846 corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
840 if (err) 847 if (err)
841 udp_v6_flush_pending_frames(sk); 848 udp_v6_flush_pending_frames(sk);
842 else if (!corkreq) 849 else if (!corkreq)