1 /*
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
6 * cluster of servers.
7 *
8 * Version: $Id: ip_vs_core.c,v 1.31.2.5 2003/07/29 14:37:12 wensong Exp $
9 *
10 * Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
11 * Peter Kese <peter.kese@ijs.si>
12 * Julian Anastasov <ja@ssi.bg>
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version
17 * 2 of the License, or (at your option) any later version.
18 *
19 * The IPVS code for kernel 2.2 was done by Wensong Zhang and Peter Kese,
20 * with changes/fixes from Julian Anastasov, Lars Marowsky-Bree, Horms
21 * and others.
22 *
23 * Changes:
24 *
25 */
26
27 #include <linux/config.h>
28 #include <linux/module.h>
29 #include <linux/types.h>
30 #include <linux/kernel.h>
31 #include <linux/errno.h>
32 #include <linux/ip.h>
33 #include <linux/tcp.h>
34 #include <linux/icmp.h>
35
36 #include <net/ip.h>
37 #include <net/tcp.h>
38 #include <net/udp.h>
39 #include <net/icmp.h> /* for icmp_send */
40 #include <net/route.h>
41
42 #include <linux/netfilter.h>
43 #include <linux/netfilter_ipv4.h>
44
45 #include <net/ip_vs.h>
46
47
48 EXPORT_SYMBOL(register_ip_vs_scheduler);
49 EXPORT_SYMBOL(unregister_ip_vs_scheduler);
50 EXPORT_SYMBOL(ip_vs_skb_replace);
51 EXPORT_SYMBOL(ip_vs_proto_name);
52 EXPORT_SYMBOL(ip_vs_conn_new);
53 EXPORT_SYMBOL(ip_vs_conn_in_get);
54 EXPORT_SYMBOL(ip_vs_conn_out_get);
55 EXPORT_SYMBOL(ip_vs_conn_listen);
56 EXPORT_SYMBOL(ip_vs_conn_put);
57 #ifdef CONFIG_IP_VS_DEBUG
58 EXPORT_SYMBOL(ip_vs_get_debug_level);
59 #endif
60 EXPORT_SYMBOL(check_for_ip_vs_out);
61
62
63 /* ID used in ICMP lookups */
64 #define icmp_id(icmph) ((icmph->un).echo.id)
65
ip_vs_proto_name(unsigned proto)66 const char *ip_vs_proto_name(unsigned proto)
67 {
68 static char buf[20];
69
70 switch (proto) {
71 case IPPROTO_IP:
72 return "IP";
73 case IPPROTO_UDP:
74 return "UDP";
75 case IPPROTO_TCP:
76 return "TCP";
77 case IPPROTO_ICMP:
78 return "ICMP";
79 default:
80 sprintf(buf, "IP_%d", proto);
81 return buf;
82 }
83 }
84
85
86 static inline void
ip_vs_in_stats(struct ip_vs_conn * cp,struct sk_buff * skb)87 ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
88 {
89 struct ip_vs_dest *dest = cp->dest;
90 if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
91 spin_lock(&dest->stats.lock);
92 dest->stats.inpkts++;
93 dest->stats.inbytes += skb->len;
94 spin_unlock(&dest->stats.lock);
95
96 spin_lock(&dest->svc->stats.lock);
97 dest->svc->stats.inpkts++;
98 dest->svc->stats.inbytes += skb->len;
99 spin_unlock(&dest->svc->stats.lock);
100
101 spin_lock(&ip_vs_stats.lock);
102 ip_vs_stats.inpkts++;
103 ip_vs_stats.inbytes += skb->len;
104 spin_unlock(&ip_vs_stats.lock);
105 }
106 }
107
108
109 static inline void
ip_vs_out_stats(struct ip_vs_conn * cp,struct sk_buff * skb)110 ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
111 {
112 struct ip_vs_dest *dest = cp->dest;
113 if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
114 spin_lock(&dest->stats.lock);
115 dest->stats.outpkts++;
116 dest->stats.outbytes += skb->len;
117 spin_unlock(&dest->stats.lock);
118
119 spin_lock(&dest->svc->stats.lock);
120 dest->svc->stats.outpkts++;
121 dest->svc->stats.outbytes += skb->len;
122 spin_unlock(&dest->svc->stats.lock);
123
124 spin_lock(&ip_vs_stats.lock);
125 ip_vs_stats.outpkts++;
126 ip_vs_stats.outbytes += skb->len;
127 spin_unlock(&ip_vs_stats.lock);
128 }
129 }
130
131
132 static inline void
ip_vs_conn_stats(struct ip_vs_conn * cp,struct ip_vs_service * svc)133 ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
134 {
135 spin_lock(&cp->dest->stats.lock);
136 cp->dest->stats.conns++;
137 spin_unlock(&cp->dest->stats.lock);
138
139 spin_lock(&svc->stats.lock);
140 svc->stats.conns++;
141 spin_unlock(&svc->stats.lock);
142
143 spin_lock(&ip_vs_stats.lock);
144 ip_vs_stats.conns++;
145 spin_unlock(&ip_vs_stats.lock);
146 }
147
148 /*
149 * IPVS persistent scheduling function
150 * It creates a connection entry according to its template if exists,
151 * or selects a server and creates a connection entry plus a template.
152 * Locking: we are svc user (svc->refcnt), so we hold all dests too
153 */
154 static struct ip_vs_conn *
ip_vs_sched_persist(struct ip_vs_service * svc,struct iphdr * iph)155 ip_vs_sched_persist(struct ip_vs_service *svc, struct iphdr *iph)
156 {
157 struct ip_vs_conn *cp = NULL;
158 struct ip_vs_dest *dest;
159 const __u16 *portp;
160 struct ip_vs_conn *ct;
161 __u16 dport; /* destination port to forward */
162 __u32 snet; /* source network of the client, after masking */
163
164 portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
165
166 /* Mask saddr with the netmask to adjust template granularity */
167 snet = iph->saddr & svc->netmask;
168
169 IP_VS_DBG(6, "P-schedule: src %u.%u.%u.%u:%u dest %u.%u.%u.%u:%u "
170 "mnet %u.%u.%u.%u\n",
171 NIPQUAD(iph->saddr), ntohs(portp[0]),
172 NIPQUAD(iph->daddr), ntohs(portp[1]),
173 NIPQUAD(snet));
174
175 /*
176 * As far as we know, FTP is a very complicated network protocol, and
177 * it uses control connection and data connections. For active FTP,
178 * FTP server initialize data connection to the client, its source port
179 * is often 20. For passive FTP, FTP server tells the clients the port
180 * that it passively listens to, and the client issues the data
181 * connection. In the tunneling or direct routing mode, the load
182 * balancer is on the client-to-server half of connection, the port
183 * number is unknown to the load balancer. So, a conn template like
184 * <caddr, 0, vaddr, 0, daddr, 0> is created for persistent FTP
185 * service, and a template like <caddr, 0, vaddr, vport, daddr, dport>
186 * is created for other persistent services.
187 */
188 if (portp[1] == svc->port) {
189 /* Check if a template already exists */
190 if (svc->port != FTPPORT)
191 ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
192 iph->daddr, portp[1]);
193 else
194 ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
195 iph->daddr, 0);
196
197 if (!ct || !ip_vs_check_template(ct)) {
198 /*
199 * No template found or the dest of the connection
200 * template is not available.
201 */
202 dest = svc->scheduler->schedule(svc, iph);
203 if (dest == NULL) {
204 IP_VS_DBG(1, "P-schedule: no dest found.\n");
205 return NULL;
206 }
207
208 /*
209 * Create a template like <protocol,caddr,0,
210 * vaddr,vport,daddr,dport> for non-ftp service,
211 * and <protocol,caddr,0,vaddr,0,daddr,0>
212 * for ftp service.
213 */
214 if (svc->port != FTPPORT)
215 ct = ip_vs_conn_new(iph->protocol,
216 snet, 0,
217 iph->daddr, portp[1],
218 dest->addr, dest->port,
219 IP_VS_CONN_F_TEMPLATE,
220 dest);
221 else
222 ct = ip_vs_conn_new(iph->protocol,
223 snet, 0,
224 iph->daddr, 0,
225 dest->addr, 0,
226 IP_VS_CONN_F_TEMPLATE,
227 dest);
228 if (ct == NULL)
229 return NULL;
230
231 ct->timeout = svc->timeout;
232 } else {
233 /* set destination with the found template */
234 dest = ct->dest;
235 }
236 dport = dest->port;
237 } else {
238 /*
239 * Note: persistent fwmark-based services and persistent
240 * port zero service are handled here.
241 * fwmark template: <IPPROTO_IP,caddr,0,fwmark,0,daddr,0>
242 * port zero template: <protocol,caddr,0,vaddr,0,daddr,0>
243 */
244 if (svc->fwmark)
245 ct = ip_vs_ct_in_get(IPPROTO_IP, snet, 0,
246 htonl(svc->fwmark), 0);
247 else
248 ct = ip_vs_ct_in_get(iph->protocol, snet, 0,
249 iph->daddr, 0);
250
251 if (!ct || !ip_vs_check_template(ct)) {
252 /*
253 * If it is not persistent port zero, return NULL,
254 * otherwise create a connection template.
255 */
256 if (svc->port)
257 return NULL;
258
259 dest = svc->scheduler->schedule(svc, iph);
260 if (dest == NULL) {
261 IP_VS_DBG(1, "P-schedule: no dest found.\n");
262 return NULL;
263 }
264
265 /*
266 * Create a template according to the service
267 */
268 if (svc->fwmark)
269 ct = ip_vs_conn_new(IPPROTO_IP,
270 snet, 0,
271 htonl(svc->fwmark), 0,
272 dest->addr, 0,
273 IP_VS_CONN_F_TEMPLATE,
274 dest);
275 else
276 ct = ip_vs_conn_new(iph->protocol,
277 snet, 0,
278 iph->daddr, 0,
279 dest->addr, 0,
280 IP_VS_CONN_F_TEMPLATE,
281 dest);
282 if (ct == NULL)
283 return NULL;
284
285 ct->timeout = svc->timeout;
286 } else {
287 /* set destination with the found template */
288 dest = ct->dest;
289 }
290 dport = portp[1];
291 }
292
293 /*
294 * Create a new connection according to the template
295 */
296 cp = ip_vs_conn_new(iph->protocol,
297 iph->saddr, portp[0],
298 iph->daddr, portp[1],
299 dest->addr, dport,
300 0,
301 dest);
302 if (cp == NULL) {
303 ip_vs_conn_put(ct);
304 return NULL;
305 }
306
307 /*
308 * Increase the inactive connection counter
309 * because it is in Syn-Received
310 * state (inactive) when the connection is created.
311 */
312 atomic_inc(&dest->inactconns);
313
314 /*
315 * Add its control
316 */
317 ip_vs_control_add(cp, ct);
318
319 ip_vs_conn_put(ct);
320 return cp;
321 }
322
323
324 /*
325 * IPVS main scheduling function
326 * It selects a server according to the virtual service, and
327 * creates a connection entry.
328 */
329 static struct ip_vs_conn *
ip_vs_schedule(struct ip_vs_service * svc,struct iphdr * iph)330 ip_vs_schedule(struct ip_vs_service *svc, struct iphdr *iph)
331 {
332 struct ip_vs_conn *cp = NULL;
333 struct ip_vs_dest *dest;
334 const __u16 *portp;
335
336 /*
337 * Persistent service
338 */
339 if (svc->flags & IP_VS_SVC_F_PERSISTENT)
340 return ip_vs_sched_persist(svc, iph);
341
342 /*
343 * Non-persistent service
344 */
345 portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
346 if (!svc->fwmark && portp[1] != svc->port) {
347 if (!svc->port)
348 IP_VS_ERR("Schedule: port zero only supported "
349 "in persistent services, "
350 "check your ipvs configuration\n");
351 return NULL;
352 }
353
354 dest = svc->scheduler->schedule(svc, iph);
355 if (dest == NULL) {
356 IP_VS_DBG(1, "Schedule: no dest found.\n");
357 return NULL;
358 }
359
360 /*
361 * Create a connection entry.
362 */
363 cp = ip_vs_conn_new(iph->protocol,
364 iph->saddr, portp[0],
365 iph->daddr, portp[1],
366 dest->addr, dest->port?dest->port:portp[1],
367 0,
368 dest);
369 if (cp == NULL)
370 return NULL;
371
372 /*
373 * Increase the inactive connection counter because it is in
374 * Syn-Received state (inactive) when the connection is created.
375 */
376 atomic_inc(&dest->inactconns);
377
378 IP_VS_DBG(6, "Schedule fwd:%c s:%s c:%u.%u.%u.%u:%u v:%u.%u.%u.%u:%u "
379 "d:%u.%u.%u.%u:%u flg:%X cnt:%d\n",
380 ip_vs_fwd_tag(cp), ip_vs_state_name(cp->state),
381 NIPQUAD(cp->caddr), ntohs(cp->cport),
382 NIPQUAD(cp->vaddr), ntohs(cp->vport),
383 NIPQUAD(cp->daddr), ntohs(cp->dport),
384 cp->flags, atomic_read(&cp->refcnt));
385
386 return cp;
387 }
388
389
390 /*
391 * Pass or drop the packet.
392 * Called by ip_vs_in, when the virtual service is available but
393 * no destination is available for a new connection.
394 */
ip_vs_leave(struct ip_vs_service * svc,struct sk_buff * skb)395 static int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb)
396 {
397 struct iphdr *iph = skb->nh.iph;
398 __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
399
400 /* if it is fwmark-based service, the cache_bypass sysctl is up
401 and the destination is RTN_UNICAST (and not local), then create
402 a cache_bypass connection entry */
403 if (sysctl_ip_vs_cache_bypass && svc->fwmark
404 && (inet_addr_type(iph->daddr) == RTN_UNICAST)) {
405 int ret;
406 struct ip_vs_conn *cp;
407
408 ip_vs_service_put(svc);
409
410 /* create a new connection entry */
411 IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n");
412 cp = ip_vs_conn_new(iph->protocol,
413 iph->saddr, portp[0],
414 iph->daddr, portp[1],
415 0, 0,
416 IP_VS_CONN_F_BYPASS,
417 NULL);
418 if (cp == NULL) {
419 kfree_skb(skb);
420 return NF_STOLEN;
421 }
422
423 /* statistics */
424 ip_vs_in_stats(cp, skb);
425
426 /* set state */
427 ip_vs_set_state(cp, VS_STATE_INPUT, iph, portp);
428
429 /* transmit the first SYN packet */
430 ret = cp->packet_xmit(skb, cp);
431
432 atomic_inc(&cp->in_pkts);
433 ip_vs_conn_put(cp);
434 return ret;
435 }
436
437 /*
438 * When the virtual ftp service is presented, packets destined
439 * for other services on the VIP may get here (except services
440 * listed in the ipvs table), pass the packets, because it is
441 * not ipvs job to decide to drop the packets.
442 */
443 if ((svc->port == FTPPORT) && (portp[1] != FTPPORT)) {
444 ip_vs_service_put(svc);
445 return NF_ACCEPT;
446 }
447
448 ip_vs_service_put(svc);
449
450 /*
451 * Notify the client that the destination is unreachable, and
452 * release the socket buffer.
453 * Since it is in IP layer, the TCP socket is not actually
454 * created, the TCP RST packet cannot be sent, instead that
455 * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ
456 */
457 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
458 kfree_skb(skb);
459 return NF_STOLEN;
460 }
461
462
463 /*
464 * It is hooked before NF_IP_PRI_NAT_SRC at the NF_IP_POST_ROUTING
465 * chain, and is used for VS/NAT.
466 * It detects packets for VS/NAT connections and sends the packets
467 * immediately. This can avoid that iptable_nat mangles the packets
468 * for VS/NAT.
469 */
ip_vs_post_routing(unsigned int hooknum,struct sk_buff ** skb_p,const struct net_device * in,const struct net_device * out,int (* okfn)(struct sk_buff *))470 static unsigned int ip_vs_post_routing(unsigned int hooknum,
471 struct sk_buff **skb_p,
472 const struct net_device *in,
473 const struct net_device *out,
474 int (*okfn)(struct sk_buff *))
475 {
476 struct sk_buff *skb = *skb_p;
477
478 if (!(skb->nfcache & NFC_IPVS_PROPERTY))
479 return NF_ACCEPT;
480
481 /* The packet was sent from IPVS, exit this chain */
482 (*okfn)(skb);
483
484 return NF_STOLEN;
485 }
486
487
488 /*
489 * Handle ICMP messages in the inside-to-outside direction (outgoing).
490 * Find any that might be relevant, check against existing connections,
491 * forward to the right destination host if relevant.
492 * Currently handles error types - unreachable, quench, ttl exceeded.
493 * (Only used in VS/NAT)
494 */
ip_vs_out_icmp(struct sk_buff ** skb_p)495 static int ip_vs_out_icmp(struct sk_buff **skb_p)
496 {
497 struct sk_buff *skb = *skb_p;
498 struct iphdr *iph;
499 struct icmphdr *icmph;
500 struct iphdr *ciph; /* The ip header contained within the ICMP */
501 __u16 *pptr; /* port numbers from TCP/UDP contained header */
502 unsigned short ihl;
503 unsigned short len;
504 unsigned short clen, csize;
505 struct ip_vs_conn *cp;
506
507 /* reassemble IP fragments, but will it happen in ICMP packets?? */
508 if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) {
509 skb = ip_defrag(skb, IP_DEFRAG_VS_OUT);
510 if (!skb)
511 return NF_STOLEN;
512 *skb_p = skb;
513 }
514
515 if (skb_is_nonlinear(skb)) {
516 if (skb_linearize(skb, GFP_ATOMIC) != 0)
517 return NF_DROP;
518 ip_send_check(skb->nh.iph);
519 }
520
521 iph = skb->nh.iph;
522 ihl = iph->ihl << 2;
523 icmph = (struct icmphdr *)((char *)iph + ihl);
524 len = ntohs(iph->tot_len) - ihl;
525 if (len < sizeof(struct icmphdr))
526 return NF_DROP;
527
528 IP_VS_DBG(12, "outgoing ICMP (%d,%d) %u.%u.%u.%u->%u.%u.%u.%u\n",
529 icmph->type, ntohs(icmp_id(icmph)),
530 NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
531
532 /*
533 * Work through seeing if this is for us.
534 * These checks are supposed to be in an order that means easy
535 * things are checked first to speed up processing.... however
536 * this means that some packets will manage to get a long way
537 * down this stack and then be rejected, but that's life.
538 */
539 if ((icmph->type != ICMP_DEST_UNREACH) &&
540 (icmph->type != ICMP_SOURCE_QUENCH) &&
541 (icmph->type != ICMP_TIME_EXCEEDED))
542 return NF_ACCEPT;
543
544 /* Now find the contained IP header */
545 clen = len - sizeof(struct icmphdr);
546 if (clen < sizeof(struct iphdr))
547 return NF_DROP;
548 ciph = (struct iphdr *) (icmph + 1);
549 csize = ciph->ihl << 2;
550 if (clen < csize)
551 return NF_DROP;
552
553 /* We are only interested ICMPs generated from TCP or UDP packets */
554 if (ciph->protocol != IPPROTO_UDP && ciph->protocol != IPPROTO_TCP)
555 return NF_ACCEPT;
556
557 /* Skip non-first embedded TCP/UDP fragments */
558 if (ciph->frag_off & __constant_htons(IP_OFFSET))
559 return NF_ACCEPT;
560
561 /* We need at least TCP/UDP ports here */
562 if (clen < csize + sizeof(struct udphdr))
563 return NF_DROP;
564
565 /*
566 * Find the ports involved - this packet was
567 * incoming so the ports are right way round
568 * (but reversed relative to outer IP header!)
569 */
570 pptr = (__u16 *)&(((char *)ciph)[csize]);
571
572 /* Ensure the checksum is correct */
573 if (ip_compute_csum((unsigned char *) icmph, len)) {
574 /* Failed checksum! */
575 IP_VS_DBG(1, "forward ICMP: failed checksum from %d.%d.%d.%d!\n",
576 NIPQUAD(iph->saddr));
577 return NF_DROP;
578 }
579
580 IP_VS_DBG(11, "Handling outgoing ICMP for "
581 "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n",
582 NIPQUAD(ciph->saddr), ntohs(pptr[0]),
583 NIPQUAD(ciph->daddr), ntohs(pptr[1]));
584
585 /* ciph content is actually <protocol, caddr, cport, daddr, dport> */
586 cp = ip_vs_conn_out_get(ciph->protocol, ciph->daddr, pptr[1],
587 ciph->saddr, pptr[0]);
588 if (!cp)
589 return NF_ACCEPT;
590
591 if (IP_VS_FWD_METHOD(cp) != 0) {
592 IP_VS_ERR("shouldn't reach here, because the box is on the"
593 "half connection in the tun/dr module.\n");
594 }
595
596 /* Now we do real damage to this packet...! */
597 /* First change the source IP address, and recalc checksum */
598 iph->saddr = cp->vaddr;
599 ip_send_check(iph);
600
601 /* Now change the *dest* address in the contained IP */
602 ciph->daddr = cp->vaddr;
603 ip_send_check(ciph);
604
605 /* the TCP/UDP dest port - cannot redo check */
606 pptr[1] = cp->vport;
607
608 /* And finally the ICMP checksum */
609 icmph->checksum = 0;
610 icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
611 skb->ip_summed = CHECKSUM_UNNECESSARY;
612
613 /* do the statistics and put it back */
614 ip_vs_out_stats(cp, skb);
615 ip_vs_conn_put(cp);
616
617 IP_VS_DBG(11, "Forwarding correct outgoing ICMP to "
618 "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n",
619 NIPQUAD(ciph->saddr), ntohs(pptr[0]),
620 NIPQUAD(ciph->daddr), ntohs(pptr[1]));
621
622 skb->nfcache |= NFC_IPVS_PROPERTY;
623
624 return NF_ACCEPT;
625 }
626
627
628 /*
629 * It is hooked at the NF_IP_FORWARD chain, used only for VS/NAT.
630 * Check if outgoing packet belongs to the established ip_vs_conn,
631 * rewrite addresses of the packet and send it on its way...
632 */
ip_vs_out(unsigned int hooknum,struct sk_buff ** skb_p,const struct net_device * in,const struct net_device * out,int (* okfn)(struct sk_buff *))633 static unsigned int ip_vs_out(unsigned int hooknum,
634 struct sk_buff **skb_p,
635 const struct net_device *in,
636 const struct net_device *out,
637 int (*okfn)(struct sk_buff *))
638 {
639 struct sk_buff *skb = *skb_p;
640 struct iphdr *iph;
641 union ip_vs_tphdr h;
642 struct ip_vs_conn *cp;
643 int size;
644 int ihl;
645
646 EnterFunction(11);
647
648 if (skb->nfcache & NFC_IPVS_PROPERTY)
649 return NF_ACCEPT;
650
651 iph = skb->nh.iph;
652 if (iph->protocol == IPPROTO_ICMP)
653 return ip_vs_out_icmp(skb_p);
654
655 /* let it go if other IP protocols */
656 if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)
657 return NF_ACCEPT;
658
659 /* reassemble IP fragments */
660 if (iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) {
661 skb = ip_defrag(skb, IP_DEFRAG_VS_OUT);
662 if (!skb)
663 return NF_STOLEN;
664 iph = skb->nh.iph;
665 *skb_p = skb;
666 }
667
668 /* make sure that protocol header available in skb data area,
669 note that skb data area may be reallocated. */
670 ihl = iph->ihl << 2;
671 if (ip_vs_header_check(skb, iph->protocol, ihl) == -1)
672 return NF_DROP;
673
674 iph = skb->nh.iph;
675 h.raw = (char*) iph + ihl;
676
677 /*
678 * Check if the packet belongs to an old entry
679 */
680 cp = ip_vs_conn_out_get(iph->protocol, iph->saddr, h.portp[0],
681 iph->daddr, h.portp[1]);
682 if (!cp) {
683 if (sysctl_ip_vs_nat_icmp_send &&
684 ip_vs_lookup_real_service(iph->protocol,
685 iph->saddr, h.portp[0])) {
686 /*
687 * Notify the real server: there is no existing
688 * entry if it is not RST packet or not TCP packet.
689 */
690 if (!h.th->rst || iph->protocol != IPPROTO_TCP) {
691 icmp_send(skb, ICMP_DEST_UNREACH,
692 ICMP_PORT_UNREACH, 0);
693 kfree_skb(skb);
694 return NF_STOLEN;
695 }
696 }
697 IP_VS_DBG(12, "packet for %s %d.%d.%d.%d:%d "
698 "continue traversal as normal.\n",
699 ip_vs_proto_name(iph->protocol),
700 NIPQUAD(iph->daddr),
701 ntohs(h.portp[1]));
702 if (skb_is_nonlinear(skb))
703 ip_send_check(iph);
704 return NF_ACCEPT;
705 }
706
707 /*
708 * If it has ip_vs_app helper, the helper may change the payload,
709 * so it needs full checksum checking and checksum calculation.
710 * If not, only the header (addr/port) is changed, so it is fast
711 * to do incremental checksum update, and let the destination host
712 * do final checksum checking.
713 */
714
715 if (cp->app && skb_is_nonlinear(skb)) {
716 if (skb_linearize(skb, GFP_ATOMIC) != 0) {
717 ip_vs_conn_put(cp);
718 return NF_DROP;
719 }
720 iph = skb->nh.iph;
721 h.raw = (char*) iph + ihl;
722 }
723
724 size = skb->len - ihl;
725 IP_VS_DBG(11, "O-pkt: %s size=%d\n",
726 ip_vs_proto_name(iph->protocol), size);
727
728 /* do TCP/UDP checksum checking if it has application helper */
729 if (cp->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) {
730 switch (skb->ip_summed) {
731 case CHECKSUM_NONE:
732 skb->csum = csum_partial(h.raw, size, 0);
733 case CHECKSUM_HW:
734 if (csum_tcpudp_magic(iph->saddr, iph->daddr, size,
735 iph->protocol, skb->csum)) {
736 ip_vs_conn_put(cp);
737 IP_VS_DBG_RL("Outgoing failed %s checksum "
738 "from %d.%d.%d.%d (size=%d)!\n",
739 ip_vs_proto_name(iph->protocol),
740 NIPQUAD(iph->saddr),
741 size);
742 return NF_DROP;
743 }
744 break;
745 default:
746 /* CHECKSUM_UNNECESSARY */
747 break;
748 }
749 }
750
751 IP_VS_DBG(11, "Outgoing %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d\n",
752 ip_vs_proto_name(iph->protocol),
753 NIPQUAD(iph->saddr), ntohs(h.portp[0]),
754 NIPQUAD(iph->daddr), ntohs(h.portp[1]));
755
756 /* mangle the packet */
757 iph->saddr = cp->vaddr;
758 h.portp[0] = cp->vport;
759
760 /*
761 * Call application helper if needed
762 */
763 if (ip_vs_app_pkt_out(cp, skb) != 0) {
764 /* skb data has probably changed, update pointers */
765 iph = skb->nh.iph;
766 h.raw = (char*)iph + ihl;
767 size = skb->len - ihl;
768 }
769
770 /*
771 * Adjust TCP/UDP checksums
772 */
773 if (!cp->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) {
774 /* Only port and addr are changed, do fast csum update */
775 ip_vs_fast_check_update(&h, cp->daddr, cp->vaddr,
776 cp->dport, cp->vport, iph->protocol);
777 if (skb->ip_summed == CHECKSUM_HW)
778 skb->ip_summed = CHECKSUM_NONE;
779 } else {
780 /* full checksum calculation */
781 switch (iph->protocol) {
782 case IPPROTO_TCP:
783 h.th->check = 0;
784 skb->csum = csum_partial(h.raw, size, 0);
785 h.th->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
786 size, iph->protocol,
787 skb->csum);
788 IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n",
789 ip_vs_proto_name(iph->protocol), h.th->check,
790 (char*)&(h.th->check) - (char*)h.raw);
791 break;
792 case IPPROTO_UDP:
793 h.uh->check = 0;
794 skb->csum = csum_partial(h.raw, size, 0);
795 h.uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
796 size, iph->protocol,
797 skb->csum);
798 if (h.uh->check == 0)
799 h.uh->check = 0xFFFF;
800 IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n",
801 ip_vs_proto_name(iph->protocol), h.uh->check,
802 (char*)&(h.uh->check) - (char*)h.raw);
803 break;
804 }
805 }
806 ip_send_check(iph);
807
808 ip_vs_out_stats(cp, skb);
809 ip_vs_set_state(cp, VS_STATE_OUTPUT, iph, h.portp);
810 ip_vs_conn_put(cp);
811
812 skb->nfcache |= NFC_IPVS_PROPERTY;
813
814 LeaveFunction(11);
815 return NF_ACCEPT;
816 }
817
818
819 /*
820 * Check if the packet is for VS/NAT connections, then send it
821 * immediately.
822 * Called by ip_fw_compact to detect packets for VS/NAT before
823 * they are changed by ipchains masquerading code.
824 */
check_for_ip_vs_out(struct sk_buff ** skb_p,int (* okfn)(struct sk_buff *))825 unsigned int check_for_ip_vs_out(struct sk_buff **skb_p,
826 int (*okfn)(struct sk_buff *))
827 {
828 unsigned int ret;
829
830 ret = ip_vs_out(NF_IP_FORWARD, skb_p, NULL, NULL, NULL);
831 if (ret != NF_ACCEPT) {
832 return ret;
833 } else {
834 /* send the packet immediately if it is already mangled
835 by ip_vs_out */
836 if ((*skb_p)->nfcache & NFC_IPVS_PROPERTY) {
837 (*okfn)(*skb_p);
838 return NF_STOLEN;
839 }
840 }
841 return NF_ACCEPT;
842 }
843
844
845 /*
846 * Handle ICMP messages in the outside-to-inside direction (incoming)
847 * and sometimes in outgoing direction from ip_vs_forward_icmp.
848 * Find any that might be relevant, check against existing connections,
849 * forward to the right destination host if relevant.
850 * Currently handles error types - unreachable, quench, ttl exceeded.
851 */
ip_vs_in_icmp(struct sk_buff ** skb_p)852 static int ip_vs_in_icmp(struct sk_buff **skb_p)
853 {
854 struct sk_buff *skb = *skb_p;
855 struct iphdr *iph;
856 struct icmphdr *icmph;
857 struct iphdr *ciph; /* The ip header contained within the ICMP */
858 __u16 *pptr; /* port numbers from TCP/UDP contained header */
859 unsigned short len;
860 unsigned short clen, csize;
861 struct ip_vs_conn *cp;
862 struct rtable *rt; /* Route to the other host */
863 int mtu;
864
865 if (skb_is_nonlinear(skb)) {
866 if (skb_linearize(skb, GFP_ATOMIC) != 0)
867 return NF_DROP;
868 }
869
870 iph = skb->nh.iph;
871 ip_send_check(iph);
872 icmph = (struct icmphdr *)((char *)iph + (iph->ihl << 2));
873 len = ntohs(iph->tot_len) - (iph->ihl<<2);
874 if (len < sizeof(struct icmphdr))
875 return NF_DROP;
876
877 IP_VS_DBG(12, "icmp in (%d,%d) %u.%u.%u.%u -> %u.%u.%u.%u\n",
878 icmph->type, ntohs(icmp_id(icmph)),
879 NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
880
881 if ((icmph->type != ICMP_DEST_UNREACH) &&
882 (icmph->type != ICMP_SOURCE_QUENCH) &&
883 (icmph->type != ICMP_TIME_EXCEEDED))
884 return NF_ACCEPT;
885
886 /*
887 * If we get here we have an ICMP error of one of the above 3 types
888 * Now find the contained IP header
889 */
890 clen = len - sizeof(struct icmphdr);
891 if (clen < sizeof(struct iphdr))
892 return NF_DROP;
893 ciph = (struct iphdr *) (icmph + 1);
894 csize = ciph->ihl << 2;
895 if (clen < csize)
896 return NF_DROP;
897
898 /* We are only interested ICMPs generated from TCP or UDP packets */
899 if (ciph->protocol != IPPROTO_UDP && ciph->protocol != IPPROTO_TCP)
900 return NF_ACCEPT;
901
902 /* Skip non-first embedded TCP/UDP fragments */
903 if (ciph->frag_off & __constant_htons(IP_OFFSET))
904 return NF_ACCEPT;
905
906 /* We need at least TCP/UDP ports here */
907 if (clen < csize + sizeof(struct udphdr))
908 return NF_DROP;
909
910 /* Ensure the checksum is correct */
911 if (ip_compute_csum((unsigned char *) icmph, len)) {
912 /* Failed checksum! */
913 IP_VS_ERR_RL("incoming ICMP: failed checksum from "
914 "%d.%d.%d.%d!\n", NIPQUAD(iph->saddr));
915 return NF_DROP;
916 }
917
918 pptr = (__u16 *)&(((char *)ciph)[csize]);
919
920 IP_VS_DBG(11, "Handling incoming ICMP for "
921 "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n",
922 NIPQUAD(ciph->saddr), ntohs(pptr[0]),
923 NIPQUAD(ciph->daddr), ntohs(pptr[1]));
924
925 /* This is pretty much what ip_vs_conn_in_get() does,
926 except parameters are in the reverse order */
927 cp = ip_vs_conn_in_get(ciph->protocol,
928 ciph->daddr, pptr[1],
929 ciph->saddr, pptr[0]);
930 if (cp == NULL)
931 return NF_ACCEPT;
932
933 ip_vs_in_stats(cp, skb);
934
935 /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
936 forwarded directly here, because there is no need to
937 translate address/port back */
938 if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
939 int ret;
940 if (cp->packet_xmit)
941 ret = cp->packet_xmit(skb, cp);
942 else
943 ret = NF_ACCEPT;
944 atomic_inc(&cp->in_pkts);
945 ip_vs_conn_put(cp);
946 return ret;
947 }
948
949 /*
950 * mangle and send the packet here
951 */
952 if (!(rt = __ip_vs_get_out_rt(cp, RT_TOS(iph->tos))))
953 goto tx_error_icmp;
954
955 /* MTU checking */
956 mtu = rt->u.dst.pmtu;
957 if ((skb->len > mtu) && (iph->frag_off&__constant_htons(IP_DF))) {
958 ip_rt_put(rt);
959 icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu));
960 IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n");
961 goto tx_error;
962 }
963
964 /* drop old route */
965 dst_release(skb->dst);
966 skb->dst = &rt->u.dst;
967
968 /* copy-on-write the packet before mangling it */
969 if (ip_vs_skb_cow(skb, rt->u.dst.dev->hard_header_len,
970 &iph, (unsigned char**)&icmph)) {
971 ip_vs_conn_put(cp);
972 return NF_DROP;
973 }
974 ciph = (struct iphdr *) (icmph + 1);
975 pptr = (__u16 *)&(((char *)ciph)[csize]);
976
977 /* The ICMP packet for VS/NAT must be written to correct addresses
978 before being forwarded to the right server */
979
980 /* First change the dest IP address, and recalc checksum */
981 iph->daddr = cp->daddr;
982 ip_send_check(iph);
983
984 /* Now change the *source* address in the contained IP */
985 ciph->saddr = cp->daddr;
986 ip_send_check(ciph);
987
988 /* the TCP/UDP source port - cannot redo check */
989 pptr[0] = cp->dport;
990
991 /* And finally the ICMP checksum */
992 icmph->checksum = 0;
993 icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
994 skb->ip_summed = CHECKSUM_UNNECESSARY;
995
996 IP_VS_DBG(11, "Forwarding incoming ICMP to "
997 "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n",
998 NIPQUAD(ciph->saddr), ntohs(pptr[0]),
999 NIPQUAD(ciph->daddr), ntohs(pptr[1]));
1000
1001 #ifdef CONFIG_NETFILTER_DEBUG
1002 skb->nf_debug = 1 << NF_IP_LOCAL_OUT;
1003 #endif /* CONFIG_NETFILTER_DEBUG */
1004 ip_send(skb);
1005 ip_vs_conn_put(cp);
1006 return NF_STOLEN;
1007
1008 tx_error_icmp:
1009 dst_link_failure(skb);
1010 tx_error:
1011 dev_kfree_skb(skb);
1012 ip_vs_conn_put(cp);
1013 return NF_STOLEN;
1014 }
1015
1016
1017 /*
1018 * Check if it's for virtual services, look it up,
1019 * and send it on its way...
1020 */
ip_vs_in(unsigned int hooknum,struct sk_buff ** skb_p,const struct net_device * in,const struct net_device * out,int (* okfn)(struct sk_buff *))1021 static unsigned int ip_vs_in(unsigned int hooknum,
1022 struct sk_buff **skb_p,
1023 const struct net_device *in,
1024 const struct net_device *out,
1025 int (*okfn)(struct sk_buff *))
1026 {
1027 struct sk_buff *skb = *skb_p;
1028 struct iphdr *iph = skb->nh.iph;
1029 union ip_vs_tphdr h;
1030 struct ip_vs_conn *cp;
1031 struct ip_vs_service *svc;
1032 int ihl;
1033 int ret;
1034
1035 /*
1036 * Big tappo: only PACKET_HOST (nor loopback neither mcasts)
1037 * ... don't know why 1st test DOES NOT include 2nd (?)
1038 */
1039 if (skb->pkt_type != PACKET_HOST || skb->dev == &loopback_dev) {
1040 IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n",
1041 skb->pkt_type,
1042 iph->protocol,
1043 NIPQUAD(iph->daddr));
1044 return NF_ACCEPT;
1045 }
1046
1047 if (iph->protocol == IPPROTO_ICMP)
1048 return ip_vs_in_icmp(skb_p);
1049
1050 /* let it go if other IP protocols */
1051 if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)
1052 return NF_ACCEPT;
1053
1054 /* make sure that protocol header available in skb data area,
1055 note that skb data area may be reallocated. */
1056 ihl = iph->ihl << 2;
1057 if (ip_vs_header_check(skb, iph->protocol, ihl) == -1)
1058 return NF_DROP;
1059 iph = skb->nh.iph;
1060 h.raw = (char*) iph + ihl;
1061
1062 /*
1063 * Check if the packet belongs to an existing connection entry
1064 */
1065 cp = ip_vs_conn_in_get(iph->protocol, iph->saddr, h.portp[0],
1066 iph->daddr, h.portp[1]);
1067
1068 if (!cp &&
1069 (h.th->syn || (iph->protocol!=IPPROTO_TCP)) &&
1070 (svc = ip_vs_service_get(skb->nfmark, iph->protocol,
1071 iph->daddr, h.portp[1]))) {
1072 if (ip_vs_todrop()) {
1073 /*
1074 * It seems that we are very loaded.
1075 * We have to drop this packet :(
1076 */
1077 ip_vs_service_put(svc);
1078 return NF_DROP;
1079 }
1080
1081 /*
1082 * Let the virtual server select a real server for the
1083 * incoming connection, and create a connection entry.
1084 */
1085 cp = ip_vs_schedule(svc, iph);
1086 if (!cp)
1087 return ip_vs_leave(svc, skb);
1088 ip_vs_conn_stats(cp, svc);
1089 ip_vs_service_put(svc);
1090 }
1091
1092 if (!cp) {
1093 /* sorry, all this trouble for a no-hit :) */
1094 IP_VS_DBG(12, "packet for %s %d.%d.%d.%d:%d continue "
1095 "traversal as normal.\n",
1096 ip_vs_proto_name(iph->protocol),
1097 NIPQUAD(iph->daddr),
1098 ntohs(h.portp[1]));
1099 return NF_ACCEPT;
1100 }
1101
1102 IP_VS_DBG(11, "Incoming %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d\n",
1103 ip_vs_proto_name(iph->protocol),
1104 NIPQUAD(iph->saddr), ntohs(h.portp[0]),
1105 NIPQUAD(iph->daddr), ntohs(h.portp[1]));
1106
1107 /* Check the server status */
1108 if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
1109 /* the destination server is not available */
1110
1111 if (sysctl_ip_vs_expire_nodest_conn) {
1112 /* try to expire the connection immediately */
1113 ip_vs_conn_expire_now(cp);
1114 }
1115 /* don't restart its timer, and silently
1116 drop the packet. */
1117 __ip_vs_conn_put(cp);
1118 return NF_DROP;
1119 }
1120
1121 ip_vs_in_stats(cp, skb);
1122 ip_vs_set_state(cp, VS_STATE_INPUT, iph, h.portp);
1123 if (cp->packet_xmit)
1124 ret = cp->packet_xmit(skb, cp);
1125 else {
1126 IP_VS_DBG_RL("warning: packet_xmit is null");
1127 ret = NF_ACCEPT;
1128 }
1129
1130 /* increase its packet counter and check if it is needed
1131 to be synchronized */
1132 atomic_inc(&cp->in_pkts);
1133 if (ip_vs_sync_state & IP_VS_STATE_MASTER &&
1134 (cp->protocol != IPPROTO_TCP ||
1135 cp->state == IP_VS_S_ESTABLISHED) &&
1136 (atomic_read(&cp->in_pkts) % 50 == sysctl_ip_vs_sync_threshold))
1137 ip_vs_sync_conn(cp);
1138
1139 ip_vs_conn_put(cp);
1140 return ret;
1141 }
1142
1143
1144 /*
1145 * It is hooked at the NF_IP_FORWARD chain, in order to catch ICMP
1146 * packets destined for 0.0.0.0/0.
1147 * When fwmark-based virtual service is used, such as transparent
1148 * cache cluster, TCP packets can be marked and routed to ip_vs_in,
1149 * but ICMP destined for 0.0.0.0/0 cannot not be easily marked and
1150 * sent to ip_vs_in_icmp. So, catch them at the NF_IP_FORWARD chain
1151 * and send them to ip_vs_in_icmp.
1152 */
ip_vs_forward_icmp(unsigned int hooknum,struct sk_buff ** skb_p,const struct net_device * in,const struct net_device * out,int (* okfn)(struct sk_buff *))1153 static unsigned int ip_vs_forward_icmp(unsigned int hooknum,
1154 struct sk_buff **skb_p,
1155 const struct net_device *in,
1156 const struct net_device *out,
1157 int (*okfn)(struct sk_buff *))
1158 {
1159 struct sk_buff *skb = *skb_p;
1160 struct iphdr *iph = skb->nh.iph;
1161
1162 if (iph->protocol != IPPROTO_ICMP)
1163 return NF_ACCEPT;
1164
1165 if (iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) {
1166 skb = ip_defrag(skb, IP_DEFRAG_VS_FWD);
1167 if (!skb)
1168 return NF_STOLEN;
1169 *skb_p = skb;
1170 }
1171
1172 return ip_vs_in_icmp(skb_p);
1173 }
1174
1175
1176 /* After packet filtering, forward packet through VS/DR, VS/TUN,
1177 or VS/NAT(change destination), so that filtering rules can be
1178 applied to IPVS. */
1179 static struct nf_hook_ops ip_vs_in_ops = {
1180 { NULL, NULL },
1181 ip_vs_in, PF_INET, NF_IP_LOCAL_IN, 100
1182 };
1183
1184 /* After packet filtering, change source only for VS/NAT */
1185 static struct nf_hook_ops ip_vs_out_ops = {
1186 { NULL, NULL },
1187 ip_vs_out, PF_INET, NF_IP_FORWARD, 100
1188 };
1189
1190 /* After packet filtering (but before ip_vs_out_icmp), catch icmp
1191 destined for 0.0.0.0/0, which is for incoming IPVS connections */
1192 static struct nf_hook_ops ip_vs_forward_icmp_ops = {
1193 { NULL, NULL },
1194 ip_vs_forward_icmp, PF_INET, NF_IP_FORWARD, 99
1195 };
1196
1197 /* Before the netfilter connection tracking, exit from POST_ROUTING */
1198 static struct nf_hook_ops ip_vs_post_routing_ops = {
1199 { NULL, NULL },
1200 ip_vs_post_routing, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_NAT_SRC-1
1201 };
1202
1203
1204 /*
1205 * Initialize IP Virtual Server
1206 */
ip_vs_init(void)1207 static int __init ip_vs_init(void)
1208 {
1209 int ret;
1210
1211 ret = ip_vs_control_init();
1212 if (ret < 0) {
1213 IP_VS_ERR("can't setup control.\n");
1214 goto cleanup_nothing;
1215 }
1216
1217 ret = ip_vs_conn_init();
1218 if (ret < 0) {
1219 IP_VS_ERR("can't setup connection table.\n");
1220 goto cleanup_control;
1221 }
1222
1223 ret = ip_vs_app_init();
1224 if (ret < 0) {
1225 IP_VS_ERR("can't setup application helper.\n");
1226 goto cleanup_conn;
1227 }
1228
1229 ret = nf_register_hook(&ip_vs_in_ops);
1230 if (ret < 0) {
1231 IP_VS_ERR("can't register in hook.\n");
1232 goto cleanup_app;
1233 }
1234 ret = nf_register_hook(&ip_vs_out_ops);
1235 if (ret < 0) {
1236 IP_VS_ERR("can't register out hook.\n");
1237 goto cleanup_inops;
1238 }
1239 ret = nf_register_hook(&ip_vs_post_routing_ops);
1240 if (ret < 0) {
1241 IP_VS_ERR("can't register post_routing hook.\n");
1242 goto cleanup_outops;
1243 }
1244 ret = nf_register_hook(&ip_vs_forward_icmp_ops);
1245 if (ret < 0) {
1246 IP_VS_ERR("can't register forward_icmp hook.\n");
1247 goto cleanup_postroutingops;
1248 }
1249
1250 IP_VS_INFO("ipvs loaded.\n");
1251 return ret;
1252
1253 cleanup_postroutingops:
1254 nf_unregister_hook(&ip_vs_post_routing_ops);
1255 cleanup_outops:
1256 nf_unregister_hook(&ip_vs_out_ops);
1257 cleanup_inops:
1258 nf_unregister_hook(&ip_vs_in_ops);
1259 cleanup_app:
1260 ip_vs_app_cleanup();
1261 cleanup_conn:
1262 ip_vs_conn_cleanup();
1263 cleanup_control:
1264 ip_vs_control_cleanup();
1265 cleanup_nothing:
1266 return ret;
1267 }
1268
ip_vs_cleanup(void)1269 static void __exit ip_vs_cleanup(void)
1270 {
1271 nf_unregister_hook(&ip_vs_forward_icmp_ops);
1272 nf_unregister_hook(&ip_vs_post_routing_ops);
1273 nf_unregister_hook(&ip_vs_out_ops);
1274 nf_unregister_hook(&ip_vs_in_ops);
1275 ip_vs_app_cleanup();
1276 ip_vs_conn_cleanup();
1277 ip_vs_control_cleanup();
1278 IP_VS_INFO("ipvs unloaded.\n");
1279 }
1280
1281 module_init(ip_vs_init);
1282 module_exit(ip_vs_cleanup);
1283 MODULE_LICENSE("GPL");
1284