diff options
Diffstat (limited to 'net/ipv6/icmp.c')
-rw-r--r-- | net/ipv6/icmp.c | 226 |
1 files changed, 121 insertions, 105 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 03e62f94ff8..83cb4f9add8 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -157,32 +157,32 @@ static int is_ineligible(struct sk_buff *skb) | |||
157 | /* | 157 | /* |
158 | * Check the ICMP output rate limit | 158 | * Check the ICMP output rate limit |
159 | */ | 159 | */ |
160 | static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type, | 160 | static inline bool icmpv6_xrlim_allow(struct sock *sk, u8 type, |
161 | struct flowi *fl) | 161 | struct flowi6 *fl6) |
162 | { | 162 | { |
163 | struct dst_entry *dst; | 163 | struct dst_entry *dst; |
164 | struct net *net = sock_net(sk); | 164 | struct net *net = sock_net(sk); |
165 | int res = 0; | 165 | bool res = false; |
166 | 166 | ||
167 | /* Informational messages are not limited. */ | 167 | /* Informational messages are not limited. */ |
168 | if (type & ICMPV6_INFOMSG_MASK) | 168 | if (type & ICMPV6_INFOMSG_MASK) |
169 | return 1; | 169 | return true; |
170 | 170 | ||
171 | /* Do not limit pmtu discovery, it would break it. */ | 171 | /* Do not limit pmtu discovery, it would break it. */ |
172 | if (type == ICMPV6_PKT_TOOBIG) | 172 | if (type == ICMPV6_PKT_TOOBIG) |
173 | return 1; | 173 | return true; |
174 | 174 | ||
175 | /* | 175 | /* |
176 | * Look up the output route. | 176 | * Look up the output route. |
177 | * XXX: perhaps the expire for routing entries cloned by | 177 | * XXX: perhaps the expire for routing entries cloned by |
178 | * this lookup should be more aggressive (not longer than timeout). | 178 | * this lookup should be more aggressive (not longer than timeout). |
179 | */ | 179 | */ |
180 | dst = ip6_route_output(net, sk, fl); | 180 | dst = ip6_route_output(net, sk, fl6); |
181 | if (dst->error) { | 181 | if (dst->error) { |
182 | IP6_INC_STATS(net, ip6_dst_idev(dst), | 182 | IP6_INC_STATS(net, ip6_dst_idev(dst), |
183 | IPSTATS_MIB_OUTNOROUTES); | 183 | IPSTATS_MIB_OUTNOROUTES); |
184 | } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) { | 184 | } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) { |
185 | res = 1; | 185 | res = true; |
186 | } else { | 186 | } else { |
187 | struct rt6_info *rt = (struct rt6_info *)dst; | 187 | struct rt6_info *rt = (struct rt6_info *)dst; |
188 | int tmo = net->ipv6.sysctl.icmpv6_time; | 188 | int tmo = net->ipv6.sysctl.icmpv6_time; |
@@ -191,7 +191,9 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type, | |||
191 | if (rt->rt6i_dst.plen < 128) | 191 | if (rt->rt6i_dst.plen < 128) |
192 | tmo >>= ((128 - rt->rt6i_dst.plen)>>5); | 192 | tmo >>= ((128 - rt->rt6i_dst.plen)>>5); |
193 | 193 | ||
194 | res = xrlim_allow(dst, tmo); | 194 | if (!rt->rt6i_peer) |
195 | rt6_bind_peer(rt, 1); | ||
196 | res = inet_peer_xrlim_allow(rt->rt6i_peer, tmo); | ||
195 | } | 197 | } |
196 | dst_release(dst); | 198 | dst_release(dst); |
197 | return res; | 199 | return res; |
@@ -215,7 +217,7 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset) | |||
215 | return (*op & 0xC0) == 0x80; | 217 | return (*op & 0xC0) == 0x80; |
216 | } | 218 | } |
217 | 219 | ||
218 | static int icmpv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct icmp6hdr *thdr, int len) | 220 | static int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len) |
219 | { | 221 | { |
220 | struct sk_buff *skb; | 222 | struct sk_buff *skb; |
221 | struct icmp6hdr *icmp6h; | 223 | struct icmp6hdr *icmp6h; |
@@ -231,9 +233,9 @@ static int icmpv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct | |||
231 | if (skb_queue_len(&sk->sk_write_queue) == 1) { | 233 | if (skb_queue_len(&sk->sk_write_queue) == 1) { |
232 | skb->csum = csum_partial(icmp6h, | 234 | skb->csum = csum_partial(icmp6h, |
233 | sizeof(struct icmp6hdr), skb->csum); | 235 | sizeof(struct icmp6hdr), skb->csum); |
234 | icmp6h->icmp6_cksum = csum_ipv6_magic(&fl->fl6_src, | 236 | icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr, |
235 | &fl->fl6_dst, | 237 | &fl6->daddr, |
236 | len, fl->proto, | 238 | len, fl6->flowi6_proto, |
237 | skb->csum); | 239 | skb->csum); |
238 | } else { | 240 | } else { |
239 | __wsum tmp_csum = 0; | 241 | __wsum tmp_csum = 0; |
@@ -244,9 +246,9 @@ static int icmpv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct | |||
244 | 246 | ||
245 | tmp_csum = csum_partial(icmp6h, | 247 | tmp_csum = csum_partial(icmp6h, |
246 | sizeof(struct icmp6hdr), tmp_csum); | 248 | sizeof(struct icmp6hdr), tmp_csum); |
247 | icmp6h->icmp6_cksum = csum_ipv6_magic(&fl->fl6_src, | 249 | icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr, |
248 | &fl->fl6_dst, | 250 | &fl6->daddr, |
249 | len, fl->proto, | 251 | len, fl6->flowi6_proto, |
250 | tmp_csum); | 252 | tmp_csum); |
251 | } | 253 | } |
252 | ip6_push_pending_frames(sk); | 254 | ip6_push_pending_frames(sk); |
@@ -298,6 +300,68 @@ static void mip6_addr_swap(struct sk_buff *skb) | |||
298 | static inline void mip6_addr_swap(struct sk_buff *skb) {} | 300 | static inline void mip6_addr_swap(struct sk_buff *skb) {} |
299 | #endif | 301 | #endif |
300 | 302 | ||
303 | static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, | ||
304 | struct sock *sk, struct flowi6 *fl6) | ||
305 | { | ||
306 | struct dst_entry *dst, *dst2; | ||
307 | struct flowi6 fl2; | ||
308 | int err; | ||
309 | |||
310 | err = ip6_dst_lookup(sk, &dst, fl6); | ||
311 | if (err) | ||
312 | return ERR_PTR(err); | ||
313 | |||
314 | /* | ||
315 | * We won't send icmp if the destination is known | ||
316 | * anycast. | ||
317 | */ | ||
318 | if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { | ||
319 | LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); | ||
320 | dst_release(dst); | ||
321 | return ERR_PTR(-EINVAL); | ||
322 | } | ||
323 | |||
324 | /* No need to clone since we're just using its address. */ | ||
325 | dst2 = dst; | ||
326 | |||
327 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), sk, 0); | ||
328 | if (!IS_ERR(dst)) { | ||
329 | if (dst != dst2) | ||
330 | return dst; | ||
331 | } else { | ||
332 | if (PTR_ERR(dst) == -EPERM) | ||
333 | dst = NULL; | ||
334 | else | ||
335 | return dst; | ||
336 | } | ||
337 | |||
338 | err = xfrm_decode_session_reverse(skb, flowi6_to_flowi(&fl2), AF_INET6); | ||
339 | if (err) | ||
340 | goto relookup_failed; | ||
341 | |||
342 | err = ip6_dst_lookup(sk, &dst2, &fl2); | ||
343 | if (err) | ||
344 | goto relookup_failed; | ||
345 | |||
346 | dst2 = xfrm_lookup(net, dst2, flowi6_to_flowi(&fl2), sk, XFRM_LOOKUP_ICMP); | ||
347 | if (!IS_ERR(dst2)) { | ||
348 | dst_release(dst); | ||
349 | dst = dst2; | ||
350 | } else { | ||
351 | err = PTR_ERR(dst2); | ||
352 | if (err == -EPERM) { | ||
353 | dst_release(dst); | ||
354 | return dst2; | ||
355 | } else | ||
356 | goto relookup_failed; | ||
357 | } | ||
358 | |||
359 | relookup_failed: | ||
360 | if (dst) | ||
361 | return dst; | ||
362 | return ERR_PTR(err); | ||
363 | } | ||
364 | |||
301 | /* | 365 | /* |
302 | * Send an ICMP message in response to a packet in error | 366 | * Send an ICMP message in response to a packet in error |
303 | */ | 367 | */ |
@@ -310,10 +374,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
310 | struct ipv6_pinfo *np; | 374 | struct ipv6_pinfo *np; |
311 | struct in6_addr *saddr = NULL; | 375 | struct in6_addr *saddr = NULL; |
312 | struct dst_entry *dst; | 376 | struct dst_entry *dst; |
313 | struct dst_entry *dst2; | ||
314 | struct icmp6hdr tmp_hdr; | 377 | struct icmp6hdr tmp_hdr; |
315 | struct flowi fl; | 378 | struct flowi6 fl6; |
316 | struct flowi fl2; | ||
317 | struct icmpv6_msg msg; | 379 | struct icmpv6_msg msg; |
318 | int iif = 0; | 380 | int iif = 0; |
319 | int addr_type = 0; | 381 | int addr_type = 0; |
@@ -380,22 +442,22 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
380 | 442 | ||
381 | mip6_addr_swap(skb); | 443 | mip6_addr_swap(skb); |
382 | 444 | ||
383 | memset(&fl, 0, sizeof(fl)); | 445 | memset(&fl6, 0, sizeof(fl6)); |
384 | fl.proto = IPPROTO_ICMPV6; | 446 | fl6.flowi6_proto = IPPROTO_ICMPV6; |
385 | ipv6_addr_copy(&fl.fl6_dst, &hdr->saddr); | 447 | ipv6_addr_copy(&fl6.daddr, &hdr->saddr); |
386 | if (saddr) | 448 | if (saddr) |
387 | ipv6_addr_copy(&fl.fl6_src, saddr); | 449 | ipv6_addr_copy(&fl6.saddr, saddr); |
388 | fl.oif = iif; | 450 | fl6.flowi6_oif = iif; |
389 | fl.fl_icmp_type = type; | 451 | fl6.fl6_icmp_type = type; |
390 | fl.fl_icmp_code = code; | 452 | fl6.fl6_icmp_code = code; |
391 | security_skb_classify_flow(skb, &fl); | 453 | security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); |
392 | 454 | ||
393 | sk = icmpv6_xmit_lock(net); | 455 | sk = icmpv6_xmit_lock(net); |
394 | if (sk == NULL) | 456 | if (sk == NULL) |
395 | return; | 457 | return; |
396 | np = inet6_sk(sk); | 458 | np = inet6_sk(sk); |
397 | 459 | ||
398 | if (!icmpv6_xrlim_allow(sk, type, &fl)) | 460 | if (!icmpv6_xrlim_allow(sk, type, &fl6)) |
399 | goto out; | 461 | goto out; |
400 | 462 | ||
401 | tmp_hdr.icmp6_type = type; | 463 | tmp_hdr.icmp6_type = type; |
@@ -403,61 +465,14 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
403 | tmp_hdr.icmp6_cksum = 0; | 465 | tmp_hdr.icmp6_cksum = 0; |
404 | tmp_hdr.icmp6_pointer = htonl(info); | 466 | tmp_hdr.icmp6_pointer = htonl(info); |
405 | 467 | ||
406 | if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) | 468 | if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) |
407 | fl.oif = np->mcast_oif; | 469 | fl6.flowi6_oif = np->mcast_oif; |
408 | 470 | ||
409 | err = ip6_dst_lookup(sk, &dst, &fl); | 471 | dst = icmpv6_route_lookup(net, skb, sk, &fl6); |
410 | if (err) | 472 | if (IS_ERR(dst)) |
411 | goto out; | 473 | goto out; |
412 | 474 | ||
413 | /* | 475 | if (ipv6_addr_is_multicast(&fl6.daddr)) |
414 | * We won't send icmp if the destination is known | ||
415 | * anycast. | ||
416 | */ | ||
417 | if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { | ||
418 | LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); | ||
419 | goto out_dst_release; | ||
420 | } | ||
421 | |||
422 | /* No need to clone since we're just using its address. */ | ||
423 | dst2 = dst; | ||
424 | |||
425 | err = xfrm_lookup(net, &dst, &fl, sk, 0); | ||
426 | switch (err) { | ||
427 | case 0: | ||
428 | if (dst != dst2) | ||
429 | goto route_done; | ||
430 | break; | ||
431 | case -EPERM: | ||
432 | dst = NULL; | ||
433 | break; | ||
434 | default: | ||
435 | goto out; | ||
436 | } | ||
437 | |||
438 | if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) | ||
439 | goto relookup_failed; | ||
440 | |||
441 | if (ip6_dst_lookup(sk, &dst2, &fl2)) | ||
442 | goto relookup_failed; | ||
443 | |||
444 | err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP); | ||
445 | switch (err) { | ||
446 | case 0: | ||
447 | dst_release(dst); | ||
448 | dst = dst2; | ||
449 | break; | ||
450 | case -EPERM: | ||
451 | goto out_dst_release; | ||
452 | default: | ||
453 | relookup_failed: | ||
454 | if (!dst) | ||
455 | goto out; | ||
456 | break; | ||
457 | } | ||
458 | |||
459 | route_done: | ||
460 | if (ipv6_addr_is_multicast(&fl.fl6_dst)) | ||
461 | hlimit = np->mcast_hops; | 476 | hlimit = np->mcast_hops; |
462 | else | 477 | else |
463 | hlimit = np->hop_limit; | 478 | hlimit = np->hop_limit; |
@@ -480,14 +495,14 @@ route_done: | |||
480 | err = ip6_append_data(sk, icmpv6_getfrag, &msg, | 495 | err = ip6_append_data(sk, icmpv6_getfrag, &msg, |
481 | len + sizeof(struct icmp6hdr), | 496 | len + sizeof(struct icmp6hdr), |
482 | sizeof(struct icmp6hdr), hlimit, | 497 | sizeof(struct icmp6hdr), hlimit, |
483 | np->tclass, NULL, &fl, (struct rt6_info*)dst, | 498 | np->tclass, NULL, &fl6, (struct rt6_info*)dst, |
484 | MSG_DONTWAIT, np->dontfrag); | 499 | MSG_DONTWAIT, np->dontfrag); |
485 | if (err) { | 500 | if (err) { |
486 | ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS); | 501 | ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS); |
487 | ip6_flush_pending_frames(sk); | 502 | ip6_flush_pending_frames(sk); |
488 | goto out_put; | 503 | goto out_put; |
489 | } | 504 | } |
490 | err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, len + sizeof(struct icmp6hdr)); | 505 | err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, len + sizeof(struct icmp6hdr)); |
491 | 506 | ||
492 | out_put: | 507 | out_put: |
493 | if (likely(idev != NULL)) | 508 | if (likely(idev != NULL)) |
@@ -509,7 +524,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) | |||
509 | struct in6_addr *saddr = NULL; | 524 | struct in6_addr *saddr = NULL; |
510 | struct icmp6hdr *icmph = icmp6_hdr(skb); | 525 | struct icmp6hdr *icmph = icmp6_hdr(skb); |
511 | struct icmp6hdr tmp_hdr; | 526 | struct icmp6hdr tmp_hdr; |
512 | struct flowi fl; | 527 | struct flowi6 fl6; |
513 | struct icmpv6_msg msg; | 528 | struct icmpv6_msg msg; |
514 | struct dst_entry *dst; | 529 | struct dst_entry *dst; |
515 | int err = 0; | 530 | int err = 0; |
@@ -523,30 +538,31 @@ static void icmpv6_echo_reply(struct sk_buff *skb) | |||
523 | memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); | 538 | memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); |
524 | tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY; | 539 | tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY; |
525 | 540 | ||
526 | memset(&fl, 0, sizeof(fl)); | 541 | memset(&fl6, 0, sizeof(fl6)); |
527 | fl.proto = IPPROTO_ICMPV6; | 542 | fl6.flowi6_proto = IPPROTO_ICMPV6; |
528 | ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr); | 543 | ipv6_addr_copy(&fl6.daddr, &ipv6_hdr(skb)->saddr); |
529 | if (saddr) | 544 | if (saddr) |
530 | ipv6_addr_copy(&fl.fl6_src, saddr); | 545 | ipv6_addr_copy(&fl6.saddr, saddr); |
531 | fl.oif = skb->dev->ifindex; | 546 | fl6.flowi6_oif = skb->dev->ifindex; |
532 | fl.fl_icmp_type = ICMPV6_ECHO_REPLY; | 547 | fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; |
533 | security_skb_classify_flow(skb, &fl); | 548 | security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); |
534 | 549 | ||
535 | sk = icmpv6_xmit_lock(net); | 550 | sk = icmpv6_xmit_lock(net); |
536 | if (sk == NULL) | 551 | if (sk == NULL) |
537 | return; | 552 | return; |
538 | np = inet6_sk(sk); | 553 | np = inet6_sk(sk); |
539 | 554 | ||
540 | if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) | 555 | if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) |
541 | fl.oif = np->mcast_oif; | 556 | fl6.flowi6_oif = np->mcast_oif; |
542 | 557 | ||
543 | err = ip6_dst_lookup(sk, &dst, &fl); | 558 | err = ip6_dst_lookup(sk, &dst, &fl6); |
544 | if (err) | 559 | if (err) |
545 | goto out; | 560 | goto out; |
546 | if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0) | 561 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0); |
562 | if (IS_ERR(dst)) | ||
547 | goto out; | 563 | goto out; |
548 | 564 | ||
549 | if (ipv6_addr_is_multicast(&fl.fl6_dst)) | 565 | if (ipv6_addr_is_multicast(&fl6.daddr)) |
550 | hlimit = np->mcast_hops; | 566 | hlimit = np->mcast_hops; |
551 | else | 567 | else |
552 | hlimit = np->hop_limit; | 568 | hlimit = np->hop_limit; |
@@ -560,7 +576,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) | |||
560 | msg.type = ICMPV6_ECHO_REPLY; | 576 | msg.type = ICMPV6_ECHO_REPLY; |
561 | 577 | ||
562 | err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), | 578 | err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), |
563 | sizeof(struct icmp6hdr), hlimit, np->tclass, NULL, &fl, | 579 | sizeof(struct icmp6hdr), hlimit, np->tclass, NULL, &fl6, |
564 | (struct rt6_info*)dst, MSG_DONTWAIT, | 580 | (struct rt6_info*)dst, MSG_DONTWAIT, |
565 | np->dontfrag); | 581 | np->dontfrag); |
566 | 582 | ||
@@ -569,7 +585,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) | |||
569 | ip6_flush_pending_frames(sk); | 585 | ip6_flush_pending_frames(sk); |
570 | goto out_put; | 586 | goto out_put; |
571 | } | 587 | } |
572 | err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, skb->len + sizeof(struct icmp6hdr)); | 588 | err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, skb->len + sizeof(struct icmp6hdr)); |
573 | 589 | ||
574 | out_put: | 590 | out_put: |
575 | if (likely(idev != NULL)) | 591 | if (likely(idev != NULL)) |
@@ -768,20 +784,20 @@ drop_no_count: | |||
768 | return 0; | 784 | return 0; |
769 | } | 785 | } |
770 | 786 | ||
771 | void icmpv6_flow_init(struct sock *sk, struct flowi *fl, | 787 | void icmpv6_flow_init(struct sock *sk, struct flowi6 *fl6, |
772 | u8 type, | 788 | u8 type, |
773 | const struct in6_addr *saddr, | 789 | const struct in6_addr *saddr, |
774 | const struct in6_addr *daddr, | 790 | const struct in6_addr *daddr, |
775 | int oif) | 791 | int oif) |
776 | { | 792 | { |
777 | memset(fl, 0, sizeof(*fl)); | 793 | memset(fl6, 0, sizeof(*fl6)); |
778 | ipv6_addr_copy(&fl->fl6_src, saddr); | 794 | ipv6_addr_copy(&fl6->saddr, saddr); |
779 | ipv6_addr_copy(&fl->fl6_dst, daddr); | 795 | ipv6_addr_copy(&fl6->daddr, daddr); |
780 | fl->proto = IPPROTO_ICMPV6; | 796 | fl6->flowi6_proto = IPPROTO_ICMPV6; |
781 | fl->fl_icmp_type = type; | 797 | fl6->fl6_icmp_type = type; |
782 | fl->fl_icmp_code = 0; | 798 | fl6->fl6_icmp_code = 0; |
783 | fl->oif = oif; | 799 | fl6->flowi6_oif = oif; |
784 | security_sk_classify_flow(sk, fl); | 800 | security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); |
785 | } | 801 | } |
786 | 802 | ||
787 | /* | 803 | /* |