diff options
author | David S. Miller <davem@davemloft.net> | 2008-09-09 22:51:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-09-09 22:51:04 -0400 |
commit | dacc62dbf56e872ad96edde0393b9deb56d80cd5 (patch) | |
tree | 3d1b3e25aba9c5324bb0f6289033f502fa6ccb8c | |
parent | 47abf28d5b36521558a848a346064a3a3c82bd9e (diff) | |
parent | c051a0a2c9e283c1123ed3ce65e66e41d2ce5e24 (diff) |
Merge branch 'lvs-next-2.6' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/lvs-2.6
-rw-r--r-- | include/net/ip_vs.h | 308 | ||||
-rw-r--r-- | net/ipv4/ipvs/Kconfig | 11 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_conn.c | 249 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_core.c | 806 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_ctl.c | 523 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_dh.c | 5 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_est.c | 40 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_ftp.c | 61 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_lblc.c | 7 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_lblcr.c | 11 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_lc.c | 11 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_nq.c | 15 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto.c | 65 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_ah_esp.c | 100 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_tcp.c | 253 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_udp.c | 226 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_rr.c | 13 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_sed.c | 15 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_sh.c | 5 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_sync.c | 40 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_wlc.c | 15 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_wrr.c | 15 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_xmit.c | 471 |
23 files changed, 2469 insertions, 796 deletions
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index a25ad243031d..33e2ac6ceb3e 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h | |||
@@ -21,11 +21,103 @@ | |||
21 | #include <linux/timer.h> | 21 | #include <linux/timer.h> |
22 | 22 | ||
23 | #include <net/checksum.h> | 23 | #include <net/checksum.h> |
24 | #include <linux/netfilter.h> /* for union nf_inet_addr */ | ||
25 | #include <linux/ipv6.h> /* for struct ipv6hdr */ | ||
26 | #include <net/ipv6.h> /* for ipv6_addr_copy */ | ||
27 | |||
28 | struct ip_vs_iphdr { | ||
29 | int len; | ||
30 | __u8 protocol; | ||
31 | union nf_inet_addr saddr; | ||
32 | union nf_inet_addr daddr; | ||
33 | }; | ||
34 | |||
35 | static inline void | ||
36 | ip_vs_fill_iphdr(int af, const void *nh, struct ip_vs_iphdr *iphdr) | ||
37 | { | ||
38 | #ifdef CONFIG_IP_VS_IPV6 | ||
39 | if (af == AF_INET6) { | ||
40 | const struct ipv6hdr *iph = nh; | ||
41 | iphdr->len = sizeof(struct ipv6hdr); | ||
42 | iphdr->protocol = iph->nexthdr; | ||
43 | ipv6_addr_copy(&iphdr->saddr.in6, &iph->saddr); | ||
44 | ipv6_addr_copy(&iphdr->daddr.in6, &iph->daddr); | ||
45 | } else | ||
46 | #endif | ||
47 | { | ||
48 | const struct iphdr *iph = nh; | ||
49 | iphdr->len = iph->ihl * 4; | ||
50 | iphdr->protocol = iph->protocol; | ||
51 | iphdr->saddr.ip = iph->saddr; | ||
52 | iphdr->daddr.ip = iph->daddr; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | static inline void ip_vs_addr_copy(int af, union nf_inet_addr *dst, | ||
57 | const union nf_inet_addr *src) | ||
58 | { | ||
59 | #ifdef CONFIG_IP_VS_IPV6 | ||
60 | if (af == AF_INET6) | ||
61 | ipv6_addr_copy(&dst->in6, &src->in6); | ||
62 | else | ||
63 | #endif | ||
64 | dst->ip = src->ip; | ||
65 | } | ||
66 | |||
67 | static inline int ip_vs_addr_equal(int af, const union nf_inet_addr *a, | ||
68 | const union nf_inet_addr *b) | ||
69 | { | ||
70 | #ifdef CONFIG_IP_VS_IPV6 | ||
71 | if (af == AF_INET6) | ||
72 | return ipv6_addr_equal(&a->in6, &b->in6); | ||
73 | #endif | ||
74 | return a->ip == b->ip; | ||
75 | } | ||
24 | 76 | ||
25 | #ifdef CONFIG_IP_VS_DEBUG | 77 | #ifdef CONFIG_IP_VS_DEBUG |
26 | #include <linux/net.h> | 78 | #include <linux/net.h> |
27 | 79 | ||
28 | extern int ip_vs_get_debug_level(void); | 80 | extern int ip_vs_get_debug_level(void); |
81 | |||
82 | static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len, | ||
83 | const union nf_inet_addr *addr, | ||
84 | int *idx) | ||
85 | { | ||
86 | int len; | ||
87 | #ifdef CONFIG_IP_VS_IPV6 | ||
88 | if (af == AF_INET6) | ||
89 | len = snprintf(&buf[*idx], buf_len - *idx, "[" NIP6_FMT "]", | ||
90 | NIP6(addr->in6)) + 1; | ||
91 | else | ||
92 | #endif | ||
93 | len = snprintf(&buf[*idx], buf_len - *idx, NIPQUAD_FMT, | ||
94 | NIPQUAD(addr->ip)) + 1; | ||
95 | |||
96 | *idx += len; | ||
97 | BUG_ON(*idx > buf_len + 1); | ||
98 | return &buf[*idx - len]; | ||
99 | } | ||
100 | |||
101 | #define IP_VS_DBG_BUF(level, msg...) \ | ||
102 | do { \ | ||
103 | char ip_vs_dbg_buf[160]; \ | ||
104 | int ip_vs_dbg_idx = 0; \ | ||
105 | if (level <= ip_vs_get_debug_level()) \ | ||
106 | printk(KERN_DEBUG "IPVS: " msg); \ | ||
107 | } while (0) | ||
108 | #define IP_VS_ERR_BUF(msg...) \ | ||
109 | do { \ | ||
110 | char ip_vs_dbg_buf[160]; \ | ||
111 | int ip_vs_dbg_idx = 0; \ | ||
112 | printk(KERN_ERR "IPVS: " msg); \ | ||
113 | } while (0) | ||
114 | |||
115 | /* Only use from within IP_VS_DBG_BUF() or IP_VS_ERR_BUF macros */ | ||
116 | #define IP_VS_DBG_ADDR(af, addr) \ | ||
117 | ip_vs_dbg_addr(af, ip_vs_dbg_buf, \ | ||
118 | sizeof(ip_vs_dbg_buf), addr, \ | ||
119 | &ip_vs_dbg_idx) | ||
120 | |||
29 | #define IP_VS_DBG(level, msg...) \ | 121 | #define IP_VS_DBG(level, msg...) \ |
30 | do { \ | 122 | do { \ |
31 | if (level <= ip_vs_get_debug_level()) \ | 123 | if (level <= ip_vs_get_debug_level()) \ |
@@ -48,6 +140,8 @@ extern int ip_vs_get_debug_level(void); | |||
48 | pp->debug_packet(pp, skb, ofs, msg); \ | 140 | pp->debug_packet(pp, skb, ofs, msg); \ |
49 | } while (0) | 141 | } while (0) |
50 | #else /* NO DEBUGGING at ALL */ | 142 | #else /* NO DEBUGGING at ALL */ |
143 | #define IP_VS_DBG_BUF(level, msg...) do {} while (0) | ||
144 | #define IP_VS_ERR_BUF(msg...) do {} while (0) | ||
51 | #define IP_VS_DBG(level, msg...) do {} while (0) | 145 | #define IP_VS_DBG(level, msg...) do {} while (0) |
52 | #define IP_VS_DBG_RL(msg...) do {} while (0) | 146 | #define IP_VS_DBG_RL(msg...) do {} while (0) |
53 | #define IP_VS_DBG_PKT(level, pp, skb, ofs, msg) do {} while (0) | 147 | #define IP_VS_DBG_PKT(level, pp, skb, ofs, msg) do {} while (0) |
@@ -160,27 +254,10 @@ struct ip_vs_estimator { | |||
160 | 254 | ||
161 | struct ip_vs_stats | 255 | struct ip_vs_stats |
162 | { | 256 | { |
163 | __u32 conns; /* connections scheduled */ | 257 | struct ip_vs_stats_user ustats; /* statistics */ |
164 | __u32 inpkts; /* incoming packets */ | 258 | struct ip_vs_estimator est; /* estimator */ |
165 | __u32 outpkts; /* outgoing packets */ | ||
166 | __u64 inbytes; /* incoming bytes */ | ||
167 | __u64 outbytes; /* outgoing bytes */ | ||
168 | |||
169 | __u32 cps; /* current connection rate */ | ||
170 | __u32 inpps; /* current in packet rate */ | ||
171 | __u32 outpps; /* current out packet rate */ | ||
172 | __u32 inbps; /* current in byte rate */ | ||
173 | __u32 outbps; /* current out byte rate */ | ||
174 | |||
175 | /* | ||
176 | * Don't add anything before the lock, because we use memcpy() to copy | ||
177 | * the members before the lock to struct ip_vs_stats_user in | ||
178 | * ip_vs_ctl.c. | ||
179 | */ | ||
180 | 259 | ||
181 | spinlock_t lock; /* spin lock */ | 260 | spinlock_t lock; /* spin lock */ |
182 | |||
183 | struct ip_vs_estimator est; /* estimator */ | ||
184 | }; | 261 | }; |
185 | 262 | ||
186 | struct dst_entry; | 263 | struct dst_entry; |
@@ -202,21 +279,23 @@ struct ip_vs_protocol { | |||
202 | 279 | ||
203 | void (*exit)(struct ip_vs_protocol *pp); | 280 | void (*exit)(struct ip_vs_protocol *pp); |
204 | 281 | ||
205 | int (*conn_schedule)(struct sk_buff *skb, | 282 | int (*conn_schedule)(int af, struct sk_buff *skb, |
206 | struct ip_vs_protocol *pp, | 283 | struct ip_vs_protocol *pp, |
207 | int *verdict, struct ip_vs_conn **cpp); | 284 | int *verdict, struct ip_vs_conn **cpp); |
208 | 285 | ||
209 | struct ip_vs_conn * | 286 | struct ip_vs_conn * |
210 | (*conn_in_get)(const struct sk_buff *skb, | 287 | (*conn_in_get)(int af, |
288 | const struct sk_buff *skb, | ||
211 | struct ip_vs_protocol *pp, | 289 | struct ip_vs_protocol *pp, |
212 | const struct iphdr *iph, | 290 | const struct ip_vs_iphdr *iph, |
213 | unsigned int proto_off, | 291 | unsigned int proto_off, |
214 | int inverse); | 292 | int inverse); |
215 | 293 | ||
216 | struct ip_vs_conn * | 294 | struct ip_vs_conn * |
217 | (*conn_out_get)(const struct sk_buff *skb, | 295 | (*conn_out_get)(int af, |
296 | const struct sk_buff *skb, | ||
218 | struct ip_vs_protocol *pp, | 297 | struct ip_vs_protocol *pp, |
219 | const struct iphdr *iph, | 298 | const struct ip_vs_iphdr *iph, |
220 | unsigned int proto_off, | 299 | unsigned int proto_off, |
221 | int inverse); | 300 | int inverse); |
222 | 301 | ||
@@ -226,7 +305,8 @@ struct ip_vs_protocol { | |||
226 | int (*dnat_handler)(struct sk_buff *skb, | 305 | int (*dnat_handler)(struct sk_buff *skb, |
227 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp); | 306 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp); |
228 | 307 | ||
229 | int (*csum_check)(struct sk_buff *skb, struct ip_vs_protocol *pp); | 308 | int (*csum_check)(int af, struct sk_buff *skb, |
309 | struct ip_vs_protocol *pp); | ||
230 | 310 | ||
231 | const char *(*state_name)(int state); | 311 | const char *(*state_name)(int state); |
232 | 312 | ||
@@ -259,9 +339,10 @@ struct ip_vs_conn { | |||
259 | struct list_head c_list; /* hashed list heads */ | 339 | struct list_head c_list; /* hashed list heads */ |
260 | 340 | ||
261 | /* Protocol, addresses and port numbers */ | 341 | /* Protocol, addresses and port numbers */ |
262 | __be32 caddr; /* client address */ | 342 | u16 af; /* address family */ |
263 | __be32 vaddr; /* virtual address */ | 343 | union nf_inet_addr caddr; /* client address */ |
264 | __be32 daddr; /* destination address */ | 344 | union nf_inet_addr vaddr; /* virtual address */ |
345 | union nf_inet_addr daddr; /* destination address */ | ||
265 | __be16 cport; | 346 | __be16 cport; |
266 | __be16 vport; | 347 | __be16 vport; |
267 | __be16 dport; | 348 | __be16 dport; |
@@ -305,6 +386,45 @@ struct ip_vs_conn { | |||
305 | 386 | ||
306 | 387 | ||
307 | /* | 388 | /* |
389 | * Extended internal versions of struct ip_vs_service_user and | ||
390 | * ip_vs_dest_user for IPv6 support. | ||
391 | * | ||
392 | * We need these to conveniently pass around service and destination | ||
393 | * options, but unfortunately, we also need to keep the old definitions to | ||
394 | * maintain userspace backwards compatibility for the setsockopt interface. | ||
395 | */ | ||
396 | struct ip_vs_service_user_kern { | ||
397 | /* virtual service addresses */ | ||
398 | u16 af; | ||
399 | u16 protocol; | ||
400 | union nf_inet_addr addr; /* virtual ip address */ | ||
401 | u16 port; | ||
402 | u32 fwmark; /* firwall mark of service */ | ||
403 | |||
404 | /* virtual service options */ | ||
405 | char *sched_name; | ||
406 | unsigned flags; /* virtual service flags */ | ||
407 | unsigned timeout; /* persistent timeout in sec */ | ||
408 | u32 netmask; /* persistent netmask */ | ||
409 | }; | ||
410 | |||
411 | |||
412 | struct ip_vs_dest_user_kern { | ||
413 | /* destination server address */ | ||
414 | union nf_inet_addr addr; | ||
415 | u16 port; | ||
416 | |||
417 | /* real server options */ | ||
418 | unsigned conn_flags; /* connection flags */ | ||
419 | int weight; /* destination weight */ | ||
420 | |||
421 | /* thresholds for active connections */ | ||
422 | u32 u_threshold; /* upper threshold */ | ||
423 | u32 l_threshold; /* lower threshold */ | ||
424 | }; | ||
425 | |||
426 | |||
427 | /* | ||
308 | * The information about the virtual service offered to the net | 428 | * The information about the virtual service offered to the net |
309 | * and the forwarding entries | 429 | * and the forwarding entries |
310 | */ | 430 | */ |
@@ -314,8 +434,9 @@ struct ip_vs_service { | |||
314 | atomic_t refcnt; /* reference counter */ | 434 | atomic_t refcnt; /* reference counter */ |
315 | atomic_t usecnt; /* use counter */ | 435 | atomic_t usecnt; /* use counter */ |
316 | 436 | ||
437 | u16 af; /* address family */ | ||
317 | __u16 protocol; /* which protocol (TCP/UDP) */ | 438 | __u16 protocol; /* which protocol (TCP/UDP) */ |
318 | __be32 addr; /* IP address for virtual service */ | 439 | union nf_inet_addr addr; /* IP address for virtual service */ |
319 | __be16 port; /* port number for the service */ | 440 | __be16 port; /* port number for the service */ |
320 | __u32 fwmark; /* firewall mark of the service */ | 441 | __u32 fwmark; /* firewall mark of the service */ |
321 | unsigned flags; /* service status flags */ | 442 | unsigned flags; /* service status flags */ |
@@ -342,7 +463,8 @@ struct ip_vs_dest { | |||
342 | struct list_head n_list; /* for the dests in the service */ | 463 | struct list_head n_list; /* for the dests in the service */ |
343 | struct list_head d_list; /* for table with all the dests */ | 464 | struct list_head d_list; /* for table with all the dests */ |
344 | 465 | ||
345 | __be32 addr; /* IP address of the server */ | 466 | u16 af; /* address family */ |
467 | union nf_inet_addr addr; /* IP address of the server */ | ||
346 | __be16 port; /* port number of the server */ | 468 | __be16 port; /* port number of the server */ |
347 | volatile unsigned flags; /* dest status flags */ | 469 | volatile unsigned flags; /* dest status flags */ |
348 | atomic_t conn_flags; /* flags to copy to conn */ | 470 | atomic_t conn_flags; /* flags to copy to conn */ |
@@ -366,7 +488,7 @@ struct ip_vs_dest { | |||
366 | /* for virtual service */ | 488 | /* for virtual service */ |
367 | struct ip_vs_service *svc; /* service it belongs to */ | 489 | struct ip_vs_service *svc; /* service it belongs to */ |
368 | __u16 protocol; /* which protocol (TCP/UDP) */ | 490 | __u16 protocol; /* which protocol (TCP/UDP) */ |
369 | __be32 vaddr; /* virtual IP address */ | 491 | union nf_inet_addr vaddr; /* virtual IP address */ |
370 | __be16 vport; /* virtual port number */ | 492 | __be16 vport; /* virtual port number */ |
371 | __u32 vfwmark; /* firewall mark of service */ | 493 | __u32 vfwmark; /* firewall mark of service */ |
372 | }; | 494 | }; |
@@ -380,6 +502,9 @@ struct ip_vs_scheduler { | |||
380 | char *name; /* scheduler name */ | 502 | char *name; /* scheduler name */ |
381 | atomic_t refcnt; /* reference counter */ | 503 | atomic_t refcnt; /* reference counter */ |
382 | struct module *module; /* THIS_MODULE/NULL */ | 504 | struct module *module; /* THIS_MODULE/NULL */ |
505 | #ifdef CONFIG_IP_VS_IPV6 | ||
506 | int supports_ipv6; /* scheduler has IPv6 support */ | ||
507 | #endif | ||
383 | 508 | ||
384 | /* scheduler initializing service */ | 509 | /* scheduler initializing service */ |
385 | int (*init_service)(struct ip_vs_service *svc); | 510 | int (*init_service)(struct ip_vs_service *svc); |
@@ -479,16 +604,8 @@ extern void ip_vs_init_hash_table(struct list_head *table, int rows); | |||
479 | #ifndef CONFIG_IP_VS_TAB_BITS | 604 | #ifndef CONFIG_IP_VS_TAB_BITS |
480 | #define CONFIG_IP_VS_TAB_BITS 12 | 605 | #define CONFIG_IP_VS_TAB_BITS 12 |
481 | #endif | 606 | #endif |
482 | /* make sure that IP_VS_CONN_TAB_BITS is located in [8, 20] */ | 607 | |
483 | #if CONFIG_IP_VS_TAB_BITS < 8 | ||
484 | #define IP_VS_CONN_TAB_BITS 8 | ||
485 | #endif | ||
486 | #if CONFIG_IP_VS_TAB_BITS > 20 | ||
487 | #define IP_VS_CONN_TAB_BITS 20 | ||
488 | #endif | ||
489 | #if 8 <= CONFIG_IP_VS_TAB_BITS && CONFIG_IP_VS_TAB_BITS <= 20 | ||
490 | #define IP_VS_CONN_TAB_BITS CONFIG_IP_VS_TAB_BITS | 608 | #define IP_VS_CONN_TAB_BITS CONFIG_IP_VS_TAB_BITS |
491 | #endif | ||
492 | #define IP_VS_CONN_TAB_SIZE (1 << IP_VS_CONN_TAB_BITS) | 609 | #define IP_VS_CONN_TAB_SIZE (1 << IP_VS_CONN_TAB_BITS) |
493 | #define IP_VS_CONN_TAB_MASK (IP_VS_CONN_TAB_SIZE - 1) | 610 | #define IP_VS_CONN_TAB_MASK (IP_VS_CONN_TAB_SIZE - 1) |
494 | 611 | ||
@@ -500,11 +617,16 @@ enum { | |||
500 | }; | 617 | }; |
501 | 618 | ||
502 | extern struct ip_vs_conn *ip_vs_conn_in_get | 619 | extern struct ip_vs_conn *ip_vs_conn_in_get |
503 | (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port); | 620 | (int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port, |
621 | const union nf_inet_addr *d_addr, __be16 d_port); | ||
622 | |||
504 | extern struct ip_vs_conn *ip_vs_ct_in_get | 623 | extern struct ip_vs_conn *ip_vs_ct_in_get |
505 | (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port); | 624 | (int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port, |
625 | const union nf_inet_addr *d_addr, __be16 d_port); | ||
626 | |||
506 | extern struct ip_vs_conn *ip_vs_conn_out_get | 627 | extern struct ip_vs_conn *ip_vs_conn_out_get |
507 | (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port); | 628 | (int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port, |
629 | const union nf_inet_addr *d_addr, __be16 d_port); | ||
508 | 630 | ||
509 | /* put back the conn without restarting its timer */ | 631 | /* put back the conn without restarting its timer */ |
510 | static inline void __ip_vs_conn_put(struct ip_vs_conn *cp) | 632 | static inline void __ip_vs_conn_put(struct ip_vs_conn *cp) |
@@ -515,8 +637,9 @@ extern void ip_vs_conn_put(struct ip_vs_conn *cp); | |||
515 | extern void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport); | 637 | extern void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport); |
516 | 638 | ||
517 | extern struct ip_vs_conn * | 639 | extern struct ip_vs_conn * |
518 | ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport, | 640 | ip_vs_conn_new(int af, int proto, const union nf_inet_addr *caddr, __be16 cport, |
519 | __be32 daddr, __be16 dport, unsigned flags, | 641 | const union nf_inet_addr *vaddr, __be16 vport, |
642 | const union nf_inet_addr *daddr, __be16 dport, unsigned flags, | ||
520 | struct ip_vs_dest *dest); | 643 | struct ip_vs_dest *dest); |
521 | extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp); | 644 | extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp); |
522 | 645 | ||
@@ -532,24 +655,32 @@ static inline void ip_vs_control_del(struct ip_vs_conn *cp) | |||
532 | { | 655 | { |
533 | struct ip_vs_conn *ctl_cp = cp->control; | 656 | struct ip_vs_conn *ctl_cp = cp->control; |
534 | if (!ctl_cp) { | 657 | if (!ctl_cp) { |
535 | IP_VS_ERR("request control DEL for uncontrolled: " | 658 | IP_VS_ERR_BUF("request control DEL for uncontrolled: " |
536 | "%d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n", | 659 | "%s:%d to %s:%d\n", |
537 | NIPQUAD(cp->caddr),ntohs(cp->cport), | 660 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
538 | NIPQUAD(cp->vaddr),ntohs(cp->vport)); | 661 | ntohs(cp->cport), |
662 | IP_VS_DBG_ADDR(cp->af, &cp->vaddr), | ||
663 | ntohs(cp->vport)); | ||
664 | |||
539 | return; | 665 | return; |
540 | } | 666 | } |
541 | 667 | ||
542 | IP_VS_DBG(7, "DELeting control for: " | 668 | IP_VS_DBG_BUF(7, "DELeting control for: " |
543 | "cp.dst=%d.%d.%d.%d:%d ctl_cp.dst=%d.%d.%d.%d:%d\n", | 669 | "cp.dst=%s:%d ctl_cp.dst=%s:%d\n", |
544 | NIPQUAD(cp->caddr),ntohs(cp->cport), | 670 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
545 | NIPQUAD(ctl_cp->caddr),ntohs(ctl_cp->cport)); | 671 | ntohs(cp->cport), |
672 | IP_VS_DBG_ADDR(cp->af, &ctl_cp->caddr), | ||
673 | ntohs(ctl_cp->cport)); | ||
546 | 674 | ||
547 | cp->control = NULL; | 675 | cp->control = NULL; |
548 | if (atomic_read(&ctl_cp->n_control) == 0) { | 676 | if (atomic_read(&ctl_cp->n_control) == 0) { |
549 | IP_VS_ERR("BUG control DEL with n=0 : " | 677 | IP_VS_ERR_BUF("BUG control DEL with n=0 : " |
550 | "%d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n", | 678 | "%s:%d to %s:%d\n", |
551 | NIPQUAD(cp->caddr),ntohs(cp->cport), | 679 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
552 | NIPQUAD(cp->vaddr),ntohs(cp->vport)); | 680 | ntohs(cp->cport), |
681 | IP_VS_DBG_ADDR(cp->af, &cp->vaddr), | ||
682 | ntohs(cp->vport)); | ||
683 | |||
553 | return; | 684 | return; |
554 | } | 685 | } |
555 | atomic_dec(&ctl_cp->n_control); | 686 | atomic_dec(&ctl_cp->n_control); |
@@ -559,17 +690,22 @@ static inline void | |||
559 | ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp) | 690 | ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp) |
560 | { | 691 | { |
561 | if (cp->control) { | 692 | if (cp->control) { |
562 | IP_VS_ERR("request control ADD for already controlled: " | 693 | IP_VS_ERR_BUF("request control ADD for already controlled: " |
563 | "%d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n", | 694 | "%s:%d to %s:%d\n", |
564 | NIPQUAD(cp->caddr),ntohs(cp->cport), | 695 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
565 | NIPQUAD(cp->vaddr),ntohs(cp->vport)); | 696 | ntohs(cp->cport), |
697 | IP_VS_DBG_ADDR(cp->af, &cp->vaddr), | ||
698 | ntohs(cp->vport)); | ||
699 | |||
566 | ip_vs_control_del(cp); | 700 | ip_vs_control_del(cp); |
567 | } | 701 | } |
568 | 702 | ||
569 | IP_VS_DBG(7, "ADDing control for: " | 703 | IP_VS_DBG_BUF(7, "ADDing control for: " |
570 | "cp.dst=%d.%d.%d.%d:%d ctl_cp.dst=%d.%d.%d.%d:%d\n", | 704 | "cp.dst=%s:%d ctl_cp.dst=%s:%d\n", |
571 | NIPQUAD(cp->caddr),ntohs(cp->cport), | 705 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
572 | NIPQUAD(ctl_cp->caddr),ntohs(ctl_cp->cport)); | 706 | ntohs(cp->cport), |
707 | IP_VS_DBG_ADDR(cp->af, &ctl_cp->caddr), | ||
708 | ntohs(ctl_cp->cport)); | ||
573 | 709 | ||
574 | cp->control = ctl_cp; | 710 | cp->control = ctl_cp; |
575 | atomic_inc(&ctl_cp->n_control); | 711 | atomic_inc(&ctl_cp->n_control); |
@@ -647,7 +783,8 @@ extern struct ip_vs_stats ip_vs_stats; | |||
647 | extern const struct ctl_path net_vs_ctl_path[]; | 783 | extern const struct ctl_path net_vs_ctl_path[]; |
648 | 784 | ||
649 | extern struct ip_vs_service * | 785 | extern struct ip_vs_service * |
650 | ip_vs_service_get(__u32 fwmark, __u16 protocol, __be32 vaddr, __be16 vport); | 786 | ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, |
787 | const union nf_inet_addr *vaddr, __be16 vport); | ||
651 | 788 | ||
652 | static inline void ip_vs_service_put(struct ip_vs_service *svc) | 789 | static inline void ip_vs_service_put(struct ip_vs_service *svc) |
653 | { | 790 | { |
@@ -655,14 +792,16 @@ static inline void ip_vs_service_put(struct ip_vs_service *svc) | |||
655 | } | 792 | } |
656 | 793 | ||
657 | extern struct ip_vs_dest * | 794 | extern struct ip_vs_dest * |
658 | ip_vs_lookup_real_service(__u16 protocol, __be32 daddr, __be16 dport); | 795 | ip_vs_lookup_real_service(int af, __u16 protocol, |
796 | const union nf_inet_addr *daddr, __be16 dport); | ||
797 | |||
659 | extern int ip_vs_use_count_inc(void); | 798 | extern int ip_vs_use_count_inc(void); |
660 | extern void ip_vs_use_count_dec(void); | 799 | extern void ip_vs_use_count_dec(void); |
661 | extern int ip_vs_control_init(void); | 800 | extern int ip_vs_control_init(void); |
662 | extern void ip_vs_control_cleanup(void); | 801 | extern void ip_vs_control_cleanup(void); |
663 | extern struct ip_vs_dest * | 802 | extern struct ip_vs_dest * |
664 | ip_vs_find_dest(__be32 daddr, __be16 dport, | 803 | ip_vs_find_dest(int af, const union nf_inet_addr *daddr, __be16 dport, |
665 | __be32 vaddr, __be16 vport, __u16 protocol); | 804 | const union nf_inet_addr *vaddr, __be16 vport, __u16 protocol); |
666 | extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp); | 805 | extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp); |
667 | 806 | ||
668 | 807 | ||
@@ -706,6 +845,19 @@ extern int ip_vs_icmp_xmit | |||
706 | (struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, int offset); | 845 | (struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, int offset); |
707 | extern void ip_vs_dst_reset(struct ip_vs_dest *dest); | 846 | extern void ip_vs_dst_reset(struct ip_vs_dest *dest); |
708 | 847 | ||
848 | #ifdef CONFIG_IP_VS_IPV6 | ||
849 | extern int ip_vs_bypass_xmit_v6 | ||
850 | (struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp); | ||
851 | extern int ip_vs_nat_xmit_v6 | ||
852 | (struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp); | ||
853 | extern int ip_vs_tunnel_xmit_v6 | ||
854 | (struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp); | ||
855 | extern int ip_vs_dr_xmit_v6 | ||
856 | (struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp); | ||
857 | extern int ip_vs_icmp_xmit_v6 | ||
858 | (struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, | ||
859 | int offset); | ||
860 | #endif | ||
709 | 861 | ||
710 | /* | 862 | /* |
711 | * This is a simple mechanism to ignore packets when | 863 | * This is a simple mechanism to ignore packets when |
@@ -750,7 +902,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) | |||
750 | } | 902 | } |
751 | 903 | ||
752 | extern void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, | 904 | extern void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, |
753 | struct ip_vs_conn *cp, int dir); | 905 | struct ip_vs_conn *cp, int dir); |
906 | |||
907 | #ifdef CONFIG_IP_VS_IPV6 | ||
908 | extern void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, | ||
909 | struct ip_vs_conn *cp, int dir); | ||
910 | #endif | ||
754 | 911 | ||
755 | extern __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); | 912 | extern __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); |
756 | 913 | ||
@@ -761,6 +918,17 @@ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) | |||
761 | return csum_partial((char *) diff, sizeof(diff), oldsum); | 918 | return csum_partial((char *) diff, sizeof(diff), oldsum); |
762 | } | 919 | } |
763 | 920 | ||
921 | #ifdef CONFIG_IP_VS_IPV6 | ||
922 | static inline __wsum ip_vs_check_diff16(const __be32 *old, const __be32 *new, | ||
923 | __wsum oldsum) | ||
924 | { | ||
925 | __be32 diff[8] = { ~old[3], ~old[2], ~old[1], ~old[0], | ||
926 | new[3], new[2], new[1], new[0] }; | ||
927 | |||
928 | return csum_partial((char *) diff, sizeof(diff), oldsum); | ||
929 | } | ||
930 | #endif | ||
931 | |||
764 | static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) | 932 | static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) |
765 | { | 933 | { |
766 | __be16 diff[2] = { ~old, new }; | 934 | __be16 diff[2] = { ~old, new }; |
diff --git a/net/ipv4/ipvs/Kconfig b/net/ipv4/ipvs/Kconfig index 2e48a7e27223..de6004de80bc 100644 --- a/net/ipv4/ipvs/Kconfig +++ b/net/ipv4/ipvs/Kconfig | |||
@@ -24,6 +24,14 @@ menuconfig IP_VS | |||
24 | 24 | ||
25 | if IP_VS | 25 | if IP_VS |
26 | 26 | ||
27 | config IP_VS_IPV6 | ||
28 | bool "IPv6 support for IPVS (DANGEROUS)" | ||
29 | depends on EXPERIMENTAL && (IPV6 = y || IP_VS = IPV6) | ||
30 | ---help--- | ||
31 | Add IPv6 support to IPVS. This is incomplete and might be dangerous. | ||
32 | |||
33 | Say N if unsure. | ||
34 | |||
27 | config IP_VS_DEBUG | 35 | config IP_VS_DEBUG |
28 | bool "IP virtual server debugging" | 36 | bool "IP virtual server debugging" |
29 | ---help--- | 37 | ---help--- |
@@ -33,7 +41,8 @@ config IP_VS_DEBUG | |||
33 | 41 | ||
34 | config IP_VS_TAB_BITS | 42 | config IP_VS_TAB_BITS |
35 | int "IPVS connection table size (the Nth power of 2)" | 43 | int "IPVS connection table size (the Nth power of 2)" |
36 | default "12" | 44 | range 8 20 |
45 | default 12 | ||
37 | ---help--- | 46 | ---help--- |
38 | The IPVS connection hash table uses the chaining scheme to handle | 47 | The IPVS connection hash table uses the chaining scheme to handle |
39 | hash collisions. Using a big IPVS connection hash table will greatly | 48 | hash collisions. Using a big IPVS connection hash table will greatly |
diff --git a/net/ipv4/ipvs/ip_vs_conn.c b/net/ipv4/ipvs/ip_vs_conn.c index 44a6872dc245..9a24332fbed8 100644 --- a/net/ipv4/ipvs/ip_vs_conn.c +++ b/net/ipv4/ipvs/ip_vs_conn.c | |||
@@ -114,9 +114,18 @@ static inline void ct_write_unlock_bh(unsigned key) | |||
114 | /* | 114 | /* |
115 | * Returns hash value for IPVS connection entry | 115 | * Returns hash value for IPVS connection entry |
116 | */ | 116 | */ |
117 | static unsigned int ip_vs_conn_hashkey(unsigned proto, __be32 addr, __be16 port) | 117 | static unsigned int ip_vs_conn_hashkey(int af, unsigned proto, |
118 | const union nf_inet_addr *addr, | ||
119 | __be16 port) | ||
118 | { | 120 | { |
119 | return jhash_3words((__force u32)addr, (__force u32)port, proto, ip_vs_conn_rnd) | 121 | #ifdef CONFIG_IP_VS_IPV6 |
122 | if (af == AF_INET6) | ||
123 | return jhash_3words(jhash(addr, 16, ip_vs_conn_rnd), | ||
124 | (__force u32)port, proto, ip_vs_conn_rnd) | ||
125 | & IP_VS_CONN_TAB_MASK; | ||
126 | #endif | ||
127 | return jhash_3words((__force u32)addr->ip, (__force u32)port, proto, | ||
128 | ip_vs_conn_rnd) | ||
120 | & IP_VS_CONN_TAB_MASK; | 129 | & IP_VS_CONN_TAB_MASK; |
121 | } | 130 | } |
122 | 131 | ||
@@ -131,7 +140,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) | |||
131 | int ret; | 140 | int ret; |
132 | 141 | ||
133 | /* Hash by protocol, client address and port */ | 142 | /* Hash by protocol, client address and port */ |
134 | hash = ip_vs_conn_hashkey(cp->protocol, cp->caddr, cp->cport); | 143 | hash = ip_vs_conn_hashkey(cp->af, cp->protocol, &cp->caddr, cp->cport); |
135 | 144 | ||
136 | ct_write_lock(hash); | 145 | ct_write_lock(hash); |
137 | 146 | ||
@@ -162,7 +171,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) | |||
162 | int ret; | 171 | int ret; |
163 | 172 | ||
164 | /* unhash it and decrease its reference counter */ | 173 | /* unhash it and decrease its reference counter */ |
165 | hash = ip_vs_conn_hashkey(cp->protocol, cp->caddr, cp->cport); | 174 | hash = ip_vs_conn_hashkey(cp->af, cp->protocol, &cp->caddr, cp->cport); |
166 | 175 | ||
167 | ct_write_lock(hash); | 176 | ct_write_lock(hash); |
168 | 177 | ||
@@ -187,20 +196,23 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) | |||
187 | * d_addr, d_port: pkt dest address (load balancer) | 196 | * d_addr, d_port: pkt dest address (load balancer) |
188 | */ | 197 | */ |
189 | static inline struct ip_vs_conn *__ip_vs_conn_in_get | 198 | static inline struct ip_vs_conn *__ip_vs_conn_in_get |
190 | (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port) | 199 | (int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port, |
200 | const union nf_inet_addr *d_addr, __be16 d_port) | ||
191 | { | 201 | { |
192 | unsigned hash; | 202 | unsigned hash; |
193 | struct ip_vs_conn *cp; | 203 | struct ip_vs_conn *cp; |
194 | 204 | ||
195 | hash = ip_vs_conn_hashkey(protocol, s_addr, s_port); | 205 | hash = ip_vs_conn_hashkey(af, protocol, s_addr, s_port); |
196 | 206 | ||
197 | ct_read_lock(hash); | 207 | ct_read_lock(hash); |
198 | 208 | ||
199 | list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { | 209 | list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { |
200 | if (s_addr==cp->caddr && s_port==cp->cport && | 210 | if (cp->af == af && |
201 | d_port==cp->vport && d_addr==cp->vaddr && | 211 | ip_vs_addr_equal(af, s_addr, &cp->caddr) && |
212 | ip_vs_addr_equal(af, d_addr, &cp->vaddr) && | ||
213 | s_port == cp->cport && d_port == cp->vport && | ||
202 | ((!s_port) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) && | 214 | ((!s_port) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) && |
203 | protocol==cp->protocol) { | 215 | protocol == cp->protocol) { |
204 | /* HIT */ | 216 | /* HIT */ |
205 | atomic_inc(&cp->refcnt); | 217 | atomic_inc(&cp->refcnt); |
206 | ct_read_unlock(hash); | 218 | ct_read_unlock(hash); |
@@ -214,39 +226,44 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get | |||
214 | } | 226 | } |
215 | 227 | ||
216 | struct ip_vs_conn *ip_vs_conn_in_get | 228 | struct ip_vs_conn *ip_vs_conn_in_get |
217 | (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port) | 229 | (int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port, |
230 | const union nf_inet_addr *d_addr, __be16 d_port) | ||
218 | { | 231 | { |
219 | struct ip_vs_conn *cp; | 232 | struct ip_vs_conn *cp; |
220 | 233 | ||
221 | cp = __ip_vs_conn_in_get(protocol, s_addr, s_port, d_addr, d_port); | 234 | cp = __ip_vs_conn_in_get(af, protocol, s_addr, s_port, d_addr, d_port); |
222 | if (!cp && atomic_read(&ip_vs_conn_no_cport_cnt)) | 235 | if (!cp && atomic_read(&ip_vs_conn_no_cport_cnt)) |
223 | cp = __ip_vs_conn_in_get(protocol, s_addr, 0, d_addr, d_port); | 236 | cp = __ip_vs_conn_in_get(af, protocol, s_addr, 0, d_addr, |
237 | d_port); | ||
224 | 238 | ||
225 | IP_VS_DBG(9, "lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n", | 239 | IP_VS_DBG_BUF(9, "lookup/in %s %s:%d->%s:%d %s\n", |
226 | ip_vs_proto_name(protocol), | 240 | ip_vs_proto_name(protocol), |
227 | NIPQUAD(s_addr), ntohs(s_port), | 241 | IP_VS_DBG_ADDR(af, s_addr), ntohs(s_port), |
228 | NIPQUAD(d_addr), ntohs(d_port), | 242 | IP_VS_DBG_ADDR(af, d_addr), ntohs(d_port), |
229 | cp?"hit":"not hit"); | 243 | cp ? "hit" : "not hit"); |
230 | 244 | ||
231 | return cp; | 245 | return cp; |
232 | } | 246 | } |
233 | 247 | ||
234 | /* Get reference to connection template */ | 248 | /* Get reference to connection template */ |
235 | struct ip_vs_conn *ip_vs_ct_in_get | 249 | struct ip_vs_conn *ip_vs_ct_in_get |
236 | (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port) | 250 | (int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port, |
251 | const union nf_inet_addr *d_addr, __be16 d_port) | ||
237 | { | 252 | { |
238 | unsigned hash; | 253 | unsigned hash; |
239 | struct ip_vs_conn *cp; | 254 | struct ip_vs_conn *cp; |
240 | 255 | ||
241 | hash = ip_vs_conn_hashkey(protocol, s_addr, s_port); | 256 | hash = ip_vs_conn_hashkey(af, protocol, s_addr, s_port); |
242 | 257 | ||
243 | ct_read_lock(hash); | 258 | ct_read_lock(hash); |
244 | 259 | ||
245 | list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { | 260 | list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { |
246 | if (s_addr==cp->caddr && s_port==cp->cport && | 261 | if (cp->af == af && |
247 | d_port==cp->vport && d_addr==cp->vaddr && | 262 | ip_vs_addr_equal(af, s_addr, &cp->caddr) && |
263 | ip_vs_addr_equal(af, d_addr, &cp->vaddr) && | ||
264 | s_port == cp->cport && d_port == cp->vport && | ||
248 | cp->flags & IP_VS_CONN_F_TEMPLATE && | 265 | cp->flags & IP_VS_CONN_F_TEMPLATE && |
249 | protocol==cp->protocol) { | 266 | protocol == cp->protocol) { |
250 | /* HIT */ | 267 | /* HIT */ |
251 | atomic_inc(&cp->refcnt); | 268 | atomic_inc(&cp->refcnt); |
252 | goto out; | 269 | goto out; |
@@ -257,11 +274,11 @@ struct ip_vs_conn *ip_vs_ct_in_get | |||
257 | out: | 274 | out: |
258 | ct_read_unlock(hash); | 275 | ct_read_unlock(hash); |
259 | 276 | ||
260 | IP_VS_DBG(9, "template lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n", | 277 | IP_VS_DBG_BUF(9, "template lookup/in %s %s:%d->%s:%d %s\n", |
261 | ip_vs_proto_name(protocol), | 278 | ip_vs_proto_name(protocol), |
262 | NIPQUAD(s_addr), ntohs(s_port), | 279 | IP_VS_DBG_ADDR(af, s_addr), ntohs(s_port), |
263 | NIPQUAD(d_addr), ntohs(d_port), | 280 | IP_VS_DBG_ADDR(af, d_addr), ntohs(d_port), |
264 | cp?"hit":"not hit"); | 281 | cp ? "hit" : "not hit"); |
265 | 282 | ||
266 | return cp; | 283 | return cp; |
267 | } | 284 | } |
@@ -273,7 +290,8 @@ struct ip_vs_conn *ip_vs_ct_in_get | |||
273 | * d_addr, d_port: pkt dest address (foreign host) | 290 | * d_addr, d_port: pkt dest address (foreign host) |
274 | */ | 291 | */ |
275 | struct ip_vs_conn *ip_vs_conn_out_get | 292 | struct ip_vs_conn *ip_vs_conn_out_get |
276 | (int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port) | 293 | (int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port, |
294 | const union nf_inet_addr *d_addr, __be16 d_port) | ||
277 | { | 295 | { |
278 | unsigned hash; | 296 | unsigned hash; |
279 | struct ip_vs_conn *cp, *ret=NULL; | 297 | struct ip_vs_conn *cp, *ret=NULL; |
@@ -281,13 +299,15 @@ struct ip_vs_conn *ip_vs_conn_out_get | |||
281 | /* | 299 | /* |
282 | * Check for "full" addressed entries | 300 | * Check for "full" addressed entries |
283 | */ | 301 | */ |
284 | hash = ip_vs_conn_hashkey(protocol, d_addr, d_port); | 302 | hash = ip_vs_conn_hashkey(af, protocol, d_addr, d_port); |
285 | 303 | ||
286 | ct_read_lock(hash); | 304 | ct_read_lock(hash); |
287 | 305 | ||
288 | list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { | 306 | list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { |
289 | if (d_addr == cp->caddr && d_port == cp->cport && | 307 | if (cp->af == af && |
290 | s_port == cp->dport && s_addr == cp->daddr && | 308 | ip_vs_addr_equal(af, d_addr, &cp->caddr) && |
309 | ip_vs_addr_equal(af, s_addr, &cp->daddr) && | ||
310 | d_port == cp->cport && s_port == cp->dport && | ||
291 | protocol == cp->protocol) { | 311 | protocol == cp->protocol) { |
292 | /* HIT */ | 312 | /* HIT */ |
293 | atomic_inc(&cp->refcnt); | 313 | atomic_inc(&cp->refcnt); |
@@ -298,11 +318,11 @@ struct ip_vs_conn *ip_vs_conn_out_get | |||
298 | 318 | ||
299 | ct_read_unlock(hash); | 319 | ct_read_unlock(hash); |
300 | 320 | ||
301 | IP_VS_DBG(9, "lookup/out %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n", | 321 | IP_VS_DBG_BUF(9, "lookup/out %s %s:%d->%s:%d %s\n", |
302 | ip_vs_proto_name(protocol), | 322 | ip_vs_proto_name(protocol), |
303 | NIPQUAD(s_addr), ntohs(s_port), | 323 | IP_VS_DBG_ADDR(af, s_addr), ntohs(s_port), |
304 | NIPQUAD(d_addr), ntohs(d_port), | 324 | IP_VS_DBG_ADDR(af, d_addr), ntohs(d_port), |
305 | ret?"hit":"not hit"); | 325 | ret ? "hit" : "not hit"); |
306 | 326 | ||
307 | return ret; | 327 | return ret; |
308 | } | 328 | } |
@@ -369,6 +389,33 @@ static inline void ip_vs_bind_xmit(struct ip_vs_conn *cp) | |||
369 | } | 389 | } |
370 | } | 390 | } |
371 | 391 | ||
392 | #ifdef CONFIG_IP_VS_IPV6 | ||
393 | static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp) | ||
394 | { | ||
395 | switch (IP_VS_FWD_METHOD(cp)) { | ||
396 | case IP_VS_CONN_F_MASQ: | ||
397 | cp->packet_xmit = ip_vs_nat_xmit_v6; | ||
398 | break; | ||
399 | |||
400 | case IP_VS_CONN_F_TUNNEL: | ||
401 | cp->packet_xmit = ip_vs_tunnel_xmit_v6; | ||
402 | break; | ||
403 | |||
404 | case IP_VS_CONN_F_DROUTE: | ||
405 | cp->packet_xmit = ip_vs_dr_xmit_v6; | ||
406 | break; | ||
407 | |||
408 | case IP_VS_CONN_F_LOCALNODE: | ||
409 | cp->packet_xmit = ip_vs_null_xmit; | ||
410 | break; | ||
411 | |||
412 | case IP_VS_CONN_F_BYPASS: | ||
413 | cp->packet_xmit = ip_vs_bypass_xmit_v6; | ||
414 | break; | ||
415 | } | ||
416 | } | ||
417 | #endif | ||
418 | |||
372 | 419 | ||
373 | static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest) | 420 | static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest) |
374 | { | 421 | { |
@@ -402,16 +449,16 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) | |||
402 | cp->flags |= atomic_read(&dest->conn_flags); | 449 | cp->flags |= atomic_read(&dest->conn_flags); |
403 | cp->dest = dest; | 450 | cp->dest = dest; |
404 | 451 | ||
405 | IP_VS_DBG(7, "Bind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d " | 452 | IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d " |
406 | "d:%u.%u.%u.%u:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d " | 453 | "d:%s:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d " |
407 | "dest->refcnt:%d\n", | 454 | "dest->refcnt:%d\n", |
408 | ip_vs_proto_name(cp->protocol), | 455 | ip_vs_proto_name(cp->protocol), |
409 | NIPQUAD(cp->caddr), ntohs(cp->cport), | 456 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport), |
410 | NIPQUAD(cp->vaddr), ntohs(cp->vport), | 457 | IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport), |
411 | NIPQUAD(cp->daddr), ntohs(cp->dport), | 458 | IP_VS_DBG_ADDR(cp->af, &cp->daddr), ntohs(cp->dport), |
412 | ip_vs_fwd_tag(cp), cp->state, | 459 | ip_vs_fwd_tag(cp), cp->state, |
413 | cp->flags, atomic_read(&cp->refcnt), | 460 | cp->flags, atomic_read(&cp->refcnt), |
414 | atomic_read(&dest->refcnt)); | 461 | atomic_read(&dest->refcnt)); |
415 | 462 | ||
416 | /* Update the connection counters */ | 463 | /* Update the connection counters */ |
417 | if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { | 464 | if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { |
@@ -444,8 +491,9 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp) | |||
444 | struct ip_vs_dest *dest; | 491 | struct ip_vs_dest *dest; |
445 | 492 | ||
446 | if ((cp) && (!cp->dest)) { | 493 | if ((cp) && (!cp->dest)) { |
447 | dest = ip_vs_find_dest(cp->daddr, cp->dport, | 494 | dest = ip_vs_find_dest(cp->af, &cp->daddr, cp->dport, |
448 | cp->vaddr, cp->vport, cp->protocol); | 495 | &cp->vaddr, cp->vport, |
496 | cp->protocol); | ||
449 | ip_vs_bind_dest(cp, dest); | 497 | ip_vs_bind_dest(cp, dest); |
450 | return dest; | 498 | return dest; |
451 | } else | 499 | } else |
@@ -464,16 +512,16 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) | |||
464 | if (!dest) | 512 | if (!dest) |
465 | return; | 513 | return; |
466 | 514 | ||
467 | IP_VS_DBG(7, "Unbind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d " | 515 | IP_VS_DBG_BUF(7, "Unbind-dest %s c:%s:%d v:%s:%d " |
468 | "d:%u.%u.%u.%u:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d " | 516 | "d:%s:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d " |
469 | "dest->refcnt:%d\n", | 517 | "dest->refcnt:%d\n", |
470 | ip_vs_proto_name(cp->protocol), | 518 | ip_vs_proto_name(cp->protocol), |
471 | NIPQUAD(cp->caddr), ntohs(cp->cport), | 519 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport), |
472 | NIPQUAD(cp->vaddr), ntohs(cp->vport), | 520 | IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport), |
473 | NIPQUAD(cp->daddr), ntohs(cp->dport), | 521 | IP_VS_DBG_ADDR(cp->af, &cp->daddr), ntohs(cp->dport), |
474 | ip_vs_fwd_tag(cp), cp->state, | 522 | ip_vs_fwd_tag(cp), cp->state, |
475 | cp->flags, atomic_read(&cp->refcnt), | 523 | cp->flags, atomic_read(&cp->refcnt), |
476 | atomic_read(&dest->refcnt)); | 524 | atomic_read(&dest->refcnt)); |
477 | 525 | ||
478 | /* Update the connection counters */ | 526 | /* Update the connection counters */ |
479 | if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { | 527 | if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { |
@@ -526,13 +574,16 @@ int ip_vs_check_template(struct ip_vs_conn *ct) | |||
526 | !(dest->flags & IP_VS_DEST_F_AVAILABLE) || | 574 | !(dest->flags & IP_VS_DEST_F_AVAILABLE) || |
527 | (sysctl_ip_vs_expire_quiescent_template && | 575 | (sysctl_ip_vs_expire_quiescent_template && |
528 | (atomic_read(&dest->weight) == 0))) { | 576 | (atomic_read(&dest->weight) == 0))) { |
529 | IP_VS_DBG(9, "check_template: dest not available for " | 577 | IP_VS_DBG_BUF(9, "check_template: dest not available for " |
530 | "protocol %s s:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d " | 578 | "protocol %s s:%s:%d v:%s:%d " |
531 | "-> d:%u.%u.%u.%u:%d\n", | 579 | "-> d:%s:%d\n", |
532 | ip_vs_proto_name(ct->protocol), | 580 | ip_vs_proto_name(ct->protocol), |
533 | NIPQUAD(ct->caddr), ntohs(ct->cport), | 581 | IP_VS_DBG_ADDR(ct->af, &ct->caddr), |
534 | NIPQUAD(ct->vaddr), ntohs(ct->vport), | 582 | ntohs(ct->cport), |
535 | NIPQUAD(ct->daddr), ntohs(ct->dport)); | 583 | IP_VS_DBG_ADDR(ct->af, &ct->vaddr), |
584 | ntohs(ct->vport), | ||
585 | IP_VS_DBG_ADDR(ct->af, &ct->daddr), | ||
586 | ntohs(ct->dport)); | ||
536 | 587 | ||
537 | /* | 588 | /* |
538 | * Invalidate the connection template | 589 | * Invalidate the connection template |
@@ -625,8 +676,9 @@ void ip_vs_conn_expire_now(struct ip_vs_conn *cp) | |||
625 | * Create a new connection entry and hash it into the ip_vs_conn_tab | 676 | * Create a new connection entry and hash it into the ip_vs_conn_tab |
626 | */ | 677 | */ |
627 | struct ip_vs_conn * | 678 | struct ip_vs_conn * |
628 | ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport, | 679 | ip_vs_conn_new(int af, int proto, const union nf_inet_addr *caddr, __be16 cport, |
629 | __be32 daddr, __be16 dport, unsigned flags, | 680 | const union nf_inet_addr *vaddr, __be16 vport, |
681 | const union nf_inet_addr *daddr, __be16 dport, unsigned flags, | ||
630 | struct ip_vs_dest *dest) | 682 | struct ip_vs_dest *dest) |
631 | { | 683 | { |
632 | struct ip_vs_conn *cp; | 684 | struct ip_vs_conn *cp; |
@@ -640,12 +692,13 @@ ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport | |||
640 | 692 | ||
641 | INIT_LIST_HEAD(&cp->c_list); | 693 | INIT_LIST_HEAD(&cp->c_list); |
642 | setup_timer(&cp->timer, ip_vs_conn_expire, (unsigned long)cp); | 694 | setup_timer(&cp->timer, ip_vs_conn_expire, (unsigned long)cp); |
695 | cp->af = af; | ||
643 | cp->protocol = proto; | 696 | cp->protocol = proto; |
644 | cp->caddr = caddr; | 697 | ip_vs_addr_copy(af, &cp->caddr, caddr); |
645 | cp->cport = cport; | 698 | cp->cport = cport; |
646 | cp->vaddr = vaddr; | 699 | ip_vs_addr_copy(af, &cp->vaddr, vaddr); |
647 | cp->vport = vport; | 700 | cp->vport = vport; |
648 | cp->daddr = daddr; | 701 | ip_vs_addr_copy(af, &cp->daddr, daddr); |
649 | cp->dport = dport; | 702 | cp->dport = dport; |
650 | cp->flags = flags; | 703 | cp->flags = flags; |
651 | spin_lock_init(&cp->lock); | 704 | spin_lock_init(&cp->lock); |
@@ -672,7 +725,12 @@ ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport | |||
672 | cp->timeout = 3*HZ; | 725 | cp->timeout = 3*HZ; |
673 | 726 | ||
674 | /* Bind its packet transmitter */ | 727 | /* Bind its packet transmitter */ |
675 | ip_vs_bind_xmit(cp); | 728 | #ifdef CONFIG_IP_VS_IPV6 |
729 | if (af == AF_INET6) | ||
730 | ip_vs_bind_xmit_v6(cp); | ||
731 | else | ||
732 | #endif | ||
733 | ip_vs_bind_xmit(cp); | ||
676 | 734 | ||
677 | if (unlikely(pp && atomic_read(&pp->appcnt))) | 735 | if (unlikely(pp && atomic_read(&pp->appcnt))) |
678 | ip_vs_bind_app(cp, pp); | 736 | ip_vs_bind_app(cp, pp); |
@@ -760,12 +818,26 @@ static int ip_vs_conn_seq_show(struct seq_file *seq, void *v) | |||
760 | else { | 818 | else { |
761 | const struct ip_vs_conn *cp = v; | 819 | const struct ip_vs_conn *cp = v; |
762 | 820 | ||
763 | seq_printf(seq, | 821 | #ifdef CONFIG_IP_VS_IPV6 |
764 | "%-3s %08X %04X %08X %04X %08X %04X %-11s %7lu\n", | 822 | if (cp->af == AF_INET6) |
823 | seq_printf(seq, | ||
824 | "%-3s " NIP6_FMT " %04X " NIP6_FMT | ||
825 | " %04X " NIP6_FMT " %04X %-11s %7lu\n", | ||
826 | ip_vs_proto_name(cp->protocol), | ||
827 | NIP6(cp->caddr.in6), ntohs(cp->cport), | ||
828 | NIP6(cp->vaddr.in6), ntohs(cp->vport), | ||
829 | NIP6(cp->daddr.in6), ntohs(cp->dport), | ||
830 | ip_vs_state_name(cp->protocol, cp->state), | ||
831 | (cp->timer.expires-jiffies)/HZ); | ||
832 | else | ||
833 | #endif | ||
834 | seq_printf(seq, | ||
835 | "%-3s %08X %04X %08X %04X" | ||
836 | " %08X %04X %-11s %7lu\n", | ||
765 | ip_vs_proto_name(cp->protocol), | 837 | ip_vs_proto_name(cp->protocol), |
766 | ntohl(cp->caddr), ntohs(cp->cport), | 838 | ntohl(cp->caddr.ip), ntohs(cp->cport), |
767 | ntohl(cp->vaddr), ntohs(cp->vport), | 839 | ntohl(cp->vaddr.ip), ntohs(cp->vport), |
768 | ntohl(cp->daddr), ntohs(cp->dport), | 840 | ntohl(cp->daddr.ip), ntohs(cp->dport), |
769 | ip_vs_state_name(cp->protocol, cp->state), | 841 | ip_vs_state_name(cp->protocol, cp->state), |
770 | (cp->timer.expires-jiffies)/HZ); | 842 | (cp->timer.expires-jiffies)/HZ); |
771 | } | 843 | } |
@@ -809,12 +881,27 @@ static int ip_vs_conn_sync_seq_show(struct seq_file *seq, void *v) | |||
809 | else { | 881 | else { |
810 | const struct ip_vs_conn *cp = v; | 882 | const struct ip_vs_conn *cp = v; |
811 | 883 | ||
812 | seq_printf(seq, | 884 | #ifdef CONFIG_IP_VS_IPV6 |
813 | "%-3s %08X %04X %08X %04X %08X %04X %-11s %-6s %7lu\n", | 885 | if (cp->af == AF_INET6) |
886 | seq_printf(seq, | ||
887 | "%-3s " NIP6_FMT " %04X " NIP6_FMT | ||
888 | " %04X " NIP6_FMT " %04X %-11s %-6s %7lu\n", | ||
889 | ip_vs_proto_name(cp->protocol), | ||
890 | NIP6(cp->caddr.in6), ntohs(cp->cport), | ||
891 | NIP6(cp->vaddr.in6), ntohs(cp->vport), | ||
892 | NIP6(cp->daddr.in6), ntohs(cp->dport), | ||
893 | ip_vs_state_name(cp->protocol, cp->state), | ||
894 | ip_vs_origin_name(cp->flags), | ||
895 | (cp->timer.expires-jiffies)/HZ); | ||
896 | else | ||
897 | #endif | ||
898 | seq_printf(seq, | ||
899 | "%-3s %08X %04X %08X %04X " | ||
900 | "%08X %04X %-11s %-6s %7lu\n", | ||
814 | ip_vs_proto_name(cp->protocol), | 901 | ip_vs_proto_name(cp->protocol), |
815 | ntohl(cp->caddr), ntohs(cp->cport), | 902 | ntohl(cp->caddr.ip), ntohs(cp->cport), |
816 | ntohl(cp->vaddr), ntohs(cp->vport), | 903 | ntohl(cp->vaddr.ip), ntohs(cp->vport), |
817 | ntohl(cp->daddr), ntohs(cp->dport), | 904 | ntohl(cp->daddr.ip), ntohs(cp->dport), |
818 | ip_vs_state_name(cp->protocol, cp->state), | 905 | ip_vs_state_name(cp->protocol, cp->state), |
819 | ip_vs_origin_name(cp->flags), | 906 | ip_vs_origin_name(cp->flags), |
820 | (cp->timer.expires-jiffies)/HZ); | 907 | (cp->timer.expires-jiffies)/HZ); |
diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c index 9fbf0a6d7392..80a4fcf33a54 100644 --- a/net/ipv4/ipvs/ip_vs_core.c +++ b/net/ipv4/ipvs/ip_vs_core.c | |||
@@ -39,6 +39,11 @@ | |||
39 | #include <linux/netfilter.h> | 39 | #include <linux/netfilter.h> |
40 | #include <linux/netfilter_ipv4.h> | 40 | #include <linux/netfilter_ipv4.h> |
41 | 41 | ||
42 | #ifdef CONFIG_IP_VS_IPV6 | ||
43 | #include <net/ipv6.h> | ||
44 | #include <linux/netfilter_ipv6.h> | ||
45 | #endif | ||
46 | |||
42 | #include <net/ip_vs.h> | 47 | #include <net/ip_vs.h> |
43 | 48 | ||
44 | 49 | ||
@@ -60,6 +65,7 @@ EXPORT_SYMBOL(ip_vs_get_debug_level); | |||
60 | 65 | ||
61 | /* ID used in ICMP lookups */ | 66 | /* ID used in ICMP lookups */ |
62 | #define icmp_id(icmph) (((icmph)->un).echo.id) | 67 | #define icmp_id(icmph) (((icmph)->un).echo.id) |
68 | #define icmpv6_id(icmph) (icmph->icmp6_dataun.u_echo.identifier) | ||
63 | 69 | ||
64 | const char *ip_vs_proto_name(unsigned proto) | 70 | const char *ip_vs_proto_name(unsigned proto) |
65 | { | 71 | { |
@@ -74,6 +80,10 @@ const char *ip_vs_proto_name(unsigned proto) | |||
74 | return "TCP"; | 80 | return "TCP"; |
75 | case IPPROTO_ICMP: | 81 | case IPPROTO_ICMP: |
76 | return "ICMP"; | 82 | return "ICMP"; |
83 | #ifdef CONFIG_IP_VS_IPV6 | ||
84 | case IPPROTO_ICMPV6: | ||
85 | return "ICMPv6"; | ||
86 | #endif | ||
77 | default: | 87 | default: |
78 | sprintf(buf, "IP_%d", proto); | 88 | sprintf(buf, "IP_%d", proto); |
79 | return buf; | 89 | return buf; |
@@ -92,18 +102,18 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) | |||
92 | struct ip_vs_dest *dest = cp->dest; | 102 | struct ip_vs_dest *dest = cp->dest; |
93 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { | 103 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { |
94 | spin_lock(&dest->stats.lock); | 104 | spin_lock(&dest->stats.lock); |
95 | dest->stats.inpkts++; | 105 | dest->stats.ustats.inpkts++; |
96 | dest->stats.inbytes += skb->len; | 106 | dest->stats.ustats.inbytes += skb->len; |
97 | spin_unlock(&dest->stats.lock); | 107 | spin_unlock(&dest->stats.lock); |
98 | 108 | ||
99 | spin_lock(&dest->svc->stats.lock); | 109 | spin_lock(&dest->svc->stats.lock); |
100 | dest->svc->stats.inpkts++; | 110 | dest->svc->stats.ustats.inpkts++; |
101 | dest->svc->stats.inbytes += skb->len; | 111 | dest->svc->stats.ustats.inbytes += skb->len; |
102 | spin_unlock(&dest->svc->stats.lock); | 112 | spin_unlock(&dest->svc->stats.lock); |
103 | 113 | ||
104 | spin_lock(&ip_vs_stats.lock); | 114 | spin_lock(&ip_vs_stats.lock); |
105 | ip_vs_stats.inpkts++; | 115 | ip_vs_stats.ustats.inpkts++; |
106 | ip_vs_stats.inbytes += skb->len; | 116 | ip_vs_stats.ustats.inbytes += skb->len; |
107 | spin_unlock(&ip_vs_stats.lock); | 117 | spin_unlock(&ip_vs_stats.lock); |
108 | } | 118 | } |
109 | } | 119 | } |
@@ -115,18 +125,18 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) | |||
115 | struct ip_vs_dest *dest = cp->dest; | 125 | struct ip_vs_dest *dest = cp->dest; |
116 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { | 126 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { |
117 | spin_lock(&dest->stats.lock); | 127 | spin_lock(&dest->stats.lock); |
118 | dest->stats.outpkts++; | 128 | dest->stats.ustats.outpkts++; |
119 | dest->stats.outbytes += skb->len; | 129 | dest->stats.ustats.outbytes += skb->len; |
120 | spin_unlock(&dest->stats.lock); | 130 | spin_unlock(&dest->stats.lock); |
121 | 131 | ||
122 | spin_lock(&dest->svc->stats.lock); | 132 | spin_lock(&dest->svc->stats.lock); |
123 | dest->svc->stats.outpkts++; | 133 | dest->svc->stats.ustats.outpkts++; |
124 | dest->svc->stats.outbytes += skb->len; | 134 | dest->svc->stats.ustats.outbytes += skb->len; |
125 | spin_unlock(&dest->svc->stats.lock); | 135 | spin_unlock(&dest->svc->stats.lock); |
126 | 136 | ||
127 | spin_lock(&ip_vs_stats.lock); | 137 | spin_lock(&ip_vs_stats.lock); |
128 | ip_vs_stats.outpkts++; | 138 | ip_vs_stats.ustats.outpkts++; |
129 | ip_vs_stats.outbytes += skb->len; | 139 | ip_vs_stats.ustats.outbytes += skb->len; |
130 | spin_unlock(&ip_vs_stats.lock); | 140 | spin_unlock(&ip_vs_stats.lock); |
131 | } | 141 | } |
132 | } | 142 | } |
@@ -136,15 +146,15 @@ static inline void | |||
136 | ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) | 146 | ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) |
137 | { | 147 | { |
138 | spin_lock(&cp->dest->stats.lock); | 148 | spin_lock(&cp->dest->stats.lock); |
139 | cp->dest->stats.conns++; | 149 | cp->dest->stats.ustats.conns++; |
140 | spin_unlock(&cp->dest->stats.lock); | 150 | spin_unlock(&cp->dest->stats.lock); |
141 | 151 | ||
142 | spin_lock(&svc->stats.lock); | 152 | spin_lock(&svc->stats.lock); |
143 | svc->stats.conns++; | 153 | svc->stats.ustats.conns++; |
144 | spin_unlock(&svc->stats.lock); | 154 | spin_unlock(&svc->stats.lock); |
145 | 155 | ||
146 | spin_lock(&ip_vs_stats.lock); | 156 | spin_lock(&ip_vs_stats.lock); |
147 | ip_vs_stats.conns++; | 157 | ip_vs_stats.ustats.conns++; |
148 | spin_unlock(&ip_vs_stats.lock); | 158 | spin_unlock(&ip_vs_stats.lock); |
149 | } | 159 | } |
150 | 160 | ||
@@ -173,20 +183,28 @@ ip_vs_sched_persist(struct ip_vs_service *svc, | |||
173 | __be16 ports[2]) | 183 | __be16 ports[2]) |
174 | { | 184 | { |
175 | struct ip_vs_conn *cp = NULL; | 185 | struct ip_vs_conn *cp = NULL; |
176 | struct iphdr *iph = ip_hdr(skb); | 186 | struct ip_vs_iphdr iph; |
177 | struct ip_vs_dest *dest; | 187 | struct ip_vs_dest *dest; |
178 | struct ip_vs_conn *ct; | 188 | struct ip_vs_conn *ct; |
179 | __be16 dport; /* destination port to forward */ | 189 | __be16 dport; /* destination port to forward */ |
180 | __be32 snet; /* source network of the client, after masking */ | 190 | union nf_inet_addr snet; /* source network of the client, |
191 | after masking */ | ||
192 | |||
193 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | ||
181 | 194 | ||
182 | /* Mask saddr with the netmask to adjust template granularity */ | 195 | /* Mask saddr with the netmask to adjust template granularity */ |
183 | snet = iph->saddr & svc->netmask; | 196 | #ifdef CONFIG_IP_VS_IPV6 |
197 | if (svc->af == AF_INET6) | ||
198 | ipv6_addr_prefix(&snet.in6, &iph.saddr.in6, svc->netmask); | ||
199 | else | ||
200 | #endif | ||
201 | snet.ip = iph.saddr.ip & svc->netmask; | ||
184 | 202 | ||
185 | IP_VS_DBG(6, "p-schedule: src %u.%u.%u.%u:%u dest %u.%u.%u.%u:%u " | 203 | IP_VS_DBG_BUF(6, "p-schedule: src %s:%u dest %s:%u " |
186 | "mnet %u.%u.%u.%u\n", | 204 | "mnet %s\n", |
187 | NIPQUAD(iph->saddr), ntohs(ports[0]), | 205 | IP_VS_DBG_ADDR(svc->af, &iph.saddr), ntohs(ports[0]), |
188 | NIPQUAD(iph->daddr), ntohs(ports[1]), | 206 | IP_VS_DBG_ADDR(svc->af, &iph.daddr), ntohs(ports[1]), |
189 | NIPQUAD(snet)); | 207 | IP_VS_DBG_ADDR(svc->af, &snet)); |
190 | 208 | ||
191 | /* | 209 | /* |
192 | * As far as we know, FTP is a very complicated network protocol, and | 210 | * As far as we know, FTP is a very complicated network protocol, and |
@@ -204,11 +222,11 @@ ip_vs_sched_persist(struct ip_vs_service *svc, | |||
204 | if (ports[1] == svc->port) { | 222 | if (ports[1] == svc->port) { |
205 | /* Check if a template already exists */ | 223 | /* Check if a template already exists */ |
206 | if (svc->port != FTPPORT) | 224 | if (svc->port != FTPPORT) |
207 | ct = ip_vs_ct_in_get(iph->protocol, snet, 0, | 225 | ct = ip_vs_ct_in_get(svc->af, iph.protocol, &snet, 0, |
208 | iph->daddr, ports[1]); | 226 | &iph.daddr, ports[1]); |
209 | else | 227 | else |
210 | ct = ip_vs_ct_in_get(iph->protocol, snet, 0, | 228 | ct = ip_vs_ct_in_get(svc->af, iph.protocol, &snet, 0, |
211 | iph->daddr, 0); | 229 | &iph.daddr, 0); |
212 | 230 | ||
213 | if (!ct || !ip_vs_check_template(ct)) { | 231 | if (!ct || !ip_vs_check_template(ct)) { |
214 | /* | 232 | /* |
@@ -228,18 +246,18 @@ ip_vs_sched_persist(struct ip_vs_service *svc, | |||
228 | * for ftp service. | 246 | * for ftp service. |
229 | */ | 247 | */ |
230 | if (svc->port != FTPPORT) | 248 | if (svc->port != FTPPORT) |
231 | ct = ip_vs_conn_new(iph->protocol, | 249 | ct = ip_vs_conn_new(svc->af, iph.protocol, |
232 | snet, 0, | 250 | &snet, 0, |
233 | iph->daddr, | 251 | &iph.daddr, |
234 | ports[1], | 252 | ports[1], |
235 | dest->addr, dest->port, | 253 | &dest->addr, dest->port, |
236 | IP_VS_CONN_F_TEMPLATE, | 254 | IP_VS_CONN_F_TEMPLATE, |
237 | dest); | 255 | dest); |
238 | else | 256 | else |
239 | ct = ip_vs_conn_new(iph->protocol, | 257 | ct = ip_vs_conn_new(svc->af, iph.protocol, |
240 | snet, 0, | 258 | &snet, 0, |
241 | iph->daddr, 0, | 259 | &iph.daddr, 0, |
242 | dest->addr, 0, | 260 | &dest->addr, 0, |
243 | IP_VS_CONN_F_TEMPLATE, | 261 | IP_VS_CONN_F_TEMPLATE, |
244 | dest); | 262 | dest); |
245 | if (ct == NULL) | 263 | if (ct == NULL) |
@@ -258,12 +276,16 @@ ip_vs_sched_persist(struct ip_vs_service *svc, | |||
258 | * fwmark template: <IPPROTO_IP,caddr,0,fwmark,0,daddr,0> | 276 | * fwmark template: <IPPROTO_IP,caddr,0,fwmark,0,daddr,0> |
259 | * port zero template: <protocol,caddr,0,vaddr,0,daddr,0> | 277 | * port zero template: <protocol,caddr,0,vaddr,0,daddr,0> |
260 | */ | 278 | */ |
261 | if (svc->fwmark) | 279 | if (svc->fwmark) { |
262 | ct = ip_vs_ct_in_get(IPPROTO_IP, snet, 0, | 280 | union nf_inet_addr fwmark = { |
263 | htonl(svc->fwmark), 0); | 281 | .all = { 0, 0, 0, htonl(svc->fwmark) } |
264 | else | 282 | }; |
265 | ct = ip_vs_ct_in_get(iph->protocol, snet, 0, | 283 | |
266 | iph->daddr, 0); | 284 | ct = ip_vs_ct_in_get(svc->af, IPPROTO_IP, &snet, 0, |
285 | &fwmark, 0); | ||
286 | } else | ||
287 | ct = ip_vs_ct_in_get(svc->af, iph.protocol, &snet, 0, | ||
288 | &iph.daddr, 0); | ||
267 | 289 | ||
268 | if (!ct || !ip_vs_check_template(ct)) { | 290 | if (!ct || !ip_vs_check_template(ct)) { |
269 | /* | 291 | /* |
@@ -282,18 +304,22 @@ ip_vs_sched_persist(struct ip_vs_service *svc, | |||
282 | /* | 304 | /* |
283 | * Create a template according to the service | 305 | * Create a template according to the service |
284 | */ | 306 | */ |
285 | if (svc->fwmark) | 307 | if (svc->fwmark) { |
286 | ct = ip_vs_conn_new(IPPROTO_IP, | 308 | union nf_inet_addr fwmark = { |
287 | snet, 0, | 309 | .all = { 0, 0, 0, htonl(svc->fwmark) } |
288 | htonl(svc->fwmark), 0, | 310 | }; |
289 | dest->addr, 0, | 311 | |
312 | ct = ip_vs_conn_new(svc->af, IPPROTO_IP, | ||
313 | &snet, 0, | ||
314 | &fwmark, 0, | ||
315 | &dest->addr, 0, | ||
290 | IP_VS_CONN_F_TEMPLATE, | 316 | IP_VS_CONN_F_TEMPLATE, |
291 | dest); | 317 | dest); |
292 | else | 318 | } else |
293 | ct = ip_vs_conn_new(iph->protocol, | 319 | ct = ip_vs_conn_new(svc->af, iph.protocol, |
294 | snet, 0, | 320 | &snet, 0, |
295 | iph->daddr, 0, | 321 | &iph.daddr, 0, |
296 | dest->addr, 0, | 322 | &dest->addr, 0, |
297 | IP_VS_CONN_F_TEMPLATE, | 323 | IP_VS_CONN_F_TEMPLATE, |
298 | dest); | 324 | dest); |
299 | if (ct == NULL) | 325 | if (ct == NULL) |
@@ -310,10 +336,10 @@ ip_vs_sched_persist(struct ip_vs_service *svc, | |||
310 | /* | 336 | /* |
311 | * Create a new connection according to the template | 337 | * Create a new connection according to the template |
312 | */ | 338 | */ |
313 | cp = ip_vs_conn_new(iph->protocol, | 339 | cp = ip_vs_conn_new(svc->af, iph.protocol, |
314 | iph->saddr, ports[0], | 340 | &iph.saddr, ports[0], |
315 | iph->daddr, ports[1], | 341 | &iph.daddr, ports[1], |
316 | dest->addr, dport, | 342 | &dest->addr, dport, |
317 | 0, | 343 | 0, |
318 | dest); | 344 | dest); |
319 | if (cp == NULL) { | 345 | if (cp == NULL) { |
@@ -342,12 +368,12 @@ struct ip_vs_conn * | |||
342 | ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | 368 | ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) |
343 | { | 369 | { |
344 | struct ip_vs_conn *cp = NULL; | 370 | struct ip_vs_conn *cp = NULL; |
345 | struct iphdr *iph = ip_hdr(skb); | 371 | struct ip_vs_iphdr iph; |
346 | struct ip_vs_dest *dest; | 372 | struct ip_vs_dest *dest; |
347 | __be16 _ports[2], *pptr; | 373 | __be16 _ports[2], *pptr; |
348 | 374 | ||
349 | pptr = skb_header_pointer(skb, iph->ihl*4, | 375 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); |
350 | sizeof(_ports), _ports); | 376 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); |
351 | if (pptr == NULL) | 377 | if (pptr == NULL) |
352 | return NULL; | 378 | return NULL; |
353 | 379 | ||
@@ -377,22 +403,22 @@ ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
377 | /* | 403 | /* |
378 | * Create a connection entry. | 404 | * Create a connection entry. |
379 | */ | 405 | */ |
380 | cp = ip_vs_conn_new(iph->protocol, | 406 | cp = ip_vs_conn_new(svc->af, iph.protocol, |
381 | iph->saddr, pptr[0], | 407 | &iph.saddr, pptr[0], |
382 | iph->daddr, pptr[1], | 408 | &iph.daddr, pptr[1], |
383 | dest->addr, dest->port?dest->port:pptr[1], | 409 | &dest->addr, dest->port ? dest->port : pptr[1], |
384 | 0, | 410 | 0, |
385 | dest); | 411 | dest); |
386 | if (cp == NULL) | 412 | if (cp == NULL) |
387 | return NULL; | 413 | return NULL; |
388 | 414 | ||
389 | IP_VS_DBG(6, "Schedule fwd:%c c:%u.%u.%u.%u:%u v:%u.%u.%u.%u:%u " | 415 | IP_VS_DBG_BUF(6, "Schedule fwd:%c c:%s:%u v:%s:%u " |
390 | "d:%u.%u.%u.%u:%u conn->flags:%X conn->refcnt:%d\n", | 416 | "d:%s:%u conn->flags:%X conn->refcnt:%d\n", |
391 | ip_vs_fwd_tag(cp), | 417 | ip_vs_fwd_tag(cp), |
392 | NIPQUAD(cp->caddr), ntohs(cp->cport), | 418 | IP_VS_DBG_ADDR(svc->af, &cp->caddr), ntohs(cp->cport), |
393 | NIPQUAD(cp->vaddr), ntohs(cp->vport), | 419 | IP_VS_DBG_ADDR(svc->af, &cp->vaddr), ntohs(cp->vport), |
394 | NIPQUAD(cp->daddr), ntohs(cp->dport), | 420 | IP_VS_DBG_ADDR(svc->af, &cp->daddr), ntohs(cp->dport), |
395 | cp->flags, atomic_read(&cp->refcnt)); | 421 | cp->flags, atomic_read(&cp->refcnt)); |
396 | 422 | ||
397 | ip_vs_conn_stats(cp, svc); | 423 | ip_vs_conn_stats(cp, svc); |
398 | return cp; | 424 | return cp; |
@@ -408,20 +434,27 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
408 | struct ip_vs_protocol *pp) | 434 | struct ip_vs_protocol *pp) |
409 | { | 435 | { |
410 | __be16 _ports[2], *pptr; | 436 | __be16 _ports[2], *pptr; |
411 | struct iphdr *iph = ip_hdr(skb); | 437 | struct ip_vs_iphdr iph; |
438 | int unicast; | ||
439 | ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); | ||
412 | 440 | ||
413 | pptr = skb_header_pointer(skb, iph->ihl*4, | 441 | pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); |
414 | sizeof(_ports), _ports); | ||
415 | if (pptr == NULL) { | 442 | if (pptr == NULL) { |
416 | ip_vs_service_put(svc); | 443 | ip_vs_service_put(svc); |
417 | return NF_DROP; | 444 | return NF_DROP; |
418 | } | 445 | } |
419 | 446 | ||
447 | #ifdef CONFIG_IP_VS_IPV6 | ||
448 | if (svc->af == AF_INET6) | ||
449 | unicast = ipv6_addr_type(&iph.daddr.in6) & IPV6_ADDR_UNICAST; | ||
450 | else | ||
451 | #endif | ||
452 | unicast = (inet_addr_type(&init_net, iph.daddr.ip) == RTN_UNICAST); | ||
453 | |||
420 | /* if it is fwmark-based service, the cache_bypass sysctl is up | 454 | /* if it is fwmark-based service, the cache_bypass sysctl is up |
421 | and the destination is RTN_UNICAST (and not local), then create | 455 | and the destination is a non-local unicast, then create |
422 | a cache_bypass connection entry */ | 456 | a cache_bypass connection entry */ |
423 | if (sysctl_ip_vs_cache_bypass && svc->fwmark | 457 | if (sysctl_ip_vs_cache_bypass && svc->fwmark && unicast) { |
424 | && (inet_addr_type(&init_net, iph->daddr) == RTN_UNICAST)) { | ||
425 | int ret, cs; | 458 | int ret, cs; |
426 | struct ip_vs_conn *cp; | 459 | struct ip_vs_conn *cp; |
427 | 460 | ||
@@ -429,9 +462,9 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
429 | 462 | ||
430 | /* create a new connection entry */ | 463 | /* create a new connection entry */ |
431 | IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n"); | 464 | IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n"); |
432 | cp = ip_vs_conn_new(iph->protocol, | 465 | cp = ip_vs_conn_new(svc->af, iph.protocol, |
433 | iph->saddr, pptr[0], | 466 | &iph.saddr, pptr[0], |
434 | iph->daddr, pptr[1], | 467 | &iph.daddr, pptr[1], |
435 | 0, 0, | 468 | 0, 0, |
436 | IP_VS_CONN_F_BYPASS, | 469 | IP_VS_CONN_F_BYPASS, |
437 | NULL); | 470 | NULL); |
@@ -473,7 +506,14 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, | |||
473 | * created, the TCP RST packet cannot be sent, instead that | 506 | * created, the TCP RST packet cannot be sent, instead that |
474 | * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ | 507 | * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ |
475 | */ | 508 | */ |
476 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | 509 | #ifdef CONFIG_IP_VS_IPV6 |
510 | if (svc->af == AF_INET6) | ||
511 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, | ||
512 | skb->dev); | ||
513 | else | ||
514 | #endif | ||
515 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | ||
516 | |||
477 | return NF_DROP; | 517 | return NF_DROP; |
478 | } | 518 | } |
479 | 519 | ||
@@ -512,6 +552,14 @@ static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user) | |||
512 | return err; | 552 | return err; |
513 | } | 553 | } |
514 | 554 | ||
555 | #ifdef CONFIG_IP_VS_IPV6 | ||
556 | static inline int ip_vs_gather_frags_v6(struct sk_buff *skb, u_int32_t user) | ||
557 | { | ||
558 | /* TODO IPv6: Find out what to do here for IPv6 */ | ||
559 | return 0; | ||
560 | } | ||
561 | #endif | ||
562 | |||
515 | /* | 563 | /* |
516 | * Packet has been made sufficiently writable in caller | 564 | * Packet has been made sufficiently writable in caller |
517 | * - inout: 1=in->out, 0=out->in | 565 | * - inout: 1=in->out, 0=out->in |
@@ -526,14 +574,14 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
526 | struct iphdr *ciph = (struct iphdr *)(icmph + 1); | 574 | struct iphdr *ciph = (struct iphdr *)(icmph + 1); |
527 | 575 | ||
528 | if (inout) { | 576 | if (inout) { |
529 | iph->saddr = cp->vaddr; | 577 | iph->saddr = cp->vaddr.ip; |
530 | ip_send_check(iph); | 578 | ip_send_check(iph); |
531 | ciph->daddr = cp->vaddr; | 579 | ciph->daddr = cp->vaddr.ip; |
532 | ip_send_check(ciph); | 580 | ip_send_check(ciph); |
533 | } else { | 581 | } else { |
534 | iph->daddr = cp->daddr; | 582 | iph->daddr = cp->daddr.ip; |
535 | ip_send_check(iph); | 583 | ip_send_check(iph); |
536 | ciph->saddr = cp->daddr; | 584 | ciph->saddr = cp->daddr.ip; |
537 | ip_send_check(ciph); | 585 | ip_send_check(ciph); |
538 | } | 586 | } |
539 | 587 | ||
@@ -560,21 +608,112 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
560 | "Forwarding altered incoming ICMP"); | 608 | "Forwarding altered incoming ICMP"); |
561 | } | 609 | } |
562 | 610 | ||
611 | #ifdef CONFIG_IP_VS_IPV6 | ||
612 | void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, | ||
613 | struct ip_vs_conn *cp, int inout) | ||
614 | { | ||
615 | struct ipv6hdr *iph = ipv6_hdr(skb); | ||
616 | unsigned int icmp_offset = sizeof(struct ipv6hdr); | ||
617 | struct icmp6hdr *icmph = (struct icmp6hdr *)(skb_network_header(skb) + | ||
618 | icmp_offset); | ||
619 | struct ipv6hdr *ciph = (struct ipv6hdr *)(icmph + 1); | ||
620 | |||
621 | if (inout) { | ||
622 | iph->saddr = cp->vaddr.in6; | ||
623 | ciph->daddr = cp->vaddr.in6; | ||
624 | } else { | ||
625 | iph->daddr = cp->daddr.in6; | ||
626 | ciph->saddr = cp->daddr.in6; | ||
627 | } | ||
628 | |||
629 | /* the TCP/UDP port */ | ||
630 | if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr) { | ||
631 | __be16 *ports = (void *)ciph + sizeof(struct ipv6hdr); | ||
632 | |||
633 | if (inout) | ||
634 | ports[1] = cp->vport; | ||
635 | else | ||
636 | ports[0] = cp->dport; | ||
637 | } | ||
638 | |||
639 | /* And finally the ICMP checksum */ | ||
640 | icmph->icmp6_cksum = 0; | ||
641 | /* TODO IPv6: is this correct for ICMPv6? */ | ||
642 | ip_vs_checksum_complete(skb, icmp_offset); | ||
643 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
644 | |||
645 | if (inout) | ||
646 | IP_VS_DBG_PKT(11, pp, skb, (void *)ciph - (void *)iph, | ||
647 | "Forwarding altered outgoing ICMPv6"); | ||
648 | else | ||
649 | IP_VS_DBG_PKT(11, pp, skb, (void *)ciph - (void *)iph, | ||
650 | "Forwarding altered incoming ICMPv6"); | ||
651 | } | ||
652 | #endif | ||
653 | |||
654 | /* Handle relevant response ICMP messages - forward to the right | ||
655 | * destination host. Used for NAT and local client. | ||
656 | */ | ||
657 | static int handle_response_icmp(int af, struct sk_buff *skb, | ||
658 | union nf_inet_addr *snet, | ||
659 | __u8 protocol, struct ip_vs_conn *cp, | ||
660 | struct ip_vs_protocol *pp, | ||
661 | unsigned int offset, unsigned int ihl) | ||
662 | { | ||
663 | unsigned int verdict = NF_DROP; | ||
664 | |||
665 | if (IP_VS_FWD_METHOD(cp) != 0) { | ||
666 | IP_VS_ERR("shouldn't reach here, because the box is on the " | ||
667 | "half connection in the tun/dr module.\n"); | ||
668 | } | ||
669 | |||
670 | /* Ensure the checksum is correct */ | ||
671 | if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { | ||
672 | /* Failed checksum! */ | ||
673 | IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", | ||
674 | IP_VS_DBG_ADDR(af, snet)); | ||
675 | goto out; | ||
676 | } | ||
677 | |||
678 | if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol) | ||
679 | offset += 2 * sizeof(__u16); | ||
680 | if (!skb_make_writable(skb, offset)) | ||
681 | goto out; | ||
682 | |||
683 | #ifdef CONFIG_IP_VS_IPV6 | ||
684 | if (af == AF_INET6) | ||
685 | ip_vs_nat_icmp_v6(skb, pp, cp, 1); | ||
686 | else | ||
687 | #endif | ||
688 | ip_vs_nat_icmp(skb, pp, cp, 1); | ||
689 | |||
690 | /* do the statistics and put it back */ | ||
691 | ip_vs_out_stats(cp, skb); | ||
692 | |||
693 | skb->ipvs_property = 1; | ||
694 | verdict = NF_ACCEPT; | ||
695 | |||
696 | out: | ||
697 | __ip_vs_conn_put(cp); | ||
698 | |||
699 | return verdict; | ||
700 | } | ||
701 | |||
563 | /* | 702 | /* |
564 | * Handle ICMP messages in the inside-to-outside direction (outgoing). | 703 | * Handle ICMP messages in the inside-to-outside direction (outgoing). |
565 | * Find any that might be relevant, check against existing connections, | 704 | * Find any that might be relevant, check against existing connections. |
566 | * forward to the right destination host if relevant. | ||
567 | * Currently handles error types - unreachable, quench, ttl exceeded. | 705 | * Currently handles error types - unreachable, quench, ttl exceeded. |
568 | * (Only used in VS/NAT) | ||
569 | */ | 706 | */ |
570 | static int ip_vs_out_icmp(struct sk_buff *skb, int *related) | 707 | static int ip_vs_out_icmp(struct sk_buff *skb, int *related) |
571 | { | 708 | { |
572 | struct iphdr *iph; | 709 | struct iphdr *iph; |
573 | struct icmphdr _icmph, *ic; | 710 | struct icmphdr _icmph, *ic; |
574 | struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ | 711 | struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ |
712 | struct ip_vs_iphdr ciph; | ||
575 | struct ip_vs_conn *cp; | 713 | struct ip_vs_conn *cp; |
576 | struct ip_vs_protocol *pp; | 714 | struct ip_vs_protocol *pp; |
577 | unsigned int offset, ihl, verdict; | 715 | unsigned int offset, ihl; |
716 | union nf_inet_addr snet; | ||
578 | 717 | ||
579 | *related = 1; | 718 | *related = 1; |
580 | 719 | ||
@@ -627,102 +766,231 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related) | |||
627 | 766 | ||
628 | offset += cih->ihl * 4; | 767 | offset += cih->ihl * 4; |
629 | 768 | ||
769 | ip_vs_fill_iphdr(AF_INET, cih, &ciph); | ||
630 | /* The embedded headers contain source and dest in reverse order */ | 770 | /* The embedded headers contain source and dest in reverse order */ |
631 | cp = pp->conn_out_get(skb, pp, cih, offset, 1); | 771 | cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1); |
632 | if (!cp) | 772 | if (!cp) |
633 | return NF_ACCEPT; | 773 | return NF_ACCEPT; |
634 | 774 | ||
635 | verdict = NF_DROP; | 775 | snet.ip = iph->saddr; |
776 | return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, | ||
777 | pp, offset, ihl); | ||
778 | } | ||
636 | 779 | ||
637 | if (IP_VS_FWD_METHOD(cp) != 0) { | 780 | #ifdef CONFIG_IP_VS_IPV6 |
638 | IP_VS_ERR("shouldn't reach here, because the box is on the " | 781 | static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related) |
639 | "half connection in the tun/dr module.\n"); | 782 | { |
783 | struct ipv6hdr *iph; | ||
784 | struct icmp6hdr _icmph, *ic; | ||
785 | struct ipv6hdr _ciph, *cih; /* The ip header contained | ||
786 | within the ICMP */ | ||
787 | struct ip_vs_iphdr ciph; | ||
788 | struct ip_vs_conn *cp; | ||
789 | struct ip_vs_protocol *pp; | ||
790 | unsigned int offset; | ||
791 | union nf_inet_addr snet; | ||
792 | |||
793 | *related = 1; | ||
794 | |||
795 | /* reassemble IP fragments */ | ||
796 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) { | ||
797 | if (ip_vs_gather_frags_v6(skb, IP_DEFRAG_VS_OUT)) | ||
798 | return NF_STOLEN; | ||
640 | } | 799 | } |
641 | 800 | ||
642 | /* Ensure the checksum is correct */ | 801 | iph = ipv6_hdr(skb); |
643 | if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { | 802 | offset = sizeof(struct ipv6hdr); |
644 | /* Failed checksum! */ | 803 | ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); |
645 | IP_VS_DBG(1, "Forward ICMP: failed checksum from %d.%d.%d.%d!\n", | 804 | if (ic == NULL) |
646 | NIPQUAD(iph->saddr)); | 805 | return NF_DROP; |
647 | goto out; | 806 | |
807 | IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) " NIP6_FMT "->" NIP6_FMT "\n", | ||
808 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
809 | NIP6(iph->saddr), NIP6(iph->daddr)); | ||
810 | |||
811 | /* | ||
812 | * Work through seeing if this is for us. | ||
813 | * These checks are supposed to be in an order that means easy | ||
814 | * things are checked first to speed up processing.... however | ||
815 | * this means that some packets will manage to get a long way | ||
816 | * down this stack and then be rejected, but that's life. | ||
817 | */ | ||
818 | if ((ic->icmp6_type != ICMPV6_DEST_UNREACH) && | ||
819 | (ic->icmp6_type != ICMPV6_PKT_TOOBIG) && | ||
820 | (ic->icmp6_type != ICMPV6_TIME_EXCEED)) { | ||
821 | *related = 0; | ||
822 | return NF_ACCEPT; | ||
648 | } | 823 | } |
649 | 824 | ||
650 | if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol) | 825 | /* Now find the contained IP header */ |
651 | offset += 2 * sizeof(__u16); | 826 | offset += sizeof(_icmph); |
652 | if (!skb_make_writable(skb, offset)) | 827 | cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); |
653 | goto out; | 828 | if (cih == NULL) |
829 | return NF_ACCEPT; /* The packet looks wrong, ignore */ | ||
654 | 830 | ||
655 | ip_vs_nat_icmp(skb, pp, cp, 1); | 831 | pp = ip_vs_proto_get(cih->nexthdr); |
832 | if (!pp) | ||
833 | return NF_ACCEPT; | ||
656 | 834 | ||
657 | /* do the statistics and put it back */ | 835 | /* Is the embedded protocol header present? */ |
658 | ip_vs_out_stats(cp, skb); | 836 | /* TODO: we don't support fragmentation at the moment anyways */ |
837 | if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag)) | ||
838 | return NF_ACCEPT; | ||
659 | 839 | ||
660 | skb->ipvs_property = 1; | 840 | IP_VS_DBG_PKT(11, pp, skb, offset, "Checking outgoing ICMPv6 for"); |
661 | verdict = NF_ACCEPT; | ||
662 | 841 | ||
663 | out: | 842 | offset += sizeof(struct ipv6hdr); |
664 | __ip_vs_conn_put(cp); | ||
665 | 843 | ||
666 | return verdict; | 844 | ip_vs_fill_iphdr(AF_INET6, cih, &ciph); |
845 | /* The embedded headers contain source and dest in reverse order */ | ||
846 | cp = pp->conn_out_get(AF_INET6, skb, pp, &ciph, offset, 1); | ||
847 | if (!cp) | ||
848 | return NF_ACCEPT; | ||
849 | |||
850 | ipv6_addr_copy(&snet.in6, &iph->saddr); | ||
851 | return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp, | ||
852 | pp, offset, sizeof(struct ipv6hdr)); | ||
667 | } | 853 | } |
854 | #endif | ||
668 | 855 | ||
669 | static inline int is_tcp_reset(const struct sk_buff *skb) | 856 | static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len) |
670 | { | 857 | { |
671 | struct tcphdr _tcph, *th; | 858 | struct tcphdr _tcph, *th; |
672 | 859 | ||
673 | th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); | 860 | th = skb_header_pointer(skb, nh_len, sizeof(_tcph), &_tcph); |
674 | if (th == NULL) | 861 | if (th == NULL) |
675 | return 0; | 862 | return 0; |
676 | return th->rst; | 863 | return th->rst; |
677 | } | 864 | } |
678 | 865 | ||
866 | /* Handle response packets: rewrite addresses and send away... | ||
867 | * Used for NAT and local client. | ||
868 | */ | ||
869 | static unsigned int | ||
870 | handle_response(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, | ||
871 | struct ip_vs_conn *cp, int ihl) | ||
872 | { | ||
873 | IP_VS_DBG_PKT(11, pp, skb, 0, "Outgoing packet"); | ||
874 | |||
875 | if (!skb_make_writable(skb, ihl)) | ||
876 | goto drop; | ||
877 | |||
878 | /* mangle the packet */ | ||
879 | if (pp->snat_handler && !pp->snat_handler(skb, pp, cp)) | ||
880 | goto drop; | ||
881 | |||
882 | #ifdef CONFIG_IP_VS_IPV6 | ||
883 | if (af == AF_INET6) | ||
884 | ipv6_hdr(skb)->saddr = cp->vaddr.in6; | ||
885 | else | ||
886 | #endif | ||
887 | { | ||
888 | ip_hdr(skb)->saddr = cp->vaddr.ip; | ||
889 | ip_send_check(ip_hdr(skb)); | ||
890 | } | ||
891 | |||
892 | /* For policy routing, packets originating from this | ||
893 | * machine itself may be routed differently to packets | ||
894 | * passing through. We want this packet to be routed as | ||
895 | * if it came from this machine itself. So re-compute | ||
896 | * the routing information. | ||
897 | */ | ||
898 | #ifdef CONFIG_IP_VS_IPV6 | ||
899 | if (af == AF_INET6) { | ||
900 | if (ip6_route_me_harder(skb) != 0) | ||
901 | goto drop; | ||
902 | } else | ||
903 | #endif | ||
904 | if (ip_route_me_harder(skb, RTN_LOCAL) != 0) | ||
905 | goto drop; | ||
906 | |||
907 | IP_VS_DBG_PKT(10, pp, skb, 0, "After SNAT"); | ||
908 | |||
909 | ip_vs_out_stats(cp, skb); | ||
910 | ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pp); | ||
911 | ip_vs_conn_put(cp); | ||
912 | |||
913 | skb->ipvs_property = 1; | ||
914 | |||
915 | LeaveFunction(11); | ||
916 | return NF_ACCEPT; | ||
917 | |||
918 | drop: | ||
919 | ip_vs_conn_put(cp); | ||
920 | kfree_skb(skb); | ||
921 | return NF_STOLEN; | ||
922 | } | ||
923 | |||
679 | /* | 924 | /* |
680 | * It is hooked at the NF_INET_FORWARD chain, used only for VS/NAT. | 925 | * It is hooked at the NF_INET_FORWARD chain, used only for VS/NAT. |
681 | * Check if outgoing packet belongs to the established ip_vs_conn, | 926 | * Check if outgoing packet belongs to the established ip_vs_conn. |
682 | * rewrite addresses of the packet and send it on its way... | ||
683 | */ | 927 | */ |
684 | static unsigned int | 928 | static unsigned int |
685 | ip_vs_out(unsigned int hooknum, struct sk_buff *skb, | 929 | ip_vs_out(unsigned int hooknum, struct sk_buff *skb, |
686 | const struct net_device *in, const struct net_device *out, | 930 | const struct net_device *in, const struct net_device *out, |
687 | int (*okfn)(struct sk_buff *)) | 931 | int (*okfn)(struct sk_buff *)) |
688 | { | 932 | { |
689 | struct iphdr *iph; | 933 | struct ip_vs_iphdr iph; |
690 | struct ip_vs_protocol *pp; | 934 | struct ip_vs_protocol *pp; |
691 | struct ip_vs_conn *cp; | 935 | struct ip_vs_conn *cp; |
692 | int ihl; | 936 | int af; |
693 | 937 | ||
694 | EnterFunction(11); | 938 | EnterFunction(11); |
695 | 939 | ||
940 | af = (skb->protocol == __constant_htons(ETH_P_IP)) ? AF_INET : AF_INET6; | ||
941 | |||
696 | if (skb->ipvs_property) | 942 | if (skb->ipvs_property) |
697 | return NF_ACCEPT; | 943 | return NF_ACCEPT; |
698 | 944 | ||
699 | iph = ip_hdr(skb); | 945 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
700 | if (unlikely(iph->protocol == IPPROTO_ICMP)) { | 946 | #ifdef CONFIG_IP_VS_IPV6 |
701 | int related, verdict = ip_vs_out_icmp(skb, &related); | 947 | if (af == AF_INET6) { |
948 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { | ||
949 | int related, verdict = ip_vs_out_icmp_v6(skb, &related); | ||
702 | 950 | ||
703 | if (related) | 951 | if (related) |
704 | return verdict; | 952 | return verdict; |
705 | iph = ip_hdr(skb); | 953 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
706 | } | 954 | } |
955 | } else | ||
956 | #endif | ||
957 | if (unlikely(iph.protocol == IPPROTO_ICMP)) { | ||
958 | int related, verdict = ip_vs_out_icmp(skb, &related); | ||
707 | 959 | ||
708 | pp = ip_vs_proto_get(iph->protocol); | 960 | if (related) |
961 | return verdict; | ||
962 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
963 | } | ||
964 | |||
965 | pp = ip_vs_proto_get(iph.protocol); | ||
709 | if (unlikely(!pp)) | 966 | if (unlikely(!pp)) |
710 | return NF_ACCEPT; | 967 | return NF_ACCEPT; |
711 | 968 | ||
712 | /* reassemble IP fragments */ | 969 | /* reassemble IP fragments */ |
713 | if (unlikely(iph->frag_off & htons(IP_MF|IP_OFFSET) && | 970 | #ifdef CONFIG_IP_VS_IPV6 |
714 | !pp->dont_defrag)) { | 971 | if (af == AF_INET6) { |
715 | if (ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT)) | 972 | if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { |
716 | return NF_STOLEN; | 973 | int related, verdict = ip_vs_out_icmp_v6(skb, &related); |
717 | iph = ip_hdr(skb); | 974 | |
718 | } | 975 | if (related) |
976 | return verdict; | ||
719 | 977 | ||
720 | ihl = iph->ihl << 2; | 978 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
979 | } | ||
980 | } else | ||
981 | #endif | ||
982 | if (unlikely(ip_hdr(skb)->frag_off & htons(IP_MF|IP_OFFSET) && | ||
983 | !pp->dont_defrag)) { | ||
984 | if (ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT)) | ||
985 | return NF_STOLEN; | ||
986 | |||
987 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
988 | } | ||
721 | 989 | ||
722 | /* | 990 | /* |
723 | * Check if the packet belongs to an existing entry | 991 | * Check if the packet belongs to an existing entry |
724 | */ | 992 | */ |
725 | cp = pp->conn_out_get(skb, pp, iph, ihl, 0); | 993 | cp = pp->conn_out_get(af, skb, pp, &iph, iph.len, 0); |
726 | 994 | ||
727 | if (unlikely(!cp)) { | 995 | if (unlikely(!cp)) { |
728 | if (sysctl_ip_vs_nat_icmp_send && | 996 | if (sysctl_ip_vs_nat_icmp_send && |
@@ -730,21 +998,31 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, | |||
730 | pp->protocol == IPPROTO_UDP)) { | 998 | pp->protocol == IPPROTO_UDP)) { |
731 | __be16 _ports[2], *pptr; | 999 | __be16 _ports[2], *pptr; |
732 | 1000 | ||
733 | pptr = skb_header_pointer(skb, ihl, | 1001 | pptr = skb_header_pointer(skb, iph.len, |
734 | sizeof(_ports), _ports); | 1002 | sizeof(_ports), _ports); |
735 | if (pptr == NULL) | 1003 | if (pptr == NULL) |
736 | return NF_ACCEPT; /* Not for me */ | 1004 | return NF_ACCEPT; /* Not for me */ |
737 | if (ip_vs_lookup_real_service(iph->protocol, | 1005 | if (ip_vs_lookup_real_service(af, iph.protocol, |
738 | iph->saddr, pptr[0])) { | 1006 | &iph.saddr, |
1007 | pptr[0])) { | ||
739 | /* | 1008 | /* |
740 | * Notify the real server: there is no | 1009 | * Notify the real server: there is no |
741 | * existing entry if it is not RST | 1010 | * existing entry if it is not RST |
742 | * packet or not TCP packet. | 1011 | * packet or not TCP packet. |
743 | */ | 1012 | */ |
744 | if (iph->protocol != IPPROTO_TCP | 1013 | if (iph.protocol != IPPROTO_TCP |
745 | || !is_tcp_reset(skb)) { | 1014 | || !is_tcp_reset(skb, iph.len)) { |
746 | icmp_send(skb,ICMP_DEST_UNREACH, | 1015 | #ifdef CONFIG_IP_VS_IPV6 |
747 | ICMP_PORT_UNREACH, 0); | 1016 | if (af == AF_INET6) |
1017 | icmpv6_send(skb, | ||
1018 | ICMPV6_DEST_UNREACH, | ||
1019 | ICMPV6_PORT_UNREACH, | ||
1020 | 0, skb->dev); | ||
1021 | else | ||
1022 | #endif | ||
1023 | icmp_send(skb, | ||
1024 | ICMP_DEST_UNREACH, | ||
1025 | ICMP_PORT_UNREACH, 0); | ||
748 | return NF_DROP; | 1026 | return NF_DROP; |
749 | } | 1027 | } |
750 | } | 1028 | } |
@@ -754,41 +1032,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, | |||
754 | return NF_ACCEPT; | 1032 | return NF_ACCEPT; |
755 | } | 1033 | } |
756 | 1034 | ||
757 | IP_VS_DBG_PKT(11, pp, skb, 0, "Outgoing packet"); | 1035 | return handle_response(af, skb, pp, cp, iph.len); |
758 | |||
759 | if (!skb_make_writable(skb, ihl)) | ||
760 | goto drop; | ||
761 | |||
762 | /* mangle the packet */ | ||
763 | if (pp->snat_handler && !pp->snat_handler(skb, pp, cp)) | ||
764 | goto drop; | ||
765 | ip_hdr(skb)->saddr = cp->vaddr; | ||
766 | ip_send_check(ip_hdr(skb)); | ||
767 | |||
768 | /* For policy routing, packets originating from this | ||
769 | * machine itself may be routed differently to packets | ||
770 | * passing through. We want this packet to be routed as | ||
771 | * if it came from this machine itself. So re-compute | ||
772 | * the routing information. | ||
773 | */ | ||
774 | if (ip_route_me_harder(skb, RTN_LOCAL) != 0) | ||
775 | goto drop; | ||
776 | |||
777 | IP_VS_DBG_PKT(10, pp, skb, 0, "After SNAT"); | ||
778 | |||
779 | ip_vs_out_stats(cp, skb); | ||
780 | ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pp); | ||
781 | ip_vs_conn_put(cp); | ||
782 | |||
783 | skb->ipvs_property = 1; | ||
784 | |||
785 | LeaveFunction(11); | ||
786 | return NF_ACCEPT; | ||
787 | |||
788 | drop: | ||
789 | ip_vs_conn_put(cp); | ||
790 | kfree_skb(skb); | ||
791 | return NF_STOLEN; | ||
792 | } | 1036 | } |
793 | 1037 | ||
794 | 1038 | ||
@@ -804,9 +1048,11 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
804 | struct iphdr *iph; | 1048 | struct iphdr *iph; |
805 | struct icmphdr _icmph, *ic; | 1049 | struct icmphdr _icmph, *ic; |
806 | struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ | 1050 | struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ |
1051 | struct ip_vs_iphdr ciph; | ||
807 | struct ip_vs_conn *cp; | 1052 | struct ip_vs_conn *cp; |
808 | struct ip_vs_protocol *pp; | 1053 | struct ip_vs_protocol *pp; |
809 | unsigned int offset, ihl, verdict; | 1054 | unsigned int offset, ihl, verdict; |
1055 | union nf_inet_addr snet; | ||
810 | 1056 | ||
811 | *related = 1; | 1057 | *related = 1; |
812 | 1058 | ||
@@ -860,10 +1106,20 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
860 | 1106 | ||
861 | offset += cih->ihl * 4; | 1107 | offset += cih->ihl * 4; |
862 | 1108 | ||
1109 | ip_vs_fill_iphdr(AF_INET, cih, &ciph); | ||
863 | /* The embedded headers contain source and dest in reverse order */ | 1110 | /* The embedded headers contain source and dest in reverse order */ |
864 | cp = pp->conn_in_get(skb, pp, cih, offset, 1); | 1111 | cp = pp->conn_in_get(AF_INET, skb, pp, &ciph, offset, 1); |
865 | if (!cp) | 1112 | if (!cp) { |
1113 | /* The packet could also belong to a local client */ | ||
1114 | cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1); | ||
1115 | if (cp) { | ||
1116 | snet.ip = iph->saddr; | ||
1117 | return handle_response_icmp(AF_INET, skb, &snet, | ||
1118 | cih->protocol, cp, pp, | ||
1119 | offset, ihl); | ||
1120 | } | ||
866 | return NF_ACCEPT; | 1121 | return NF_ACCEPT; |
1122 | } | ||
867 | 1123 | ||
868 | verdict = NF_DROP; | 1124 | verdict = NF_DROP; |
869 | 1125 | ||
@@ -888,6 +1144,105 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) | |||
888 | return verdict; | 1144 | return verdict; |
889 | } | 1145 | } |
890 | 1146 | ||
1147 | #ifdef CONFIG_IP_VS_IPV6 | ||
1148 | static int | ||
1149 | ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) | ||
1150 | { | ||
1151 | struct ipv6hdr *iph; | ||
1152 | struct icmp6hdr _icmph, *ic; | ||
1153 | struct ipv6hdr _ciph, *cih; /* The ip header contained | ||
1154 | within the ICMP */ | ||
1155 | struct ip_vs_iphdr ciph; | ||
1156 | struct ip_vs_conn *cp; | ||
1157 | struct ip_vs_protocol *pp; | ||
1158 | unsigned int offset, verdict; | ||
1159 | union nf_inet_addr snet; | ||
1160 | |||
1161 | *related = 1; | ||
1162 | |||
1163 | /* reassemble IP fragments */ | ||
1164 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) { | ||
1165 | if (ip_vs_gather_frags_v6(skb, hooknum == NF_INET_LOCAL_IN ? | ||
1166 | IP_DEFRAG_VS_IN : | ||
1167 | IP_DEFRAG_VS_FWD)) | ||
1168 | return NF_STOLEN; | ||
1169 | } | ||
1170 | |||
1171 | iph = ipv6_hdr(skb); | ||
1172 | offset = sizeof(struct ipv6hdr); | ||
1173 | ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); | ||
1174 | if (ic == NULL) | ||
1175 | return NF_DROP; | ||
1176 | |||
1177 | IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) " NIP6_FMT "->" NIP6_FMT "\n", | ||
1178 | ic->icmp6_type, ntohs(icmpv6_id(ic)), | ||
1179 | NIP6(iph->saddr), NIP6(iph->daddr)); | ||
1180 | |||
1181 | /* | ||
1182 | * Work through seeing if this is for us. | ||
1183 | * These checks are supposed to be in an order that means easy | ||
1184 | * things are checked first to speed up processing.... however | ||
1185 | * this means that some packets will manage to get a long way | ||
1186 | * down this stack and then be rejected, but that's life. | ||
1187 | */ | ||
1188 | if ((ic->icmp6_type != ICMPV6_DEST_UNREACH) && | ||
1189 | (ic->icmp6_type != ICMPV6_PKT_TOOBIG) && | ||
1190 | (ic->icmp6_type != ICMPV6_TIME_EXCEED)) { | ||
1191 | *related = 0; | ||
1192 | return NF_ACCEPT; | ||
1193 | } | ||
1194 | |||
1195 | /* Now find the contained IP header */ | ||
1196 | offset += sizeof(_icmph); | ||
1197 | cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); | ||
1198 | if (cih == NULL) | ||
1199 | return NF_ACCEPT; /* The packet looks wrong, ignore */ | ||
1200 | |||
1201 | pp = ip_vs_proto_get(cih->nexthdr); | ||
1202 | if (!pp) | ||
1203 | return NF_ACCEPT; | ||
1204 | |||
1205 | /* Is the embedded protocol header present? */ | ||
1206 | /* TODO: we don't support fragmentation at the moment anyways */ | ||
1207 | if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag)) | ||
1208 | return NF_ACCEPT; | ||
1209 | |||
1210 | IP_VS_DBG_PKT(11, pp, skb, offset, "Checking incoming ICMPv6 for"); | ||
1211 | |||
1212 | offset += sizeof(struct ipv6hdr); | ||
1213 | |||
1214 | ip_vs_fill_iphdr(AF_INET6, cih, &ciph); | ||
1215 | /* The embedded headers contain source and dest in reverse order */ | ||
1216 | cp = pp->conn_in_get(AF_INET6, skb, pp, &ciph, offset, 1); | ||
1217 | if (!cp) { | ||
1218 | /* The packet could also belong to a local client */ | ||
1219 | cp = pp->conn_out_get(AF_INET6, skb, pp, &ciph, offset, 1); | ||
1220 | if (cp) { | ||
1221 | ipv6_addr_copy(&snet.in6, &iph->saddr); | ||
1222 | return handle_response_icmp(AF_INET6, skb, &snet, | ||
1223 | cih->nexthdr, | ||
1224 | cp, pp, offset, | ||
1225 | sizeof(struct ipv6hdr)); | ||
1226 | } | ||
1227 | return NF_ACCEPT; | ||
1228 | } | ||
1229 | |||
1230 | verdict = NF_DROP; | ||
1231 | |||
1232 | /* do the statistics and put it back */ | ||
1233 | ip_vs_in_stats(cp, skb); | ||
1234 | if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr) | ||
1235 | offset += 2 * sizeof(__u16); | ||
1236 | verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset); | ||
1237 | /* do not touch skb anymore */ | ||
1238 | |||
1239 | __ip_vs_conn_put(cp); | ||
1240 | |||
1241 | return verdict; | ||
1242 | } | ||
1243 | #endif | ||
1244 | |||
1245 | |||
891 | /* | 1246 | /* |
892 | * Check if it's for virtual services, look it up, | 1247 | * Check if it's for virtual services, look it up, |
893 | * and send it on its way... | 1248 | * and send it on its way... |
@@ -897,50 +1252,54 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, | |||
897 | const struct net_device *in, const struct net_device *out, | 1252 | const struct net_device *in, const struct net_device *out, |
898 | int (*okfn)(struct sk_buff *)) | 1253 | int (*okfn)(struct sk_buff *)) |
899 | { | 1254 | { |
900 | struct iphdr *iph; | 1255 | struct ip_vs_iphdr iph; |
901 | struct ip_vs_protocol *pp; | 1256 | struct ip_vs_protocol *pp; |
902 | struct ip_vs_conn *cp; | 1257 | struct ip_vs_conn *cp; |
903 | int ret, restart; | 1258 | int ret, restart, af; |
904 | int ihl; | 1259 | |
1260 | af = (skb->protocol == __constant_htons(ETH_P_IP)) ? AF_INET : AF_INET6; | ||
1261 | |||
1262 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
905 | 1263 | ||
906 | /* | 1264 | /* |
907 | * Big tappo: only PACKET_HOST (neither loopback nor mcasts) | 1265 | * Big tappo: only PACKET_HOST, including loopback for local client |
908 | * ... don't know why 1st test DOES NOT include 2nd (?) | 1266 | * Don't handle local packets on IPv6 for now |
909 | */ | 1267 | */ |
910 | if (unlikely(skb->pkt_type != PACKET_HOST | 1268 | if (unlikely(skb->pkt_type != PACKET_HOST)) { |
911 | || skb->dev->flags & IFF_LOOPBACK || skb->sk)) { | 1269 | IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n", |
912 | IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n", | 1270 | skb->pkt_type, |
913 | skb->pkt_type, | 1271 | iph.protocol, |
914 | ip_hdr(skb)->protocol, | 1272 | IP_VS_DBG_ADDR(af, &iph.daddr)); |
915 | NIPQUAD(ip_hdr(skb)->daddr)); | ||
916 | return NF_ACCEPT; | 1273 | return NF_ACCEPT; |
917 | } | 1274 | } |
918 | 1275 | ||
919 | iph = ip_hdr(skb); | 1276 | if (unlikely(iph.protocol == IPPROTO_ICMP)) { |
920 | if (unlikely(iph->protocol == IPPROTO_ICMP)) { | ||
921 | int related, verdict = ip_vs_in_icmp(skb, &related, hooknum); | 1277 | int related, verdict = ip_vs_in_icmp(skb, &related, hooknum); |
922 | 1278 | ||
923 | if (related) | 1279 | if (related) |
924 | return verdict; | 1280 | return verdict; |
925 | iph = ip_hdr(skb); | 1281 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
926 | } | 1282 | } |
927 | 1283 | ||
928 | /* Protocol supported? */ | 1284 | /* Protocol supported? */ |
929 | pp = ip_vs_proto_get(iph->protocol); | 1285 | pp = ip_vs_proto_get(iph.protocol); |
930 | if (unlikely(!pp)) | 1286 | if (unlikely(!pp)) |
931 | return NF_ACCEPT; | 1287 | return NF_ACCEPT; |
932 | 1288 | ||
933 | ihl = iph->ihl << 2; | ||
934 | |||
935 | /* | 1289 | /* |
936 | * Check if the packet belongs to an existing connection entry | 1290 | * Check if the packet belongs to an existing connection entry |
937 | */ | 1291 | */ |
938 | cp = pp->conn_in_get(skb, pp, iph, ihl, 0); | 1292 | cp = pp->conn_in_get(af, skb, pp, &iph, iph.len, 0); |
939 | 1293 | ||
940 | if (unlikely(!cp)) { | 1294 | if (unlikely(!cp)) { |
941 | int v; | 1295 | int v; |
942 | 1296 | ||
943 | if (!pp->conn_schedule(skb, pp, &v, &cp)) | 1297 | /* For local client packets, it could be a response */ |
1298 | cp = pp->conn_out_get(af, skb, pp, &iph, iph.len, 0); | ||
1299 | if (cp) | ||
1300 | return handle_response(af, skb, pp, cp, iph.len); | ||
1301 | |||
1302 | if (!pp->conn_schedule(af, skb, pp, &v, &cp)) | ||
944 | return v; | 1303 | return v; |
945 | } | 1304 | } |
946 | 1305 | ||
@@ -984,7 +1343,8 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, | |||
984 | * encorage the standby servers to update the connections timeout | 1343 | * encorage the standby servers to update the connections timeout |
985 | */ | 1344 | */ |
986 | atomic_inc(&cp->in_pkts); | 1345 | atomic_inc(&cp->in_pkts); |
987 | if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && | 1346 | if (af == AF_INET && |
1347 | (ip_vs_sync_state & IP_VS_STATE_MASTER) && | ||
988 | (((cp->protocol != IPPROTO_TCP || | 1348 | (((cp->protocol != IPPROTO_TCP || |
989 | cp->state == IP_VS_TCP_S_ESTABLISHED) && | 1349 | cp->state == IP_VS_TCP_S_ESTABLISHED) && |
990 | (atomic_read(&cp->in_pkts) % sysctl_ip_vs_sync_threshold[1] | 1350 | (atomic_read(&cp->in_pkts) % sysctl_ip_vs_sync_threshold[1] |
@@ -1023,6 +1383,21 @@ ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb, | |||
1023 | return ip_vs_in_icmp(skb, &r, hooknum); | 1383 | return ip_vs_in_icmp(skb, &r, hooknum); |
1024 | } | 1384 | } |
1025 | 1385 | ||
1386 | #ifdef CONFIG_IP_VS_IPV6 | ||
1387 | static unsigned int | ||
1388 | ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb, | ||
1389 | const struct net_device *in, const struct net_device *out, | ||
1390 | int (*okfn)(struct sk_buff *)) | ||
1391 | { | ||
1392 | int r; | ||
1393 | |||
1394 | if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) | ||
1395 | return NF_ACCEPT; | ||
1396 | |||
1397 | return ip_vs_in_icmp_v6(skb, &r, hooknum); | ||
1398 | } | ||
1399 | #endif | ||
1400 | |||
1026 | 1401 | ||
1027 | static struct nf_hook_ops ip_vs_ops[] __read_mostly = { | 1402 | static struct nf_hook_ops ip_vs_ops[] __read_mostly = { |
1028 | /* After packet filtering, forward packet through VS/DR, VS/TUN, | 1403 | /* After packet filtering, forward packet through VS/DR, VS/TUN, |
@@ -1060,6 +1435,43 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = { | |||
1060 | .hooknum = NF_INET_POST_ROUTING, | 1435 | .hooknum = NF_INET_POST_ROUTING, |
1061 | .priority = NF_IP_PRI_NAT_SRC-1, | 1436 | .priority = NF_IP_PRI_NAT_SRC-1, |
1062 | }, | 1437 | }, |
1438 | #ifdef CONFIG_IP_VS_IPV6 | ||
1439 | /* After packet filtering, forward packet through VS/DR, VS/TUN, | ||
1440 | * or VS/NAT(change destination), so that filtering rules can be | ||
1441 | * applied to IPVS. */ | ||
1442 | { | ||
1443 | .hook = ip_vs_in, | ||
1444 | .owner = THIS_MODULE, | ||
1445 | .pf = PF_INET6, | ||
1446 | .hooknum = NF_INET_LOCAL_IN, | ||
1447 | .priority = 100, | ||
1448 | }, | ||
1449 | /* After packet filtering, change source only for VS/NAT */ | ||
1450 | { | ||
1451 | .hook = ip_vs_out, | ||
1452 | .owner = THIS_MODULE, | ||
1453 | .pf = PF_INET6, | ||
1454 | .hooknum = NF_INET_FORWARD, | ||
1455 | .priority = 100, | ||
1456 | }, | ||
1457 | /* After packet filtering (but before ip_vs_out_icmp), catch icmp | ||
1458 | * destined for 0.0.0.0/0, which is for incoming IPVS connections */ | ||
1459 | { | ||
1460 | .hook = ip_vs_forward_icmp_v6, | ||
1461 | .owner = THIS_MODULE, | ||
1462 | .pf = PF_INET6, | ||
1463 | .hooknum = NF_INET_FORWARD, | ||
1464 | .priority = 99, | ||
1465 | }, | ||
1466 | /* Before the netfilter connection tracking, exit from POST_ROUTING */ | ||
1467 | { | ||
1468 | .hook = ip_vs_post_routing, | ||
1469 | .owner = THIS_MODULE, | ||
1470 | .pf = PF_INET6, | ||
1471 | .hooknum = NF_INET_POST_ROUTING, | ||
1472 | .priority = NF_IP6_PRI_NAT_SRC-1, | ||
1473 | }, | ||
1474 | #endif | ||
1063 | }; | 1475 | }; |
1064 | 1476 | ||
1065 | 1477 | ||
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c index ede101eeec17..993a83fb0d56 100644 --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c | |||
@@ -35,6 +35,10 @@ | |||
35 | 35 | ||
36 | #include <net/net_namespace.h> | 36 | #include <net/net_namespace.h> |
37 | #include <net/ip.h> | 37 | #include <net/ip.h> |
38 | #ifdef CONFIG_IP_VS_IPV6 | ||
39 | #include <net/ipv6.h> | ||
40 | #include <net/ip6_route.h> | ||
41 | #endif | ||
38 | #include <net/route.h> | 42 | #include <net/route.h> |
39 | #include <net/sock.h> | 43 | #include <net/sock.h> |
40 | #include <net/genetlink.h> | 44 | #include <net/genetlink.h> |
@@ -91,6 +95,26 @@ int ip_vs_get_debug_level(void) | |||
91 | } | 95 | } |
92 | #endif | 96 | #endif |
93 | 97 | ||
98 | #ifdef CONFIG_IP_VS_IPV6 | ||
99 | /* Taken from rt6_fill_node() in net/ipv6/route.c, is there a better way? */ | ||
100 | static int __ip_vs_addr_is_local_v6(const struct in6_addr *addr) | ||
101 | { | ||
102 | struct rt6_info *rt; | ||
103 | struct flowi fl = { | ||
104 | .oif = 0, | ||
105 | .nl_u = { | ||
106 | .ip6_u = { | ||
107 | .daddr = *addr, | ||
108 | .saddr = { .s6_addr32 = {0, 0, 0, 0} }, } }, | ||
109 | }; | ||
110 | |||
111 | rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl); | ||
112 | if (rt && rt->rt6i_dev && (rt->rt6i_dev->flags & IFF_LOOPBACK)) | ||
113 | return 1; | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | #endif | ||
94 | /* | 118 | /* |
95 | * update_defense_level is called from keventd and from sysctl, | 119 | * update_defense_level is called from keventd and from sysctl, |
96 | * so it needs to protect itself from softirqs | 120 | * so it needs to protect itself from softirqs |
@@ -282,11 +306,19 @@ static atomic_t ip_vs_nullsvc_counter = ATOMIC_INIT(0); | |||
282 | * Returns hash value for virtual service | 306 | * Returns hash value for virtual service |
283 | */ | 307 | */ |
284 | static __inline__ unsigned | 308 | static __inline__ unsigned |
285 | ip_vs_svc_hashkey(unsigned proto, __be32 addr, __be16 port) | 309 | ip_vs_svc_hashkey(int af, unsigned proto, const union nf_inet_addr *addr, |
310 | __be16 port) | ||
286 | { | 311 | { |
287 | register unsigned porth = ntohs(port); | 312 | register unsigned porth = ntohs(port); |
313 | __be32 addr_fold = addr->ip; | ||
314 | |||
315 | #ifdef CONFIG_IP_VS_IPV6 | ||
316 | if (af == AF_INET6) | ||
317 | addr_fold = addr->ip6[0]^addr->ip6[1]^ | ||
318 | addr->ip6[2]^addr->ip6[3]; | ||
319 | #endif | ||
288 | 320 | ||
289 | return (proto^ntohl(addr)^(porth>>IP_VS_SVC_TAB_BITS)^porth) | 321 | return (proto^ntohl(addr_fold)^(porth>>IP_VS_SVC_TAB_BITS)^porth) |
290 | & IP_VS_SVC_TAB_MASK; | 322 | & IP_VS_SVC_TAB_MASK; |
291 | } | 323 | } |
292 | 324 | ||
@@ -317,7 +349,8 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc) | |||
317 | /* | 349 | /* |
318 | * Hash it by <protocol,addr,port> in ip_vs_svc_table | 350 | * Hash it by <protocol,addr,port> in ip_vs_svc_table |
319 | */ | 351 | */ |
320 | hash = ip_vs_svc_hashkey(svc->protocol, svc->addr, svc->port); | 352 | hash = ip_vs_svc_hashkey(svc->af, svc->protocol, &svc->addr, |
353 | svc->port); | ||
321 | list_add(&svc->s_list, &ip_vs_svc_table[hash]); | 354 | list_add(&svc->s_list, &ip_vs_svc_table[hash]); |
322 | } else { | 355 | } else { |
323 | /* | 356 | /* |
@@ -363,17 +396,19 @@ static int ip_vs_svc_unhash(struct ip_vs_service *svc) | |||
363 | /* | 396 | /* |
364 | * Get service by {proto,addr,port} in the service table. | 397 | * Get service by {proto,addr,port} in the service table. |
365 | */ | 398 | */ |
366 | static __inline__ struct ip_vs_service * | 399 | static inline struct ip_vs_service * |
367 | __ip_vs_service_get(__u16 protocol, __be32 vaddr, __be16 vport) | 400 | __ip_vs_service_get(int af, __u16 protocol, const union nf_inet_addr *vaddr, |
401 | __be16 vport) | ||
368 | { | 402 | { |
369 | unsigned hash; | 403 | unsigned hash; |
370 | struct ip_vs_service *svc; | 404 | struct ip_vs_service *svc; |
371 | 405 | ||
372 | /* Check for "full" addressed entries */ | 406 | /* Check for "full" addressed entries */ |
373 | hash = ip_vs_svc_hashkey(protocol, vaddr, vport); | 407 | hash = ip_vs_svc_hashkey(af, protocol, vaddr, vport); |
374 | 408 | ||
375 | list_for_each_entry(svc, &ip_vs_svc_table[hash], s_list){ | 409 | list_for_each_entry(svc, &ip_vs_svc_table[hash], s_list){ |
376 | if ((svc->addr == vaddr) | 410 | if ((svc->af == af) |
411 | && ip_vs_addr_equal(af, &svc->addr, vaddr) | ||
377 | && (svc->port == vport) | 412 | && (svc->port == vport) |
378 | && (svc->protocol == protocol)) { | 413 | && (svc->protocol == protocol)) { |
379 | /* HIT */ | 414 | /* HIT */ |
@@ -389,7 +424,8 @@ __ip_vs_service_get(__u16 protocol, __be32 vaddr, __be16 vport) | |||
389 | /* | 424 | /* |
390 | * Get service by {fwmark} in the service table. | 425 | * Get service by {fwmark} in the service table. |
391 | */ | 426 | */ |
392 | static __inline__ struct ip_vs_service *__ip_vs_svc_fwm_get(__u32 fwmark) | 427 | static inline struct ip_vs_service * |
428 | __ip_vs_svc_fwm_get(int af, __u32 fwmark) | ||
393 | { | 429 | { |
394 | unsigned hash; | 430 | unsigned hash; |
395 | struct ip_vs_service *svc; | 431 | struct ip_vs_service *svc; |
@@ -398,7 +434,7 @@ static __inline__ struct ip_vs_service *__ip_vs_svc_fwm_get(__u32 fwmark) | |||
398 | hash = ip_vs_svc_fwm_hashkey(fwmark); | 434 | hash = ip_vs_svc_fwm_hashkey(fwmark); |
399 | 435 | ||
400 | list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) { | 436 | list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) { |
401 | if (svc->fwmark == fwmark) { | 437 | if (svc->fwmark == fwmark && svc->af == af) { |
402 | /* HIT */ | 438 | /* HIT */ |
403 | atomic_inc(&svc->usecnt); | 439 | atomic_inc(&svc->usecnt); |
404 | return svc; | 440 | return svc; |
@@ -409,7 +445,8 @@ static __inline__ struct ip_vs_service *__ip_vs_svc_fwm_get(__u32 fwmark) | |||
409 | } | 445 | } |
410 | 446 | ||
411 | struct ip_vs_service * | 447 | struct ip_vs_service * |
412 | ip_vs_service_get(__u32 fwmark, __u16 protocol, __be32 vaddr, __be16 vport) | 448 | ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, |
449 | const union nf_inet_addr *vaddr, __be16 vport) | ||
413 | { | 450 | { |
414 | struct ip_vs_service *svc; | 451 | struct ip_vs_service *svc; |
415 | 452 | ||
@@ -418,14 +455,14 @@ ip_vs_service_get(__u32 fwmark, __u16 protocol, __be32 vaddr, __be16 vport) | |||
418 | /* | 455 | /* |
419 | * Check the table hashed by fwmark first | 456 | * Check the table hashed by fwmark first |
420 | */ | 457 | */ |
421 | if (fwmark && (svc = __ip_vs_svc_fwm_get(fwmark))) | 458 | if (fwmark && (svc = __ip_vs_svc_fwm_get(af, fwmark))) |
422 | goto out; | 459 | goto out; |
423 | 460 | ||
424 | /* | 461 | /* |
425 | * Check the table hashed by <protocol,addr,port> | 462 | * Check the table hashed by <protocol,addr,port> |
426 | * for "full" addressed entries | 463 | * for "full" addressed entries |
427 | */ | 464 | */ |
428 | svc = __ip_vs_service_get(protocol, vaddr, vport); | 465 | svc = __ip_vs_service_get(af, protocol, vaddr, vport); |
429 | 466 | ||
430 | if (svc == NULL | 467 | if (svc == NULL |
431 | && protocol == IPPROTO_TCP | 468 | && protocol == IPPROTO_TCP |
@@ -435,7 +472,7 @@ ip_vs_service_get(__u32 fwmark, __u16 protocol, __be32 vaddr, __be16 vport) | |||
435 | * Check if ftp service entry exists, the packet | 472 | * Check if ftp service entry exists, the packet |
436 | * might belong to FTP data connections. | 473 | * might belong to FTP data connections. |
437 | */ | 474 | */ |
438 | svc = __ip_vs_service_get(protocol, vaddr, FTPPORT); | 475 | svc = __ip_vs_service_get(af, protocol, vaddr, FTPPORT); |
439 | } | 476 | } |
440 | 477 | ||
441 | if (svc == NULL | 478 | if (svc == NULL |
@@ -443,16 +480,16 @@ ip_vs_service_get(__u32 fwmark, __u16 protocol, __be32 vaddr, __be16 vport) | |||
443 | /* | 480 | /* |
444 | * Check if the catch-all port (port zero) exists | 481 | * Check if the catch-all port (port zero) exists |
445 | */ | 482 | */ |
446 | svc = __ip_vs_service_get(protocol, vaddr, 0); | 483 | svc = __ip_vs_service_get(af, protocol, vaddr, 0); |
447 | } | 484 | } |
448 | 485 | ||
449 | out: | 486 | out: |
450 | read_unlock(&__ip_vs_svc_lock); | 487 | read_unlock(&__ip_vs_svc_lock); |
451 | 488 | ||
452 | IP_VS_DBG(9, "lookup service: fwm %u %s %u.%u.%u.%u:%u %s\n", | 489 | IP_VS_DBG_BUF(9, "lookup service: fwm %u %s %s:%u %s\n", |
453 | fwmark, ip_vs_proto_name(protocol), | 490 | fwmark, ip_vs_proto_name(protocol), |
454 | NIPQUAD(vaddr), ntohs(vport), | 491 | IP_VS_DBG_ADDR(af, vaddr), ntohs(vport), |
455 | svc?"hit":"not hit"); | 492 | svc ? "hit" : "not hit"); |
456 | 493 | ||
457 | return svc; | 494 | return svc; |
458 | } | 495 | } |
@@ -479,11 +516,20 @@ __ip_vs_unbind_svc(struct ip_vs_dest *dest) | |||
479 | /* | 516 | /* |
480 | * Returns hash value for real service | 517 | * Returns hash value for real service |
481 | */ | 518 | */ |
482 | static __inline__ unsigned ip_vs_rs_hashkey(__be32 addr, __be16 port) | 519 | static inline unsigned ip_vs_rs_hashkey(int af, |
520 | const union nf_inet_addr *addr, | ||
521 | __be16 port) | ||
483 | { | 522 | { |
484 | register unsigned porth = ntohs(port); | 523 | register unsigned porth = ntohs(port); |
524 | __be32 addr_fold = addr->ip; | ||
525 | |||
526 | #ifdef CONFIG_IP_VS_IPV6 | ||
527 | if (af == AF_INET6) | ||
528 | addr_fold = addr->ip6[0]^addr->ip6[1]^ | ||
529 | addr->ip6[2]^addr->ip6[3]; | ||
530 | #endif | ||
485 | 531 | ||
486 | return (ntohl(addr)^(porth>>IP_VS_RTAB_BITS)^porth) | 532 | return (ntohl(addr_fold)^(porth>>IP_VS_RTAB_BITS)^porth) |
487 | & IP_VS_RTAB_MASK; | 533 | & IP_VS_RTAB_MASK; |
488 | } | 534 | } |
489 | 535 | ||
@@ -503,7 +549,8 @@ static int ip_vs_rs_hash(struct ip_vs_dest *dest) | |||
503 | * Hash by proto,addr,port, | 549 | * Hash by proto,addr,port, |
504 | * which are the parameters of the real service. | 550 | * which are the parameters of the real service. |
505 | */ | 551 | */ |
506 | hash = ip_vs_rs_hashkey(dest->addr, dest->port); | 552 | hash = ip_vs_rs_hashkey(dest->af, &dest->addr, dest->port); |
553 | |||
507 | list_add(&dest->d_list, &ip_vs_rtable[hash]); | 554 | list_add(&dest->d_list, &ip_vs_rtable[hash]); |
508 | 555 | ||
509 | return 1; | 556 | return 1; |
@@ -530,7 +577,9 @@ static int ip_vs_rs_unhash(struct ip_vs_dest *dest) | |||
530 | * Lookup real service by <proto,addr,port> in the real service table. | 577 | * Lookup real service by <proto,addr,port> in the real service table. |
531 | */ | 578 | */ |
532 | struct ip_vs_dest * | 579 | struct ip_vs_dest * |
533 | ip_vs_lookup_real_service(__u16 protocol, __be32 daddr, __be16 dport) | 580 | ip_vs_lookup_real_service(int af, __u16 protocol, |
581 | const union nf_inet_addr *daddr, | ||
582 | __be16 dport) | ||
534 | { | 583 | { |
535 | unsigned hash; | 584 | unsigned hash; |
536 | struct ip_vs_dest *dest; | 585 | struct ip_vs_dest *dest; |
@@ -539,11 +588,12 @@ ip_vs_lookup_real_service(__u16 protocol, __be32 daddr, __be16 dport) | |||
539 | * Check for "full" addressed entries | 588 | * Check for "full" addressed entries |
540 | * Return the first found entry | 589 | * Return the first found entry |
541 | */ | 590 | */ |
542 | hash = ip_vs_rs_hashkey(daddr, dport); | 591 | hash = ip_vs_rs_hashkey(af, daddr, dport); |
543 | 592 | ||
544 | read_lock(&__ip_vs_rs_lock); | 593 | read_lock(&__ip_vs_rs_lock); |
545 | list_for_each_entry(dest, &ip_vs_rtable[hash], d_list) { | 594 | list_for_each_entry(dest, &ip_vs_rtable[hash], d_list) { |
546 | if ((dest->addr == daddr) | 595 | if ((dest->af == af) |
596 | && ip_vs_addr_equal(af, &dest->addr, daddr) | ||
547 | && (dest->port == dport) | 597 | && (dest->port == dport) |
548 | && ((dest->protocol == protocol) || | 598 | && ((dest->protocol == protocol) || |
549 | dest->vfwmark)) { | 599 | dest->vfwmark)) { |
@@ -561,7 +611,8 @@ ip_vs_lookup_real_service(__u16 protocol, __be32 daddr, __be16 dport) | |||
561 | * Lookup destination by {addr,port} in the given service | 611 | * Lookup destination by {addr,port} in the given service |
562 | */ | 612 | */ |
563 | static struct ip_vs_dest * | 613 | static struct ip_vs_dest * |
564 | ip_vs_lookup_dest(struct ip_vs_service *svc, __be32 daddr, __be16 dport) | 614 | ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, |
615 | __be16 dport) | ||
565 | { | 616 | { |
566 | struct ip_vs_dest *dest; | 617 | struct ip_vs_dest *dest; |
567 | 618 | ||
@@ -569,7 +620,9 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, __be32 daddr, __be16 dport) | |||
569 | * Find the destination for the given service | 620 | * Find the destination for the given service |
570 | */ | 621 | */ |
571 | list_for_each_entry(dest, &svc->destinations, n_list) { | 622 | list_for_each_entry(dest, &svc->destinations, n_list) { |
572 | if ((dest->addr == daddr) && (dest->port == dport)) { | 623 | if ((dest->af == svc->af) |
624 | && ip_vs_addr_equal(svc->af, &dest->addr, daddr) | ||
625 | && (dest->port == dport)) { | ||
573 | /* HIT */ | 626 | /* HIT */ |
574 | return dest; | 627 | return dest; |
575 | } | 628 | } |
@@ -588,13 +641,15 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, __be32 daddr, __be16 dport) | |||
588 | * ip_vs_lookup_real_service() looked promissing, but | 641 | * ip_vs_lookup_real_service() looked promissing, but |
589 | * seems not working as expected. | 642 | * seems not working as expected. |
590 | */ | 643 | */ |
591 | struct ip_vs_dest *ip_vs_find_dest(__be32 daddr, __be16 dport, | 644 | struct ip_vs_dest *ip_vs_find_dest(int af, const union nf_inet_addr *daddr, |
592 | __be32 vaddr, __be16 vport, __u16 protocol) | 645 | __be16 dport, |
646 | const union nf_inet_addr *vaddr, | ||
647 | __be16 vport, __u16 protocol) | ||
593 | { | 648 | { |
594 | struct ip_vs_dest *dest; | 649 | struct ip_vs_dest *dest; |
595 | struct ip_vs_service *svc; | 650 | struct ip_vs_service *svc; |
596 | 651 | ||
597 | svc = ip_vs_service_get(0, protocol, vaddr, vport); | 652 | svc = ip_vs_service_get(af, 0, protocol, vaddr, vport); |
598 | if (!svc) | 653 | if (!svc) |
599 | return NULL; | 654 | return NULL; |
600 | dest = ip_vs_lookup_dest(svc, daddr, dport); | 655 | dest = ip_vs_lookup_dest(svc, daddr, dport); |
@@ -615,7 +670,8 @@ struct ip_vs_dest *ip_vs_find_dest(__be32 daddr, __be16 dport, | |||
615 | * scheduling. | 670 | * scheduling. |
616 | */ | 671 | */ |
617 | static struct ip_vs_dest * | 672 | static struct ip_vs_dest * |
618 | ip_vs_trash_get_dest(struct ip_vs_service *svc, __be32 daddr, __be16 dport) | 673 | ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, |
674 | __be16 dport) | ||
619 | { | 675 | { |
620 | struct ip_vs_dest *dest, *nxt; | 676 | struct ip_vs_dest *dest, *nxt; |
621 | 677 | ||
@@ -623,17 +679,19 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, __be32 daddr, __be16 dport) | |||
623 | * Find the destination in trash | 679 | * Find the destination in trash |
624 | */ | 680 | */ |
625 | list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) { | 681 | list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) { |
626 | IP_VS_DBG(3, "Destination %u/%u.%u.%u.%u:%u still in trash, " | 682 | IP_VS_DBG_BUF(3, "Destination %u/%s:%u still in trash, " |
627 | "dest->refcnt=%d\n", | 683 | "dest->refcnt=%d\n", |
628 | dest->vfwmark, | 684 | dest->vfwmark, |
629 | NIPQUAD(dest->addr), ntohs(dest->port), | 685 | IP_VS_DBG_ADDR(svc->af, &dest->addr), |
630 | atomic_read(&dest->refcnt)); | 686 | ntohs(dest->port), |
631 | if (dest->addr == daddr && | 687 | atomic_read(&dest->refcnt)); |
688 | if (dest->af == svc->af && | ||
689 | ip_vs_addr_equal(svc->af, &dest->addr, daddr) && | ||
632 | dest->port == dport && | 690 | dest->port == dport && |
633 | dest->vfwmark == svc->fwmark && | 691 | dest->vfwmark == svc->fwmark && |
634 | dest->protocol == svc->protocol && | 692 | dest->protocol == svc->protocol && |
635 | (svc->fwmark || | 693 | (svc->fwmark || |
636 | (dest->vaddr == svc->addr && | 694 | (ip_vs_addr_equal(svc->af, &dest->vaddr, &svc->addr) && |
637 | dest->vport == svc->port))) { | 695 | dest->vport == svc->port))) { |
638 | /* HIT */ | 696 | /* HIT */ |
639 | return dest; | 697 | return dest; |
@@ -643,10 +701,11 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, __be32 daddr, __be16 dport) | |||
643 | * Try to purge the destination from trash if not referenced | 701 | * Try to purge the destination from trash if not referenced |
644 | */ | 702 | */ |
645 | if (atomic_read(&dest->refcnt) == 1) { | 703 | if (atomic_read(&dest->refcnt) == 1) { |
646 | IP_VS_DBG(3, "Removing destination %u/%u.%u.%u.%u:%u " | 704 | IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u " |
647 | "from trash\n", | 705 | "from trash\n", |
648 | dest->vfwmark, | 706 | dest->vfwmark, |
649 | NIPQUAD(dest->addr), ntohs(dest->port)); | 707 | IP_VS_DBG_ADDR(svc->af, &dest->addr), |
708 | ntohs(dest->port)); | ||
650 | list_del(&dest->n_list); | 709 | list_del(&dest->n_list); |
651 | ip_vs_dst_reset(dest); | 710 | ip_vs_dst_reset(dest); |
652 | __ip_vs_unbind_svc(dest); | 711 | __ip_vs_unbind_svc(dest); |
@@ -685,18 +744,7 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) | |||
685 | { | 744 | { |
686 | spin_lock_bh(&stats->lock); | 745 | spin_lock_bh(&stats->lock); |
687 | 746 | ||
688 | stats->conns = 0; | 747 | memset(&stats->ustats, 0, sizeof(stats->ustats)); |
689 | stats->inpkts = 0; | ||
690 | stats->outpkts = 0; | ||
691 | stats->inbytes = 0; | ||
692 | stats->outbytes = 0; | ||
693 | |||
694 | stats->cps = 0; | ||
695 | stats->inpps = 0; | ||
696 | stats->outpps = 0; | ||
697 | stats->inbps = 0; | ||
698 | stats->outbps = 0; | ||
699 | |||
700 | ip_vs_zero_estimator(stats); | 748 | ip_vs_zero_estimator(stats); |
701 | 749 | ||
702 | spin_unlock_bh(&stats->lock); | 750 | spin_unlock_bh(&stats->lock); |
@@ -707,7 +755,7 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) | |||
707 | */ | 755 | */ |
708 | static void | 756 | static void |
709 | __ip_vs_update_dest(struct ip_vs_service *svc, | 757 | __ip_vs_update_dest(struct ip_vs_service *svc, |
710 | struct ip_vs_dest *dest, struct ip_vs_dest_user *udest) | 758 | struct ip_vs_dest *dest, struct ip_vs_dest_user_kern *udest) |
711 | { | 759 | { |
712 | int conn_flags; | 760 | int conn_flags; |
713 | 761 | ||
@@ -716,10 +764,18 @@ __ip_vs_update_dest(struct ip_vs_service *svc, | |||
716 | conn_flags = udest->conn_flags | IP_VS_CONN_F_INACTIVE; | 764 | conn_flags = udest->conn_flags | IP_VS_CONN_F_INACTIVE; |
717 | 765 | ||
718 | /* check if local node and update the flags */ | 766 | /* check if local node and update the flags */ |
719 | if (inet_addr_type(&init_net, udest->addr) == RTN_LOCAL) { | 767 | #ifdef CONFIG_IP_VS_IPV6 |
720 | conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) | 768 | if (svc->af == AF_INET6) { |
721 | | IP_VS_CONN_F_LOCALNODE; | 769 | if (__ip_vs_addr_is_local_v6(&udest->addr.in6)) { |
722 | } | 770 | conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) |
771 | | IP_VS_CONN_F_LOCALNODE; | ||
772 | } | ||
773 | } else | ||
774 | #endif | ||
775 | if (inet_addr_type(&init_net, udest->addr.ip) == RTN_LOCAL) { | ||
776 | conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) | ||
777 | | IP_VS_CONN_F_LOCALNODE; | ||
778 | } | ||
723 | 779 | ||
724 | /* set the IP_VS_CONN_F_NOOUTPUT flag if not masquerading/NAT */ | 780 | /* set the IP_VS_CONN_F_NOOUTPUT flag if not masquerading/NAT */ |
725 | if ((conn_flags & IP_VS_CONN_F_FWD_MASK) != 0) { | 781 | if ((conn_flags & IP_VS_CONN_F_FWD_MASK) != 0) { |
@@ -760,7 +816,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, | |||
760 | * Create a destination for the given service | 816 | * Create a destination for the given service |
761 | */ | 817 | */ |
762 | static int | 818 | static int |
763 | ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, | 819 | ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, |
764 | struct ip_vs_dest **dest_p) | 820 | struct ip_vs_dest **dest_p) |
765 | { | 821 | { |
766 | struct ip_vs_dest *dest; | 822 | struct ip_vs_dest *dest; |
@@ -768,9 +824,20 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, | |||
768 | 824 | ||
769 | EnterFunction(2); | 825 | EnterFunction(2); |
770 | 826 | ||
771 | atype = inet_addr_type(&init_net, udest->addr); | 827 | #ifdef CONFIG_IP_VS_IPV6 |
772 | if (atype != RTN_LOCAL && atype != RTN_UNICAST) | 828 | if (svc->af == AF_INET6) { |
773 | return -EINVAL; | 829 | atype = ipv6_addr_type(&udest->addr.in6); |
830 | if ((!(atype & IPV6_ADDR_UNICAST) || | ||
831 | atype & IPV6_ADDR_LINKLOCAL) && | ||
832 | !__ip_vs_addr_is_local_v6(&udest->addr.in6)) | ||
833 | return -EINVAL; | ||
834 | } else | ||
835 | #endif | ||
836 | { | ||
837 | atype = inet_addr_type(&init_net, udest->addr.ip); | ||
838 | if (atype != RTN_LOCAL && atype != RTN_UNICAST) | ||
839 | return -EINVAL; | ||
840 | } | ||
774 | 841 | ||
775 | dest = kzalloc(sizeof(struct ip_vs_dest), GFP_ATOMIC); | 842 | dest = kzalloc(sizeof(struct ip_vs_dest), GFP_ATOMIC); |
776 | if (dest == NULL) { | 843 | if (dest == NULL) { |
@@ -778,11 +845,12 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, | |||
778 | return -ENOMEM; | 845 | return -ENOMEM; |
779 | } | 846 | } |
780 | 847 | ||
848 | dest->af = svc->af; | ||
781 | dest->protocol = svc->protocol; | 849 | dest->protocol = svc->protocol; |
782 | dest->vaddr = svc->addr; | 850 | dest->vaddr = svc->addr; |
783 | dest->vport = svc->port; | 851 | dest->vport = svc->port; |
784 | dest->vfwmark = svc->fwmark; | 852 | dest->vfwmark = svc->fwmark; |
785 | dest->addr = udest->addr; | 853 | ip_vs_addr_copy(svc->af, &dest->addr, &udest->addr); |
786 | dest->port = udest->port; | 854 | dest->port = udest->port; |
787 | 855 | ||
788 | atomic_set(&dest->activeconns, 0); | 856 | atomic_set(&dest->activeconns, 0); |
@@ -807,10 +875,10 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, | |||
807 | * Add a destination into an existing service | 875 | * Add a destination into an existing service |
808 | */ | 876 | */ |
809 | static int | 877 | static int |
810 | ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) | 878 | ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) |
811 | { | 879 | { |
812 | struct ip_vs_dest *dest; | 880 | struct ip_vs_dest *dest; |
813 | __be32 daddr = udest->addr; | 881 | union nf_inet_addr daddr; |
814 | __be16 dport = udest->port; | 882 | __be16 dport = udest->port; |
815 | int ret; | 883 | int ret; |
816 | 884 | ||
@@ -827,10 +895,13 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) | |||
827 | return -ERANGE; | 895 | return -ERANGE; |
828 | } | 896 | } |
829 | 897 | ||
898 | ip_vs_addr_copy(svc->af, &daddr, &udest->addr); | ||
899 | |||
830 | /* | 900 | /* |
831 | * Check if the dest already exists in the list | 901 | * Check if the dest already exists in the list |
832 | */ | 902 | */ |
833 | dest = ip_vs_lookup_dest(svc, daddr, dport); | 903 | dest = ip_vs_lookup_dest(svc, &daddr, dport); |
904 | |||
834 | if (dest != NULL) { | 905 | if (dest != NULL) { |
835 | IP_VS_DBG(1, "ip_vs_add_dest(): dest already exists\n"); | 906 | IP_VS_DBG(1, "ip_vs_add_dest(): dest already exists\n"); |
836 | return -EEXIST; | 907 | return -EEXIST; |
@@ -840,15 +911,17 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) | |||
840 | * Check if the dest already exists in the trash and | 911 | * Check if the dest already exists in the trash and |
841 | * is from the same service | 912 | * is from the same service |
842 | */ | 913 | */ |
843 | dest = ip_vs_trash_get_dest(svc, daddr, dport); | 914 | dest = ip_vs_trash_get_dest(svc, &daddr, dport); |
915 | |||
844 | if (dest != NULL) { | 916 | if (dest != NULL) { |
845 | IP_VS_DBG(3, "Get destination %u.%u.%u.%u:%u from trash, " | 917 | IP_VS_DBG_BUF(3, "Get destination %s:%u from trash, " |
846 | "dest->refcnt=%d, service %u/%u.%u.%u.%u:%u\n", | 918 | "dest->refcnt=%d, service %u/%s:%u\n", |
847 | NIPQUAD(daddr), ntohs(dport), | 919 | IP_VS_DBG_ADDR(svc->af, &daddr), ntohs(dport), |
848 | atomic_read(&dest->refcnt), | 920 | atomic_read(&dest->refcnt), |
849 | dest->vfwmark, | 921 | dest->vfwmark, |
850 | NIPQUAD(dest->vaddr), | 922 | IP_VS_DBG_ADDR(svc->af, &dest->vaddr), |
851 | ntohs(dest->vport)); | 923 | ntohs(dest->vport)); |
924 | |||
852 | __ip_vs_update_dest(svc, dest, udest); | 925 | __ip_vs_update_dest(svc, dest, udest); |
853 | 926 | ||
854 | /* | 927 | /* |
@@ -915,10 +988,10 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) | |||
915 | * Edit a destination in the given service | 988 | * Edit a destination in the given service |
916 | */ | 989 | */ |
917 | static int | 990 | static int |
918 | ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) | 991 | ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) |
919 | { | 992 | { |
920 | struct ip_vs_dest *dest; | 993 | struct ip_vs_dest *dest; |
921 | __be32 daddr = udest->addr; | 994 | union nf_inet_addr daddr; |
922 | __be16 dport = udest->port; | 995 | __be16 dport = udest->port; |
923 | 996 | ||
924 | EnterFunction(2); | 997 | EnterFunction(2); |
@@ -934,10 +1007,13 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) | |||
934 | return -ERANGE; | 1007 | return -ERANGE; |
935 | } | 1008 | } |
936 | 1009 | ||
1010 | ip_vs_addr_copy(svc->af, &daddr, &udest->addr); | ||
1011 | |||
937 | /* | 1012 | /* |
938 | * Lookup the destination list | 1013 | * Lookup the destination list |
939 | */ | 1014 | */ |
940 | dest = ip_vs_lookup_dest(svc, daddr, dport); | 1015 | dest = ip_vs_lookup_dest(svc, &daddr, dport); |
1016 | |||
941 | if (dest == NULL) { | 1017 | if (dest == NULL) { |
942 | IP_VS_DBG(1, "ip_vs_edit_dest(): dest doesn't exist\n"); | 1018 | IP_VS_DBG(1, "ip_vs_edit_dest(): dest doesn't exist\n"); |
943 | return -ENOENT; | 1019 | return -ENOENT; |
@@ -991,10 +1067,11 @@ static void __ip_vs_del_dest(struct ip_vs_dest *dest) | |||
991 | atomic_dec(&dest->svc->refcnt); | 1067 | atomic_dec(&dest->svc->refcnt); |
992 | kfree(dest); | 1068 | kfree(dest); |
993 | } else { | 1069 | } else { |
994 | IP_VS_DBG(3, "Moving dest %u.%u.%u.%u:%u into trash, " | 1070 | IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, " |
995 | "dest->refcnt=%d\n", | 1071 | "dest->refcnt=%d\n", |
996 | NIPQUAD(dest->addr), ntohs(dest->port), | 1072 | IP_VS_DBG_ADDR(dest->af, &dest->addr), |
997 | atomic_read(&dest->refcnt)); | 1073 | ntohs(dest->port), |
1074 | atomic_read(&dest->refcnt)); | ||
998 | list_add(&dest->n_list, &ip_vs_dest_trash); | 1075 | list_add(&dest->n_list, &ip_vs_dest_trash); |
999 | atomic_inc(&dest->refcnt); | 1076 | atomic_inc(&dest->refcnt); |
1000 | } | 1077 | } |
@@ -1028,15 +1105,15 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc, | |||
1028 | * Delete a destination server in the given service | 1105 | * Delete a destination server in the given service |
1029 | */ | 1106 | */ |
1030 | static int | 1107 | static int |
1031 | ip_vs_del_dest(struct ip_vs_service *svc,struct ip_vs_dest_user *udest) | 1108 | ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) |
1032 | { | 1109 | { |
1033 | struct ip_vs_dest *dest; | 1110 | struct ip_vs_dest *dest; |
1034 | __be32 daddr = udest->addr; | ||
1035 | __be16 dport = udest->port; | 1111 | __be16 dport = udest->port; |
1036 | 1112 | ||
1037 | EnterFunction(2); | 1113 | EnterFunction(2); |
1038 | 1114 | ||
1039 | dest = ip_vs_lookup_dest(svc, daddr, dport); | 1115 | dest = ip_vs_lookup_dest(svc, &udest->addr, dport); |
1116 | |||
1040 | if (dest == NULL) { | 1117 | if (dest == NULL) { |
1041 | IP_VS_DBG(1, "ip_vs_del_dest(): destination not found!\n"); | 1118 | IP_VS_DBG(1, "ip_vs_del_dest(): destination not found!\n"); |
1042 | return -ENOENT; | 1119 | return -ENOENT; |
@@ -1071,7 +1148,8 @@ ip_vs_del_dest(struct ip_vs_service *svc,struct ip_vs_dest_user *udest) | |||
1071 | * Add a service into the service hash table | 1148 | * Add a service into the service hash table |
1072 | */ | 1149 | */ |
1073 | static int | 1150 | static int |
1074 | ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) | 1151 | ip_vs_add_service(struct ip_vs_service_user_kern *u, |
1152 | struct ip_vs_service **svc_p) | ||
1075 | { | 1153 | { |
1076 | int ret = 0; | 1154 | int ret = 0; |
1077 | struct ip_vs_scheduler *sched = NULL; | 1155 | struct ip_vs_scheduler *sched = NULL; |
@@ -1089,6 +1167,19 @@ ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) | |||
1089 | goto out_mod_dec; | 1167 | goto out_mod_dec; |
1090 | } | 1168 | } |
1091 | 1169 | ||
1170 | #ifdef CONFIG_IP_VS_IPV6 | ||
1171 | if (u->af == AF_INET6) { | ||
1172 | if (!sched->supports_ipv6) { | ||
1173 | ret = -EAFNOSUPPORT; | ||
1174 | goto out_err; | ||
1175 | } | ||
1176 | if ((u->netmask < 1) || (u->netmask > 128)) { | ||
1177 | ret = -EINVAL; | ||
1178 | goto out_err; | ||
1179 | } | ||
1180 | } | ||
1181 | #endif | ||
1182 | |||
1092 | svc = kzalloc(sizeof(struct ip_vs_service), GFP_ATOMIC); | 1183 | svc = kzalloc(sizeof(struct ip_vs_service), GFP_ATOMIC); |
1093 | if (svc == NULL) { | 1184 | if (svc == NULL) { |
1094 | IP_VS_DBG(1, "ip_vs_add_service: kmalloc failed.\n"); | 1185 | IP_VS_DBG(1, "ip_vs_add_service: kmalloc failed.\n"); |
@@ -1100,8 +1191,9 @@ ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) | |||
1100 | atomic_set(&svc->usecnt, 1); | 1191 | atomic_set(&svc->usecnt, 1); |
1101 | atomic_set(&svc->refcnt, 0); | 1192 | atomic_set(&svc->refcnt, 0); |
1102 | 1193 | ||
1194 | svc->af = u->af; | ||
1103 | svc->protocol = u->protocol; | 1195 | svc->protocol = u->protocol; |
1104 | svc->addr = u->addr; | 1196 | ip_vs_addr_copy(svc->af, &svc->addr, &u->addr); |
1105 | svc->port = u->port; | 1197 | svc->port = u->port; |
1106 | svc->fwmark = u->fwmark; | 1198 | svc->fwmark = u->fwmark; |
1107 | svc->flags = u->flags; | 1199 | svc->flags = u->flags; |
@@ -1125,7 +1217,10 @@ ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) | |||
1125 | atomic_inc(&ip_vs_nullsvc_counter); | 1217 | atomic_inc(&ip_vs_nullsvc_counter); |
1126 | 1218 | ||
1127 | ip_vs_new_estimator(&svc->stats); | 1219 | ip_vs_new_estimator(&svc->stats); |
1128 | ip_vs_num_services++; | 1220 | |
1221 | /* Count only IPv4 services for old get/setsockopt interface */ | ||
1222 | if (svc->af == AF_INET) | ||
1223 | ip_vs_num_services++; | ||
1129 | 1224 | ||
1130 | /* Hash the service into the service table */ | 1225 | /* Hash the service into the service table */ |
1131 | write_lock_bh(&__ip_vs_svc_lock); | 1226 | write_lock_bh(&__ip_vs_svc_lock); |
@@ -1160,7 +1255,7 @@ ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) | |||
1160 | * Edit a service and bind it with a new scheduler | 1255 | * Edit a service and bind it with a new scheduler |
1161 | */ | 1256 | */ |
1162 | static int | 1257 | static int |
1163 | ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user *u) | 1258 | ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) |
1164 | { | 1259 | { |
1165 | struct ip_vs_scheduler *sched, *old_sched; | 1260 | struct ip_vs_scheduler *sched, *old_sched; |
1166 | int ret = 0; | 1261 | int ret = 0; |
@@ -1176,6 +1271,19 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user *u) | |||
1176 | } | 1271 | } |
1177 | old_sched = sched; | 1272 | old_sched = sched; |
1178 | 1273 | ||
1274 | #ifdef CONFIG_IP_VS_IPV6 | ||
1275 | if (u->af == AF_INET6) { | ||
1276 | if (!sched->supports_ipv6) { | ||
1277 | ret = -EAFNOSUPPORT; | ||
1278 | goto out; | ||
1279 | } | ||
1280 | if ((u->netmask < 1) || (u->netmask > 128)) { | ||
1281 | ret = -EINVAL; | ||
1282 | goto out; | ||
1283 | } | ||
1284 | } | ||
1285 | #endif | ||
1286 | |||
1179 | write_lock_bh(&__ip_vs_svc_lock); | 1287 | write_lock_bh(&__ip_vs_svc_lock); |
1180 | 1288 | ||
1181 | /* | 1289 | /* |
@@ -1240,7 +1348,10 @@ static void __ip_vs_del_service(struct ip_vs_service *svc) | |||
1240 | struct ip_vs_dest *dest, *nxt; | 1348 | struct ip_vs_dest *dest, *nxt; |
1241 | struct ip_vs_scheduler *old_sched; | 1349 | struct ip_vs_scheduler *old_sched; |
1242 | 1350 | ||
1243 | ip_vs_num_services--; | 1351 | /* Count only IPv4 services for old get/setsockopt interface */ |
1352 | if (svc->af == AF_INET) | ||
1353 | ip_vs_num_services--; | ||
1354 | |||
1244 | ip_vs_kill_estimator(&svc->stats); | 1355 | ip_vs_kill_estimator(&svc->stats); |
1245 | 1356 | ||
1246 | /* Unbind scheduler */ | 1357 | /* Unbind scheduler */ |
@@ -1748,15 +1859,25 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v) | |||
1748 | const struct ip_vs_iter *iter = seq->private; | 1859 | const struct ip_vs_iter *iter = seq->private; |
1749 | const struct ip_vs_dest *dest; | 1860 | const struct ip_vs_dest *dest; |
1750 | 1861 | ||
1751 | if (iter->table == ip_vs_svc_table) | 1862 | if (iter->table == ip_vs_svc_table) { |
1752 | seq_printf(seq, "%s %08X:%04X %s ", | 1863 | #ifdef CONFIG_IP_VS_IPV6 |
1753 | ip_vs_proto_name(svc->protocol), | 1864 | if (svc->af == AF_INET6) |
1754 | ntohl(svc->addr), | 1865 | seq_printf(seq, "%s [" NIP6_FMT "]:%04X %s ", |
1755 | ntohs(svc->port), | 1866 | ip_vs_proto_name(svc->protocol), |
1756 | svc->scheduler->name); | 1867 | NIP6(svc->addr.in6), |
1757 | else | 1868 | ntohs(svc->port), |
1869 | svc->scheduler->name); | ||
1870 | else | ||
1871 | #endif | ||
1872 | seq_printf(seq, "%s %08X:%04X %s ", | ||
1873 | ip_vs_proto_name(svc->protocol), | ||
1874 | ntohl(svc->addr.ip), | ||
1875 | ntohs(svc->port), | ||
1876 | svc->scheduler->name); | ||
1877 | } else { | ||
1758 | seq_printf(seq, "FWM %08X %s ", | 1878 | seq_printf(seq, "FWM %08X %s ", |
1759 | svc->fwmark, svc->scheduler->name); | 1879 | svc->fwmark, svc->scheduler->name); |
1880 | } | ||
1760 | 1881 | ||
1761 | if (svc->flags & IP_VS_SVC_F_PERSISTENT) | 1882 | if (svc->flags & IP_VS_SVC_F_PERSISTENT) |
1762 | seq_printf(seq, "persistent %d %08X\n", | 1883 | seq_printf(seq, "persistent %d %08X\n", |
@@ -1766,13 +1887,29 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v) | |||
1766 | seq_putc(seq, '\n'); | 1887 | seq_putc(seq, '\n'); |
1767 | 1888 | ||
1768 | list_for_each_entry(dest, &svc->destinations, n_list) { | 1889 | list_for_each_entry(dest, &svc->destinations, n_list) { |
1769 | seq_printf(seq, | 1890 | #ifdef CONFIG_IP_VS_IPV6 |
1770 | " -> %08X:%04X %-7s %-6d %-10d %-10d\n", | 1891 | if (dest->af == AF_INET6) |
1771 | ntohl(dest->addr), ntohs(dest->port), | 1892 | seq_printf(seq, |
1772 | ip_vs_fwd_name(atomic_read(&dest->conn_flags)), | 1893 | " -> [" NIP6_FMT "]:%04X" |
1773 | atomic_read(&dest->weight), | 1894 | " %-7s %-6d %-10d %-10d\n", |
1774 | atomic_read(&dest->activeconns), | 1895 | NIP6(dest->addr.in6), |
1775 | atomic_read(&dest->inactconns)); | 1896 | ntohs(dest->port), |
1897 | ip_vs_fwd_name(atomic_read(&dest->conn_flags)), | ||
1898 | atomic_read(&dest->weight), | ||
1899 | atomic_read(&dest->activeconns), | ||
1900 | atomic_read(&dest->inactconns)); | ||
1901 | else | ||
1902 | #endif | ||
1903 | seq_printf(seq, | ||
1904 | " -> %08X:%04X " | ||
1905 | "%-7s %-6d %-10d %-10d\n", | ||
1906 | ntohl(dest->addr.ip), | ||
1907 | ntohs(dest->port), | ||
1908 | ip_vs_fwd_name(atomic_read(&dest->conn_flags)), | ||
1909 | atomic_read(&dest->weight), | ||
1910 | atomic_read(&dest->activeconns), | ||
1911 | atomic_read(&dest->inactconns)); | ||
1912 | |||
1776 | } | 1913 | } |
1777 | } | 1914 | } |
1778 | return 0; | 1915 | return 0; |
@@ -1816,20 +1953,20 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) | |||
1816 | " Conns Packets Packets Bytes Bytes\n"); | 1953 | " Conns Packets Packets Bytes Bytes\n"); |
1817 | 1954 | ||
1818 | spin_lock_bh(&ip_vs_stats.lock); | 1955 | spin_lock_bh(&ip_vs_stats.lock); |
1819 | seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", ip_vs_stats.conns, | 1956 | seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", ip_vs_stats.ustats.conns, |
1820 | ip_vs_stats.inpkts, ip_vs_stats.outpkts, | 1957 | ip_vs_stats.ustats.inpkts, ip_vs_stats.ustats.outpkts, |
1821 | (unsigned long long) ip_vs_stats.inbytes, | 1958 | (unsigned long long) ip_vs_stats.ustats.inbytes, |
1822 | (unsigned long long) ip_vs_stats.outbytes); | 1959 | (unsigned long long) ip_vs_stats.ustats.outbytes); |
1823 | 1960 | ||
1824 | /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ | 1961 | /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ |
1825 | seq_puts(seq, | 1962 | seq_puts(seq, |
1826 | " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); | 1963 | " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); |
1827 | seq_printf(seq,"%8X %8X %8X %16X %16X\n", | 1964 | seq_printf(seq,"%8X %8X %8X %16X %16X\n", |
1828 | ip_vs_stats.cps, | 1965 | ip_vs_stats.ustats.cps, |
1829 | ip_vs_stats.inpps, | 1966 | ip_vs_stats.ustats.inpps, |
1830 | ip_vs_stats.outpps, | 1967 | ip_vs_stats.ustats.outpps, |
1831 | ip_vs_stats.inbps, | 1968 | ip_vs_stats.ustats.inbps, |
1832 | ip_vs_stats.outbps); | 1969 | ip_vs_stats.ustats.outbps); |
1833 | spin_unlock_bh(&ip_vs_stats.lock); | 1970 | spin_unlock_bh(&ip_vs_stats.lock); |
1834 | 1971 | ||
1835 | return 0; | 1972 | return 0; |
@@ -1904,14 +2041,44 @@ static const unsigned char set_arglen[SET_CMDID(IP_VS_SO_SET_MAX)+1] = { | |||
1904 | [SET_CMDID(IP_VS_SO_SET_ZERO)] = SERVICE_ARG_LEN, | 2041 | [SET_CMDID(IP_VS_SO_SET_ZERO)] = SERVICE_ARG_LEN, |
1905 | }; | 2042 | }; |
1906 | 2043 | ||
2044 | static void ip_vs_copy_usvc_compat(struct ip_vs_service_user_kern *usvc, | ||
2045 | struct ip_vs_service_user *usvc_compat) | ||
2046 | { | ||
2047 | usvc->af = AF_INET; | ||
2048 | usvc->protocol = usvc_compat->protocol; | ||
2049 | usvc->addr.ip = usvc_compat->addr; | ||
2050 | usvc->port = usvc_compat->port; | ||
2051 | usvc->fwmark = usvc_compat->fwmark; | ||
2052 | |||
2053 | /* Deep copy of sched_name is not needed here */ | ||
2054 | usvc->sched_name = usvc_compat->sched_name; | ||
2055 | |||
2056 | usvc->flags = usvc_compat->flags; | ||
2057 | usvc->timeout = usvc_compat->timeout; | ||
2058 | usvc->netmask = usvc_compat->netmask; | ||
2059 | } | ||
2060 | |||
2061 | static void ip_vs_copy_udest_compat(struct ip_vs_dest_user_kern *udest, | ||
2062 | struct ip_vs_dest_user *udest_compat) | ||
2063 | { | ||
2064 | udest->addr.ip = udest_compat->addr; | ||
2065 | udest->port = udest_compat->port; | ||
2066 | udest->conn_flags = udest_compat->conn_flags; | ||
2067 | udest->weight = udest_compat->weight; | ||
2068 | udest->u_threshold = udest_compat->u_threshold; | ||
2069 | udest->l_threshold = udest_compat->l_threshold; | ||
2070 | } | ||
2071 | |||
1907 | static int | 2072 | static int |
1908 | do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | 2073 | do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) |
1909 | { | 2074 | { |
1910 | int ret; | 2075 | int ret; |
1911 | unsigned char arg[MAX_ARG_LEN]; | 2076 | unsigned char arg[MAX_ARG_LEN]; |
1912 | struct ip_vs_service_user *usvc; | 2077 | struct ip_vs_service_user *usvc_compat; |
2078 | struct ip_vs_service_user_kern usvc; | ||
1913 | struct ip_vs_service *svc; | 2079 | struct ip_vs_service *svc; |
1914 | struct ip_vs_dest_user *udest; | 2080 | struct ip_vs_dest_user *udest_compat; |
2081 | struct ip_vs_dest_user_kern udest; | ||
1915 | 2082 | ||
1916 | if (!capable(CAP_NET_ADMIN)) | 2083 | if (!capable(CAP_NET_ADMIN)) |
1917 | return -EPERM; | 2084 | return -EPERM; |
@@ -1951,35 +2118,40 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | |||
1951 | goto out_unlock; | 2118 | goto out_unlock; |
1952 | } | 2119 | } |
1953 | 2120 | ||
1954 | usvc = (struct ip_vs_service_user *)arg; | 2121 | usvc_compat = (struct ip_vs_service_user *)arg; |
1955 | udest = (struct ip_vs_dest_user *)(usvc + 1); | 2122 | udest_compat = (struct ip_vs_dest_user *)(usvc_compat + 1); |
2123 | |||
2124 | /* We only use the new structs internally, so copy userspace compat | ||
2125 | * structs to extended internal versions */ | ||
2126 | ip_vs_copy_usvc_compat(&usvc, usvc_compat); | ||
2127 | ip_vs_copy_udest_compat(&udest, udest_compat); | ||
1956 | 2128 | ||
1957 | if (cmd == IP_VS_SO_SET_ZERO) { | 2129 | if (cmd == IP_VS_SO_SET_ZERO) { |
1958 | /* if no service address is set, zero counters in all */ | 2130 | /* if no service address is set, zero counters in all */ |
1959 | if (!usvc->fwmark && !usvc->addr && !usvc->port) { | 2131 | if (!usvc.fwmark && !usvc.addr.ip && !usvc.port) { |
1960 | ret = ip_vs_zero_all(); | 2132 | ret = ip_vs_zero_all(); |
1961 | goto out_unlock; | 2133 | goto out_unlock; |
1962 | } | 2134 | } |
1963 | } | 2135 | } |
1964 | 2136 | ||
1965 | /* Check for valid protocol: TCP or UDP, even for fwmark!=0 */ | 2137 | /* Check for valid protocol: TCP or UDP, even for fwmark!=0 */ |
1966 | if (usvc->protocol!=IPPROTO_TCP && usvc->protocol!=IPPROTO_UDP) { | 2138 | if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP) { |
1967 | IP_VS_ERR("set_ctl: invalid protocol: %d %d.%d.%d.%d:%d %s\n", | 2139 | IP_VS_ERR("set_ctl: invalid protocol: %d %d.%d.%d.%d:%d %s\n", |
1968 | usvc->protocol, NIPQUAD(usvc->addr), | 2140 | usvc.protocol, NIPQUAD(usvc.addr.ip), |
1969 | ntohs(usvc->port), usvc->sched_name); | 2141 | ntohs(usvc.port), usvc.sched_name); |
1970 | ret = -EFAULT; | 2142 | ret = -EFAULT; |
1971 | goto out_unlock; | 2143 | goto out_unlock; |
1972 | } | 2144 | } |
1973 | 2145 | ||
1974 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ | 2146 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ |
1975 | if (usvc->fwmark == 0) | 2147 | if (usvc.fwmark == 0) |
1976 | svc = __ip_vs_service_get(usvc->protocol, | 2148 | svc = __ip_vs_service_get(usvc.af, usvc.protocol, |
1977 | usvc->addr, usvc->port); | 2149 | &usvc.addr, usvc.port); |
1978 | else | 2150 | else |
1979 | svc = __ip_vs_svc_fwm_get(usvc->fwmark); | 2151 | svc = __ip_vs_svc_fwm_get(usvc.af, usvc.fwmark); |
1980 | 2152 | ||
1981 | if (cmd != IP_VS_SO_SET_ADD | 2153 | if (cmd != IP_VS_SO_SET_ADD |
1982 | && (svc == NULL || svc->protocol != usvc->protocol)) { | 2154 | && (svc == NULL || svc->protocol != usvc.protocol)) { |
1983 | ret = -ESRCH; | 2155 | ret = -ESRCH; |
1984 | goto out_unlock; | 2156 | goto out_unlock; |
1985 | } | 2157 | } |
@@ -1989,10 +2161,10 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | |||
1989 | if (svc != NULL) | 2161 | if (svc != NULL) |
1990 | ret = -EEXIST; | 2162 | ret = -EEXIST; |
1991 | else | 2163 | else |
1992 | ret = ip_vs_add_service(usvc, &svc); | 2164 | ret = ip_vs_add_service(&usvc, &svc); |
1993 | break; | 2165 | break; |
1994 | case IP_VS_SO_SET_EDIT: | 2166 | case IP_VS_SO_SET_EDIT: |
1995 | ret = ip_vs_edit_service(svc, usvc); | 2167 | ret = ip_vs_edit_service(svc, &usvc); |
1996 | break; | 2168 | break; |
1997 | case IP_VS_SO_SET_DEL: | 2169 | case IP_VS_SO_SET_DEL: |
1998 | ret = ip_vs_del_service(svc); | 2170 | ret = ip_vs_del_service(svc); |
@@ -2003,13 +2175,13 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | |||
2003 | ret = ip_vs_zero_service(svc); | 2175 | ret = ip_vs_zero_service(svc); |
2004 | break; | 2176 | break; |
2005 | case IP_VS_SO_SET_ADDDEST: | 2177 | case IP_VS_SO_SET_ADDDEST: |
2006 | ret = ip_vs_add_dest(svc, udest); | 2178 | ret = ip_vs_add_dest(svc, &udest); |
2007 | break; | 2179 | break; |
2008 | case IP_VS_SO_SET_EDITDEST: | 2180 | case IP_VS_SO_SET_EDITDEST: |
2009 | ret = ip_vs_edit_dest(svc, udest); | 2181 | ret = ip_vs_edit_dest(svc, &udest); |
2010 | break; | 2182 | break; |
2011 | case IP_VS_SO_SET_DELDEST: | 2183 | case IP_VS_SO_SET_DELDEST: |
2012 | ret = ip_vs_del_dest(svc, udest); | 2184 | ret = ip_vs_del_dest(svc, &udest); |
2013 | break; | 2185 | break; |
2014 | default: | 2186 | default: |
2015 | ret = -EINVAL; | 2187 | ret = -EINVAL; |
@@ -2032,7 +2204,7 @@ static void | |||
2032 | ip_vs_copy_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src) | 2204 | ip_vs_copy_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src) |
2033 | { | 2205 | { |
2034 | spin_lock_bh(&src->lock); | 2206 | spin_lock_bh(&src->lock); |
2035 | memcpy(dst, src, (char*)&src->lock - (char*)src); | 2207 | memcpy(dst, &src->ustats, sizeof(*dst)); |
2036 | spin_unlock_bh(&src->lock); | 2208 | spin_unlock_bh(&src->lock); |
2037 | } | 2209 | } |
2038 | 2210 | ||
@@ -2040,7 +2212,7 @@ static void | |||
2040 | ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) | 2212 | ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) |
2041 | { | 2213 | { |
2042 | dst->protocol = src->protocol; | 2214 | dst->protocol = src->protocol; |
2043 | dst->addr = src->addr; | 2215 | dst->addr = src->addr.ip; |
2044 | dst->port = src->port; | 2216 | dst->port = src->port; |
2045 | dst->fwmark = src->fwmark; | 2217 | dst->fwmark = src->fwmark; |
2046 | strlcpy(dst->sched_name, src->scheduler->name, sizeof(dst->sched_name)); | 2218 | strlcpy(dst->sched_name, src->scheduler->name, sizeof(dst->sched_name)); |
@@ -2062,6 +2234,10 @@ __ip_vs_get_service_entries(const struct ip_vs_get_services *get, | |||
2062 | 2234 | ||
2063 | for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { | 2235 | for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { |
2064 | list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { | 2236 | list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { |
2237 | /* Only expose IPv4 entries to old interface */ | ||
2238 | if (svc->af != AF_INET) | ||
2239 | continue; | ||
2240 | |||
2065 | if (count >= get->num_services) | 2241 | if (count >= get->num_services) |
2066 | goto out; | 2242 | goto out; |
2067 | memset(&entry, 0, sizeof(entry)); | 2243 | memset(&entry, 0, sizeof(entry)); |
@@ -2077,6 +2253,10 @@ __ip_vs_get_service_entries(const struct ip_vs_get_services *get, | |||
2077 | 2253 | ||
2078 | for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { | 2254 | for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { |
2079 | list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { | 2255 | list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { |
2256 | /* Only expose IPv4 entries to old interface */ | ||
2257 | if (svc->af != AF_INET) | ||
2258 | continue; | ||
2259 | |||
2080 | if (count >= get->num_services) | 2260 | if (count >= get->num_services) |
2081 | goto out; | 2261 | goto out; |
2082 | memset(&entry, 0, sizeof(entry)); | 2262 | memset(&entry, 0, sizeof(entry)); |
@@ -2098,13 +2278,15 @@ __ip_vs_get_dest_entries(const struct ip_vs_get_dests *get, | |||
2098 | struct ip_vs_get_dests __user *uptr) | 2278 | struct ip_vs_get_dests __user *uptr) |
2099 | { | 2279 | { |
2100 | struct ip_vs_service *svc; | 2280 | struct ip_vs_service *svc; |
2281 | union nf_inet_addr addr = { .ip = get->addr }; | ||
2101 | int ret = 0; | 2282 | int ret = 0; |
2102 | 2283 | ||
2103 | if (get->fwmark) | 2284 | if (get->fwmark) |
2104 | svc = __ip_vs_svc_fwm_get(get->fwmark); | 2285 | svc = __ip_vs_svc_fwm_get(AF_INET, get->fwmark); |
2105 | else | 2286 | else |
2106 | svc = __ip_vs_service_get(get->protocol, | 2287 | svc = __ip_vs_service_get(AF_INET, get->protocol, &addr, |
2107 | get->addr, get->port); | 2288 | get->port); |
2289 | |||
2108 | if (svc) { | 2290 | if (svc) { |
2109 | int count = 0; | 2291 | int count = 0; |
2110 | struct ip_vs_dest *dest; | 2292 | struct ip_vs_dest *dest; |
@@ -2114,7 +2296,7 @@ __ip_vs_get_dest_entries(const struct ip_vs_get_dests *get, | |||
2114 | if (count >= get->num_dests) | 2296 | if (count >= get->num_dests) |
2115 | break; | 2297 | break; |
2116 | 2298 | ||
2117 | entry.addr = dest->addr; | 2299 | entry.addr = dest->addr.ip; |
2118 | entry.port = dest->port; | 2300 | entry.port = dest->port; |
2119 | entry.conn_flags = atomic_read(&dest->conn_flags); | 2301 | entry.conn_flags = atomic_read(&dest->conn_flags); |
2120 | entry.weight = atomic_read(&dest->weight); | 2302 | entry.weight = atomic_read(&dest->weight); |
@@ -2239,13 +2421,15 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
2239 | { | 2421 | { |
2240 | struct ip_vs_service_entry *entry; | 2422 | struct ip_vs_service_entry *entry; |
2241 | struct ip_vs_service *svc; | 2423 | struct ip_vs_service *svc; |
2424 | union nf_inet_addr addr; | ||
2242 | 2425 | ||
2243 | entry = (struct ip_vs_service_entry *)arg; | 2426 | entry = (struct ip_vs_service_entry *)arg; |
2427 | addr.ip = entry->addr; | ||
2244 | if (entry->fwmark) | 2428 | if (entry->fwmark) |
2245 | svc = __ip_vs_svc_fwm_get(entry->fwmark); | 2429 | svc = __ip_vs_svc_fwm_get(AF_INET, entry->fwmark); |
2246 | else | 2430 | else |
2247 | svc = __ip_vs_service_get(entry->protocol, | 2431 | svc = __ip_vs_service_get(AF_INET, entry->protocol, |
2248 | entry->addr, entry->port); | 2432 | &addr, entry->port); |
2249 | if (svc) { | 2433 | if (svc) { |
2250 | ip_vs_copy_service(entry, svc); | 2434 | ip_vs_copy_service(entry, svc); |
2251 | if (copy_to_user(user, entry, sizeof(*entry)) != 0) | 2435 | if (copy_to_user(user, entry, sizeof(*entry)) != 0) |
@@ -2396,16 +2580,16 @@ static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type, | |||
2396 | 2580 | ||
2397 | spin_lock_bh(&stats->lock); | 2581 | spin_lock_bh(&stats->lock); |
2398 | 2582 | ||
2399 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns); | 2583 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->ustats.conns); |
2400 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts); | 2584 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->ustats.inpkts); |
2401 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts); | 2585 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->ustats.outpkts); |
2402 | NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes); | 2586 | NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->ustats.inbytes); |
2403 | NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes); | 2587 | NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->ustats.outbytes); |
2404 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps); | 2588 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->ustats.cps); |
2405 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps); | 2589 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->ustats.inpps); |
2406 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps); | 2590 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->ustats.outpps); |
2407 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps); | 2591 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->ustats.inbps); |
2408 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps); | 2592 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->ustats.outbps); |
2409 | 2593 | ||
2410 | spin_unlock_bh(&stats->lock); | 2594 | spin_unlock_bh(&stats->lock); |
2411 | 2595 | ||
@@ -2430,7 +2614,7 @@ static int ip_vs_genl_fill_service(struct sk_buff *skb, | |||
2430 | if (!nl_service) | 2614 | if (!nl_service) |
2431 | return -EMSGSIZE; | 2615 | return -EMSGSIZE; |
2432 | 2616 | ||
2433 | NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET); | 2617 | NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, svc->af); |
2434 | 2618 | ||
2435 | if (svc->fwmark) { | 2619 | if (svc->fwmark) { |
2436 | NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark); | 2620 | NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark); |
@@ -2516,7 +2700,7 @@ nla_put_failure: | |||
2516 | return skb->len; | 2700 | return skb->len; |
2517 | } | 2701 | } |
2518 | 2702 | ||
2519 | static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, | 2703 | static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, |
2520 | struct nlattr *nla, int full_entry) | 2704 | struct nlattr *nla, int full_entry) |
2521 | { | 2705 | { |
2522 | struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1]; | 2706 | struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1]; |
@@ -2536,8 +2720,12 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, | |||
2536 | if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr)))) | 2720 | if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr)))) |
2537 | return -EINVAL; | 2721 | return -EINVAL; |
2538 | 2722 | ||
2539 | /* For now, only support IPv4 */ | 2723 | usvc->af = nla_get_u16(nla_af); |
2540 | if (nla_get_u16(nla_af) != AF_INET) | 2724 | #ifdef CONFIG_IP_VS_IPV6 |
2725 | if (usvc->af != AF_INET && usvc->af != AF_INET6) | ||
2726 | #else | ||
2727 | if (usvc->af != AF_INET) | ||
2728 | #endif | ||
2541 | return -EAFNOSUPPORT; | 2729 | return -EAFNOSUPPORT; |
2542 | 2730 | ||
2543 | if (nla_fwmark) { | 2731 | if (nla_fwmark) { |
@@ -2569,10 +2757,10 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, | |||
2569 | 2757 | ||
2570 | /* prefill flags from service if it already exists */ | 2758 | /* prefill flags from service if it already exists */ |
2571 | if (usvc->fwmark) | 2759 | if (usvc->fwmark) |
2572 | svc = __ip_vs_svc_fwm_get(usvc->fwmark); | 2760 | svc = __ip_vs_svc_fwm_get(usvc->af, usvc->fwmark); |
2573 | else | 2761 | else |
2574 | svc = __ip_vs_service_get(usvc->protocol, usvc->addr, | 2762 | svc = __ip_vs_service_get(usvc->af, usvc->protocol, |
2575 | usvc->port); | 2763 | &usvc->addr, usvc->port); |
2576 | if (svc) { | 2764 | if (svc) { |
2577 | usvc->flags = svc->flags; | 2765 | usvc->flags = svc->flags; |
2578 | ip_vs_service_put(svc); | 2766 | ip_vs_service_put(svc); |
@@ -2582,9 +2770,7 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, | |||
2582 | /* set new flags from userland */ | 2770 | /* set new flags from userland */ |
2583 | usvc->flags = (usvc->flags & ~flags.mask) | | 2771 | usvc->flags = (usvc->flags & ~flags.mask) | |
2584 | (flags.flags & flags.mask); | 2772 | (flags.flags & flags.mask); |
2585 | 2773 | usvc->sched_name = nla_data(nla_sched); | |
2586 | strlcpy(usvc->sched_name, nla_data(nla_sched), | ||
2587 | sizeof(usvc->sched_name)); | ||
2588 | usvc->timeout = nla_get_u32(nla_timeout); | 2774 | usvc->timeout = nla_get_u32(nla_timeout); |
2589 | usvc->netmask = nla_get_u32(nla_netmask); | 2775 | usvc->netmask = nla_get_u32(nla_netmask); |
2590 | } | 2776 | } |
@@ -2594,7 +2780,7 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, | |||
2594 | 2780 | ||
2595 | static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) | 2781 | static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) |
2596 | { | 2782 | { |
2597 | struct ip_vs_service_user usvc; | 2783 | struct ip_vs_service_user_kern usvc; |
2598 | int ret; | 2784 | int ret; |
2599 | 2785 | ||
2600 | ret = ip_vs_genl_parse_service(&usvc, nla, 0); | 2786 | ret = ip_vs_genl_parse_service(&usvc, nla, 0); |
@@ -2602,10 +2788,10 @@ static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) | |||
2602 | return ERR_PTR(ret); | 2788 | return ERR_PTR(ret); |
2603 | 2789 | ||
2604 | if (usvc.fwmark) | 2790 | if (usvc.fwmark) |
2605 | return __ip_vs_svc_fwm_get(usvc.fwmark); | 2791 | return __ip_vs_svc_fwm_get(usvc.af, usvc.fwmark); |
2606 | else | 2792 | else |
2607 | return __ip_vs_service_get(usvc.protocol, usvc.addr, | 2793 | return __ip_vs_service_get(usvc.af, usvc.protocol, |
2608 | usvc.port); | 2794 | &usvc.addr, usvc.port); |
2609 | } | 2795 | } |
2610 | 2796 | ||
2611 | static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) | 2797 | static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) |
@@ -2704,7 +2890,7 @@ out_err: | |||
2704 | return skb->len; | 2890 | return skb->len; |
2705 | } | 2891 | } |
2706 | 2892 | ||
2707 | static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest, | 2893 | static int ip_vs_genl_parse_dest(struct ip_vs_dest_user_kern *udest, |
2708 | struct nlattr *nla, int full_entry) | 2894 | struct nlattr *nla, int full_entry) |
2709 | { | 2895 | { |
2710 | struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1]; | 2896 | struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1]; |
@@ -2860,8 +3046,8 @@ static int ip_vs_genl_set_config(struct nlattr **attrs) | |||
2860 | static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) | 3046 | static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) |
2861 | { | 3047 | { |
2862 | struct ip_vs_service *svc = NULL; | 3048 | struct ip_vs_service *svc = NULL; |
2863 | struct ip_vs_service_user usvc; | 3049 | struct ip_vs_service_user_kern usvc; |
2864 | struct ip_vs_dest_user udest; | 3050 | struct ip_vs_dest_user_kern udest; |
2865 | int ret = 0, cmd; | 3051 | int ret = 0, cmd; |
2866 | int need_full_svc = 0, need_full_dest = 0; | 3052 | int need_full_svc = 0, need_full_dest = 0; |
2867 | 3053 | ||
@@ -2913,9 +3099,10 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) | |||
2913 | 3099 | ||
2914 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ | 3100 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ |
2915 | if (usvc.fwmark == 0) | 3101 | if (usvc.fwmark == 0) |
2916 | svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port); | 3102 | svc = __ip_vs_service_get(usvc.af, usvc.protocol, |
3103 | &usvc.addr, usvc.port); | ||
2917 | else | 3104 | else |
2918 | svc = __ip_vs_svc_fwm_get(usvc.fwmark); | 3105 | svc = __ip_vs_svc_fwm_get(usvc.af, usvc.fwmark); |
2919 | 3106 | ||
2920 | /* Unless we're adding a new service, the service must already exist */ | 3107 | /* Unless we're adding a new service, the service must already exist */ |
2921 | if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) { | 3108 | if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) { |
diff --git a/net/ipv4/ipvs/ip_vs_dh.c b/net/ipv4/ipvs/ip_vs_dh.c index fa66824d264f..a16943fd72f1 100644 --- a/net/ipv4/ipvs/ip_vs_dh.c +++ b/net/ipv4/ipvs/ip_vs_dh.c | |||
@@ -218,7 +218,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
218 | IP_VS_DBG(6, "DH: destination IP address %u.%u.%u.%u " | 218 | IP_VS_DBG(6, "DH: destination IP address %u.%u.%u.%u " |
219 | "--> server %u.%u.%u.%u:%d\n", | 219 | "--> server %u.%u.%u.%u:%d\n", |
220 | NIPQUAD(iph->daddr), | 220 | NIPQUAD(iph->daddr), |
221 | NIPQUAD(dest->addr), | 221 | NIPQUAD(dest->addr.ip), |
222 | ntohs(dest->port)); | 222 | ntohs(dest->port)); |
223 | 223 | ||
224 | return dest; | 224 | return dest; |
@@ -234,6 +234,9 @@ static struct ip_vs_scheduler ip_vs_dh_scheduler = | |||
234 | .refcnt = ATOMIC_INIT(0), | 234 | .refcnt = ATOMIC_INIT(0), |
235 | .module = THIS_MODULE, | 235 | .module = THIS_MODULE, |
236 | .n_list = LIST_HEAD_INIT(ip_vs_dh_scheduler.n_list), | 236 | .n_list = LIST_HEAD_INIT(ip_vs_dh_scheduler.n_list), |
237 | #ifdef CONFIG_IP_VS_IPV6 | ||
238 | .supports_ipv6 = 0, | ||
239 | #endif | ||
237 | .init_service = ip_vs_dh_init_svc, | 240 | .init_service = ip_vs_dh_init_svc, |
238 | .done_service = ip_vs_dh_done_svc, | 241 | .done_service = ip_vs_dh_done_svc, |
239 | .update_service = ip_vs_dh_update_svc, | 242 | .update_service = ip_vs_dh_update_svc, |
diff --git a/net/ipv4/ipvs/ip_vs_est.c b/net/ipv4/ipvs/ip_vs_est.c index 4fb620ec2086..2eb2860dabb5 100644 --- a/net/ipv4/ipvs/ip_vs_est.c +++ b/net/ipv4/ipvs/ip_vs_est.c | |||
@@ -65,37 +65,37 @@ static void estimation_timer(unsigned long arg) | |||
65 | s = container_of(e, struct ip_vs_stats, est); | 65 | s = container_of(e, struct ip_vs_stats, est); |
66 | 66 | ||
67 | spin_lock(&s->lock); | 67 | spin_lock(&s->lock); |
68 | n_conns = s->conns; | 68 | n_conns = s->ustats.conns; |
69 | n_inpkts = s->inpkts; | 69 | n_inpkts = s->ustats.inpkts; |
70 | n_outpkts = s->outpkts; | 70 | n_outpkts = s->ustats.outpkts; |
71 | n_inbytes = s->inbytes; | 71 | n_inbytes = s->ustats.inbytes; |
72 | n_outbytes = s->outbytes; | 72 | n_outbytes = s->ustats.outbytes; |
73 | 73 | ||
74 | /* scaled by 2^10, but divided 2 seconds */ | 74 | /* scaled by 2^10, but divided 2 seconds */ |
75 | rate = (n_conns - e->last_conns)<<9; | 75 | rate = (n_conns - e->last_conns)<<9; |
76 | e->last_conns = n_conns; | 76 | e->last_conns = n_conns; |
77 | e->cps += ((long)rate - (long)e->cps)>>2; | 77 | e->cps += ((long)rate - (long)e->cps)>>2; |
78 | s->cps = (e->cps+0x1FF)>>10; | 78 | s->ustats.cps = (e->cps+0x1FF)>>10; |
79 | 79 | ||
80 | rate = (n_inpkts - e->last_inpkts)<<9; | 80 | rate = (n_inpkts - e->last_inpkts)<<9; |
81 | e->last_inpkts = n_inpkts; | 81 | e->last_inpkts = n_inpkts; |
82 | e->inpps += ((long)rate - (long)e->inpps)>>2; | 82 | e->inpps += ((long)rate - (long)e->inpps)>>2; |
83 | s->inpps = (e->inpps+0x1FF)>>10; | 83 | s->ustats.inpps = (e->inpps+0x1FF)>>10; |
84 | 84 | ||
85 | rate = (n_outpkts - e->last_outpkts)<<9; | 85 | rate = (n_outpkts - e->last_outpkts)<<9; |
86 | e->last_outpkts = n_outpkts; | 86 | e->last_outpkts = n_outpkts; |
87 | e->outpps += ((long)rate - (long)e->outpps)>>2; | 87 | e->outpps += ((long)rate - (long)e->outpps)>>2; |
88 | s->outpps = (e->outpps+0x1FF)>>10; | 88 | s->ustats.outpps = (e->outpps+0x1FF)>>10; |
89 | 89 | ||
90 | rate = (n_inbytes - e->last_inbytes)<<4; | 90 | rate = (n_inbytes - e->last_inbytes)<<4; |
91 | e->last_inbytes = n_inbytes; | 91 | e->last_inbytes = n_inbytes; |
92 | e->inbps += ((long)rate - (long)e->inbps)>>2; | 92 | e->inbps += ((long)rate - (long)e->inbps)>>2; |
93 | s->inbps = (e->inbps+0xF)>>5; | 93 | s->ustats.inbps = (e->inbps+0xF)>>5; |
94 | 94 | ||
95 | rate = (n_outbytes - e->last_outbytes)<<4; | 95 | rate = (n_outbytes - e->last_outbytes)<<4; |
96 | e->last_outbytes = n_outbytes; | 96 | e->last_outbytes = n_outbytes; |
97 | e->outbps += ((long)rate - (long)e->outbps)>>2; | 97 | e->outbps += ((long)rate - (long)e->outbps)>>2; |
98 | s->outbps = (e->outbps+0xF)>>5; | 98 | s->ustats.outbps = (e->outbps+0xF)>>5; |
99 | spin_unlock(&s->lock); | 99 | spin_unlock(&s->lock); |
100 | } | 100 | } |
101 | spin_unlock(&est_lock); | 101 | spin_unlock(&est_lock); |
@@ -108,20 +108,20 @@ void ip_vs_new_estimator(struct ip_vs_stats *stats) | |||
108 | 108 | ||
109 | INIT_LIST_HEAD(&est->list); | 109 | INIT_LIST_HEAD(&est->list); |
110 | 110 | ||
111 | est->last_conns = stats->conns; | 111 | est->last_conns = stats->ustats.conns; |
112 | est->cps = stats->cps<<10; | 112 | est->cps = stats->ustats.cps<<10; |
113 | 113 | ||
114 | est->last_inpkts = stats->inpkts; | 114 | est->last_inpkts = stats->ustats.inpkts; |
115 | est->inpps = stats->inpps<<10; | 115 | est->inpps = stats->ustats.inpps<<10; |
116 | 116 | ||
117 | est->last_outpkts = stats->outpkts; | 117 | est->last_outpkts = stats->ustats.outpkts; |
118 | est->outpps = stats->outpps<<10; | 118 | est->outpps = stats->ustats.outpps<<10; |
119 | 119 | ||
120 | est->last_inbytes = stats->inbytes; | 120 | est->last_inbytes = stats->ustats.inbytes; |
121 | est->inbps = stats->inbps<<5; | 121 | est->inbps = stats->ustats.inbps<<5; |
122 | 122 | ||
123 | est->last_outbytes = stats->outbytes; | 123 | est->last_outbytes = stats->ustats.outbytes; |
124 | est->outbps = stats->outbps<<5; | 124 | est->outbps = stats->ustats.outbps<<5; |
125 | 125 | ||
126 | spin_lock_bh(&est_lock); | 126 | spin_lock_bh(&est_lock); |
127 | list_add(&est->list, &est_list); | 127 | list_add(&est->list, &est_list); |
diff --git a/net/ipv4/ipvs/ip_vs_ftp.c b/net/ipv4/ipvs/ip_vs_ftp.c index c1c758e4f733..2e7dbd8b73a4 100644 --- a/net/ipv4/ipvs/ip_vs_ftp.c +++ b/net/ipv4/ipvs/ip_vs_ftp.c | |||
@@ -140,13 +140,21 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, | |||
140 | struct tcphdr *th; | 140 | struct tcphdr *th; |
141 | char *data, *data_limit; | 141 | char *data, *data_limit; |
142 | char *start, *end; | 142 | char *start, *end; |
143 | __be32 from; | 143 | union nf_inet_addr from; |
144 | __be16 port; | 144 | __be16 port; |
145 | struct ip_vs_conn *n_cp; | 145 | struct ip_vs_conn *n_cp; |
146 | char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ | 146 | char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ |
147 | unsigned buf_len; | 147 | unsigned buf_len; |
148 | int ret; | 148 | int ret; |
149 | 149 | ||
150 | #ifdef CONFIG_IP_VS_IPV6 | ||
151 | /* This application helper doesn't work with IPv6 yet, | ||
152 | * so turn this into a no-op for IPv6 packets | ||
153 | */ | ||
154 | if (cp->af == AF_INET6) | ||
155 | return 1; | ||
156 | #endif | ||
157 | |||
150 | *diff = 0; | 158 | *diff = 0; |
151 | 159 | ||
152 | /* Only useful for established sessions */ | 160 | /* Only useful for established sessions */ |
@@ -166,24 +174,25 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, | |||
166 | if (ip_vs_ftp_get_addrport(data, data_limit, | 174 | if (ip_vs_ftp_get_addrport(data, data_limit, |
167 | SERVER_STRING, | 175 | SERVER_STRING, |
168 | sizeof(SERVER_STRING)-1, ')', | 176 | sizeof(SERVER_STRING)-1, ')', |
169 | &from, &port, | 177 | &from.ip, &port, |
170 | &start, &end) != 1) | 178 | &start, &end) != 1) |
171 | return 1; | 179 | return 1; |
172 | 180 | ||
173 | IP_VS_DBG(7, "PASV response (%u.%u.%u.%u:%d) -> " | 181 | IP_VS_DBG(7, "PASV response (%u.%u.%u.%u:%d) -> " |
174 | "%u.%u.%u.%u:%d detected\n", | 182 | "%u.%u.%u.%u:%d detected\n", |
175 | NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0); | 183 | NIPQUAD(from.ip), ntohs(port), |
184 | NIPQUAD(cp->caddr.ip), 0); | ||
176 | 185 | ||
177 | /* | 186 | /* |
178 | * Now update or create an connection entry for it | 187 | * Now update or create an connection entry for it |
179 | */ | 188 | */ |
180 | n_cp = ip_vs_conn_out_get(iph->protocol, from, port, | 189 | n_cp = ip_vs_conn_out_get(AF_INET, iph->protocol, &from, port, |
181 | cp->caddr, 0); | 190 | &cp->caddr, 0); |
182 | if (!n_cp) { | 191 | if (!n_cp) { |
183 | n_cp = ip_vs_conn_new(IPPROTO_TCP, | 192 | n_cp = ip_vs_conn_new(AF_INET, IPPROTO_TCP, |
184 | cp->caddr, 0, | 193 | &cp->caddr, 0, |
185 | cp->vaddr, port, | 194 | &cp->vaddr, port, |
186 | from, port, | 195 | &from, port, |
187 | IP_VS_CONN_F_NO_CPORT, | 196 | IP_VS_CONN_F_NO_CPORT, |
188 | cp->dest); | 197 | cp->dest); |
189 | if (!n_cp) | 198 | if (!n_cp) |
@@ -196,9 +205,9 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, | |||
196 | /* | 205 | /* |
197 | * Replace the old passive address with the new one | 206 | * Replace the old passive address with the new one |
198 | */ | 207 | */ |
199 | from = n_cp->vaddr; | 208 | from.ip = n_cp->vaddr.ip; |
200 | port = n_cp->vport; | 209 | port = n_cp->vport; |
201 | sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from), | 210 | sprintf(buf, "%d,%d,%d,%d,%d,%d", NIPQUAD(from.ip), |
202 | (ntohs(port)>>8)&255, ntohs(port)&255); | 211 | (ntohs(port)>>8)&255, ntohs(port)&255); |
203 | buf_len = strlen(buf); | 212 | buf_len = strlen(buf); |
204 | 213 | ||
@@ -243,10 +252,18 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, | |||
243 | struct tcphdr *th; | 252 | struct tcphdr *th; |
244 | char *data, *data_start, *data_limit; | 253 | char *data, *data_start, *data_limit; |
245 | char *start, *end; | 254 | char *start, *end; |
246 | __be32 to; | 255 | union nf_inet_addr to; |
247 | __be16 port; | 256 | __be16 port; |
248 | struct ip_vs_conn *n_cp; | 257 | struct ip_vs_conn *n_cp; |
249 | 258 | ||
259 | #ifdef CONFIG_IP_VS_IPV6 | ||
260 | /* This application helper doesn't work with IPv6 yet, | ||
261 | * so turn this into a no-op for IPv6 packets | ||
262 | */ | ||
263 | if (cp->af == AF_INET6) | ||
264 | return 1; | ||
265 | #endif | ||
266 | |||
250 | /* no diff required for incoming packets */ | 267 | /* no diff required for incoming packets */ |
251 | *diff = 0; | 268 | *diff = 0; |
252 | 269 | ||
@@ -291,12 +308,12 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, | |||
291 | */ | 308 | */ |
292 | if (ip_vs_ftp_get_addrport(data_start, data_limit, | 309 | if (ip_vs_ftp_get_addrport(data_start, data_limit, |
293 | CLIENT_STRING, sizeof(CLIENT_STRING)-1, | 310 | CLIENT_STRING, sizeof(CLIENT_STRING)-1, |
294 | '\r', &to, &port, | 311 | '\r', &to.ip, &port, |
295 | &start, &end) != 1) | 312 | &start, &end) != 1) |
296 | return 1; | 313 | return 1; |
297 | 314 | ||
298 | IP_VS_DBG(7, "PORT %u.%u.%u.%u:%d detected\n", | 315 | IP_VS_DBG(7, "PORT %u.%u.%u.%u:%d detected\n", |
299 | NIPQUAD(to), ntohs(port)); | 316 | NIPQUAD(to.ip), ntohs(port)); |
300 | 317 | ||
301 | /* Passive mode off */ | 318 | /* Passive mode off */ |
302 | cp->app_data = NULL; | 319 | cp->app_data = NULL; |
@@ -306,16 +323,16 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, | |||
306 | */ | 323 | */ |
307 | IP_VS_DBG(7, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n", | 324 | IP_VS_DBG(7, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n", |
308 | ip_vs_proto_name(iph->protocol), | 325 | ip_vs_proto_name(iph->protocol), |
309 | NIPQUAD(to), ntohs(port), NIPQUAD(cp->vaddr), 0); | 326 | NIPQUAD(to.ip), ntohs(port), NIPQUAD(cp->vaddr.ip), 0); |
310 | 327 | ||
311 | n_cp = ip_vs_conn_in_get(iph->protocol, | 328 | n_cp = ip_vs_conn_in_get(AF_INET, iph->protocol, |
312 | to, port, | 329 | &to, port, |
313 | cp->vaddr, htons(ntohs(cp->vport)-1)); | 330 | &cp->vaddr, htons(ntohs(cp->vport)-1)); |
314 | if (!n_cp) { | 331 | if (!n_cp) { |
315 | n_cp = ip_vs_conn_new(IPPROTO_TCP, | 332 | n_cp = ip_vs_conn_new(AF_INET, IPPROTO_TCP, |
316 | to, port, | 333 | &to, port, |
317 | cp->vaddr, htons(ntohs(cp->vport)-1), | 334 | &cp->vaddr, htons(ntohs(cp->vport)-1), |
318 | cp->daddr, htons(ntohs(cp->dport)-1), | 335 | &cp->daddr, htons(ntohs(cp->dport)-1), |
319 | 0, | 336 | 0, |
320 | cp->dest); | 337 | cp->dest); |
321 | if (!n_cp) | 338 | if (!n_cp) |
diff --git a/net/ipv4/ipvs/ip_vs_lblc.c b/net/ipv4/ipvs/ip_vs_lblc.c index d2a43aa3fe4c..6ecef3518cac 100644 --- a/net/ipv4/ipvs/ip_vs_lblc.c +++ b/net/ipv4/ipvs/ip_vs_lblc.c | |||
@@ -422,7 +422,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc, struct iphdr *iph) | |||
422 | 422 | ||
423 | IP_VS_DBG(6, "LBLC: server %d.%d.%d.%d:%d " | 423 | IP_VS_DBG(6, "LBLC: server %d.%d.%d.%d:%d " |
424 | "activeconns %d refcnt %d weight %d overhead %d\n", | 424 | "activeconns %d refcnt %d weight %d overhead %d\n", |
425 | NIPQUAD(least->addr), ntohs(least->port), | 425 | NIPQUAD(least->addr.ip), ntohs(least->port), |
426 | atomic_read(&least->activeconns), | 426 | atomic_read(&least->activeconns), |
427 | atomic_read(&least->refcnt), | 427 | atomic_read(&least->refcnt), |
428 | atomic_read(&least->weight), loh); | 428 | atomic_read(&least->weight), loh); |
@@ -506,7 +506,7 @@ out: | |||
506 | IP_VS_DBG(6, "LBLC: destination IP address %u.%u.%u.%u " | 506 | IP_VS_DBG(6, "LBLC: destination IP address %u.%u.%u.%u " |
507 | "--> server %u.%u.%u.%u:%d\n", | 507 | "--> server %u.%u.%u.%u:%d\n", |
508 | NIPQUAD(iph->daddr), | 508 | NIPQUAD(iph->daddr), |
509 | NIPQUAD(dest->addr), | 509 | NIPQUAD(dest->addr.ip), |
510 | ntohs(dest->port)); | 510 | ntohs(dest->port)); |
511 | 511 | ||
512 | return dest; | 512 | return dest; |
@@ -522,6 +522,9 @@ static struct ip_vs_scheduler ip_vs_lblc_scheduler = | |||
522 | .refcnt = ATOMIC_INIT(0), | 522 | .refcnt = ATOMIC_INIT(0), |
523 | .module = THIS_MODULE, | 523 | .module = THIS_MODULE, |
524 | .n_list = LIST_HEAD_INIT(ip_vs_lblc_scheduler.n_list), | 524 | .n_list = LIST_HEAD_INIT(ip_vs_lblc_scheduler.n_list), |
525 | #ifdef CONFIG_IP_VS_IPV6 | ||
526 | .supports_ipv6 = 0, | ||
527 | #endif | ||
525 | .init_service = ip_vs_lblc_init_svc, | 528 | .init_service = ip_vs_lblc_init_svc, |
526 | .done_service = ip_vs_lblc_done_svc, | 529 | .done_service = ip_vs_lblc_done_svc, |
527 | .schedule = ip_vs_lblc_schedule, | 530 | .schedule = ip_vs_lblc_schedule, |
diff --git a/net/ipv4/ipvs/ip_vs_lblcr.c b/net/ipv4/ipvs/ip_vs_lblcr.c index 375a1ffb6b65..1f75ea83bcf8 100644 --- a/net/ipv4/ipvs/ip_vs_lblcr.c +++ b/net/ipv4/ipvs/ip_vs_lblcr.c | |||
@@ -204,7 +204,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) | |||
204 | 204 | ||
205 | IP_VS_DBG(6, "ip_vs_dest_set_min: server %d.%d.%d.%d:%d " | 205 | IP_VS_DBG(6, "ip_vs_dest_set_min: server %d.%d.%d.%d:%d " |
206 | "activeconns %d refcnt %d weight %d overhead %d\n", | 206 | "activeconns %d refcnt %d weight %d overhead %d\n", |
207 | NIPQUAD(least->addr), ntohs(least->port), | 207 | NIPQUAD(least->addr.ip), ntohs(least->port), |
208 | atomic_read(&least->activeconns), | 208 | atomic_read(&least->activeconns), |
209 | atomic_read(&least->refcnt), | 209 | atomic_read(&least->refcnt), |
210 | atomic_read(&least->weight), loh); | 210 | atomic_read(&least->weight), loh); |
@@ -250,7 +250,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) | |||
250 | 250 | ||
251 | IP_VS_DBG(6, "ip_vs_dest_set_max: server %d.%d.%d.%d:%d " | 251 | IP_VS_DBG(6, "ip_vs_dest_set_max: server %d.%d.%d.%d:%d " |
252 | "activeconns %d refcnt %d weight %d overhead %d\n", | 252 | "activeconns %d refcnt %d weight %d overhead %d\n", |
253 | NIPQUAD(most->addr), ntohs(most->port), | 253 | NIPQUAD(most->addr.ip), ntohs(most->port), |
254 | atomic_read(&most->activeconns), | 254 | atomic_read(&most->activeconns), |
255 | atomic_read(&most->refcnt), | 255 | atomic_read(&most->refcnt), |
256 | atomic_read(&most->weight), moh); | 256 | atomic_read(&most->weight), moh); |
@@ -598,7 +598,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc, struct iphdr *iph) | |||
598 | 598 | ||
599 | IP_VS_DBG(6, "LBLCR: server %d.%d.%d.%d:%d " | 599 | IP_VS_DBG(6, "LBLCR: server %d.%d.%d.%d:%d " |
600 | "activeconns %d refcnt %d weight %d overhead %d\n", | 600 | "activeconns %d refcnt %d weight %d overhead %d\n", |
601 | NIPQUAD(least->addr), ntohs(least->port), | 601 | NIPQUAD(least->addr.ip), ntohs(least->port), |
602 | atomic_read(&least->activeconns), | 602 | atomic_read(&least->activeconns), |
603 | atomic_read(&least->refcnt), | 603 | atomic_read(&least->refcnt), |
604 | atomic_read(&least->weight), loh); | 604 | atomic_read(&least->weight), loh); |
@@ -706,7 +706,7 @@ out: | |||
706 | IP_VS_DBG(6, "LBLCR: destination IP address %u.%u.%u.%u " | 706 | IP_VS_DBG(6, "LBLCR: destination IP address %u.%u.%u.%u " |
707 | "--> server %u.%u.%u.%u:%d\n", | 707 | "--> server %u.%u.%u.%u:%d\n", |
708 | NIPQUAD(iph->daddr), | 708 | NIPQUAD(iph->daddr), |
709 | NIPQUAD(dest->addr), | 709 | NIPQUAD(dest->addr.ip), |
710 | ntohs(dest->port)); | 710 | ntohs(dest->port)); |
711 | 711 | ||
712 | return dest; | 712 | return dest; |
@@ -722,6 +722,9 @@ static struct ip_vs_scheduler ip_vs_lblcr_scheduler = | |||
722 | .refcnt = ATOMIC_INIT(0), | 722 | .refcnt = ATOMIC_INIT(0), |
723 | .module = THIS_MODULE, | 723 | .module = THIS_MODULE, |
724 | .n_list = LIST_HEAD_INIT(ip_vs_lblcr_scheduler.n_list), | 724 | .n_list = LIST_HEAD_INIT(ip_vs_lblcr_scheduler.n_list), |
725 | #ifdef CONFIG_IP_VS_IPV6 | ||
726 | .supports_ipv6 = 0, | ||
727 | #endif | ||
725 | .init_service = ip_vs_lblcr_init_svc, | 728 | .init_service = ip_vs_lblcr_init_svc, |
726 | .done_service = ip_vs_lblcr_done_svc, | 729 | .done_service = ip_vs_lblcr_done_svc, |
727 | .schedule = ip_vs_lblcr_schedule, | 730 | .schedule = ip_vs_lblcr_schedule, |
diff --git a/net/ipv4/ipvs/ip_vs_lc.c b/net/ipv4/ipvs/ip_vs_lc.c index 2c3de1b63518..b69f808ac461 100644 --- a/net/ipv4/ipvs/ip_vs_lc.c +++ b/net/ipv4/ipvs/ip_vs_lc.c | |||
@@ -67,10 +67,10 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
67 | } | 67 | } |
68 | 68 | ||
69 | if (least) | 69 | if (least) |
70 | IP_VS_DBG(6, "LC: server %u.%u.%u.%u:%u activeconns %d inactconns %d\n", | 70 | IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d inactconns %d\n", |
71 | NIPQUAD(least->addr), ntohs(least->port), | 71 | IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port), |
72 | atomic_read(&least->activeconns), | 72 | atomic_read(&least->activeconns), |
73 | atomic_read(&least->inactconns)); | 73 | atomic_read(&least->inactconns)); |
74 | 74 | ||
75 | return least; | 75 | return least; |
76 | } | 76 | } |
@@ -81,6 +81,9 @@ static struct ip_vs_scheduler ip_vs_lc_scheduler = { | |||
81 | .refcnt = ATOMIC_INIT(0), | 81 | .refcnt = ATOMIC_INIT(0), |
82 | .module = THIS_MODULE, | 82 | .module = THIS_MODULE, |
83 | .n_list = LIST_HEAD_INIT(ip_vs_lc_scheduler.n_list), | 83 | .n_list = LIST_HEAD_INIT(ip_vs_lc_scheduler.n_list), |
84 | #ifdef CONFIG_IP_VS_IPV6 | ||
85 | .supports_ipv6 = 1, | ||
86 | #endif | ||
84 | .schedule = ip_vs_lc_schedule, | 87 | .schedule = ip_vs_lc_schedule, |
85 | }; | 88 | }; |
86 | 89 | ||
diff --git a/net/ipv4/ipvs/ip_vs_nq.c b/net/ipv4/ipvs/ip_vs_nq.c index 5330d5a2de14..9a2d8033f08f 100644 --- a/net/ipv4/ipvs/ip_vs_nq.c +++ b/net/ipv4/ipvs/ip_vs_nq.c | |||
@@ -99,12 +99,12 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
99 | return NULL; | 99 | return NULL; |
100 | 100 | ||
101 | out: | 101 | out: |
102 | IP_VS_DBG(6, "NQ: server %u.%u.%u.%u:%u " | 102 | IP_VS_DBG_BUF(6, "NQ: server %s:%u " |
103 | "activeconns %d refcnt %d weight %d overhead %d\n", | 103 | "activeconns %d refcnt %d weight %d overhead %d\n", |
104 | NIPQUAD(least->addr), ntohs(least->port), | 104 | IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port), |
105 | atomic_read(&least->activeconns), | 105 | atomic_read(&least->activeconns), |
106 | atomic_read(&least->refcnt), | 106 | atomic_read(&least->refcnt), |
107 | atomic_read(&least->weight), loh); | 107 | atomic_read(&least->weight), loh); |
108 | 108 | ||
109 | return least; | 109 | return least; |
110 | } | 110 | } |
@@ -116,6 +116,9 @@ static struct ip_vs_scheduler ip_vs_nq_scheduler = | |||
116 | .refcnt = ATOMIC_INIT(0), | 116 | .refcnt = ATOMIC_INIT(0), |
117 | .module = THIS_MODULE, | 117 | .module = THIS_MODULE, |
118 | .n_list = LIST_HEAD_INIT(ip_vs_nq_scheduler.n_list), | 118 | .n_list = LIST_HEAD_INIT(ip_vs_nq_scheduler.n_list), |
119 | #ifdef CONFIG_IP_VS_IPV6 | ||
120 | .supports_ipv6 = 1, | ||
121 | #endif | ||
119 | .schedule = ip_vs_nq_schedule, | 122 | .schedule = ip_vs_nq_schedule, |
120 | }; | 123 | }; |
121 | 124 | ||
diff --git a/net/ipv4/ipvs/ip_vs_proto.c b/net/ipv4/ipvs/ip_vs_proto.c index 6099a88fc200..b06da1c3445a 100644 --- a/net/ipv4/ipvs/ip_vs_proto.c +++ b/net/ipv4/ipvs/ip_vs_proto.c | |||
@@ -151,11 +151,11 @@ const char * ip_vs_state_name(__u16 proto, int state) | |||
151 | } | 151 | } |
152 | 152 | ||
153 | 153 | ||
154 | void | 154 | static void |
155 | ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp, | 155 | ip_vs_tcpudp_debug_packet_v4(struct ip_vs_protocol *pp, |
156 | const struct sk_buff *skb, | 156 | const struct sk_buff *skb, |
157 | int offset, | 157 | int offset, |
158 | const char *msg) | 158 | const char *msg) |
159 | { | 159 | { |
160 | char buf[128]; | 160 | char buf[128]; |
161 | struct iphdr _iph, *ih; | 161 | struct iphdr _iph, *ih; |
@@ -189,6 +189,61 @@ ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp, | |||
189 | printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); | 189 | printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); |
190 | } | 190 | } |
191 | 191 | ||
192 | #ifdef CONFIG_IP_VS_IPV6 | ||
193 | static void | ||
194 | ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp, | ||
195 | const struct sk_buff *skb, | ||
196 | int offset, | ||
197 | const char *msg) | ||
198 | { | ||
199 | char buf[192]; | ||
200 | struct ipv6hdr _iph, *ih; | ||
201 | |||
202 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | ||
203 | if (ih == NULL) | ||
204 | sprintf(buf, "%s TRUNCATED", pp->name); | ||
205 | else if (ih->nexthdr == IPPROTO_FRAGMENT) | ||
206 | sprintf(buf, "%s " NIP6_FMT "->" NIP6_FMT " frag", | ||
207 | pp->name, NIP6(ih->saddr), | ||
208 | NIP6(ih->daddr)); | ||
209 | else { | ||
210 | __be16 _ports[2], *pptr; | ||
211 | |||
212 | pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr), | ||
213 | sizeof(_ports), _ports); | ||
214 | if (pptr == NULL) | ||
215 | sprintf(buf, "%s TRUNCATED " NIP6_FMT "->" NIP6_FMT, | ||
216 | pp->name, | ||
217 | NIP6(ih->saddr), | ||
218 | NIP6(ih->daddr)); | ||
219 | else | ||
220 | sprintf(buf, "%s " NIP6_FMT ":%u->" NIP6_FMT ":%u", | ||
221 | pp->name, | ||
222 | NIP6(ih->saddr), | ||
223 | ntohs(pptr[0]), | ||
224 | NIP6(ih->daddr), | ||
225 | ntohs(pptr[1])); | ||
226 | } | ||
227 | |||
228 | printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); | ||
229 | } | ||
230 | #endif | ||
231 | |||
232 | |||
233 | void | ||
234 | ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp, | ||
235 | const struct sk_buff *skb, | ||
236 | int offset, | ||
237 | const char *msg) | ||
238 | { | ||
239 | #ifdef CONFIG_IP_VS_IPV6 | ||
240 | if (skb->protocol == __constant_htons(ETH_P_IPV6)) | ||
241 | ip_vs_tcpudp_debug_packet_v6(pp, skb, offset, msg); | ||
242 | else | ||
243 | #endif | ||
244 | ip_vs_tcpudp_debug_packet_v4(pp, skb, offset, msg); | ||
245 | } | ||
246 | |||
192 | 247 | ||
193 | int __init ip_vs_protocol_init(void) | 248 | int __init ip_vs_protocol_init(void) |
194 | { | 249 | { |
diff --git a/net/ipv4/ipvs/ip_vs_proto_ah_esp.c b/net/ipv4/ipvs/ip_vs_proto_ah_esp.c index 3f9ebd7639ae..2b18a78d0399 100644 --- a/net/ipv4/ipvs/ip_vs_proto_ah_esp.c +++ b/net/ipv4/ipvs/ip_vs_proto_ah_esp.c | |||
@@ -39,25 +39,23 @@ struct isakmp_hdr { | |||
39 | 39 | ||
40 | 40 | ||
41 | static struct ip_vs_conn * | 41 | static struct ip_vs_conn * |
42 | ah_esp_conn_in_get(const struct sk_buff *skb, | 42 | ah_esp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, |
43 | struct ip_vs_protocol *pp, | 43 | const struct ip_vs_iphdr *iph, unsigned int proto_off, |
44 | const struct iphdr *iph, | ||
45 | unsigned int proto_off, | ||
46 | int inverse) | 44 | int inverse) |
47 | { | 45 | { |
48 | struct ip_vs_conn *cp; | 46 | struct ip_vs_conn *cp; |
49 | 47 | ||
50 | if (likely(!inverse)) { | 48 | if (likely(!inverse)) { |
51 | cp = ip_vs_conn_in_get(IPPROTO_UDP, | 49 | cp = ip_vs_conn_in_get(af, IPPROTO_UDP, |
52 | iph->saddr, | 50 | &iph->saddr, |
53 | htons(PORT_ISAKMP), | 51 | htons(PORT_ISAKMP), |
54 | iph->daddr, | 52 | &iph->daddr, |
55 | htons(PORT_ISAKMP)); | 53 | htons(PORT_ISAKMP)); |
56 | } else { | 54 | } else { |
57 | cp = ip_vs_conn_in_get(IPPROTO_UDP, | 55 | cp = ip_vs_conn_in_get(af, IPPROTO_UDP, |
58 | iph->daddr, | 56 | &iph->daddr, |
59 | htons(PORT_ISAKMP), | 57 | htons(PORT_ISAKMP), |
60 | iph->saddr, | 58 | &iph->saddr, |
61 | htons(PORT_ISAKMP)); | 59 | htons(PORT_ISAKMP)); |
62 | } | 60 | } |
63 | 61 | ||
@@ -66,12 +64,12 @@ ah_esp_conn_in_get(const struct sk_buff *skb, | |||
66 | * We are not sure if the packet is from our | 64 | * We are not sure if the packet is from our |
67 | * service, so our conn_schedule hook should return NF_ACCEPT | 65 | * service, so our conn_schedule hook should return NF_ACCEPT |
68 | */ | 66 | */ |
69 | IP_VS_DBG(12, "Unknown ISAKMP entry for outin packet " | 67 | IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for outin packet " |
70 | "%s%s %u.%u.%u.%u->%u.%u.%u.%u\n", | 68 | "%s%s %s->%s\n", |
71 | inverse ? "ICMP+" : "", | 69 | inverse ? "ICMP+" : "", |
72 | pp->name, | 70 | pp->name, |
73 | NIPQUAD(iph->saddr), | 71 | IP_VS_DBG_ADDR(af, &iph->saddr), |
74 | NIPQUAD(iph->daddr)); | 72 | IP_VS_DBG_ADDR(af, &iph->daddr)); |
75 | } | 73 | } |
76 | 74 | ||
77 | return cp; | 75 | return cp; |
@@ -79,32 +77,35 @@ ah_esp_conn_in_get(const struct sk_buff *skb, | |||
79 | 77 | ||
80 | 78 | ||
81 | static struct ip_vs_conn * | 79 | static struct ip_vs_conn * |
82 | ah_esp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | 80 | ah_esp_conn_out_get(int af, const struct sk_buff *skb, |
83 | const struct iphdr *iph, unsigned int proto_off, int inverse) | 81 | struct ip_vs_protocol *pp, |
82 | const struct ip_vs_iphdr *iph, | ||
83 | unsigned int proto_off, | ||
84 | int inverse) | ||
84 | { | 85 | { |
85 | struct ip_vs_conn *cp; | 86 | struct ip_vs_conn *cp; |
86 | 87 | ||
87 | if (likely(!inverse)) { | 88 | if (likely(!inverse)) { |
88 | cp = ip_vs_conn_out_get(IPPROTO_UDP, | 89 | cp = ip_vs_conn_out_get(af, IPPROTO_UDP, |
89 | iph->saddr, | 90 | &iph->saddr, |
90 | htons(PORT_ISAKMP), | 91 | htons(PORT_ISAKMP), |
91 | iph->daddr, | 92 | &iph->daddr, |
92 | htons(PORT_ISAKMP)); | 93 | htons(PORT_ISAKMP)); |
93 | } else { | 94 | } else { |
94 | cp = ip_vs_conn_out_get(IPPROTO_UDP, | 95 | cp = ip_vs_conn_out_get(af, IPPROTO_UDP, |
95 | iph->daddr, | 96 | &iph->daddr, |
96 | htons(PORT_ISAKMP), | 97 | htons(PORT_ISAKMP), |
97 | iph->saddr, | 98 | &iph->saddr, |
98 | htons(PORT_ISAKMP)); | 99 | htons(PORT_ISAKMP)); |
99 | } | 100 | } |
100 | 101 | ||
101 | if (!cp) { | 102 | if (!cp) { |
102 | IP_VS_DBG(12, "Unknown ISAKMP entry for inout packet " | 103 | IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for inout packet " |
103 | "%s%s %u.%u.%u.%u->%u.%u.%u.%u\n", | 104 | "%s%s %s->%s\n", |
104 | inverse ? "ICMP+" : "", | 105 | inverse ? "ICMP+" : "", |
105 | pp->name, | 106 | pp->name, |
106 | NIPQUAD(iph->saddr), | 107 | IP_VS_DBG_ADDR(af, &iph->saddr), |
107 | NIPQUAD(iph->daddr)); | 108 | IP_VS_DBG_ADDR(af, &iph->daddr)); |
108 | } | 109 | } |
109 | 110 | ||
110 | return cp; | 111 | return cp; |
@@ -112,8 +113,7 @@ ah_esp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
112 | 113 | ||
113 | 114 | ||
114 | static int | 115 | static int |
115 | ah_esp_conn_schedule(struct sk_buff *skb, | 116 | ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, |
116 | struct ip_vs_protocol *pp, | ||
117 | int *verdict, struct ip_vs_conn **cpp) | 117 | int *verdict, struct ip_vs_conn **cpp) |
118 | { | 118 | { |
119 | /* | 119 | /* |
@@ -125,8 +125,8 @@ ah_esp_conn_schedule(struct sk_buff *skb, | |||
125 | 125 | ||
126 | 126 | ||
127 | static void | 127 | static void |
128 | ah_esp_debug_packet(struct ip_vs_protocol *pp, const struct sk_buff *skb, | 128 | ah_esp_debug_packet_v4(struct ip_vs_protocol *pp, const struct sk_buff *skb, |
129 | int offset, const char *msg) | 129 | int offset, const char *msg) |
130 | { | 130 | { |
131 | char buf[256]; | 131 | char buf[256]; |
132 | struct iphdr _iph, *ih; | 132 | struct iphdr _iph, *ih; |
@@ -142,6 +142,38 @@ ah_esp_debug_packet(struct ip_vs_protocol *pp, const struct sk_buff *skb, | |||
142 | printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); | 142 | printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); |
143 | } | 143 | } |
144 | 144 | ||
145 | #ifdef CONFIG_IP_VS_IPV6 | ||
146 | static void | ||
147 | ah_esp_debug_packet_v6(struct ip_vs_protocol *pp, const struct sk_buff *skb, | ||
148 | int offset, const char *msg) | ||
149 | { | ||
150 | char buf[256]; | ||
151 | struct ipv6hdr _iph, *ih; | ||
152 | |||
153 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | ||
154 | if (ih == NULL) | ||
155 | sprintf(buf, "%s TRUNCATED", pp->name); | ||
156 | else | ||
157 | sprintf(buf, "%s " NIP6_FMT "->" NIP6_FMT, | ||
158 | pp->name, NIP6(ih->saddr), | ||
159 | NIP6(ih->daddr)); | ||
160 | |||
161 | printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); | ||
162 | } | ||
163 | #endif | ||
164 | |||
165 | static void | ||
166 | ah_esp_debug_packet(struct ip_vs_protocol *pp, const struct sk_buff *skb, | ||
167 | int offset, const char *msg) | ||
168 | { | ||
169 | #ifdef CONFIG_IP_VS_IPV6 | ||
170 | if (skb->protocol == __constant_htons(ETH_P_IPV6)) | ||
171 | ah_esp_debug_packet_v6(pp, skb, offset, msg); | ||
172 | else | ||
173 | #endif | ||
174 | ah_esp_debug_packet_v4(pp, skb, offset, msg); | ||
175 | } | ||
176 | |||
145 | 177 | ||
146 | static void ah_esp_init(struct ip_vs_protocol *pp) | 178 | static void ah_esp_init(struct ip_vs_protocol *pp) |
147 | { | 179 | { |
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index d0ea467986a0..537f616776da 100644 --- a/net/ipv4/ipvs/ip_vs_proto_tcp.c +++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c | |||
@@ -25,8 +25,9 @@ | |||
25 | 25 | ||
26 | 26 | ||
27 | static struct ip_vs_conn * | 27 | static struct ip_vs_conn * |
28 | tcp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | 28 | tcp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, |
29 | const struct iphdr *iph, unsigned int proto_off, int inverse) | 29 | const struct ip_vs_iphdr *iph, unsigned int proto_off, |
30 | int inverse) | ||
30 | { | 31 | { |
31 | __be16 _ports[2], *pptr; | 32 | __be16 _ports[2], *pptr; |
32 | 33 | ||
@@ -35,19 +36,20 @@ tcp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
35 | return NULL; | 36 | return NULL; |
36 | 37 | ||
37 | if (likely(!inverse)) { | 38 | if (likely(!inverse)) { |
38 | return ip_vs_conn_in_get(iph->protocol, | 39 | return ip_vs_conn_in_get(af, iph->protocol, |
39 | iph->saddr, pptr[0], | 40 | &iph->saddr, pptr[0], |
40 | iph->daddr, pptr[1]); | 41 | &iph->daddr, pptr[1]); |
41 | } else { | 42 | } else { |
42 | return ip_vs_conn_in_get(iph->protocol, | 43 | return ip_vs_conn_in_get(af, iph->protocol, |
43 | iph->daddr, pptr[1], | 44 | &iph->daddr, pptr[1], |
44 | iph->saddr, pptr[0]); | 45 | &iph->saddr, pptr[0]); |
45 | } | 46 | } |
46 | } | 47 | } |
47 | 48 | ||
48 | static struct ip_vs_conn * | 49 | static struct ip_vs_conn * |
49 | tcp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | 50 | tcp_conn_out_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, |
50 | const struct iphdr *iph, unsigned int proto_off, int inverse) | 51 | const struct ip_vs_iphdr *iph, unsigned int proto_off, |
52 | int inverse) | ||
51 | { | 53 | { |
52 | __be16 _ports[2], *pptr; | 54 | __be16 _ports[2], *pptr; |
53 | 55 | ||
@@ -56,34 +58,36 @@ tcp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
56 | return NULL; | 58 | return NULL; |
57 | 59 | ||
58 | if (likely(!inverse)) { | 60 | if (likely(!inverse)) { |
59 | return ip_vs_conn_out_get(iph->protocol, | 61 | return ip_vs_conn_out_get(af, iph->protocol, |
60 | iph->saddr, pptr[0], | 62 | &iph->saddr, pptr[0], |
61 | iph->daddr, pptr[1]); | 63 | &iph->daddr, pptr[1]); |
62 | } else { | 64 | } else { |
63 | return ip_vs_conn_out_get(iph->protocol, | 65 | return ip_vs_conn_out_get(af, iph->protocol, |
64 | iph->daddr, pptr[1], | 66 | &iph->daddr, pptr[1], |
65 | iph->saddr, pptr[0]); | 67 | &iph->saddr, pptr[0]); |
66 | } | 68 | } |
67 | } | 69 | } |
68 | 70 | ||
69 | 71 | ||
70 | static int | 72 | static int |
71 | tcp_conn_schedule(struct sk_buff *skb, | 73 | tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, |
72 | struct ip_vs_protocol *pp, | ||
73 | int *verdict, struct ip_vs_conn **cpp) | 74 | int *verdict, struct ip_vs_conn **cpp) |
74 | { | 75 | { |
75 | struct ip_vs_service *svc; | 76 | struct ip_vs_service *svc; |
76 | struct tcphdr _tcph, *th; | 77 | struct tcphdr _tcph, *th; |
78 | struct ip_vs_iphdr iph; | ||
77 | 79 | ||
78 | th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); | 80 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); |
81 | |||
82 | th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph); | ||
79 | if (th == NULL) { | 83 | if (th == NULL) { |
80 | *verdict = NF_DROP; | 84 | *verdict = NF_DROP; |
81 | return 0; | 85 | return 0; |
82 | } | 86 | } |
83 | 87 | ||
84 | if (th->syn && | 88 | if (th->syn && |
85 | (svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol, | 89 | (svc = ip_vs_service_get(af, skb->mark, iph.protocol, &iph.daddr, |
86 | ip_hdr(skb)->daddr, th->dest))) { | 90 | th->dest))) { |
87 | if (ip_vs_todrop()) { | 91 | if (ip_vs_todrop()) { |
88 | /* | 92 | /* |
89 | * It seems that we are very loaded. | 93 | * It seems that we are very loaded. |
@@ -110,22 +114,62 @@ tcp_conn_schedule(struct sk_buff *skb, | |||
110 | 114 | ||
111 | 115 | ||
112 | static inline void | 116 | static inline void |
113 | 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, | ||
114 | __be16 oldport, __be16 newport) | 120 | __be16 oldport, __be16 newport) |
115 | { | 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 | ||
116 | tcph->check = | 130 | tcph->check = |
117 | csum_fold(ip_vs_check_diff4(oldip, newip, | 131 | csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip, |
118 | ip_vs_check_diff2(oldport, newport, | 132 | ip_vs_check_diff2(oldport, newport, |
119 | ~csum_unfold(tcph->check)))); | 133 | ~csum_unfold(tcph->check)))); |
120 | } | 134 | } |
121 | 135 | ||
122 | 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 | |||
123 | static int | 158 | static int |
124 | tcp_snat_handler(struct sk_buff *skb, | 159 | tcp_snat_handler(struct sk_buff *skb, |
125 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 160 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
126 | { | 161 | { |
127 | struct tcphdr *tcph; | 162 | struct tcphdr *tcph; |
128 | const unsigned int tcphoff = ip_hdrlen(skb); | 163 | unsigned int tcphoff; |
164 | int oldlen; | ||
165 | |||
166 | #ifdef CONFIG_IP_VS_IPV6 | ||
167 | if (cp->af == AF_INET6) | ||
168 | tcphoff = sizeof(struct ipv6hdr); | ||
169 | else | ||
170 | #endif | ||
171 | tcphoff = ip_hdrlen(skb); | ||
172 | oldlen = skb->len - tcphoff; | ||
129 | 173 | ||
130 | /* csum_check requires unshared skb */ | 174 | /* csum_check requires unshared skb */ |
131 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) | 175 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) |
@@ -133,7 +177,7 @@ tcp_snat_handler(struct sk_buff *skb, | |||
133 | 177 | ||
134 | if (unlikely(cp->app != NULL)) { | 178 | if (unlikely(cp->app != NULL)) { |
135 | /* Some checks before mangling */ | 179 | /* Some checks before mangling */ |
136 | if (pp->csum_check && !pp->csum_check(skb, pp)) | 180 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
137 | return 0; | 181 | return 0; |
138 | 182 | ||
139 | /* Call application helper if needed */ | 183 | /* Call application helper if needed */ |
@@ -141,13 +185,17 @@ tcp_snat_handler(struct sk_buff *skb, | |||
141 | return 0; | 185 | return 0; |
142 | } | 186 | } |
143 | 187 | ||
144 | tcph = (void *)ip_hdr(skb) + tcphoff; | 188 | tcph = (void *)skb_network_header(skb) + tcphoff; |
145 | tcph->source = cp->vport; | 189 | tcph->source = cp->vport; |
146 | 190 | ||
147 | /* Adjust TCP checksums */ | 191 | /* Adjust TCP checksums */ |
148 | if (!cp->app) { | 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) { | ||
149 | /* Only port and addr are changed, do fast csum update */ | 197 | /* Only port and addr are changed, do fast csum update */ |
150 | tcp_fast_csum_update(tcph, cp->daddr, cp->vaddr, | 198 | tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr, |
151 | cp->dport, cp->vport); | 199 | cp->dport, cp->vport); |
152 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 200 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
153 | skb->ip_summed = CHECKSUM_NONE; | 201 | skb->ip_summed = CHECKSUM_NONE; |
@@ -155,9 +203,20 @@ tcp_snat_handler(struct sk_buff *skb, | |||
155 | /* full checksum calculation */ | 203 | /* full checksum calculation */ |
156 | tcph->check = 0; | 204 | tcph->check = 0; |
157 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); | 205 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); |
158 | tcph->check = csum_tcpudp_magic(cp->vaddr, cp->caddr, | 206 | #ifdef CONFIG_IP_VS_IPV6 |
159 | skb->len - tcphoff, | 207 | if (cp->af == AF_INET6) |
160 | cp->protocol, skb->csum); | 208 | tcph->check = csum_ipv6_magic(&cp->vaddr.in6, |
209 | &cp->caddr.in6, | ||
210 | skb->len - tcphoff, | ||
211 | cp->protocol, skb->csum); | ||
212 | else | ||
213 | #endif | ||
214 | tcph->check = csum_tcpudp_magic(cp->vaddr.ip, | ||
215 | cp->caddr.ip, | ||
216 | skb->len - tcphoff, | ||
217 | cp->protocol, | ||
218 | skb->csum); | ||
219 | |||
161 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", | 220 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", |
162 | pp->name, tcph->check, | 221 | pp->name, tcph->check, |
163 | (char*)&(tcph->check) - (char*)tcph); | 222 | (char*)&(tcph->check) - (char*)tcph); |
@@ -171,7 +230,16 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
171 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 230 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
172 | { | 231 | { |
173 | struct tcphdr *tcph; | 232 | struct tcphdr *tcph; |
174 | const unsigned int tcphoff = ip_hdrlen(skb); | 233 | unsigned int tcphoff; |
234 | int oldlen; | ||
235 | |||
236 | #ifdef CONFIG_IP_VS_IPV6 | ||
237 | if (cp->af == AF_INET6) | ||
238 | tcphoff = sizeof(struct ipv6hdr); | ||
239 | else | ||
240 | #endif | ||
241 | tcphoff = ip_hdrlen(skb); | ||
242 | oldlen = skb->len - tcphoff; | ||
175 | 243 | ||
176 | /* csum_check requires unshared skb */ | 244 | /* csum_check requires unshared skb */ |
177 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) | 245 | if (!skb_make_writable(skb, tcphoff+sizeof(*tcph))) |
@@ -179,7 +247,7 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
179 | 247 | ||
180 | if (unlikely(cp->app != NULL)) { | 248 | if (unlikely(cp->app != NULL)) { |
181 | /* Some checks before mangling */ | 249 | /* Some checks before mangling */ |
182 | if (pp->csum_check && !pp->csum_check(skb, pp)) | 250 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
183 | return 0; | 251 | return 0; |
184 | 252 | ||
185 | /* | 253 | /* |
@@ -190,15 +258,19 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
190 | return 0; | 258 | return 0; |
191 | } | 259 | } |
192 | 260 | ||
193 | tcph = (void *)ip_hdr(skb) + tcphoff; | 261 | tcph = (void *)skb_network_header(skb) + tcphoff; |
194 | tcph->dest = cp->dport; | 262 | tcph->dest = cp->dport; |
195 | 263 | ||
196 | /* | 264 | /* |
197 | * Adjust TCP checksums | 265 | * Adjust TCP checksums |
198 | */ | 266 | */ |
199 | if (!cp->app) { | 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) { | ||
200 | /* Only port and addr are changed, do fast csum update */ | 272 | /* Only port and addr are changed, do fast csum update */ |
201 | tcp_fast_csum_update(tcph, cp->vaddr, cp->daddr, | 273 | tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr, |
202 | cp->vport, cp->dport); | 274 | cp->vport, cp->dport); |
203 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 275 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
204 | skb->ip_summed = CHECKSUM_NONE; | 276 | skb->ip_summed = CHECKSUM_NONE; |
@@ -206,9 +278,19 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
206 | /* full checksum calculation */ | 278 | /* full checksum calculation */ |
207 | tcph->check = 0; | 279 | tcph->check = 0; |
208 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); | 280 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); |
209 | tcph->check = csum_tcpudp_magic(cp->caddr, cp->daddr, | 281 | #ifdef CONFIG_IP_VS_IPV6 |
210 | skb->len - tcphoff, | 282 | if (cp->af == AF_INET6) |
211 | cp->protocol, skb->csum); | 283 | tcph->check = csum_ipv6_magic(&cp->caddr.in6, |
284 | &cp->daddr.in6, | ||
285 | skb->len - tcphoff, | ||
286 | cp->protocol, skb->csum); | ||
287 | else | ||
288 | #endif | ||
289 | tcph->check = csum_tcpudp_magic(cp->caddr.ip, | ||
290 | cp->daddr.ip, | ||
291 | skb->len - tcphoff, | ||
292 | cp->protocol, | ||
293 | skb->csum); | ||
212 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 294 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
213 | } | 295 | } |
214 | return 1; | 296 | return 1; |
@@ -216,21 +298,43 @@ tcp_dnat_handler(struct sk_buff *skb, | |||
216 | 298 | ||
217 | 299 | ||
218 | static int | 300 | static int |
219 | tcp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp) | 301 | tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp) |
220 | { | 302 | { |
221 | const unsigned int tcphoff = ip_hdrlen(skb); | 303 | unsigned int tcphoff; |
304 | |||
305 | #ifdef CONFIG_IP_VS_IPV6 | ||
306 | if (af == AF_INET6) | ||
307 | tcphoff = sizeof(struct ipv6hdr); | ||
308 | else | ||
309 | #endif | ||
310 | tcphoff = ip_hdrlen(skb); | ||
222 | 311 | ||
223 | switch (skb->ip_summed) { | 312 | switch (skb->ip_summed) { |
224 | case CHECKSUM_NONE: | 313 | case CHECKSUM_NONE: |
225 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); | 314 | skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); |
226 | case CHECKSUM_COMPLETE: | 315 | case CHECKSUM_COMPLETE: |
227 | if (csum_tcpudp_magic(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, | 316 | #ifdef CONFIG_IP_VS_IPV6 |
228 | skb->len - tcphoff, | 317 | if (af == AF_INET6) { |
229 | ip_hdr(skb)->protocol, skb->csum)) { | 318 | if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, |
230 | IP_VS_DBG_RL_PKT(0, pp, skb, 0, | 319 | &ipv6_hdr(skb)->daddr, |
231 | "Failed checksum for"); | 320 | skb->len - tcphoff, |
232 | return 0; | 321 | ipv6_hdr(skb)->nexthdr, |
233 | } | 322 | skb->csum)) { |
323 | IP_VS_DBG_RL_PKT(0, pp, skb, 0, | ||
324 | "Failed checksum for"); | ||
325 | return 0; | ||
326 | } | ||
327 | } else | ||
328 | #endif | ||
329 | if (csum_tcpudp_magic(ip_hdr(skb)->saddr, | ||
330 | ip_hdr(skb)->daddr, | ||
331 | skb->len - tcphoff, | ||
332 | ip_hdr(skb)->protocol, | ||
333 | skb->csum)) { | ||
334 | IP_VS_DBG_RL_PKT(0, pp, skb, 0, | ||
335 | "Failed checksum for"); | ||
336 | return 0; | ||
337 | } | ||
234 | break; | 338 | break; |
235 | default: | 339 | default: |
236 | /* No need to checksum. */ | 340 | /* No need to checksum. */ |
@@ -419,19 +523,23 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, | |||
419 | if (new_state != cp->state) { | 523 | if (new_state != cp->state) { |
420 | struct ip_vs_dest *dest = cp->dest; | 524 | struct ip_vs_dest *dest = cp->dest; |
421 | 525 | ||
422 | IP_VS_DBG(8, "%s %s [%c%c%c%c] %u.%u.%u.%u:%d->" | 526 | IP_VS_DBG_BUF(8, "%s %s [%c%c%c%c] %s:%d->" |
423 | "%u.%u.%u.%u:%d state: %s->%s conn->refcnt:%d\n", | 527 | "%s:%d state: %s->%s conn->refcnt:%d\n", |
424 | pp->name, | 528 | pp->name, |
425 | (state_off==TCP_DIR_OUTPUT)?"output ":"input ", | 529 | ((state_off == TCP_DIR_OUTPUT) ? |
426 | th->syn? 'S' : '.', | 530 | "output " : "input "), |
427 | th->fin? 'F' : '.', | 531 | th->syn ? 'S' : '.', |
428 | th->ack? 'A' : '.', | 532 | th->fin ? 'F' : '.', |
429 | th->rst? 'R' : '.', | 533 | th->ack ? 'A' : '.', |
430 | NIPQUAD(cp->daddr), ntohs(cp->dport), | 534 | th->rst ? 'R' : '.', |
431 | NIPQUAD(cp->caddr), ntohs(cp->cport), | 535 | IP_VS_DBG_ADDR(cp->af, &cp->daddr), |
432 | tcp_state_name(cp->state), | 536 | ntohs(cp->dport), |
433 | tcp_state_name(new_state), | 537 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
434 | atomic_read(&cp->refcnt)); | 538 | ntohs(cp->cport), |
539 | tcp_state_name(cp->state), | ||
540 | tcp_state_name(new_state), | ||
541 | atomic_read(&cp->refcnt)); | ||
542 | |||
435 | if (dest) { | 543 | if (dest) { |
436 | if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && | 544 | if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && |
437 | (new_state != IP_VS_TCP_S_ESTABLISHED)) { | 545 | (new_state != IP_VS_TCP_S_ESTABLISHED)) { |
@@ -461,7 +569,13 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, | |||
461 | { | 569 | { |
462 | struct tcphdr _tcph, *th; | 570 | struct tcphdr _tcph, *th; |
463 | 571 | ||
464 | th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); | 572 | #ifdef CONFIG_IP_VS_IPV6 |
573 | int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); | ||
574 | #else | ||
575 | int ihl = ip_hdrlen(skb); | ||
576 | #endif | ||
577 | |||
578 | th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); | ||
465 | if (th == NULL) | 579 | if (th == NULL) |
466 | return 0; | 580 | return 0; |
467 | 581 | ||
@@ -546,12 +660,15 @@ tcp_app_conn_bind(struct ip_vs_conn *cp) | |||
546 | break; | 660 | break; |
547 | spin_unlock(&tcp_app_lock); | 661 | spin_unlock(&tcp_app_lock); |
548 | 662 | ||
549 | IP_VS_DBG(9, "%s: Binding conn %u.%u.%u.%u:%u->" | 663 | IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->" |
550 | "%u.%u.%u.%u:%u to app %s on port %u\n", | 664 | "%s:%u to app %s on port %u\n", |
551 | __func__, | 665 | __func__, |
552 | NIPQUAD(cp->caddr), ntohs(cp->cport), | 666 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
553 | NIPQUAD(cp->vaddr), ntohs(cp->vport), | 667 | ntohs(cp->cport), |
554 | inc->name, ntohs(inc->port)); | 668 | IP_VS_DBG_ADDR(cp->af, &cp->vaddr), |
669 | ntohs(cp->vport), | ||
670 | inc->name, ntohs(inc->port)); | ||
671 | |||
555 | cp->app = inc; | 672 | cp->app = inc; |
556 | if (inc->init_conn) | 673 | if (inc->init_conn) |
557 | result = inc->init_conn(inc, cp); | 674 | result = inc->init_conn(inc, cp); |
diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c index c6be5d56823f..e3ee26bd1de7 100644 --- a/net/ipv4/ipvs/ip_vs_proto_udp.c +++ b/net/ipv4/ipvs/ip_vs_proto_udp.c | |||
@@ -24,8 +24,9 @@ | |||
24 | #include <net/ip.h> | 24 | #include <net/ip.h> |
25 | 25 | ||
26 | static struct ip_vs_conn * | 26 | static struct ip_vs_conn * |
27 | udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | 27 | udp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, |
28 | const struct iphdr *iph, unsigned int proto_off, int inverse) | 28 | const struct ip_vs_iphdr *iph, unsigned int proto_off, |
29 | int inverse) | ||
29 | { | 30 | { |
30 | struct ip_vs_conn *cp; | 31 | struct ip_vs_conn *cp; |
31 | __be16 _ports[2], *pptr; | 32 | __be16 _ports[2], *pptr; |
@@ -35,13 +36,13 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
35 | return NULL; | 36 | return NULL; |
36 | 37 | ||
37 | if (likely(!inverse)) { | 38 | if (likely(!inverse)) { |
38 | cp = ip_vs_conn_in_get(iph->protocol, | 39 | cp = ip_vs_conn_in_get(af, iph->protocol, |
39 | iph->saddr, pptr[0], | 40 | &iph->saddr, pptr[0], |
40 | iph->daddr, pptr[1]); | 41 | &iph->daddr, pptr[1]); |
41 | } else { | 42 | } else { |
42 | cp = ip_vs_conn_in_get(iph->protocol, | 43 | cp = ip_vs_conn_in_get(af, iph->protocol, |
43 | iph->daddr, pptr[1], | 44 | &iph->daddr, pptr[1], |
44 | iph->saddr, pptr[0]); | 45 | &iph->saddr, pptr[0]); |
45 | } | 46 | } |
46 | 47 | ||
47 | return cp; | 48 | return cp; |
@@ -49,25 +50,25 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
49 | 50 | ||
50 | 51 | ||
51 | static struct ip_vs_conn * | 52 | static struct ip_vs_conn * |
52 | udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | 53 | udp_conn_out_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, |
53 | const struct iphdr *iph, unsigned int proto_off, int inverse) | 54 | const struct ip_vs_iphdr *iph, unsigned int proto_off, |
55 | int inverse) | ||
54 | { | 56 | { |
55 | struct ip_vs_conn *cp; | 57 | struct ip_vs_conn *cp; |
56 | __be16 _ports[2], *pptr; | 58 | __be16 _ports[2], *pptr; |
57 | 59 | ||
58 | pptr = skb_header_pointer(skb, ip_hdrlen(skb), | 60 | pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports); |
59 | sizeof(_ports), _ports); | ||
60 | if (pptr == NULL) | 61 | if (pptr == NULL) |
61 | return NULL; | 62 | return NULL; |
62 | 63 | ||
63 | if (likely(!inverse)) { | 64 | if (likely(!inverse)) { |
64 | cp = ip_vs_conn_out_get(iph->protocol, | 65 | cp = ip_vs_conn_out_get(af, iph->protocol, |
65 | iph->saddr, pptr[0], | 66 | &iph->saddr, pptr[0], |
66 | iph->daddr, pptr[1]); | 67 | &iph->daddr, pptr[1]); |
67 | } else { | 68 | } else { |
68 | cp = ip_vs_conn_out_get(iph->protocol, | 69 | cp = ip_vs_conn_out_get(af, iph->protocol, |
69 | iph->daddr, pptr[1], | 70 | &iph->daddr, pptr[1], |
70 | iph->saddr, pptr[0]); | 71 | &iph->saddr, pptr[0]); |
71 | } | 72 | } |
72 | 73 | ||
73 | return cp; | 74 | return cp; |
@@ -75,21 +76,24 @@ udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
75 | 76 | ||
76 | 77 | ||
77 | static int | 78 | static int |
78 | udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp, | 79 | udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, |
79 | int *verdict, struct ip_vs_conn **cpp) | 80 | int *verdict, struct ip_vs_conn **cpp) |
80 | { | 81 | { |
81 | struct ip_vs_service *svc; | 82 | struct ip_vs_service *svc; |
82 | struct udphdr _udph, *uh; | 83 | struct udphdr _udph, *uh; |
84 | struct ip_vs_iphdr iph; | ||
85 | |||
86 | ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); | ||
83 | 87 | ||
84 | uh = skb_header_pointer(skb, ip_hdrlen(skb), | 88 | uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph); |
85 | sizeof(_udph), &_udph); | ||
86 | if (uh == NULL) { | 89 | if (uh == NULL) { |
87 | *verdict = NF_DROP; | 90 | *verdict = NF_DROP; |
88 | return 0; | 91 | return 0; |
89 | } | 92 | } |
90 | 93 | ||
91 | if ((svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol, | 94 | svc = ip_vs_service_get(af, skb->mark, iph.protocol, |
92 | ip_hdr(skb)->daddr, uh->dest))) { | 95 | &iph.daddr, uh->dest); |
96 | if (svc) { | ||
93 | if (ip_vs_todrop()) { | 97 | if (ip_vs_todrop()) { |
94 | /* | 98 | /* |
95 | * It seems that we are very loaded. | 99 | * It seems that we are very loaded. |
@@ -116,23 +120,63 @@ udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp, | |||
116 | 120 | ||
117 | 121 | ||
118 | static inline void | 122 | static inline void |
119 | 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, | ||
120 | __be16 oldport, __be16 newport) | 126 | __be16 oldport, __be16 newport) |
121 | { | 127 | { |
122 | uhdr->check = | 128 | #ifdef CONFIG_IP_VS_IPV6 |
123 | csum_fold(ip_vs_check_diff4(oldip, newip, | 129 | if (af == AF_INET6) |
124 | ip_vs_check_diff2(oldport, newport, | 130 | uhdr->check = |
125 | ~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)))); | ||
126 | if (!uhdr->check) | 140 | if (!uhdr->check) |
127 | uhdr->check = CSUM_MANGLED_0; | 141 | uhdr->check = CSUM_MANGLED_0; |
128 | } | 142 | } |
129 | 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 | |||
130 | static int | 165 | static int |
131 | udp_snat_handler(struct sk_buff *skb, | 166 | udp_snat_handler(struct sk_buff *skb, |
132 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 167 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
133 | { | 168 | { |
134 | struct udphdr *udph; | 169 | struct udphdr *udph; |
135 | const unsigned int udphoff = ip_hdrlen(skb); | 170 | unsigned int udphoff; |
171 | int oldlen; | ||
172 | |||
173 | #ifdef CONFIG_IP_VS_IPV6 | ||
174 | if (cp->af == AF_INET6) | ||
175 | udphoff = sizeof(struct ipv6hdr); | ||
176 | else | ||
177 | #endif | ||
178 | udphoff = ip_hdrlen(skb); | ||
179 | oldlen = skb->len - udphoff; | ||
136 | 180 | ||
137 | /* csum_check requires unshared skb */ | 181 | /* csum_check requires unshared skb */ |
138 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) | 182 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) |
@@ -140,7 +184,7 @@ udp_snat_handler(struct sk_buff *skb, | |||
140 | 184 | ||
141 | if (unlikely(cp->app != NULL)) { | 185 | if (unlikely(cp->app != NULL)) { |
142 | /* Some checks before mangling */ | 186 | /* Some checks before mangling */ |
143 | if (pp->csum_check && !pp->csum_check(skb, pp)) | 187 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
144 | return 0; | 188 | return 0; |
145 | 189 | ||
146 | /* | 190 | /* |
@@ -150,15 +194,19 @@ udp_snat_handler(struct sk_buff *skb, | |||
150 | return 0; | 194 | return 0; |
151 | } | 195 | } |
152 | 196 | ||
153 | udph = (void *)ip_hdr(skb) + udphoff; | 197 | udph = (void *)skb_network_header(skb) + udphoff; |
154 | udph->source = cp->vport; | 198 | udph->source = cp->vport; |
155 | 199 | ||
156 | /* | 200 | /* |
157 | * Adjust UDP checksums | 201 | * Adjust UDP checksums |
158 | */ | 202 | */ |
159 | 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)) { | ||
160 | /* Only port and addr are changed, do fast csum update */ | 208 | /* Only port and addr are changed, do fast csum update */ |
161 | udp_fast_csum_update(udph, cp->daddr, cp->vaddr, | 209 | udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr, |
162 | cp->dport, cp->vport); | 210 | cp->dport, cp->vport); |
163 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 211 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
164 | skb->ip_summed = CHECKSUM_NONE; | 212 | skb->ip_summed = CHECKSUM_NONE; |
@@ -166,9 +214,19 @@ udp_snat_handler(struct sk_buff *skb, | |||
166 | /* full checksum calculation */ | 214 | /* full checksum calculation */ |
167 | udph->check = 0; | 215 | udph->check = 0; |
168 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); | 216 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); |
169 | udph->check = csum_tcpudp_magic(cp->vaddr, cp->caddr, | 217 | #ifdef CONFIG_IP_VS_IPV6 |
170 | skb->len - udphoff, | 218 | if (cp->af == AF_INET6) |
171 | cp->protocol, skb->csum); | 219 | udph->check = csum_ipv6_magic(&cp->vaddr.in6, |
220 | &cp->caddr.in6, | ||
221 | skb->len - udphoff, | ||
222 | cp->protocol, skb->csum); | ||
223 | else | ||
224 | #endif | ||
225 | udph->check = csum_tcpudp_magic(cp->vaddr.ip, | ||
226 | cp->caddr.ip, | ||
227 | skb->len - udphoff, | ||
228 | cp->protocol, | ||
229 | skb->csum); | ||
172 | if (udph->check == 0) | 230 | if (udph->check == 0) |
173 | udph->check = CSUM_MANGLED_0; | 231 | udph->check = CSUM_MANGLED_0; |
174 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", | 232 | IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n", |
@@ -184,7 +242,16 @@ udp_dnat_handler(struct sk_buff *skb, | |||
184 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) | 242 | struct ip_vs_protocol *pp, struct ip_vs_conn *cp) |
185 | { | 243 | { |
186 | struct udphdr *udph; | 244 | struct udphdr *udph; |
187 | unsigned int udphoff = ip_hdrlen(skb); | 245 | unsigned int udphoff; |
246 | int oldlen; | ||
247 | |||
248 | #ifdef CONFIG_IP_VS_IPV6 | ||
249 | if (cp->af == AF_INET6) | ||
250 | udphoff = sizeof(struct ipv6hdr); | ||
251 | else | ||
252 | #endif | ||
253 | udphoff = ip_hdrlen(skb); | ||
254 | oldlen = skb->len - udphoff; | ||
188 | 255 | ||
189 | /* csum_check requires unshared skb */ | 256 | /* csum_check requires unshared skb */ |
190 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) | 257 | if (!skb_make_writable(skb, udphoff+sizeof(*udph))) |
@@ -192,7 +259,7 @@ udp_dnat_handler(struct sk_buff *skb, | |||
192 | 259 | ||
193 | if (unlikely(cp->app != NULL)) { | 260 | if (unlikely(cp->app != NULL)) { |
194 | /* Some checks before mangling */ | 261 | /* Some checks before mangling */ |
195 | if (pp->csum_check && !pp->csum_check(skb, pp)) | 262 | if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) |
196 | return 0; | 263 | return 0; |
197 | 264 | ||
198 | /* | 265 | /* |
@@ -203,15 +270,19 @@ udp_dnat_handler(struct sk_buff *skb, | |||
203 | return 0; | 270 | return 0; |
204 | } | 271 | } |
205 | 272 | ||
206 | udph = (void *)ip_hdr(skb) + udphoff; | 273 | udph = (void *)skb_network_header(skb) + udphoff; |
207 | udph->dest = cp->dport; | 274 | udph->dest = cp->dport; |
208 | 275 | ||
209 | /* | 276 | /* |
210 | * Adjust UDP checksums | 277 | * Adjust UDP checksums |
211 | */ | 278 | */ |
212 | 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)) { | ||
213 | /* Only port and addr are changed, do fast csum update */ | 284 | /* Only port and addr are changed, do fast csum update */ |
214 | udp_fast_csum_update(udph, cp->vaddr, cp->daddr, | 285 | udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr, |
215 | cp->vport, cp->dport); | 286 | cp->vport, cp->dport); |
216 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 287 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
217 | skb->ip_summed = CHECKSUM_NONE; | 288 | skb->ip_summed = CHECKSUM_NONE; |
@@ -219,9 +290,19 @@ udp_dnat_handler(struct sk_buff *skb, | |||
219 | /* full checksum calculation */ | 290 | /* full checksum calculation */ |
220 | udph->check = 0; | 291 | udph->check = 0; |
221 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); | 292 | skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0); |
222 | udph->check = csum_tcpudp_magic(cp->caddr, cp->daddr, | 293 | #ifdef CONFIG_IP_VS_IPV6 |
223 | skb->len - udphoff, | 294 | if (cp->af == AF_INET6) |
224 | cp->protocol, skb->csum); | 295 | udph->check = csum_ipv6_magic(&cp->caddr.in6, |
296 | &cp->daddr.in6, | ||
297 | skb->len - udphoff, | ||
298 | cp->protocol, skb->csum); | ||
299 | else | ||
300 | #endif | ||
301 | udph->check = csum_tcpudp_magic(cp->caddr.ip, | ||
302 | cp->daddr.ip, | ||
303 | skb->len - udphoff, | ||
304 | cp->protocol, | ||
305 | skb->csum); | ||
225 | if (udph->check == 0) | 306 | if (udph->check == 0) |
226 | udph->check = CSUM_MANGLED_0; | 307 | udph->check = CSUM_MANGLED_0; |
227 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 308 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
@@ -231,10 +312,17 @@ udp_dnat_handler(struct sk_buff *skb, | |||
231 | 312 | ||
232 | 313 | ||
233 | static int | 314 | static int |
234 | udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp) | 315 | udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp) |
235 | { | 316 | { |
236 | struct udphdr _udph, *uh; | 317 | struct udphdr _udph, *uh; |
237 | const unsigned int udphoff = ip_hdrlen(skb); | 318 | unsigned int udphoff; |
319 | |||
320 | #ifdef CONFIG_IP_VS_IPV6 | ||
321 | if (af == AF_INET6) | ||
322 | udphoff = sizeof(struct ipv6hdr); | ||
323 | else | ||
324 | #endif | ||
325 | udphoff = ip_hdrlen(skb); | ||
238 | 326 | ||
239 | uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); | 327 | uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); |
240 | if (uh == NULL) | 328 | if (uh == NULL) |
@@ -246,15 +334,28 @@ udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp) | |||
246 | skb->csum = skb_checksum(skb, udphoff, | 334 | skb->csum = skb_checksum(skb, udphoff, |
247 | skb->len - udphoff, 0); | 335 | skb->len - udphoff, 0); |
248 | case CHECKSUM_COMPLETE: | 336 | case CHECKSUM_COMPLETE: |
249 | if (csum_tcpudp_magic(ip_hdr(skb)->saddr, | 337 | #ifdef CONFIG_IP_VS_IPV6 |
250 | ip_hdr(skb)->daddr, | 338 | if (af == AF_INET6) { |
251 | skb->len - udphoff, | 339 | if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, |
252 | ip_hdr(skb)->protocol, | 340 | &ipv6_hdr(skb)->daddr, |
253 | skb->csum)) { | 341 | skb->len - udphoff, |
254 | IP_VS_DBG_RL_PKT(0, pp, skb, 0, | 342 | ipv6_hdr(skb)->nexthdr, |
255 | "Failed checksum for"); | 343 | skb->csum)) { |
256 | return 0; | 344 | IP_VS_DBG_RL_PKT(0, pp, skb, 0, |
257 | } | 345 | "Failed checksum for"); |
346 | return 0; | ||
347 | } | ||
348 | } else | ||
349 | #endif | ||
350 | if (csum_tcpudp_magic(ip_hdr(skb)->saddr, | ||
351 | ip_hdr(skb)->daddr, | ||
352 | skb->len - udphoff, | ||
353 | ip_hdr(skb)->protocol, | ||
354 | skb->csum)) { | ||
355 | IP_VS_DBG_RL_PKT(0, pp, skb, 0, | ||
356 | "Failed checksum for"); | ||
357 | return 0; | ||
358 | } | ||
258 | break; | 359 | break; |
259 | default: | 360 | default: |
260 | /* No need to checksum. */ | 361 | /* No need to checksum. */ |
@@ -340,12 +441,15 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp) | |||
340 | break; | 441 | break; |
341 | spin_unlock(&udp_app_lock); | 442 | spin_unlock(&udp_app_lock); |
342 | 443 | ||
343 | IP_VS_DBG(9, "%s: Binding conn %u.%u.%u.%u:%u->" | 444 | IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->" |
344 | "%u.%u.%u.%u:%u to app %s on port %u\n", | 445 | "%s:%u to app %s on port %u\n", |
345 | __func__, | 446 | __func__, |
346 | NIPQUAD(cp->caddr), ntohs(cp->cport), | 447 | IP_VS_DBG_ADDR(cp->af, &cp->caddr), |
347 | NIPQUAD(cp->vaddr), ntohs(cp->vport), | 448 | ntohs(cp->cport), |
348 | inc->name, ntohs(inc->port)); | 449 | IP_VS_DBG_ADDR(cp->af, &cp->vaddr), |
450 | ntohs(cp->vport), | ||
451 | inc->name, ntohs(inc->port)); | ||
452 | |||
349 | cp->app = inc; | 453 | cp->app = inc; |
350 | if (inc->init_conn) | 454 | if (inc->init_conn) |
351 | result = inc->init_conn(inc, cp); | 455 | result = inc->init_conn(inc, cp); |
diff --git a/net/ipv4/ipvs/ip_vs_rr.c b/net/ipv4/ipvs/ip_vs_rr.c index f74929117534..a22195f68ac4 100644 --- a/net/ipv4/ipvs/ip_vs_rr.c +++ b/net/ipv4/ipvs/ip_vs_rr.c | |||
@@ -74,11 +74,11 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
74 | out: | 74 | out: |
75 | svc->sched_data = q; | 75 | svc->sched_data = q; |
76 | write_unlock(&svc->sched_lock); | 76 | write_unlock(&svc->sched_lock); |
77 | IP_VS_DBG(6, "RR: server %u.%u.%u.%u:%u " | 77 | IP_VS_DBG_BUF(6, "RR: server %s:%u " |
78 | "activeconns %d refcnt %d weight %d\n", | 78 | "activeconns %d refcnt %d weight %d\n", |
79 | NIPQUAD(dest->addr), ntohs(dest->port), | 79 | IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port), |
80 | atomic_read(&dest->activeconns), | 80 | atomic_read(&dest->activeconns), |
81 | atomic_read(&dest->refcnt), atomic_read(&dest->weight)); | 81 | atomic_read(&dest->refcnt), atomic_read(&dest->weight)); |
82 | 82 | ||
83 | return dest; | 83 | return dest; |
84 | } | 84 | } |
@@ -89,6 +89,9 @@ static struct ip_vs_scheduler ip_vs_rr_scheduler = { | |||
89 | .refcnt = ATOMIC_INIT(0), | 89 | .refcnt = ATOMIC_INIT(0), |
90 | .module = THIS_MODULE, | 90 | .module = THIS_MODULE, |
91 | .n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list), | 91 | .n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list), |
92 | #ifdef CONFIG_IP_VS_IPV6 | ||
93 | .supports_ipv6 = 1, | ||
94 | #endif | ||
92 | .init_service = ip_vs_rr_init_svc, | 95 | .init_service = ip_vs_rr_init_svc, |
93 | .update_service = ip_vs_rr_update_svc, | 96 | .update_service = ip_vs_rr_update_svc, |
94 | .schedule = ip_vs_rr_schedule, | 97 | .schedule = ip_vs_rr_schedule, |
diff --git a/net/ipv4/ipvs/ip_vs_sed.c b/net/ipv4/ipvs/ip_vs_sed.c index 53f73bea66ce..7d2f22f04b83 100644 --- a/net/ipv4/ipvs/ip_vs_sed.c +++ b/net/ipv4/ipvs/ip_vs_sed.c | |||
@@ -101,12 +101,12 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
101 | } | 101 | } |
102 | } | 102 | } |
103 | 103 | ||
104 | IP_VS_DBG(6, "SED: server %u.%u.%u.%u:%u " | 104 | IP_VS_DBG_BUF(6, "SED: server %s:%u " |
105 | "activeconns %d refcnt %d weight %d overhead %d\n", | 105 | "activeconns %d refcnt %d weight %d overhead %d\n", |
106 | NIPQUAD(least->addr), ntohs(least->port), | 106 | IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port), |
107 | atomic_read(&least->activeconns), | 107 | atomic_read(&least->activeconns), |
108 | atomic_read(&least->refcnt), | 108 | atomic_read(&least->refcnt), |
109 | atomic_read(&least->weight), loh); | 109 | atomic_read(&least->weight), loh); |
110 | 110 | ||
111 | return least; | 111 | return least; |
112 | } | 112 | } |
@@ -118,6 +118,9 @@ static struct ip_vs_scheduler ip_vs_sed_scheduler = | |||
118 | .refcnt = ATOMIC_INIT(0), | 118 | .refcnt = ATOMIC_INIT(0), |
119 | .module = THIS_MODULE, | 119 | .module = THIS_MODULE, |
120 | .n_list = LIST_HEAD_INIT(ip_vs_sed_scheduler.n_list), | 120 | .n_list = LIST_HEAD_INIT(ip_vs_sed_scheduler.n_list), |
121 | #ifdef CONFIG_IP_VS_IPV6 | ||
122 | .supports_ipv6 = 1, | ||
123 | #endif | ||
121 | .schedule = ip_vs_sed_schedule, | 124 | .schedule = ip_vs_sed_schedule, |
122 | }; | 125 | }; |
123 | 126 | ||
diff --git a/net/ipv4/ipvs/ip_vs_sh.c b/net/ipv4/ipvs/ip_vs_sh.c index 7b979e228056..1d96de27fefd 100644 --- a/net/ipv4/ipvs/ip_vs_sh.c +++ b/net/ipv4/ipvs/ip_vs_sh.c | |||
@@ -215,7 +215,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
215 | IP_VS_DBG(6, "SH: source IP address %u.%u.%u.%u " | 215 | IP_VS_DBG(6, "SH: source IP address %u.%u.%u.%u " |
216 | "--> server %u.%u.%u.%u:%d\n", | 216 | "--> server %u.%u.%u.%u:%d\n", |
217 | NIPQUAD(iph->saddr), | 217 | NIPQUAD(iph->saddr), |
218 | NIPQUAD(dest->addr), | 218 | NIPQUAD(dest->addr.ip), |
219 | ntohs(dest->port)); | 219 | ntohs(dest->port)); |
220 | 220 | ||
221 | return dest; | 221 | return dest; |
@@ -231,6 +231,9 @@ static struct ip_vs_scheduler ip_vs_sh_scheduler = | |||
231 | .refcnt = ATOMIC_INIT(0), | 231 | .refcnt = ATOMIC_INIT(0), |
232 | .module = THIS_MODULE, | 232 | .module = THIS_MODULE, |
233 | .n_list = LIST_HEAD_INIT(ip_vs_sh_scheduler.n_list), | 233 | .n_list = LIST_HEAD_INIT(ip_vs_sh_scheduler.n_list), |
234 | #ifdef CONFIG_IP_VS_IPV6 | ||
235 | .supports_ipv6 = 0, | ||
236 | #endif | ||
234 | .init_service = ip_vs_sh_init_svc, | 237 | .init_service = ip_vs_sh_init_svc, |
235 | .done_service = ip_vs_sh_done_svc, | 238 | .done_service = ip_vs_sh_done_svc, |
236 | .update_service = ip_vs_sh_update_svc, | 239 | .update_service = ip_vs_sh_update_svc, |
diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c index a652da2c3200..28237a5f62e2 100644 --- a/net/ipv4/ipvs/ip_vs_sync.c +++ b/net/ipv4/ipvs/ip_vs_sync.c | |||
@@ -256,9 +256,9 @@ void ip_vs_sync_conn(struct ip_vs_conn *cp) | |||
256 | s->cport = cp->cport; | 256 | s->cport = cp->cport; |
257 | s->vport = cp->vport; | 257 | s->vport = cp->vport; |
258 | s->dport = cp->dport; | 258 | s->dport = cp->dport; |
259 | s->caddr = cp->caddr; | 259 | s->caddr = cp->caddr.ip; |
260 | s->vaddr = cp->vaddr; | 260 | s->vaddr = cp->vaddr.ip; |
261 | s->daddr = cp->daddr; | 261 | s->daddr = cp->daddr.ip; |
262 | s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED); | 262 | s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED); |
263 | s->state = htons(cp->state); | 263 | s->state = htons(cp->state); |
264 | if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { | 264 | if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { |
@@ -366,21 +366,28 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) | |||
366 | } | 366 | } |
367 | 367 | ||
368 | if (!(flags & IP_VS_CONN_F_TEMPLATE)) | 368 | if (!(flags & IP_VS_CONN_F_TEMPLATE)) |
369 | cp = ip_vs_conn_in_get(s->protocol, | 369 | cp = ip_vs_conn_in_get(AF_INET, s->protocol, |
370 | s->caddr, s->cport, | 370 | (union nf_inet_addr *)&s->caddr, |
371 | s->vaddr, s->vport); | 371 | s->cport, |
372 | (union nf_inet_addr *)&s->vaddr, | ||
373 | s->vport); | ||
372 | else | 374 | else |
373 | cp = ip_vs_ct_in_get(s->protocol, | 375 | cp = ip_vs_ct_in_get(AF_INET, s->protocol, |
374 | s->caddr, s->cport, | 376 | (union nf_inet_addr *)&s->caddr, |
375 | s->vaddr, s->vport); | 377 | s->cport, |
378 | (union nf_inet_addr *)&s->vaddr, | ||
379 | s->vport); | ||
376 | if (!cp) { | 380 | if (!cp) { |
377 | /* | 381 | /* |
378 | * Find the appropriate destination for the connection. | 382 | * Find the appropriate destination for the connection. |
379 | * If it is not found the connection will remain unbound | 383 | * If it is not found the connection will remain unbound |
380 | * but still handled. | 384 | * but still handled. |
381 | */ | 385 | */ |
382 | dest = ip_vs_find_dest(s->daddr, s->dport, | 386 | dest = ip_vs_find_dest(AF_INET, |
383 | s->vaddr, s->vport, | 387 | (union nf_inet_addr *)&s->daddr, |
388 | s->dport, | ||
389 | (union nf_inet_addr *)&s->vaddr, | ||
390 | s->vport, | ||
384 | s->protocol); | 391 | s->protocol); |
385 | /* Set the approprite ativity flag */ | 392 | /* Set the approprite ativity flag */ |
386 | if (s->protocol == IPPROTO_TCP) { | 393 | if (s->protocol == IPPROTO_TCP) { |
@@ -389,10 +396,13 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) | |||
389 | else | 396 | else |
390 | flags &= ~IP_VS_CONN_F_INACTIVE; | 397 | flags &= ~IP_VS_CONN_F_INACTIVE; |
391 | } | 398 | } |
392 | cp = ip_vs_conn_new(s->protocol, | 399 | cp = ip_vs_conn_new(AF_INET, s->protocol, |
393 | s->caddr, s->cport, | 400 | (union nf_inet_addr *)&s->caddr, |
394 | s->vaddr, s->vport, | 401 | s->cport, |
395 | s->daddr, s->dport, | 402 | (union nf_inet_addr *)&s->vaddr, |
403 | s->vport, | ||
404 | (union nf_inet_addr *)&s->daddr, | ||
405 | s->dport, | ||
396 | flags, dest); | 406 | flags, dest); |
397 | if (dest) | 407 | if (dest) |
398 | atomic_dec(&dest->refcnt); | 408 | atomic_dec(&dest->refcnt); |
diff --git a/net/ipv4/ipvs/ip_vs_wlc.c b/net/ipv4/ipvs/ip_vs_wlc.c index df7ad8d74766..8c596e712599 100644 --- a/net/ipv4/ipvs/ip_vs_wlc.c +++ b/net/ipv4/ipvs/ip_vs_wlc.c | |||
@@ -89,12 +89,12 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
89 | } | 89 | } |
90 | } | 90 | } |
91 | 91 | ||
92 | IP_VS_DBG(6, "WLC: server %u.%u.%u.%u:%u " | 92 | IP_VS_DBG_BUF(6, "WLC: server %s:%u " |
93 | "activeconns %d refcnt %d weight %d overhead %d\n", | 93 | "activeconns %d refcnt %d weight %d overhead %d\n", |
94 | NIPQUAD(least->addr), ntohs(least->port), | 94 | IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port), |
95 | atomic_read(&least->activeconns), | 95 | atomic_read(&least->activeconns), |
96 | atomic_read(&least->refcnt), | 96 | atomic_read(&least->refcnt), |
97 | atomic_read(&least->weight), loh); | 97 | atomic_read(&least->weight), loh); |
98 | 98 | ||
99 | return least; | 99 | return least; |
100 | } | 100 | } |
@@ -106,6 +106,9 @@ static struct ip_vs_scheduler ip_vs_wlc_scheduler = | |||
106 | .refcnt = ATOMIC_INIT(0), | 106 | .refcnt = ATOMIC_INIT(0), |
107 | .module = THIS_MODULE, | 107 | .module = THIS_MODULE, |
108 | .n_list = LIST_HEAD_INIT(ip_vs_wlc_scheduler.n_list), | 108 | .n_list = LIST_HEAD_INIT(ip_vs_wlc_scheduler.n_list), |
109 | #ifdef CONFIG_IP_VS_IPV6 | ||
110 | .supports_ipv6 = 1, | ||
111 | #endif | ||
109 | .schedule = ip_vs_wlc_schedule, | 112 | .schedule = ip_vs_wlc_schedule, |
110 | }; | 113 | }; |
111 | 114 | ||
diff --git a/net/ipv4/ipvs/ip_vs_wrr.c b/net/ipv4/ipvs/ip_vs_wrr.c index 0d86a79b87b5..7ea92fed50bf 100644 --- a/net/ipv4/ipvs/ip_vs_wrr.c +++ b/net/ipv4/ipvs/ip_vs_wrr.c | |||
@@ -195,12 +195,12 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) | |||
195 | } | 195 | } |
196 | } | 196 | } |
197 | 197 | ||
198 | IP_VS_DBG(6, "WRR: server %u.%u.%u.%u:%u " | 198 | IP_VS_DBG_BUF(6, "WRR: server %s:%u " |
199 | "activeconns %d refcnt %d weight %d\n", | 199 | "activeconns %d refcnt %d weight %d\n", |
200 | NIPQUAD(dest->addr), ntohs(dest->port), | 200 | IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port), |
201 | atomic_read(&dest->activeconns), | 201 | atomic_read(&dest->activeconns), |
202 | atomic_read(&dest->refcnt), | 202 | atomic_read(&dest->refcnt), |
203 | atomic_read(&dest->weight)); | 203 | atomic_read(&dest->weight)); |
204 | 204 | ||
205 | out: | 205 | out: |
206 | write_unlock(&svc->sched_lock); | 206 | write_unlock(&svc->sched_lock); |
@@ -213,6 +213,9 @@ static struct ip_vs_scheduler ip_vs_wrr_scheduler = { | |||
213 | .refcnt = ATOMIC_INIT(0), | 213 | .refcnt = ATOMIC_INIT(0), |
214 | .module = THIS_MODULE, | 214 | .module = THIS_MODULE, |
215 | .n_list = LIST_HEAD_INIT(ip_vs_wrr_scheduler.n_list), | 215 | .n_list = LIST_HEAD_INIT(ip_vs_wrr_scheduler.n_list), |
216 | #ifdef CONFIG_IP_VS_IPV6 | ||
217 | .supports_ipv6 = 1, | ||
218 | #endif | ||
216 | .init_service = ip_vs_wrr_init_svc, | 219 | .init_service = ip_vs_wrr_init_svc, |
217 | .done_service = ip_vs_wrr_done_svc, | 220 | .done_service = ip_vs_wrr_done_svc, |
218 | .update_service = ip_vs_wrr_update_svc, | 221 | .update_service = ip_vs_wrr_update_svc, |
diff --git a/net/ipv4/ipvs/ip_vs_xmit.c b/net/ipv4/ipvs/ip_vs_xmit.c index 9892d4aca42e..02ddc2b3ce2e 100644 --- a/net/ipv4/ipvs/ip_vs_xmit.c +++ b/net/ipv4/ipvs/ip_vs_xmit.c | |||
@@ -20,6 +20,9 @@ | |||
20 | #include <net/udp.h> | 20 | #include <net/udp.h> |
21 | #include <net/icmp.h> /* for icmp_send */ | 21 | #include <net/icmp.h> /* for icmp_send */ |
22 | #include <net/route.h> /* for ip_route_output */ | 22 | #include <net/route.h> /* for ip_route_output */ |
23 | #include <net/ipv6.h> | ||
24 | #include <net/ip6_route.h> | ||
25 | #include <linux/icmpv6.h> | ||
23 | #include <linux/netfilter.h> | 26 | #include <linux/netfilter.h> |
24 | #include <linux/netfilter_ipv4.h> | 27 | #include <linux/netfilter_ipv4.h> |
25 | 28 | ||
@@ -47,7 +50,8 @@ __ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos, u32 cookie) | |||
47 | 50 | ||
48 | if (!dst) | 51 | if (!dst) |
49 | return NULL; | 52 | return NULL; |
50 | if ((dst->obsolete || rtos != dest->dst_rtos) && | 53 | if ((dst->obsolete |
54 | || (dest->af == AF_INET && rtos != dest->dst_rtos)) && | ||
51 | dst->ops->check(dst, cookie) == NULL) { | 55 | dst->ops->check(dst, cookie) == NULL) { |
52 | dest->dst_cache = NULL; | 56 | dest->dst_cache = NULL; |
53 | dst_release(dst); | 57 | dst_release(dst); |
@@ -71,7 +75,7 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos) | |||
71 | .oif = 0, | 75 | .oif = 0, |
72 | .nl_u = { | 76 | .nl_u = { |
73 | .ip4_u = { | 77 | .ip4_u = { |
74 | .daddr = dest->addr, | 78 | .daddr = dest->addr.ip, |
75 | .saddr = 0, | 79 | .saddr = 0, |
76 | .tos = rtos, } }, | 80 | .tos = rtos, } }, |
77 | }; | 81 | }; |
@@ -80,12 +84,12 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos) | |||
80 | spin_unlock(&dest->dst_lock); | 84 | spin_unlock(&dest->dst_lock); |
81 | IP_VS_DBG_RL("ip_route_output error, " | 85 | IP_VS_DBG_RL("ip_route_output error, " |
82 | "dest: %u.%u.%u.%u\n", | 86 | "dest: %u.%u.%u.%u\n", |
83 | NIPQUAD(dest->addr)); | 87 | NIPQUAD(dest->addr.ip)); |
84 | return NULL; | 88 | return NULL; |
85 | } | 89 | } |
86 | __ip_vs_dst_set(dest, rtos, dst_clone(&rt->u.dst)); | 90 | __ip_vs_dst_set(dest, rtos, dst_clone(&rt->u.dst)); |
87 | IP_VS_DBG(10, "new dst %u.%u.%u.%u, refcnt=%d, rtos=%X\n", | 91 | IP_VS_DBG(10, "new dst %u.%u.%u.%u, refcnt=%d, rtos=%X\n", |
88 | NIPQUAD(dest->addr), | 92 | NIPQUAD(dest->addr.ip), |
89 | atomic_read(&rt->u.dst.__refcnt), rtos); | 93 | atomic_read(&rt->u.dst.__refcnt), rtos); |
90 | } | 94 | } |
91 | spin_unlock(&dest->dst_lock); | 95 | spin_unlock(&dest->dst_lock); |
@@ -94,14 +98,14 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos) | |||
94 | .oif = 0, | 98 | .oif = 0, |
95 | .nl_u = { | 99 | .nl_u = { |
96 | .ip4_u = { | 100 | .ip4_u = { |
97 | .daddr = cp->daddr, | 101 | .daddr = cp->daddr.ip, |
98 | .saddr = 0, | 102 | .saddr = 0, |
99 | .tos = rtos, } }, | 103 | .tos = rtos, } }, |
100 | }; | 104 | }; |
101 | 105 | ||
102 | if (ip_route_output_key(&init_net, &rt, &fl)) { | 106 | if (ip_route_output_key(&init_net, &rt, &fl)) { |
103 | IP_VS_DBG_RL("ip_route_output error, dest: " | 107 | IP_VS_DBG_RL("ip_route_output error, dest: " |
104 | "%u.%u.%u.%u\n", NIPQUAD(cp->daddr)); | 108 | "%u.%u.%u.%u\n", NIPQUAD(cp->daddr.ip)); |
105 | return NULL; | 109 | return NULL; |
106 | } | 110 | } |
107 | } | 111 | } |
@@ -109,6 +113,70 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos) | |||
109 | return rt; | 113 | return rt; |
110 | } | 114 | } |
111 | 115 | ||
116 | #ifdef CONFIG_IP_VS_IPV6 | ||
117 | static struct rt6_info * | ||
118 | __ip_vs_get_out_rt_v6(struct ip_vs_conn *cp) | ||
119 | { | ||
120 | struct rt6_info *rt; /* Route to the other host */ | ||
121 | struct ip_vs_dest *dest = cp->dest; | ||
122 | |||
123 | if (dest) { | ||
124 | spin_lock(&dest->dst_lock); | ||
125 | rt = (struct rt6_info *)__ip_vs_dst_check(dest, 0, 0); | ||
126 | if (!rt) { | ||
127 | struct flowi fl = { | ||
128 | .oif = 0, | ||
129 | .nl_u = { | ||
130 | .ip6_u = { | ||
131 | .daddr = dest->addr.in6, | ||
132 | .saddr = { | ||
133 | .s6_addr32 = | ||
134 | { 0, 0, 0, 0 }, | ||
135 | }, | ||
136 | }, | ||
137 | }, | ||
138 | }; | ||
139 | |||
140 | rt = (struct rt6_info *)ip6_route_output(&init_net, | ||
141 | NULL, &fl); | ||
142 | if (!rt) { | ||
143 | spin_unlock(&dest->dst_lock); | ||
144 | IP_VS_DBG_RL("ip6_route_output error, " | ||
145 | "dest: " NIP6_FMT "\n", | ||
146 | NIP6(dest->addr.in6)); | ||
147 | return NULL; | ||
148 | } | ||
149 | __ip_vs_dst_set(dest, 0, dst_clone(&rt->u.dst)); | ||
150 | IP_VS_DBG(10, "new dst " NIP6_FMT ", refcnt=%d\n", | ||
151 | NIP6(dest->addr.in6), | ||
152 | atomic_read(&rt->u.dst.__refcnt)); | ||
153 | } | ||
154 | spin_unlock(&dest->dst_lock); | ||
155 | } else { | ||
156 | struct flowi fl = { | ||
157 | .oif = 0, | ||
158 | .nl_u = { | ||
159 | .ip6_u = { | ||
160 | .daddr = cp->daddr.in6, | ||
161 | .saddr = { | ||
162 | .s6_addr32 = { 0, 0, 0, 0 }, | ||
163 | }, | ||
164 | }, | ||
165 | }, | ||
166 | }; | ||
167 | |||
168 | rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl); | ||
169 | if (!rt) { | ||
170 | IP_VS_DBG_RL("ip6_route_output error, dest: " | ||
171 | NIP6_FMT "\n", NIP6(cp->daddr.in6)); | ||
172 | return NULL; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | return rt; | ||
177 | } | ||
178 | #endif | ||
179 | |||
112 | 180 | ||
113 | /* | 181 | /* |
114 | * Release dest->dst_cache before a dest is removed | 182 | * Release dest->dst_cache before a dest is removed |
@@ -123,11 +191,11 @@ ip_vs_dst_reset(struct ip_vs_dest *dest) | |||
123 | dst_release(old_dst); | 191 | dst_release(old_dst); |
124 | } | 192 | } |
125 | 193 | ||
126 | #define IP_VS_XMIT(skb, rt) \ | 194 | #define IP_VS_XMIT(pf, skb, rt) \ |
127 | do { \ | 195 | do { \ |
128 | (skb)->ipvs_property = 1; \ | 196 | (skb)->ipvs_property = 1; \ |
129 | skb_forward_csum(skb); \ | 197 | skb_forward_csum(skb); \ |
130 | NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, (skb), NULL, \ | 198 | NF_HOOK(pf, NF_INET_LOCAL_OUT, (skb), NULL, \ |
131 | (rt)->u.dst.dev, dst_output); \ | 199 | (rt)->u.dst.dev, dst_output); \ |
132 | } while (0) | 200 | } while (0) |
133 | 201 | ||
@@ -200,7 +268,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
200 | /* Another hack: avoid icmp_send in ip_fragment */ | 268 | /* Another hack: avoid icmp_send in ip_fragment */ |
201 | skb->local_df = 1; | 269 | skb->local_df = 1; |
202 | 270 | ||
203 | IP_VS_XMIT(skb, rt); | 271 | IP_VS_XMIT(PF_INET, skb, rt); |
204 | 272 | ||
205 | LeaveFunction(10); | 273 | LeaveFunction(10); |
206 | return NF_STOLEN; | 274 | return NF_STOLEN; |
@@ -213,6 +281,70 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
213 | return NF_STOLEN; | 281 | return NF_STOLEN; |
214 | } | 282 | } |
215 | 283 | ||
284 | #ifdef CONFIG_IP_VS_IPV6 | ||
285 | int | ||
286 | ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | ||
287 | struct ip_vs_protocol *pp) | ||
288 | { | ||
289 | struct rt6_info *rt; /* Route to the other host */ | ||
290 | struct ipv6hdr *iph = ipv6_hdr(skb); | ||
291 | int mtu; | ||
292 | struct flowi fl = { | ||
293 | .oif = 0, | ||
294 | .nl_u = { | ||
295 | .ip6_u = { | ||
296 | .daddr = iph->daddr, | ||
297 | .saddr = { .s6_addr32 = {0, 0, 0, 0} }, } }, | ||
298 | }; | ||
299 | |||
300 | EnterFunction(10); | ||
301 | |||
302 | rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl); | ||
303 | if (!rt) { | ||
304 | IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): ip6_route_output error, " | ||
305 | "dest: " NIP6_FMT "\n", NIP6(iph->daddr)); | ||
306 | goto tx_error_icmp; | ||
307 | } | ||
308 | |||
309 | /* MTU checking */ | ||
310 | mtu = dst_mtu(&rt->u.dst); | ||
311 | if (skb->len > mtu) { | ||
312 | dst_release(&rt->u.dst); | ||
313 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); | ||
314 | IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): frag needed\n"); | ||
315 | goto tx_error; | ||
316 | } | ||
317 | |||
318 | /* | ||
319 | * Call ip_send_check because we are not sure it is called | ||
320 | * after ip_defrag. Is copy-on-write needed? | ||
321 | */ | ||
322 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
323 | if (unlikely(skb == NULL)) { | ||
324 | dst_release(&rt->u.dst); | ||
325 | return NF_STOLEN; | ||
326 | } | ||
327 | |||
328 | /* drop old route */ | ||
329 | dst_release(skb->dst); | ||
330 | skb->dst = &rt->u.dst; | ||
331 | |||
332 | /* Another hack: avoid icmp_send in ip_fragment */ | ||
333 | skb->local_df = 1; | ||
334 | |||
335 | IP_VS_XMIT(PF_INET6, skb, rt); | ||
336 | |||
337 | LeaveFunction(10); | ||
338 | return NF_STOLEN; | ||
339 | |||
340 | tx_error_icmp: | ||
341 | dst_link_failure(skb); | ||
342 | tx_error: | ||
343 | kfree_skb(skb); | ||
344 | LeaveFunction(10); | ||
345 | return NF_STOLEN; | ||
346 | } | ||
347 | #endif | ||
216 | 348 | ||
217 | /* | 349 | /* |
218 | * NAT transmitter (only for outside-to-inside nat forwarding) | 350 | * NAT transmitter (only for outside-to-inside nat forwarding) |
@@ -264,7 +396,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
264 | /* mangle the packet */ | 396 | /* mangle the packet */ |
265 | if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp)) | 397 | if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp)) |
266 | goto tx_error; | 398 | goto tx_error; |
267 | ip_hdr(skb)->daddr = cp->daddr; | 399 | ip_hdr(skb)->daddr = cp->daddr.ip; |
268 | ip_send_check(ip_hdr(skb)); | 400 | ip_send_check(ip_hdr(skb)); |
269 | 401 | ||
270 | IP_VS_DBG_PKT(10, pp, skb, 0, "After DNAT"); | 402 | IP_VS_DBG_PKT(10, pp, skb, 0, "After DNAT"); |
@@ -276,7 +408,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
276 | /* Another hack: avoid icmp_send in ip_fragment */ | 408 | /* Another hack: avoid icmp_send in ip_fragment */ |
277 | skb->local_df = 1; | 409 | skb->local_df = 1; |
278 | 410 | ||
279 | IP_VS_XMIT(skb, rt); | 411 | IP_VS_XMIT(PF_INET, skb, rt); |
280 | 412 | ||
281 | LeaveFunction(10); | 413 | LeaveFunction(10); |
282 | return NF_STOLEN; | 414 | return NF_STOLEN; |
@@ -292,6 +424,83 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
292 | goto tx_error; | 424 | goto tx_error; |
293 | } | 425 | } |
294 | 426 | ||
427 | #ifdef CONFIG_IP_VS_IPV6 | ||
428 | int | ||
429 | ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | ||
430 | struct ip_vs_protocol *pp) | ||
431 | { | ||
432 | struct rt6_info *rt; /* Route to the other host */ | ||
433 | int mtu; | ||
434 | |||
435 | EnterFunction(10); | ||
436 | |||
437 | /* check if it is a connection of no-client-port */ | ||
438 | if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) { | ||
439 | __be16 _pt, *p; | ||
440 | p = skb_header_pointer(skb, sizeof(struct ipv6hdr), | ||
441 | sizeof(_pt), &_pt); | ||
442 | if (p == NULL) | ||
443 | goto tx_error; | ||
444 | ip_vs_conn_fill_cport(cp, *p); | ||
445 | IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p)); | ||
446 | } | ||
447 | |||
448 | rt = __ip_vs_get_out_rt_v6(cp); | ||
449 | if (!rt) | ||
450 | goto tx_error_icmp; | ||
451 | |||
452 | /* MTU checking */ | ||
453 | mtu = dst_mtu(&rt->u.dst); | ||
454 | if (skb->len > mtu) { | ||
455 | dst_release(&rt->u.dst); | ||
456 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); | ||
457 | IP_VS_DBG_RL_PKT(0, pp, skb, 0, | ||
458 | "ip_vs_nat_xmit_v6(): frag needed for"); | ||
459 | goto tx_error; | ||
460 | } | ||
461 | |||
462 | /* copy-on-write the packet before mangling it */ | ||
463 | if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) | ||
464 | goto tx_error_put; | ||
465 | |||
466 | if (skb_cow(skb, rt->u.dst.dev->hard_header_len)) | ||
467 | goto tx_error_put; | ||
468 | |||
469 | /* drop old route */ | ||
470 | dst_release(skb->dst); | ||
471 | skb->dst = &rt->u.dst; | ||
472 | |||
473 | /* mangle the packet */ | ||
474 | if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp)) | ||
475 | goto tx_error; | ||
476 | ipv6_hdr(skb)->daddr = cp->daddr.in6; | ||
477 | |||
478 | IP_VS_DBG_PKT(10, pp, skb, 0, "After DNAT"); | ||
479 | |||
480 | /* FIXME: when application helper enlarges the packet and the length | ||
481 | is larger than the MTU of outgoing device, there will be still | ||
482 | MTU problem. */ | ||
483 | |||
484 | /* Another hack: avoid icmp_send in ip_fragment */ | ||
485 | skb->local_df = 1; | ||
486 | |||
487 | IP_VS_XMIT(PF_INET6, skb, rt); | ||
488 | |||
489 | LeaveFunction(10); | ||
490 | return NF_STOLEN; | ||
491 | |||
492 | tx_error_icmp: | ||
493 | dst_link_failure(skb); | ||
494 | tx_error: | ||
495 | LeaveFunction(10); | ||
496 | kfree_skb(skb); | ||
497 | return NF_STOLEN; | ||
498 | tx_error_put: | ||
499 | dst_release(&rt->u.dst); | ||
500 | goto tx_error; | ||
501 | } | ||
502 | #endif | ||
503 | |||
295 | 504 | ||
296 | /* | 505 | /* |
297 | * IP Tunneling transmitter | 506 | * IP Tunneling transmitter |
@@ -423,6 +632,112 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
423 | return NF_STOLEN; | 632 | return NF_STOLEN; |
424 | } | 633 | } |
425 | 634 | ||
635 | #ifdef CONFIG_IP_VS_IPV6 | ||
636 | int | ||
637 | ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | ||
638 | struct ip_vs_protocol *pp) | ||
639 | { | ||
640 | struct rt6_info *rt; /* Route to the other host */ | ||
641 | struct net_device *tdev; /* Device to other host */ | ||
642 | struct ipv6hdr *old_iph = ipv6_hdr(skb); | ||
643 | sk_buff_data_t old_transport_header = skb->transport_header; | ||
644 | struct ipv6hdr *iph; /* Our new IP header */ | ||
645 | unsigned int max_headroom; /* The extra header space needed */ | ||
646 | int mtu; | ||
647 | |||
648 | EnterFunction(10); | ||
649 | |||
650 | if (skb->protocol != htons(ETH_P_IPV6)) { | ||
651 | IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): protocol error, " | ||
652 | "ETH_P_IPV6: %d, skb protocol: %d\n", | ||
653 | htons(ETH_P_IPV6), skb->protocol); | ||
654 | goto tx_error; | ||
655 | } | ||
656 | |||
657 | rt = __ip_vs_get_out_rt_v6(cp); | ||
658 | if (!rt) | ||
659 | goto tx_error_icmp; | ||
660 | |||
661 | tdev = rt->u.dst.dev; | ||
662 | |||
663 | mtu = dst_mtu(&rt->u.dst) - sizeof(struct ipv6hdr); | ||
664 | /* TODO IPv6: do we need this check in IPv6? */ | ||
665 | if (mtu < 1280) { | ||
666 | dst_release(&rt->u.dst); | ||
667 | IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): mtu less than 1280\n"); | ||
668 | goto tx_error; | ||
669 | } | ||
670 | if (skb->dst) | ||
671 | skb->dst->ops->update_pmtu(skb->dst, mtu); | ||
672 | |||
673 | if (mtu < ntohs(old_iph->payload_len) + sizeof(struct ipv6hdr)) { | ||
674 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); | ||
675 | dst_release(&rt->u.dst); | ||
676 | IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): frag needed\n"); | ||
677 | goto tx_error; | ||
678 | } | ||
679 | |||
680 | /* | ||
681 | * Okay, now see if we can stuff it in the buffer as-is. | ||
682 | */ | ||
683 | max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr); | ||
684 | |||
685 | if (skb_headroom(skb) < max_headroom | ||
686 | || skb_cloned(skb) || skb_shared(skb)) { | ||
687 | struct sk_buff *new_skb = | ||
688 | skb_realloc_headroom(skb, max_headroom); | ||
689 | if (!new_skb) { | ||
690 | dst_release(&rt->u.dst); | ||
691 | kfree_skb(skb); | ||
692 | IP_VS_ERR_RL("ip_vs_tunnel_xmit_v6(): no memory\n"); | ||
693 | return NF_STOLEN; | ||
694 | } | ||
695 | kfree_skb(skb); | ||
696 | skb = new_skb; | ||
697 | old_iph = ipv6_hdr(skb); | ||
698 | } | ||
699 | |||
700 | skb->transport_header = old_transport_header; | ||
701 | |||
702 | skb_push(skb, sizeof(struct ipv6hdr)); | ||
703 | skb_reset_network_header(skb); | ||
704 | memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); | ||
705 | |||
706 | /* drop old route */ | ||
707 | dst_release(skb->dst); | ||
708 | skb->dst = &rt->u.dst; | ||
709 | |||
710 | /* | ||
711 | * Push down and install the IPIP header. | ||
712 | */ | ||
713 | iph = ipv6_hdr(skb); | ||
714 | iph->version = 6; | ||
715 | iph->nexthdr = IPPROTO_IPV6; | ||
716 | iph->payload_len = old_iph->payload_len + sizeof(old_iph); | ||
717 | iph->priority = old_iph->priority; | ||
718 | memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl)); | ||
719 | iph->daddr = rt->rt6i_dst.addr; | ||
720 | iph->saddr = cp->vaddr.in6; /* rt->rt6i_src.addr; */ | ||
721 | iph->hop_limit = old_iph->hop_limit; | ||
722 | |||
723 | /* Another hack: avoid icmp_send in ip_fragment */ | ||
724 | skb->local_df = 1; | ||
725 | |||
726 | ip6_local_out(skb); | ||
727 | |||
728 | LeaveFunction(10); | ||
729 | |||
730 | return NF_STOLEN; | ||
731 | |||
732 | tx_error_icmp: | ||
733 | dst_link_failure(skb); | ||
734 | tx_error: | ||
735 | kfree_skb(skb); | ||
736 | LeaveFunction(10); | ||
737 | return NF_STOLEN; | ||
738 | } | ||
739 | #endif | ||
740 | |||
426 | 741 | ||
427 | /* | 742 | /* |
428 | * Direct Routing transmitter | 743 | * Direct Routing transmitter |
@@ -467,7 +782,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
467 | /* Another hack: avoid icmp_send in ip_fragment */ | 782 | /* Another hack: avoid icmp_send in ip_fragment */ |
468 | skb->local_df = 1; | 783 | skb->local_df = 1; |
469 | 784 | ||
470 | IP_VS_XMIT(skb, rt); | 785 | IP_VS_XMIT(PF_INET, skb, rt); |
471 | 786 | ||
472 | LeaveFunction(10); | 787 | LeaveFunction(10); |
473 | return NF_STOLEN; | 788 | return NF_STOLEN; |
@@ -480,6 +795,60 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
480 | return NF_STOLEN; | 795 | return NF_STOLEN; |
481 | } | 796 | } |
482 | 797 | ||
798 | #ifdef CONFIG_IP_VS_IPV6 | ||
799 | int | ||
800 | ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | ||
801 | struct ip_vs_protocol *pp) | ||
802 | { | ||
803 | struct rt6_info *rt; /* Route to the other host */ | ||
804 | int mtu; | ||
805 | |||
806 | EnterFunction(10); | ||
807 | |||
808 | rt = __ip_vs_get_out_rt_v6(cp); | ||
809 | if (!rt) | ||
810 | goto tx_error_icmp; | ||
811 | |||
812 | /* MTU checking */ | ||
813 | mtu = dst_mtu(&rt->u.dst); | ||
814 | if (skb->len > mtu) { | ||
815 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); | ||
816 | dst_release(&rt->u.dst); | ||
817 | IP_VS_DBG_RL("ip_vs_dr_xmit_v6(): frag needed\n"); | ||
818 | goto tx_error; | ||
819 | } | ||
820 | |||
821 | /* | ||
822 | * Call ip_send_check because we are not sure it is called | ||
823 | * after ip_defrag. Is copy-on-write needed? | ||
824 | */ | ||
825 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
826 | if (unlikely(skb == NULL)) { | ||
827 | dst_release(&rt->u.dst); | ||
828 | return NF_STOLEN; | ||
829 | } | ||
830 | |||
831 | /* drop old route */ | ||
832 | dst_release(skb->dst); | ||
833 | skb->dst = &rt->u.dst; | ||
834 | |||
835 | /* Another hack: avoid icmp_send in ip_fragment */ | ||
836 | skb->local_df = 1; | ||
837 | |||
838 | IP_VS_XMIT(PF_INET6, skb, rt); | ||
839 | |||
840 | LeaveFunction(10); | ||
841 | return NF_STOLEN; | ||
842 | |||
843 | tx_error_icmp: | ||
844 | dst_link_failure(skb); | ||
845 | tx_error: | ||
846 | kfree_skb(skb); | ||
847 | LeaveFunction(10); | ||
848 | return NF_STOLEN; | ||
849 | } | ||
850 | #endif | ||
851 | |||
483 | 852 | ||
484 | /* | 853 | /* |
485 | * ICMP packet transmitter | 854 | * ICMP packet transmitter |
@@ -540,7 +909,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
540 | /* Another hack: avoid icmp_send in ip_fragment */ | 909 | /* Another hack: avoid icmp_send in ip_fragment */ |
541 | skb->local_df = 1; | 910 | skb->local_df = 1; |
542 | 911 | ||
543 | IP_VS_XMIT(skb, rt); | 912 | IP_VS_XMIT(PF_INET, skb, rt); |
544 | 913 | ||
545 | rc = NF_STOLEN; | 914 | rc = NF_STOLEN; |
546 | goto out; | 915 | goto out; |
@@ -557,3 +926,79 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
557 | ip_rt_put(rt); | 926 | ip_rt_put(rt); |
558 | goto tx_error; | 927 | goto tx_error; |
559 | } | 928 | } |
929 | |||
930 | #ifdef CONFIG_IP_VS_IPV6 | ||
931 | int | ||
932 | ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, | ||
933 | struct ip_vs_protocol *pp, int offset) | ||
934 | { | ||
935 | struct rt6_info *rt; /* Route to the other host */ | ||
936 | int mtu; | ||
937 | int rc; | ||
938 | |||
939 | EnterFunction(10); | ||
940 | |||
941 | /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be | ||
942 | forwarded directly here, because there is no need to | ||
943 | translate address/port back */ | ||
944 | if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { | ||
945 | if (cp->packet_xmit) | ||
946 | rc = cp->packet_xmit(skb, cp, pp); | ||
947 | else | ||
948 | rc = NF_ACCEPT; | ||
949 | /* do not touch skb anymore */ | ||
950 | atomic_inc(&cp->in_pkts); | ||
951 | goto out; | ||
952 | } | ||
953 | |||
954 | /* | ||
955 | * mangle and send the packet here (only for VS/NAT) | ||
956 | */ | ||
957 | |||
958 | rt = __ip_vs_get_out_rt_v6(cp); | ||
959 | if (!rt) | ||
960 | goto tx_error_icmp; | ||
961 | |||
962 | /* MTU checking */ | ||
963 | mtu = dst_mtu(&rt->u.dst); | ||
964 | if (skb->len > mtu) { | ||
965 | dst_release(&rt->u.dst); | ||
966 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); | ||
967 | IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n"); | ||
968 | goto tx_error; | ||
969 | } | ||
970 | |||
971 | /* copy-on-write the packet before mangling it */ | ||
972 | if (!skb_make_writable(skb, offset)) | ||
973 | goto tx_error_put; | ||
974 | |||
975 | if (skb_cow(skb, rt->u.dst.dev->hard_header_len)) | ||
976 | goto tx_error_put; | ||
977 | |||
978 | /* drop the old route when skb is not shared */ | ||
979 | dst_release(skb->dst); | ||
980 | skb->dst = &rt->u.dst; | ||
981 | |||
982 | ip_vs_nat_icmp_v6(skb, pp, cp, 0); | ||
983 | |||
984 | /* Another hack: avoid icmp_send in ip_fragment */ | ||
985 | skb->local_df = 1; | ||
986 | |||
987 | IP_VS_XMIT(PF_INET6, skb, rt); | ||
988 | |||
989 | rc = NF_STOLEN; | ||
990 | goto out; | ||
991 | |||
992 | tx_error_icmp: | ||
993 | dst_link_failure(skb); | ||
994 | tx_error: | ||
995 | dev_kfree_skb(skb); | ||
996 | rc = NF_STOLEN; | ||
997 | out: | ||
998 | LeaveFunction(10); | ||
999 | return rc; | ||
1000 | tx_error_put: | ||
1001 | dst_release(&rt->u.dst); | ||
1002 | goto tx_error; | ||
1003 | } | ||
1004 | #endif | ||