diff options
author | David S. Miller <davem@davemloft.net> | 2012-07-12 00:27:49 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-07-12 00:27:49 -0400 |
commit | 55be7a9c6074f749d617a7fc1914c9a23505438c (patch) | |
tree | 507268878abc1c5f5476823264b6f9f8acefa205 /net | |
parent | b42597e2f36e2043756aa7462faddf630962f061 (diff) |
ipv4: Add redirect support to all protocol icmp error handlers.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/dccp/ipv4.c | 11 | ||||
-rw-r--r-- | net/ipv4/ah4.c | 18 | ||||
-rw-r--r-- | net/ipv4/esp4.c | 18 | ||||
-rw-r--r-- | net/ipv4/ip_gre.c | 9 | ||||
-rw-r--r-- | net/ipv4/ipcomp.c | 18 | ||||
-rw-r--r-- | net/ipv4/ipip.c | 9 | ||||
-rw-r--r-- | net/ipv4/ping.c | 1 | ||||
-rw-r--r-- | net/ipv4/raw.c | 2 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 11 | ||||
-rw-r--r-- | net/ipv4/udp.c | 3 | ||||
-rw-r--r-- | net/ipv4/xfrm4_policy.c | 10 | ||||
-rw-r--r-- | net/sctp/input.c | 16 |
12 files changed, 110 insertions, 16 deletions
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 3eb76b5f221a..8f41a3190858 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -195,6 +195,14 @@ static inline void dccp_do_pmtu_discovery(struct sock *sk, | |||
195 | } /* else let the usual retransmit timer handle it */ | 195 | } /* else let the usual retransmit timer handle it */ |
196 | } | 196 | } |
197 | 197 | ||
198 | static void dccp_do_redirect(struct sk_buff *skb, struct sock *sk) | ||
199 | { | ||
200 | struct dst_entry *dst = __sk_dst_check(sk, 0); | ||
201 | |||
202 | if (dst && dst->ops->redirect) | ||
203 | dst->ops->redirect(dst, skb); | ||
204 | } | ||
205 | |||
198 | /* | 206 | /* |
199 | * This routine is called by the ICMP module when it gets some sort of error | 207 | * This routine is called by the ICMP module when it gets some sort of error |
200 | * condition. If err < 0 then the socket should be closed and the error | 208 | * condition. If err < 0 then the socket should be closed and the error |
@@ -259,6 +267,9 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) | |||
259 | } | 267 | } |
260 | 268 | ||
261 | switch (type) { | 269 | switch (type) { |
270 | case ICMP_REDIRECT: | ||
271 | dccp_do_redirect(skb, sk); | ||
272 | goto out; | ||
262 | case ICMP_SOURCE_QUENCH: | 273 | case ICMP_SOURCE_QUENCH: |
263 | /* Just silently ignore these. */ | 274 | /* Just silently ignore these. */ |
264 | goto out; | 275 | goto out; |
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 916d5ecaf6c6..a0d8392491c3 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c | |||
@@ -398,17 +398,25 @@ static void ah4_err(struct sk_buff *skb, u32 info) | |||
398 | struct ip_auth_hdr *ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2)); | 398 | struct ip_auth_hdr *ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2)); |
399 | struct xfrm_state *x; | 399 | struct xfrm_state *x; |
400 | 400 | ||
401 | if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH || | 401 | switch (icmp_hdr(skb)->type) { |
402 | icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) | 402 | case ICMP_DEST_UNREACH: |
403 | if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) | ||
404 | return; | ||
405 | case ICMP_REDIRECT: | ||
406 | break; | ||
407 | default: | ||
403 | return; | 408 | return; |
409 | } | ||
404 | 410 | ||
405 | x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, | 411 | x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, |
406 | ah->spi, IPPROTO_AH, AF_INET); | 412 | ah->spi, IPPROTO_AH, AF_INET); |
407 | if (!x) | 413 | if (!x) |
408 | return; | 414 | return; |
409 | pr_debug("pmtu discovery on SA AH/%08x/%08x\n", | 415 | |
410 | ntohl(ah->spi), ntohl(iph->daddr)); | 416 | if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) |
411 | ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0); | 417 | ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0); |
418 | else | ||
419 | ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0); | ||
412 | xfrm_state_put(x); | 420 | xfrm_state_put(x); |
413 | } | 421 | } |
414 | 422 | ||
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 7b95b49a36ce..b61e9deb7c7e 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c | |||
@@ -484,17 +484,25 @@ static void esp4_err(struct sk_buff *skb, u32 info) | |||
484 | struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); | 484 | struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); |
485 | struct xfrm_state *x; | 485 | struct xfrm_state *x; |
486 | 486 | ||
487 | if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH || | 487 | switch (icmp_hdr(skb)->type) { |
488 | icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) | 488 | case ICMP_DEST_UNREACH: |
489 | if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) | ||
490 | return; | ||
491 | case ICMP_REDIRECT: | ||
492 | break; | ||
493 | default: | ||
489 | return; | 494 | return; |
495 | } | ||
490 | 496 | ||
491 | x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, | 497 | x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, |
492 | esph->spi, IPPROTO_ESP, AF_INET); | 498 | esph->spi, IPPROTO_ESP, AF_INET); |
493 | if (!x) | 499 | if (!x) |
494 | return; | 500 | return; |
495 | NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n", | 501 | |
496 | ntohl(esph->spi), ntohl(iph->daddr)); | 502 | if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) |
497 | ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0); | 503 | ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0); |
504 | else | ||
505 | ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0); | ||
498 | xfrm_state_put(x); | 506 | xfrm_state_put(x); |
499 | } | 507 | } |
500 | 508 | ||
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 594cec35ac4d..0c3123566d76 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c | |||
@@ -528,6 +528,9 @@ static void ipgre_err(struct sk_buff *skb, u32 info) | |||
528 | if (code != ICMP_EXC_TTL) | 528 | if (code != ICMP_EXC_TTL) |
529 | return; | 529 | return; |
530 | break; | 530 | break; |
531 | |||
532 | case ICMP_REDIRECT: | ||
533 | break; | ||
531 | } | 534 | } |
532 | 535 | ||
533 | rcu_read_lock(); | 536 | rcu_read_lock(); |
@@ -543,7 +546,11 @@ static void ipgre_err(struct sk_buff *skb, u32 info) | |||
543 | t->parms.link, 0, IPPROTO_GRE, 0); | 546 | t->parms.link, 0, IPPROTO_GRE, 0); |
544 | goto out; | 547 | goto out; |
545 | } | 548 | } |
546 | 549 | if (type == ICMP_REDIRECT) { | |
550 | ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0, | ||
551 | IPPROTO_GRE, 0); | ||
552 | goto out; | ||
553 | } | ||
547 | if (t->parms.iph.daddr == 0 || | 554 | if (t->parms.iph.daddr == 0 || |
548 | ipv4_is_multicast(t->parms.iph.daddr)) | 555 | ipv4_is_multicast(t->parms.iph.daddr)) |
549 | goto out; | 556 | goto out; |
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index b91375482d84..d3ab47e19a89 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c | |||
@@ -31,18 +31,26 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info) | |||
31 | struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); | 31 | struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); |
32 | struct xfrm_state *x; | 32 | struct xfrm_state *x; |
33 | 33 | ||
34 | if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH || | 34 | switch (icmp_hdr(skb)->type) { |
35 | icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) | 35 | case ICMP_DEST_UNREACH: |
36 | if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) | ||
37 | return; | ||
38 | case ICMP_REDIRECT: | ||
39 | break; | ||
40 | default: | ||
36 | return; | 41 | return; |
42 | } | ||
37 | 43 | ||
38 | spi = htonl(ntohs(ipch->cpi)); | 44 | spi = htonl(ntohs(ipch->cpi)); |
39 | x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, | 45 | x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, |
40 | spi, IPPROTO_COMP, AF_INET); | 46 | spi, IPPROTO_COMP, AF_INET); |
41 | if (!x) | 47 | if (!x) |
42 | return; | 48 | return; |
43 | NETDEBUG(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/%pI4\n", | 49 | |
44 | spi, &iph->daddr); | 50 | if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) |
45 | ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0); | 51 | ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0); |
52 | else | ||
53 | ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0); | ||
46 | xfrm_state_put(x); | 54 | xfrm_state_put(x); |
47 | } | 55 | } |
48 | 56 | ||
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 715338a1b205..c2d0e6d8baaf 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c | |||
@@ -360,6 +360,8 @@ static int ipip_err(struct sk_buff *skb, u32 info) | |||
360 | if (code != ICMP_EXC_TTL) | 360 | if (code != ICMP_EXC_TTL) |
361 | return 0; | 361 | return 0; |
362 | break; | 362 | break; |
363 | case ICMP_REDIRECT: | ||
364 | break; | ||
363 | } | 365 | } |
364 | 366 | ||
365 | err = -ENOENT; | 367 | err = -ENOENT; |
@@ -376,6 +378,13 @@ static int ipip_err(struct sk_buff *skb, u32 info) | |||
376 | goto out; | 378 | goto out; |
377 | } | 379 | } |
378 | 380 | ||
381 | if (type == ICMP_REDIRECT) { | ||
382 | ipv4_redirect(skb, dev_net(skb->dev), t->dev->ifindex, 0, | ||
383 | IPPROTO_IPIP, 0); | ||
384 | err = 0; | ||
385 | goto out; | ||
386 | } | ||
387 | |||
379 | if (t->parms.iph.daddr == 0) | 388 | if (t->parms.iph.daddr == 0) |
380 | goto out; | 389 | goto out; |
381 | 390 | ||
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 340fcf29a966..6232d476f37e 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c | |||
@@ -387,6 +387,7 @@ void ping_err(struct sk_buff *skb, u32 info) | |||
387 | break; | 387 | break; |
388 | case ICMP_REDIRECT: | 388 | case ICMP_REDIRECT: |
389 | /* See ICMP_SOURCE_QUENCH */ | 389 | /* See ICMP_SOURCE_QUENCH */ |
390 | ipv4_sk_redirect(skb, sk); | ||
390 | err = EREMOTEIO; | 391 | err = EREMOTEIO; |
391 | break; | 392 | break; |
392 | } | 393 | } |
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 659ddfb10947..ff0f071969ea 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c | |||
@@ -218,6 +218,8 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) | |||
218 | 218 | ||
219 | if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) | 219 | if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) |
220 | ipv4_sk_update_pmtu(skb, sk, info); | 220 | ipv4_sk_update_pmtu(skb, sk, info); |
221 | else if (type == ICMP_REDIRECT) | ||
222 | ipv4_sk_redirect(skb, sk); | ||
221 | 223 | ||
222 | /* Report error on raw socket, if: | 224 | /* Report error on raw socket, if: |
223 | 1. User requested ip_recverr. | 225 | 1. User requested ip_recverr. |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 01545a3fc0f2..087a8488843f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -321,6 +321,14 @@ static void do_pmtu_discovery(struct sock *sk, const struct iphdr *iph, u32 mtu) | |||
321 | } /* else let the usual retransmit timer handle it */ | 321 | } /* else let the usual retransmit timer handle it */ |
322 | } | 322 | } |
323 | 323 | ||
324 | static void do_redirect(struct sk_buff *skb, struct sock *sk) | ||
325 | { | ||
326 | struct dst_entry *dst = __sk_dst_check(sk, 0); | ||
327 | |||
328 | if (dst && dst->ops->redirect) | ||
329 | dst->ops->redirect(dst, skb); | ||
330 | } | ||
331 | |||
324 | /* | 332 | /* |
325 | * This routine is called by the ICMP module when it gets some | 333 | * This routine is called by the ICMP module when it gets some |
326 | * sort of error condition. If err < 0 then the socket should | 334 | * sort of error condition. If err < 0 then the socket should |
@@ -394,6 +402,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) | |||
394 | } | 402 | } |
395 | 403 | ||
396 | switch (type) { | 404 | switch (type) { |
405 | case ICMP_REDIRECT: | ||
406 | do_redirect(icmp_skb, sk); | ||
407 | goto out; | ||
397 | case ICMP_SOURCE_QUENCH: | 408 | case ICMP_SOURCE_QUENCH: |
398 | /* Just silently ignore these. */ | 409 | /* Just silently ignore these. */ |
399 | goto out; | 410 | goto out; |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index ee37d47d472e..b4c3582a991f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -630,6 +630,9 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) | |||
630 | err = icmp_err_convert[code].errno; | 630 | err = icmp_err_convert[code].errno; |
631 | } | 631 | } |
632 | break; | 632 | break; |
633 | case ICMP_REDIRECT: | ||
634 | ipv4_sk_redirect(skb, sk); | ||
635 | break; | ||
633 | } | 636 | } |
634 | 637 | ||
635 | /* | 638 | /* |
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 87d3fcc302d4..258ebd7b268b 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c | |||
@@ -202,6 +202,15 @@ static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu) | |||
202 | path->ops->update_pmtu(path, mtu); | 202 | path->ops->update_pmtu(path, mtu); |
203 | } | 203 | } |
204 | 204 | ||
205 | static void xfrm4_redirect(struct dst_entry *dst, struct sk_buff *skb) | ||
206 | { | ||
207 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; | ||
208 | struct dst_entry *path = xdst->route; | ||
209 | |||
210 | if (path->ops->redirect) | ||
211 | path->ops->redirect(path, skb); | ||
212 | } | ||
213 | |||
205 | static void xfrm4_dst_destroy(struct dst_entry *dst) | 214 | static void xfrm4_dst_destroy(struct dst_entry *dst) |
206 | { | 215 | { |
207 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; | 216 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; |
@@ -225,6 +234,7 @@ static struct dst_ops xfrm4_dst_ops = { | |||
225 | .protocol = cpu_to_be16(ETH_P_IP), | 234 | .protocol = cpu_to_be16(ETH_P_IP), |
226 | .gc = xfrm4_garbage_collect, | 235 | .gc = xfrm4_garbage_collect, |
227 | .update_pmtu = xfrm4_update_pmtu, | 236 | .update_pmtu = xfrm4_update_pmtu, |
237 | .redirect = xfrm4_redirect, | ||
228 | .cow_metrics = dst_cow_metrics_generic, | 238 | .cow_metrics = dst_cow_metrics_generic, |
229 | .destroy = xfrm4_dst_destroy, | 239 | .destroy = xfrm4_dst_destroy, |
230 | .ifdown = xfrm4_dst_ifdown, | 240 | .ifdown = xfrm4_dst_ifdown, |
diff --git a/net/sctp/input.c b/net/sctp/input.c index 80564fe03024..9fb4247f9a99 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c | |||
@@ -423,6 +423,18 @@ void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc, | |||
423 | sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD); | 423 | sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD); |
424 | } | 424 | } |
425 | 425 | ||
426 | static void sctp_icmp_redirect(struct sock *sk, struct sctp_transport *t, | ||
427 | struct sk_buff *skb) | ||
428 | { | ||
429 | struct dst_entry *dst; | ||
430 | |||
431 | if (!t) | ||
432 | return; | ||
433 | dst = sctp_transport_dst_check(t); | ||
434 | if (dst && dst->ops->redirect) | ||
435 | dst->ops->redirect(dst, skb); | ||
436 | } | ||
437 | |||
426 | /* | 438 | /* |
427 | * SCTP Implementer's Guide, 2.37 ICMP handling procedures | 439 | * SCTP Implementer's Guide, 2.37 ICMP handling procedures |
428 | * | 440 | * |
@@ -628,6 +640,10 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) | |||
628 | 640 | ||
629 | err = EHOSTUNREACH; | 641 | err = EHOSTUNREACH; |
630 | break; | 642 | break; |
643 | case ICMP_REDIRECT: | ||
644 | sctp_icmp_redirect(sk, transport, skb); | ||
645 | err = 0; | ||
646 | break; | ||
631 | default: | 647 | default: |
632 | goto out_unlock; | 648 | goto out_unlock; |
633 | } | 649 | } |