diff options
author | Jesper Dangaard Brouer <brouer@redhat.com> | 2012-09-26 08:06:59 -0400 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2012-09-27 22:34:24 -0400 |
commit | 2f74713d1436b7d2d0506ba1bc5f10915a73bbec (patch) | |
tree | 7443abafe2fed8dc8018fdb6bbd6a6619c11eb24 /net/netfilter/ipvs | |
parent | 63dca2c0b0e7a92cb39d1b1ecefa32ffda201975 (diff) |
ipvs: Complete IPv6 fragment handling for IPVS
IPVS now supports fragmented packets, with support from nf_conntrack_reasm.c
Based on patch from: Hans Schillstrom.
IPVS do like conntrack i.e. use the skb->nfct_reasm
(i.e. when all fragments is collected, nf_ct_frag6_output()
starts a "re-play" of all fragments into the interrupted
PREROUTING chain at prio -399 (NF_IP6_PRI_CONNTRACK_DEFRAG+1)
with nfct_reasm pointing to the assembled packet.)
Notice, module nf_defrag_ipv6 must be loaded for this to work.
Report unhandled fragments, and recommend user to load nf_defrag_ipv6.
To handle fw-mark for fragments. Add a new IPVS hook into prerouting
chain at prio -99 (NF_IP6_PRI_NAT_DST+1) to catch fragments, and copy
fw-mark info from the first packet with an upper layer header.
IPv6 fragment handling should be the last thing on the IPVS IPv6
missing support list.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Hans Schillstrom <hans@schillstrom.com>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'net/netfilter/ipvs')
-rw-r--r-- | net/netfilter/ipvs/Kconfig | 6 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_conn.c | 2 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_core.c | 117 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_xmit.c | 36 |
4 files changed, 126 insertions, 35 deletions
diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig index a97ae5328ae..0c3b1670b0d 100644 --- a/net/netfilter/ipvs/Kconfig +++ b/net/netfilter/ipvs/Kconfig | |||
@@ -30,11 +30,9 @@ config IP_VS_IPV6 | |||
30 | depends on IPV6 = y || IP_VS = IPV6 | 30 | depends on IPV6 = y || IP_VS = IPV6 |
31 | select IP6_NF_IPTABLES | 31 | select IP6_NF_IPTABLES |
32 | ---help--- | 32 | ---help--- |
33 | Add IPv6 support to IPVS. This is incomplete and might be dangerous. | 33 | Add IPv6 support to IPVS. |
34 | 34 | ||
35 | See http://www.mindbasket.com/ipvs for more information. | 35 | Say Y if unsure. |
36 | |||
37 | Say N if unsure. | ||
38 | 36 | ||
39 | config IP_VS_DEBUG | 37 | config IP_VS_DEBUG |
40 | bool "IP virtual server debugging" | 38 | bool "IP virtual server debugging" |
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 1548df9a752..d6c1c2636dd 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c | |||
@@ -314,7 +314,7 @@ ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb, | |||
314 | __be16 _ports[2], *pptr; | 314 | __be16 _ports[2], *pptr; |
315 | struct net *net = skb_net(skb); | 315 | struct net *net = skb_net(skb); |
316 | 316 | ||
317 | pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports); | 317 | pptr = frag_safe_skb_hp(skb, proto_off, sizeof(_ports), _ports, iph); |
318 | if (pptr == NULL) | 318 | if (pptr == NULL) |
319 | return 1; | 319 | return 1; |
320 | 320 | ||
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 19c08425e13..19b89ff94cd 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c | |||
@@ -402,8 +402,12 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, | |||
402 | unsigned int flags; | 402 | unsigned int flags; |
403 | 403 | ||
404 | *ignored = 1; | 404 | *ignored = 1; |
405 | |||
406 | /* | ||
407 | * IPv6 frags, only the first hit here. | ||
408 | */ | ||
405 | ip_vs_fill_iph_skb(svc->af, skb, &iph); | 409 | ip_vs_fill_iph_skb(svc->af, skb, &iph); |
406 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); | 410 | pptr = frag_safe_skb_hp(skb, iph.len, sizeof(_ports), _ports, &iph); |
407 | if (pptr == NULL) | 411 | if (pptr == NULL) |
408 | return NULL; | 412 | return NULL; |
409 | 413 | ||
@@ -507,8 +511,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
507 | #endif | 511 | #endif |
508 | 512 | ||
509 | ip_vs_fill_iph_skb(svc->af, skb, &iph); | 513 | ip_vs_fill_iph_skb(svc->af, skb, &iph); |
510 | 514 | pptr = frag_safe_skb_hp(skb, iph.len, sizeof(_ports), _ports, &iph); | |
511 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); | ||
512 | if (pptr == NULL) { | 515 | if (pptr == NULL) { |
513 | ip_vs_service_put(svc); | 516 | ip_vs_service_put(svc); |
514 | return NF_DROP; | 517 | return NF_DROP; |
@@ -654,14 +657,6 @@ static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user) | |||
654 | return err; | 657 | return err; |
655 | } | 658 | } |
656 | 659 | ||
657 | #ifdef CONFIG_IP_VS_IPV6 | ||
658 | static inline int ip_vs_gather_frags_v6(struct sk_buff *skb, u_int32_t user) | ||
659 | { | ||
660 | /* TODO IPv6: Find out what to do here for IPv6 */ | ||
661 | return 0; | ||
662 | } | ||
663 | #endif | ||
664 | |||
665 | static int ip_vs_route_me_harder(int af, struct sk_buff *skb) | 660 | static int ip_vs_route_me_harder(int af, struct sk_buff *skb) |
666 | { | 661 | { |
667 | #ifdef CONFIG_IP_VS_IPV6 | 662 | #ifdef CONFIG_IP_VS_IPV6 |
@@ -939,8 +934,7 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, | |||
939 | ip_vs_fill_iph_skb(AF_INET6, skb, ipvsh); | 934 | ip_vs_fill_iph_skb(AF_INET6, skb, ipvsh); |
940 | 935 | ||
941 | *related = 1; | 936 | *related = 1; |
942 | 937 | ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph, ipvsh); | |
943 | ic = skb_header_pointer(skb, ipvsh->len, sizeof(_icmph), &_icmph); | ||
944 | if (ic == NULL) | 938 | if (ic == NULL) |
945 | return NF_DROP; | 939 | return NF_DROP; |
946 | 940 | ||
@@ -955,6 +949,11 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, | |||
955 | *related = 0; | 949 | *related = 0; |
956 | return NF_ACCEPT; | 950 | return NF_ACCEPT; |
957 | } | 951 | } |
952 | /* Fragment header that is before ICMP header tells us that: | ||
953 | * it's not an error message since they can't be fragmented. | ||
954 | */ | ||
955 | if (ipvsh->flags & IP6T_FH_F_FRAG) | ||
956 | return NF_DROP; | ||
958 | 957 | ||
959 | IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n", | 958 | IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n", |
960 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | 959 | ic->icmp6_type, ntohs(icmpv6_id(ic)), |
@@ -1117,6 +1116,12 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1117 | ip_vs_fill_iph_skb(af, skb, &iph); | 1116 | ip_vs_fill_iph_skb(af, skb, &iph); |
1118 | #ifdef CONFIG_IP_VS_IPV6 | 1117 | #ifdef CONFIG_IP_VS_IPV6 |
1119 | if (af == AF_INET6) { | 1118 | if (af == AF_INET6) { |
1119 | if (!iph.fragoffs && skb_nfct_reasm(skb)) { | ||
1120 | struct sk_buff *reasm = skb_nfct_reasm(skb); | ||
1121 | /* Save fw mark for coming frags */ | ||
1122 | reasm->ipvs_property = 1; | ||
1123 | reasm->mark = skb->mark; | ||
1124 | } | ||
1120 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { | 1125 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { |
1121 | int related; | 1126 | int related; |
1122 | int verdict = ip_vs_out_icmp_v6(skb, &related, | 1127 | int verdict = ip_vs_out_icmp_v6(skb, &related, |
@@ -1124,7 +1129,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1124 | 1129 | ||
1125 | if (related) | 1130 | if (related) |
1126 | return verdict; | 1131 | return verdict; |
1127 | ip_vs_fill_iph_skb(af, skb, &iph); | ||
1128 | } | 1132 | } |
1129 | } else | 1133 | } else |
1130 | #endif | 1134 | #endif |
@@ -1134,7 +1138,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1134 | 1138 | ||
1135 | if (related) | 1139 | if (related) |
1136 | return verdict; | 1140 | return verdict; |
1137 | ip_vs_fill_ip4hdr(skb_network_header(skb), &iph); | ||
1138 | } | 1141 | } |
1139 | 1142 | ||
1140 | pd = ip_vs_proto_data_get(net, iph.protocol); | 1143 | pd = ip_vs_proto_data_get(net, iph.protocol); |
@@ -1167,8 +1170,8 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1167 | pp->protocol == IPPROTO_SCTP)) { | 1170 | pp->protocol == IPPROTO_SCTP)) { |
1168 | __be16 _ports[2], *pptr; | 1171 | __be16 _ports[2], *pptr; |
1169 | 1172 | ||
1170 | pptr = skb_header_pointer(skb, iph.len, | 1173 | pptr = frag_safe_skb_hp(skb, iph.len, |
1171 | sizeof(_ports), _ports); | 1174 | sizeof(_ports), _ports, &iph); |
1172 | if (pptr == NULL) | 1175 | if (pptr == NULL) |
1173 | return NF_ACCEPT; /* Not for me */ | 1176 | return NF_ACCEPT; /* Not for me */ |
1174 | if (ip_vs_lookup_real_service(net, af, iph.protocol, | 1177 | if (ip_vs_lookup_real_service(net, af, iph.protocol, |
@@ -1468,7 +1471,7 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
1468 | 1471 | ||
1469 | *related = 1; | 1472 | *related = 1; |
1470 | 1473 | ||
1471 | ic = skb_header_pointer(skb, iph->len, sizeof(_icmph), &_icmph); | 1474 | ic = frag_safe_skb_hp(skb, iph->len, sizeof(_icmph), &_icmph, iph); |
1472 | if (ic == NULL) | 1475 | if (ic == NULL) |
1473 | return NF_DROP; | 1476 | return NF_DROP; |
1474 | 1477 | ||
@@ -1483,6 +1486,11 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
1483 | *related = 0; | 1486 | *related = 0; |
1484 | return NF_ACCEPT; | 1487 | return NF_ACCEPT; |
1485 | } | 1488 | } |
1489 | /* Fragment header that is before ICMP header tells us that: | ||
1490 | * it's not an error message since they can't be fragmented. | ||
1491 | */ | ||
1492 | if (iph->flags & IP6T_FH_F_FRAG) | ||
1493 | return NF_DROP; | ||
1486 | 1494 | ||
1487 | IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n", | 1495 | IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n", |
1488 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | 1496 | ic->icmp6_type, ntohs(icmpv6_id(ic)), |
@@ -1514,10 +1522,20 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
1514 | IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offs_ciph, | 1522 | IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offs_ciph, |
1515 | "Checking incoming ICMPv6 for"); | 1523 | "Checking incoming ICMPv6 for"); |
1516 | 1524 | ||
1517 | /* The embedded headers contain source and dest in reverse order */ | 1525 | /* The embedded headers contain source and dest in reverse order |
1518 | cp = pp->conn_in_get(AF_INET6, skb, &ciph, ciph.len, 1); | 1526 | * if not from localhost |
1527 | */ | ||
1528 | cp = pp->conn_in_get(AF_INET6, skb, &ciph, ciph.len, | ||
1529 | (hooknum == NF_INET_LOCAL_OUT) ? 0 : 1); | ||
1530 | |||
1519 | if (!cp) | 1531 | if (!cp) |
1520 | return NF_ACCEPT; | 1532 | return NF_ACCEPT; |
1533 | /* VS/TUN, VS/DR and LOCALNODE just let it go */ | ||
1534 | if ((hooknum == NF_INET_LOCAL_OUT) && | ||
1535 | (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { | ||
1536 | __ip_vs_conn_put(cp); | ||
1537 | return NF_ACCEPT; | ||
1538 | } | ||
1521 | 1539 | ||
1522 | /* do the statistics and put it back */ | 1540 | /* do the statistics and put it back */ |
1523 | ip_vs_in_stats(cp, skb); | 1541 | ip_vs_in_stats(cp, skb); |
@@ -1590,6 +1608,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1590 | 1608 | ||
1591 | #ifdef CONFIG_IP_VS_IPV6 | 1609 | #ifdef CONFIG_IP_VS_IPV6 |
1592 | if (af == AF_INET6) { | 1610 | if (af == AF_INET6) { |
1611 | if (!iph.fragoffs && skb_nfct_reasm(skb)) { | ||
1612 | struct sk_buff *reasm = skb_nfct_reasm(skb); | ||
1613 | /* Save fw mark for coming frags. */ | ||
1614 | reasm->ipvs_property = 1; | ||
1615 | reasm->mark = skb->mark; | ||
1616 | } | ||
1593 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { | 1617 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { |
1594 | int related; | 1618 | int related; |
1595 | int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum); | 1619 | int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum); |
@@ -1614,13 +1638,16 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1614 | pp = pd->pp; | 1638 | pp = pd->pp; |
1615 | /* | 1639 | /* |
1616 | * Check if the packet belongs to an existing connection entry | 1640 | * Check if the packet belongs to an existing connection entry |
1617 | * Only sched first IPv6 fragment. | ||
1618 | */ | 1641 | */ |
1619 | cp = pp->conn_in_get(af, skb, &iph, iph.len, 0); | 1642 | cp = pp->conn_in_get(af, skb, &iph, iph.len, 0); |
1620 | 1643 | ||
1621 | if (unlikely(!cp) && !iph.fragoffs) { | 1644 | if (unlikely(!cp) && !iph.fragoffs) { |
1645 | /* No (second) fragments need to enter here, as nf_defrag_ipv6 | ||
1646 | * replayed fragment zero will already have created the cp | ||
1647 | */ | ||
1622 | int v; | 1648 | int v; |
1623 | 1649 | ||
1650 | /* Schedule and create new connection entry into &cp */ | ||
1624 | if (!pp->conn_schedule(af, skb, pd, &v, &cp)) | 1651 | if (!pp->conn_schedule(af, skb, pd, &v, &cp)) |
1625 | return v; | 1652 | return v; |
1626 | } | 1653 | } |
@@ -1629,6 +1656,14 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) | |||
1629 | /* sorry, all this trouble for a no-hit :) */ | 1656 | /* sorry, all this trouble for a no-hit :) */ |
1630 | IP_VS_DBG_PKT(12, af, pp, skb, 0, | 1657 | IP_VS_DBG_PKT(12, af, pp, skb, 0, |
1631 | "ip_vs_in: packet continues traversal as normal"); | 1658 | "ip_vs_in: packet continues traversal as normal"); |
1659 | if (iph.fragoffs && !skb_nfct_reasm(skb)) { | ||
1660 | /* Fragment that couldn't be mapped to a conn entry | ||
1661 | * and don't have any pointer to a reasm skb | ||
1662 | * is missing module nf_defrag_ipv6 | ||
1663 | */ | ||
1664 | IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n"); | ||
1665 | IP_VS_DBG_PKT(7, af, pp, skb, 0, "unhandled fragment"); | ||
1666 | } | ||
1632 | return NF_ACCEPT; | 1667 | return NF_ACCEPT; |
1633 | } | 1668 | } |
1634 | 1669 | ||
@@ -1713,6 +1748,38 @@ ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb, | |||
1713 | #ifdef CONFIG_IP_VS_IPV6 | 1748 | #ifdef CONFIG_IP_VS_IPV6 |
1714 | 1749 | ||
1715 | /* | 1750 | /* |
1751 | * AF_INET6 fragment handling | ||
1752 | * Copy info from first fragment, to the rest of them. | ||
1753 | */ | ||
1754 | static unsigned int | ||
1755 | ip_vs_preroute_frag6(unsigned int hooknum, struct sk_buff *skb, | ||
1756 | const struct net_device *in, | ||
1757 | const struct net_device *out, | ||
1758 | int (*okfn)(struct sk_buff *)) | ||
1759 | { | ||
1760 | struct sk_buff *reasm = skb_nfct_reasm(skb); | ||
1761 | struct net *net; | ||
1762 | |||
1763 | /* Skip if not a "replay" from nf_ct_frag6_output or first fragment. | ||
1764 | * ipvs_property is set when checking first fragment | ||
1765 | * in ip_vs_in() and ip_vs_out(). | ||
1766 | */ | ||
1767 | if (reasm) | ||
1768 | IP_VS_DBG(2, "Fragment recv prop:%d\n", reasm->ipvs_property); | ||
1769 | if (!reasm || !reasm->ipvs_property) | ||
1770 | return NF_ACCEPT; | ||
1771 | |||
1772 | net = skb_net(skb); | ||
1773 | if (!net_ipvs(net)->enable) | ||
1774 | return NF_ACCEPT; | ||
1775 | |||
1776 | /* Copy stored fw mark, saved in ip_vs_{in,out} */ | ||
1777 | skb->mark = reasm->mark; | ||
1778 | |||
1779 | return NF_ACCEPT; | ||
1780 | } | ||
1781 | |||
1782 | /* | ||
1716 | * AF_INET6 handler in NF_INET_LOCAL_IN chain | 1783 | * AF_INET6 handler in NF_INET_LOCAL_IN chain |
1717 | * Schedule and forward packets from remote clients | 1784 | * Schedule and forward packets from remote clients |
1718 | */ | 1785 | */ |
@@ -1851,6 +1918,14 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = { | |||
1851 | .priority = 100, | 1918 | .priority = 100, |
1852 | }, | 1919 | }, |
1853 | #ifdef CONFIG_IP_VS_IPV6 | 1920 | #ifdef CONFIG_IP_VS_IPV6 |
1921 | /* After mangle & nat fetch 2:nd fragment and following */ | ||
1922 | { | ||
1923 | .hook = ip_vs_preroute_frag6, | ||
1924 | .owner = THIS_MODULE, | ||
1925 | .pf = NFPROTO_IPV6, | ||
1926 | .hooknum = NF_INET_PRE_ROUTING, | ||
1927 | .priority = NF_IP6_PRI_NAT_DST + 1, | ||
1928 | }, | ||
1854 | /* After packet filtering, change source only for VS/NAT */ | 1929 | /* After packet filtering, change source only for VS/NAT */ |
1855 | { | 1930 | { |
1856 | .hook = ip_vs_reply6, | 1931 | .hook = ip_vs_reply6, |
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 428de757957..a8b75fc8e6a 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c | |||
@@ -496,13 +496,15 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
496 | struct ip_vs_protocol *pp) | 496 | struct ip_vs_protocol *pp) |
497 | { | 497 | { |
498 | struct rt6_info *rt; /* Route to the other host */ | 498 | struct rt6_info *rt; /* Route to the other host */ |
499 | struct ipv6hdr *iph = ipv6_hdr(skb); | 499 | struct ip_vs_iphdr iph; |
500 | int mtu; | 500 | int mtu; |
501 | 501 | ||
502 | EnterFunction(10); | 502 | EnterFunction(10); |
503 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
503 | 504 | ||
504 | if (!(rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr, NULL, 0, | 505 | rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph.daddr.in6, NULL, 0, |
505 | IP_VS_RT_MODE_NON_LOCAL))) | 506 | IP_VS_RT_MODE_NON_LOCAL); |
507 | if (!rt) | ||
506 | goto tx_error_icmp; | 508 | goto tx_error_icmp; |
507 | 509 | ||
508 | /* MTU checking */ | 510 | /* MTU checking */ |
@@ -513,7 +515,9 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
513 | 515 | ||
514 | skb->dev = net->loopback_dev; | 516 | skb->dev = net->loopback_dev; |
515 | } | 517 | } |
516 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | 518 | /* only send ICMP too big on first fragment */ |
519 | if (!iph.fragoffs) | ||
520 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | ||
517 | dst_release(&rt->dst); | 521 | dst_release(&rt->dst); |
518 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); | 522 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); |
519 | goto tx_error; | 523 | goto tx_error; |
@@ -685,7 +689,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
685 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | 689 | ip_vs_fill_iph_skb(cp->af, skb, &iph); |
686 | 690 | ||
687 | /* check if it is a connection of no-client-port */ | 691 | /* check if it is a connection of no-client-port */ |
688 | if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) { | 692 | if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT && !iph.fragoffs)) { |
689 | __be16 _pt, *p; | 693 | __be16 _pt, *p; |
690 | p = skb_header_pointer(skb, iph.len, sizeof(_pt), &_pt); | 694 | p = skb_header_pointer(skb, iph.len, sizeof(_pt), &_pt); |
691 | if (p == NULL) | 695 | if (p == NULL) |
@@ -735,7 +739,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
735 | 739 | ||
736 | skb->dev = net->loopback_dev; | 740 | skb->dev = net->loopback_dev; |
737 | } | 741 | } |
738 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | 742 | /* only send ICMP too big on first fragment */ |
743 | if (!iph.fragoffs) | ||
744 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | ||
739 | IP_VS_DBG_RL_PKT(0, AF_INET6, pp, skb, 0, | 745 | IP_VS_DBG_RL_PKT(0, AF_INET6, pp, skb, 0, |
740 | "ip_vs_nat_xmit_v6(): frag needed for"); | 746 | "ip_vs_nat_xmit_v6(): frag needed for"); |
741 | goto tx_error_put; | 747 | goto tx_error_put; |
@@ -940,8 +946,10 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
940 | unsigned int max_headroom; /* The extra header space needed */ | 946 | unsigned int max_headroom; /* The extra header space needed */ |
941 | int mtu; | 947 | int mtu; |
942 | int ret; | 948 | int ret; |
949 | struct ip_vs_iphdr ipvsh; | ||
943 | 950 | ||
944 | EnterFunction(10); | 951 | EnterFunction(10); |
952 | ip_vs_fill_iph_skb(cp->af, skb, &ipvsh); | ||
945 | 953 | ||
946 | if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, | 954 | if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, |
947 | &saddr, 1, (IP_VS_RT_MODE_LOCAL | | 955 | &saddr, 1, (IP_VS_RT_MODE_LOCAL | |
@@ -970,7 +978,9 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
970 | 978 | ||
971 | skb->dev = net->loopback_dev; | 979 | skb->dev = net->loopback_dev; |
972 | } | 980 | } |
973 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | 981 | /* only send ICMP too big on first fragment */ |
982 | if (!ipvsh.fragoffs) | ||
983 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | ||
974 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); | 984 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); |
975 | goto tx_error_put; | 985 | goto tx_error_put; |
976 | } | 986 | } |
@@ -1116,8 +1126,10 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
1116 | { | 1126 | { |
1117 | struct rt6_info *rt; /* Route to the other host */ | 1127 | struct rt6_info *rt; /* Route to the other host */ |
1118 | int mtu; | 1128 | int mtu; |
1129 | struct ip_vs_iphdr iph; | ||
1119 | 1130 | ||
1120 | EnterFunction(10); | 1131 | EnterFunction(10); |
1132 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
1121 | 1133 | ||
1122 | if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL, | 1134 | if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL, |
1123 | 0, (IP_VS_RT_MODE_LOCAL | | 1135 | 0, (IP_VS_RT_MODE_LOCAL | |
@@ -1136,7 +1148,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
1136 | 1148 | ||
1137 | skb->dev = net->loopback_dev; | 1149 | skb->dev = net->loopback_dev; |
1138 | } | 1150 | } |
1139 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | 1151 | /* only send ICMP too big on first fragment */ |
1152 | if (!iph.fragoffs) | ||
1153 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | ||
1140 | dst_release(&rt->dst); | 1154 | dst_release(&rt->dst); |
1141 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); | 1155 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); |
1142 | goto tx_error; | 1156 | goto tx_error; |
@@ -1308,8 +1322,10 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
1308 | int rc; | 1322 | int rc; |
1309 | int local; | 1323 | int local; |
1310 | int rt_mode; | 1324 | int rt_mode; |
1325 | struct ip_vs_iphdr iph; | ||
1311 | 1326 | ||
1312 | EnterFunction(10); | 1327 | EnterFunction(10); |
1328 | ip_vs_fill_iph_skb(cp->af, skb, &iph); | ||
1313 | 1329 | ||
1314 | /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be | 1330 | /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be |
1315 | forwarded directly here, because there is no need to | 1331 | forwarded directly here, because there is no need to |
@@ -1372,7 +1388,9 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
1372 | 1388 | ||
1373 | skb->dev = net->loopback_dev; | 1389 | skb->dev = net->loopback_dev; |
1374 | } | 1390 | } |
1375 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | 1391 | /* only send ICMP too big on first fragment */ |
1392 | if (!iph.fragoffs) | ||
1393 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | ||
1376 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); | 1394 | IP_VS_DBG_RL("%s(): frag needed\n", __func__); |
1377 | goto tx_error_put; | 1395 | goto tx_error_put; |
1378 | } | 1396 | } |