blob: cbd58c60e1bffd62b479de8e4a75c192a0fb2d46 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
9 * Peter Kese <peter.kese@ijs.si>
10 * Julian Anastasov <ja@ssi.bg>
11 *
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.
16 *
17 * Changes:
18 *
19 */
20
Hannes Eder9aada7a2009-07-30 14:29:44 -070021#define KMSG_COMPONENT "IPVS"
22#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
23
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080027#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/fs.h>
29#include <linux/sysctl.h>
30#include <linux/proc_fs.h>
31#include <linux/workqueue.h>
32#include <linux/swap.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/seq_file.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090034#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36#include <linux/netfilter.h>
37#include <linux/netfilter_ipv4.h>
Ingo Molnar14cc3e22006-03-26 01:37:14 -080038#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020040#include <net/net_namespace.h>
Hans Schillstrom93304192011-01-03 14:44:51 +010041#include <linux/nsproxy.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <net/ip.h>
Vince Busam09571c72008-09-02 15:55:52 +020043#ifdef CONFIG_IP_VS_IPV6
44#include <net/ipv6.h>
45#include <net/ip6_route.h>
46#endif
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020047#include <net/route.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <net/sock.h>
Julius Volz9a812192008-08-14 14:08:44 +020049#include <net/genetlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
51#include <asm/uaccess.h>
52
53#include <net/ip_vs.h>
54
55/* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */
Ingo Molnar14cc3e22006-03-26 01:37:14 -080056static DEFINE_MUTEX(__ip_vs_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58/* lock for service table */
59static DEFINE_RWLOCK(__ip_vs_svc_lock);
60
61/* lock for table with the real services */
62static DEFINE_RWLOCK(__ip_vs_rs_lock);
63
64/* lock for state and timeout tables */
Simon Horman4f728162010-08-26 02:54:30 +000065static DEFINE_SPINLOCK(ip_vs_securetcp_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67/* lock for drop entry handling */
68static DEFINE_SPINLOCK(__ip_vs_dropentry_lock);
69
70/* lock for drop packet handling */
71static DEFINE_SPINLOCK(__ip_vs_droppacket_lock);
72
73/* 1/rate drop and drop-entry variables */
74int ip_vs_drop_rate = 0;
75int ip_vs_drop_counter = 0;
76static atomic_t ip_vs_dropentry = ATOMIC_INIT(0);
77
78/* number of virtual services */
79static int ip_vs_num_services = 0;
80
81/* sysctl variables */
82static int sysctl_ip_vs_drop_entry = 0;
83static int sysctl_ip_vs_drop_packet = 0;
84static int sysctl_ip_vs_secure_tcp = 0;
85static int sysctl_ip_vs_amemthresh = 1024;
86static int sysctl_ip_vs_am_droprate = 10;
87int sysctl_ip_vs_cache_bypass = 0;
88int sysctl_ip_vs_expire_nodest_conn = 0;
89int sysctl_ip_vs_expire_quiescent_template = 0;
90int sysctl_ip_vs_sync_threshold[2] = { 3, 50 };
91int sysctl_ip_vs_nat_icmp_send = 0;
Julian Anastasovf4bc17c2010-09-21 17:35:41 +020092#ifdef CONFIG_IP_VS_NFCT
93int sysctl_ip_vs_conntrack;
94#endif
Julian Anastasov8a803042010-09-21 17:38:57 +020095int sysctl_ip_vs_snat_reroute = 1;
Hans Schillstromb880c1f2010-11-19 14:25:14 +010096int sysctl_ip_vs_sync_ver = 1; /* Default version of sync proto */
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
98#ifdef CONFIG_IP_VS_DEBUG
99static int sysctl_ip_vs_debug_level = 0;
100
101int ip_vs_get_debug_level(void)
102{
103 return sysctl_ip_vs_debug_level;
104}
105#endif
106
Vince Busam09571c72008-09-02 15:55:52 +0200107#ifdef CONFIG_IP_VS_IPV6
108/* Taken from rt6_fill_node() in net/ipv6/route.c, is there a better way? */
109static int __ip_vs_addr_is_local_v6(const struct in6_addr *addr)
110{
111 struct rt6_info *rt;
112 struct flowi fl = {
113 .oif = 0,
Changli Gao58116622010-11-12 18:43:55 +0000114 .fl6_dst = *addr,
115 .fl6_src = { .s6_addr32 = {0, 0, 0, 0} },
Vince Busam09571c72008-09-02 15:55:52 +0200116 };
117
118 rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
119 if (rt && rt->rt6i_dev && (rt->rt6i_dev->flags & IFF_LOOPBACK))
120 return 1;
121
122 return 0;
123}
124#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125/*
Julian Anastasovaf9debd2005-07-11 20:59:57 -0700126 * update_defense_level is called from keventd and from sysctl,
127 * so it needs to protect itself from softirqs
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 */
Hans Schillstrom93304192011-01-03 14:44:51 +0100129static void update_defense_level(struct netns_ipvs *ipvs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
131 struct sysinfo i;
132 static int old_secure_tcp = 0;
133 int availmem;
134 int nomem;
135 int to_change = -1;
136
137 /* we only count free and buffered memory (in pages) */
138 si_meminfo(&i);
139 availmem = i.freeram + i.bufferram;
140 /* however in linux 2.5 the i.bufferram is total page cache size,
141 we need adjust it */
142 /* si_swapinfo(&i); */
143 /* availmem = availmem - (i.totalswap - i.freeswap); */
144
145 nomem = (availmem < sysctl_ip_vs_amemthresh);
146
Julian Anastasovaf9debd2005-07-11 20:59:57 -0700147 local_bh_disable();
148
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 /* drop_entry */
150 spin_lock(&__ip_vs_dropentry_lock);
151 switch (sysctl_ip_vs_drop_entry) {
152 case 0:
153 atomic_set(&ip_vs_dropentry, 0);
154 break;
155 case 1:
156 if (nomem) {
157 atomic_set(&ip_vs_dropentry, 1);
158 sysctl_ip_vs_drop_entry = 2;
159 } else {
160 atomic_set(&ip_vs_dropentry, 0);
161 }
162 break;
163 case 2:
164 if (nomem) {
165 atomic_set(&ip_vs_dropentry, 1);
166 } else {
167 atomic_set(&ip_vs_dropentry, 0);
168 sysctl_ip_vs_drop_entry = 1;
169 };
170 break;
171 case 3:
172 atomic_set(&ip_vs_dropentry, 1);
173 break;
174 }
175 spin_unlock(&__ip_vs_dropentry_lock);
176
177 /* drop_packet */
178 spin_lock(&__ip_vs_droppacket_lock);
179 switch (sysctl_ip_vs_drop_packet) {
180 case 0:
181 ip_vs_drop_rate = 0;
182 break;
183 case 1:
184 if (nomem) {
185 ip_vs_drop_rate = ip_vs_drop_counter
186 = sysctl_ip_vs_amemthresh /
187 (sysctl_ip_vs_amemthresh-availmem);
188 sysctl_ip_vs_drop_packet = 2;
189 } else {
190 ip_vs_drop_rate = 0;
191 }
192 break;
193 case 2:
194 if (nomem) {
195 ip_vs_drop_rate = ip_vs_drop_counter
196 = sysctl_ip_vs_amemthresh /
197 (sysctl_ip_vs_amemthresh-availmem);
198 } else {
199 ip_vs_drop_rate = 0;
200 sysctl_ip_vs_drop_packet = 1;
201 }
202 break;
203 case 3:
204 ip_vs_drop_rate = sysctl_ip_vs_am_droprate;
205 break;
206 }
207 spin_unlock(&__ip_vs_droppacket_lock);
208
209 /* secure_tcp */
Simon Horman4f728162010-08-26 02:54:30 +0000210 spin_lock(&ip_vs_securetcp_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 switch (sysctl_ip_vs_secure_tcp) {
212 case 0:
213 if (old_secure_tcp >= 2)
214 to_change = 0;
215 break;
216 case 1:
217 if (nomem) {
218 if (old_secure_tcp < 2)
219 to_change = 1;
220 sysctl_ip_vs_secure_tcp = 2;
221 } else {
222 if (old_secure_tcp >= 2)
223 to_change = 0;
224 }
225 break;
226 case 2:
227 if (nomem) {
228 if (old_secure_tcp < 2)
229 to_change = 1;
230 } else {
231 if (old_secure_tcp >= 2)
232 to_change = 0;
233 sysctl_ip_vs_secure_tcp = 1;
234 }
235 break;
236 case 3:
237 if (old_secure_tcp < 2)
238 to_change = 1;
239 break;
240 }
241 old_secure_tcp = sysctl_ip_vs_secure_tcp;
242 if (to_change >= 0)
Hans Schillstrom93304192011-01-03 14:44:51 +0100243 ip_vs_protocol_timeout_change(ipvs,
244 sysctl_ip_vs_secure_tcp > 1);
Simon Horman4f728162010-08-26 02:54:30 +0000245 spin_unlock(&ip_vs_securetcp_lock);
Julian Anastasovaf9debd2005-07-11 20:59:57 -0700246
247 local_bh_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
250
251/*
252 * Timer for checking the defense
253 */
254#define DEFENSE_TIMER_PERIOD 1*HZ
David Howellsc4028952006-11-22 14:57:56 +0000255static void defense_work_handler(struct work_struct *work);
256static DECLARE_DELAYED_WORK(defense_work, defense_work_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
David Howellsc4028952006-11-22 14:57:56 +0000258static void defense_work_handler(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
Hans Schillstromb17fc992011-01-03 14:44:56 +0100260 struct netns_ipvs *ipvs = net_ipvs(&init_net);
Hans Schillstrom93304192011-01-03 14:44:51 +0100261
262 update_defense_level(ipvs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 if (atomic_read(&ip_vs_dropentry))
264 ip_vs_random_dropentry();
265
266 schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD);
267}
268
269int
270ip_vs_use_count_inc(void)
271{
272 return try_module_get(THIS_MODULE);
273}
274
275void
276ip_vs_use_count_dec(void)
277{
278 module_put(THIS_MODULE);
279}
280
281
282/*
283 * Hash table: for virtual service lookups
284 */
285#define IP_VS_SVC_TAB_BITS 8
286#define IP_VS_SVC_TAB_SIZE (1 << IP_VS_SVC_TAB_BITS)
287#define IP_VS_SVC_TAB_MASK (IP_VS_SVC_TAB_SIZE - 1)
288
289/* the service table hashed by <protocol, addr, port> */
290static struct list_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE];
291/* the service table hashed by fwmark */
292static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE];
293
294/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 * Trash for destinations
296 */
297static LIST_HEAD(ip_vs_dest_trash);
298
299/*
300 * FTP & NULL virtual service counters
301 */
302static atomic_t ip_vs_ftpsvc_counter = ATOMIC_INIT(0);
303static atomic_t ip_vs_nullsvc_counter = ATOMIC_INIT(0);
304
305
306/*
307 * Returns hash value for virtual service
308 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100309static inline unsigned
310ip_vs_svc_hashkey(struct net *net, int af, unsigned proto,
311 const union nf_inet_addr *addr, __be16 port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
313 register unsigned porth = ntohs(port);
Julius Volzb18610d2008-09-02 15:55:37 +0200314 __be32 addr_fold = addr->ip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Julius Volzb18610d2008-09-02 15:55:37 +0200316#ifdef CONFIG_IP_VS_IPV6
317 if (af == AF_INET6)
318 addr_fold = addr->ip6[0]^addr->ip6[1]^
319 addr->ip6[2]^addr->ip6[3];
320#endif
Hans Schillstromfc723252011-01-03 14:44:43 +0100321 addr_fold ^= ((size_t)net>>8);
Julius Volzb18610d2008-09-02 15:55:37 +0200322
323 return (proto^ntohl(addr_fold)^(porth>>IP_VS_SVC_TAB_BITS)^porth)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 & IP_VS_SVC_TAB_MASK;
325}
326
327/*
328 * Returns hash value of fwmark for virtual service lookup
329 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100330static inline unsigned ip_vs_svc_fwm_hashkey(struct net *net, __u32 fwmark)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331{
Hans Schillstromfc723252011-01-03 14:44:43 +0100332 return (((size_t)net>>8) ^ fwmark) & IP_VS_SVC_TAB_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
335/*
Hans Schillstromfc723252011-01-03 14:44:43 +0100336 * Hashes a service in the ip_vs_svc_table by <netns,proto,addr,port>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 * or in the ip_vs_svc_fwm_table by fwmark.
338 * Should be called with locked tables.
339 */
340static int ip_vs_svc_hash(struct ip_vs_service *svc)
341{
342 unsigned hash;
343
344 if (svc->flags & IP_VS_SVC_F_HASHED) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000345 pr_err("%s(): request for already hashed, called from %pF\n",
346 __func__, __builtin_return_address(0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 return 0;
348 }
349
350 if (svc->fwmark == 0) {
351 /*
Hans Schillstromfc723252011-01-03 14:44:43 +0100352 * Hash it by <netns,protocol,addr,port> in ip_vs_svc_table
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100354 hash = ip_vs_svc_hashkey(svc->net, svc->af, svc->protocol,
355 &svc->addr, svc->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 list_add(&svc->s_list, &ip_vs_svc_table[hash]);
357 } else {
358 /*
Hans Schillstromfc723252011-01-03 14:44:43 +0100359 * Hash it by fwmark in svc_fwm_table
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100361 hash = ip_vs_svc_fwm_hashkey(svc->net, svc->fwmark);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 list_add(&svc->f_list, &ip_vs_svc_fwm_table[hash]);
363 }
364
365 svc->flags |= IP_VS_SVC_F_HASHED;
366 /* increase its refcnt because it is referenced by the svc table */
367 atomic_inc(&svc->refcnt);
368 return 1;
369}
370
371
372/*
Hans Schillstromfc723252011-01-03 14:44:43 +0100373 * Unhashes a service from svc_table / svc_fwm_table.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 * Should be called with locked tables.
375 */
376static int ip_vs_svc_unhash(struct ip_vs_service *svc)
377{
378 if (!(svc->flags & IP_VS_SVC_F_HASHED)) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000379 pr_err("%s(): request for unhash flagged, called from %pF\n",
380 __func__, __builtin_return_address(0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 return 0;
382 }
383
384 if (svc->fwmark == 0) {
Hans Schillstromfc723252011-01-03 14:44:43 +0100385 /* Remove it from the svc_table table */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 list_del(&svc->s_list);
387 } else {
Hans Schillstromfc723252011-01-03 14:44:43 +0100388 /* Remove it from the svc_fwm_table table */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 list_del(&svc->f_list);
390 }
391
392 svc->flags &= ~IP_VS_SVC_F_HASHED;
393 atomic_dec(&svc->refcnt);
394 return 1;
395}
396
397
398/*
Hans Schillstromfc723252011-01-03 14:44:43 +0100399 * Get service by {netns, proto,addr,port} in the service table.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 */
Julius Volzb18610d2008-09-02 15:55:37 +0200401static inline struct ip_vs_service *
Hans Schillstromfc723252011-01-03 14:44:43 +0100402__ip_vs_service_find(struct net *net, int af, __u16 protocol,
403 const union nf_inet_addr *vaddr, __be16 vport)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404{
405 unsigned hash;
406 struct ip_vs_service *svc;
407
408 /* Check for "full" addressed entries */
Hans Schillstromfc723252011-01-03 14:44:43 +0100409 hash = ip_vs_svc_hashkey(net, af, protocol, vaddr, vport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411 list_for_each_entry(svc, &ip_vs_svc_table[hash], s_list){
Julius Volzb18610d2008-09-02 15:55:37 +0200412 if ((svc->af == af)
413 && ip_vs_addr_equal(af, &svc->addr, vaddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 && (svc->port == vport)
Hans Schillstromfc723252011-01-03 14:44:43 +0100415 && (svc->protocol == protocol)
416 && net_eq(svc->net, net)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 /* HIT */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 return svc;
419 }
420 }
421
422 return NULL;
423}
424
425
426/*
427 * Get service by {fwmark} in the service table.
428 */
Julius Volzb18610d2008-09-02 15:55:37 +0200429static inline struct ip_vs_service *
Hans Schillstromfc723252011-01-03 14:44:43 +0100430__ip_vs_svc_fwm_find(struct net *net, int af, __u32 fwmark)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
432 unsigned hash;
433 struct ip_vs_service *svc;
434
435 /* Check for fwmark addressed entries */
Hans Schillstromfc723252011-01-03 14:44:43 +0100436 hash = ip_vs_svc_fwm_hashkey(net, fwmark);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
438 list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +0100439 if (svc->fwmark == fwmark && svc->af == af
440 && net_eq(svc->net, net)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 /* HIT */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 return svc;
443 }
444 }
445
446 return NULL;
447}
448
449struct ip_vs_service *
Hans Schillstromfc723252011-01-03 14:44:43 +0100450ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol,
Julius Volz3c2e0502008-09-02 15:55:38 +0200451 const union nf_inet_addr *vaddr, __be16 vport)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
453 struct ip_vs_service *svc;
Julius Volz3c2e0502008-09-02 15:55:38 +0200454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 read_lock(&__ip_vs_svc_lock);
456
457 /*
458 * Check the table hashed by fwmark first
459 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100460 svc = __ip_vs_svc_fwm_find(net, af, fwmark);
461 if (fwmark && svc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 goto out;
463
464 /*
465 * Check the table hashed by <protocol,addr,port>
466 * for "full" addressed entries
467 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100468 svc = __ip_vs_service_find(net, af, protocol, vaddr, vport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
470 if (svc == NULL
471 && protocol == IPPROTO_TCP
472 && atomic_read(&ip_vs_ftpsvc_counter)
473 && (vport == FTPDATA || ntohs(vport) >= PROT_SOCK)) {
474 /*
475 * Check if ftp service entry exists, the packet
476 * might belong to FTP data connections.
477 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100478 svc = __ip_vs_service_find(net, af, protocol, vaddr, FTPPORT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 }
480
481 if (svc == NULL
482 && atomic_read(&ip_vs_nullsvc_counter)) {
483 /*
484 * Check if the catch-all port (port zero) exists
485 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100486 svc = __ip_vs_service_find(net, af, protocol, vaddr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 }
488
489 out:
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200490 if (svc)
491 atomic_inc(&svc->usecnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 read_unlock(&__ip_vs_svc_lock);
493
Julius Volz3c2e0502008-09-02 15:55:38 +0200494 IP_VS_DBG_BUF(9, "lookup service: fwm %u %s %s:%u %s\n",
495 fwmark, ip_vs_proto_name(protocol),
496 IP_VS_DBG_ADDR(af, vaddr), ntohs(vport),
497 svc ? "hit" : "not hit");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499 return svc;
500}
501
502
503static inline void
504__ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc)
505{
506 atomic_inc(&svc->refcnt);
507 dest->svc = svc;
508}
509
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200510static void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511__ip_vs_unbind_svc(struct ip_vs_dest *dest)
512{
513 struct ip_vs_service *svc = dest->svc;
514
515 dest->svc = NULL;
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200516 if (atomic_dec_and_test(&svc->refcnt)) {
517 IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n",
518 svc->fwmark,
519 IP_VS_DBG_ADDR(svc->af, &svc->addr),
520 ntohs(svc->port), atomic_read(&svc->usecnt));
Hans Schillstromb17fc992011-01-03 14:44:56 +0100521 free_percpu(svc->stats.cpustats);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 kfree(svc);
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200523 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524}
525
526
527/*
528 * Returns hash value for real service
529 */
Julius Volz7937df12008-09-02 15:55:48 +0200530static inline unsigned ip_vs_rs_hashkey(int af,
531 const union nf_inet_addr *addr,
532 __be16 port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533{
534 register unsigned porth = ntohs(port);
Julius Volz7937df12008-09-02 15:55:48 +0200535 __be32 addr_fold = addr->ip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Julius Volz7937df12008-09-02 15:55:48 +0200537#ifdef CONFIG_IP_VS_IPV6
538 if (af == AF_INET6)
539 addr_fold = addr->ip6[0]^addr->ip6[1]^
540 addr->ip6[2]^addr->ip6[3];
541#endif
542
543 return (ntohl(addr_fold)^(porth>>IP_VS_RTAB_BITS)^porth)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 & IP_VS_RTAB_MASK;
545}
546
547/*
Hans Schillstromfc723252011-01-03 14:44:43 +0100548 * Hashes ip_vs_dest in rs_table by <proto,addr,port>.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 * should be called with locked tables.
550 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100551static int ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
553 unsigned hash;
554
555 if (!list_empty(&dest->d_list)) {
556 return 0;
557 }
558
559 /*
560 * Hash by proto,addr,port,
561 * which are the parameters of the real service.
562 */
Julius Volz7937df12008-09-02 15:55:48 +0200563 hash = ip_vs_rs_hashkey(dest->af, &dest->addr, dest->port);
564
Hans Schillstromfc723252011-01-03 14:44:43 +0100565 list_add(&dest->d_list, &ipvs->rs_table[hash]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
567 return 1;
568}
569
570/*
Hans Schillstromfc723252011-01-03 14:44:43 +0100571 * UNhashes ip_vs_dest from rs_table.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 * should be called with locked tables.
573 */
574static int ip_vs_rs_unhash(struct ip_vs_dest *dest)
575{
576 /*
Hans Schillstromfc723252011-01-03 14:44:43 +0100577 * Remove it from the rs_table table.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 */
579 if (!list_empty(&dest->d_list)) {
580 list_del(&dest->d_list);
581 INIT_LIST_HEAD(&dest->d_list);
582 }
583
584 return 1;
585}
586
587/*
588 * Lookup real service by <proto,addr,port> in the real service table.
589 */
590struct ip_vs_dest *
Hans Schillstromfc723252011-01-03 14:44:43 +0100591ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol,
Julius Volz7937df12008-09-02 15:55:48 +0200592 const union nf_inet_addr *daddr,
593 __be16 dport)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
Hans Schillstromfc723252011-01-03 14:44:43 +0100595 struct netns_ipvs *ipvs = net_ipvs(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 unsigned hash;
597 struct ip_vs_dest *dest;
598
599 /*
600 * Check for "full" addressed entries
601 * Return the first found entry
602 */
Julius Volz7937df12008-09-02 15:55:48 +0200603 hash = ip_vs_rs_hashkey(af, daddr, dport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
605 read_lock(&__ip_vs_rs_lock);
Hans Schillstromfc723252011-01-03 14:44:43 +0100606 list_for_each_entry(dest, &ipvs->rs_table[hash], d_list) {
Julius Volz7937df12008-09-02 15:55:48 +0200607 if ((dest->af == af)
608 && ip_vs_addr_equal(af, &dest->addr, daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 && (dest->port == dport)
610 && ((dest->protocol == protocol) ||
611 dest->vfwmark)) {
612 /* HIT */
613 read_unlock(&__ip_vs_rs_lock);
614 return dest;
615 }
616 }
617 read_unlock(&__ip_vs_rs_lock);
618
619 return NULL;
620}
621
622/*
623 * Lookup destination by {addr,port} in the given service
624 */
625static struct ip_vs_dest *
Julius Volz7937df12008-09-02 15:55:48 +0200626ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
627 __be16 dport)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
629 struct ip_vs_dest *dest;
630
631 /*
632 * Find the destination for the given service
633 */
634 list_for_each_entry(dest, &svc->destinations, n_list) {
Julius Volz7937df12008-09-02 15:55:48 +0200635 if ((dest->af == svc->af)
636 && ip_vs_addr_equal(svc->af, &dest->addr, daddr)
637 && (dest->port == dport)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 /* HIT */
639 return dest;
640 }
641 }
642
643 return NULL;
644}
645
Rumen G. Bogdanovski1e356f92007-11-07 02:35:54 -0800646/*
647 * Find destination by {daddr,dport,vaddr,protocol}
648 * Cretaed to be used in ip_vs_process_message() in
649 * the backup synchronization daemon. It finds the
650 * destination to be bound to the received connection
651 * on the backup.
652 *
653 * ip_vs_lookup_real_service() looked promissing, but
654 * seems not working as expected.
655 */
Hans Schillstromfc723252011-01-03 14:44:43 +0100656struct ip_vs_dest *ip_vs_find_dest(struct net *net, int af,
657 const union nf_inet_addr *daddr,
Julius Volz7937df12008-09-02 15:55:48 +0200658 __be16 dport,
659 const union nf_inet_addr *vaddr,
Hans Schillstrom0e051e62010-11-19 14:25:07 +0100660 __be16 vport, __u16 protocol, __u32 fwmark)
Rumen G. Bogdanovski1e356f92007-11-07 02:35:54 -0800661{
662 struct ip_vs_dest *dest;
663 struct ip_vs_service *svc;
664
Hans Schillstromfc723252011-01-03 14:44:43 +0100665 svc = ip_vs_service_get(net, af, fwmark, protocol, vaddr, vport);
Rumen G. Bogdanovski1e356f92007-11-07 02:35:54 -0800666 if (!svc)
667 return NULL;
668 dest = ip_vs_lookup_dest(svc, daddr, dport);
669 if (dest)
670 atomic_inc(&dest->refcnt);
671 ip_vs_service_put(svc);
672 return dest;
673}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675/*
676 * Lookup dest by {svc,addr,port} in the destination trash.
677 * The destination trash is used to hold the destinations that are removed
678 * from the service table but are still referenced by some conn entries.
679 * The reason to add the destination trash is when the dest is temporary
680 * down (either by administrator or by monitor program), the dest can be
681 * picked back from the trash, the remaining connections to the dest can
682 * continue, and the counting information of the dest is also useful for
683 * scheduling.
684 */
685static struct ip_vs_dest *
Julius Volz7937df12008-09-02 15:55:48 +0200686ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
687 __be16 dport)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
689 struct ip_vs_dest *dest, *nxt;
690
691 /*
692 * Find the destination in trash
693 */
694 list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) {
Julius Volz7937df12008-09-02 15:55:48 +0200695 IP_VS_DBG_BUF(3, "Destination %u/%s:%u still in trash, "
696 "dest->refcnt=%d\n",
697 dest->vfwmark,
698 IP_VS_DBG_ADDR(svc->af, &dest->addr),
699 ntohs(dest->port),
700 atomic_read(&dest->refcnt));
701 if (dest->af == svc->af &&
702 ip_vs_addr_equal(svc->af, &dest->addr, daddr) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 dest->port == dport &&
704 dest->vfwmark == svc->fwmark &&
705 dest->protocol == svc->protocol &&
706 (svc->fwmark ||
Julius Volz7937df12008-09-02 15:55:48 +0200707 (ip_vs_addr_equal(svc->af, &dest->vaddr, &svc->addr) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 dest->vport == svc->port))) {
709 /* HIT */
710 return dest;
711 }
712
713 /*
714 * Try to purge the destination from trash if not referenced
715 */
716 if (atomic_read(&dest->refcnt) == 1) {
Julius Volz7937df12008-09-02 15:55:48 +0200717 IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u "
718 "from trash\n",
719 dest->vfwmark,
720 IP_VS_DBG_ADDR(svc->af, &dest->addr),
721 ntohs(dest->port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 list_del(&dest->n_list);
723 ip_vs_dst_reset(dest);
724 __ip_vs_unbind_svc(dest);
Hans Schillstromb17fc992011-01-03 14:44:56 +0100725 free_percpu(dest->stats.cpustats);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 kfree(dest);
727 }
728 }
729
730 return NULL;
731}
732
733
734/*
735 * Clean up all the destinations in the trash
736 * Called by the ip_vs_control_cleanup()
737 *
738 * When the ip_vs_control_clearup is activated by ipvs module exit,
739 * the service tables must have been flushed and all the connections
740 * are expired, and the refcnt of each destination in the trash must
741 * be 1, so we simply release them here.
742 */
743static void ip_vs_trash_cleanup(void)
744{
745 struct ip_vs_dest *dest, *nxt;
746
747 list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) {
748 list_del(&dest->n_list);
749 ip_vs_dst_reset(dest);
750 __ip_vs_unbind_svc(dest);
Hans Schillstromb17fc992011-01-03 14:44:56 +0100751 free_percpu(dest->stats.cpustats);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 kfree(dest);
753 }
754}
755
756
757static void
758ip_vs_zero_stats(struct ip_vs_stats *stats)
759{
760 spin_lock_bh(&stats->lock);
Simon Hormane93615d2008-08-11 17:19:14 +1000761
Sven Wegenere9c0ce22008-09-08 13:39:04 +0200762 memset(&stats->ustats, 0, sizeof(stats->ustats));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 ip_vs_zero_estimator(stats);
Simon Hormane93615d2008-08-11 17:19:14 +1000764
Sven Wegener3a14a3132008-08-10 18:24:41 +0000765 spin_unlock_bh(&stats->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766}
767
768/*
769 * Update a destination in the given service
770 */
771static void
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200772__ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
773 struct ip_vs_dest_user_kern *udest, int add)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
Hans Schillstromfc723252011-01-03 14:44:43 +0100775 struct netns_ipvs *ipvs = net_ipvs(svc->net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 int conn_flags;
777
778 /* set the weight and the flags */
779 atomic_set(&dest->weight, udest->weight);
Julian Anastasov35757922010-09-17 14:18:16 +0200780 conn_flags = udest->conn_flags & IP_VS_CONN_F_DEST_MASK;
781 conn_flags |= IP_VS_CONN_F_INACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 /* set the IP_VS_CONN_F_NOOUTPUT flag if not masquerading/NAT */
Julian Anastasov35757922010-09-17 14:18:16 +0200784 if ((conn_flags & IP_VS_CONN_F_FWD_MASK) != IP_VS_CONN_F_MASQ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 conn_flags |= IP_VS_CONN_F_NOOUTPUT;
786 } else {
787 /*
Hans Schillstromfc723252011-01-03 14:44:43 +0100788 * Put the real service in rs_table if not present.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 * For now only for NAT!
790 */
791 write_lock_bh(&__ip_vs_rs_lock);
Hans Schillstromfc723252011-01-03 14:44:43 +0100792 ip_vs_rs_hash(ipvs, dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 write_unlock_bh(&__ip_vs_rs_lock);
794 }
795 atomic_set(&dest->conn_flags, conn_flags);
796
797 /* bind the service */
798 if (!dest->svc) {
799 __ip_vs_bind_svc(dest, svc);
800 } else {
801 if (dest->svc != svc) {
802 __ip_vs_unbind_svc(dest);
803 ip_vs_zero_stats(&dest->stats);
804 __ip_vs_bind_svc(dest, svc);
805 }
806 }
807
808 /* set the dest status flags */
809 dest->flags |= IP_VS_DEST_F_AVAILABLE;
810
811 if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold)
812 dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
813 dest->u_threshold = udest->u_threshold;
814 dest->l_threshold = udest->l_threshold;
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200815
Julian Anastasovfc604762010-10-17 16:38:15 +0300816 spin_lock(&dest->dst_lock);
817 ip_vs_dst_reset(dest);
818 spin_unlock(&dest->dst_lock);
819
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200820 if (add)
Hans Schillstrom29c20262011-01-03 14:44:54 +0100821 ip_vs_new_estimator(svc->net, &dest->stats);
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200822
823 write_lock_bh(&__ip_vs_svc_lock);
824
825 /* Wait until all other svc users go away */
826 IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
827
828 if (add) {
829 list_add(&dest->n_list, &svc->destinations);
830 svc->num_dests++;
831 }
832
833 /* call the update_service, because server weight may be changed */
834 if (svc->scheduler->update_service)
835 svc->scheduler->update_service(svc);
836
837 write_unlock_bh(&__ip_vs_svc_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838}
839
840
841/*
842 * Create a destination for the given service
843 */
844static int
Julius Volzc860c6b2008-09-02 15:55:36 +0200845ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 struct ip_vs_dest **dest_p)
847{
848 struct ip_vs_dest *dest;
849 unsigned atype;
850
851 EnterFunction(2);
852
Vince Busam09571c72008-09-02 15:55:52 +0200853#ifdef CONFIG_IP_VS_IPV6
854 if (svc->af == AF_INET6) {
855 atype = ipv6_addr_type(&udest->addr.in6);
Sven Wegener3bfb92f2008-09-05 16:53:49 +0200856 if ((!(atype & IPV6_ADDR_UNICAST) ||
857 atype & IPV6_ADDR_LINKLOCAL) &&
Vince Busam09571c72008-09-02 15:55:52 +0200858 !__ip_vs_addr_is_local_v6(&udest->addr.in6))
859 return -EINVAL;
860 } else
861#endif
862 {
863 atype = inet_addr_type(&init_net, udest->addr.ip);
864 if (atype != RTN_LOCAL && atype != RTN_UNICAST)
865 return -EINVAL;
866 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Simon Hormandee06e42010-08-26 02:54:31 +0000868 dest = kzalloc(sizeof(struct ip_vs_dest), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 if (dest == NULL) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000870 pr_err("%s(): no memory.\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 return -ENOMEM;
872 }
Hans Schillstromb17fc992011-01-03 14:44:56 +0100873 dest->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats);
874 if (!dest->stats.cpustats) {
875 pr_err("%s() alloc_percpu failed\n", __func__);
876 goto err_alloc;
877 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Julius Volzc860c6b2008-09-02 15:55:36 +0200879 dest->af = svc->af;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 dest->protocol = svc->protocol;
Julius Volzc860c6b2008-09-02 15:55:36 +0200881 dest->vaddr = svc->addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 dest->vport = svc->port;
883 dest->vfwmark = svc->fwmark;
Julius Volzc860c6b2008-09-02 15:55:36 +0200884 ip_vs_addr_copy(svc->af, &dest->addr, &udest->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 dest->port = udest->port;
886
887 atomic_set(&dest->activeconns, 0);
888 atomic_set(&dest->inactconns, 0);
889 atomic_set(&dest->persistconns, 0);
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200890 atomic_set(&dest->refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
892 INIT_LIST_HEAD(&dest->d_list);
893 spin_lock_init(&dest->dst_lock);
894 spin_lock_init(&dest->stats.lock);
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200895 __ip_vs_update_dest(svc, dest, udest, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
897 *dest_p = dest;
898
899 LeaveFunction(2);
900 return 0;
Hans Schillstromb17fc992011-01-03 14:44:56 +0100901
902err_alloc:
903 kfree(dest);
904 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905}
906
907
908/*
909 * Add a destination into an existing service
910 */
911static int
Julius Volzc860c6b2008-09-02 15:55:36 +0200912ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913{
914 struct ip_vs_dest *dest;
Julius Volzc860c6b2008-09-02 15:55:36 +0200915 union nf_inet_addr daddr;
Al Viro014d7302006-09-28 14:29:52 -0700916 __be16 dport = udest->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 int ret;
918
919 EnterFunction(2);
920
921 if (udest->weight < 0) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000922 pr_err("%s(): server weight less than zero\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 return -ERANGE;
924 }
925
926 if (udest->l_threshold > udest->u_threshold) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000927 pr_err("%s(): lower threshold is higher than upper threshold\n",
928 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 return -ERANGE;
930 }
931
Julius Volzc860c6b2008-09-02 15:55:36 +0200932 ip_vs_addr_copy(svc->af, &daddr, &udest->addr);
933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 /*
935 * Check if the dest already exists in the list
936 */
Julius Volz7937df12008-09-02 15:55:48 +0200937 dest = ip_vs_lookup_dest(svc, &daddr, dport);
938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 if (dest != NULL) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000940 IP_VS_DBG(1, "%s(): dest already exists\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 return -EEXIST;
942 }
943
944 /*
945 * Check if the dest already exists in the trash and
946 * is from the same service
947 */
Julius Volz7937df12008-09-02 15:55:48 +0200948 dest = ip_vs_trash_get_dest(svc, &daddr, dport);
949
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 if (dest != NULL) {
Julius Volzcfc78c52008-09-02 15:55:53 +0200951 IP_VS_DBG_BUF(3, "Get destination %s:%u from trash, "
952 "dest->refcnt=%d, service %u/%s:%u\n",
953 IP_VS_DBG_ADDR(svc->af, &daddr), ntohs(dport),
954 atomic_read(&dest->refcnt),
955 dest->vfwmark,
956 IP_VS_DBG_ADDR(svc->af, &dest->vaddr),
957 ntohs(dest->vport));
958
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 /*
960 * Get the destination from the trash
961 */
962 list_del(&dest->n_list);
963
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200964 __ip_vs_update_dest(svc, dest, udest, 1);
965 ret = 0;
966 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 /*
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200968 * Allocate and initialize the dest structure
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 */
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200970 ret = ip_vs_new_dest(svc, udest, &dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 LeaveFunction(2);
973
Julian Anastasov26c15cf2010-09-21 18:12:30 +0200974 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975}
976
977
978/*
979 * Edit a destination in the given service
980 */
981static int
Julius Volzc860c6b2008-09-02 15:55:36 +0200982ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983{
984 struct ip_vs_dest *dest;
Julius Volzc860c6b2008-09-02 15:55:36 +0200985 union nf_inet_addr daddr;
Al Viro014d7302006-09-28 14:29:52 -0700986 __be16 dport = udest->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
988 EnterFunction(2);
989
990 if (udest->weight < 0) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000991 pr_err("%s(): server weight less than zero\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 return -ERANGE;
993 }
994
995 if (udest->l_threshold > udest->u_threshold) {
Hannes Eder1e3e2382009-08-02 11:05:41 +0000996 pr_err("%s(): lower threshold is higher than upper threshold\n",
997 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 return -ERANGE;
999 }
1000
Julius Volzc860c6b2008-09-02 15:55:36 +02001001 ip_vs_addr_copy(svc->af, &daddr, &udest->addr);
1002
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 /*
1004 * Lookup the destination list
1005 */
Julius Volz7937df12008-09-02 15:55:48 +02001006 dest = ip_vs_lookup_dest(svc, &daddr, dport);
1007
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 if (dest == NULL) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00001009 IP_VS_DBG(1, "%s(): dest doesn't exist\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 return -ENOENT;
1011 }
1012
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001013 __ip_vs_update_dest(svc, dest, udest, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 LeaveFunction(2);
1015
1016 return 0;
1017}
1018
1019
1020/*
1021 * Delete a destination (must be already unlinked from the service)
1022 */
Hans Schillstrom29c20262011-01-03 14:44:54 +01001023static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024{
Hans Schillstrom29c20262011-01-03 14:44:54 +01001025 ip_vs_kill_estimator(net, &dest->stats);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
1027 /*
1028 * Remove it from the d-linked list with the real services.
1029 */
1030 write_lock_bh(&__ip_vs_rs_lock);
1031 ip_vs_rs_unhash(dest);
1032 write_unlock_bh(&__ip_vs_rs_lock);
1033
1034 /*
1035 * Decrease the refcnt of the dest, and free the dest
1036 * if nobody refers to it (refcnt=0). Otherwise, throw
1037 * the destination into the trash.
1038 */
1039 if (atomic_dec_and_test(&dest->refcnt)) {
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001040 IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u\n",
1041 dest->vfwmark,
1042 IP_VS_DBG_ADDR(dest->af, &dest->addr),
1043 ntohs(dest->port));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 ip_vs_dst_reset(dest);
1045 /* simply decrease svc->refcnt here, let the caller check
1046 and release the service if nobody refers to it.
1047 Only user context can release destination and service,
1048 and only one user context can update virtual service at a
1049 time, so the operation here is OK */
1050 atomic_dec(&dest->svc->refcnt);
Hans Schillstromb17fc992011-01-03 14:44:56 +01001051 free_percpu(dest->stats.cpustats);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 kfree(dest);
1053 } else {
Julius Volzcfc78c52008-09-02 15:55:53 +02001054 IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, "
1055 "dest->refcnt=%d\n",
1056 IP_VS_DBG_ADDR(dest->af, &dest->addr),
1057 ntohs(dest->port),
1058 atomic_read(&dest->refcnt));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 list_add(&dest->n_list, &ip_vs_dest_trash);
1060 atomic_inc(&dest->refcnt);
1061 }
1062}
1063
1064
1065/*
1066 * Unlink a destination from the given service
1067 */
1068static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
1069 struct ip_vs_dest *dest,
1070 int svcupd)
1071{
1072 dest->flags &= ~IP_VS_DEST_F_AVAILABLE;
1073
1074 /*
1075 * Remove it from the d-linked destination list.
1076 */
1077 list_del(&dest->n_list);
1078 svc->num_dests--;
Sven Wegener82dfb6f2008-08-11 19:36:06 +00001079
1080 /*
1081 * Call the update_service function of its scheduler
1082 */
1083 if (svcupd && svc->scheduler->update_service)
1084 svc->scheduler->update_service(svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085}
1086
1087
1088/*
1089 * Delete a destination server in the given service
1090 */
1091static int
Julius Volzc860c6b2008-09-02 15:55:36 +02001092ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093{
1094 struct ip_vs_dest *dest;
Hans Schillstrom29c20262011-01-03 14:44:54 +01001095 struct net *net = svc->net;
Al Viro014d7302006-09-28 14:29:52 -07001096 __be16 dport = udest->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097
1098 EnterFunction(2);
1099
Julius Volz7937df12008-09-02 15:55:48 +02001100 dest = ip_vs_lookup_dest(svc, &udest->addr, dport);
Julius Volzc860c6b2008-09-02 15:55:36 +02001101
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 if (dest == NULL) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00001103 IP_VS_DBG(1, "%s(): destination not found!\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 return -ENOENT;
1105 }
1106
1107 write_lock_bh(&__ip_vs_svc_lock);
1108
1109 /*
1110 * Wait until all other svc users go away.
1111 */
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001112 IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
1114 /*
1115 * Unlink dest from the service
1116 */
1117 __ip_vs_unlink_dest(svc, dest, 1);
1118
1119 write_unlock_bh(&__ip_vs_svc_lock);
1120
1121 /*
1122 * Delete the destination
1123 */
Hans Schillstrom29c20262011-01-03 14:44:54 +01001124 __ip_vs_del_dest(net, dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
1126 LeaveFunction(2);
1127
1128 return 0;
1129}
1130
1131
1132/*
1133 * Add a service into the service hash table
1134 */
1135static int
Hans Schillstromfc723252011-01-03 14:44:43 +01001136ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
Julius Volzc860c6b2008-09-02 15:55:36 +02001137 struct ip_vs_service **svc_p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138{
1139 int ret = 0;
1140 struct ip_vs_scheduler *sched = NULL;
Simon Horman0d1e71b2010-08-22 21:37:54 +09001141 struct ip_vs_pe *pe = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 struct ip_vs_service *svc = NULL;
1143
1144 /* increase the module use count */
1145 ip_vs_use_count_inc();
1146
1147 /* Lookup the scheduler by 'u->sched_name' */
1148 sched = ip_vs_scheduler_get(u->sched_name);
1149 if (sched == NULL) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00001150 pr_info("Scheduler module ip_vs_%s not found\n", u->sched_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 ret = -ENOENT;
Simon Horman6e08bfb2010-08-22 21:37:52 +09001152 goto out_err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 }
1154
Simon Horman0d1e71b2010-08-22 21:37:54 +09001155 if (u->pe_name && *u->pe_name) {
Simon Hormane9e5eee2010-11-08 20:05:57 +09001156 pe = ip_vs_pe_getbyname(u->pe_name);
Simon Horman0d1e71b2010-08-22 21:37:54 +09001157 if (pe == NULL) {
1158 pr_info("persistence engine module ip_vs_pe_%s "
1159 "not found\n", u->pe_name);
1160 ret = -ENOENT;
1161 goto out_err;
1162 }
1163 }
1164
Julius Volzf94fd042008-09-02 15:55:55 +02001165#ifdef CONFIG_IP_VS_IPV6
Julius Volz48148932008-11-03 17:08:56 -08001166 if (u->af == AF_INET6 && (u->netmask < 1 || u->netmask > 128)) {
1167 ret = -EINVAL;
1168 goto out_err;
Julius Volzf94fd042008-09-02 15:55:55 +02001169 }
1170#endif
1171
Simon Hormandee06e42010-08-26 02:54:31 +00001172 svc = kzalloc(sizeof(struct ip_vs_service), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 if (svc == NULL) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00001174 IP_VS_DBG(1, "%s(): no memory\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 ret = -ENOMEM;
1176 goto out_err;
1177 }
Hans Schillstromb17fc992011-01-03 14:44:56 +01001178 svc->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats);
1179 if (!svc->stats.cpustats) {
1180 pr_err("%s() alloc_percpu failed\n", __func__);
1181 goto out_err;
1182 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183
1184 /* I'm the first user of the service */
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001185 atomic_set(&svc->usecnt, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 atomic_set(&svc->refcnt, 0);
1187
Julius Volzc860c6b2008-09-02 15:55:36 +02001188 svc->af = u->af;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 svc->protocol = u->protocol;
Julius Volzc860c6b2008-09-02 15:55:36 +02001190 ip_vs_addr_copy(svc->af, &svc->addr, &u->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 svc->port = u->port;
1192 svc->fwmark = u->fwmark;
1193 svc->flags = u->flags;
1194 svc->timeout = u->timeout * HZ;
1195 svc->netmask = u->netmask;
Hans Schillstromfc723252011-01-03 14:44:43 +01001196 svc->net = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197
1198 INIT_LIST_HEAD(&svc->destinations);
1199 rwlock_init(&svc->sched_lock);
1200 spin_lock_init(&svc->stats.lock);
1201
1202 /* Bind the scheduler */
1203 ret = ip_vs_bind_scheduler(svc, sched);
1204 if (ret)
1205 goto out_err;
1206 sched = NULL;
1207
Simon Horman0d1e71b2010-08-22 21:37:54 +09001208 /* Bind the ct retriever */
1209 ip_vs_bind_pe(svc, pe);
1210 pe = NULL;
1211
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 /* Update the virtual service counters */
1213 if (svc->port == FTPPORT)
1214 atomic_inc(&ip_vs_ftpsvc_counter);
1215 else if (svc->port == 0)
1216 atomic_inc(&ip_vs_nullsvc_counter);
1217
Hans Schillstrom29c20262011-01-03 14:44:54 +01001218 ip_vs_new_estimator(net, &svc->stats);
Julius Volzf94fd042008-09-02 15:55:55 +02001219
1220 /* Count only IPv4 services for old get/setsockopt interface */
1221 if (svc->af == AF_INET)
1222 ip_vs_num_services++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223
1224 /* Hash the service into the service table */
1225 write_lock_bh(&__ip_vs_svc_lock);
1226 ip_vs_svc_hash(svc);
1227 write_unlock_bh(&__ip_vs_svc_lock);
1228
1229 *svc_p = svc;
1230 return 0;
1231
Hans Schillstromb17fc992011-01-03 14:44:56 +01001232
Simon Horman6e08bfb2010-08-22 21:37:52 +09001233 out_err:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 if (svc != NULL) {
Simon Horman2fabf352010-08-22 21:37:52 +09001235 ip_vs_unbind_scheduler(svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 if (svc->inc) {
1237 local_bh_disable();
1238 ip_vs_app_inc_put(svc->inc);
1239 local_bh_enable();
1240 }
Hans Schillstromb17fc992011-01-03 14:44:56 +01001241 if (svc->stats.cpustats)
1242 free_percpu(svc->stats.cpustats);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 kfree(svc);
1244 }
1245 ip_vs_scheduler_put(sched);
Simon Horman0d1e71b2010-08-22 21:37:54 +09001246 ip_vs_pe_put(pe);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 /* decrease the module use count */
1249 ip_vs_use_count_dec();
1250
1251 return ret;
1252}
1253
1254
1255/*
1256 * Edit a service and bind it with a new scheduler
1257 */
1258static int
Julius Volzc860c6b2008-09-02 15:55:36 +02001259ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260{
1261 struct ip_vs_scheduler *sched, *old_sched;
Simon Horman0d1e71b2010-08-22 21:37:54 +09001262 struct ip_vs_pe *pe = NULL, *old_pe = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 int ret = 0;
1264
1265 /*
1266 * Lookup the scheduler, by 'u->sched_name'
1267 */
1268 sched = ip_vs_scheduler_get(u->sched_name);
1269 if (sched == NULL) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00001270 pr_info("Scheduler module ip_vs_%s not found\n", u->sched_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 return -ENOENT;
1272 }
1273 old_sched = sched;
1274
Simon Horman0d1e71b2010-08-22 21:37:54 +09001275 if (u->pe_name && *u->pe_name) {
Simon Hormane9e5eee2010-11-08 20:05:57 +09001276 pe = ip_vs_pe_getbyname(u->pe_name);
Simon Horman0d1e71b2010-08-22 21:37:54 +09001277 if (pe == NULL) {
1278 pr_info("persistence engine module ip_vs_pe_%s "
1279 "not found\n", u->pe_name);
1280 ret = -ENOENT;
1281 goto out;
1282 }
1283 old_pe = pe;
1284 }
1285
Julius Volzf94fd042008-09-02 15:55:55 +02001286#ifdef CONFIG_IP_VS_IPV6
Julius Volz48148932008-11-03 17:08:56 -08001287 if (u->af == AF_INET6 && (u->netmask < 1 || u->netmask > 128)) {
1288 ret = -EINVAL;
1289 goto out;
Julius Volzf94fd042008-09-02 15:55:55 +02001290 }
1291#endif
1292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 write_lock_bh(&__ip_vs_svc_lock);
1294
1295 /*
1296 * Wait until all other svc users go away.
1297 */
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001298 IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299
1300 /*
1301 * Set the flags and timeout value
1302 */
1303 svc->flags = u->flags | IP_VS_SVC_F_HASHED;
1304 svc->timeout = u->timeout * HZ;
1305 svc->netmask = u->netmask;
1306
1307 old_sched = svc->scheduler;
1308 if (sched != old_sched) {
1309 /*
1310 * Unbind the old scheduler
1311 */
1312 if ((ret = ip_vs_unbind_scheduler(svc))) {
1313 old_sched = sched;
Simon Horman9e691ed2008-09-17 10:10:41 +10001314 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 }
1316
1317 /*
1318 * Bind the new scheduler
1319 */
1320 if ((ret = ip_vs_bind_scheduler(svc, sched))) {
1321 /*
1322 * If ip_vs_bind_scheduler fails, restore the old
1323 * scheduler.
1324 * The main reason of failure is out of memory.
1325 *
1326 * The question is if the old scheduler can be
1327 * restored all the time. TODO: if it cannot be
1328 * restored some time, we must delete the service,
1329 * otherwise the system may crash.
1330 */
1331 ip_vs_bind_scheduler(svc, old_sched);
1332 old_sched = sched;
Simon Horman9e691ed2008-09-17 10:10:41 +10001333 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 }
1335 }
1336
Simon Horman0d1e71b2010-08-22 21:37:54 +09001337 old_pe = svc->pe;
1338 if (pe != old_pe) {
1339 ip_vs_unbind_pe(svc);
1340 ip_vs_bind_pe(svc, pe);
1341 }
1342
Simon Horman9e691ed2008-09-17 10:10:41 +10001343 out_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 write_unlock_bh(&__ip_vs_svc_lock);
Simon Horman9e691ed2008-09-17 10:10:41 +10001345 out:
Simon Horman6e08bfb2010-08-22 21:37:52 +09001346 ip_vs_scheduler_put(old_sched);
Simon Horman0d1e71b2010-08-22 21:37:54 +09001347 ip_vs_pe_put(old_pe);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 return ret;
1349}
1350
1351
1352/*
1353 * Delete a service from the service list
1354 * - The service must be unlinked, unlocked and not referenced!
1355 * - We are called under _bh lock
1356 */
1357static void __ip_vs_del_service(struct ip_vs_service *svc)
1358{
1359 struct ip_vs_dest *dest, *nxt;
1360 struct ip_vs_scheduler *old_sched;
Simon Horman0d1e71b2010-08-22 21:37:54 +09001361 struct ip_vs_pe *old_pe;
1362
1363 pr_info("%s: enter\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
Julius Volzf94fd042008-09-02 15:55:55 +02001365 /* Count only IPv4 services for old get/setsockopt interface */
1366 if (svc->af == AF_INET)
1367 ip_vs_num_services--;
1368
Hans Schillstrom29c20262011-01-03 14:44:54 +01001369 ip_vs_kill_estimator(svc->net, &svc->stats);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
1371 /* Unbind scheduler */
1372 old_sched = svc->scheduler;
1373 ip_vs_unbind_scheduler(svc);
Simon Horman6e08bfb2010-08-22 21:37:52 +09001374 ip_vs_scheduler_put(old_sched);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Simon Horman0d1e71b2010-08-22 21:37:54 +09001376 /* Unbind persistence engine */
1377 old_pe = svc->pe;
1378 ip_vs_unbind_pe(svc);
1379 ip_vs_pe_put(old_pe);
1380
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 /* Unbind app inc */
1382 if (svc->inc) {
1383 ip_vs_app_inc_put(svc->inc);
1384 svc->inc = NULL;
1385 }
1386
1387 /*
1388 * Unlink the whole destination list
1389 */
1390 list_for_each_entry_safe(dest, nxt, &svc->destinations, n_list) {
1391 __ip_vs_unlink_dest(svc, dest, 0);
Hans Schillstrom29c20262011-01-03 14:44:54 +01001392 __ip_vs_del_dest(svc->net, dest);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 }
1394
1395 /*
1396 * Update the virtual service counters
1397 */
1398 if (svc->port == FTPPORT)
1399 atomic_dec(&ip_vs_ftpsvc_counter);
1400 else if (svc->port == 0)
1401 atomic_dec(&ip_vs_nullsvc_counter);
1402
1403 /*
1404 * Free the service if nobody refers to it
1405 */
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001406 if (atomic_read(&svc->refcnt) == 0) {
1407 IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n",
1408 svc->fwmark,
1409 IP_VS_DBG_ADDR(svc->af, &svc->addr),
1410 ntohs(svc->port), atomic_read(&svc->usecnt));
Hans Schillstromb17fc992011-01-03 14:44:56 +01001411 free_percpu(svc->stats.cpustats);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 kfree(svc);
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001413 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
1415 /* decrease the module use count */
1416 ip_vs_use_count_dec();
1417}
1418
1419/*
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001420 * Unlink a service from list and try to delete it if its refcnt reached 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 */
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001422static void ip_vs_unlink_service(struct ip_vs_service *svc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 /*
1425 * Unhash it from the service table
1426 */
1427 write_lock_bh(&__ip_vs_svc_lock);
1428
1429 ip_vs_svc_unhash(svc);
1430
1431 /*
1432 * Wait until all the svc users go away.
1433 */
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001434 IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435
1436 __ip_vs_del_service(svc);
1437
1438 write_unlock_bh(&__ip_vs_svc_lock);
Julian Anastasov26c15cf2010-09-21 18:12:30 +02001439}
1440
1441/*
1442 * Delete a service from the service list
1443 */
1444static int ip_vs_del_service(struct ip_vs_service *svc)
1445{
1446 if (svc == NULL)
1447 return -EEXIST;
1448 ip_vs_unlink_service(svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449
1450 return 0;
1451}
1452
1453
1454/*
1455 * Flush all the virtual services
1456 */
Hans Schillstromfc723252011-01-03 14:44:43 +01001457static int ip_vs_flush(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458{
1459 int idx;
1460 struct ip_vs_service *svc, *nxt;
1461
1462 /*
Hans Schillstromfc723252011-01-03 14:44:43 +01001463 * Flush the service table hashed by <netns,protocol,addr,port>
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 */
1465 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
Hans Schillstromfc723252011-01-03 14:44:43 +01001466 list_for_each_entry_safe(svc, nxt, &ip_vs_svc_table[idx],
1467 s_list) {
1468 if (net_eq(svc->net, net))
1469 ip_vs_unlink_service(svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 }
1471 }
1472
1473 /*
1474 * Flush the service table hashed by fwmark
1475 */
1476 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
1477 list_for_each_entry_safe(svc, nxt,
1478 &ip_vs_svc_fwm_table[idx], f_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +01001479 if (net_eq(svc->net, net))
1480 ip_vs_unlink_service(svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 }
1482 }
1483
1484 return 0;
1485}
1486
1487
1488/*
1489 * Zero counters in a service or all services
1490 */
1491static int ip_vs_zero_service(struct ip_vs_service *svc)
1492{
1493 struct ip_vs_dest *dest;
1494
1495 write_lock_bh(&__ip_vs_svc_lock);
1496 list_for_each_entry(dest, &svc->destinations, n_list) {
1497 ip_vs_zero_stats(&dest->stats);
1498 }
1499 ip_vs_zero_stats(&svc->stats);
1500 write_unlock_bh(&__ip_vs_svc_lock);
1501 return 0;
1502}
1503
Hans Schillstromfc723252011-01-03 14:44:43 +01001504static int ip_vs_zero_all(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505{
1506 int idx;
1507 struct ip_vs_service *svc;
1508
1509 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
1510 list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +01001511 if (net_eq(svc->net, net))
1512 ip_vs_zero_service(svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 }
1514 }
1515
1516 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
1517 list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +01001518 if (net_eq(svc->net, net))
1519 ip_vs_zero_service(svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 }
1521 }
1522
Hans Schillstromb17fc992011-01-03 14:44:56 +01001523 ip_vs_zero_stats(net_ipvs(net)->tot_stats);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 return 0;
1525}
1526
1527
1528static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001529proc_do_defense_mode(ctl_table *table, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 void __user *buffer, size_t *lenp, loff_t *ppos)
1531{
Hans Schillstrom93304192011-01-03 14:44:51 +01001532 struct net *net = current->nsproxy->net_ns;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 int *valp = table->data;
1534 int val = *valp;
1535 int rc;
1536
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001537 rc = proc_dointvec(table, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 if (write && (*valp != val)) {
1539 if ((*valp < 0) || (*valp > 3)) {
1540 /* Restore the correct value */
1541 *valp = val;
1542 } else {
Hans Schillstrom93304192011-01-03 14:44:51 +01001543 update_defense_level(net_ipvs(net));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 }
1545 }
1546 return rc;
1547}
1548
1549
1550static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001551proc_do_sync_threshold(ctl_table *table, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 void __user *buffer, size_t *lenp, loff_t *ppos)
1553{
1554 int *valp = table->data;
1555 int val[2];
1556 int rc;
1557
1558 /* backup the value first */
1559 memcpy(val, valp, sizeof(val));
1560
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001561 rc = proc_dointvec(table, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 if (write && (valp[0] < 0 || valp[1] < 0 || valp[0] >= valp[1])) {
1563 /* Restore the correct value */
1564 memcpy(valp, val, sizeof(val));
1565 }
1566 return rc;
1567}
1568
Hans Schillstromb880c1f2010-11-19 14:25:14 +01001569static int
1570proc_do_sync_mode(ctl_table *table, int write,
1571 void __user *buffer, size_t *lenp, loff_t *ppos)
1572{
1573 int *valp = table->data;
1574 int val = *valp;
1575 int rc;
1576
1577 rc = proc_dointvec(table, write, buffer, lenp, ppos);
1578 if (write && (*valp != val)) {
1579 if ((*valp < 0) || (*valp > 1)) {
1580 /* Restore the correct value */
1581 *valp = val;
1582 } else {
Hans Schillstromf1313152011-01-03 14:44:55 +01001583 struct net *net = current->nsproxy->net_ns;
1584 ip_vs_sync_switch_mode(net, val);
Hans Schillstromb880c1f2010-11-19 14:25:14 +01001585 }
1586 }
1587 return rc;
1588}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
1590/*
1591 * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/)
1592 */
1593
1594static struct ctl_table vs_vars[] = {
1595 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596 .procname = "amemthresh",
1597 .data = &sysctl_ip_vs_amemthresh,
1598 .maxlen = sizeof(int),
1599 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001600 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 },
1602#ifdef CONFIG_IP_VS_DEBUG
1603 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 .procname = "debug_level",
1605 .data = &sysctl_ip_vs_debug_level,
1606 .maxlen = sizeof(int),
1607 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001608 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 },
1610#endif
1611 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 .procname = "am_droprate",
1613 .data = &sysctl_ip_vs_am_droprate,
1614 .maxlen = sizeof(int),
1615 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001616 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 },
1618 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 .procname = "drop_entry",
1620 .data = &sysctl_ip_vs_drop_entry,
1621 .maxlen = sizeof(int),
1622 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001623 .proc_handler = proc_do_defense_mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 },
1625 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 .procname = "drop_packet",
1627 .data = &sysctl_ip_vs_drop_packet,
1628 .maxlen = sizeof(int),
1629 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001630 .proc_handler = proc_do_defense_mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 },
Julian Anastasovf4bc17c2010-09-21 17:35:41 +02001632#ifdef CONFIG_IP_VS_NFCT
1633 {
1634 .procname = "conntrack",
1635 .data = &sysctl_ip_vs_conntrack,
1636 .maxlen = sizeof(int),
1637 .mode = 0644,
1638 .proc_handler = &proc_dointvec,
1639 },
1640#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 .procname = "secure_tcp",
1643 .data = &sysctl_ip_vs_secure_tcp,
1644 .maxlen = sizeof(int),
1645 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001646 .proc_handler = proc_do_defense_mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 },
Julian Anastasov8a803042010-09-21 17:38:57 +02001648 {
1649 .procname = "snat_reroute",
1650 .data = &sysctl_ip_vs_snat_reroute,
1651 .maxlen = sizeof(int),
1652 .mode = 0644,
1653 .proc_handler = &proc_dointvec,
1654 },
Hans Schillstromb880c1f2010-11-19 14:25:14 +01001655 {
1656 .procname = "sync_version",
1657 .data = &sysctl_ip_vs_sync_ver,
1658 .maxlen = sizeof(int),
1659 .mode = 0644,
1660 .proc_handler = &proc_do_sync_mode,
1661 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662#if 0
1663 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 .procname = "timeout_established",
1665 .data = &vs_timeout_table_dos.timeout[IP_VS_S_ESTABLISHED],
1666 .maxlen = sizeof(int),
1667 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001668 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 },
1670 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 .procname = "timeout_synsent",
1672 .data = &vs_timeout_table_dos.timeout[IP_VS_S_SYN_SENT],
1673 .maxlen = sizeof(int),
1674 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001675 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 },
1677 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 .procname = "timeout_synrecv",
1679 .data = &vs_timeout_table_dos.timeout[IP_VS_S_SYN_RECV],
1680 .maxlen = sizeof(int),
1681 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001682 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 },
1684 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 .procname = "timeout_finwait",
1686 .data = &vs_timeout_table_dos.timeout[IP_VS_S_FIN_WAIT],
1687 .maxlen = sizeof(int),
1688 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001689 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 },
1691 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 .procname = "timeout_timewait",
1693 .data = &vs_timeout_table_dos.timeout[IP_VS_S_TIME_WAIT],
1694 .maxlen = sizeof(int),
1695 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001696 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 },
1698 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 .procname = "timeout_close",
1700 .data = &vs_timeout_table_dos.timeout[IP_VS_S_CLOSE],
1701 .maxlen = sizeof(int),
1702 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001703 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 },
1705 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 .procname = "timeout_closewait",
1707 .data = &vs_timeout_table_dos.timeout[IP_VS_S_CLOSE_WAIT],
1708 .maxlen = sizeof(int),
1709 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001710 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 },
1712 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 .procname = "timeout_lastack",
1714 .data = &vs_timeout_table_dos.timeout[IP_VS_S_LAST_ACK],
1715 .maxlen = sizeof(int),
1716 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001717 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718 },
1719 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 .procname = "timeout_listen",
1721 .data = &vs_timeout_table_dos.timeout[IP_VS_S_LISTEN],
1722 .maxlen = sizeof(int),
1723 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001724 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 },
1726 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 .procname = "timeout_synack",
1728 .data = &vs_timeout_table_dos.timeout[IP_VS_S_SYNACK],
1729 .maxlen = sizeof(int),
1730 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001731 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 },
1733 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 .procname = "timeout_udp",
1735 .data = &vs_timeout_table_dos.timeout[IP_VS_S_UDP],
1736 .maxlen = sizeof(int),
1737 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001738 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 },
1740 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 .procname = "timeout_icmp",
1742 .data = &vs_timeout_table_dos.timeout[IP_VS_S_ICMP],
1743 .maxlen = sizeof(int),
1744 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001745 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 },
1747#endif
1748 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 .procname = "cache_bypass",
1750 .data = &sysctl_ip_vs_cache_bypass,
1751 .maxlen = sizeof(int),
1752 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001753 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 },
1755 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 .procname = "expire_nodest_conn",
1757 .data = &sysctl_ip_vs_expire_nodest_conn,
1758 .maxlen = sizeof(int),
1759 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001760 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 },
1762 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 .procname = "expire_quiescent_template",
1764 .data = &sysctl_ip_vs_expire_quiescent_template,
1765 .maxlen = sizeof(int),
1766 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001767 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 },
1769 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 .procname = "sync_threshold",
1771 .data = &sysctl_ip_vs_sync_threshold,
1772 .maxlen = sizeof(sysctl_ip_vs_sync_threshold),
1773 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001774 .proc_handler = proc_do_sync_threshold,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775 },
1776 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777 .procname = "nat_icmp_send",
1778 .data = &sysctl_ip_vs_nat_icmp_send,
1779 .maxlen = sizeof(int),
1780 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08001781 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001783 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784};
1785
Sven Wegener5587da52008-08-10 18:24:40 +00001786const struct ctl_path net_vs_ctl_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001787 { .procname = "net", },
1788 { .procname = "ipv4", },
Pavel Emelyanov90754f82008-01-12 02:33:50 -08001789 { .procname = "vs", },
1790 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791};
Pavel Emelyanov90754f82008-01-12 02:33:50 -08001792EXPORT_SYMBOL_GPL(net_vs_ctl_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793
1794static struct ctl_table_header * sysctl_header;
1795
1796#ifdef CONFIG_PROC_FS
1797
1798struct ip_vs_iter {
Hans Schillstromfc723252011-01-03 14:44:43 +01001799 struct seq_net_private p; /* Do not move this, netns depends upon it*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 struct list_head *table;
1801 int bucket;
1802};
1803
1804/*
1805 * Write the contents of the VS rule table to a PROCfs file.
1806 * (It is kept just for backward compatibility)
1807 */
1808static inline const char *ip_vs_fwd_name(unsigned flags)
1809{
1810 switch (flags & IP_VS_CONN_F_FWD_MASK) {
1811 case IP_VS_CONN_F_LOCALNODE:
1812 return "Local";
1813 case IP_VS_CONN_F_TUNNEL:
1814 return "Tunnel";
1815 case IP_VS_CONN_F_DROUTE:
1816 return "Route";
1817 default:
1818 return "Masq";
1819 }
1820}
1821
1822
1823/* Get the Nth entry in the two lists */
1824static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
1825{
Hans Schillstromfc723252011-01-03 14:44:43 +01001826 struct net *net = seq_file_net(seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 struct ip_vs_iter *iter = seq->private;
1828 int idx;
1829 struct ip_vs_service *svc;
1830
1831 /* look in hash by protocol */
1832 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
1833 list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +01001834 if (net_eq(svc->net, net) && pos-- == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 iter->table = ip_vs_svc_table;
1836 iter->bucket = idx;
1837 return svc;
1838 }
1839 }
1840 }
1841
1842 /* keep looking in fwmark */
1843 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
1844 list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +01001845 if (net_eq(svc->net, net) && pos-- == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 iter->table = ip_vs_svc_fwm_table;
1847 iter->bucket = idx;
1848 return svc;
1849 }
1850 }
1851 }
1852
1853 return NULL;
1854}
1855
1856static void *ip_vs_info_seq_start(struct seq_file *seq, loff_t *pos)
Simon Horman563e94f2008-09-17 10:10:42 +10001857__acquires(__ip_vs_svc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858{
1859
1860 read_lock_bh(&__ip_vs_svc_lock);
1861 return *pos ? ip_vs_info_array(seq, *pos - 1) : SEQ_START_TOKEN;
1862}
1863
1864
1865static void *ip_vs_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
1866{
1867 struct list_head *e;
1868 struct ip_vs_iter *iter;
1869 struct ip_vs_service *svc;
1870
1871 ++*pos;
1872 if (v == SEQ_START_TOKEN)
1873 return ip_vs_info_array(seq,0);
1874
1875 svc = v;
1876 iter = seq->private;
1877
1878 if (iter->table == ip_vs_svc_table) {
1879 /* next service in table hashed by protocol */
1880 if ((e = svc->s_list.next) != &ip_vs_svc_table[iter->bucket])
1881 return list_entry(e, struct ip_vs_service, s_list);
1882
1883
1884 while (++iter->bucket < IP_VS_SVC_TAB_SIZE) {
1885 list_for_each_entry(svc,&ip_vs_svc_table[iter->bucket],
1886 s_list) {
1887 return svc;
1888 }
1889 }
1890
1891 iter->table = ip_vs_svc_fwm_table;
1892 iter->bucket = -1;
1893 goto scan_fwmark;
1894 }
1895
1896 /* next service in hashed by fwmark */
1897 if ((e = svc->f_list.next) != &ip_vs_svc_fwm_table[iter->bucket])
1898 return list_entry(e, struct ip_vs_service, f_list);
1899
1900 scan_fwmark:
1901 while (++iter->bucket < IP_VS_SVC_TAB_SIZE) {
1902 list_for_each_entry(svc, &ip_vs_svc_fwm_table[iter->bucket],
1903 f_list)
1904 return svc;
1905 }
1906
1907 return NULL;
1908}
1909
1910static void ip_vs_info_seq_stop(struct seq_file *seq, void *v)
Simon Horman563e94f2008-09-17 10:10:42 +10001911__releases(__ip_vs_svc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912{
1913 read_unlock_bh(&__ip_vs_svc_lock);
1914}
1915
1916
1917static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
1918{
1919 if (v == SEQ_START_TOKEN) {
1920 seq_printf(seq,
1921 "IP Virtual Server version %d.%d.%d (size=%d)\n",
Catalin(ux) M. BOIE6f7edb42010-01-05 05:50:24 +01001922 NVERSION(IP_VS_VERSION_CODE), ip_vs_conn_tab_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 seq_puts(seq,
1924 "Prot LocalAddress:Port Scheduler Flags\n");
1925 seq_puts(seq,
1926 " -> RemoteAddress:Port Forward Weight ActiveConn InActConn\n");
1927 } else {
1928 const struct ip_vs_service *svc = v;
1929 const struct ip_vs_iter *iter = seq->private;
1930 const struct ip_vs_dest *dest;
1931
Vince Busam667a5f12008-09-02 15:55:49 +02001932 if (iter->table == ip_vs_svc_table) {
1933#ifdef CONFIG_IP_VS_IPV6
1934 if (svc->af == AF_INET6)
Harvey Harrison5b095d9892008-10-29 12:52:50 -07001935 seq_printf(seq, "%s [%pI6]:%04X %s ",
Vince Busam667a5f12008-09-02 15:55:49 +02001936 ip_vs_proto_name(svc->protocol),
Harvey Harrison38ff4fa2008-10-28 16:08:13 -07001937 &svc->addr.in6,
Vince Busam667a5f12008-09-02 15:55:49 +02001938 ntohs(svc->port),
1939 svc->scheduler->name);
1940 else
1941#endif
Nick Chalk26ec0372010-06-22 08:07:01 +02001942 seq_printf(seq, "%s %08X:%04X %s %s ",
Vince Busam667a5f12008-09-02 15:55:49 +02001943 ip_vs_proto_name(svc->protocol),
1944 ntohl(svc->addr.ip),
1945 ntohs(svc->port),
Nick Chalk26ec0372010-06-22 08:07:01 +02001946 svc->scheduler->name,
1947 (svc->flags & IP_VS_SVC_F_ONEPACKET)?"ops ":"");
Vince Busam667a5f12008-09-02 15:55:49 +02001948 } else {
Nick Chalk26ec0372010-06-22 08:07:01 +02001949 seq_printf(seq, "FWM %08X %s %s",
1950 svc->fwmark, svc->scheduler->name,
1951 (svc->flags & IP_VS_SVC_F_ONEPACKET)?"ops ":"");
Vince Busam667a5f12008-09-02 15:55:49 +02001952 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953
1954 if (svc->flags & IP_VS_SVC_F_PERSISTENT)
1955 seq_printf(seq, "persistent %d %08X\n",
1956 svc->timeout,
1957 ntohl(svc->netmask));
1958 else
1959 seq_putc(seq, '\n');
1960
1961 list_for_each_entry(dest, &svc->destinations, n_list) {
Vince Busam667a5f12008-09-02 15:55:49 +02001962#ifdef CONFIG_IP_VS_IPV6
1963 if (dest->af == AF_INET6)
1964 seq_printf(seq,
Harvey Harrison5b095d9892008-10-29 12:52:50 -07001965 " -> [%pI6]:%04X"
Vince Busam667a5f12008-09-02 15:55:49 +02001966 " %-7s %-6d %-10d %-10d\n",
Harvey Harrison38ff4fa2008-10-28 16:08:13 -07001967 &dest->addr.in6,
Vince Busam667a5f12008-09-02 15:55:49 +02001968 ntohs(dest->port),
1969 ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
1970 atomic_read(&dest->weight),
1971 atomic_read(&dest->activeconns),
1972 atomic_read(&dest->inactconns));
1973 else
1974#endif
1975 seq_printf(seq,
1976 " -> %08X:%04X "
1977 "%-7s %-6d %-10d %-10d\n",
1978 ntohl(dest->addr.ip),
1979 ntohs(dest->port),
1980 ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
1981 atomic_read(&dest->weight),
1982 atomic_read(&dest->activeconns),
1983 atomic_read(&dest->inactconns));
1984
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 }
1986 }
1987 return 0;
1988}
1989
Philippe De Muyter56b3d972007-07-10 23:07:31 -07001990static const struct seq_operations ip_vs_info_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 .start = ip_vs_info_seq_start,
1992 .next = ip_vs_info_seq_next,
1993 .stop = ip_vs_info_seq_stop,
1994 .show = ip_vs_info_seq_show,
1995};
1996
1997static int ip_vs_info_open(struct inode *inode, struct file *file)
1998{
Hans Schillstromfc723252011-01-03 14:44:43 +01001999 return seq_open_net(inode, file, &ip_vs_info_seq_ops,
Pavel Emelyanovcf7732e2007-10-10 02:29:29 -07002000 sizeof(struct ip_vs_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001}
2002
Arjan van de Ven9a321442007-02-12 00:55:35 -08002003static const struct file_operations ip_vs_info_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 .owner = THIS_MODULE,
2005 .open = ip_vs_info_open,
2006 .read = seq_read,
2007 .llseek = seq_lseek,
2008 .release = seq_release_private,
2009};
2010
2011#endif
2012
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013#ifdef CONFIG_PROC_FS
2014static int ip_vs_stats_show(struct seq_file *seq, void *v)
2015{
Hans Schillstromb17fc992011-01-03 14:44:56 +01002016 struct net *net = seq_file_single_net(seq);
2017 struct ip_vs_stats *tot_stats = net_ipvs(net)->tot_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018
2019/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */
2020 seq_puts(seq,
2021 " Total Incoming Outgoing Incoming Outgoing\n");
2022 seq_printf(seq,
2023 " Conns Packets Packets Bytes Bytes\n");
2024
Hans Schillstromb17fc992011-01-03 14:44:56 +01002025 spin_lock_bh(&tot_stats->lock);
2026 seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", tot_stats->ustats.conns,
2027 tot_stats->ustats.inpkts, tot_stats->ustats.outpkts,
2028 (unsigned long long) tot_stats->ustats.inbytes,
2029 (unsigned long long) tot_stats->ustats.outbytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030
2031/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */
2032 seq_puts(seq,
2033 " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n");
2034 seq_printf(seq,"%8X %8X %8X %16X %16X\n",
Hans Schillstromb17fc992011-01-03 14:44:56 +01002035 tot_stats->ustats.cps,
2036 tot_stats->ustats.inpps,
2037 tot_stats->ustats.outpps,
2038 tot_stats->ustats.inbps,
2039 tot_stats->ustats.outbps);
2040 spin_unlock_bh(&tot_stats->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041
2042 return 0;
2043}
2044
2045static int ip_vs_stats_seq_open(struct inode *inode, struct file *file)
2046{
Hans Schillstromfc723252011-01-03 14:44:43 +01002047 return single_open_net(inode, file, ip_vs_stats_show);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048}
2049
Arjan van de Ven9a321442007-02-12 00:55:35 -08002050static const struct file_operations ip_vs_stats_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 .owner = THIS_MODULE,
2052 .open = ip_vs_stats_seq_open,
2053 .read = seq_read,
2054 .llseek = seq_lseek,
2055 .release = single_release,
2056};
2057
Hans Schillstromb17fc992011-01-03 14:44:56 +01002058static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v)
2059{
2060 struct net *net = seq_file_single_net(seq);
2061 struct ip_vs_stats *tot_stats = net_ipvs(net)->tot_stats;
2062 int i;
2063
2064/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */
2065 seq_puts(seq,
2066 " Total Incoming Outgoing Incoming Outgoing\n");
2067 seq_printf(seq,
2068 "CPU Conns Packets Packets Bytes Bytes\n");
2069
2070 for_each_possible_cpu(i) {
2071 struct ip_vs_cpu_stats *u = per_cpu_ptr(net->ipvs->cpustats, i);
2072 seq_printf(seq, "%3X %8X %8X %8X %16LX %16LX\n",
2073 i, u->ustats.conns, u->ustats.inpkts,
2074 u->ustats.outpkts, (__u64)u->ustats.inbytes,
2075 (__u64)u->ustats.outbytes);
2076 }
2077
2078 spin_lock_bh(&tot_stats->lock);
2079 seq_printf(seq, " ~ %8X %8X %8X %16LX %16LX\n\n",
2080 tot_stats->ustats.conns, tot_stats->ustats.inpkts,
2081 tot_stats->ustats.outpkts,
2082 (unsigned long long) tot_stats->ustats.inbytes,
2083 (unsigned long long) tot_stats->ustats.outbytes);
2084
2085/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */
2086 seq_puts(seq,
2087 " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n");
2088 seq_printf(seq, " %8X %8X %8X %16X %16X\n",
2089 tot_stats->ustats.cps,
2090 tot_stats->ustats.inpps,
2091 tot_stats->ustats.outpps,
2092 tot_stats->ustats.inbps,
2093 tot_stats->ustats.outbps);
2094 spin_unlock_bh(&tot_stats->lock);
2095
2096 return 0;
2097}
2098
2099static int ip_vs_stats_percpu_seq_open(struct inode *inode, struct file *file)
2100{
2101 return single_open_net(inode, file, ip_vs_stats_percpu_show);
2102}
2103
2104static const struct file_operations ip_vs_stats_percpu_fops = {
2105 .owner = THIS_MODULE,
2106 .open = ip_vs_stats_percpu_seq_open,
2107 .read = seq_read,
2108 .llseek = seq_lseek,
2109 .release = single_release,
2110};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111#endif
2112
2113/*
2114 * Set timeout values for tcp tcpfin udp in the timeout_table.
2115 */
Hans Schillstrom93304192011-01-03 14:44:51 +01002116static int ip_vs_set_timeout(struct net *net, struct ip_vs_timeout_user *u)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117{
Hans Schillstrom93304192011-01-03 14:44:51 +01002118 struct ip_vs_proto_data *pd;
2119
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 IP_VS_DBG(2, "Setting timeout tcp:%d tcpfin:%d udp:%d\n",
2121 u->tcp_timeout,
2122 u->tcp_fin_timeout,
2123 u->udp_timeout);
2124
2125#ifdef CONFIG_IP_VS_PROTO_TCP
2126 if (u->tcp_timeout) {
Hans Schillstrom93304192011-01-03 14:44:51 +01002127 pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
2128 pd->timeout_table[IP_VS_TCP_S_ESTABLISHED]
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 = u->tcp_timeout * HZ;
2130 }
2131
2132 if (u->tcp_fin_timeout) {
Hans Schillstrom93304192011-01-03 14:44:51 +01002133 pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
2134 pd->timeout_table[IP_VS_TCP_S_FIN_WAIT]
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 = u->tcp_fin_timeout * HZ;
2136 }
2137#endif
2138
2139#ifdef CONFIG_IP_VS_PROTO_UDP
2140 if (u->udp_timeout) {
Hans Schillstrom93304192011-01-03 14:44:51 +01002141 pd = ip_vs_proto_data_get(net, IPPROTO_UDP);
2142 pd->timeout_table[IP_VS_UDP_S_NORMAL]
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 = u->udp_timeout * HZ;
2144 }
2145#endif
2146 return 0;
2147}
2148
2149
2150#define SET_CMDID(cmd) (cmd - IP_VS_BASE_CTL)
2151#define SERVICE_ARG_LEN (sizeof(struct ip_vs_service_user))
2152#define SVCDEST_ARG_LEN (sizeof(struct ip_vs_service_user) + \
2153 sizeof(struct ip_vs_dest_user))
2154#define TIMEOUT_ARG_LEN (sizeof(struct ip_vs_timeout_user))
2155#define DAEMON_ARG_LEN (sizeof(struct ip_vs_daemon_user))
2156#define MAX_ARG_LEN SVCDEST_ARG_LEN
2157
Arjan van de Ven9b5b5cf2005-11-29 16:21:38 -08002158static const unsigned char set_arglen[SET_CMDID(IP_VS_SO_SET_MAX)+1] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 [SET_CMDID(IP_VS_SO_SET_ADD)] = SERVICE_ARG_LEN,
2160 [SET_CMDID(IP_VS_SO_SET_EDIT)] = SERVICE_ARG_LEN,
2161 [SET_CMDID(IP_VS_SO_SET_DEL)] = SERVICE_ARG_LEN,
2162 [SET_CMDID(IP_VS_SO_SET_FLUSH)] = 0,
2163 [SET_CMDID(IP_VS_SO_SET_ADDDEST)] = SVCDEST_ARG_LEN,
2164 [SET_CMDID(IP_VS_SO_SET_DELDEST)] = SVCDEST_ARG_LEN,
2165 [SET_CMDID(IP_VS_SO_SET_EDITDEST)] = SVCDEST_ARG_LEN,
2166 [SET_CMDID(IP_VS_SO_SET_TIMEOUT)] = TIMEOUT_ARG_LEN,
2167 [SET_CMDID(IP_VS_SO_SET_STARTDAEMON)] = DAEMON_ARG_LEN,
2168 [SET_CMDID(IP_VS_SO_SET_STOPDAEMON)] = DAEMON_ARG_LEN,
2169 [SET_CMDID(IP_VS_SO_SET_ZERO)] = SERVICE_ARG_LEN,
2170};
2171
Julius Volzc860c6b2008-09-02 15:55:36 +02002172static void ip_vs_copy_usvc_compat(struct ip_vs_service_user_kern *usvc,
2173 struct ip_vs_service_user *usvc_compat)
2174{
Simon Horman0d1e71b2010-08-22 21:37:54 +09002175 memset(usvc, 0, sizeof(*usvc));
2176
Julius Volzc860c6b2008-09-02 15:55:36 +02002177 usvc->af = AF_INET;
2178 usvc->protocol = usvc_compat->protocol;
2179 usvc->addr.ip = usvc_compat->addr;
2180 usvc->port = usvc_compat->port;
2181 usvc->fwmark = usvc_compat->fwmark;
2182
2183 /* Deep copy of sched_name is not needed here */
2184 usvc->sched_name = usvc_compat->sched_name;
2185
2186 usvc->flags = usvc_compat->flags;
2187 usvc->timeout = usvc_compat->timeout;
2188 usvc->netmask = usvc_compat->netmask;
2189}
2190
2191static void ip_vs_copy_udest_compat(struct ip_vs_dest_user_kern *udest,
2192 struct ip_vs_dest_user *udest_compat)
2193{
Simon Horman0d1e71b2010-08-22 21:37:54 +09002194 memset(udest, 0, sizeof(*udest));
2195
Julius Volzc860c6b2008-09-02 15:55:36 +02002196 udest->addr.ip = udest_compat->addr;
2197 udest->port = udest_compat->port;
2198 udest->conn_flags = udest_compat->conn_flags;
2199 udest->weight = udest_compat->weight;
2200 udest->u_threshold = udest_compat->u_threshold;
2201 udest->l_threshold = udest_compat->l_threshold;
2202}
2203
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204static int
2205do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2206{
Hans Schillstromfc723252011-01-03 14:44:43 +01002207 struct net *net = sock_net(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 int ret;
2209 unsigned char arg[MAX_ARG_LEN];
Julius Volzc860c6b2008-09-02 15:55:36 +02002210 struct ip_vs_service_user *usvc_compat;
2211 struct ip_vs_service_user_kern usvc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 struct ip_vs_service *svc;
Julius Volzc860c6b2008-09-02 15:55:36 +02002213 struct ip_vs_dest_user *udest_compat;
2214 struct ip_vs_dest_user_kern udest;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215
2216 if (!capable(CAP_NET_ADMIN))
2217 return -EPERM;
2218
Arjan van de Ven04bcef22010-01-04 16:37:12 +01002219 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_SET_MAX)
2220 return -EINVAL;
2221 if (len < 0 || len > MAX_ARG_LEN)
2222 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 if (len != set_arglen[SET_CMDID(cmd)]) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00002224 pr_err("set_ctl: len %u != %u\n",
2225 len, set_arglen[SET_CMDID(cmd)]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226 return -EINVAL;
2227 }
2228
2229 if (copy_from_user(arg, user, len) != 0)
2230 return -EFAULT;
2231
2232 /* increase the module use count */
2233 ip_vs_use_count_inc();
2234
Ingo Molnar14cc3e22006-03-26 01:37:14 -08002235 if (mutex_lock_interruptible(&__ip_vs_mutex)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 ret = -ERESTARTSYS;
2237 goto out_dec;
2238 }
2239
2240 if (cmd == IP_VS_SO_SET_FLUSH) {
2241 /* Flush the virtual service */
Hans Schillstromfc723252011-01-03 14:44:43 +01002242 ret = ip_vs_flush(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 goto out_unlock;
2244 } else if (cmd == IP_VS_SO_SET_TIMEOUT) {
2245 /* Set timeout values for (tcp tcpfin udp) */
Hans Schillstrom93304192011-01-03 14:44:51 +01002246 ret = ip_vs_set_timeout(net, (struct ip_vs_timeout_user *)arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 goto out_unlock;
2248 } else if (cmd == IP_VS_SO_SET_STARTDAEMON) {
2249 struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg;
Hans Schillstromf1313152011-01-03 14:44:55 +01002250 ret = start_sync_thread(net, dm->state, dm->mcast_ifn,
2251 dm->syncid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 goto out_unlock;
2253 } else if (cmd == IP_VS_SO_SET_STOPDAEMON) {
2254 struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg;
Hans Schillstromf1313152011-01-03 14:44:55 +01002255 ret = stop_sync_thread(net, dm->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 goto out_unlock;
2257 }
2258
Julius Volzc860c6b2008-09-02 15:55:36 +02002259 usvc_compat = (struct ip_vs_service_user *)arg;
2260 udest_compat = (struct ip_vs_dest_user *)(usvc_compat + 1);
2261
2262 /* We only use the new structs internally, so copy userspace compat
2263 * structs to extended internal versions */
2264 ip_vs_copy_usvc_compat(&usvc, usvc_compat);
2265 ip_vs_copy_udest_compat(&udest, udest_compat);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266
2267 if (cmd == IP_VS_SO_SET_ZERO) {
2268 /* if no service address is set, zero counters in all */
Julius Volzc860c6b2008-09-02 15:55:36 +02002269 if (!usvc.fwmark && !usvc.addr.ip && !usvc.port) {
Hans Schillstromfc723252011-01-03 14:44:43 +01002270 ret = ip_vs_zero_all(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 goto out_unlock;
2272 }
2273 }
2274
Venkata Mohan Reddy2906f662010-02-18 12:31:05 +01002275 /* Check for valid protocol: TCP or UDP or SCTP, even for fwmark!=0 */
2276 if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP &&
2277 usvc.protocol != IPPROTO_SCTP) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00002278 pr_err("set_ctl: invalid protocol: %d %pI4:%d %s\n",
2279 usvc.protocol, &usvc.addr.ip,
2280 ntohs(usvc.port), usvc.sched_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 ret = -EFAULT;
2282 goto out_unlock;
2283 }
2284
2285 /* Lookup the exact service by <protocol, addr, port> or fwmark */
Julius Volzc860c6b2008-09-02 15:55:36 +02002286 if (usvc.fwmark == 0)
Hans Schillstromfc723252011-01-03 14:44:43 +01002287 svc = __ip_vs_service_find(net, usvc.af, usvc.protocol,
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002288 &usvc.addr, usvc.port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 else
Hans Schillstromfc723252011-01-03 14:44:43 +01002290 svc = __ip_vs_svc_fwm_find(net, usvc.af, usvc.fwmark);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291
2292 if (cmd != IP_VS_SO_SET_ADD
Julius Volzc860c6b2008-09-02 15:55:36 +02002293 && (svc == NULL || svc->protocol != usvc.protocol)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 ret = -ESRCH;
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002295 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 }
2297
2298 switch (cmd) {
2299 case IP_VS_SO_SET_ADD:
2300 if (svc != NULL)
2301 ret = -EEXIST;
2302 else
Hans Schillstromfc723252011-01-03 14:44:43 +01002303 ret = ip_vs_add_service(net, &usvc, &svc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 break;
2305 case IP_VS_SO_SET_EDIT:
Julius Volzc860c6b2008-09-02 15:55:36 +02002306 ret = ip_vs_edit_service(svc, &usvc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 break;
2308 case IP_VS_SO_SET_DEL:
2309 ret = ip_vs_del_service(svc);
2310 if (!ret)
2311 goto out_unlock;
2312 break;
2313 case IP_VS_SO_SET_ZERO:
2314 ret = ip_vs_zero_service(svc);
2315 break;
2316 case IP_VS_SO_SET_ADDDEST:
Julius Volzc860c6b2008-09-02 15:55:36 +02002317 ret = ip_vs_add_dest(svc, &udest);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 break;
2319 case IP_VS_SO_SET_EDITDEST:
Julius Volzc860c6b2008-09-02 15:55:36 +02002320 ret = ip_vs_edit_dest(svc, &udest);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 break;
2322 case IP_VS_SO_SET_DELDEST:
Julius Volzc860c6b2008-09-02 15:55:36 +02002323 ret = ip_vs_del_dest(svc, &udest);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 break;
2325 default:
2326 ret = -EINVAL;
2327 }
2328
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 out_unlock:
Ingo Molnar14cc3e22006-03-26 01:37:14 -08002330 mutex_unlock(&__ip_vs_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 out_dec:
2332 /* decrease the module use count */
2333 ip_vs_use_count_dec();
2334
2335 return ret;
2336}
2337
2338
2339static void
2340ip_vs_copy_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src)
2341{
2342 spin_lock_bh(&src->lock);
Sven Wegenere9c0ce22008-09-08 13:39:04 +02002343 memcpy(dst, &src->ustats, sizeof(*dst));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344 spin_unlock_bh(&src->lock);
2345}
2346
2347static void
2348ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src)
2349{
2350 dst->protocol = src->protocol;
Julius Volze7ade462008-09-02 15:55:33 +02002351 dst->addr = src->addr.ip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352 dst->port = src->port;
2353 dst->fwmark = src->fwmark;
pageexec4da62fc2005-06-26 16:00:19 -07002354 strlcpy(dst->sched_name, src->scheduler->name, sizeof(dst->sched_name));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 dst->flags = src->flags;
2356 dst->timeout = src->timeout / HZ;
2357 dst->netmask = src->netmask;
2358 dst->num_dests = src->num_dests;
2359 ip_vs_copy_stats(&dst->stats, &src->stats);
2360}
2361
2362static inline int
Hans Schillstromfc723252011-01-03 14:44:43 +01002363__ip_vs_get_service_entries(struct net *net,
2364 const struct ip_vs_get_services *get,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 struct ip_vs_get_services __user *uptr)
2366{
2367 int idx, count=0;
2368 struct ip_vs_service *svc;
2369 struct ip_vs_service_entry entry;
2370 int ret = 0;
2371
2372 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
2373 list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) {
Julius Volzf94fd042008-09-02 15:55:55 +02002374 /* Only expose IPv4 entries to old interface */
Hans Schillstromfc723252011-01-03 14:44:43 +01002375 if (svc->af != AF_INET || !net_eq(svc->net, net))
Julius Volzf94fd042008-09-02 15:55:55 +02002376 continue;
2377
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 if (count >= get->num_services)
2379 goto out;
pageexec4da62fc2005-06-26 16:00:19 -07002380 memset(&entry, 0, sizeof(entry));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381 ip_vs_copy_service(&entry, svc);
2382 if (copy_to_user(&uptr->entrytable[count],
2383 &entry, sizeof(entry))) {
2384 ret = -EFAULT;
2385 goto out;
2386 }
2387 count++;
2388 }
2389 }
2390
2391 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
2392 list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) {
Julius Volzf94fd042008-09-02 15:55:55 +02002393 /* Only expose IPv4 entries to old interface */
Hans Schillstromfc723252011-01-03 14:44:43 +01002394 if (svc->af != AF_INET || !net_eq(svc->net, net))
Julius Volzf94fd042008-09-02 15:55:55 +02002395 continue;
2396
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 if (count >= get->num_services)
2398 goto out;
pageexec4da62fc2005-06-26 16:00:19 -07002399 memset(&entry, 0, sizeof(entry));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 ip_vs_copy_service(&entry, svc);
2401 if (copy_to_user(&uptr->entrytable[count],
2402 &entry, sizeof(entry))) {
2403 ret = -EFAULT;
2404 goto out;
2405 }
2406 count++;
2407 }
2408 }
2409 out:
2410 return ret;
2411}
2412
2413static inline int
Hans Schillstromfc723252011-01-03 14:44:43 +01002414__ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 struct ip_vs_get_dests __user *uptr)
2416{
2417 struct ip_vs_service *svc;
Julius Volzb18610d2008-09-02 15:55:37 +02002418 union nf_inet_addr addr = { .ip = get->addr };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419 int ret = 0;
2420
2421 if (get->fwmark)
Hans Schillstromfc723252011-01-03 14:44:43 +01002422 svc = __ip_vs_svc_fwm_find(net, AF_INET, get->fwmark);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 else
Hans Schillstromfc723252011-01-03 14:44:43 +01002424 svc = __ip_vs_service_find(net, AF_INET, get->protocol, &addr,
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002425 get->port);
Julius Volzb18610d2008-09-02 15:55:37 +02002426
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 if (svc) {
2428 int count = 0;
2429 struct ip_vs_dest *dest;
2430 struct ip_vs_dest_entry entry;
2431
2432 list_for_each_entry(dest, &svc->destinations, n_list) {
2433 if (count >= get->num_dests)
2434 break;
2435
Julius Volze7ade462008-09-02 15:55:33 +02002436 entry.addr = dest->addr.ip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437 entry.port = dest->port;
2438 entry.conn_flags = atomic_read(&dest->conn_flags);
2439 entry.weight = atomic_read(&dest->weight);
2440 entry.u_threshold = dest->u_threshold;
2441 entry.l_threshold = dest->l_threshold;
2442 entry.activeconns = atomic_read(&dest->activeconns);
2443 entry.inactconns = atomic_read(&dest->inactconns);
2444 entry.persistconns = atomic_read(&dest->persistconns);
2445 ip_vs_copy_stats(&entry.stats, &dest->stats);
2446 if (copy_to_user(&uptr->entrytable[count],
2447 &entry, sizeof(entry))) {
2448 ret = -EFAULT;
2449 break;
2450 }
2451 count++;
2452 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453 } else
2454 ret = -ESRCH;
2455 return ret;
2456}
2457
2458static inline void
Hans Schillstrom93304192011-01-03 14:44:51 +01002459__ip_vs_get_timeouts(struct net *net, struct ip_vs_timeout_user *u)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460{
Hans Schillstrom93304192011-01-03 14:44:51 +01002461 struct ip_vs_proto_data *pd;
2462
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463#ifdef CONFIG_IP_VS_PROTO_TCP
Hans Schillstrom93304192011-01-03 14:44:51 +01002464 pd = ip_vs_proto_data_get(net, IPPROTO_TCP);
2465 u->tcp_timeout = pd->timeout_table[IP_VS_TCP_S_ESTABLISHED] / HZ;
2466 u->tcp_fin_timeout = pd->timeout_table[IP_VS_TCP_S_FIN_WAIT] / HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467#endif
2468#ifdef CONFIG_IP_VS_PROTO_UDP
Hans Schillstrom93304192011-01-03 14:44:51 +01002469 pd = ip_vs_proto_data_get(net, IPPROTO_UDP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 u->udp_timeout =
Hans Schillstrom93304192011-01-03 14:44:51 +01002471 pd->timeout_table[IP_VS_UDP_S_NORMAL] / HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472#endif
2473}
2474
2475
2476#define GET_CMDID(cmd) (cmd - IP_VS_BASE_CTL)
2477#define GET_INFO_ARG_LEN (sizeof(struct ip_vs_getinfo))
2478#define GET_SERVICES_ARG_LEN (sizeof(struct ip_vs_get_services))
2479#define GET_SERVICE_ARG_LEN (sizeof(struct ip_vs_service_entry))
2480#define GET_DESTS_ARG_LEN (sizeof(struct ip_vs_get_dests))
2481#define GET_TIMEOUT_ARG_LEN (sizeof(struct ip_vs_timeout_user))
2482#define GET_DAEMON_ARG_LEN (sizeof(struct ip_vs_daemon_user) * 2)
2483
Arjan van de Ven9b5b5cf2005-11-29 16:21:38 -08002484static const unsigned char get_arglen[GET_CMDID(IP_VS_SO_GET_MAX)+1] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 [GET_CMDID(IP_VS_SO_GET_VERSION)] = 64,
2486 [GET_CMDID(IP_VS_SO_GET_INFO)] = GET_INFO_ARG_LEN,
2487 [GET_CMDID(IP_VS_SO_GET_SERVICES)] = GET_SERVICES_ARG_LEN,
2488 [GET_CMDID(IP_VS_SO_GET_SERVICE)] = GET_SERVICE_ARG_LEN,
2489 [GET_CMDID(IP_VS_SO_GET_DESTS)] = GET_DESTS_ARG_LEN,
2490 [GET_CMDID(IP_VS_SO_GET_TIMEOUT)] = GET_TIMEOUT_ARG_LEN,
2491 [GET_CMDID(IP_VS_SO_GET_DAEMON)] = GET_DAEMON_ARG_LEN,
2492};
2493
2494static int
2495do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2496{
2497 unsigned char arg[128];
2498 int ret = 0;
Arjan van de Ven04bcef22010-01-04 16:37:12 +01002499 unsigned int copylen;
Hans Schillstromfc723252011-01-03 14:44:43 +01002500 struct net *net = sock_net(sk);
Hans Schillstromf1313152011-01-03 14:44:55 +01002501 struct netns_ipvs *ipvs = net_ipvs(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502
Hans Schillstromfc723252011-01-03 14:44:43 +01002503 BUG_ON(!net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 if (!capable(CAP_NET_ADMIN))
2505 return -EPERM;
2506
Arjan van de Ven04bcef22010-01-04 16:37:12 +01002507 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_GET_MAX)
2508 return -EINVAL;
2509
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 if (*len < get_arglen[GET_CMDID(cmd)]) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00002511 pr_err("get_ctl: len %u < %u\n",
2512 *len, get_arglen[GET_CMDID(cmd)]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513 return -EINVAL;
2514 }
2515
Arjan van de Ven04bcef22010-01-04 16:37:12 +01002516 copylen = get_arglen[GET_CMDID(cmd)];
2517 if (copylen > 128)
2518 return -EINVAL;
2519
2520 if (copy_from_user(arg, user, copylen) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 return -EFAULT;
2522
Ingo Molnar14cc3e22006-03-26 01:37:14 -08002523 if (mutex_lock_interruptible(&__ip_vs_mutex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 return -ERESTARTSYS;
2525
2526 switch (cmd) {
2527 case IP_VS_SO_GET_VERSION:
2528 {
2529 char buf[64];
2530
2531 sprintf(buf, "IP Virtual Server version %d.%d.%d (size=%d)",
Catalin(ux) M. BOIE6f7edb42010-01-05 05:50:24 +01002532 NVERSION(IP_VS_VERSION_CODE), ip_vs_conn_tab_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533 if (copy_to_user(user, buf, strlen(buf)+1) != 0) {
2534 ret = -EFAULT;
2535 goto out;
2536 }
2537 *len = strlen(buf)+1;
2538 }
2539 break;
2540
2541 case IP_VS_SO_GET_INFO:
2542 {
2543 struct ip_vs_getinfo info;
2544 info.version = IP_VS_VERSION_CODE;
Catalin(ux) M. BOIE6f7edb42010-01-05 05:50:24 +01002545 info.size = ip_vs_conn_tab_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 info.num_services = ip_vs_num_services;
2547 if (copy_to_user(user, &info, sizeof(info)) != 0)
2548 ret = -EFAULT;
2549 }
2550 break;
2551
2552 case IP_VS_SO_GET_SERVICES:
2553 {
2554 struct ip_vs_get_services *get;
2555 int size;
2556
2557 get = (struct ip_vs_get_services *)arg;
2558 size = sizeof(*get) +
2559 sizeof(struct ip_vs_service_entry) * get->num_services;
2560 if (*len != size) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00002561 pr_err("length: %u != %u\n", *len, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 ret = -EINVAL;
2563 goto out;
2564 }
Hans Schillstromfc723252011-01-03 14:44:43 +01002565 ret = __ip_vs_get_service_entries(net, get, user);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 }
2567 break;
2568
2569 case IP_VS_SO_GET_SERVICE:
2570 {
2571 struct ip_vs_service_entry *entry;
2572 struct ip_vs_service *svc;
Julius Volzb18610d2008-09-02 15:55:37 +02002573 union nf_inet_addr addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574
2575 entry = (struct ip_vs_service_entry *)arg;
Julius Volzb18610d2008-09-02 15:55:37 +02002576 addr.ip = entry->addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 if (entry->fwmark)
Hans Schillstromfc723252011-01-03 14:44:43 +01002578 svc = __ip_vs_svc_fwm_find(net, AF_INET, entry->fwmark);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 else
Hans Schillstromfc723252011-01-03 14:44:43 +01002580 svc = __ip_vs_service_find(net, AF_INET,
2581 entry->protocol, &addr,
2582 entry->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 if (svc) {
2584 ip_vs_copy_service(entry, svc);
2585 if (copy_to_user(user, entry, sizeof(*entry)) != 0)
2586 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 } else
2588 ret = -ESRCH;
2589 }
2590 break;
2591
2592 case IP_VS_SO_GET_DESTS:
2593 {
2594 struct ip_vs_get_dests *get;
2595 int size;
2596
2597 get = (struct ip_vs_get_dests *)arg;
2598 size = sizeof(*get) +
2599 sizeof(struct ip_vs_dest_entry) * get->num_dests;
2600 if (*len != size) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00002601 pr_err("length: %u != %u\n", *len, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 ret = -EINVAL;
2603 goto out;
2604 }
Hans Schillstromfc723252011-01-03 14:44:43 +01002605 ret = __ip_vs_get_dest_entries(net, get, user);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606 }
2607 break;
2608
2609 case IP_VS_SO_GET_TIMEOUT:
2610 {
2611 struct ip_vs_timeout_user t;
2612
Hans Schillstrom93304192011-01-03 14:44:51 +01002613 __ip_vs_get_timeouts(net, &t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 if (copy_to_user(user, &t, sizeof(t)) != 0)
2615 ret = -EFAULT;
2616 }
2617 break;
2618
2619 case IP_VS_SO_GET_DAEMON:
2620 {
2621 struct ip_vs_daemon_user d[2];
2622
2623 memset(&d, 0, sizeof(d));
Hans Schillstromf1313152011-01-03 14:44:55 +01002624 if (ipvs->sync_state & IP_VS_STATE_MASTER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 d[0].state = IP_VS_STATE_MASTER;
Hans Schillstromf1313152011-01-03 14:44:55 +01002626 strlcpy(d[0].mcast_ifn, ipvs->master_mcast_ifn,
2627 sizeof(d[0].mcast_ifn));
2628 d[0].syncid = ipvs->master_syncid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 }
Hans Schillstromf1313152011-01-03 14:44:55 +01002630 if (ipvs->sync_state & IP_VS_STATE_BACKUP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 d[1].state = IP_VS_STATE_BACKUP;
Hans Schillstromf1313152011-01-03 14:44:55 +01002632 strlcpy(d[1].mcast_ifn, ipvs->backup_mcast_ifn,
2633 sizeof(d[1].mcast_ifn));
2634 d[1].syncid = ipvs->backup_syncid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635 }
2636 if (copy_to_user(user, &d, sizeof(d)) != 0)
2637 ret = -EFAULT;
2638 }
2639 break;
2640
2641 default:
2642 ret = -EINVAL;
2643 }
2644
2645 out:
Ingo Molnar14cc3e22006-03-26 01:37:14 -08002646 mutex_unlock(&__ip_vs_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 return ret;
2648}
2649
2650
2651static struct nf_sockopt_ops ip_vs_sockopts = {
2652 .pf = PF_INET,
2653 .set_optmin = IP_VS_BASE_CTL,
2654 .set_optmax = IP_VS_SO_SET_MAX+1,
2655 .set = do_ip_vs_set_ctl,
2656 .get_optmin = IP_VS_BASE_CTL,
2657 .get_optmax = IP_VS_SO_GET_MAX+1,
2658 .get = do_ip_vs_get_ctl,
Neil Horman16fcec32007-09-11 11:28:26 +02002659 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660};
2661
Julius Volz9a812192008-08-14 14:08:44 +02002662/*
2663 * Generic Netlink interface
2664 */
2665
2666/* IPVS genetlink family */
2667static struct genl_family ip_vs_genl_family = {
2668 .id = GENL_ID_GENERATE,
2669 .hdrsize = 0,
2670 .name = IPVS_GENL_NAME,
2671 .version = IPVS_GENL_VERSION,
2672 .maxattr = IPVS_CMD_MAX,
2673};
2674
2675/* Policy used for first-level command attributes */
2676static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
2677 [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
2678 [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
2679 [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
2680 [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
2681 [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
2682 [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
2683};
2684
2685/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
2686static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
2687 [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
2688 [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
2689 .len = IP_VS_IFNAME_MAXLEN },
2690 [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
2691};
2692
2693/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
2694static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
2695 [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
2696 [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
2697 [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
2698 .len = sizeof(union nf_inet_addr) },
2699 [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
2700 [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
2701 [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
2702 .len = IP_VS_SCHEDNAME_MAXLEN },
Simon Horman0d1e71b2010-08-22 21:37:54 +09002703 [IPVS_SVC_ATTR_PE_NAME] = { .type = NLA_NUL_STRING,
2704 .len = IP_VS_PENAME_MAXLEN },
Julius Volz9a812192008-08-14 14:08:44 +02002705 [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
2706 .len = sizeof(struct ip_vs_flags) },
2707 [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
2708 [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
2709 [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
2710};
2711
2712/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
2713static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
2714 [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
2715 .len = sizeof(union nf_inet_addr) },
2716 [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
2717 [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
2718 [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
2719 [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
2720 [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
2721 [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
2722 [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
2723 [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
2724 [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
2725};
2726
2727static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
2728 struct ip_vs_stats *stats)
2729{
2730 struct nlattr *nl_stats = nla_nest_start(skb, container_type);
2731 if (!nl_stats)
2732 return -EMSGSIZE;
2733
2734 spin_lock_bh(&stats->lock);
2735
Sven Wegenere9c0ce22008-09-08 13:39:04 +02002736 NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->ustats.conns);
2737 NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->ustats.inpkts);
2738 NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->ustats.outpkts);
2739 NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->ustats.inbytes);
2740 NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->ustats.outbytes);
2741 NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->ustats.cps);
2742 NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->ustats.inpps);
2743 NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->ustats.outpps);
2744 NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->ustats.inbps);
2745 NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->ustats.outbps);
Julius Volz9a812192008-08-14 14:08:44 +02002746
2747 spin_unlock_bh(&stats->lock);
2748
2749 nla_nest_end(skb, nl_stats);
2750
2751 return 0;
2752
2753nla_put_failure:
2754 spin_unlock_bh(&stats->lock);
2755 nla_nest_cancel(skb, nl_stats);
2756 return -EMSGSIZE;
2757}
2758
2759static int ip_vs_genl_fill_service(struct sk_buff *skb,
2760 struct ip_vs_service *svc)
2761{
2762 struct nlattr *nl_service;
2763 struct ip_vs_flags flags = { .flags = svc->flags,
2764 .mask = ~0 };
2765
2766 nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
2767 if (!nl_service)
2768 return -EMSGSIZE;
2769
Julius Volzf94fd042008-09-02 15:55:55 +02002770 NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, svc->af);
Julius Volz9a812192008-08-14 14:08:44 +02002771
2772 if (svc->fwmark) {
2773 NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
2774 } else {
2775 NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
2776 NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
2777 NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
2778 }
2779
2780 NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
Simon Horman0d1e71b2010-08-22 21:37:54 +09002781 if (svc->pe)
2782 NLA_PUT_STRING(skb, IPVS_SVC_ATTR_PE_NAME, svc->pe->name);
Julius Volz9a812192008-08-14 14:08:44 +02002783 NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
2784 NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
2785 NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
2786
2787 if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
2788 goto nla_put_failure;
2789
2790 nla_nest_end(skb, nl_service);
2791
2792 return 0;
2793
2794nla_put_failure:
2795 nla_nest_cancel(skb, nl_service);
2796 return -EMSGSIZE;
2797}
2798
2799static int ip_vs_genl_dump_service(struct sk_buff *skb,
2800 struct ip_vs_service *svc,
2801 struct netlink_callback *cb)
2802{
2803 void *hdr;
2804
2805 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
2806 &ip_vs_genl_family, NLM_F_MULTI,
2807 IPVS_CMD_NEW_SERVICE);
2808 if (!hdr)
2809 return -EMSGSIZE;
2810
2811 if (ip_vs_genl_fill_service(skb, svc) < 0)
2812 goto nla_put_failure;
2813
2814 return genlmsg_end(skb, hdr);
2815
2816nla_put_failure:
2817 genlmsg_cancel(skb, hdr);
2818 return -EMSGSIZE;
2819}
2820
2821static int ip_vs_genl_dump_services(struct sk_buff *skb,
2822 struct netlink_callback *cb)
2823{
2824 int idx = 0, i;
2825 int start = cb->args[0];
2826 struct ip_vs_service *svc;
Hans Schillstromfc723252011-01-03 14:44:43 +01002827 struct net *net = skb_sknet(skb);
Julius Volz9a812192008-08-14 14:08:44 +02002828
2829 mutex_lock(&__ip_vs_mutex);
2830 for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
2831 list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +01002832 if (++idx <= start || !net_eq(svc->net, net))
Julius Volz9a812192008-08-14 14:08:44 +02002833 continue;
2834 if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
2835 idx--;
2836 goto nla_put_failure;
2837 }
2838 }
2839 }
2840
2841 for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
2842 list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
Hans Schillstromfc723252011-01-03 14:44:43 +01002843 if (++idx <= start || !net_eq(svc->net, net))
Julius Volz9a812192008-08-14 14:08:44 +02002844 continue;
2845 if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
2846 idx--;
2847 goto nla_put_failure;
2848 }
2849 }
2850 }
2851
2852nla_put_failure:
2853 mutex_unlock(&__ip_vs_mutex);
2854 cb->args[0] = idx;
2855
2856 return skb->len;
2857}
2858
Hans Schillstromfc723252011-01-03 14:44:43 +01002859static int ip_vs_genl_parse_service(struct net *net,
2860 struct ip_vs_service_user_kern *usvc,
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002861 struct nlattr *nla, int full_entry,
2862 struct ip_vs_service **ret_svc)
Julius Volz9a812192008-08-14 14:08:44 +02002863{
2864 struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
2865 struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002866 struct ip_vs_service *svc;
Julius Volz9a812192008-08-14 14:08:44 +02002867
2868 /* Parse mandatory identifying service fields first */
2869 if (nla == NULL ||
2870 nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
2871 return -EINVAL;
2872
2873 nla_af = attrs[IPVS_SVC_ATTR_AF];
2874 nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
2875 nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
2876 nla_port = attrs[IPVS_SVC_ATTR_PORT];
2877 nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
2878
2879 if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
2880 return -EINVAL;
2881
Simon Horman258c8892009-12-15 17:01:25 +01002882 memset(usvc, 0, sizeof(*usvc));
2883
Julius Volzc860c6b2008-09-02 15:55:36 +02002884 usvc->af = nla_get_u16(nla_af);
Julius Volzf94fd042008-09-02 15:55:55 +02002885#ifdef CONFIG_IP_VS_IPV6
2886 if (usvc->af != AF_INET && usvc->af != AF_INET6)
2887#else
2888 if (usvc->af != AF_INET)
2889#endif
Julius Volz9a812192008-08-14 14:08:44 +02002890 return -EAFNOSUPPORT;
2891
2892 if (nla_fwmark) {
2893 usvc->protocol = IPPROTO_TCP;
2894 usvc->fwmark = nla_get_u32(nla_fwmark);
2895 } else {
2896 usvc->protocol = nla_get_u16(nla_protocol);
2897 nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
2898 usvc->port = nla_get_u16(nla_port);
2899 usvc->fwmark = 0;
2900 }
2901
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002902 if (usvc->fwmark)
Hans Schillstromfc723252011-01-03 14:44:43 +01002903 svc = __ip_vs_svc_fwm_find(net, usvc->af, usvc->fwmark);
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002904 else
Hans Schillstromfc723252011-01-03 14:44:43 +01002905 svc = __ip_vs_service_find(net, usvc->af, usvc->protocol,
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002906 &usvc->addr, usvc->port);
2907 *ret_svc = svc;
2908
Julius Volz9a812192008-08-14 14:08:44 +02002909 /* If a full entry was requested, check for the additional fields */
2910 if (full_entry) {
Simon Horman0d1e71b2010-08-22 21:37:54 +09002911 struct nlattr *nla_sched, *nla_flags, *nla_pe, *nla_timeout,
Julius Volz9a812192008-08-14 14:08:44 +02002912 *nla_netmask;
2913 struct ip_vs_flags flags;
Julius Volz9a812192008-08-14 14:08:44 +02002914
2915 nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
Simon Horman0d1e71b2010-08-22 21:37:54 +09002916 nla_pe = attrs[IPVS_SVC_ATTR_PE_NAME];
Julius Volz9a812192008-08-14 14:08:44 +02002917 nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
2918 nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
2919 nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
2920
2921 if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
2922 return -EINVAL;
2923
2924 nla_memcpy(&flags, nla_flags, sizeof(flags));
2925
2926 /* prefill flags from service if it already exists */
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002927 if (svc)
Julius Volz9a812192008-08-14 14:08:44 +02002928 usvc->flags = svc->flags;
Julius Volz9a812192008-08-14 14:08:44 +02002929
2930 /* set new flags from userland */
2931 usvc->flags = (usvc->flags & ~flags.mask) |
2932 (flags.flags & flags.mask);
Julius Volzc860c6b2008-09-02 15:55:36 +02002933 usvc->sched_name = nla_data(nla_sched);
Simon Horman0d1e71b2010-08-22 21:37:54 +09002934 usvc->pe_name = nla_pe ? nla_data(nla_pe) : NULL;
Julius Volz9a812192008-08-14 14:08:44 +02002935 usvc->timeout = nla_get_u32(nla_timeout);
2936 usvc->netmask = nla_get_u32(nla_netmask);
2937 }
2938
2939 return 0;
2940}
2941
Hans Schillstromfc723252011-01-03 14:44:43 +01002942static struct ip_vs_service *ip_vs_genl_find_service(struct net *net,
2943 struct nlattr *nla)
Julius Volz9a812192008-08-14 14:08:44 +02002944{
Julius Volzc860c6b2008-09-02 15:55:36 +02002945 struct ip_vs_service_user_kern usvc;
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002946 struct ip_vs_service *svc;
Julius Volz9a812192008-08-14 14:08:44 +02002947 int ret;
2948
Hans Schillstromfc723252011-01-03 14:44:43 +01002949 ret = ip_vs_genl_parse_service(net, &usvc, nla, 0, &svc);
Julian Anastasov26c15cf2010-09-21 18:12:30 +02002950 return ret ? ERR_PTR(ret) : svc;
Julius Volz9a812192008-08-14 14:08:44 +02002951}
2952
2953static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
2954{
2955 struct nlattr *nl_dest;
2956
2957 nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
2958 if (!nl_dest)
2959 return -EMSGSIZE;
2960
2961 NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
2962 NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
2963
2964 NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
2965 atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
2966 NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
2967 NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
2968 NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
2969 NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
2970 atomic_read(&dest->activeconns));
2971 NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
2972 atomic_read(&dest->inactconns));
2973 NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
2974 atomic_read(&dest->persistconns));
2975
2976 if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
2977 goto nla_put_failure;
2978
2979 nla_nest_end(skb, nl_dest);
2980
2981 return 0;
2982
2983nla_put_failure:
2984 nla_nest_cancel(skb, nl_dest);
2985 return -EMSGSIZE;
2986}
2987
2988static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
2989 struct netlink_callback *cb)
2990{
2991 void *hdr;
2992
2993 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
2994 &ip_vs_genl_family, NLM_F_MULTI,
2995 IPVS_CMD_NEW_DEST);
2996 if (!hdr)
2997 return -EMSGSIZE;
2998
2999 if (ip_vs_genl_fill_dest(skb, dest) < 0)
3000 goto nla_put_failure;
3001
3002 return genlmsg_end(skb, hdr);
3003
3004nla_put_failure:
3005 genlmsg_cancel(skb, hdr);
3006 return -EMSGSIZE;
3007}
3008
3009static int ip_vs_genl_dump_dests(struct sk_buff *skb,
3010 struct netlink_callback *cb)
3011{
3012 int idx = 0;
3013 int start = cb->args[0];
3014 struct ip_vs_service *svc;
3015 struct ip_vs_dest *dest;
3016 struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
Hans Schillstromfc723252011-01-03 14:44:43 +01003017 struct net *net;
Julius Volz9a812192008-08-14 14:08:44 +02003018
3019 mutex_lock(&__ip_vs_mutex);
3020
3021 /* Try to find the service for which to dump destinations */
3022 if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
3023 IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
3024 goto out_err;
3025
Hans Schillstromfc723252011-01-03 14:44:43 +01003026 net = skb_sknet(skb);
3027 svc = ip_vs_genl_find_service(net, attrs[IPVS_CMD_ATTR_SERVICE]);
Julius Volz9a812192008-08-14 14:08:44 +02003028 if (IS_ERR(svc) || svc == NULL)
3029 goto out_err;
3030
3031 /* Dump the destinations */
3032 list_for_each_entry(dest, &svc->destinations, n_list) {
3033 if (++idx <= start)
3034 continue;
3035 if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
3036 idx--;
3037 goto nla_put_failure;
3038 }
3039 }
3040
3041nla_put_failure:
3042 cb->args[0] = idx;
Julius Volz9a812192008-08-14 14:08:44 +02003043
3044out_err:
3045 mutex_unlock(&__ip_vs_mutex);
3046
3047 return skb->len;
3048}
3049
Julius Volzc860c6b2008-09-02 15:55:36 +02003050static int ip_vs_genl_parse_dest(struct ip_vs_dest_user_kern *udest,
Julius Volz9a812192008-08-14 14:08:44 +02003051 struct nlattr *nla, int full_entry)
3052{
3053 struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
3054 struct nlattr *nla_addr, *nla_port;
3055
3056 /* Parse mandatory identifying destination fields first */
3057 if (nla == NULL ||
3058 nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
3059 return -EINVAL;
3060
3061 nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
3062 nla_port = attrs[IPVS_DEST_ATTR_PORT];
3063
3064 if (!(nla_addr && nla_port))
3065 return -EINVAL;
3066
Simon Horman258c8892009-12-15 17:01:25 +01003067 memset(udest, 0, sizeof(*udest));
3068
Julius Volz9a812192008-08-14 14:08:44 +02003069 nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
3070 udest->port = nla_get_u16(nla_port);
3071
3072 /* If a full entry was requested, check for the additional fields */
3073 if (full_entry) {
3074 struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
3075 *nla_l_thresh;
3076
3077 nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
3078 nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
3079 nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
3080 nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
3081
3082 if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
3083 return -EINVAL;
3084
3085 udest->conn_flags = nla_get_u32(nla_fwd)
3086 & IP_VS_CONN_F_FWD_MASK;
3087 udest->weight = nla_get_u32(nla_weight);
3088 udest->u_threshold = nla_get_u32(nla_u_thresh);
3089 udest->l_threshold = nla_get_u32(nla_l_thresh);
3090 }
3091
3092 return 0;
3093}
3094
3095static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
3096 const char *mcast_ifn, __be32 syncid)
3097{
3098 struct nlattr *nl_daemon;
3099
3100 nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
3101 if (!nl_daemon)
3102 return -EMSGSIZE;
3103
3104 NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
3105 NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
3106 NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
3107
3108 nla_nest_end(skb, nl_daemon);
3109
3110 return 0;
3111
3112nla_put_failure:
3113 nla_nest_cancel(skb, nl_daemon);
3114 return -EMSGSIZE;
3115}
3116
3117static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
3118 const char *mcast_ifn, __be32 syncid,
3119 struct netlink_callback *cb)
3120{
3121 void *hdr;
3122 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
3123 &ip_vs_genl_family, NLM_F_MULTI,
3124 IPVS_CMD_NEW_DAEMON);
3125 if (!hdr)
3126 return -EMSGSIZE;
3127
3128 if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
3129 goto nla_put_failure;
3130
3131 return genlmsg_end(skb, hdr);
3132
3133nla_put_failure:
3134 genlmsg_cancel(skb, hdr);
3135 return -EMSGSIZE;
3136}
3137
3138static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
3139 struct netlink_callback *cb)
3140{
Hans Schillstromf1313152011-01-03 14:44:55 +01003141 struct net *net = skb_net(skb);
3142 struct netns_ipvs *ipvs = net_ipvs(net);
3143
Julius Volz9a812192008-08-14 14:08:44 +02003144 mutex_lock(&__ip_vs_mutex);
Hans Schillstromf1313152011-01-03 14:44:55 +01003145 if ((ipvs->sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
Julius Volz9a812192008-08-14 14:08:44 +02003146 if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
Hans Schillstromf1313152011-01-03 14:44:55 +01003147 ipvs->master_mcast_ifn,
3148 ipvs->master_syncid, cb) < 0)
Julius Volz9a812192008-08-14 14:08:44 +02003149 goto nla_put_failure;
3150
3151 cb->args[0] = 1;
3152 }
3153
Hans Schillstromf1313152011-01-03 14:44:55 +01003154 if ((ipvs->sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
Julius Volz9a812192008-08-14 14:08:44 +02003155 if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
Hans Schillstromf1313152011-01-03 14:44:55 +01003156 ipvs->backup_mcast_ifn,
3157 ipvs->backup_syncid, cb) < 0)
Julius Volz9a812192008-08-14 14:08:44 +02003158 goto nla_put_failure;
3159
3160 cb->args[1] = 1;
3161 }
3162
3163nla_put_failure:
3164 mutex_unlock(&__ip_vs_mutex);
3165
3166 return skb->len;
3167}
3168
Hans Schillstromf1313152011-01-03 14:44:55 +01003169static int ip_vs_genl_new_daemon(struct net *net, struct nlattr **attrs)
Julius Volz9a812192008-08-14 14:08:44 +02003170{
3171 if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
3172 attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
3173 attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
3174 return -EINVAL;
3175
Hans Schillstromf1313152011-01-03 14:44:55 +01003176 return start_sync_thread(net,
3177 nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
Julius Volz9a812192008-08-14 14:08:44 +02003178 nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
3179 nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
3180}
3181
Hans Schillstromf1313152011-01-03 14:44:55 +01003182static int ip_vs_genl_del_daemon(struct net *net, struct nlattr **attrs)
Julius Volz9a812192008-08-14 14:08:44 +02003183{
3184 if (!attrs[IPVS_DAEMON_ATTR_STATE])
3185 return -EINVAL;
3186
Hans Schillstromf1313152011-01-03 14:44:55 +01003187 return stop_sync_thread(net,
3188 nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
Julius Volz9a812192008-08-14 14:08:44 +02003189}
3190
Hans Schillstrom93304192011-01-03 14:44:51 +01003191static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs)
Julius Volz9a812192008-08-14 14:08:44 +02003192{
3193 struct ip_vs_timeout_user t;
3194
Hans Schillstrom93304192011-01-03 14:44:51 +01003195 __ip_vs_get_timeouts(net, &t);
Julius Volz9a812192008-08-14 14:08:44 +02003196
3197 if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
3198 t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
3199
3200 if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
3201 t.tcp_fin_timeout =
3202 nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
3203
3204 if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
3205 t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
3206
Hans Schillstrom93304192011-01-03 14:44:51 +01003207 return ip_vs_set_timeout(net, &t);
Julius Volz9a812192008-08-14 14:08:44 +02003208}
3209
3210static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
3211{
3212 struct ip_vs_service *svc = NULL;
Julius Volzc860c6b2008-09-02 15:55:36 +02003213 struct ip_vs_service_user_kern usvc;
3214 struct ip_vs_dest_user_kern udest;
Julius Volz9a812192008-08-14 14:08:44 +02003215 int ret = 0, cmd;
3216 int need_full_svc = 0, need_full_dest = 0;
Hans Schillstromfc723252011-01-03 14:44:43 +01003217 struct net *net;
Julius Volz9a812192008-08-14 14:08:44 +02003218
Hans Schillstromfc723252011-01-03 14:44:43 +01003219 net = skb_sknet(skb);
Julius Volz9a812192008-08-14 14:08:44 +02003220 cmd = info->genlhdr->cmd;
3221
3222 mutex_lock(&__ip_vs_mutex);
3223
3224 if (cmd == IPVS_CMD_FLUSH) {
Hans Schillstromfc723252011-01-03 14:44:43 +01003225 ret = ip_vs_flush(net);
Julius Volz9a812192008-08-14 14:08:44 +02003226 goto out;
3227 } else if (cmd == IPVS_CMD_SET_CONFIG) {
Hans Schillstrom93304192011-01-03 14:44:51 +01003228 ret = ip_vs_genl_set_config(net, info->attrs);
Julius Volz9a812192008-08-14 14:08:44 +02003229 goto out;
3230 } else if (cmd == IPVS_CMD_NEW_DAEMON ||
3231 cmd == IPVS_CMD_DEL_DAEMON) {
3232
3233 struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
3234
3235 if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
3236 nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
3237 info->attrs[IPVS_CMD_ATTR_DAEMON],
3238 ip_vs_daemon_policy)) {
3239 ret = -EINVAL;
3240 goto out;
3241 }
3242
3243 if (cmd == IPVS_CMD_NEW_DAEMON)
Hans Schillstromf1313152011-01-03 14:44:55 +01003244 ret = ip_vs_genl_new_daemon(net, daemon_attrs);
Julius Volz9a812192008-08-14 14:08:44 +02003245 else
Hans Schillstromf1313152011-01-03 14:44:55 +01003246 ret = ip_vs_genl_del_daemon(net, daemon_attrs);
Julius Volz9a812192008-08-14 14:08:44 +02003247 goto out;
3248 } else if (cmd == IPVS_CMD_ZERO &&
3249 !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
Hans Schillstromfc723252011-01-03 14:44:43 +01003250 ret = ip_vs_zero_all(net);
Julius Volz9a812192008-08-14 14:08:44 +02003251 goto out;
3252 }
3253
3254 /* All following commands require a service argument, so check if we
3255 * received a valid one. We need a full service specification when
3256 * adding / editing a service. Only identifying members otherwise. */
3257 if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
3258 need_full_svc = 1;
3259
Hans Schillstromfc723252011-01-03 14:44:43 +01003260 ret = ip_vs_genl_parse_service(net, &usvc,
Julius Volz9a812192008-08-14 14:08:44 +02003261 info->attrs[IPVS_CMD_ATTR_SERVICE],
Julian Anastasov26c15cf2010-09-21 18:12:30 +02003262 need_full_svc, &svc);
Julius Volz9a812192008-08-14 14:08:44 +02003263 if (ret)
3264 goto out;
3265
Julius Volz9a812192008-08-14 14:08:44 +02003266 /* Unless we're adding a new service, the service must already exist */
3267 if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
3268 ret = -ESRCH;
3269 goto out;
3270 }
3271
3272 /* Destination commands require a valid destination argument. For
3273 * adding / editing a destination, we need a full destination
3274 * specification. */
3275 if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
3276 cmd == IPVS_CMD_DEL_DEST) {
3277 if (cmd != IPVS_CMD_DEL_DEST)
3278 need_full_dest = 1;
3279
3280 ret = ip_vs_genl_parse_dest(&udest,
3281 info->attrs[IPVS_CMD_ATTR_DEST],
3282 need_full_dest);
3283 if (ret)
3284 goto out;
3285 }
3286
3287 switch (cmd) {
3288 case IPVS_CMD_NEW_SERVICE:
3289 if (svc == NULL)
Hans Schillstromfc723252011-01-03 14:44:43 +01003290 ret = ip_vs_add_service(net, &usvc, &svc);
Julius Volz9a812192008-08-14 14:08:44 +02003291 else
3292 ret = -EEXIST;
3293 break;
3294 case IPVS_CMD_SET_SERVICE:
3295 ret = ip_vs_edit_service(svc, &usvc);
3296 break;
3297 case IPVS_CMD_DEL_SERVICE:
3298 ret = ip_vs_del_service(svc);
Julian Anastasov26c15cf2010-09-21 18:12:30 +02003299 /* do not use svc, it can be freed */
Julius Volz9a812192008-08-14 14:08:44 +02003300 break;
3301 case IPVS_CMD_NEW_DEST:
3302 ret = ip_vs_add_dest(svc, &udest);
3303 break;
3304 case IPVS_CMD_SET_DEST:
3305 ret = ip_vs_edit_dest(svc, &udest);
3306 break;
3307 case IPVS_CMD_DEL_DEST:
3308 ret = ip_vs_del_dest(svc, &udest);
3309 break;
3310 case IPVS_CMD_ZERO:
3311 ret = ip_vs_zero_service(svc);
3312 break;
3313 default:
3314 ret = -EINVAL;
3315 }
3316
3317out:
Julius Volz9a812192008-08-14 14:08:44 +02003318 mutex_unlock(&__ip_vs_mutex);
3319
3320 return ret;
3321}
3322
3323static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
3324{
3325 struct sk_buff *msg;
3326 void *reply;
3327 int ret, cmd, reply_cmd;
Hans Schillstromfc723252011-01-03 14:44:43 +01003328 struct net *net;
Julius Volz9a812192008-08-14 14:08:44 +02003329
Hans Schillstromfc723252011-01-03 14:44:43 +01003330 net = skb_sknet(skb);
Julius Volz9a812192008-08-14 14:08:44 +02003331 cmd = info->genlhdr->cmd;
3332
3333 if (cmd == IPVS_CMD_GET_SERVICE)
3334 reply_cmd = IPVS_CMD_NEW_SERVICE;
3335 else if (cmd == IPVS_CMD_GET_INFO)
3336 reply_cmd = IPVS_CMD_SET_INFO;
3337 else if (cmd == IPVS_CMD_GET_CONFIG)
3338 reply_cmd = IPVS_CMD_SET_CONFIG;
3339 else {
Hannes Eder1e3e2382009-08-02 11:05:41 +00003340 pr_err("unknown Generic Netlink command\n");
Julius Volz9a812192008-08-14 14:08:44 +02003341 return -EINVAL;
3342 }
3343
3344 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
3345 if (!msg)
3346 return -ENOMEM;
3347
3348 mutex_lock(&__ip_vs_mutex);
3349
3350 reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
3351 if (reply == NULL)
3352 goto nla_put_failure;
3353
3354 switch (cmd) {
3355 case IPVS_CMD_GET_SERVICE:
3356 {
3357 struct ip_vs_service *svc;
3358
Hans Schillstromfc723252011-01-03 14:44:43 +01003359 svc = ip_vs_genl_find_service(net,
3360 info->attrs[IPVS_CMD_ATTR_SERVICE]);
Julius Volz9a812192008-08-14 14:08:44 +02003361 if (IS_ERR(svc)) {
3362 ret = PTR_ERR(svc);
3363 goto out_err;
3364 } else if (svc) {
3365 ret = ip_vs_genl_fill_service(msg, svc);
Julius Volz9a812192008-08-14 14:08:44 +02003366 if (ret)
3367 goto nla_put_failure;
3368 } else {
3369 ret = -ESRCH;
3370 goto out_err;
3371 }
3372
3373 break;
3374 }
3375
3376 case IPVS_CMD_GET_CONFIG:
3377 {
3378 struct ip_vs_timeout_user t;
3379
Hans Schillstrom93304192011-01-03 14:44:51 +01003380 __ip_vs_get_timeouts(net, &t);
Julius Volz9a812192008-08-14 14:08:44 +02003381#ifdef CONFIG_IP_VS_PROTO_TCP
3382 NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
3383 NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
3384 t.tcp_fin_timeout);
3385#endif
3386#ifdef CONFIG_IP_VS_PROTO_UDP
3387 NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
3388#endif
3389
3390 break;
3391 }
3392
3393 case IPVS_CMD_GET_INFO:
3394 NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
3395 NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
Catalin(ux) M. BOIE6f7edb42010-01-05 05:50:24 +01003396 ip_vs_conn_tab_size);
Julius Volz9a812192008-08-14 14:08:44 +02003397 break;
3398 }
3399
3400 genlmsg_end(msg, reply);
Johannes Berg134e6372009-07-10 09:51:34 +00003401 ret = genlmsg_reply(msg, info);
Julius Volz9a812192008-08-14 14:08:44 +02003402 goto out;
3403
3404nla_put_failure:
Hannes Eder1e3e2382009-08-02 11:05:41 +00003405 pr_err("not enough space in Netlink message\n");
Julius Volz9a812192008-08-14 14:08:44 +02003406 ret = -EMSGSIZE;
3407
3408out_err:
3409 nlmsg_free(msg);
3410out:
3411 mutex_unlock(&__ip_vs_mutex);
3412
3413 return ret;
3414}
3415
3416
3417static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
3418 {
3419 .cmd = IPVS_CMD_NEW_SERVICE,
3420 .flags = GENL_ADMIN_PERM,
3421 .policy = ip_vs_cmd_policy,
3422 .doit = ip_vs_genl_set_cmd,
3423 },
3424 {
3425 .cmd = IPVS_CMD_SET_SERVICE,
3426 .flags = GENL_ADMIN_PERM,
3427 .policy = ip_vs_cmd_policy,
3428 .doit = ip_vs_genl_set_cmd,
3429 },
3430 {
3431 .cmd = IPVS_CMD_DEL_SERVICE,
3432 .flags = GENL_ADMIN_PERM,
3433 .policy = ip_vs_cmd_policy,
3434 .doit = ip_vs_genl_set_cmd,
3435 },
3436 {
3437 .cmd = IPVS_CMD_GET_SERVICE,
3438 .flags = GENL_ADMIN_PERM,
3439 .doit = ip_vs_genl_get_cmd,
3440 .dumpit = ip_vs_genl_dump_services,
3441 .policy = ip_vs_cmd_policy,
3442 },
3443 {
3444 .cmd = IPVS_CMD_NEW_DEST,
3445 .flags = GENL_ADMIN_PERM,
3446 .policy = ip_vs_cmd_policy,
3447 .doit = ip_vs_genl_set_cmd,
3448 },
3449 {
3450 .cmd = IPVS_CMD_SET_DEST,
3451 .flags = GENL_ADMIN_PERM,
3452 .policy = ip_vs_cmd_policy,
3453 .doit = ip_vs_genl_set_cmd,
3454 },
3455 {
3456 .cmd = IPVS_CMD_DEL_DEST,
3457 .flags = GENL_ADMIN_PERM,
3458 .policy = ip_vs_cmd_policy,
3459 .doit = ip_vs_genl_set_cmd,
3460 },
3461 {
3462 .cmd = IPVS_CMD_GET_DEST,
3463 .flags = GENL_ADMIN_PERM,
3464 .policy = ip_vs_cmd_policy,
3465 .dumpit = ip_vs_genl_dump_dests,
3466 },
3467 {
3468 .cmd = IPVS_CMD_NEW_DAEMON,
3469 .flags = GENL_ADMIN_PERM,
3470 .policy = ip_vs_cmd_policy,
3471 .doit = ip_vs_genl_set_cmd,
3472 },
3473 {
3474 .cmd = IPVS_CMD_DEL_DAEMON,
3475 .flags = GENL_ADMIN_PERM,
3476 .policy = ip_vs_cmd_policy,
3477 .doit = ip_vs_genl_set_cmd,
3478 },
3479 {
3480 .cmd = IPVS_CMD_GET_DAEMON,
3481 .flags = GENL_ADMIN_PERM,
3482 .dumpit = ip_vs_genl_dump_daemons,
3483 },
3484 {
3485 .cmd = IPVS_CMD_SET_CONFIG,
3486 .flags = GENL_ADMIN_PERM,
3487 .policy = ip_vs_cmd_policy,
3488 .doit = ip_vs_genl_set_cmd,
3489 },
3490 {
3491 .cmd = IPVS_CMD_GET_CONFIG,
3492 .flags = GENL_ADMIN_PERM,
3493 .doit = ip_vs_genl_get_cmd,
3494 },
3495 {
3496 .cmd = IPVS_CMD_GET_INFO,
3497 .flags = GENL_ADMIN_PERM,
3498 .doit = ip_vs_genl_get_cmd,
3499 },
3500 {
3501 .cmd = IPVS_CMD_ZERO,
3502 .flags = GENL_ADMIN_PERM,
3503 .policy = ip_vs_cmd_policy,
3504 .doit = ip_vs_genl_set_cmd,
3505 },
3506 {
3507 .cmd = IPVS_CMD_FLUSH,
3508 .flags = GENL_ADMIN_PERM,
3509 .doit = ip_vs_genl_set_cmd,
3510 },
3511};
3512
3513static int __init ip_vs_genl_register(void)
3514{
Michał Mirosław8f698d52009-05-21 10:34:05 +00003515 return genl_register_family_with_ops(&ip_vs_genl_family,
3516 ip_vs_genl_ops, ARRAY_SIZE(ip_vs_genl_ops));
Julius Volz9a812192008-08-14 14:08:44 +02003517}
3518
3519static void ip_vs_genl_unregister(void)
3520{
3521 genl_unregister_family(&ip_vs_genl_family);
3522}
3523
3524/* End of Generic Netlink interface definitions */
3525
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003526/*
3527 * per netns intit/exit func.
3528 */
3529int __net_init __ip_vs_control_init(struct net *net)
3530{
Hans Schillstromfc723252011-01-03 14:44:43 +01003531 int idx;
3532 struct netns_ipvs *ipvs = net_ipvs(net);
3533
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003534 if (!net_eq(net, &init_net)) /* netns not enabled yet */
3535 return -EPERM;
Hans Schillstromb17fc992011-01-03 14:44:56 +01003536 /* procfs stats */
3537 ipvs->tot_stats = kzalloc(sizeof(struct ip_vs_stats), GFP_KERNEL);
3538 if (ipvs->tot_stats == NULL) {
3539 pr_err("%s(): no memory.\n", __func__);
3540 return -ENOMEM;
3541 }
3542 ipvs->cpustats = alloc_percpu(struct ip_vs_cpu_stats);
3543 if (!ipvs->cpustats) {
3544 pr_err("%s() alloc_percpu failed\n", __func__);
3545 goto err_alloc;
3546 }
3547 spin_lock_init(&ipvs->tot_stats->lock);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003548
Hans Schillstromfc723252011-01-03 14:44:43 +01003549 for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++)
3550 INIT_LIST_HEAD(&ipvs->rs_table[idx]);
3551
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003552 proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops);
3553 proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops);
Hans Schillstromb17fc992011-01-03 14:44:56 +01003554 proc_net_fops_create(net, "ip_vs_stats_percpu", 0,
3555 &ip_vs_stats_percpu_fops);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003556 sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path,
3557 vs_vars);
3558 if (sysctl_header == NULL)
3559 goto err_reg;
Hans Schillstromb17fc992011-01-03 14:44:56 +01003560 ip_vs_new_estimator(net, ipvs->tot_stats);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003561 return 0;
3562
3563err_reg:
Hans Schillstromb17fc992011-01-03 14:44:56 +01003564 free_percpu(ipvs->cpustats);
3565err_alloc:
3566 kfree(ipvs->tot_stats);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003567 return -ENOMEM;
3568}
3569
3570static void __net_exit __ip_vs_control_cleanup(struct net *net)
3571{
Hans Schillstromb17fc992011-01-03 14:44:56 +01003572 struct netns_ipvs *ipvs = net_ipvs(net);
3573
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003574 if (!net_eq(net, &init_net)) /* netns not enabled yet */
3575 return;
3576
Hans Schillstromb17fc992011-01-03 14:44:56 +01003577 ip_vs_kill_estimator(net, ipvs->tot_stats);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003578 unregister_net_sysctl_table(sysctl_header);
Hans Schillstromb17fc992011-01-03 14:44:56 +01003579 proc_net_remove(net, "ip_vs_stats_percpu");
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003580 proc_net_remove(net, "ip_vs_stats");
3581 proc_net_remove(net, "ip_vs");
Hans Schillstromb17fc992011-01-03 14:44:56 +01003582 free_percpu(ipvs->cpustats);
3583 kfree(ipvs->tot_stats);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003584}
3585
3586static struct pernet_operations ipvs_control_ops = {
3587 .init = __ip_vs_control_init,
3588 .exit = __ip_vs_control_cleanup,
3589};
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590
Sven Wegener048cf482008-08-10 18:24:35 +00003591int __init ip_vs_control_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003593 int idx;
Hans Schillstromfc723252011-01-03 14:44:43 +01003594 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003595
3596 EnterFunction(2);
3597
Hans Schillstromfc723252011-01-03 14:44:43 +01003598 /* Initialize svc_table, ip_vs_svc_fwm_table, rs_table */
Eduardo Blancod86bef72010-10-19 10:26:47 +01003599 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
3600 INIT_LIST_HEAD(&ip_vs_svc_table[idx]);
3601 INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]);
3602 }
Hans Schillstromfc723252011-01-03 14:44:43 +01003603
3604 ret = register_pernet_subsys(&ipvs_control_ops);
3605 if (ret) {
3606 pr_err("cannot register namespace.\n");
3607 goto err;
Eduardo Blancod86bef72010-10-19 10:26:47 +01003608 }
Hans Schillstromfc723252011-01-03 14:44:43 +01003609
3610 smp_wmb(); /* Do we really need it now ? */
Eduardo Blancod86bef72010-10-19 10:26:47 +01003611
Linus Torvalds1da177e2005-04-16 15:20:36 -07003612 ret = nf_register_sockopt(&ip_vs_sockopts);
3613 if (ret) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00003614 pr_err("cannot register sockopt.\n");
Hans Schillstromfc723252011-01-03 14:44:43 +01003615 goto err_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003616 }
3617
Julius Volz9a812192008-08-14 14:08:44 +02003618 ret = ip_vs_genl_register();
3619 if (ret) {
Hannes Eder1e3e2382009-08-02 11:05:41 +00003620 pr_err("cannot register Generic Netlink interface.\n");
Julius Volz9a812192008-08-14 14:08:44 +02003621 nf_unregister_sockopt(&ip_vs_sockopts);
Hans Schillstromfc723252011-01-03 14:44:43 +01003622 goto err_net;
Julius Volz9a812192008-08-14 14:08:44 +02003623 }
3624
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625 /* Hook the defense timer */
3626 schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD);
3627
3628 LeaveFunction(2);
3629 return 0;
Hans Schillstromfc723252011-01-03 14:44:43 +01003630
3631err_net:
3632 unregister_pernet_subsys(&ipvs_control_ops);
3633err:
3634 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003635}
3636
3637
3638void ip_vs_control_cleanup(void)
3639{
3640 EnterFunction(2);
3641 ip_vs_trash_cleanup();
Tejun Heoafe2c512010-12-14 16:21:17 +01003642 cancel_delayed_work_sync(&defense_work);
Oleg Nesterov28e53bd2007-05-09 02:34:22 -07003643 cancel_work_sync(&defense_work.work);
Hans Schillstrom61b1ab42011-01-03 14:44:42 +01003644 unregister_pernet_subsys(&ipvs_control_ops);
Julius Volz9a812192008-08-14 14:08:44 +02003645 ip_vs_genl_unregister();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003646 nf_unregister_sockopt(&ip_vs_sockopts);
3647 LeaveFunction(2);
3648}