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 | |
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>
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_tcp.c | 37 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_udp.c | 37 |
2 files changed, 70 insertions, 4 deletions
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index 808e8be0280a..537f616776da 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); |
diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c index 5f2073e41cf6..e3ee26bd1de7 100644 --- a/net/ipv4/ipvs/ip_vs_proto_udp.c +++ b/net/ipv4/ipvs/ip_vs_proto_udp.c | |||
@@ -141,12 +141,34 @@ udp_fast_csum_update(int af, struct udphdr *uhdr, | |||
141 | uhdr->check = CSUM_MANGLED_0; | 141 | uhdr->check = CSUM_MANGLED_0; |
142 | } | 142 | } |
143 | 143 | ||
144 | static inline void | ||
145 | udp_partial_csum_update(int af, struct udphdr *uhdr, | ||
146 | const union nf_inet_addr *oldip, | ||
147 | const union nf_inet_addr *newip, | ||
148 | __be16 oldlen, __be16 newlen) | ||
149 | { | ||
150 | #ifdef CONFIG_IP_VS_IPV6 | ||
151 | if (af == AF_INET6) | ||
152 | uhdr->check = | ||
153 | csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6, | ||
154 | ip_vs_check_diff2(oldlen, newlen, | ||
155 | ~csum_unfold(uhdr->check)))); | ||
156 | else | ||
157 | #endif | ||
158 | uhdr->check = | ||
159 | csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip, | ||
160 | ip_vs_check_diff2(oldlen, newlen, | ||
161 | ~csum_unfold(uhdr->check)))); | ||
162 | } | ||
163 | |||
164 | |||
144 | static int | 165 | static int |
145 | udp_snat_handler(struct sk_buff *skb, | 166 | udp_snat_handler(struct sk_buff *skb, |
146 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 167 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
147 | { | 168 | { |
148 | struct udphdr *udph; | 169 | struct udphdr *udph; |
149 | unsigned int udphoff; | 170 | unsigned int udphoff; |
171 | int oldlen; | ||
150 | 172 | ||
151 | #ifdef CONFIG_IP_VS_IPV6 | 173 | #ifdef CONFIG_IP_VS_IPV6 |
152 | if (cp->af == AF_INET6) | 174 | if (cp->af == AF_INET6) |
@@ -154,6 +176,7 @@ udp_snat_handler(struct sk_buff *skb, | |||
154 | else | 176 | else |
155 | #endif | 177 | #endif |
156 | udphoff = ip_hdrlen(skb); | 178 | udphoff = ip_hdrlen(skb); |
179 | oldlen = skb->len - udphoff; | ||
157 | 180 | ||
158 | /* csum_check requires unshared skb */ | 181 | /* csum_check requires unshared skb */ |
159 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) | 182 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) |
@@ -177,7 +200,11 @@ udp_snat_handler(struct sk_buff *skb, | |||
177 | /* | 200 | /* |
178 | * Adjust UDP checksums | 201 | * Adjust UDP checksums |
179 | */ | 202 | */ |
180 | if (!cp->app && (udph->check != 0)) { | 203 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
204 | udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, | ||
205 | htonl(oldlen), | ||
206 | htonl(skb->len - udphoff)); | ||
207 | } else if (!cp->app && (udph->check != 0)) { | ||
181 | /* Only port and addr are changed, do fast csum update */ | 208 | /* Only port and addr are changed, do fast csum update */ |
182 | udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, | 209 | udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, |
183 | cp->dport, cp->vport); | 210 | cp->dport, cp->vport); |
@@ -216,6 +243,7 @@ udp_dnat_handler(struct sk_buff *skb, | |||
216 | { | 243 | { |
217 | struct udphdr *udph; | 244 | struct udphdr *udph; |
218 | unsigned int udphoff; | 245 | unsigned int udphoff; |
246 | int oldlen; | ||
219 | 247 | ||
220 | #ifdef CONFIG_IP_VS_IPV6 | 248 | #ifdef CONFIG_IP_VS_IPV6 |
221 | if (cp->af == AF_INET6) | 249 | if (cp->af == AF_INET6) |
@@ -223,6 +251,7 @@ udp_dnat_handler(struct sk_buff *skb, | |||
223 | else | 251 | else |
224 | #endif | 252 | #endif |
225 | udphoff = ip_hdrlen(skb); | 253 | udphoff = ip_hdrlen(skb); |
254 | oldlen = skb->len - udphoff; | ||
226 | 255 | ||
227 | /* csum_check requires unshared skb */ | 256 | /* csum_check requires unshared skb */ |
228 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) | 257 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) |
@@ -247,7 +276,11 @@ udp_dnat_handler(struct sk_buff *skb, | |||
247 | /* | 276 | /* |
248 | * Adjust UDP checksums | 277 | * Adjust UDP checksums |
249 | */ | 278 | */ |
250 | if (!cp->app && (udph->check != 0)) { | 279 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
280 | udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, | ||
281 | htonl(oldlen), | ||
282 | htonl(skb->len - udphoff)); | ||
283 | } else if (!cp->app && (udph->check != 0)) { | ||
251 | /* Only port and addr are changed, do fast csum update */ | 284 | /* Only port and addr are changed, do fast csum update */ |
252 | udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, | 285 | udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, |
253 | cp->vport, cp->dport); | 286 | cp->vport, cp->dport); |