Commit 8b27b10f authored by Julian Anastasov's avatar Julian Anastasov Committed by Simon Horman

ipvs: optimize checksums for apps

 	Avoid full checksum calculation for apps that can provide
info whether csum was broken after payload mangling. For now only
ip_vs_ftp mangles payload and it updates the csum, so the full
recalculation is avoided for all packets.

 	Add CHECKSUM_UNNECESSARY for snat_handler (TCP and UDP).
It is needed to support SNAT from local address for the case
when csum is fully recalculated.
Signed-off-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarSimon Horman <horms@verge.net.au>
parent 5bc9068e
...@@ -597,11 +597,19 @@ struct ip_vs_app { ...@@ -597,11 +597,19 @@ struct ip_vs_app {
__be16 port; /* port number in net order */ __be16 port; /* port number in net order */
atomic_t usecnt; /* usage counter */ atomic_t usecnt; /* usage counter */
/* output hook: return false if can't linearize. diff set for TCP. */ /*
* output hook: Process packet in inout direction, diff set for TCP.
* Return: 0=Error, 1=Payload Not Mangled/Mangled but checksum is ok,
* 2=Mangled but checksum was not updated
*/
int (*pkt_out)(struct ip_vs_app *, struct ip_vs_conn *, int (*pkt_out)(struct ip_vs_app *, struct ip_vs_conn *,
struct sk_buff *, int *diff); struct sk_buff *, int *diff);
/* input hook: return false if can't linearize. diff set for TCP. */ /*
* input hook: Process packet in outin direction, diff set for TCP.
* Return: 0=Error, 1=Payload Not Mangled/Mangled but checksum is ok,
* 2=Mangled but checksum was not updated
*/
int (*pkt_in)(struct ip_vs_app *, struct ip_vs_conn *, int (*pkt_in)(struct ip_vs_app *, struct ip_vs_conn *,
struct sk_buff *, int *diff); struct sk_buff *, int *diff);
......
...@@ -242,9 +242,14 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, ...@@ -242,9 +242,14 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
start-data, end-start, start-data, end-start,
buf, buf_len); buf, buf_len);
if (ret) if (ret) {
ip_vs_nfct_expect_related(skb, ct, n_cp, ip_vs_nfct_expect_related(skb, ct, n_cp,
IPPROTO_TCP, 0, 0); IPPROTO_TCP, 0, 0);
if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->ip_summed = CHECKSUM_UNNECESSARY;
/* csum is updated */
ret = 1;
}
} }
/* /*
......
...@@ -120,6 +120,7 @@ tcp_snat_handler(struct sk_buff *skb, ...@@ -120,6 +120,7 @@ tcp_snat_handler(struct sk_buff *skb,
struct tcphdr *tcph; struct tcphdr *tcph;
unsigned int tcphoff; unsigned int tcphoff;
int oldlen; int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6)
...@@ -134,13 +135,20 @@ tcp_snat_handler(struct sk_buff *skb, ...@@ -134,13 +135,20 @@ tcp_snat_handler(struct sk_buff *skb,
return 0; return 0;
if (unlikely(cp->app != NULL)) { if (unlikely(cp->app != NULL)) {
int ret;
/* Some checks before mangling */ /* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
return 0; return 0;
/* Call application helper if needed */ /* Call application helper if needed */
if (!ip_vs_app_pkt_out(cp, skb)) if (!(ret = ip_vs_app_pkt_out(cp, skb)))
return 0; return 0;
/* ret=2: csum update is needed after payload mangling */
if (ret == 1)
oldlen = skb->len - tcphoff;
else
payload_csum = 1;
} }
tcph = (void *)skb_network_header(skb) + tcphoff; tcph = (void *)skb_network_header(skb) + tcphoff;
...@@ -151,12 +159,13 @@ tcp_snat_handler(struct sk_buff *skb, ...@@ -151,12 +159,13 @@ tcp_snat_handler(struct sk_buff *skb,
tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
htons(oldlen), htons(oldlen),
htons(skb->len - tcphoff)); htons(skb->len - tcphoff));
} else if (!cp->app) { } else if (!payload_csum) {
/* Only port and addr are changed, do fast csum update */ /* Only port and addr are changed, do fast csum update */
tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
cp->dport, cp->vport); cp->dport, cp->vport);
if (skb->ip_summed == CHECKSUM_COMPLETE) if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = (cp->app && pp->csum_check) ?
CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
} else { } else {
/* full checksum calculation */ /* full checksum calculation */
tcph->check = 0; tcph->check = 0;
...@@ -174,6 +183,7 @@ tcp_snat_handler(struct sk_buff *skb, ...@@ -174,6 +183,7 @@ tcp_snat_handler(struct sk_buff *skb,
skb->len - tcphoff, skb->len - tcphoff,
cp->protocol, cp->protocol,
skb->csum); skb->csum);
skb->ip_summed = CHECKSUM_UNNECESSARY;
IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
pp->name, tcph->check, pp->name, tcph->check,
...@@ -190,6 +200,7 @@ tcp_dnat_handler(struct sk_buff *skb, ...@@ -190,6 +200,7 @@ tcp_dnat_handler(struct sk_buff *skb,
struct tcphdr *tcph; struct tcphdr *tcph;
unsigned int tcphoff; unsigned int tcphoff;
int oldlen; int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6)
...@@ -204,6 +215,8 @@ tcp_dnat_handler(struct sk_buff *skb, ...@@ -204,6 +215,8 @@ tcp_dnat_handler(struct sk_buff *skb,
return 0; return 0;
if (unlikely(cp->app != NULL)) { if (unlikely(cp->app != NULL)) {
int ret;
/* Some checks before mangling */ /* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
return 0; return 0;
...@@ -212,8 +225,13 @@ tcp_dnat_handler(struct sk_buff *skb, ...@@ -212,8 +225,13 @@ tcp_dnat_handler(struct sk_buff *skb,
* Attempt ip_vs_app call. * Attempt ip_vs_app call.
* It will fix ip_vs_conn and iph ack_seq stuff * It will fix ip_vs_conn and iph ack_seq stuff
*/ */
if (!ip_vs_app_pkt_in(cp, skb)) if (!(ret = ip_vs_app_pkt_in(cp, skb)))
return 0; return 0;
/* ret=2: csum update is needed after payload mangling */
if (ret == 1)
oldlen = skb->len - tcphoff;
else
payload_csum = 1;
} }
tcph = (void *)skb_network_header(skb) + tcphoff; tcph = (void *)skb_network_header(skb) + tcphoff;
...@@ -226,12 +244,13 @@ tcp_dnat_handler(struct sk_buff *skb, ...@@ -226,12 +244,13 @@ tcp_dnat_handler(struct sk_buff *skb,
tcp_partial_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, tcp_partial_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr,
htons(oldlen), htons(oldlen),
htons(skb->len - tcphoff)); htons(skb->len - tcphoff));
} else if (!cp->app) { } else if (!payload_csum) {
/* Only port and addr are changed, do fast csum update */ /* Only port and addr are changed, do fast csum update */
tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr,
cp->vport, cp->dport); cp->vport, cp->dport);
if (skb->ip_summed == CHECKSUM_COMPLETE) if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = (cp->app && pp->csum_check) ?
CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
} else { } else {
/* full checksum calculation */ /* full checksum calculation */
tcph->check = 0; tcph->check = 0;
......
...@@ -121,6 +121,7 @@ udp_snat_handler(struct sk_buff *skb, ...@@ -121,6 +121,7 @@ udp_snat_handler(struct sk_buff *skb,
struct udphdr *udph; struct udphdr *udph;
unsigned int udphoff; unsigned int udphoff;
int oldlen; int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6)
...@@ -135,6 +136,8 @@ udp_snat_handler(struct sk_buff *skb, ...@@ -135,6 +136,8 @@ udp_snat_handler(struct sk_buff *skb,
return 0; return 0;
if (unlikely(cp->app != NULL)) { if (unlikely(cp->app != NULL)) {
int ret;
/* Some checks before mangling */ /* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
return 0; return 0;
...@@ -142,8 +145,13 @@ udp_snat_handler(struct sk_buff *skb, ...@@ -142,8 +145,13 @@ udp_snat_handler(struct sk_buff *skb,
/* /*
* Call application helper if needed * Call application helper if needed
*/ */
if (!ip_vs_app_pkt_out(cp, skb)) if (!(ret = ip_vs_app_pkt_out(cp, skb)))
return 0; return 0;
/* ret=2: csum update is needed after payload mangling */
if (ret == 1)
oldlen = skb->len - udphoff;
else
payload_csum = 1;
} }
udph = (void *)skb_network_header(skb) + udphoff; udph = (void *)skb_network_header(skb) + udphoff;
...@@ -156,12 +164,13 @@ udp_snat_handler(struct sk_buff *skb, ...@@ -156,12 +164,13 @@ udp_snat_handler(struct sk_buff *skb,
udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
htons(oldlen), htons(oldlen),
htons(skb->len - udphoff)); htons(skb->len - udphoff));
} else if (!cp->app && (udph->check != 0)) { } else if (!payload_csum && (udph->check != 0)) {
/* Only port and addr are changed, do fast csum update */ /* Only port and addr are changed, do fast csum update */
udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
cp->dport, cp->vport); cp->dport, cp->vport);
if (skb->ip_summed == CHECKSUM_COMPLETE) if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = (cp->app && pp->csum_check) ?
CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
} else { } else {
/* full checksum calculation */ /* full checksum calculation */
udph->check = 0; udph->check = 0;
...@@ -181,6 +190,7 @@ udp_snat_handler(struct sk_buff *skb, ...@@ -181,6 +190,7 @@ udp_snat_handler(struct sk_buff *skb,
skb->csum); skb->csum);
if (udph->check == 0) if (udph->check == 0)
udph->check = CSUM_MANGLED_0; udph->check = CSUM_MANGLED_0;
skb->ip_summed = CHECKSUM_UNNECESSARY;
IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
pp->name, udph->check, pp->name, udph->check,
(char*)&(udph->check) - (char*)udph); (char*)&(udph->check) - (char*)udph);
...@@ -196,6 +206,7 @@ udp_dnat_handler(struct sk_buff *skb, ...@@ -196,6 +206,7 @@ udp_dnat_handler(struct sk_buff *skb,
struct udphdr *udph; struct udphdr *udph;
unsigned int udphoff; unsigned int udphoff;
int oldlen; int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6)
...@@ -210,6 +221,8 @@ udp_dnat_handler(struct sk_buff *skb, ...@@ -210,6 +221,8 @@ udp_dnat_handler(struct sk_buff *skb,
return 0; return 0;
if (unlikely(cp->app != NULL)) { if (unlikely(cp->app != NULL)) {
int ret;
/* Some checks before mangling */ /* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
return 0; return 0;
...@@ -218,8 +231,13 @@ udp_dnat_handler(struct sk_buff *skb, ...@@ -218,8 +231,13 @@ udp_dnat_handler(struct sk_buff *skb,
* Attempt ip_vs_app call. * Attempt ip_vs_app call.
* It will fix ip_vs_conn * It will fix ip_vs_conn
*/ */
if (!ip_vs_app_pkt_in(cp, skb)) if (!(ret = ip_vs_app_pkt_in(cp, skb)))
return 0; return 0;
/* ret=2: csum update is needed after payload mangling */
if (ret == 1)
oldlen = skb->len - udphoff;
else
payload_csum = 1;
} }
udph = (void *)skb_network_header(skb) + udphoff; udph = (void *)skb_network_header(skb) + udphoff;
...@@ -232,12 +250,13 @@ udp_dnat_handler(struct sk_buff *skb, ...@@ -232,12 +250,13 @@ udp_dnat_handler(struct sk_buff *skb,
udp_partial_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, udp_partial_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr,
htons(oldlen), htons(oldlen),
htons(skb->len - udphoff)); htons(skb->len - udphoff));
} else if (!cp->app && (udph->check != 0)) { } else if (!payload_csum && (udph->check != 0)) {
/* Only port and addr are changed, do fast csum update */ /* Only port and addr are changed, do fast csum update */
udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr,
cp->vport, cp->dport); cp->vport, cp->dport);
if (skb->ip_summed == CHECKSUM_COMPLETE) if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = (cp->app && pp->csum_check) ?
CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
} else { } else {
/* full checksum calculation */ /* full checksum calculation */
udph->check = 0; udph->check = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment