diff options
-rw-r--r-- | net/ipv4/ipvs/ip_vs_core.c | 91 |
1 files changed, 41 insertions, 50 deletions
diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c index 26e3d99bbee..05797a5ce15 100644 --- a/net/ipv4/ipvs/ip_vs_core.c +++ b/net/ipv4/ipvs/ip_vs_core.c @@ -654,8 +654,9 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, /* Handle relevant response ICMP messages - forward to the right * destination host. Used for NAT and local client. */ -static int handle_response_icmp(struct sk_buff *skb, struct iphdr *iph, - struct iphdr *cih, struct ip_vs_conn *cp, +static int handle_response_icmp(int af, struct sk_buff *skb, + union nf_inet_addr *snet, + __u8 protocol, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int offset, unsigned int ihl) { @@ -669,18 +670,22 @@ static int handle_response_icmp(struct sk_buff *skb, struct iphdr *iph, /* Ensure the checksum is correct */ if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { /* Failed checksum! */ - IP_VS_DBG(1, - "Forward ICMP: failed checksum from %d.%d.%d.%d!\n", - NIPQUAD(iph->saddr)); + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); goto out; } - if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol) + if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol) offset += 2 * sizeof(__u16); if (!skb_make_writable(skb, offset)) goto out; - ip_vs_nat_icmp(skb, pp, cp, 1); +#ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) + ip_vs_nat_icmp_v6(skb, pp, cp, 1); + else +#endif + ip_vs_nat_icmp(skb, pp, cp, 1); /* do the statistics and put it back */ ip_vs_out_stats(cp, skb); @@ -708,6 +713,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related) struct ip_vs_conn *cp; struct ip_vs_protocol *pp; unsigned int offset, ihl; + union nf_inet_addr snet; *related = 1; @@ -766,7 +772,9 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related) if (!cp) return NF_ACCEPT; - return handle_response_icmp(skb, iph, cih, cp, pp, offset, ihl); + snet.ip = iph->saddr; + return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, + pp, offset, ihl); } #ifdef CONFIG_IP_VS_IPV6 @@ -779,7 +787,8 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related) struct ip_vs_iphdr ciph; struct ip_vs_conn *cp; struct ip_vs_protocol *pp; - unsigned int offset, verdict; + unsigned int offset; + union nf_inet_addr snet; *related = 1; @@ -838,40 +847,9 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related) if (!cp) return NF_ACCEPT; - verdict = NF_DROP; - - if (IP_VS_FWD_METHOD(cp) != 0) { - IP_VS_ERR("shouldn't reach here, because the box is on the " - "half connection in the tun/dr module.\n"); - } - - /* Ensure the checksum is correct */ - if (!skb_csum_unnecessary(skb) - && ip_vs_checksum_complete(skb, sizeof(struct ipv6hdr))) { - /* Failed checksum! */ - IP_VS_DBG(1, "Forward ICMPv6: failed checksum from " - NIP6_FMT "!\n", - NIP6(iph->saddr)); - goto out; - } - - if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr) - offset += 2 * sizeof(__u16); - if (!skb_make_writable(skb, offset)) - goto out; - - ip_vs_nat_icmp_v6(skb, pp, cp, 1); - - /* do the statistics and put it back */ - ip_vs_out_stats(cp, skb); - - skb->ipvs_property = 1; - verdict = NF_ACCEPT; - -out: - __ip_vs_conn_put(cp); - - return verdict; + snet.in6 = iph->saddr; + return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp, + pp, offset, sizeof(struct ipv6hdr)); } #endif @@ -1055,7 +1033,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); return NF_DROP; - } + } } } IP_VS_DBG_PKT(12, pp, skb, 0, @@ -1083,6 +1061,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) struct ip_vs_conn *cp; struct ip_vs_protocol *pp; unsigned int offset, ihl, verdict; + union nf_inet_addr snet; *related = 1; @@ -1142,9 +1121,12 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) if (!cp) { /* The packet could also belong to a local client */ cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1); - if (cp) - return handle_response_icmp(skb, iph, cih, cp, pp, + if (cp) { + snet.ip = iph->saddr; + return handle_response_icmp(AF_INET, skb, &snet, + cih->protocol, cp, pp, offset, ihl); + } return NF_ACCEPT; } @@ -1183,6 +1165,7 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) struct ip_vs_conn *cp; struct ip_vs_protocol *pp; unsigned int offset, verdict; + union nf_inet_addr snet; *related = 1; @@ -1240,8 +1223,18 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) ip_vs_fill_iphdr(AF_INET6, cih, &ciph); /* The embedded headers contain source and dest in reverse order */ cp = pp->conn_in_get(AF_INET6, skb, pp, &ciph, offset, 1); - if (!cp) + if (!cp) { + /* The packet could also belong to a local client */ + cp = pp->conn_out_get(AF_INET6, skb, pp, &ciph, offset, 1); + if (cp) { + snet.in6 = iph->saddr; + return handle_response_icmp(AF_INET6, skb, &snet, + cih->nexthdr, + cp, pp, offset, + sizeof(struct ipv6hdr)); + } return NF_ACCEPT; + } verdict = NF_DROP; @@ -1281,9 +1274,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, * Big tappo: only PACKET_HOST, including loopback for local client * Don't handle local packets on IPv6 for now */ - if (unlikely(skb->pkt_type != PACKET_HOST || - (af == AF_INET6 || (skb->dev->flags & IFF_LOOPBACK || - skb->sk)))) { + if (unlikely(skb->pkt_type != PACKET_HOST)) { IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n", skb->pkt_type, iph.protocol, |