2 * IPVS An implementation of the IP virtual server support for the
3 * LINUX operating system. IPVS is now implemented as a module
4 * over the Netfilter framework. IPVS can be used to build a
5 * high-performance and highly available server based on a
8 * Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
9 * Peter Kese <peter.kese@ijs.si>
10 * Julian Anastasov <ja@ssi.bg>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
17 * The IPVS code for kernel 2.2 was done by Wensong Zhang and Peter Kese,
18 * with changes/fixes from Julian Anastasov, Lars Marowsky-Bree, Horms
22 * Paul `Rusty' Russell properly handle non-linear skbs
23 * Harald Welte don't use nfcache
27 #include <linux/module.h>
28 #include <linux/kernel.h>
30 #include <linux/tcp.h>
31 #include <linux/icmp.h>
36 #include <net/icmp.h> /* for icmp_send */
37 #include <net/route.h>
39 #include <linux/netfilter.h>
40 #include <linux/netfilter_ipv4.h>
42 #ifdef CONFIG_IP_VS_IPV6
44 #include <linux/netfilter_ipv6.h>
47 #include <net/ip_vs.h>
50 EXPORT_SYMBOL(register_ip_vs_scheduler
);
51 EXPORT_SYMBOL(unregister_ip_vs_scheduler
);
52 EXPORT_SYMBOL(ip_vs_skb_replace
);
53 EXPORT_SYMBOL(ip_vs_proto_name
);
54 EXPORT_SYMBOL(ip_vs_conn_new
);
55 EXPORT_SYMBOL(ip_vs_conn_in_get
);
56 EXPORT_SYMBOL(ip_vs_conn_out_get
);
57 #ifdef CONFIG_IP_VS_PROTO_TCP
58 EXPORT_SYMBOL(ip_vs_tcp_conn_listen
);
60 EXPORT_SYMBOL(ip_vs_conn_put
);
61 #ifdef CONFIG_IP_VS_DEBUG
62 EXPORT_SYMBOL(ip_vs_get_debug_level
);
66 /* ID used in ICMP lookups */
67 #define icmp_id(icmph) (((icmph)->un).echo.id)
68 #define icmpv6_id(icmph) (icmph->icmp6_dataun.u_echo.identifier)
70 const char *ip_vs_proto_name(unsigned proto
)
83 #ifdef CONFIG_IP_VS_IPV6
88 sprintf(buf
, "IP_%d", proto
);
93 void ip_vs_init_hash_table(struct list_head
*table
, int rows
)
96 INIT_LIST_HEAD(&table
[rows
]);
100 ip_vs_in_stats(struct ip_vs_conn
*cp
, struct sk_buff
*skb
)
102 struct ip_vs_dest
*dest
= cp
->dest
;
103 if (dest
&& (dest
->flags
& IP_VS_DEST_F_AVAILABLE
)) {
104 spin_lock(&dest
->stats
.lock
);
105 dest
->stats
.ustats
.inpkts
++;
106 dest
->stats
.ustats
.inbytes
+= skb
->len
;
107 spin_unlock(&dest
->stats
.lock
);
109 spin_lock(&dest
->svc
->stats
.lock
);
110 dest
->svc
->stats
.ustats
.inpkts
++;
111 dest
->svc
->stats
.ustats
.inbytes
+= skb
->len
;
112 spin_unlock(&dest
->svc
->stats
.lock
);
114 spin_lock(&ip_vs_stats
.lock
);
115 ip_vs_stats
.ustats
.inpkts
++;
116 ip_vs_stats
.ustats
.inbytes
+= skb
->len
;
117 spin_unlock(&ip_vs_stats
.lock
);
123 ip_vs_out_stats(struct ip_vs_conn
*cp
, struct sk_buff
*skb
)
125 struct ip_vs_dest
*dest
= cp
->dest
;
126 if (dest
&& (dest
->flags
& IP_VS_DEST_F_AVAILABLE
)) {
127 spin_lock(&dest
->stats
.lock
);
128 dest
->stats
.ustats
.outpkts
++;
129 dest
->stats
.ustats
.outbytes
+= skb
->len
;
130 spin_unlock(&dest
->stats
.lock
);
132 spin_lock(&dest
->svc
->stats
.lock
);
133 dest
->svc
->stats
.ustats
.outpkts
++;
134 dest
->svc
->stats
.ustats
.outbytes
+= skb
->len
;
135 spin_unlock(&dest
->svc
->stats
.lock
);
137 spin_lock(&ip_vs_stats
.lock
);
138 ip_vs_stats
.ustats
.outpkts
++;
139 ip_vs_stats
.ustats
.outbytes
+= skb
->len
;
140 spin_unlock(&ip_vs_stats
.lock
);
146 ip_vs_conn_stats(struct ip_vs_conn
*cp
, struct ip_vs_service
*svc
)
148 spin_lock(&cp
->dest
->stats
.lock
);
149 cp
->dest
->stats
.ustats
.conns
++;
150 spin_unlock(&cp
->dest
->stats
.lock
);
152 spin_lock(&svc
->stats
.lock
);
153 svc
->stats
.ustats
.conns
++;
154 spin_unlock(&svc
->stats
.lock
);
156 spin_lock(&ip_vs_stats
.lock
);
157 ip_vs_stats
.ustats
.conns
++;
158 spin_unlock(&ip_vs_stats
.lock
);
163 ip_vs_set_state(struct ip_vs_conn
*cp
, int direction
,
164 const struct sk_buff
*skb
,
165 struct ip_vs_protocol
*pp
)
167 if (unlikely(!pp
->state_transition
))
169 return pp
->state_transition(cp
, direction
, skb
, pp
);
174 * IPVS persistent scheduling function
175 * It creates a connection entry according to its template if exists,
176 * or selects a server and creates a connection entry plus a template.
177 * Locking: we are svc user (svc->refcnt), so we hold all dests too
178 * Protocols supported: TCP, UDP
180 static struct ip_vs_conn
*
181 ip_vs_sched_persist(struct ip_vs_service
*svc
,
182 const struct sk_buff
*skb
,
185 struct ip_vs_conn
*cp
= NULL
;
186 struct ip_vs_iphdr iph
;
187 struct ip_vs_dest
*dest
;
188 struct ip_vs_conn
*ct
;
189 __be16 dport
; /* destination port to forward */
190 union nf_inet_addr snet
; /* source network of the client,
193 ip_vs_fill_iphdr(svc
->af
, skb_network_header(skb
), &iph
);
195 /* Mask saddr with the netmask to adjust template granularity */
196 #ifdef CONFIG_IP_VS_IPV6
197 if (svc
->af
== AF_INET6
)
198 ipv6_addr_prefix(&snet
.in6
, &iph
.saddr
.in6
, svc
->netmask
);
201 snet
.ip
= iph
.saddr
.ip
& svc
->netmask
;
203 IP_VS_DBG_BUF(6, "p-schedule: src %s:%u dest %s:%u "
205 IP_VS_DBG_ADDR(svc
->af
, &iph
.saddr
), ntohs(ports
[0]),
206 IP_VS_DBG_ADDR(svc
->af
, &iph
.daddr
), ntohs(ports
[1]),
207 IP_VS_DBG_ADDR(svc
->af
, &snet
));
210 * As far as we know, FTP is a very complicated network protocol, and
211 * it uses control connection and data connections. For active FTP,
212 * FTP server initialize data connection to the client, its source port
213 * is often 20. For passive FTP, FTP server tells the clients the port
214 * that it passively listens to, and the client issues the data
215 * connection. In the tunneling or direct routing mode, the load
216 * balancer is on the client-to-server half of connection, the port
217 * number is unknown to the load balancer. So, a conn template like
218 * <caddr, 0, vaddr, 0, daddr, 0> is created for persistent FTP
219 * service, and a template like <caddr, 0, vaddr, vport, daddr, dport>
220 * is created for other persistent services.
222 if (ports
[1] == svc
->port
) {
223 /* Check if a template already exists */
224 if (svc
->port
!= FTPPORT
)
225 ct
= ip_vs_ct_in_get(svc
->af
, iph
.protocol
, &snet
, 0,
226 &iph
.daddr
, ports
[1]);
228 ct
= ip_vs_ct_in_get(svc
->af
, iph
.protocol
, &snet
, 0,
231 if (!ct
|| !ip_vs_check_template(ct
)) {
233 * No template found or the dest of the connection
234 * template is not available.
236 dest
= svc
->scheduler
->schedule(svc
, skb
);
238 IP_VS_DBG(1, "p-schedule: no dest found.\n");
243 * Create a template like <protocol,caddr,0,
244 * vaddr,vport,daddr,dport> for non-ftp service,
245 * and <protocol,caddr,0,vaddr,0,daddr,0>
248 if (svc
->port
!= FTPPORT
)
249 ct
= ip_vs_conn_new(svc
->af
, iph
.protocol
,
253 &dest
->addr
, dest
->port
,
254 IP_VS_CONN_F_TEMPLATE
,
257 ct
= ip_vs_conn_new(svc
->af
, iph
.protocol
,
261 IP_VS_CONN_F_TEMPLATE
,
266 ct
->timeout
= svc
->timeout
;
268 /* set destination with the found template */
274 * Note: persistent fwmark-based services and persistent
275 * port zero service are handled here.
276 * fwmark template: <IPPROTO_IP,caddr,0,fwmark,0,daddr,0>
277 * port zero template: <protocol,caddr,0,vaddr,0,daddr,0>
280 union nf_inet_addr fwmark
= {
281 .all
= { 0, 0, 0, htonl(svc
->fwmark
) }
284 ct
= ip_vs_ct_in_get(svc
->af
, IPPROTO_IP
, &snet
, 0,
287 ct
= ip_vs_ct_in_get(svc
->af
, iph
.protocol
, &snet
, 0,
290 if (!ct
|| !ip_vs_check_template(ct
)) {
292 * If it is not persistent port zero, return NULL,
293 * otherwise create a connection template.
298 dest
= svc
->scheduler
->schedule(svc
, skb
);
300 IP_VS_DBG(1, "p-schedule: no dest found.\n");
305 * Create a template according to the service
308 union nf_inet_addr fwmark
= {
309 .all
= { 0, 0, 0, htonl(svc
->fwmark
) }
312 ct
= ip_vs_conn_new(svc
->af
, IPPROTO_IP
,
316 IP_VS_CONN_F_TEMPLATE
,
319 ct
= ip_vs_conn_new(svc
->af
, iph
.protocol
,
323 IP_VS_CONN_F_TEMPLATE
,
328 ct
->timeout
= svc
->timeout
;
330 /* set destination with the found template */
337 * Create a new connection according to the template
339 cp
= ip_vs_conn_new(svc
->af
, iph
.protocol
,
340 &iph
.saddr
, ports
[0],
341 &iph
.daddr
, ports
[1],
353 ip_vs_control_add(cp
, ct
);
356 ip_vs_conn_stats(cp
, svc
);
362 * IPVS main scheduling function
363 * It selects a server according to the virtual service, and
364 * creates a connection entry.
365 * Protocols supported: TCP, UDP
368 ip_vs_schedule(struct ip_vs_service
*svc
, const struct sk_buff
*skb
)
370 struct ip_vs_conn
*cp
= NULL
;
371 struct ip_vs_iphdr iph
;
372 struct ip_vs_dest
*dest
;
373 __be16 _ports
[2], *pptr
;
375 ip_vs_fill_iphdr(svc
->af
, skb_network_header(skb
), &iph
);
376 pptr
= skb_header_pointer(skb
, iph
.len
, sizeof(_ports
), _ports
);
383 if (svc
->flags
& IP_VS_SVC_F_PERSISTENT
)
384 return ip_vs_sched_persist(svc
, skb
, pptr
);
387 * Non-persistent service
389 if (!svc
->fwmark
&& pptr
[1] != svc
->port
) {
391 IP_VS_ERR("Schedule: port zero only supported "
392 "in persistent services, "
393 "check your ipvs configuration\n");
397 dest
= svc
->scheduler
->schedule(svc
, skb
);
399 IP_VS_DBG(1, "Schedule: no dest found.\n");
404 * Create a connection entry.
406 cp
= ip_vs_conn_new(svc
->af
, iph
.protocol
,
409 &dest
->addr
, dest
->port
? dest
->port
: pptr
[1],
415 IP_VS_DBG_BUF(6, "Schedule fwd:%c c:%s:%u v:%s:%u "
416 "d:%s:%u conn->flags:%X conn->refcnt:%d\n",
418 IP_VS_DBG_ADDR(svc
->af
, &cp
->caddr
), ntohs(cp
->cport
),
419 IP_VS_DBG_ADDR(svc
->af
, &cp
->vaddr
), ntohs(cp
->vport
),
420 IP_VS_DBG_ADDR(svc
->af
, &cp
->daddr
), ntohs(cp
->dport
),
421 cp
->flags
, atomic_read(&cp
->refcnt
));
423 ip_vs_conn_stats(cp
, svc
);
429 * Pass or drop the packet.
430 * Called by ip_vs_in, when the virtual service is available but
431 * no destination is available for a new connection.
433 int ip_vs_leave(struct ip_vs_service
*svc
, struct sk_buff
*skb
,
434 struct ip_vs_protocol
*pp
)
436 __be16 _ports
[2], *pptr
;
437 struct ip_vs_iphdr iph
;
439 ip_vs_fill_iphdr(svc
->af
, skb_network_header(skb
), &iph
);
441 pptr
= skb_header_pointer(skb
, iph
.len
, sizeof(_ports
), _ports
);
443 ip_vs_service_put(svc
);
447 #ifdef CONFIG_IP_VS_IPV6
448 if (svc
->af
== AF_INET6
)
449 unicast
= ipv6_addr_type(&iph
.daddr
.in6
) & IPV6_ADDR_UNICAST
;
452 unicast
= (inet_addr_type(&init_net
, iph
.daddr
.ip
) == RTN_UNICAST
);
454 /* if it is fwmark-based service, the cache_bypass sysctl is up
455 and the destination is a non-local unicast, then create
456 a cache_bypass connection entry */
457 if (sysctl_ip_vs_cache_bypass
&& svc
->fwmark
&& unicast
) {
459 struct ip_vs_conn
*cp
;
460 union nf_inet_addr daddr
= { .all
= { 0, 0, 0, 0 } };
462 ip_vs_service_put(svc
);
464 /* create a new connection entry */
465 IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n");
466 cp
= ip_vs_conn_new(svc
->af
, iph
.protocol
,
476 ip_vs_in_stats(cp
, skb
);
479 cs
= ip_vs_set_state(cp
, IP_VS_DIR_INPUT
, skb
, pp
);
481 /* transmit the first SYN packet */
482 ret
= cp
->packet_xmit(skb
, cp
, pp
);
483 /* do not touch skb anymore */
485 atomic_inc(&cp
->in_pkts
);
491 * When the virtual ftp service is presented, packets destined
492 * for other services on the VIP may get here (except services
493 * listed in the ipvs table), pass the packets, because it is
494 * not ipvs job to decide to drop the packets.
496 if ((svc
->port
== FTPPORT
) && (pptr
[1] != FTPPORT
)) {
497 ip_vs_service_put(svc
);
501 ip_vs_service_put(svc
);
504 * Notify the client that the destination is unreachable, and
505 * release the socket buffer.
506 * Since it is in IP layer, the TCP socket is not actually
507 * created, the TCP RST packet cannot be sent, instead that
508 * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ
510 #ifdef CONFIG_IP_VS_IPV6
511 if (svc
->af
== AF_INET6
)
512 icmpv6_send(skb
, ICMPV6_DEST_UNREACH
, ICMPV6_PORT_UNREACH
, 0,
516 icmp_send(skb
, ICMP_DEST_UNREACH
, ICMP_PORT_UNREACH
, 0);
523 * It is hooked before NF_IP_PRI_NAT_SRC at the NF_INET_POST_ROUTING
524 * chain, and is used for VS/NAT.
525 * It detects packets for VS/NAT connections and sends the packets
526 * immediately. This can avoid that iptable_nat mangles the packets
529 static unsigned int ip_vs_post_routing(unsigned int hooknum
,
531 const struct net_device
*in
,
532 const struct net_device
*out
,
533 int (*okfn
)(struct sk_buff
*))
535 if (!skb
->ipvs_property
)
537 /* The packet was sent from IPVS, exit this chain */
541 __sum16
ip_vs_checksum_complete(struct sk_buff
*skb
, int offset
)
543 return csum_fold(skb_checksum(skb
, offset
, skb
->len
- offset
, 0));
546 static inline int ip_vs_gather_frags(struct sk_buff
*skb
, u_int32_t user
)
548 int err
= ip_defrag(skb
, user
);
551 ip_send_check(ip_hdr(skb
));
556 #ifdef CONFIG_IP_VS_IPV6
557 static inline int ip_vs_gather_frags_v6(struct sk_buff
*skb
, u_int32_t user
)
559 /* TODO IPv6: Find out what to do here for IPv6 */
565 * Packet has been made sufficiently writable in caller
566 * - inout: 1=in->out, 0=out->in
568 void ip_vs_nat_icmp(struct sk_buff
*skb
, struct ip_vs_protocol
*pp
,
569 struct ip_vs_conn
*cp
, int inout
)
571 struct iphdr
*iph
= ip_hdr(skb
);
572 unsigned int icmp_offset
= iph
->ihl
*4;
573 struct icmphdr
*icmph
= (struct icmphdr
*)(skb_network_header(skb
) +
575 struct iphdr
*ciph
= (struct iphdr
*)(icmph
+ 1);
578 iph
->saddr
= cp
->vaddr
.ip
;
580 ciph
->daddr
= cp
->vaddr
.ip
;
583 iph
->daddr
= cp
->daddr
.ip
;
585 ciph
->saddr
= cp
->daddr
.ip
;
589 /* the TCP/UDP port */
590 if (IPPROTO_TCP
== ciph
->protocol
|| IPPROTO_UDP
== ciph
->protocol
) {
591 __be16
*ports
= (void *)ciph
+ ciph
->ihl
*4;
594 ports
[1] = cp
->vport
;
596 ports
[0] = cp
->dport
;
599 /* And finally the ICMP checksum */
601 icmph
->checksum
= ip_vs_checksum_complete(skb
, icmp_offset
);
602 skb
->ip_summed
= CHECKSUM_UNNECESSARY
;
605 IP_VS_DBG_PKT(11, pp
, skb
, (void *)ciph
- (void *)iph
,
606 "Forwarding altered outgoing ICMP");
608 IP_VS_DBG_PKT(11, pp
, skb
, (void *)ciph
- (void *)iph
,
609 "Forwarding altered incoming ICMP");
612 #ifdef CONFIG_IP_VS_IPV6
613 void ip_vs_nat_icmp_v6(struct sk_buff
*skb
, struct ip_vs_protocol
*pp
,
614 struct ip_vs_conn
*cp
, int inout
)
616 struct ipv6hdr
*iph
= ipv6_hdr(skb
);
617 unsigned int icmp_offset
= sizeof(struct ipv6hdr
);
618 struct icmp6hdr
*icmph
= (struct icmp6hdr
*)(skb_network_header(skb
) +
620 struct ipv6hdr
*ciph
= (struct ipv6hdr
*)(icmph
+ 1);
623 iph
->saddr
= cp
->vaddr
.in6
;
624 ciph
->daddr
= cp
->vaddr
.in6
;
626 iph
->daddr
= cp
->daddr
.in6
;
627 ciph
->saddr
= cp
->daddr
.in6
;
630 /* the TCP/UDP port */
631 if (IPPROTO_TCP
== ciph
->nexthdr
|| IPPROTO_UDP
== ciph
->nexthdr
) {
632 __be16
*ports
= (void *)ciph
+ sizeof(struct ipv6hdr
);
635 ports
[1] = cp
->vport
;
637 ports
[0] = cp
->dport
;
640 /* And finally the ICMP checksum */
641 icmph
->icmp6_cksum
= 0;
642 /* TODO IPv6: is this correct for ICMPv6? */
643 ip_vs_checksum_complete(skb
, icmp_offset
);
644 skb
->ip_summed
= CHECKSUM_UNNECESSARY
;
647 IP_VS_DBG_PKT(11, pp
, skb
, (void *)ciph
- (void *)iph
,
648 "Forwarding altered outgoing ICMPv6");
650 IP_VS_DBG_PKT(11, pp
, skb
, (void *)ciph
- (void *)iph
,
651 "Forwarding altered incoming ICMPv6");
655 /* Handle relevant response ICMP messages - forward to the right
656 * destination host. Used for NAT and local client.
658 static int handle_response_icmp(int af
, struct sk_buff
*skb
,
659 union nf_inet_addr
*snet
,
660 __u8 protocol
, struct ip_vs_conn
*cp
,
661 struct ip_vs_protocol
*pp
,
662 unsigned int offset
, unsigned int ihl
)
664 unsigned int verdict
= NF_DROP
;
666 if (IP_VS_FWD_METHOD(cp
) != 0) {
667 IP_VS_ERR("shouldn't reach here, because the box is on the "
668 "half connection in the tun/dr module.\n");
671 /* Ensure the checksum is correct */
672 if (!skb_csum_unnecessary(skb
) && ip_vs_checksum_complete(skb
, ihl
)) {
673 /* Failed checksum! */
674 IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
675 IP_VS_DBG_ADDR(af
, snet
));
679 if (IPPROTO_TCP
== protocol
|| IPPROTO_UDP
== protocol
)
680 offset
+= 2 * sizeof(__u16
);
681 if (!skb_make_writable(skb
, offset
))
684 #ifdef CONFIG_IP_VS_IPV6
686 ip_vs_nat_icmp_v6(skb
, pp
, cp
, 1);
689 ip_vs_nat_icmp(skb
, pp
, cp
, 1);
691 /* do the statistics and put it back */
692 ip_vs_out_stats(cp
, skb
);
694 skb
->ipvs_property
= 1;
698 __ip_vs_conn_put(cp
);
704 * Handle ICMP messages in the inside-to-outside direction (outgoing).
705 * Find any that might be relevant, check against existing connections.
706 * Currently handles error types - unreachable, quench, ttl exceeded.
708 static int ip_vs_out_icmp(struct sk_buff
*skb
, int *related
)
711 struct icmphdr _icmph
, *ic
;
712 struct iphdr _ciph
, *cih
; /* The ip header contained within the ICMP */
713 struct ip_vs_iphdr ciph
;
714 struct ip_vs_conn
*cp
;
715 struct ip_vs_protocol
*pp
;
716 unsigned int offset
, ihl
;
717 union nf_inet_addr snet
;
721 /* reassemble IP fragments */
722 if (ip_hdr(skb
)->frag_off
& htons(IP_MF
| IP_OFFSET
)) {
723 if (ip_vs_gather_frags(skb
, IP_DEFRAG_VS_OUT
))
728 offset
= ihl
= iph
->ihl
* 4;
729 ic
= skb_header_pointer(skb
, offset
, sizeof(_icmph
), &_icmph
);
733 IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
734 ic
->type
, ntohs(icmp_id(ic
)),
735 &iph
->saddr
, &iph
->daddr
);
738 * Work through seeing if this is for us.
739 * These checks are supposed to be in an order that means easy
740 * things are checked first to speed up processing.... however
741 * this means that some packets will manage to get a long way
742 * down this stack and then be rejected, but that's life.
744 if ((ic
->type
!= ICMP_DEST_UNREACH
) &&
745 (ic
->type
!= ICMP_SOURCE_QUENCH
) &&
746 (ic
->type
!= ICMP_TIME_EXCEEDED
)) {
751 /* Now find the contained IP header */
752 offset
+= sizeof(_icmph
);
753 cih
= skb_header_pointer(skb
, offset
, sizeof(_ciph
), &_ciph
);
755 return NF_ACCEPT
; /* The packet looks wrong, ignore */
757 pp
= ip_vs_proto_get(cih
->protocol
);
761 /* Is the embedded protocol header present? */
762 if (unlikely(cih
->frag_off
& htons(IP_OFFSET
) &&
766 IP_VS_DBG_PKT(11, pp
, skb
, offset
, "Checking outgoing ICMP for");
768 offset
+= cih
->ihl
* 4;
770 ip_vs_fill_iphdr(AF_INET
, cih
, &ciph
);
771 /* The embedded headers contain source and dest in reverse order */
772 cp
= pp
->conn_out_get(AF_INET
, skb
, pp
, &ciph
, offset
, 1);
776 snet
.ip
= iph
->saddr
;
777 return handle_response_icmp(AF_INET
, skb
, &snet
, cih
->protocol
, cp
,
781 #ifdef CONFIG_IP_VS_IPV6
782 static int ip_vs_out_icmp_v6(struct sk_buff
*skb
, int *related
)
785 struct icmp6hdr _icmph
, *ic
;
786 struct ipv6hdr _ciph
, *cih
; /* The ip header contained
788 struct ip_vs_iphdr ciph
;
789 struct ip_vs_conn
*cp
;
790 struct ip_vs_protocol
*pp
;
792 union nf_inet_addr snet
;
796 /* reassemble IP fragments */
797 if (ipv6_hdr(skb
)->nexthdr
== IPPROTO_FRAGMENT
) {
798 if (ip_vs_gather_frags_v6(skb
, IP_DEFRAG_VS_OUT
))
803 offset
= sizeof(struct ipv6hdr
);
804 ic
= skb_header_pointer(skb
, offset
, sizeof(_icmph
), &_icmph
);
808 IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) %pI6->%pI6\n",
809 ic
->icmp6_type
, ntohs(icmpv6_id(ic
)),
810 &iph
->saddr
, &iph
->daddr
);
813 * Work through seeing if this is for us.
814 * These checks are supposed to be in an order that means easy
815 * things are checked first to speed up processing.... however
816 * this means that some packets will manage to get a long way
817 * down this stack and then be rejected, but that's life.
819 if ((ic
->icmp6_type
!= ICMPV6_DEST_UNREACH
) &&
820 (ic
->icmp6_type
!= ICMPV6_PKT_TOOBIG
) &&
821 (ic
->icmp6_type
!= ICMPV6_TIME_EXCEED
)) {
826 /* Now find the contained IP header */
827 offset
+= sizeof(_icmph
);
828 cih
= skb_header_pointer(skb
, offset
, sizeof(_ciph
), &_ciph
);
830 return NF_ACCEPT
; /* The packet looks wrong, ignore */
832 pp
= ip_vs_proto_get(cih
->nexthdr
);
836 /* Is the embedded protocol header present? */
837 /* TODO: we don't support fragmentation at the moment anyways */
838 if (unlikely(cih
->nexthdr
== IPPROTO_FRAGMENT
&& pp
->dont_defrag
))
841 IP_VS_DBG_PKT(11, pp
, skb
, offset
, "Checking outgoing ICMPv6 for");
843 offset
+= sizeof(struct ipv6hdr
);
845 ip_vs_fill_iphdr(AF_INET6
, cih
, &ciph
);
846 /* The embedded headers contain source and dest in reverse order */
847 cp
= pp
->conn_out_get(AF_INET6
, skb
, pp
, &ciph
, offset
, 1);
851 ipv6_addr_copy(&snet
.in6
, &iph
->saddr
);
852 return handle_response_icmp(AF_INET6
, skb
, &snet
, cih
->nexthdr
, cp
,
853 pp
, offset
, sizeof(struct ipv6hdr
));
857 static inline int is_tcp_reset(const struct sk_buff
*skb
, int nh_len
)
859 struct tcphdr _tcph
, *th
;
861 th
= skb_header_pointer(skb
, nh_len
, sizeof(_tcph
), &_tcph
);
867 /* Handle response packets: rewrite addresses and send away...
868 * Used for NAT and local client.
871 handle_response(int af
, struct sk_buff
*skb
, struct ip_vs_protocol
*pp
,
872 struct ip_vs_conn
*cp
, int ihl
)
874 IP_VS_DBG_PKT(11, pp
, skb
, 0, "Outgoing packet");
876 if (!skb_make_writable(skb
, ihl
))
879 /* mangle the packet */
880 if (pp
->snat_handler
&& !pp
->snat_handler(skb
, pp
, cp
))
883 #ifdef CONFIG_IP_VS_IPV6
885 ipv6_hdr(skb
)->saddr
= cp
->vaddr
.in6
;
889 ip_hdr(skb
)->saddr
= cp
->vaddr
.ip
;
890 ip_send_check(ip_hdr(skb
));
893 /* For policy routing, packets originating from this
894 * machine itself may be routed differently to packets
895 * passing through. We want this packet to be routed as
896 * if it came from this machine itself. So re-compute
897 * the routing information.
899 #ifdef CONFIG_IP_VS_IPV6
900 if (af
== AF_INET6
) {
901 if (ip6_route_me_harder(skb
) != 0)
905 if (ip_route_me_harder(skb
, RTN_LOCAL
) != 0)
908 IP_VS_DBG_PKT(10, pp
, skb
, 0, "After SNAT");
910 ip_vs_out_stats(cp
, skb
);
911 ip_vs_set_state(cp
, IP_VS_DIR_OUTPUT
, skb
, pp
);
914 skb
->ipvs_property
= 1;
926 * It is hooked at the NF_INET_FORWARD chain, used only for VS/NAT.
927 * Check if outgoing packet belongs to the established ip_vs_conn.
930 ip_vs_out(unsigned int hooknum
, struct sk_buff
*skb
,
931 const struct net_device
*in
, const struct net_device
*out
,
932 int (*okfn
)(struct sk_buff
*))
934 struct ip_vs_iphdr iph
;
935 struct ip_vs_protocol
*pp
;
936 struct ip_vs_conn
*cp
;
941 af
= (skb
->protocol
== htons(ETH_P_IP
)) ? AF_INET
: AF_INET6
;
943 if (skb
->ipvs_property
)
946 ip_vs_fill_iphdr(af
, skb_network_header(skb
), &iph
);
947 #ifdef CONFIG_IP_VS_IPV6
948 if (af
== AF_INET6
) {
949 if (unlikely(iph
.protocol
== IPPROTO_ICMPV6
)) {
950 int related
, verdict
= ip_vs_out_icmp_v6(skb
, &related
);
954 ip_vs_fill_iphdr(af
, skb_network_header(skb
), &iph
);
958 if (unlikely(iph
.protocol
== IPPROTO_ICMP
)) {
959 int related
, verdict
= ip_vs_out_icmp(skb
, &related
);
963 ip_vs_fill_iphdr(af
, skb_network_header(skb
), &iph
);
966 pp
= ip_vs_proto_get(iph
.protocol
);
970 /* reassemble IP fragments */
971 #ifdef CONFIG_IP_VS_IPV6
972 if (af
== AF_INET6
) {
973 if (unlikely(iph
.protocol
== IPPROTO_ICMPV6
)) {
974 int related
, verdict
= ip_vs_out_icmp_v6(skb
, &related
);
979 ip_vs_fill_iphdr(af
, skb_network_header(skb
), &iph
);
983 if (unlikely(ip_hdr(skb
)->frag_off
& htons(IP_MF
|IP_OFFSET
) &&
985 if (ip_vs_gather_frags(skb
, IP_DEFRAG_VS_OUT
))
988 ip_vs_fill_iphdr(af
, skb_network_header(skb
), &iph
);
992 * Check if the packet belongs to an existing entry
994 cp
= pp
->conn_out_get(af
, skb
, pp
, &iph
, iph
.len
, 0);
997 if (sysctl_ip_vs_nat_icmp_send
&&
998 (pp
->protocol
== IPPROTO_TCP
||
999 pp
->protocol
== IPPROTO_UDP
)) {
1000 __be16 _ports
[2], *pptr
;
1002 pptr
= skb_header_pointer(skb
, iph
.len
,
1003 sizeof(_ports
), _ports
);
1005 return NF_ACCEPT
; /* Not for me */
1006 if (ip_vs_lookup_real_service(af
, iph
.protocol
,
1010 * Notify the real server: there is no
1011 * existing entry if it is not RST
1012 * packet or not TCP packet.
1014 if (iph
.protocol
!= IPPROTO_TCP
1015 || !is_tcp_reset(skb
, iph
.len
)) {
1016 #ifdef CONFIG_IP_VS_IPV6
1019 ICMPV6_DEST_UNREACH
,
1020 ICMPV6_PORT_UNREACH
,
1026 ICMP_PORT_UNREACH
, 0);
1031 IP_VS_DBG_PKT(12, pp
, skb
, 0,
1032 "packet continues traversal as normal");
1036 return handle_response(af
, skb
, pp
, cp
, iph
.len
);
1041 * Handle ICMP messages in the outside-to-inside direction (incoming).
1042 * Find any that might be relevant, check against existing connections,
1043 * forward to the right destination host if relevant.
1044 * Currently handles error types - unreachable, quench, ttl exceeded.
1047 ip_vs_in_icmp(struct sk_buff
*skb
, int *related
, unsigned int hooknum
)
1050 struct icmphdr _icmph
, *ic
;
1051 struct iphdr _ciph
, *cih
; /* The ip header contained within the ICMP */
1052 struct ip_vs_iphdr ciph
;
1053 struct ip_vs_conn
*cp
;
1054 struct ip_vs_protocol
*pp
;
1055 unsigned int offset
, ihl
, verdict
;
1056 union nf_inet_addr snet
;
1060 /* reassemble IP fragments */
1061 if (ip_hdr(skb
)->frag_off
& htons(IP_MF
| IP_OFFSET
)) {
1062 if (ip_vs_gather_frags(skb
, hooknum
== NF_INET_LOCAL_IN
?
1063 IP_DEFRAG_VS_IN
: IP_DEFRAG_VS_FWD
))
1068 offset
= ihl
= iph
->ihl
* 4;
1069 ic
= skb_header_pointer(skb
, offset
, sizeof(_icmph
), &_icmph
);
1073 IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
1074 ic
->type
, ntohs(icmp_id(ic
)),
1075 &iph
->saddr
, &iph
->daddr
);
1078 * Work through seeing if this is for us.
1079 * These checks are supposed to be in an order that means easy
1080 * things are checked first to speed up processing.... however
1081 * this means that some packets will manage to get a long way
1082 * down this stack and then be rejected, but that's life.
1084 if ((ic
->type
!= ICMP_DEST_UNREACH
) &&
1085 (ic
->type
!= ICMP_SOURCE_QUENCH
) &&
1086 (ic
->type
!= ICMP_TIME_EXCEEDED
)) {
1091 /* Now find the contained IP header */
1092 offset
+= sizeof(_icmph
);
1093 cih
= skb_header_pointer(skb
, offset
, sizeof(_ciph
), &_ciph
);
1095 return NF_ACCEPT
; /* The packet looks wrong, ignore */
1097 pp
= ip_vs_proto_get(cih
->protocol
);
1101 /* Is the embedded protocol header present? */
1102 if (unlikely(cih
->frag_off
& htons(IP_OFFSET
) &&
1106 IP_VS_DBG_PKT(11, pp
, skb
, offset
, "Checking incoming ICMP for");
1108 offset
+= cih
->ihl
* 4;
1110 ip_vs_fill_iphdr(AF_INET
, cih
, &ciph
);
1111 /* The embedded headers contain source and dest in reverse order */
1112 cp
= pp
->conn_in_get(AF_INET
, skb
, pp
, &ciph
, offset
, 1);
1114 /* The packet could also belong to a local client */
1115 cp
= pp
->conn_out_get(AF_INET
, skb
, pp
, &ciph
, offset
, 1);
1117 snet
.ip
= iph
->saddr
;
1118 return handle_response_icmp(AF_INET
, skb
, &snet
,
1119 cih
->protocol
, cp
, pp
,
1127 /* Ensure the checksum is correct */
1128 if (!skb_csum_unnecessary(skb
) && ip_vs_checksum_complete(skb
, ihl
)) {
1129 /* Failed checksum! */
1130 IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
1135 /* do the statistics and put it back */
1136 ip_vs_in_stats(cp
, skb
);
1137 if (IPPROTO_TCP
== cih
->protocol
|| IPPROTO_UDP
== cih
->protocol
)
1138 offset
+= 2 * sizeof(__u16
);
1139 verdict
= ip_vs_icmp_xmit(skb
, cp
, pp
, offset
);
1140 /* do not touch skb anymore */
1143 __ip_vs_conn_put(cp
);
1148 #ifdef CONFIG_IP_VS_IPV6
1150 ip_vs_in_icmp_v6(struct sk_buff
*skb
, int *related
, unsigned int hooknum
)
1152 struct ipv6hdr
*iph
;
1153 struct icmp6hdr _icmph
, *ic
;
1154 struct ipv6hdr _ciph
, *cih
; /* The ip header contained
1156 struct ip_vs_iphdr ciph
;
1157 struct ip_vs_conn
*cp
;
1158 struct ip_vs_protocol
*pp
;
1159 unsigned int offset
, verdict
;
1160 union nf_inet_addr snet
;
1164 /* reassemble IP fragments */
1165 if (ipv6_hdr(skb
)->nexthdr
== IPPROTO_FRAGMENT
) {
1166 if (ip_vs_gather_frags_v6(skb
, hooknum
== NF_INET_LOCAL_IN
?
1172 iph
= ipv6_hdr(skb
);
1173 offset
= sizeof(struct ipv6hdr
);
1174 ic
= skb_header_pointer(skb
, offset
, sizeof(_icmph
), &_icmph
);
1178 IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) %pI6->%pI6\n",
1179 ic
->icmp6_type
, ntohs(icmpv6_id(ic
)),
1180 &iph
->saddr
, &iph
->daddr
);
1183 * Work through seeing if this is for us.
1184 * These checks are supposed to be in an order that means easy
1185 * things are checked first to speed up processing.... however
1186 * this means that some packets will manage to get a long way
1187 * down this stack and then be rejected, but that's life.
1189 if ((ic
->icmp6_type
!= ICMPV6_DEST_UNREACH
) &&
1190 (ic
->icmp6_type
!= ICMPV6_PKT_TOOBIG
) &&
1191 (ic
->icmp6_type
!= ICMPV6_TIME_EXCEED
)) {
1196 /* Now find the contained IP header */
1197 offset
+= sizeof(_icmph
);
1198 cih
= skb_header_pointer(skb
, offset
, sizeof(_ciph
), &_ciph
);
1200 return NF_ACCEPT
; /* The packet looks wrong, ignore */
1202 pp
= ip_vs_proto_get(cih
->nexthdr
);
1206 /* Is the embedded protocol header present? */
1207 /* TODO: we don't support fragmentation at the moment anyways */
1208 if (unlikely(cih
->nexthdr
== IPPROTO_FRAGMENT
&& pp
->dont_defrag
))
1211 IP_VS_DBG_PKT(11, pp
, skb
, offset
, "Checking incoming ICMPv6 for");
1213 offset
+= sizeof(struct ipv6hdr
);
1215 ip_vs_fill_iphdr(AF_INET6
, cih
, &ciph
);
1216 /* The embedded headers contain source and dest in reverse order */
1217 cp
= pp
->conn_in_get(AF_INET6
, skb
, pp
, &ciph
, offset
, 1);
1219 /* The packet could also belong to a local client */
1220 cp
= pp
->conn_out_get(AF_INET6
, skb
, pp
, &ciph
, offset
, 1);
1222 ipv6_addr_copy(&snet
.in6
, &iph
->saddr
);
1223 return handle_response_icmp(AF_INET6
, skb
, &snet
,
1226 sizeof(struct ipv6hdr
));
1233 /* do the statistics and put it back */
1234 ip_vs_in_stats(cp
, skb
);
1235 if (IPPROTO_TCP
== cih
->nexthdr
|| IPPROTO_UDP
== cih
->nexthdr
)
1236 offset
+= 2 * sizeof(__u16
);
1237 verdict
= ip_vs_icmp_xmit_v6(skb
, cp
, pp
, offset
);
1238 /* do not touch skb anymore */
1240 __ip_vs_conn_put(cp
);
1248 * Check if it's for virtual services, look it up,
1249 * and send it on its way...
1252 ip_vs_in(unsigned int hooknum
, struct sk_buff
*skb
,
1253 const struct net_device
*in
, const struct net_device
*out
,
1254 int (*okfn
)(struct sk_buff
*))
1256 struct ip_vs_iphdr iph
;
1257 struct ip_vs_protocol
*pp
;
1258 struct ip_vs_conn
*cp
;
1259 int ret
, restart
, af
;
1261 af
= (skb
->protocol
== htons(ETH_P_IP
)) ? AF_INET
: AF_INET6
;
1263 ip_vs_fill_iphdr(af
, skb_network_header(skb
), &iph
);
1266 * Big tappo: only PACKET_HOST, including loopback for local client
1267 * Don't handle local packets on IPv6 for now
1269 if (unlikely(skb
->pkt_type
!= PACKET_HOST
)) {
1270 IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n",
1273 IP_VS_DBG_ADDR(af
, &iph
.daddr
));
1277 if (unlikely(iph
.protocol
== IPPROTO_ICMP
)) {
1278 int related
, verdict
= ip_vs_in_icmp(skb
, &related
, hooknum
);
1282 ip_vs_fill_iphdr(af
, skb_network_header(skb
), &iph
);
1285 /* Protocol supported? */
1286 pp
= ip_vs_proto_get(iph
.protocol
);
1291 * Check if the packet belongs to an existing connection entry
1293 cp
= pp
->conn_in_get(af
, skb
, pp
, &iph
, iph
.len
, 0);
1295 if (unlikely(!cp
)) {
1298 /* For local client packets, it could be a response */
1299 cp
= pp
->conn_out_get(af
, skb
, pp
, &iph
, iph
.len
, 0);
1301 return handle_response(af
, skb
, pp
, cp
, iph
.len
);
1303 if (!pp
->conn_schedule(af
, skb
, pp
, &v
, &cp
))
1307 if (unlikely(!cp
)) {
1308 /* sorry, all this trouble for a no-hit :) */
1309 IP_VS_DBG_PKT(12, pp
, skb
, 0,
1310 "packet continues traversal as normal");
1314 IP_VS_DBG_PKT(11, pp
, skb
, 0, "Incoming packet");
1316 /* Check the server status */
1317 if (cp
->dest
&& !(cp
->dest
->flags
& IP_VS_DEST_F_AVAILABLE
)) {
1318 /* the destination server is not available */
1320 if (sysctl_ip_vs_expire_nodest_conn
) {
1321 /* try to expire the connection immediately */
1322 ip_vs_conn_expire_now(cp
);
1324 /* don't restart its timer, and silently
1326 __ip_vs_conn_put(cp
);
1330 ip_vs_in_stats(cp
, skb
);
1331 restart
= ip_vs_set_state(cp
, IP_VS_DIR_INPUT
, skb
, pp
);
1332 if (cp
->packet_xmit
)
1333 ret
= cp
->packet_xmit(skb
, cp
, pp
);
1334 /* do not touch skb anymore */
1336 IP_VS_DBG_RL("warning: packet_xmit is null");
1340 /* Increase its packet counter and check if it is needed
1341 * to be synchronized
1343 * Sync connection if it is about to close to
1344 * encorage the standby servers to update the connections timeout
1346 atomic_inc(&cp
->in_pkts
);
1347 if (af
== AF_INET
&&
1348 (ip_vs_sync_state
& IP_VS_STATE_MASTER
) &&
1349 (((cp
->protocol
!= IPPROTO_TCP
||
1350 cp
->state
== IP_VS_TCP_S_ESTABLISHED
) &&
1351 (atomic_read(&cp
->in_pkts
) % sysctl_ip_vs_sync_threshold
[1]
1352 == sysctl_ip_vs_sync_threshold
[0])) ||
1353 ((cp
->protocol
== IPPROTO_TCP
) && (cp
->old_state
!= cp
->state
) &&
1354 ((cp
->state
== IP_VS_TCP_S_FIN_WAIT
) ||
1355 (cp
->state
== IP_VS_TCP_S_CLOSE_WAIT
) ||
1356 (cp
->state
== IP_VS_TCP_S_TIME_WAIT
)))))
1357 ip_vs_sync_conn(cp
);
1358 cp
->old_state
= cp
->state
;
1366 * It is hooked at the NF_INET_FORWARD chain, in order to catch ICMP
1367 * related packets destined for 0.0.0.0/0.
1368 * When fwmark-based virtual service is used, such as transparent
1369 * cache cluster, TCP packets can be marked and routed to ip_vs_in,
1370 * but ICMP destined for 0.0.0.0/0 cannot not be easily marked and
1371 * sent to ip_vs_in_icmp. So, catch them at the NF_INET_FORWARD chain
1372 * and send them to ip_vs_in_icmp.
1375 ip_vs_forward_icmp(unsigned int hooknum
, struct sk_buff
*skb
,
1376 const struct net_device
*in
, const struct net_device
*out
,
1377 int (*okfn
)(struct sk_buff
*))
1381 if (ip_hdr(skb
)->protocol
!= IPPROTO_ICMP
)
1384 return ip_vs_in_icmp(skb
, &r
, hooknum
);
1387 #ifdef CONFIG_IP_VS_IPV6
1389 ip_vs_forward_icmp_v6(unsigned int hooknum
, struct sk_buff
*skb
,
1390 const struct net_device
*in
, const struct net_device
*out
,
1391 int (*okfn
)(struct sk_buff
*))
1395 if (ipv6_hdr(skb
)->nexthdr
!= IPPROTO_ICMPV6
)
1398 return ip_vs_in_icmp_v6(skb
, &r
, hooknum
);
1403 static struct nf_hook_ops ip_vs_ops
[] __read_mostly
= {
1404 /* After packet filtering, forward packet through VS/DR, VS/TUN,
1405 * or VS/NAT(change destination), so that filtering rules can be
1406 * applied to IPVS. */
1409 .owner
= THIS_MODULE
,
1411 .hooknum
= NF_INET_LOCAL_IN
,
1414 /* After packet filtering, change source only for VS/NAT */
1417 .owner
= THIS_MODULE
,
1419 .hooknum
= NF_INET_FORWARD
,
1422 /* After packet filtering (but before ip_vs_out_icmp), catch icmp
1423 * destined for 0.0.0.0/0, which is for incoming IPVS connections */
1425 .hook
= ip_vs_forward_icmp
,
1426 .owner
= THIS_MODULE
,
1428 .hooknum
= NF_INET_FORWARD
,
1431 /* Before the netfilter connection tracking, exit from POST_ROUTING */
1433 .hook
= ip_vs_post_routing
,
1434 .owner
= THIS_MODULE
,
1436 .hooknum
= NF_INET_POST_ROUTING
,
1437 .priority
= NF_IP_PRI_NAT_SRC
-1,
1439 #ifdef CONFIG_IP_VS_IPV6
1440 /* After packet filtering, forward packet through VS/DR, VS/TUN,
1441 * or VS/NAT(change destination), so that filtering rules can be
1442 * applied to IPVS. */
1445 .owner
= THIS_MODULE
,
1447 .hooknum
= NF_INET_LOCAL_IN
,
1450 /* After packet filtering, change source only for VS/NAT */
1453 .owner
= THIS_MODULE
,
1455 .hooknum
= NF_INET_FORWARD
,
1458 /* After packet filtering (but before ip_vs_out_icmp), catch icmp
1459 * destined for 0.0.0.0/0, which is for incoming IPVS connections */
1461 .hook
= ip_vs_forward_icmp_v6
,
1462 .owner
= THIS_MODULE
,
1464 .hooknum
= NF_INET_FORWARD
,
1467 /* Before the netfilter connection tracking, exit from POST_ROUTING */
1469 .hook
= ip_vs_post_routing
,
1470 .owner
= THIS_MODULE
,
1472 .hooknum
= NF_INET_POST_ROUTING
,
1473 .priority
= NF_IP6_PRI_NAT_SRC
-1,
1480 * Initialize IP Virtual Server
1482 static int __init
ip_vs_init(void)
1486 ip_vs_estimator_init();
1488 ret
= ip_vs_control_init();
1490 IP_VS_ERR("can't setup control.\n");
1491 goto cleanup_estimator
;
1494 ip_vs_protocol_init();
1496 ret
= ip_vs_app_init();
1498 IP_VS_ERR("can't setup application helper.\n");
1499 goto cleanup_protocol
;
1502 ret
= ip_vs_conn_init();
1504 IP_VS_ERR("can't setup connection table.\n");
1508 ret
= nf_register_hooks(ip_vs_ops
, ARRAY_SIZE(ip_vs_ops
));
1510 IP_VS_ERR("can't register hooks.\n");
1514 IP_VS_INFO("ipvs loaded.\n");
1518 ip_vs_conn_cleanup();
1520 ip_vs_app_cleanup();
1522 ip_vs_protocol_cleanup();
1523 ip_vs_control_cleanup();
1525 ip_vs_estimator_cleanup();
1529 static void __exit
ip_vs_cleanup(void)
1531 nf_unregister_hooks(ip_vs_ops
, ARRAY_SIZE(ip_vs_ops
));
1532 ip_vs_conn_cleanup();
1533 ip_vs_app_cleanup();
1534 ip_vs_protocol_cleanup();
1535 ip_vs_control_cleanup();
1536 ip_vs_estimator_cleanup();
1537 IP_VS_INFO("ipvs unloaded.\n");
1540 module_init(ip_vs_init
);
1541 module_exit(ip_vs_cleanup
);
1542 MODULE_LICENSE("GPL");