diff options
Diffstat (limited to 'net/ipv4/ipvs/ip_vs_proto_udp.c')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_udp.c | 37 |
1 files changed, 35 insertions, 2 deletions
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); |