diff options
author | Julius Volz <juliusv@google.com> | 2008-09-02 09:55:42 -0400 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2008-09-04 21:17:07 -0400 |
commit | 0bbdd42b7efa66685b6d74701bcde3a596a3a59d (patch) | |
tree | 98b1cc6fc7b856768b7923f2747e64c2ccc7878b /net | |
parent | 3b047d9d0407e78a52f009835a0e26cb62edb8c7 (diff) |
IPVS: Extend protocol DNAT/SNAT and state handlers
Extend protocol DNAT/SNAT and state handlers to work with IPv6. Also
change/introduce new checksumming helper functions for this.
Signed-off-by: Julius Volz <juliusv@google.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_tcp.c | 85 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_udp.c | 82 |
2 files changed, 131 insertions, 36 deletions
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index 9211afa8f303..3daae43ae44b 100644 --- a/net/ipv4/ipvs/ip_vs_proto_tcp.c +++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c | |||
@@ -114,11 +114,21 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
114 | 114 | ||
115 | 115 | ||
116 | static inline void | 116 | static inline void |
117 | tcp_fast_csum_update(struct tcphdr *tcph, __be32 oldip, __be32 newip, | 117 | tcp_fast_csum_update(int af, struct tcphdr *tcph, |
118 | const union nf_inet_addr *oldip, | ||
119 | const union nf_inet_addr *newip, | ||
118 | __be16 oldport, __be16 newport) | 120 | __be16 oldport, __be16 newport) |
119 | { | 121 | { |
122 | #ifdef CONFIG_IP_VS_IPV6 | ||
123 | if (af == AF_INET6) | ||
124 | tcph->check = | ||
125 | csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6, | ||
126 | ip_vs_check_diff2(oldport, newport, | ||
127 | ~csum_unfold(tcph->check)))); | ||
128 | else | ||
129 | #endif | ||
120 | tcph->check = | 130 | tcph->check = |
121 | csum_fold(ip_vs_check_diff4(oldip, newip, | 131 | csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip, |
122 | ip_vs_check_diff2(oldport, newport, | 132 | ip_vs_check_diff2(oldport, newport, |
123 | ~csum_unfold(tcph->check)))); | 133 | ~csum_unfold(tcph->check)))); |
124 | } | 134 | } |
@@ -129,7 +139,14 @@ tcp_snat_handler(struct sk_buff *skb, | |||
129 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 139 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
130 | { | 140 | { |
131 | struct tcphdr *tcph; | 141 | struct tcphdr *tcph; |
132 | const unsigned int tcphoff = ip_hdrlen(skb); | 142 | unsigned int tcphoff; |
143 | |||
144 | #ifdef CONFIG_IP_VS_IPV6 | ||
145 | if (cp->af == AF_INET6) | ||
146 | tcphoff = sizeof(struct ipv6hdr); | ||
147 | else | ||
148 | #endif | ||
149 | tcphoff = ip_hdrlen(skb); | ||
133 | 150 | ||
134 | /* csum_check requires unshared skb */ | 151 | /* csum_check requires unshared skb */ |
135 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) | 152 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) |
@@ -137,7 +154,7 @@ tcp_snat_handler(struct sk_buff *skb, | |||
137 | 154 | ||
138 | if (unlikely(cp->app != NULL)) { | 155 | if (unlikely(cp->app != NULL)) { |
139 | /* Some checks before mangling */ | 156 | /* Some checks before mangling */ |
140 | if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp)) | 157 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
141 | return 0; | 158 | return 0; |
142 | 159 | ||
143 | /* Call application helper if needed */ | 160 | /* Call application helper if needed */ |
@@ -145,13 +162,13 @@ tcp_snat_handler(struct sk_buff *skb, | |||
145 | return 0; | 162 | return 0; |
146 | } | 163 | } |
147 | 164 | ||
148 | tcph = (void *)ip_hdr(skb) + tcphoff; | 165 | tcph = (void *)skb_network_header(skb) + tcphoff; |
149 | tcph->source = cp->vport; | 166 | tcph->source = cp->vport; |
150 | 167 | ||
151 | /* Adjust TCP checksums */ | 168 | /* Adjust TCP checksums */ |
152 | if (!cp->app) { | 169 | if (!cp->app) { |
153 | /* Only port and addr are changed, do fast csum update */ | 170 | /* Only port and addr are changed, do fast csum update */ |
154 | tcp_fast_csum_update(tcph, cp->daddr.ip, cp->vaddr.ip, | 171 | tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, |
155 | cp->dport, cp->vport); | 172 | cp->dport, cp->vport); |
156 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 173 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
157 | skb->ip_summed = CHECKSUM_NONE; | 174 | skb->ip_summed = CHECKSUM_NONE; |
@@ -159,9 +176,20 @@ tcp_snat_handler(struct sk_buff *skb, | |||
159 | /* full checksum calculation */ | 176 | /* full checksum calculation */ |
160 | tcph->check = 0; | 177 | tcph->check = 0; |
161 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); | 178 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); |
162 | tcph->check = csum_tcpudp_magic(cp->vaddr.ip, cp->caddr.ip, | 179 | #ifdef CONFIG_IP_VS_IPV6 |
163 | skb->len - tcphoff, | 180 | if (cp->af == AF_INET6) |
164 | cp->protocol, skb->csum); | 181 | tcph->check = csum_ipv6_magic(&cp->vaddr.in6, |
182 | &cp->caddr.in6, | ||
183 | skb->len - tcphoff, | ||
184 | cp->protocol, skb->csum); | ||
185 | else | ||
186 | #endif | ||
187 | tcph->check = csum_tcpudp_magic(cp->vaddr.ip, | ||
188 | cp->caddr.ip, | ||
189 | skb->len - tcphoff, | ||
190 | cp->protocol, | ||
191 | skb->csum); | ||
192 | |||
165 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", | 193 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", |
166 | pp->name, tcph->check, | 194 | pp->name, tcph->check, |
167 | (char*)&(tcph->check) - (char*)tcph); | 195 | (char*)&(tcph->check) - (char*)tcph); |
@@ -175,7 +203,14 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
175 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 203 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
176 | { | 204 | { |
177 | struct tcphdr *tcph; | 205 | struct tcphdr *tcph; |
178 | const unsigned int tcphoff = ip_hdrlen(skb); | 206 | unsigned int tcphoff; |
207 | |||
208 | #ifdef CONFIG_IP_VS_IPV6 | ||
209 | if (cp->af == AF_INET6) | ||
210 | tcphoff = sizeof(struct ipv6hdr); | ||
211 | else | ||
212 | #endif | ||
213 | tcphoff = ip_hdrlen(skb); | ||
179 | 214 | ||
180 | /* csum_check requires unshared skb */ | 215 | /* csum_check requires unshared skb */ |
181 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) | 216 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) |
@@ -183,7 +218,7 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
183 | 218 | ||
184 | if (unlikely(cp->app != NULL)) { | 219 | if (unlikely(cp->app != NULL)) { |
185 | /* Some checks before mangling */ | 220 | /* Some checks before mangling */ |
186 | if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp)) | 221 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
187 | return 0; | 222 | return 0; |
188 | 223 | ||
189 | /* | 224 | /* |
@@ -194,7 +229,7 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
194 | return 0; | 229 | return 0; |
195 | } | 230 | } |
196 | 231 | ||
197 | tcph = (void *)ip_hdr(skb) + tcphoff; | 232 | tcph = (void *)skb_network_header(skb) + tcphoff; |
198 | tcph->dest = cp->dport; | 233 | tcph->dest = cp->dport; |
199 | 234 | ||
200 | /* | 235 | /* |
@@ -202,7 +237,7 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
202 | */ | 237 | */ |
203 | if (!cp->app) { | 238 | if (!cp->app) { |
204 | /* Only port and addr are changed, do fast csum update */ | 239 | /* Only port and addr are changed, do fast csum update */ |
205 | tcp_fast_csum_update(tcph, cp->vaddr.ip, cp->daddr.ip, | 240 | tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, |
206 | cp->vport, cp->dport); | 241 | cp->vport, cp->dport); |
207 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 242 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
208 | skb->ip_summed = CHECKSUM_NONE; | 243 | skb->ip_summed = CHECKSUM_NONE; |
@@ -210,9 +245,19 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
210 | /* full checksum calculation */ | 245 | /* full checksum calculation */ |
211 | tcph->check = 0; | 246 | tcph->check = 0; |
212 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); | 247 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); |
213 | tcph->check = csum_tcpudp_magic(cp->caddr.ip, cp->daddr.ip, | 248 | #ifdef CONFIG_IP_VS_IPV6 |
214 | skb->len - tcphoff, | 249 | if (cp->af == AF_INET6) |
215 | cp->protocol, skb->csum); | 250 | tcph->check = csum_ipv6_magic(&cp->caddr.in6, |
251 | &cp->daddr.in6, | ||
252 | skb->len - tcphoff, | ||
253 | cp->protocol, skb->csum); | ||
254 | else | ||
255 | #endif | ||
256 | tcph->check = csum_tcpudp_magic(cp->caddr.ip, | ||
257 | cp->daddr.ip, | ||
258 | skb->len - tcphoff, | ||
259 | cp->protocol, | ||
260 | skb->csum); | ||
216 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 261 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
217 | } | 262 | } |
218 | return 1; | 263 | return 1; |
@@ -487,7 +532,13 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, | |||
487 | { | 532 | { |
488 | struct tcphdr _tcph, *th; | 533 | struct tcphdr _tcph, *th; |
489 | 534 | ||
490 | th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); | 535 | #ifdef CONFIG_IP_VS_IPV6 |
536 | int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); | ||
537 | #else | ||
538 | int ihl = ip_hdrlen(skb); | ||
539 | #endif | ||
540 | |||
541 | th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); | ||
491 | if (th == NULL) | 542 | if (th == NULL) |
492 | return 0; | 543 | return 0; |
493 | 544 | ||
diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c index d3a1b1f2d10d..6cca0ad8e325 100644 --- a/net/ipv4/ipvs/ip_vs_proto_udp.c +++ b/net/ipv4/ipvs/ip_vs_proto_udp.c | |||
@@ -120,13 +120,23 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
120 | 120 | ||
121 | 121 | ||
122 | static inline void | 122 | static inline void |
123 | udp_fast_csum_update(struct udphdr *uhdr, __be32 oldip, __be32 newip, | 123 | udp_fast_csum_update(int af, struct udphdr *uhdr, |
124 | const union nf_inet_addr *oldip, | ||
125 | const union nf_inet_addr *newip, | ||
124 | __be16 oldport, __be16 newport) | 126 | __be16 oldport, __be16 newport) |
125 | { | 127 | { |
126 | uhdr->check = | 128 | #ifdef CONFIG_IP_VS_IPV6 |
127 | csum_fold(ip_vs_check_diff4(oldip, newip, | 129 | if (af == AF_INET6) |
128 | ip_vs_check_diff2(oldport, newport, | 130 | uhdr->check = |
129 | ~csum_unfold(uhdr->check)))); | 131 | csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6, |
132 | ip_vs_check_diff2(oldport, newport, | ||
133 | ~csum_unfold(uhdr->check)))); | ||
134 | else | ||
135 | #endif | ||
136 | uhdr->check = | ||
137 | csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip, | ||
138 | ip_vs_check_diff2(oldport, newport, | ||
139 | ~csum_unfold(uhdr->check)))); | ||
130 | if (!uhdr->check) | 140 | if (!uhdr->check) |
131 | uhdr->check = CSUM_MANGLED_0; | 141 | uhdr->check = CSUM_MANGLED_0; |
132 | } | 142 | } |
@@ -136,7 +146,14 @@ udp_snat_handler(struct sk_buff *skb, | |||
136 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 146 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
137 | { | 147 | { |
138 | struct udphdr *udph; | 148 | struct udphdr *udph; |
139 | const unsigned int udphoff = ip_hdrlen(skb); | 149 | unsigned int udphoff; |
150 | |||
151 | #ifdef CONFIG_IP_VS_IPV6 | ||
152 | if (cp->af == AF_INET6) | ||
153 | udphoff = sizeof(struct ipv6hdr); | ||
154 | else | ||
155 | #endif | ||
156 | udphoff = ip_hdrlen(skb); | ||
140 | 157 | ||
141 | /* csum_check requires unshared skb */ | 158 | /* csum_check requires unshared skb */ |
142 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) | 159 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) |
@@ -144,7 +161,7 @@ udp_snat_handler(struct sk_buff *skb, | |||
144 | 161 | ||
145 | if (unlikely(cp->app != NULL)) { | 162 | if (unlikely(cp->app != NULL)) { |
146 | /* Some checks before mangling */ | 163 | /* Some checks before mangling */ |
147 | if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp)) | 164 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
148 | return 0; | 165 | return 0; |
149 | 166 | ||
150 | /* | 167 | /* |
@@ -154,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, | |||
154 | return 0; | 171 | return 0; |
155 | } | 172 | } |
156 | 173 | ||
157 | udph = (void *)ip_hdr(skb) + udphoff; | 174 | udph = (void *)skb_network_header(skb) + udphoff; |
158 | udph->source = cp->vport; | 175 | udph->source = cp->vport; |
159 | 176 | ||
160 | /* | 177 | /* |
@@ -162,7 +179,7 @@ udp_snat_handler(struct sk_buff *skb, | |||
162 | */ | 179 | */ |
163 | if (!cp->app && (udph->check != 0)) { | 180 | if (!cp->app && (udph->check != 0)) { |
164 | /* Only port and addr are changed, do fast csum update */ | 181 | /* Only port and addr are changed, do fast csum update */ |
165 | udp_fast_csum_update(udph, cp->daddr.ip, cp->vaddr.ip, | 182 | udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, |
166 | cp->dport, cp->vport); | 183 | cp->dport, cp->vport); |
167 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 184 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
168 | skb->ip_summed = CHECKSUM_NONE; | 185 | skb->ip_summed = CHECKSUM_NONE; |
@@ -170,9 +187,19 @@ udp_snat_handler(struct sk_buff *skb, | |||
170 | /* full checksum calculation */ | 187 | /* full checksum calculation */ |
171 | udph->check = 0; | 188 | udph->check = 0; |
172 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); | 189 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); |
173 | udph->check = csum_tcpudp_magic(cp->vaddr.ip, cp->caddr.ip, | 190 | #ifdef CONFIG_IP_VS_IPV6 |
174 | skb->len - udphoff, | 191 | if (cp->af == AF_INET6) |
175 | cp->protocol, skb->csum); | 192 | udph->check = csum_ipv6_magic(&cp->vaddr.in6, |
193 | &cp->caddr.in6, | ||
194 | skb->len - udphoff, | ||
195 | cp->protocol, skb->csum); | ||
196 | else | ||
197 | #endif | ||
198 | udph->check = csum_tcpudp_magic(cp->vaddr.ip, | ||
199 | cp->caddr.ip, | ||
200 | skb->len - udphoff, | ||
201 | cp->protocol, | ||
202 | skb->csum); | ||
176 | if (udph->check == 0) | 203 | if (udph->check == 0) |
177 | udph->check = CSUM_MANGLED_0; | 204 | udph->check = CSUM_MANGLED_0; |
178 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", | 205 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", |
@@ -188,7 +215,14 @@ udp_dnat_handler(struct sk_buff *skb, | |||
188 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 215 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
189 | { | 216 | { |
190 | struct udphdr *udph; | 217 | struct udphdr *udph; |
191 | unsigned int udphoff = ip_hdrlen(skb); | 218 | unsigned int udphoff; |
219 | |||
220 | #ifdef CONFIG_IP_VS_IPV6 | ||
221 | if (cp->af == AF_INET6) | ||
222 | udphoff = sizeof(struct ipv6hdr); | ||
223 | else | ||
224 | #endif | ||
225 | udphoff = ip_hdrlen(skb); | ||
192 | 226 | ||
193 | /* csum_check requires unshared skb */ | 227 | /* csum_check requires unshared skb */ |
194 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) | 228 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) |
@@ -196,7 +230,7 @@ udp_dnat_handler(struct sk_buff *skb, | |||
196 | 230 | ||
197 | if (unlikely(cp->app != NULL)) { | 231 | if (unlikely(cp->app != NULL)) { |
198 | /* Some checks before mangling */ | 232 | /* Some checks before mangling */ |
199 | if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp)) | 233 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
200 | return 0; | 234 | return 0; |
201 | 235 | ||
202 | /* | 236 | /* |
@@ -207,7 +241,7 @@ udp_dnat_handler(struct sk_buff *skb, | |||
207 | return 0; | 241 | return 0; |
208 | } | 242 | } |
209 | 243 | ||
210 | udph = (void *)ip_hdr(skb) + udphoff; | 244 | udph = (void *)skb_network_header(skb) + udphoff; |
211 | udph->dest = cp->dport; | 245 | udph->dest = cp->dport; |
212 | 246 | ||
213 | /* | 247 | /* |
@@ -215,7 +249,7 @@ udp_dnat_handler(struct sk_buff *skb, | |||
215 | */ | 249 | */ |
216 | if (!cp->app && (udph->check != 0)) { | 250 | if (!cp->app && (udph->check != 0)) { |
217 | /* Only port and addr are changed, do fast csum update */ | 251 | /* Only port and addr are changed, do fast csum update */ |
218 | udp_fast_csum_update(udph, cp->vaddr.ip, cp->daddr.ip, | 252 | udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, |
219 | cp->vport, cp->dport); | 253 | cp->vport, cp->dport); |
220 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 254 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
221 | skb->ip_summed = CHECKSUM_NONE; | 255 | skb->ip_summed = CHECKSUM_NONE; |
@@ -223,9 +257,19 @@ udp_dnat_handler(struct sk_buff *skb, | |||
223 | /* full checksum calculation */ | 257 | /* full checksum calculation */ |
224 | udph->check = 0; | 258 | udph->check = 0; |
225 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); | 259 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); |
226 | udph->check = csum_tcpudp_magic(cp->caddr.ip, cp->daddr.ip, | 260 | #ifdef CONFIG_IP_VS_IPV6 |
227 | skb->len - udphoff, | 261 | if (cp->af == AF_INET6) |
228 | cp->protocol, skb->csum); | 262 | udph->check = csum_ipv6_magic(&cp->caddr.in6, |
263 | &cp->daddr.in6, | ||
264 | skb->len - udphoff, | ||
265 | cp->protocol, skb->csum); | ||
266 | else | ||
267 | #endif | ||
268 | udph->check = csum_tcpudp_magic(cp->caddr.ip, | ||
269 | cp->daddr.ip, | ||
270 | skb->len - udphoff, | ||
271 | cp->protocol, | ||
272 | skb->csum); | ||
229 | if (udph->check == 0) | 273 | if (udph->check == 0) |
230 | udph->check = CSUM_MANGLED_0; | 274 | udph->check = CSUM_MANGLED_0; |
231 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 275 | skb->ip_summed = CHECKSUM_UNNECESSARY; |