aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ipv6_sockglue.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
-rw-r--r--net/ipv6/ipv6_sockglue.c164
1 files changed, 153 insertions, 11 deletions
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;