diff options
Diffstat (limited to 'net/ipv4/icmp.c')
-rw-r--r-- | net/ipv4/icmp.c | 240 |
1 files changed, 113 insertions, 127 deletions
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 4aa1b7f01ea0..a91dc1611081 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c | |||
@@ -233,48 +233,11 @@ static inline void icmp_xmit_unlock(struct sock *sk) | |||
233 | * Send an ICMP frame. | 233 | * Send an ICMP frame. |
234 | */ | 234 | */ |
235 | 235 | ||
236 | /* | 236 | static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, |
237 | * Check transmit rate limitation for given message. | ||
238 | * The rate information is held in the destination cache now. | ||
239 | * This function is generic and could be used for other purposes | ||
240 | * too. It uses a Token bucket filter as suggested by Alexey Kuznetsov. | ||
241 | * | ||
242 | * Note that the same dst_entry fields are modified by functions in | ||
243 | * route.c too, but these work for packet destinations while xrlim_allow | ||
244 | * works for icmp destinations. This means the rate limiting information | ||
245 | * for one "ip object" is shared - and these ICMPs are twice limited: | ||
246 | * by source and by destination. | ||
247 | * | ||
248 | * RFC 1812: 4.3.2.8 SHOULD be able to limit error message rate | ||
249 | * SHOULD allow setting of rate limits | ||
250 | * | ||
251 | * Shared between ICMPv4 and ICMPv6. | ||
252 | */ | ||
253 | #define XRLIM_BURST_FACTOR 6 | ||
254 | int xrlim_allow(struct dst_entry *dst, int timeout) | ||
255 | { | ||
256 | unsigned long now, token = dst->rate_tokens; | ||
257 | int rc = 0; | ||
258 | |||
259 | now = jiffies; | ||
260 | token += now - dst->rate_last; | ||
261 | dst->rate_last = now; | ||
262 | if (token > XRLIM_BURST_FACTOR * timeout) | ||
263 | token = XRLIM_BURST_FACTOR * timeout; | ||
264 | if (token >= timeout) { | ||
265 | token -= timeout; | ||
266 | rc = 1; | ||
267 | } | ||
268 | dst->rate_tokens = token; | ||
269 | return rc; | ||
270 | } | ||
271 | EXPORT_SYMBOL(xrlim_allow); | ||
272 | |||
273 | static inline int icmpv4_xrlim_allow(struct net *net, struct rtable *rt, | ||
274 | int type, int code) | 237 | int type, int code) |
275 | { | 238 | { |
276 | struct dst_entry *dst = &rt->dst; | 239 | struct dst_entry *dst = &rt->dst; |
277 | int rc = 1; | 240 | bool rc = true; |
278 | 241 | ||
279 | if (type > NR_ICMP_TYPES) | 242 | if (type > NR_ICMP_TYPES) |
280 | goto out; | 243 | goto out; |
@@ -288,8 +251,12 @@ static inline int icmpv4_xrlim_allow(struct net *net, struct rtable *rt, | |||
288 | goto out; | 251 | goto out; |
289 | 252 | ||
290 | /* Limit if icmp type is enabled in ratemask. */ | 253 | /* Limit if icmp type is enabled in ratemask. */ |
291 | if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) | 254 | if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) { |
292 | rc = xrlim_allow(dst, net->ipv4.sysctl_icmp_ratelimit); | 255 | if (!rt->peer) |
256 | rt_bind_peer(rt, 1); | ||
257 | rc = inet_peer_xrlim_allow(rt->peer, | ||
258 | net->ipv4.sysctl_icmp_ratelimit); | ||
259 | } | ||
293 | out: | 260 | out: |
294 | return rc; | 261 | return rc; |
295 | } | 262 | } |
@@ -386,12 +353,15 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) | |||
386 | daddr = icmp_param->replyopts.faddr; | 353 | daddr = icmp_param->replyopts.faddr; |
387 | } | 354 | } |
388 | { | 355 | { |
389 | struct flowi fl = { .fl4_dst= daddr, | 356 | struct flowi4 fl4 = { |
390 | .fl4_src = rt->rt_spec_dst, | 357 | .daddr = daddr, |
391 | .fl4_tos = RT_TOS(ip_hdr(skb)->tos), | 358 | .saddr = rt->rt_spec_dst, |
392 | .proto = IPPROTO_ICMP }; | 359 | .flowi4_tos = RT_TOS(ip_hdr(skb)->tos), |
393 | security_skb_classify_flow(skb, &fl); | 360 | .flowi4_proto = IPPROTO_ICMP, |
394 | if (ip_route_output_key(net, &rt, &fl)) | 361 | }; |
362 | security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); | ||
363 | rt = ip_route_output_key(net, &fl4); | ||
364 | if (IS_ERR(rt)) | ||
395 | goto out_unlock; | 365 | goto out_unlock; |
396 | } | 366 | } |
397 | if (icmpv4_xrlim_allow(net, rt, icmp_param->data.icmph.type, | 367 | if (icmpv4_xrlim_allow(net, rt, icmp_param->data.icmph.type, |
@@ -402,6 +372,97 @@ out_unlock: | |||
402 | icmp_xmit_unlock(sk); | 372 | icmp_xmit_unlock(sk); |
403 | } | 373 | } |
404 | 374 | ||
375 | static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, | ||
376 | struct iphdr *iph, | ||
377 | __be32 saddr, u8 tos, | ||
378 | int type, int code, | ||
379 | struct icmp_bxm *param) | ||
380 | { | ||
381 | struct flowi4 fl4 = { | ||
382 | .daddr = (param->replyopts.srr ? | ||
383 | param->replyopts.faddr : iph->saddr), | ||
384 | .saddr = saddr, | ||
385 | .flowi4_tos = RT_TOS(tos), | ||
386 | .flowi4_proto = IPPROTO_ICMP, | ||
387 | .fl4_icmp_type = type, | ||
388 | .fl4_icmp_code = code, | ||
389 | }; | ||
390 | struct rtable *rt, *rt2; | ||
391 | int err; | ||
392 | |||
393 | security_skb_classify_flow(skb_in, flowi4_to_flowi(&fl4)); | ||
394 | rt = __ip_route_output_key(net, &fl4); | ||
395 | if (IS_ERR(rt)) | ||
396 | return rt; | ||
397 | |||
398 | /* No need to clone since we're just using its address. */ | ||
399 | rt2 = rt; | ||
400 | |||
401 | if (!fl4.saddr) | ||
402 | fl4.saddr = rt->rt_src; | ||
403 | |||
404 | rt = (struct rtable *) xfrm_lookup(net, &rt->dst, | ||
405 | flowi4_to_flowi(&fl4), NULL, 0); | ||
406 | if (!IS_ERR(rt)) { | ||
407 | if (rt != rt2) | ||
408 | return rt; | ||
409 | } else if (PTR_ERR(rt) == -EPERM) { | ||
410 | rt = NULL; | ||
411 | } else | ||
412 | return rt; | ||
413 | |||
414 | err = xfrm_decode_session_reverse(skb_in, flowi4_to_flowi(&fl4), AF_INET); | ||
415 | if (err) | ||
416 | goto relookup_failed; | ||
417 | |||
418 | if (inet_addr_type(net, fl4.saddr) == RTN_LOCAL) { | ||
419 | rt2 = __ip_route_output_key(net, &fl4); | ||
420 | if (IS_ERR(rt2)) | ||
421 | err = PTR_ERR(rt2); | ||
422 | } else { | ||
423 | struct flowi4 fl4_2 = {}; | ||
424 | unsigned long orefdst; | ||
425 | |||
426 | fl4_2.daddr = fl4.saddr; | ||
427 | rt2 = ip_route_output_key(net, &fl4_2); | ||
428 | if (IS_ERR(rt2)) { | ||
429 | err = PTR_ERR(rt2); | ||
430 | goto relookup_failed; | ||
431 | } | ||
432 | /* Ugh! */ | ||
433 | orefdst = skb_in->_skb_refdst; /* save old refdst */ | ||
434 | err = ip_route_input(skb_in, fl4.daddr, fl4.saddr, | ||
435 | RT_TOS(tos), rt2->dst.dev); | ||
436 | |||
437 | dst_release(&rt2->dst); | ||
438 | rt2 = skb_rtable(skb_in); | ||
439 | skb_in->_skb_refdst = orefdst; /* restore old refdst */ | ||
440 | } | ||
441 | |||
442 | if (err) | ||
443 | goto relookup_failed; | ||
444 | |||
445 | rt2 = (struct rtable *) xfrm_lookup(net, &rt2->dst, | ||
446 | flowi4_to_flowi(&fl4), NULL, | ||
447 | XFRM_LOOKUP_ICMP); | ||
448 | if (!IS_ERR(rt2)) { | ||
449 | dst_release(&rt->dst); | ||
450 | rt = rt2; | ||
451 | } else if (PTR_ERR(rt2) == -EPERM) { | ||
452 | if (rt) | ||
453 | dst_release(&rt->dst); | ||
454 | return rt2; | ||
455 | } else { | ||
456 | err = PTR_ERR(rt2); | ||
457 | goto relookup_failed; | ||
458 | } | ||
459 | return rt; | ||
460 | |||
461 | relookup_failed: | ||
462 | if (rt) | ||
463 | return rt; | ||
464 | return ERR_PTR(err); | ||
465 | } | ||
405 | 466 | ||
406 | /* | 467 | /* |
407 | * Send an ICMP message in response to a situation | 468 | * Send an ICMP message in response to a situation |
@@ -507,7 +568,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | |||
507 | rcu_read_lock(); | 568 | rcu_read_lock(); |
508 | if (rt_is_input_route(rt) && | 569 | if (rt_is_input_route(rt) && |
509 | net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr) | 570 | net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr) |
510 | dev = dev_get_by_index_rcu(net, rt->fl.iif); | 571 | dev = dev_get_by_index_rcu(net, rt->rt_iif); |
511 | 572 | ||
512 | if (dev) | 573 | if (dev) |
513 | saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK); | 574 | saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK); |
@@ -539,86 +600,11 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) | |||
539 | ipc.opt = &icmp_param.replyopts; | 600 | ipc.opt = &icmp_param.replyopts; |
540 | ipc.tx_flags = 0; | 601 | ipc.tx_flags = 0; |
541 | 602 | ||
542 | { | 603 | rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, |
543 | struct flowi fl = { | 604 | type, code, &icmp_param); |
544 | .fl4_dst = icmp_param.replyopts.srr ? | 605 | if (IS_ERR(rt)) |
545 | icmp_param.replyopts.faddr : iph->saddr, | 606 | goto out_unlock; |
546 | .fl4_src = saddr, | ||
547 | .fl4_tos = RT_TOS(tos), | ||
548 | .proto = IPPROTO_ICMP, | ||
549 | .fl_icmp_type = type, | ||
550 | .fl_icmp_code = code, | ||
551 | }; | ||
552 | int err; | ||
553 | struct rtable *rt2; | ||
554 | |||
555 | security_skb_classify_flow(skb_in, &fl); | ||
556 | if (__ip_route_output_key(net, &rt, &fl)) | ||
557 | goto out_unlock; | ||
558 | |||
559 | /* No need to clone since we're just using its address. */ | ||
560 | rt2 = rt; | ||
561 | |||
562 | if (!fl.nl_u.ip4_u.saddr) | ||
563 | fl.nl_u.ip4_u.saddr = rt->rt_src; | ||
564 | |||
565 | err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0); | ||
566 | switch (err) { | ||
567 | case 0: | ||
568 | if (rt != rt2) | ||
569 | goto route_done; | ||
570 | break; | ||
571 | case -EPERM: | ||
572 | rt = NULL; | ||
573 | break; | ||
574 | default: | ||
575 | goto out_unlock; | ||
576 | } | ||
577 | |||
578 | if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET)) | ||
579 | goto relookup_failed; | ||
580 | |||
581 | if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) | ||
582 | err = __ip_route_output_key(net, &rt2, &fl); | ||
583 | else { | ||
584 | struct flowi fl2 = {}; | ||
585 | unsigned long orefdst; | ||
586 | |||
587 | fl2.fl4_dst = fl.fl4_src; | ||
588 | if (ip_route_output_key(net, &rt2, &fl2)) | ||
589 | goto relookup_failed; | ||
590 | |||
591 | /* Ugh! */ | ||
592 | orefdst = skb_in->_skb_refdst; /* save old refdst */ | ||
593 | err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src, | ||
594 | RT_TOS(tos), rt2->dst.dev); | ||
595 | |||
596 | dst_release(&rt2->dst); | ||
597 | rt2 = skb_rtable(skb_in); | ||
598 | skb_in->_skb_refdst = orefdst; /* restore old refdst */ | ||
599 | } | ||
600 | |||
601 | if (err) | ||
602 | goto relookup_failed; | ||
603 | |||
604 | err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL, | ||
605 | XFRM_LOOKUP_ICMP); | ||
606 | switch (err) { | ||
607 | case 0: | ||
608 | dst_release(&rt->dst); | ||
609 | rt = rt2; | ||
610 | break; | ||
611 | case -EPERM: | ||
612 | goto ende; | ||
613 | default: | ||
614 | relookup_failed: | ||
615 | if (!rt) | ||
616 | goto out_unlock; | ||
617 | break; | ||
618 | } | ||
619 | } | ||
620 | 607 | ||
621 | route_done: | ||
622 | if (!icmpv4_xrlim_allow(net, rt, type, code)) | 608 | if (!icmpv4_xrlim_allow(net, rt, type, code)) |
623 | goto ende; | 609 | goto ende; |
624 | 610 | ||