aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/ipvs/ip_vs_proto_tcp.c
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 /net/ipv4/ipvs/ip_vs_proto_tcp.c
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>
Diffstat (limited to 'net/ipv4/ipvs/ip_vs_proto_tcp.c')
-rw-r--r--net/ipv4/ipvs/ip_vs_proto_tcp.c37
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
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);