diff options
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 365 |
1 files changed, 186 insertions, 179 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index bf2a686aa13d..06de9d0e1f6b 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -16,7 +16,6 @@ | |||
16 | * | 16 | * |
17 | * FIXME: Make the setsockopt code POSIX compliant: That is | 17 | * FIXME: Make the setsockopt code POSIX compliant: That is |
18 | * | 18 | * |
19 | * o Return -EINVAL for setsockopt of short lengths | ||
20 | * o Truncate getsockopt returns | 19 | * o Truncate getsockopt returns |
21 | * o Return an optlen of the truncated length if need be | 20 | * o Return an optlen of the truncated length if need be |
22 | * | 21 | * |
@@ -33,6 +32,7 @@ | |||
33 | #include <linux/sockios.h> | 32 | #include <linux/sockios.h> |
34 | #include <linux/net.h> | 33 | #include <linux/net.h> |
35 | #include <linux/in6.h> | 34 | #include <linux/in6.h> |
35 | #include <linux/mroute6.h> | ||
36 | #include <linux/netdevice.h> | 36 | #include <linux/netdevice.h> |
37 | #include <linux/if_arp.h> | 37 | #include <linux/if_arp.h> |
38 | #include <linux/init.h> | 38 | #include <linux/init.h> |
@@ -57,118 +57,6 @@ | |||
57 | 57 | ||
58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; | 58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; |
59 | 59 | ||
60 | static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb, | ||
61 | int proto) | ||
62 | { | ||
63 | struct inet6_protocol *ops = NULL; | ||
64 | |||
65 | for (;;) { | ||
66 | struct ipv6_opt_hdr *opth; | ||
67 | int len; | ||
68 | |||
69 | if (proto != NEXTHDR_HOP) { | ||
70 | ops = rcu_dereference(inet6_protos[proto]); | ||
71 | |||
72 | if (unlikely(!ops)) | ||
73 | break; | ||
74 | |||
75 | if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) | ||
76 | break; | ||
77 | } | ||
78 | |||
79 | if (unlikely(!pskb_may_pull(skb, 8))) | ||
80 | break; | ||
81 | |||
82 | opth = (void *)skb->data; | ||
83 | len = opth->hdrlen * 8 + 8; | ||
84 | |||
85 | if (unlikely(!pskb_may_pull(skb, len))) | ||
86 | break; | ||
87 | |||
88 | proto = opth->nexthdr; | ||
89 | __skb_pull(skb, len); | ||
90 | } | ||
91 | |||
92 | return ops; | ||
93 | } | ||
94 | |||
95 | static int ipv6_gso_send_check(struct sk_buff *skb) | ||
96 | { | ||
97 | struct ipv6hdr *ipv6h; | ||
98 | struct inet6_protocol *ops; | ||
99 | int err = -EINVAL; | ||
100 | |||
101 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
102 | goto out; | ||
103 | |||
104 | ipv6h = ipv6_hdr(skb); | ||
105 | __skb_pull(skb, sizeof(*ipv6h)); | ||
106 | err = -EPROTONOSUPPORT; | ||
107 | |||
108 | rcu_read_lock(); | ||
109 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | ||
110 | if (likely(ops && ops->gso_send_check)) { | ||
111 | skb_reset_transport_header(skb); | ||
112 | err = ops->gso_send_check(skb); | ||
113 | } | ||
114 | rcu_read_unlock(); | ||
115 | |||
116 | out: | ||
117 | return err; | ||
118 | } | ||
119 | |||
120 | static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) | ||
121 | { | ||
122 | struct sk_buff *segs = ERR_PTR(-EINVAL); | ||
123 | struct ipv6hdr *ipv6h; | ||
124 | struct inet6_protocol *ops; | ||
125 | |||
126 | if (!(features & NETIF_F_V6_CSUM)) | ||
127 | features &= ~NETIF_F_SG; | ||
128 | |||
129 | if (unlikely(skb_shinfo(skb)->gso_type & | ||
130 | ~(SKB_GSO_UDP | | ||
131 | SKB_GSO_DODGY | | ||
132 | SKB_GSO_TCP_ECN | | ||
133 | SKB_GSO_TCPV6 | | ||
134 | 0))) | ||
135 | goto out; | ||
136 | |||
137 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
138 | goto out; | ||
139 | |||
140 | ipv6h = ipv6_hdr(skb); | ||
141 | __skb_pull(skb, sizeof(*ipv6h)); | ||
142 | segs = ERR_PTR(-EPROTONOSUPPORT); | ||
143 | |||
144 | rcu_read_lock(); | ||
145 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | ||
146 | if (likely(ops && ops->gso_segment)) { | ||
147 | skb_reset_transport_header(skb); | ||
148 | segs = ops->gso_segment(skb, features); | ||
149 | } | ||
150 | rcu_read_unlock(); | ||
151 | |||
152 | if (unlikely(IS_ERR(segs))) | ||
153 | goto out; | ||
154 | |||
155 | for (skb = segs; skb; skb = skb->next) { | ||
156 | ipv6h = ipv6_hdr(skb); | ||
157 | ipv6h->payload_len = htons(skb->len - skb->mac_len - | ||
158 | sizeof(*ipv6h)); | ||
159 | } | ||
160 | |||
161 | out: | ||
162 | return segs; | ||
163 | } | ||
164 | |||
165 | static struct packet_type ipv6_packet_type = { | ||
166 | .type = __constant_htons(ETH_P_IPV6), | ||
167 | .func = ipv6_rcv, | ||
168 | .gso_send_check = ipv6_gso_send_check, | ||
169 | .gso_segment = ipv6_gso_segment, | ||
170 | }; | ||
171 | |||
172 | struct ip6_ra_chain *ip6_ra_chain; | 60 | struct ip6_ra_chain *ip6_ra_chain; |
173 | DEFINE_RWLOCK(ip6_ra_lock); | 61 | DEFINE_RWLOCK(ip6_ra_lock); |
174 | 62 | ||
@@ -215,25 +103,59 @@ int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *)) | |||
215 | return 0; | 103 | return 0; |
216 | } | 104 | } |
217 | 105 | ||
106 | static | ||
107 | struct ipv6_txoptions *ipv6_update_options(struct sock *sk, | ||
108 | struct ipv6_txoptions *opt) | ||
109 | { | ||
110 | if (inet_sk(sk)->is_icsk) { | ||
111 | if (opt && | ||
112 | !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) && | ||
113 | inet_sk(sk)->daddr != LOOPBACK4_IPV6) { | ||
114 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
115 | icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; | ||
116 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | ||
117 | } | ||
118 | opt = xchg(&inet6_sk(sk)->opt, opt); | ||
119 | } else { | ||
120 | write_lock(&sk->sk_dst_lock); | ||
121 | opt = xchg(&inet6_sk(sk)->opt, opt); | ||
122 | write_unlock(&sk->sk_dst_lock); | ||
123 | } | ||
124 | sk_dst_reset(sk); | ||
125 | |||
126 | return opt; | ||
127 | } | ||
128 | |||
218 | static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | 129 | static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, |
219 | char __user *optval, int optlen) | 130 | char __user *optval, int optlen) |
220 | { | 131 | { |
221 | struct ipv6_pinfo *np = inet6_sk(sk); | 132 | struct ipv6_pinfo *np = inet6_sk(sk); |
133 | struct net *net = sock_net(sk); | ||
222 | int val, valbool; | 134 | int val, valbool; |
223 | int retv = -ENOPROTOOPT; | 135 | int retv = -ENOPROTOOPT; |
224 | 136 | ||
225 | if (optval == NULL) | 137 | if (optval == NULL) |
226 | val=0; | 138 | val=0; |
227 | else if (get_user(val, (int __user *) optval)) | 139 | else { |
228 | return -EFAULT; | 140 | if (optlen >= sizeof(int)) { |
141 | if (get_user(val, (int __user *) optval)) | ||
142 | return -EFAULT; | ||
143 | } else | ||
144 | val = 0; | ||
145 | } | ||
229 | 146 | ||
230 | valbool = (val!=0); | 147 | valbool = (val!=0); |
231 | 148 | ||
149 | if (ip6_mroute_opt(optname)) | ||
150 | return ip6_mroute_setsockopt(sk, optname, optval, optlen); | ||
151 | |||
232 | lock_sock(sk); | 152 | lock_sock(sk); |
233 | 153 | ||
234 | switch (optname) { | 154 | switch (optname) { |
235 | 155 | ||
236 | case IPV6_ADDRFORM: | 156 | case IPV6_ADDRFORM: |
157 | if (optlen < sizeof(int)) | ||
158 | goto e_inval; | ||
237 | if (val == PF_INET) { | 159 | if (val == PF_INET) { |
238 | struct ipv6_txoptions *opt; | 160 | struct ipv6_txoptions *opt; |
239 | struct sk_buff *pktopt; | 161 | struct sk_buff *pktopt; |
@@ -266,10 +188,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
266 | 188 | ||
267 | if (sk->sk_protocol == IPPROTO_TCP) { | 189 | if (sk->sk_protocol == IPPROTO_TCP) { |
268 | struct inet_connection_sock *icsk = inet_csk(sk); | 190 | struct inet_connection_sock *icsk = inet_csk(sk); |
269 | |||
270 | local_bh_disable(); | 191 | local_bh_disable(); |
271 | sock_prot_inuse_add(sk->sk_prot, -1); | 192 | sock_prot_inuse_add(net, sk->sk_prot, -1); |
272 | sock_prot_inuse_add(&tcp_prot, 1); | 193 | sock_prot_inuse_add(net, &tcp_prot, 1); |
273 | local_bh_enable(); | 194 | local_bh_enable(); |
274 | sk->sk_prot = &tcp_prot; | 195 | sk->sk_prot = &tcp_prot; |
275 | icsk->icsk_af_ops = &ipv4_specific; | 196 | icsk->icsk_af_ops = &ipv4_specific; |
@@ -282,8 +203,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
282 | if (sk->sk_protocol == IPPROTO_UDPLITE) | 203 | if (sk->sk_protocol == IPPROTO_UDPLITE) |
283 | prot = &udplite_prot; | 204 | prot = &udplite_prot; |
284 | local_bh_disable(); | 205 | local_bh_disable(); |
285 | sock_prot_inuse_add(sk->sk_prot, -1); | 206 | sock_prot_inuse_add(net, sk->sk_prot, -1); |
286 | sock_prot_inuse_add(prot, 1); | 207 | sock_prot_inuse_add(net, prot, 1); |
287 | local_bh_enable(); | 208 | local_bh_enable(); |
288 | sk->sk_prot = prot; | 209 | sk->sk_prot = prot; |
289 | sk->sk_socket->ops = &inet_dgram_ops; | 210 | sk->sk_socket->ops = &inet_dgram_ops; |
@@ -309,63 +230,86 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
309 | goto e_inval; | 230 | goto e_inval; |
310 | 231 | ||
311 | case IPV6_V6ONLY: | 232 | case IPV6_V6ONLY: |
312 | if (inet_sk(sk)->num) | 233 | if (optlen < sizeof(int) || |
234 | inet_sk(sk)->num) | ||
313 | goto e_inval; | 235 | goto e_inval; |
314 | np->ipv6only = valbool; | 236 | np->ipv6only = valbool; |
315 | retv = 0; | 237 | retv = 0; |
316 | break; | 238 | break; |
317 | 239 | ||
318 | case IPV6_RECVPKTINFO: | 240 | case IPV6_RECVPKTINFO: |
241 | if (optlen < sizeof(int)) | ||
242 | goto e_inval; | ||
319 | np->rxopt.bits.rxinfo = valbool; | 243 | np->rxopt.bits.rxinfo = valbool; |
320 | retv = 0; | 244 | retv = 0; |
321 | break; | 245 | break; |
322 | 246 | ||
323 | case IPV6_2292PKTINFO: | 247 | case IPV6_2292PKTINFO: |
248 | if (optlen < sizeof(int)) | ||
249 | goto e_inval; | ||
324 | np->rxopt.bits.rxoinfo = valbool; | 250 | np->rxopt.bits.rxoinfo = valbool; |
325 | retv = 0; | 251 | retv = 0; |
326 | break; | 252 | break; |
327 | 253 | ||
328 | case IPV6_RECVHOPLIMIT: | 254 | case IPV6_RECVHOPLIMIT: |
255 | if (optlen < sizeof(int)) | ||
256 | goto e_inval; | ||
329 | np->rxopt.bits.rxhlim = valbool; | 257 | np->rxopt.bits.rxhlim = valbool; |
330 | retv = 0; | 258 | retv = 0; |
331 | break; | 259 | break; |
332 | 260 | ||
333 | case IPV6_2292HOPLIMIT: | 261 | case IPV6_2292HOPLIMIT: |
262 | if (optlen < sizeof(int)) | ||
263 | goto e_inval; | ||
334 | np->rxopt.bits.rxohlim = valbool; | 264 | np->rxopt.bits.rxohlim = valbool; |
335 | retv = 0; | 265 | retv = 0; |
336 | break; | 266 | break; |
337 | 267 | ||
338 | case IPV6_RECVRTHDR: | 268 | case IPV6_RECVRTHDR: |
269 | if (optlen < sizeof(int)) | ||
270 | goto e_inval; | ||
339 | np->rxopt.bits.srcrt = valbool; | 271 | np->rxopt.bits.srcrt = valbool; |
340 | retv = 0; | 272 | retv = 0; |
341 | break; | 273 | break; |
342 | 274 | ||
343 | case IPV6_2292RTHDR: | 275 | case IPV6_2292RTHDR: |
276 | if (optlen < sizeof(int)) | ||
277 | goto e_inval; | ||
344 | np->rxopt.bits.osrcrt = valbool; | 278 | np->rxopt.bits.osrcrt = valbool; |
345 | retv = 0; | 279 | retv = 0; |
346 | break; | 280 | break; |
347 | 281 | ||
348 | case IPV6_RECVHOPOPTS: | 282 | case IPV6_RECVHOPOPTS: |
283 | if (optlen < sizeof(int)) | ||
284 | goto e_inval; | ||
349 | np->rxopt.bits.hopopts = valbool; | 285 | np->rxopt.bits.hopopts = valbool; |
350 | retv = 0; | 286 | retv = 0; |
351 | break; | 287 | break; |
352 | 288 | ||
353 | case IPV6_2292HOPOPTS: | 289 | case IPV6_2292HOPOPTS: |
290 | if (optlen < sizeof(int)) | ||
291 | goto e_inval; | ||
354 | np->rxopt.bits.ohopopts = valbool; | 292 | np->rxopt.bits.ohopopts = valbool; |
355 | retv = 0; | 293 | retv = 0; |
356 | break; | 294 | break; |
357 | 295 | ||
358 | case IPV6_RECVDSTOPTS: | 296 | case IPV6_RECVDSTOPTS: |
297 | if (optlen < sizeof(int)) | ||
298 | goto e_inval; | ||
359 | np->rxopt.bits.dstopts = valbool; | 299 | np->rxopt.bits.dstopts = valbool; |
360 | retv = 0; | 300 | retv = 0; |
361 | break; | 301 | break; |
362 | 302 | ||
363 | case IPV6_2292DSTOPTS: | 303 | case IPV6_2292DSTOPTS: |
304 | if (optlen < sizeof(int)) | ||
305 | goto e_inval; | ||
364 | np->rxopt.bits.odstopts = valbool; | 306 | np->rxopt.bits.odstopts = valbool; |
365 | retv = 0; | 307 | retv = 0; |
366 | break; | 308 | break; |
367 | 309 | ||
368 | case IPV6_TCLASS: | 310 | case IPV6_TCLASS: |
311 | if (optlen < sizeof(int)) | ||
312 | goto e_inval; | ||
369 | if (val < -1 || val > 0xff) | 313 | if (val < -1 || val > 0xff) |
370 | goto e_inval; | 314 | goto e_inval; |
371 | np->tclass = val; | 315 | np->tclass = val; |
@@ -373,11 +317,15 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
373 | break; | 317 | break; |
374 | 318 | ||
375 | case IPV6_RECVTCLASS: | 319 | case IPV6_RECVTCLASS: |
320 | if (optlen < sizeof(int)) | ||
321 | goto e_inval; | ||
376 | np->rxopt.bits.rxtclass = valbool; | 322 | np->rxopt.bits.rxtclass = valbool; |
377 | retv = 0; | 323 | retv = 0; |
378 | break; | 324 | break; |
379 | 325 | ||
380 | case IPV6_FLOWINFO: | 326 | case IPV6_FLOWINFO: |
327 | if (optlen < sizeof(int)) | ||
328 | goto e_inval; | ||
381 | np->rxopt.bits.rxflow = valbool; | 329 | np->rxopt.bits.rxflow = valbool; |
382 | retv = 0; | 330 | retv = 0; |
383 | break; | 331 | break; |
@@ -396,9 +344,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
396 | if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW)) | 344 | if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW)) |
397 | break; | 345 | break; |
398 | 346 | ||
399 | retv = -EINVAL; | 347 | if (optlen < sizeof(struct ipv6_opt_hdr) || |
400 | if (optlen & 0x7 || optlen > 8 * 255) | 348 | optlen & 0x7 || optlen > 8 * 255) |
401 | break; | 349 | goto e_inval; |
402 | 350 | ||
403 | opt = ipv6_renew_options(sk, np->opt, optname, | 351 | opt = ipv6_renew_options(sk, np->opt, optname, |
404 | (struct ipv6_opt_hdr __user *)optval, | 352 | (struct ipv6_opt_hdr __user *)optval, |
@@ -426,25 +374,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
426 | } | 374 | } |
427 | 375 | ||
428 | retv = 0; | 376 | retv = 0; |
429 | if (inet_sk(sk)->is_icsk) { | 377 | opt = ipv6_update_options(sk, opt); |
430 | if (opt) { | ||
431 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
432 | if (!((1 << sk->sk_state) & | ||
433 | (TCPF_LISTEN | TCPF_CLOSE)) | ||
434 | && inet_sk(sk)->daddr != LOOPBACK4_IPV6) { | ||
435 | icsk->icsk_ext_hdr_len = | ||
436 | opt->opt_flen + opt->opt_nflen; | ||
437 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | ||
438 | } | ||
439 | } | ||
440 | opt = xchg(&np->opt, opt); | ||
441 | sk_dst_reset(sk); | ||
442 | } else { | ||
443 | write_lock(&sk->sk_dst_lock); | ||
444 | opt = xchg(&np->opt, opt); | ||
445 | write_unlock(&sk->sk_dst_lock); | ||
446 | sk_dst_reset(sk); | ||
447 | } | ||
448 | sticky_done: | 378 | sticky_done: |
449 | if (opt) | 379 | if (opt) |
450 | sock_kfree_s(sk, opt, opt->tot_len); | 380 | sock_kfree_s(sk, opt, opt->tot_len); |
@@ -490,32 +420,15 @@ sticky_done: | |||
490 | goto done; | 420 | goto done; |
491 | update: | 421 | update: |
492 | retv = 0; | 422 | retv = 0; |
493 | if (inet_sk(sk)->is_icsk) { | 423 | opt = ipv6_update_options(sk, opt); |
494 | if (opt) { | ||
495 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
496 | if (!((1 << sk->sk_state) & | ||
497 | (TCPF_LISTEN | TCPF_CLOSE)) | ||
498 | && inet_sk(sk)->daddr != LOOPBACK4_IPV6) { | ||
499 | icsk->icsk_ext_hdr_len = | ||
500 | opt->opt_flen + opt->opt_nflen; | ||
501 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | ||
502 | } | ||
503 | } | ||
504 | opt = xchg(&np->opt, opt); | ||
505 | sk_dst_reset(sk); | ||
506 | } else { | ||
507 | write_lock(&sk->sk_dst_lock); | ||
508 | opt = xchg(&np->opt, opt); | ||
509 | write_unlock(&sk->sk_dst_lock); | ||
510 | sk_dst_reset(sk); | ||
511 | } | ||
512 | |||
513 | done: | 424 | done: |
514 | if (opt) | 425 | if (opt) |
515 | sock_kfree_s(sk, opt, opt->tot_len); | 426 | sock_kfree_s(sk, opt, opt->tot_len); |
516 | break; | 427 | break; |
517 | } | 428 | } |
518 | case IPV6_UNICAST_HOPS: | 429 | case IPV6_UNICAST_HOPS: |
430 | if (optlen < sizeof(int)) | ||
431 | goto e_inval; | ||
519 | if (val > 255 || val < -1) | 432 | if (val > 255 || val < -1) |
520 | goto e_inval; | 433 | goto e_inval; |
521 | np->hop_limit = val; | 434 | np->hop_limit = val; |
@@ -525,6 +438,8 @@ done: | |||
525 | case IPV6_MULTICAST_HOPS: | 438 | case IPV6_MULTICAST_HOPS: |
526 | if (sk->sk_type == SOCK_STREAM) | 439 | if (sk->sk_type == SOCK_STREAM) |
527 | goto e_inval; | 440 | goto e_inval; |
441 | if (optlen < sizeof(int)) | ||
442 | goto e_inval; | ||
528 | if (val > 255 || val < -1) | 443 | if (val > 255 || val < -1) |
529 | goto e_inval; | 444 | goto e_inval; |
530 | np->mcast_hops = val; | 445 | np->mcast_hops = val; |
@@ -532,6 +447,8 @@ done: | |||
532 | break; | 447 | break; |
533 | 448 | ||
534 | case IPV6_MULTICAST_LOOP: | 449 | case IPV6_MULTICAST_LOOP: |
450 | if (optlen < sizeof(int)) | ||
451 | goto e_inval; | ||
535 | np->mc_loop = valbool; | 452 | np->mc_loop = valbool; |
536 | retv = 0; | 453 | retv = 0; |
537 | break; | 454 | break; |
@@ -539,12 +456,14 @@ done: | |||
539 | case IPV6_MULTICAST_IF: | 456 | case IPV6_MULTICAST_IF: |
540 | if (sk->sk_type == SOCK_STREAM) | 457 | if (sk->sk_type == SOCK_STREAM) |
541 | goto e_inval; | 458 | goto e_inval; |
459 | if (optlen < sizeof(int)) | ||
460 | goto e_inval; | ||
542 | 461 | ||
543 | if (val) { | 462 | if (val) { |
544 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val) | 463 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val) |
545 | goto e_inval; | 464 | goto e_inval; |
546 | 465 | ||
547 | if (__dev_get_by_index(&init_net, val) == NULL) { | 466 | if (__dev_get_by_index(net, val) == NULL) { |
548 | retv = -ENODEV; | 467 | retv = -ENODEV; |
549 | break; | 468 | break; |
550 | } | 469 | } |
@@ -557,6 +476,9 @@ done: | |||
557 | { | 476 | { |
558 | struct ipv6_mreq mreq; | 477 | struct ipv6_mreq mreq; |
559 | 478 | ||
479 | if (optlen < sizeof(struct ipv6_mreq)) | ||
480 | goto e_inval; | ||
481 | |||
560 | retv = -EPROTO; | 482 | retv = -EPROTO; |
561 | if (inet_sk(sk)->is_icsk) | 483 | if (inet_sk(sk)->is_icsk) |
562 | break; | 484 | break; |
@@ -576,7 +498,7 @@ done: | |||
576 | { | 498 | { |
577 | struct ipv6_mreq mreq; | 499 | struct ipv6_mreq mreq; |
578 | 500 | ||
579 | if (optlen != sizeof(struct ipv6_mreq)) | 501 | if (optlen < sizeof(struct ipv6_mreq)) |
580 | goto e_inval; | 502 | goto e_inval; |
581 | 503 | ||
582 | retv = -EFAULT; | 504 | retv = -EFAULT; |
@@ -595,6 +517,9 @@ done: | |||
595 | struct group_req greq; | 517 | struct group_req greq; |
596 | struct sockaddr_in6 *psin6; | 518 | struct sockaddr_in6 *psin6; |
597 | 519 | ||
520 | if (optlen < sizeof(struct group_req)) | ||
521 | goto e_inval; | ||
522 | |||
598 | retv = -EFAULT; | 523 | retv = -EFAULT; |
599 | if (copy_from_user(&greq, optval, sizeof(struct group_req))) | 524 | if (copy_from_user(&greq, optval, sizeof(struct group_req))) |
600 | break; | 525 | break; |
@@ -619,7 +544,7 @@ done: | |||
619 | struct group_source_req greqs; | 544 | struct group_source_req greqs; |
620 | int omode, add; | 545 | int omode, add; |
621 | 546 | ||
622 | if (optlen != sizeof(struct group_source_req)) | 547 | if (optlen < sizeof(struct group_source_req)) |
623 | goto e_inval; | 548 | goto e_inval; |
624 | if (copy_from_user(&greqs, optval, sizeof(greqs))) { | 549 | if (copy_from_user(&greqs, optval, sizeof(greqs))) { |
625 | retv = -EFAULT; | 550 | retv = -EFAULT; |
@@ -693,27 +618,37 @@ done: | |||
693 | break; | 618 | break; |
694 | } | 619 | } |
695 | case IPV6_ROUTER_ALERT: | 620 | case IPV6_ROUTER_ALERT: |
621 | if (optlen < sizeof(int)) | ||
622 | goto e_inval; | ||
696 | retv = ip6_ra_control(sk, val, NULL); | 623 | retv = ip6_ra_control(sk, val, NULL); |
697 | break; | 624 | break; |
698 | case IPV6_MTU_DISCOVER: | 625 | case IPV6_MTU_DISCOVER: |
626 | if (optlen < sizeof(int)) | ||
627 | goto e_inval; | ||
699 | if (val<0 || val>3) | 628 | if (val<0 || val>3) |
700 | goto e_inval; | 629 | goto e_inval; |
701 | np->pmtudisc = val; | 630 | np->pmtudisc = val; |
702 | retv = 0; | 631 | retv = 0; |
703 | break; | 632 | break; |
704 | case IPV6_MTU: | 633 | case IPV6_MTU: |
634 | if (optlen < sizeof(int)) | ||
635 | goto e_inval; | ||
705 | if (val && val < IPV6_MIN_MTU) | 636 | if (val && val < IPV6_MIN_MTU) |
706 | goto e_inval; | 637 | goto e_inval; |
707 | np->frag_size = val; | 638 | np->frag_size = val; |
708 | retv = 0; | 639 | retv = 0; |
709 | break; | 640 | break; |
710 | case IPV6_RECVERR: | 641 | case IPV6_RECVERR: |
642 | if (optlen < sizeof(int)) | ||
643 | goto e_inval; | ||
711 | np->recverr = valbool; | 644 | np->recverr = valbool; |
712 | if (!val) | 645 | if (!val) |
713 | skb_queue_purge(&sk->sk_error_queue); | 646 | skb_queue_purge(&sk->sk_error_queue); |
714 | retv = 0; | 647 | retv = 0; |
715 | break; | 648 | break; |
716 | case IPV6_FLOWINFO_SEND: | 649 | case IPV6_FLOWINFO_SEND: |
650 | if (optlen < sizeof(int)) | ||
651 | goto e_inval; | ||
717 | np->sndflow = valbool; | 652 | np->sndflow = valbool; |
718 | retv = 0; | 653 | retv = 0; |
719 | break; | 654 | break; |
@@ -728,7 +663,70 @@ done: | |||
728 | retv = xfrm_user_policy(sk, optname, optval, optlen); | 663 | retv = xfrm_user_policy(sk, optname, optval, optlen); |
729 | break; | 664 | break; |
730 | 665 | ||
666 | case IPV6_ADDR_PREFERENCES: | ||
667 | { | ||
668 | unsigned int pref = 0; | ||
669 | unsigned int prefmask = ~0; | ||
670 | |||
671 | if (optlen < sizeof(int)) | ||
672 | goto e_inval; | ||
673 | |||
674 | retv = -EINVAL; | ||
675 | |||
676 | /* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */ | ||
677 | switch (val & (IPV6_PREFER_SRC_PUBLIC| | ||
678 | IPV6_PREFER_SRC_TMP| | ||
679 | IPV6_PREFER_SRC_PUBTMP_DEFAULT)) { | ||
680 | case IPV6_PREFER_SRC_PUBLIC: | ||
681 | pref |= IPV6_PREFER_SRC_PUBLIC; | ||
682 | break; | ||
683 | case IPV6_PREFER_SRC_TMP: | ||
684 | pref |= IPV6_PREFER_SRC_TMP; | ||
685 | break; | ||
686 | case IPV6_PREFER_SRC_PUBTMP_DEFAULT: | ||
687 | break; | ||
688 | case 0: | ||
689 | goto pref_skip_pubtmp; | ||
690 | default: | ||
691 | goto e_inval; | ||
692 | } | ||
693 | |||
694 | prefmask &= ~(IPV6_PREFER_SRC_PUBLIC| | ||
695 | IPV6_PREFER_SRC_TMP); | ||
696 | pref_skip_pubtmp: | ||
697 | |||
698 | /* check HOME/COA conflicts */ | ||
699 | switch (val & (IPV6_PREFER_SRC_HOME|IPV6_PREFER_SRC_COA)) { | ||
700 | case IPV6_PREFER_SRC_HOME: | ||
701 | break; | ||
702 | case IPV6_PREFER_SRC_COA: | ||
703 | pref |= IPV6_PREFER_SRC_COA; | ||
704 | case 0: | ||
705 | goto pref_skip_coa; | ||
706 | default: | ||
707 | goto e_inval; | ||
708 | } | ||
709 | |||
710 | prefmask &= ~IPV6_PREFER_SRC_COA; | ||
711 | pref_skip_coa: | ||
712 | |||
713 | /* check CGA/NONCGA conflicts */ | ||
714 | switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) { | ||
715 | case IPV6_PREFER_SRC_CGA: | ||
716 | case IPV6_PREFER_SRC_NONCGA: | ||
717 | case 0: | ||
718 | break; | ||
719 | default: | ||
720 | goto e_inval; | ||
721 | } | ||
722 | |||
723 | np->srcprefs = (np->srcprefs & prefmask) | pref; | ||
724 | retv = 0; | ||
725 | |||
726 | break; | ||
727 | } | ||
731 | } | 728 | } |
729 | |||
732 | release_sock(sk); | 730 | release_sock(sk); |
733 | 731 | ||
734 | return retv; | 732 | return retv; |
@@ -839,6 +837,9 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
839 | int len; | 837 | int len; |
840 | int val; | 838 | int val; |
841 | 839 | ||
840 | if (ip6_mroute_opt(optname)) | ||
841 | return ip6_mroute_getsockopt(sk, optname, optval, optlen); | ||
842 | |||
842 | if (get_user(len, optlen)) | 843 | if (get_user(len, optlen)) |
843 | return -EFAULT; | 844 | return -EFAULT; |
844 | switch (optname) { | 845 | switch (optname) { |
@@ -1015,9 +1016,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1015 | dst = sk_dst_get(sk); | 1016 | dst = sk_dst_get(sk); |
1016 | if (dst) { | 1017 | if (dst) { |
1017 | if (val < 0) | 1018 | if (val < 0) |
1018 | val = dst_metric(dst, RTAX_HOPLIMIT); | 1019 | val = ip6_dst_hoplimit(dst); |
1019 | if (val < 0) | ||
1020 | val = ipv6_get_hoplimit(dst->dev); | ||
1021 | dst_release(dst); | 1020 | dst_release(dst); |
1022 | } | 1021 | } |
1023 | if (val < 0) | 1022 | if (val < 0) |
@@ -1045,6 +1044,24 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1045 | val = np->sndflow; | 1044 | val = np->sndflow; |
1046 | break; | 1045 | break; |
1047 | 1046 | ||
1047 | case IPV6_ADDR_PREFERENCES: | ||
1048 | val = 0; | ||
1049 | |||
1050 | if (np->srcprefs & IPV6_PREFER_SRC_TMP) | ||
1051 | val |= IPV6_PREFER_SRC_TMP; | ||
1052 | else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC) | ||
1053 | val |= IPV6_PREFER_SRC_PUBLIC; | ||
1054 | else { | ||
1055 | /* XXX: should we return system default? */ | ||
1056 | val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT; | ||
1057 | } | ||
1058 | |||
1059 | if (np->srcprefs & IPV6_PREFER_SRC_COA) | ||
1060 | val |= IPV6_PREFER_SRC_COA; | ||
1061 | else | ||
1062 | val |= IPV6_PREFER_SRC_HOME; | ||
1063 | break; | ||
1064 | |||
1048 | default: | 1065 | default: |
1049 | return -ENOPROTOOPT; | 1066 | return -ENOPROTOOPT; |
1050 | } | 1067 | } |
@@ -1128,13 +1145,3 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1128 | EXPORT_SYMBOL(compat_ipv6_getsockopt); | 1145 | EXPORT_SYMBOL(compat_ipv6_getsockopt); |
1129 | #endif | 1146 | #endif |
1130 | 1147 | ||
1131 | int __init ipv6_packet_init(void) | ||
1132 | { | ||
1133 | dev_add_pack(&ipv6_packet_type); | ||
1134 | return 0; | ||
1135 | } | ||
1136 | |||
1137 | void ipv6_packet_cleanup(void) | ||
1138 | { | ||
1139 | dev_remove_pack(&ipv6_packet_type); | ||
1140 | } | ||