diff options
| author | Julian Anastasov <ja@ssi.bg> | 2010-10-17 09:17:20 -0400 |
|---|---|---|
| committer | Simon Horman <horms@verge.net.au> | 2010-10-21 04:50:02 -0400 |
| commit | 8b27b10f5863a5b63e46304a71aa01463d1efac4 (patch) | |
| tree | 89f5db8881b0297cda00cad3c2cdedf685e2757f | |
| parent | 5bc9068e9d962ca6b8bec3f0eb6f60ab4dee1d04 (diff) | |
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: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
| -rw-r--r-- | include/net/ip_vs.h | 12 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_ftp.c | 7 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_proto_tcp.c | 31 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_proto_udp.c | 31 |
4 files changed, 66 insertions, 15 deletions
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 6e8a6192e574..adcdba9dd183 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h | |||
| @@ -597,11 +597,19 @@ struct ip_vs_app { | |||
| 597 | __be16 port; /* port number in net order */ | 597 | __be16 port; /* port number in net order */ |
| 598 | atomic_t usecnt; /* usage counter */ | 598 | atomic_t usecnt; /* usage counter */ |
| 599 | 599 | ||
| 600 | /* output hook: return false if can't linearize. diff set for TCP. */ | 600 | /* |
| 601 | * output hook: Process packet in inout direction, diff set for TCP. | ||
| 602 | * Return: 0=Error, 1=Payload Not Mangled/Mangled but checksum is ok, | ||
| 603 | * 2=Mangled but checksum was not updated | ||
| 604 | */ | ||
| 601 | int (*pkt_out)(struct ip_vs_app *, struct ip_vs_conn *, | 605 | int (*pkt_out)(struct ip_vs_app *, struct ip_vs_conn *, |
| 602 | struct sk_buff *, int *diff); | 606 | struct sk_buff *, int *diff); |
| 603 | 607 | ||
| 604 | /* input hook: return false if can't linearize. diff set for TCP. */ | 608 | /* |
| 609 | * input hook: Process packet in outin direction, diff set for TCP. | ||
| 610 | * Return: 0=Error, 1=Payload Not Mangled/Mangled but checksum is ok, | ||
| 611 | * 2=Mangled but checksum was not updated | ||
| 612 | */ | ||
| 605 | int (*pkt_in)(struct ip_vs_app *, struct ip_vs_conn *, | 613 | int (*pkt_in)(struct ip_vs_app *, struct ip_vs_conn *, |
| 606 | struct sk_buff *, int *diff); | 614 | struct sk_buff *, int *diff); |
| 607 | 615 | ||
diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 090889a3b3af..75455000ad1c 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c | |||
| @@ -242,9 +242,14 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, | |||
| 242 | ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, | 242 | ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, |
| 243 | start-data, end-start, | 243 | start-data, end-start, |
| 244 | buf, buf_len); | 244 | buf, buf_len); |
| 245 | if (ret) | 245 | if (ret) { |
| 246 | ip_vs_nfct_expect_related(skb, ct, n_cp, | 246 | ip_vs_nfct_expect_related(skb, ct, n_cp, |
| 247 | IPPROTO_TCP, 0, 0); | 247 | IPPROTO_TCP, 0, 0); |
| 248 | if (skb->ip_summed == CHECKSUM_COMPLETE) | ||
| 249 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 250 | /* csum is updated */ | ||
| 251 | ret = 1; | ||
| 252 | } | ||
| 248 | } | 253 | } |
| 249 | 254 | ||
| 250 | /* | 255 | /* |
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 318d011036db..64dc2954cf78 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c | |||
| @@ -120,6 +120,7 @@ tcp_snat_handler(struct sk_buff *skb, | |||
| 120 | struct tcphdr *tcph; | 120 | struct tcphdr *tcph; |
| 121 | unsigned int tcphoff; | 121 | unsigned int tcphoff; |
| 122 | int oldlen; | 122 | int oldlen; |
| 123 | int payload_csum = 0; | ||
| 123 | 124 | ||
| 124 | #ifdef CONFIG_IP_VS_IPV6 | 125 | #ifdef CONFIG_IP_VS_IPV6 |
| 125 | if (cp->af == AF_INET6) | 126 | if (cp->af == AF_INET6) |
| @@ -134,13 +135,20 @@ tcp_snat_handler(struct sk_buff *skb, | |||
| 134 | return 0; | 135 | return 0; |
| 135 | 136 | ||
| 136 | if (unlikely(cp->app != NULL)) { | 137 | if (unlikely(cp->app != NULL)) { |
| 138 | int ret; | ||
| 139 | |||
| 137 | /* Some checks before mangling */ | 140 | /* Some checks before mangling */ |
| 138 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) | 141 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
| 139 | return 0; | 142 | return 0; |
| 140 | 143 | ||
| 141 | /* Call application helper if needed */ | 144 | /* Call application helper if needed */ |
| 142 | if (!ip_vs_app_pkt_out(cp, skb)) | 145 | if (!(ret = ip_vs_app_pkt_out(cp, skb))) |
| 143 | return 0; | 146 | return 0; |
| 147 | /* ret=2: csum update is needed after payload mangling */ | ||
| 148 | if (ret == 1) | ||
| 149 | oldlen = skb->len - tcphoff; | ||
| 150 | else | ||
| 151 | payload_csum = 1; | ||
| 144 | } | 152 | } |
| 145 | 153 | ||
| 146 | tcph = (void *)skb_network_header(skb) + tcphoff; | 154 | tcph = (void *)skb_network_header(skb) + tcphoff; |
| @@ -151,12 +159,13 @@ tcp_snat_handler(struct sk_buff *skb, | |||
| 151 | tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, | 159 | tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, |
| 152 | htons(oldlen), | 160 | htons(oldlen), |
| 153 | htons(skb->len - tcphoff)); | 161 | htons(skb->len - tcphoff)); |
| 154 | } else if (!cp->app) { | 162 | } else if (!payload_csum) { |
| 155 | /* Only port and addr are changed, do fast csum update */ | 163 | /* Only port and addr are changed, do fast csum update */ |
| 156 | tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, | 164 | tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, |
| 157 | cp->dport, cp->vport); | 165 | cp->dport, cp->vport); |
| 158 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 166 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
| 159 | skb->ip_summed = CHECKSUM_NONE; | 167 | skb->ip_summed = (cp->app && pp->csum_check) ? |
| 168 | CHECKSUM_UNNECESSARY : CHECKSUM_NONE; | ||
| 160 | } else { | 169 | } else { |
| 161 | /* full checksum calculation */ | 170 | /* full checksum calculation */ |
| 162 | tcph->check = 0; | 171 | tcph->check = 0; |
| @@ -174,6 +183,7 @@ tcp_snat_handler(struct sk_buff *skb, | |||
| 174 | skb->len - tcphoff, | 183 | skb->len - tcphoff, |
| 175 | cp->protocol, | 184 | cp->protocol, |
| 176 | skb->csum); | 185 | skb->csum); |
| 186 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 177 | 187 | ||
| 178 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", | 188 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", |
| 179 | pp->name, tcph->check, | 189 | pp->name, tcph->check, |
| @@ -190,6 +200,7 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
| 190 | struct tcphdr *tcph; | 200 | struct tcphdr *tcph; |
| 191 | unsigned int tcphoff; | 201 | unsigned int tcphoff; |
| 192 | int oldlen; | 202 | int oldlen; |
| 203 | int payload_csum = 0; | ||
| 193 | 204 | ||
| 194 | #ifdef CONFIG_IP_VS_IPV6 | 205 | #ifdef CONFIG_IP_VS_IPV6 |
| 195 | if (cp->af == AF_INET6) | 206 | if (cp->af == AF_INET6) |
| @@ -204,6 +215,8 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
| 204 | return 0; | 215 | return 0; |
| 205 | 216 | ||
| 206 | if (unlikely(cp->app != NULL)) { | 217 | if (unlikely(cp->app != NULL)) { |
| 218 | int ret; | ||
| 219 | |||
| 207 | /* Some checks before mangling */ | 220 | /* Some checks before mangling */ |
| 208 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) | 221 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
| 209 | return 0; | 222 | return 0; |
| @@ -212,8 +225,13 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
| 212 | * Attempt ip_vs_app call. | 225 | * Attempt ip_vs_app call. |
| 213 | * It will fix ip_vs_conn and iph ack_seq stuff | 226 | * It will fix ip_vs_conn and iph ack_seq stuff |
| 214 | */ | 227 | */ |
| 215 | if (!ip_vs_app_pkt_in(cp, skb)) | 228 | if (!(ret = ip_vs_app_pkt_in(cp, skb))) |
| 216 | return 0; | 229 | return 0; |
| 230 | /* ret=2: csum update is needed after payload mangling */ | ||
| 231 | if (ret == 1) | ||
| 232 | oldlen = skb->len - tcphoff; | ||
| 233 | else | ||
| 234 | payload_csum = 1; | ||
| 217 | } | 235 | } |
| 218 | 236 | ||
| 219 | tcph = (void *)skb_network_header(skb) + tcphoff; | 237 | tcph = (void *)skb_network_header(skb) + tcphoff; |
| @@ -226,12 +244,13 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
| 226 | tcp_partial_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, | 244 | tcp_partial_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, |
| 227 | htons(oldlen), | 245 | htons(oldlen), |
| 228 | htons(skb->len - tcphoff)); | 246 | htons(skb->len - tcphoff)); |
| 229 | } else if (!cp->app) { | 247 | } else if (!payload_csum) { |
| 230 | /* Only port and addr are changed, do fast csum update */ | 248 | /* Only port and addr are changed, do fast csum update */ |
| 231 | tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, | 249 | tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, |
| 232 | cp->vport, cp->dport); | 250 | cp->vport, cp->dport); |
| 233 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 251 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
| 234 | skb->ip_summed = CHECKSUM_NONE; | 252 | skb->ip_summed = (cp->app && pp->csum_check) ? |
| 253 | CHECKSUM_UNNECESSARY : CHECKSUM_NONE; | ||
| 235 | } else { | 254 | } else { |
| 236 | /* full checksum calculation */ | 255 | /* full checksum calculation */ |
| 237 | tcph->check = 0; | 256 | tcph->check = 0; |
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index f9290893bd93..9c558c40bfbb 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c | |||
| @@ -121,6 +121,7 @@ udp_snat_handler(struct sk_buff *skb, | |||
| 121 | struct udphdr *udph; | 121 | struct udphdr *udph; |
| 122 | unsigned int udphoff; | 122 | unsigned int udphoff; |
| 123 | int oldlen; | 123 | int oldlen; |
| 124 | int payload_csum = 0; | ||
| 124 | 125 | ||
| 125 | #ifdef CONFIG_IP_VS_IPV6 | 126 | #ifdef CONFIG_IP_VS_IPV6 |
| 126 | if (cp->af == AF_INET6) | 127 | if (cp->af == AF_INET6) |
| @@ -135,6 +136,8 @@ udp_snat_handler(struct sk_buff *skb, | |||
| 135 | return 0; | 136 | return 0; |
| 136 | 137 | ||
| 137 | if (unlikely(cp->app != NULL)) { | 138 | if (unlikely(cp->app != NULL)) { |
| 139 | int ret; | ||
| 140 | |||
| 138 | /* Some checks before mangling */ | 141 | /* Some checks before mangling */ |
| 139 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) | 142 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
| 140 | return 0; | 143 | return 0; |
| @@ -142,8 +145,13 @@ udp_snat_handler(struct sk_buff *skb, | |||
| 142 | /* | 145 | /* |
| 143 | * Call application helper if needed | 146 | * Call application helper if needed |
| 144 | */ | 147 | */ |
| 145 | if (!ip_vs_app_pkt_out(cp, skb)) | 148 | if (!(ret = ip_vs_app_pkt_out(cp, skb))) |
| 146 | return 0; | 149 | return 0; |
| 150 | /* ret=2: csum update is needed after payload mangling */ | ||
| 151 | if (ret == 1) | ||
| 152 | oldlen = skb->len - udphoff; | ||
| 153 | else | ||
| 154 | payload_csum = 1; | ||
| 147 | } | 155 | } |
| 148 | 156 | ||
| 149 | udph = (void *)skb_network_header(skb) + udphoff; | 157 | udph = (void *)skb_network_header(skb) + udphoff; |
| @@ -156,12 +164,13 @@ udp_snat_handler(struct sk_buff *skb, | |||
| 156 | udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, | 164 | udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, |
| 157 | htons(oldlen), | 165 | htons(oldlen), |
| 158 | htons(skb->len - udphoff)); | 166 | htons(skb->len - udphoff)); |
| 159 | } else if (!cp->app && (udph->check != 0)) { | 167 | } else if (!payload_csum && (udph->check != 0)) { |
| 160 | /* Only port and addr are changed, do fast csum update */ | 168 | /* Only port and addr are changed, do fast csum update */ |
| 161 | udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, | 169 | udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, |
| 162 | cp->dport, cp->vport); | 170 | cp->dport, cp->vport); |
| 163 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 171 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
| 164 | skb->ip_summed = CHECKSUM_NONE; | 172 | skb->ip_summed = (cp->app && pp->csum_check) ? |
| 173 | CHECKSUM_UNNECESSARY : CHECKSUM_NONE; | ||
| 165 | } else { | 174 | } else { |
| 166 | /* full checksum calculation */ | 175 | /* full checksum calculation */ |
| 167 | udph->check = 0; | 176 | udph->check = 0; |
| @@ -181,6 +190,7 @@ udp_snat_handler(struct sk_buff *skb, | |||
| 181 | skb->csum); | 190 | skb->csum); |
| 182 | if (udph->check == 0) | 191 | if (udph->check == 0) |
| 183 | udph->check = CSUM_MANGLED_0; | 192 | udph->check = CSUM_MANGLED_0; |
| 193 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 184 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", | 194 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", |
| 185 | pp->name, udph->check, | 195 | pp->name, udph->check, |
| 186 | (char*)&(udph->check) - (char*)udph); | 196 | (char*)&(udph->check) - (char*)udph); |
| @@ -196,6 +206,7 @@ udp_dnat_handler(struct sk_buff *skb, | |||
| 196 | struct udphdr *udph; | 206 | struct udphdr *udph; |
| 197 | unsigned int udphoff; | 207 | unsigned int udphoff; |
| 198 | int oldlen; | 208 | int oldlen; |
| 209 | int payload_csum = 0; | ||
| 199 | 210 | ||
| 200 | #ifdef CONFIG_IP_VS_IPV6 | 211 | #ifdef CONFIG_IP_VS_IPV6 |
| 201 | if (cp->af == AF_INET6) | 212 | if (cp->af == AF_INET6) |
| @@ -210,6 +221,8 @@ udp_dnat_handler(struct sk_buff *skb, | |||
| 210 | return 0; | 221 | return 0; |
| 211 | 222 | ||
| 212 | if (unlikely(cp->app != NULL)) { | 223 | if (unlikely(cp->app != NULL)) { |
| 224 | int ret; | ||
| 225 | |||
| 213 | /* Some checks before mangling */ | 226 | /* Some checks before mangling */ |
| 214 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) | 227 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
| 215 | return 0; | 228 | return 0; |
| @@ -218,8 +231,13 @@ udp_dnat_handler(struct sk_buff *skb, | |||
| 218 | * Attempt ip_vs_app call. | 231 | * Attempt ip_vs_app call. |
| 219 | * It will fix ip_vs_conn | 232 | * It will fix ip_vs_conn |
| 220 | */ | 233 | */ |
| 221 | if (!ip_vs_app_pkt_in(cp, skb)) | 234 | if (!(ret = ip_vs_app_pkt_in(cp, skb))) |
| 222 | return 0; | 235 | return 0; |
| 236 | /* ret=2: csum update is needed after payload mangling */ | ||
| 237 | if (ret == 1) | ||
| 238 | oldlen = skb->len - udphoff; | ||
| 239 | else | ||
| 240 | payload_csum = 1; | ||
| 223 | } | 241 | } |
| 224 | 242 | ||
| 225 | udph = (void *)skb_network_header(skb) + udphoff; | 243 | udph = (void *)skb_network_header(skb) + udphoff; |
| @@ -232,12 +250,13 @@ udp_dnat_handler(struct sk_buff *skb, | |||
| 232 | udp_partial_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, | 250 | udp_partial_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, |
| 233 | htons(oldlen), | 251 | htons(oldlen), |
| 234 | htons(skb->len - udphoff)); | 252 | htons(skb->len - udphoff)); |
| 235 | } else if (!cp->app && (udph->check != 0)) { | 253 | } else if (!payload_csum && (udph->check != 0)) { |
| 236 | /* Only port and addr are changed, do fast csum update */ | 254 | /* Only port and addr are changed, do fast csum update */ |
| 237 | udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, | 255 | udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, |
| 238 | cp->vport, cp->dport); | 256 | cp->vport, cp->dport); |
| 239 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 257 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
| 240 | skb->ip_summed = CHECKSUM_NONE; | 258 | skb->ip_summed = (cp->app && pp->csum_check) ? |
| 259 | CHECKSUM_UNNECESSARY : CHECKSUM_NONE; | ||
| 241 | } else { | 260 | } else { |
| 242 | /* full checksum calculation */ | 261 | /* full checksum calculation */ |
| 243 | udph->check = 0; | 262 | udph->check = 0; |
