aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Horman <horms@verge.net.au>2008-09-07 22:04:21 -0400
committerSimon Horman <horms@verge.net.au>2008-09-08 19:36:32 -0400
commit503e81f65adac596a0275ea0230f2ae1fd64c301 (patch)
tree8ed9f04bba89e3ddbf1ab24428a4f80408ca8984
parent178f5e494e3c0252d06a9b1473016addff71e01e (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.c37
-rw-r--r--net/ipv4/ipvs/ip_vs_proto_udp.c37
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
137static inline void
138tcp_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
137static int 158static int
138tcp_snat_handler(struct sk_buff *skb, 159tcp_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
144static inline void
145udp_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
144static int 165static int
145udp_snat_handler(struct sk_buff *skb, 166udp_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);