diff options
author | Simon Horman <horms@verge.net.au> | 2008-09-07 22:04:21 -0400 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2008-09-08 19:36:32 -0400 |
commit | 503e81f65adac596a0275ea0230f2ae1fd64c301 (patch) | |
tree | 8ed9f04bba89e3ddbf1ab24428a4f80408ca8984 /net/ipv4/ipvs/ip_vs_proto_tcp.c | |
parent | 178f5e494e3c0252d06a9b1473016addff71e01e (diff) |
ipvs: handle PARTIAL_CHECKSUM
Now that LVS can load balance locally generated traffic, packets may come
from the loopback device and thus may have a partial checksum.
The existing code allows for the case where there is no checksum at all for
TCP, however Herbert Xu has confirmed that this is not legal.
Signed-off-by: Simon Horman <horms@verge.net.au>
Acked-by: Julius Volz <juliusv@google.com>
Diffstat (limited to 'net/ipv4/ipvs/ip_vs_proto_tcp.c')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_tcp.c | 37 |
1 files changed, 35 insertions, 2 deletions
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index 808e8be0280..537f616776d 100644 --- a/net/ipv4/ipvs/ip_vs_proto_tcp.c +++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c | |||
@@ -134,12 +134,34 @@ tcp_fast_csum_update(int af, struct tcphdr *tcph, | |||
134 | } | 134 | } |
135 | 135 | ||
136 | 136 | ||
137 | static inline void | ||
138 | tcp_partial_csum_update(int af, struct tcphdr *tcph, | ||
139 | const union nf_inet_addr *oldip, | ||
140 | const union nf_inet_addr *newip, | ||
141 | __be16 oldlen, __be16 newlen) | ||
142 | { | ||
143 | #ifdef CONFIG_IP_VS_IPV6 | ||
144 | if (af == AF_INET6) | ||
145 | tcph->check = | ||
146 | csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6, | ||
147 | ip_vs_check_diff2(oldlen, newlen, | ||
148 | ~csum_unfold(tcph->check)))); | ||
149 | else | ||
150 | #endif | ||
151 | tcph->check = | ||
152 | csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip, | ||
153 | ip_vs_check_diff2(oldlen, newlen, | ||
154 | ~csum_unfold(tcph->check)))); | ||
155 | } | ||
156 | |||
157 | |||
137 | static int | 158 | static int |
138 | tcp_snat_handler(struct sk_buff *skb, | 159 | tcp_snat_handler(struct sk_buff *skb, |
139 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 160 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
140 | { | 161 | { |
141 | struct tcphdr *tcph; | 162 | struct tcphdr *tcph; |
142 | unsigned int tcphoff; | 163 | unsigned int tcphoff; |
164 | int oldlen; | ||
143 | 165 | ||
144 | #ifdef CONFIG_IP_VS_IPV6 | 166 | #ifdef CONFIG_IP_VS_IPV6 |
145 | if (cp->af == AF_INET6) | 167 | if (cp->af == AF_INET6) |
@@ -147,6 +169,7 @@ tcp_snat_handler(struct sk_buff *skb, | |||
147 | else | 169 | else |
148 | #endif | 170 | #endif |
149 | tcphoff = ip_hdrlen(skb); | 171 | tcphoff = ip_hdrlen(skb); |
172 | oldlen = skb->len - tcphoff; | ||
150 | 173 | ||
151 | /* csum_check requires unshared skb */ | 174 | /* csum_check requires unshared skb */ |
152 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) | 175 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) |
@@ -166,7 +189,11 @@ tcp_snat_handler(struct sk_buff *skb, | |||
166 | tcph->source = cp->vport; | 189 | tcph->source = cp->vport; |
167 | 190 | ||
168 | /* Adjust TCP checksums */ | 191 | /* Adjust TCP checksums */ |
169 | if (!cp->app && (tcph->check != 0)) { | 192 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
193 | tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, | ||
194 | htonl(oldlen), | ||
195 | htonl(skb->len - tcphoff)); | ||
196 | } else if (!cp->app) { | ||
170 | /* Only port and addr are changed, do fast csum update */ | 197 | /* Only port and addr are changed, do fast csum update */ |
171 | tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, | 198 | tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, |
172 | cp->dport, cp->vport); | 199 | cp->dport, cp->vport); |
@@ -204,6 +231,7 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
204 | { | 231 | { |
205 | struct tcphdr *tcph; | 232 | struct tcphdr *tcph; |
206 | unsigned int tcphoff; | 233 | unsigned int tcphoff; |
234 | int oldlen; | ||
207 | 235 | ||
208 | #ifdef CONFIG_IP_VS_IPV6 | 236 | #ifdef CONFIG_IP_VS_IPV6 |
209 | if (cp->af == AF_INET6) | 237 | if (cp->af == AF_INET6) |
@@ -211,6 +239,7 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
211 | else | 239 | else |
212 | #endif | 240 | #endif |
213 | tcphoff = ip_hdrlen(skb); | 241 | tcphoff = ip_hdrlen(skb); |
242 | oldlen = skb->len - tcphoff; | ||
214 | 243 | ||
215 | /* csum_check requires unshared skb */ | 244 | /* csum_check requires unshared skb */ |
216 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) | 245 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) |
@@ -235,7 +264,11 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
235 | /* | 264 | /* |
236 | * Adjust TCP checksums | 265 | * Adjust TCP checksums |
237 | */ | 266 | */ |
238 | if (!cp->app && (tcph->check != 0)) { | 267 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
268 | tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, | ||
269 | htonl(oldlen), | ||
270 | htonl(skb->len - tcphoff)); | ||
271 | } else if (!cp->app) { | ||
239 | /* Only port and addr are changed, do fast csum update */ | 272 | /* Only port and addr are changed, do fast csum update */ |
240 | tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, | 273 | tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, |
241 | cp->vport, cp->dport); | 274 | cp->vport, cp->dport); |