aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/datagram.c110
-rw-r--r--net/ipv6/exthdrs.c107
-rw-r--r--net/ipv6/ip6_flowlabel.c12
-rw-r--r--net/ipv6/ipv6_sockglue.c164
-rw-r--r--net/ipv6/raw.c3
-rw-r--r--net/ipv6/tcp_ipv6.c21
-rw-r--r--net/ipv6/udp.c3
7 files changed, 379 insertions, 41 deletions
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 01468fab3d3d..832476bbc5cb 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -394,21 +394,85 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
394 u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK; 394 u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
395 put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); 395 put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
396 } 396 }
397
398 /* HbH is allowed only once */
397 if (np->rxopt.bits.hopopts && opt->hop) { 399 if (np->rxopt.bits.hopopts && opt->hop) {
398 u8 *ptr = skb->nh.raw + opt->hop; 400 u8 *ptr = skb->nh.raw + opt->hop;
399 put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); 401 put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
400 } 402 }
401 if (np->rxopt.bits.dstopts && opt->dst0) { 403
404 if (opt->lastopt &&
405 (np->rxopt.bits.dstopts || np->rxopt.bits.srcrt)) {
406 /*
407 * Silly enough, but we need to reparse in order to
408 * report extension headers (except for HbH)
409 * in order.
410 *
411 * Also note that IPV6_RECVRTHDRDSTOPTS is NOT
412 * (and WILL NOT be) defined because
413 * IPV6_RECVDSTOPTS is more generic. --yoshfuji
414 */
415 unsigned int off = sizeof(struct ipv6hdr);
416 u8 nexthdr = skb->nh.ipv6h->nexthdr;
417
418 while (off <= opt->lastopt) {
419 unsigned len;
420 u8 *ptr = skb->nh.raw + off;
421
422 switch(nexthdr) {
423 case IPPROTO_DSTOPTS:
424 nexthdr = ptr[0];
425 len = (ptr[1] + 1) << 3;
426 if (np->rxopt.bits.dstopts)
427 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
428 break;
429 case IPPROTO_ROUTING:
430 nexthdr = ptr[0];
431 len = (ptr[1] + 1) << 3;
432 if (np->rxopt.bits.srcrt)
433 put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
434 break;
435 case IPPROTO_AH:
436 nexthdr = ptr[0];
437 len = (ptr[1] + 1) << 2;
438 break;
439 default:
440 nexthdr = ptr[0];
441 len = (ptr[1] + 1) << 3;
442 break;
443 }
444
445 off += len;
446 }
447 }
448
449 /* socket options in old style */
450 if (np->rxopt.bits.rxoinfo) {
451 struct in6_pktinfo src_info;
452
453 src_info.ipi6_ifindex = opt->iif;
454 ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
455 put_cmsg(msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
456 }
457 if (np->rxopt.bits.rxohlim) {
458 int hlim = skb->nh.ipv6h->hop_limit;
459 put_cmsg(msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
460 }
461 if (np->rxopt.bits.ohopopts && opt->hop) {
462 u8 *ptr = skb->nh.raw + opt->hop;
463 put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
464 }
465 if (np->rxopt.bits.odstopts && opt->dst0) {
402 u8 *ptr = skb->nh.raw + opt->dst0; 466 u8 *ptr = skb->nh.raw + opt->dst0;
403 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); 467 put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
404 } 468 }
405 if (np->rxopt.bits.srcrt && opt->srcrt) { 469 if (np->rxopt.bits.osrcrt && opt->srcrt) {
406 struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); 470 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); 471 put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
408 } 472 }
409 if (np->rxopt.bits.dstopts && opt->dst1) { 473 if (np->rxopt.bits.odstopts && opt->dst1) {
410 u8 *ptr = skb->nh.raw + opt->dst1; 474 u8 *ptr = skb->nh.raw + opt->dst1;
411 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); 475 put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
412 } 476 }
413 return 0; 477 return 0;
414} 478}
@@ -438,6 +502,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
438 502
439 switch (cmsg->cmsg_type) { 503 switch (cmsg->cmsg_type) {
440 case IPV6_PKTINFO: 504 case IPV6_PKTINFO:
505 case IPV6_2292PKTINFO:
441 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) { 506 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
442 err = -EINVAL; 507 err = -EINVAL;
443 goto exit_f; 508 goto exit_f;
@@ -492,6 +557,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
492 fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg); 557 fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
493 break; 558 break;
494 559
560 case IPV6_2292HOPOPTS:
495 case IPV6_HOPOPTS: 561 case IPV6_HOPOPTS:
496 if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { 562 if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
497 err = -EINVAL; 563 err = -EINVAL;
@@ -512,7 +578,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
512 opt->hopopt = hdr; 578 opt->hopopt = hdr;
513 break; 579 break;
514 580
515 case IPV6_DSTOPTS: 581 case IPV6_2292DSTOPTS:
516 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { 582 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
517 err = -EINVAL; 583 err = -EINVAL;
518 goto exit_f; 584 goto exit_f;
@@ -536,6 +602,33 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
536 opt->dst1opt = hdr; 602 opt->dst1opt = hdr;
537 break; 603 break;
538 604
605 case IPV6_DSTOPTS:
606 case IPV6_RTHDRDSTOPTS:
607 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
608 err = -EINVAL;
609 goto exit_f;
610 }
611
612 hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
613 len = ((hdr->hdrlen + 1) << 3);
614 if (cmsg->cmsg_len < CMSG_LEN(len)) {
615 err = -EINVAL;
616 goto exit_f;
617 }
618 if (!capable(CAP_NET_RAW)) {
619 err = -EPERM;
620 goto exit_f;
621 }
622 if (cmsg->cmsg_type == IPV6_DSTOPTS) {
623 opt->opt_flen += len;
624 opt->dst1opt = hdr;
625 } else {
626 opt->opt_nflen += len;
627 opt->dst0opt = hdr;
628 }
629 break;
630
631 case IPV6_2292RTHDR:
539 case IPV6_RTHDR: 632 case IPV6_RTHDR:
540 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) { 633 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
541 err = -EINVAL; 634 err = -EINVAL;
@@ -568,7 +661,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
568 opt->opt_nflen += len; 661 opt->opt_nflen += len;
569 opt->srcrt = rthdr; 662 opt->srcrt = rthdr;
570 663
571 if (opt->dst1opt) { 664 if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
572 int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3); 665 int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
573 666
574 opt->opt_nflen += dsthdrlen; 667 opt->opt_nflen += dsthdrlen;
@@ -579,6 +672,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
579 672
580 break; 673 break;
581 674
675 case IPV6_2292HOPLIMIT:
582 case IPV6_HOPLIMIT: 676 case IPV6_HOPLIMIT:
583 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { 677 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
584 err = -EINVAL; 678 err = -EINVAL;
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 5be6da2584ee..ffcda45e2c1e 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;
@@ -539,10 +541,15 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
539 u8 *proto, 541 u8 *proto,
540 struct in6_addr **daddr) 542 struct in6_addr **daddr)
541{ 543{
542 if (opt->srcrt) 544 if (opt->srcrt) {
543 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); 545 ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
544 if (opt->dst0opt) 546 /*
545 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); 547 * IPV6_RTHDRDSTOPTS is ignored
548 * unless IPV6_RTHDR is set (RFC3542).
549 */
550 if (opt->dst0opt)
551 ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
552 }
546 if (opt->hopopt) 553 if (opt->hopopt)
547 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); 554 ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
548} 555}
@@ -573,3 +580,97 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
573 } 580 }
574 return opt2; 581 return opt2;
575} 582}
583
584static int ipv6_renew_option(void *ohdr,
585 struct ipv6_opt_hdr __user *newopt, int newoptlen,
586 int inherit,
587 struct ipv6_opt_hdr **hdr,
588 char **p)
589{
590 if (inherit) {
591 if (ohdr) {
592 memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
593 *hdr = (struct ipv6_opt_hdr *)*p;
594 *p += CMSG_ALIGN(ipv6_optlen(*(struct ipv6_opt_hdr **)hdr));
595 }
596 } else {
597 if (newopt) {
598 if (copy_from_user(*p, newopt, newoptlen))
599 return -EFAULT;
600 *hdr = (struct ipv6_opt_hdr *)*p;
601 if (ipv6_optlen(*(struct ipv6_opt_hdr **)hdr) > newoptlen)
602 return -EINVAL;
603 *p += CMSG_ALIGN(newoptlen);
604 }
605 }
606 return 0;
607}
608
609struct ipv6_txoptions *
610ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
611 int newtype,
612 struct ipv6_opt_hdr __user *newopt, int newoptlen)
613{
614 int tot_len = 0;
615 char *p;
616 struct ipv6_txoptions *opt2;
617 int err;
618
619 if (newtype != IPV6_HOPOPTS && opt->hopopt)
620 tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
621 if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
622 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
623 if (newtype != IPV6_RTHDR && opt->srcrt)
624 tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
625 if (newtype != IPV6_DSTOPTS && opt->dst1opt)
626 tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
627 if (newopt && newoptlen)
628 tot_len += CMSG_ALIGN(newoptlen);
629
630 if (!tot_len)
631 return NULL;
632
633 opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
634 if (!opt2)
635 return ERR_PTR(-ENOBUFS);
636
637 memset(opt2, 0, tot_len);
638
639 opt2->tot_len = tot_len;
640 p = (char *)(opt2 + 1);
641
642 err = ipv6_renew_option(opt->hopopt, newopt, newoptlen,
643 newtype != IPV6_HOPOPTS,
644 &opt2->hopopt, &p);
645 if (err)
646 goto out;
647
648 err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen,
649 newtype != IPV6_RTHDRDSTOPTS,
650 &opt2->dst0opt, &p);
651 if (err)
652 goto out;
653
654 err = ipv6_renew_option(opt->srcrt, newopt, newoptlen,
655 newtype != IPV6_RTHDR,
656 (struct ipv6_opt_hdr **)opt2->srcrt, &p);
657 if (err)
658 goto out;
659
660 err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen,
661 newtype != IPV6_DSTOPTS,
662 &opt2->dst1opt, &p);
663 if (err)
664 goto out;
665
666 opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
667 (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
668 (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
669 opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
670
671 return opt2;
672out:
673 sock_kfree_s(sk, p, tot_len);
674 return ERR_PTR(err);
675}
676
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index b6c73da5ff35..2d5ce376c265 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;
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 76466af8331e..dc1d9914bf7d 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -210,39 +210,127 @@ 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
240 case IPV6_FLOWINFO: 267 case IPV6_FLOWINFO:
241 np->rxopt.bits.rxflow = valbool; 268 np->rxopt.bits.rxflow = valbool;
242 retv = 0; 269 retv = 0;
243 break; 270 break;
244 271
245 case IPV6_PKTOPTIONS: 272 case IPV6_HOPOPTS:
273 case IPV6_RTHDRDSTOPTS:
274 case IPV6_RTHDR:
275 case IPV6_DSTOPTS:
276 {
277 struct ipv6_txoptions *opt;
278 if (optlen == 0)
279 optval = 0;
280
281 /* hop-by-hop / destination options are privileged option */
282 retv = -EPERM;
283 if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))
284 break;
285
286 retv = -EINVAL;
287 if (optlen & 0x7 || optlen > 8 * 255)
288 break;
289
290 opt = ipv6_renew_options(sk, np->opt, optname,
291 (struct ipv6_opt_hdr __user *)optval,
292 optlen);
293 if (IS_ERR(opt)) {
294 retv = PTR_ERR(opt);
295 break;
296 }
297
298 /* routing header option needs extra check */
299 if (optname == IPV6_RTHDR && opt->srcrt) {
300 struct ipv6_rt_hdr *rthdr = opt->srcrt;
301 if (rthdr->type)
302 goto sticky_done;
303 if ((rthdr->hdrlen & 1) ||
304 (rthdr->hdrlen >> 1) != rthdr->segments_left)
305 goto sticky_done;
306 }
307
308 retv = 0;
309 if (sk->sk_type == SOCK_STREAM) {
310 if (opt) {
311 struct tcp_sock *tp = tcp_sk(sk);
312 if (!((1 << sk->sk_state) &
313 (TCPF_LISTEN | TCPF_CLOSE))
314 && inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
315 tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
316 tcp_sync_mss(sk, tp->pmtu_cookie);
317 }
318 }
319 opt = xchg(&np->opt, opt);
320 sk_dst_reset(sk);
321 } else {
322 write_lock(&sk->sk_dst_lock);
323 opt = xchg(&np->opt, opt);
324 write_unlock(&sk->sk_dst_lock);
325 sk_dst_reset(sk);
326 }
327sticky_done:
328 if (opt)
329 sock_kfree_s(sk, opt, opt->tot_len);
330 break;
331 }
332
333 case IPV6_2292PKTOPTIONS:
246 { 334 {
247 struct ipv6_txoptions *opt = NULL; 335 struct ipv6_txoptions *opt = NULL;
248 struct msghdr msg; 336 struct msghdr msg;
@@ -529,6 +617,17 @@ e_inval:
529 return -EINVAL; 617 return -EINVAL;
530} 618}
531 619
620int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_opt_hdr *hdr,
621 char __user *optval, int len)
622{
623 if (!hdr)
624 return 0;
625 len = min_t(int, len, ipv6_optlen(hdr));
626 if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
627 return -EFAULT;
628 return len;
629}
630
532int ipv6_getsockopt(struct sock *sk, int level, int optname, 631int ipv6_getsockopt(struct sock *sk, int level, int optname,
533 char __user *optval, int __user *optlen) 632 char __user *optval, int __user *optlen)
534{ 633{
@@ -567,7 +666,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
567 return err; 666 return err;
568 } 667 }
569 668
570 case IPV6_PKTOPTIONS: 669 case IPV6_2292PKTOPTIONS:
571 { 670 {
572 struct msghdr msg; 671 struct msghdr msg;
573 struct sk_buff *skb; 672 struct sk_buff *skb;
@@ -601,6 +700,16 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
601 int hlim = np->mcast_hops; 700 int hlim = np->mcast_hops;
602 put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); 701 put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
603 } 702 }
703 if (np->rxopt.bits.rxoinfo) {
704 struct in6_pktinfo src_info;
705 src_info.ipi6_ifindex = np->mcast_oif;
706 ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
707 put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
708 }
709 if (np->rxopt.bits.rxohlim) {
710 int hlim = np->mcast_hops;
711 put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
712 }
604 } 713 }
605 len -= msg.msg_controllen; 714 len -= msg.msg_controllen;
606 return put_user(len, optlen); 715 return put_user(len, optlen);
@@ -625,26 +734,59 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
625 val = np->ipv6only; 734 val = np->ipv6only;
626 break; 735 break;
627 736
628 case IPV6_PKTINFO: 737 case IPV6_RECVPKTINFO:
629 val = np->rxopt.bits.rxinfo; 738 val = np->rxopt.bits.rxinfo;
630 break; 739 break;
631 740
632 case IPV6_HOPLIMIT: 741 case IPV6_2292PKTINFO:
742 val = np->rxopt.bits.rxoinfo;
743 break;
744
745 case IPV6_RECVHOPLIMIT:
633 val = np->rxopt.bits.rxhlim; 746 val = np->rxopt.bits.rxhlim;
634 break; 747 break;
635 748
636 case IPV6_RTHDR: 749 case IPV6_2292HOPLIMIT:
750 val = np->rxopt.bits.rxohlim;
751 break;
752
753 case IPV6_RECVRTHDR:
637 val = np->rxopt.bits.srcrt; 754 val = np->rxopt.bits.srcrt;
638 break; 755 break;
639 756
757 case IPV6_2292RTHDR:
758 val = np->rxopt.bits.osrcrt;
759 break;
760
640 case IPV6_HOPOPTS: 761 case IPV6_HOPOPTS:
762 case IPV6_RTHDRDSTOPTS:
763 case IPV6_RTHDR:
764 case IPV6_DSTOPTS:
765 {
766
767 lock_sock(sk);
768 len = ipv6_getsockopt_sticky(sk, np->opt->hopopt,
769 optval, len);
770 release_sock(sk);
771 return put_user(len, optlen);
772 }
773
774 case IPV6_RECVHOPOPTS:
641 val = np->rxopt.bits.hopopts; 775 val = np->rxopt.bits.hopopts;
642 break; 776 break;
643 777
644 case IPV6_DSTOPTS: 778 case IPV6_2292HOPOPTS:
779 val = np->rxopt.bits.ohopopts;
780 break;
781
782 case IPV6_RECVDSTOPTS:
645 val = np->rxopt.bits.dstopts; 783 val = np->rxopt.bits.dstopts;
646 break; 784 break;
647 785
786 case IPV6_2292DSTOPTS:
787 val = np->rxopt.bits.odstopts;
788 break;
789
648 case IPV6_FLOWINFO: 790 case IPV6_FLOWINFO:
649 val = np->rxopt.bits.rxflow; 791 val = np->rxopt.bits.rxflow;
650 break; 792 break;
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index ed3a76b30fd9..e527a1652d7c 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -755,8 +755,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
755 } 755 }
756 if (opt == NULL) 756 if (opt == NULL)
757 opt = np->opt; 757 opt = np->opt;
758 if (flowlabel) 758 opt = fl6_merge_options(&opt_space, flowlabel, opt);
759 opt = fl6_merge_options(&opt_space, flowlabel, opt);
760 759
761 fl.proto = proto; 760 fl.proto = proto;
762 rawv6_probe_proto_opt(&fl, msg); 761 rawv6_probe_proto_opt(&fl, msg);
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 390d750449ce..aa6eaf3f18a6 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -773,8 +773,7 @@ do_udp_sendmsg:
773 } 773 }
774 if (opt == NULL) 774 if (opt == NULL)
775 opt = np->opt; 775 opt = np->opt;
776 if (flowlabel) 776 opt = fl6_merge_options(&opt_space, flowlabel, opt);
777 opt = fl6_merge_options(&opt_space, flowlabel, opt);
778 777
779 fl->proto = IPPROTO_UDP; 778 fl->proto = IPPROTO_UDP;
780 ipv6_addr_copy(&fl->fl6_dst, daddr); 779 ipv6_addr_copy(&fl->fl6_dst, daddr);