2007-02-09 14:24:47 +00:00
|
|
|
/* Cluster IP hashmark target
|
2005-04-16 22:20:36 +00:00
|
|
|
* (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
|
|
|
|
* based on ideas of Fabio Olive Leite <olive@unixforge.org>
|
|
|
|
*
|
|
|
|
* Development of this code funded by SuSE Linux AG, http://www.suse.com/
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
*/
|
2010-03-19 20:08:16 +00:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/jhash.h>
|
2005-09-17 00:00:04 +00:00
|
|
|
#include <linux/bitops.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/skbuff.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/ip.h>
|
|
|
|
#include <linux/tcp.h>
|
|
|
|
#include <linux/udp.h>
|
|
|
|
#include <linux/icmp.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/netfilter_arp.h>
|
2007-02-07 23:11:19 +00:00
|
|
|
#include <linux/netfilter/x_tables.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/netfilter_ipv4/ip_tables.h>
|
|
|
|
#include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
|
2007-03-14 23:37:25 +00:00
|
|
|
#include <net/netfilter/nf_conntrack.h>
|
2007-09-12 10:01:34 +00:00
|
|
|
#include <net/net_namespace.h>
|
2007-03-14 23:37:25 +00:00
|
|
|
#include <net/checksum.h>
|
2010-08-17 20:34:40 +00:00
|
|
|
#include <net/ip.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
#define CLUSTERIP_VERSION "0.8"
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
2008-01-15 07:42:28 +00:00
|
|
|
MODULE_DESCRIPTION("Xtables: CLUSTERIP target");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
struct clusterip_config {
|
|
|
|
struct list_head list; /* list of all configs */
|
|
|
|
atomic_t refcount; /* reference count */
|
2005-09-16 23:59:46 +00:00
|
|
|
atomic_t entries; /* number of entries/rules
|
|
|
|
* referencing us */
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-09-28 21:22:24 +00:00
|
|
|
__be32 clusterip; /* the IP address */
|
2005-04-16 22:20:36 +00:00
|
|
|
u_int8_t clustermac[ETH_ALEN]; /* the MAC address */
|
|
|
|
struct net_device *dev; /* device */
|
|
|
|
u_int16_t num_total_nodes; /* total number of nodes */
|
2005-09-17 00:00:04 +00:00
|
|
|
unsigned long local_nodes; /* node number array */
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
struct proc_dir_entry *pde; /* proc dir entry */
|
|
|
|
#endif
|
|
|
|
enum clusterip_hashmode hash_mode; /* which hashing mode */
|
|
|
|
u_int32_t hash_initval; /* hash initialization */
|
2010-06-15 11:08:51 +00:00
|
|
|
struct rcu_head rcu;
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static LIST_HEAD(clusterip_configs);
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
/* clusterip_lock protects the clusterip_configs list */
|
2010-06-15 11:08:51 +00:00
|
|
|
static DEFINE_SPINLOCK(clusterip_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2007-02-12 08:55:35 +00:00
|
|
|
static const struct file_operations clusterip_proc_fops;
|
2005-04-16 22:20:36 +00:00
|
|
|
static struct proc_dir_entry *clusterip_procdir;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline void
|
2005-09-16 23:59:46 +00:00
|
|
|
clusterip_config_get(struct clusterip_config *c)
|
|
|
|
{
|
2005-04-16 22:20:36 +00:00
|
|
|
atomic_inc(&c->refcount);
|
|
|
|
}
|
|
|
|
|
2010-06-15 11:08:51 +00:00
|
|
|
|
|
|
|
static void clusterip_config_rcu_free(struct rcu_head *head)
|
|
|
|
{
|
|
|
|
kfree(container_of(head, struct clusterip_config, rcu));
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static inline void
|
2005-09-16 23:59:46 +00:00
|
|
|
clusterip_config_put(struct clusterip_config *c)
|
|
|
|
{
|
|
|
|
if (atomic_dec_and_test(&c->refcount))
|
2010-06-15 11:08:51 +00:00
|
|
|
call_rcu_bh(&c->rcu, clusterip_config_rcu_free);
|
2005-09-16 23:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* decrease the count of entries using/referencing this config. If last
|
|
|
|
* entry(rule) is removed, remove the config from lists, but don't free it
|
|
|
|
* yet, since proc-files could still be holding references */
|
|
|
|
static inline void
|
|
|
|
clusterip_config_entry_put(struct clusterip_config *c)
|
|
|
|
{
|
2010-06-15 11:08:51 +00:00
|
|
|
local_bh_disable();
|
|
|
|
if (atomic_dec_and_lock(&c->entries, &clusterip_lock)) {
|
|
|
|
list_del_rcu(&c->list);
|
|
|
|
spin_unlock(&clusterip_lock);
|
|
|
|
local_bh_enable();
|
2005-09-16 23:59:46 +00:00
|
|
|
|
2010-04-01 21:22:57 +00:00
|
|
|
dev_mc_del(c->dev, c->clustermac);
|
2005-04-16 22:20:36 +00:00
|
|
|
dev_put(c->dev);
|
2005-09-16 23:59:46 +00:00
|
|
|
|
|
|
|
/* In case anyone still accesses the file, the open/close
|
|
|
|
* functions are also incrementing the refcount on their own,
|
|
|
|
* so it's safe to remove the entry even if it's in use. */
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2013-04-12 16:27:28 +00:00
|
|
|
proc_remove(c->pde);
|
2005-09-16 23:59:46 +00:00
|
|
|
#endif
|
2008-04-14 07:44:52 +00:00
|
|
|
return;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2010-06-15 11:08:51 +00:00
|
|
|
local_bh_enable();
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct clusterip_config *
|
2006-09-28 21:22:24 +00:00
|
|
|
__clusterip_config_find(__be32 clusterip)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-12-05 07:22:26 +00:00
|
|
|
struct clusterip_config *c;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-06-15 11:08:51 +00:00
|
|
|
list_for_each_entry_rcu(c, &clusterip_configs, list) {
|
2007-07-08 05:19:08 +00:00
|
|
|
if (c->clusterip == clusterip)
|
2005-04-16 22:20:36 +00:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct clusterip_config *
|
2006-09-28 21:22:24 +00:00
|
|
|
clusterip_config_find_get(__be32 clusterip, int entry)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct clusterip_config *c;
|
|
|
|
|
2010-06-15 11:08:51 +00:00
|
|
|
rcu_read_lock_bh();
|
2005-04-16 22:20:36 +00:00
|
|
|
c = __clusterip_config_find(clusterip);
|
2010-06-15 11:08:51 +00:00
|
|
|
if (c) {
|
|
|
|
if (unlikely(!atomic_inc_not_zero(&c->refcount)))
|
|
|
|
c = NULL;
|
2018-02-08 21:53:52 +00:00
|
|
|
else if (entry) {
|
|
|
|
if (unlikely(!atomic_inc_not_zero(&c->entries))) {
|
|
|
|
clusterip_config_put(c);
|
|
|
|
c = NULL;
|
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2010-06-15 11:08:51 +00:00
|
|
|
rcu_read_unlock_bh();
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
static void
|
|
|
|
clusterip_config_init_nodelist(struct clusterip_config *c,
|
|
|
|
const struct ipt_clusterip_tgt_info *i)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
2007-07-08 05:19:08 +00:00
|
|
|
for (n = 0; n < i->num_local_nodes; n++)
|
2005-09-17 00:00:04 +00:00
|
|
|
set_bit(i->local_nodes[n] - 1, &c->local_nodes);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static struct clusterip_config *
|
2008-04-14 07:56:05 +00:00
|
|
|
clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
|
2005-04-16 22:20:36 +00:00
|
|
|
struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct clusterip_config *c;
|
|
|
|
|
2006-07-21 21:51:30 +00:00
|
|
|
c = kzalloc(sizeof(*c), GFP_ATOMIC);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!c)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
c->dev = dev;
|
|
|
|
c->clusterip = ip;
|
|
|
|
memcpy(&c->clustermac, &i->clustermac, ETH_ALEN);
|
|
|
|
c->num_total_nodes = i->num_total_nodes;
|
2005-09-17 00:00:04 +00:00
|
|
|
clusterip_config_init_nodelist(c, i);
|
2005-04-16 22:20:36 +00:00
|
|
|
c->hash_mode = i->hash_mode;
|
|
|
|
c->hash_initval = i->hash_initval;
|
|
|
|
atomic_set(&c->refcount, 1);
|
2005-09-16 23:59:46 +00:00
|
|
|
atomic_set(&c->entries, 1);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2006-11-29 01:35:42 +00:00
|
|
|
{
|
|
|
|
char buffer[16];
|
|
|
|
|
|
|
|
/* create proc dir entry */
|
2008-10-31 07:53:08 +00:00
|
|
|
sprintf(buffer, "%pI4", &ip);
|
2008-05-02 09:45:42 +00:00
|
|
|
c->pde = proc_create_data(buffer, S_IWUSR|S_IRUSR,
|
|
|
|
clusterip_procdir,
|
|
|
|
&clusterip_proc_fops, c);
|
2006-11-29 01:35:42 +00:00
|
|
|
if (!c->pde) {
|
|
|
|
kfree(c);
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-06-15 11:08:51 +00:00
|
|
|
spin_lock_bh(&clusterip_lock);
|
|
|
|
list_add_rcu(&c->list, &clusterip_configs);
|
|
|
|
spin_unlock_bh(&clusterip_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2006-11-29 01:35:42 +00:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2005-04-16 22:20:36 +00:00
|
|
|
static int
|
|
|
|
clusterip_add_node(struct clusterip_config *c, u_int16_t nodenum)
|
|
|
|
{
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
if (nodenum == 0 ||
|
|
|
|
nodenum > c->num_total_nodes)
|
2005-04-16 22:20:36 +00:00
|
|
|
return 1;
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
/* check if we already have this number in our bitfield */
|
|
|
|
if (test_and_set_bit(nodenum - 1, &c->local_nodes))
|
|
|
|
return 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-08 05:16:26 +00:00
|
|
|
static bool
|
2005-04-16 22:20:36 +00:00
|
|
|
clusterip_del_node(struct clusterip_config *c, u_int16_t nodenum)
|
|
|
|
{
|
2005-09-17 00:00:04 +00:00
|
|
|
if (nodenum == 0 ||
|
|
|
|
nodenum > c->num_total_nodes)
|
2007-07-08 05:16:26 +00:00
|
|
|
return true;
|
2007-02-09 14:24:47 +00:00
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
if (test_and_clear_bit(nodenum - 1, &c->local_nodes))
|
2007-07-08 05:16:26 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-07-08 05:16:26 +00:00
|
|
|
return true;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2006-11-29 01:35:42 +00:00
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
static inline u_int32_t
|
2007-07-08 05:16:55 +00:00
|
|
|
clusterip_hashfn(const struct sk_buff *skb,
|
|
|
|
const struct clusterip_config *config)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-07-08 05:16:55 +00:00
|
|
|
const struct iphdr *iph = ip_hdr(skb);
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned long hashval;
|
2010-08-17 20:34:40 +00:00
|
|
|
u_int16_t sport = 0, dport = 0;
|
|
|
|
int poff;
|
|
|
|
|
|
|
|
poff = proto_ports_offset(iph->protocol);
|
|
|
|
if (poff >= 0) {
|
|
|
|
const u_int16_t *ports;
|
|
|
|
u16 _ports[2];
|
|
|
|
|
|
|
|
ports = skb_header_pointer(skb, iph->ihl * 4 + poff, 4, _ports);
|
|
|
|
if (ports) {
|
|
|
|
sport = ports[0];
|
|
|
|
dport = ports[1];
|
|
|
|
}
|
|
|
|
} else {
|
2012-05-13 21:56:26 +00:00
|
|
|
net_info_ratelimited("unknown protocol %u\n", iph->protocol);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (config->hash_mode) {
|
|
|
|
case CLUSTERIP_HASHMODE_SIP:
|
|
|
|
hashval = jhash_1word(ntohl(iph->saddr),
|
|
|
|
config->hash_initval);
|
|
|
|
break;
|
|
|
|
case CLUSTERIP_HASHMODE_SIP_SPT:
|
2007-02-09 14:24:47 +00:00
|
|
|
hashval = jhash_2words(ntohl(iph->saddr), sport,
|
2005-04-16 22:20:36 +00:00
|
|
|
config->hash_initval);
|
|
|
|
break;
|
|
|
|
case CLUSTERIP_HASHMODE_SIP_SPT_DPT:
|
|
|
|
hashval = jhash_3words(ntohl(iph->saddr), sport, dport,
|
|
|
|
config->hash_initval);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* to make gcc happy */
|
|
|
|
hashval = 0;
|
|
|
|
/* This cannot happen, unless the check function wasn't called
|
|
|
|
* at rule load time */
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("unknown mode %u\n", config->hash_mode);
|
2005-04-16 22:20:36 +00:00
|
|
|
BUG();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* node numbers are 1..n, not 0..n */
|
2007-12-18 06:45:52 +00:00
|
|
|
return (((u64)hashval * config->num_total_nodes) >> 32) + 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2007-07-08 05:16:55 +00:00
|
|
|
clusterip_responsible(const struct clusterip_config *config, u_int32_t hash)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-09-17 00:00:04 +00:00
|
|
|
return test_bit(hash - 1, &config->local_nodes);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* IPTABLES TARGET
|
2005-04-16 22:20:36 +00:00
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
static unsigned int
|
2009-07-05 17:43:26 +00:00
|
|
|
clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-10-08 09:35:19 +00:00
|
|
|
const struct ipt_clusterip_tgt_info *cipinfo = par->targinfo;
|
2007-03-14 23:37:25 +00:00
|
|
|
struct nf_conn *ct;
|
2005-04-16 22:20:36 +00:00
|
|
|
enum ip_conntrack_info ctinfo;
|
2007-03-14 23:37:25 +00:00
|
|
|
u_int32_t hash;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* don't need to clusterip_config_get() here, since refcount
|
|
|
|
* is only decremented by destroy() - and ip_tables guarantees
|
|
|
|
* that the ->target() function isn't called after ->destroy() */
|
|
|
|
|
2007-10-15 07:53:15 +00:00
|
|
|
ct = nf_ct_get(skb, &ctinfo);
|
2011-01-18 15:27:56 +00:00
|
|
|
if (ct == NULL)
|
2005-04-16 22:20:36 +00:00
|
|
|
return NF_DROP;
|
|
|
|
|
|
|
|
/* special case: ICMP error handling. conntrack distinguishes between
|
|
|
|
* error messages (RELATED) and information requests (see below) */
|
2009-11-23 22:17:06 +00:00
|
|
|
if (ip_hdr(skb)->protocol == IPPROTO_ICMP &&
|
|
|
|
(ctinfo == IP_CT_RELATED ||
|
2011-05-19 13:44:27 +00:00
|
|
|
ctinfo == IP_CT_RELATED_REPLY))
|
2007-02-07 23:11:19 +00:00
|
|
|
return XT_CONTINUE;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
/* ip_conntrack_icmp guarantees us that we only have ICMP_ECHO,
|
2005-04-16 22:20:36 +00:00
|
|
|
* TIMESTAMP, INFO_REQUEST or ADDRESS type icmp packets from here
|
|
|
|
* on, which all have an ID field [relevant for hashing]. */
|
|
|
|
|
2007-10-15 07:53:15 +00:00
|
|
|
hash = clusterip_hashfn(skb, cipinfo->config);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
switch (ctinfo) {
|
2011-07-01 09:43:06 +00:00
|
|
|
case IP_CT_NEW:
|
|
|
|
ct->mark = hash;
|
|
|
|
break;
|
|
|
|
case IP_CT_RELATED:
|
|
|
|
case IP_CT_RELATED_REPLY:
|
|
|
|
/* FIXME: we don't handle expectations at the moment.
|
|
|
|
* They can arrive on a different node than
|
|
|
|
* the master connection (e.g. FTP passive mode) */
|
|
|
|
case IP_CT_ESTABLISHED:
|
|
|
|
case IP_CT_ESTABLISHED_REPLY:
|
|
|
|
break;
|
|
|
|
default: /* Prevent gcc warnings */
|
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-07-08 05:39:38 +00:00
|
|
|
#ifdef DEBUG
|
2008-04-14 09:15:54 +00:00
|
|
|
nf_ct_dump_tuple_ip(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif
|
2007-07-08 05:39:38 +00:00
|
|
|
pr_debug("hash=%u ct_hash=%u ", hash, ct->mark);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!clusterip_responsible(cipinfo->config, hash)) {
|
2007-07-08 05:39:38 +00:00
|
|
|
pr_debug("not responsible\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return NF_DROP;
|
|
|
|
}
|
2007-07-08 05:39:38 +00:00
|
|
|
pr_debug("responsible\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* despite being received via linklayer multicast, this is
|
|
|
|
* actually a unicast IP packet. TCP doesn't like PACKET_MULTICAST */
|
2007-10-15 07:53:15 +00:00
|
|
|
skb->pkt_type = PACKET_HOST;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-02-07 23:11:19 +00:00
|
|
|
return XT_CONTINUE;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2010-03-19 16:16:42 +00:00
|
|
|
static int clusterip_tg_check(const struct xt_tgchk_param *par)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-10-08 09:35:19 +00:00
|
|
|
struct ipt_clusterip_tgt_info *cipinfo = par->targinfo;
|
|
|
|
const struct ipt_entry *e = par->entryinfo;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct clusterip_config *config;
|
2018-01-30 14:21:34 +00:00
|
|
|
int ret, i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP &&
|
|
|
|
cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP_SPT &&
|
|
|
|
cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP_SPT_DPT) {
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("unknown mode %u\n", cipinfo->hash_mode);
|
2010-03-25 15:34:45 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
}
|
2009-11-23 22:17:06 +00:00
|
|
|
if (e->ip.dmsk.s_addr != htonl(0xffffffff) ||
|
|
|
|
e->ip.dst.s_addr == 0) {
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("Please specify destination IP\n");
|
2010-03-25 15:34:45 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2018-01-30 14:21:34 +00:00
|
|
|
if (cipinfo->num_local_nodes > ARRAY_SIZE(cipinfo->local_nodes)) {
|
|
|
|
pr_info("bad num_local_nodes %u\n", cipinfo->num_local_nodes);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
for (i = 0; i < cipinfo->num_local_nodes; i++) {
|
|
|
|
if (cipinfo->local_nodes[i] - 1 >=
|
|
|
|
sizeof(config->local_nodes) * 8) {
|
|
|
|
pr_info("bad local_nodes[%d] %u\n",
|
|
|
|
i, cipinfo->local_nodes[i]);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-16 23:59:46 +00:00
|
|
|
config = clusterip_config_find_get(e->ip.dst.s_addr, 1);
|
2007-07-08 05:38:30 +00:00
|
|
|
if (!config) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("no config found for %pI4, need 'new'\n",
|
|
|
|
&e->ip.dst.s_addr);
|
2010-03-25 15:34:45 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
|
|
|
struct net_device *dev;
|
|
|
|
|
|
|
|
if (e->ip.iniface[0] == '\0') {
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("Please specify an interface name\n");
|
2010-03-25 15:34:45 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-09-17 18:56:21 +00:00
|
|
|
dev = dev_get_by_name(&init_net, e->ip.iniface);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!dev) {
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("no such interface %s\n",
|
|
|
|
e->ip.iniface);
|
2010-03-19 16:32:59 +00:00
|
|
|
return -ENOENT;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
config = clusterip_config_init(cipinfo,
|
2005-04-16 22:20:36 +00:00
|
|
|
e->ip.dst.s_addr, dev);
|
|
|
|
if (!config) {
|
|
|
|
dev_put(dev);
|
2010-03-19 16:32:59 +00:00
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2010-04-01 21:22:57 +00:00
|
|
|
dev_mc_add(config->dev, config->clustermac);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2007-07-08 05:38:30 +00:00
|
|
|
cipinfo->config = config;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-03-19 16:32:59 +00:00
|
|
|
ret = nf_ct_l3proto_try_module_get(par->family);
|
2010-03-21 03:05:56 +00:00
|
|
|
if (ret < 0)
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("cannot load conntrack support for proto=%u\n",
|
|
|
|
par->family);
|
2010-03-21 03:05:56 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* drop reference count of cluster config when rule is deleted */
|
2008-10-08 09:35:19 +00:00
|
|
|
static void clusterip_tg_destroy(const struct xt_tgdtor_param *par)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-10-08 09:35:19 +00:00
|
|
|
const struct ipt_clusterip_tgt_info *cipinfo = par->targinfo;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-16 23:59:46 +00:00
|
|
|
/* if no more entries are referencing the config, remove it
|
|
|
|
* from the list and destroy the proc entry */
|
|
|
|
clusterip_config_entry_put(cipinfo->config);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
clusterip_config_put(cipinfo->config);
|
2006-12-12 08:29:02 +00:00
|
|
|
|
2010-03-19 17:47:51 +00:00
|
|
|
nf_ct_l3proto_module_put(par->family);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-07-08 05:38:30 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
struct compat_ipt_clusterip_tgt_info
|
|
|
|
{
|
|
|
|
u_int32_t flags;
|
|
|
|
u_int8_t clustermac[6];
|
|
|
|
u_int16_t num_total_nodes;
|
|
|
|
u_int16_t num_local_nodes;
|
|
|
|
u_int16_t local_nodes[CLUSTERIP_MAX_NODES];
|
|
|
|
u_int32_t hash_mode;
|
|
|
|
u_int32_t hash_initval;
|
|
|
|
compat_uptr_t config;
|
|
|
|
};
|
|
|
|
#endif /* CONFIG_COMPAT */
|
|
|
|
|
2007-12-05 07:24:03 +00:00
|
|
|
static struct xt_target clusterip_tg_reg __read_mostly = {
|
2006-03-21 02:01:14 +00:00
|
|
|
.name = "CLUSTERIP",
|
2008-10-08 09:35:01 +00:00
|
|
|
.family = NFPROTO_IPV4,
|
2007-12-05 07:24:03 +00:00
|
|
|
.target = clusterip_tg,
|
|
|
|
.checkentry = clusterip_tg_check,
|
|
|
|
.destroy = clusterip_tg_destroy,
|
2007-07-08 05:38:30 +00:00
|
|
|
.targetsize = sizeof(struct ipt_clusterip_tgt_info),
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compatsize = sizeof(struct compat_ipt_clusterip_tgt_info),
|
|
|
|
#endif /* CONFIG_COMPAT */
|
2006-03-21 02:01:14 +00:00
|
|
|
.me = THIS_MODULE
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* ARP MANGLING CODE
|
2005-04-16 22:20:36 +00:00
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/* hardcoded for 48bit ethernet and 32bit ipv4 addresses */
|
|
|
|
struct arp_payload {
|
|
|
|
u_int8_t src_hw[ETH_ALEN];
|
2006-09-28 21:22:24 +00:00
|
|
|
__be32 src_ip;
|
2005-04-16 22:20:36 +00:00
|
|
|
u_int8_t dst_hw[ETH_ALEN];
|
2006-09-28 21:22:24 +00:00
|
|
|
__be32 dst_ip;
|
2010-07-21 10:59:58 +00:00
|
|
|
} __packed;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-07-08 05:39:38 +00:00
|
|
|
#ifdef DEBUG
|
2007-02-09 14:24:47 +00:00
|
|
|
static void arp_print(struct arp_payload *payload)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
#define HBUFFERLEN 30
|
|
|
|
char hbuffer[HBUFFERLEN];
|
|
|
|
int j,k;
|
|
|
|
|
|
|
|
for (k=0, j=0; k < HBUFFERLEN-3 && j < ETH_ALEN; j++) {
|
2008-07-30 23:30:15 +00:00
|
|
|
hbuffer[k++] = hex_asc_hi(payload->src_hw[j]);
|
|
|
|
hbuffer[k++] = hex_asc_lo(payload->src_hw[j]);
|
2005-04-16 22:20:36 +00:00
|
|
|
hbuffer[k++]=':';
|
|
|
|
}
|
|
|
|
hbuffer[--k]='\0';
|
|
|
|
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_debug("src %pI4@%s, dst %pI4\n",
|
|
|
|
&payload->src_ip, hbuffer, &payload->dst_ip);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
arp_mangle(unsigned int hook,
|
2007-10-15 07:53:15 +00:00
|
|
|
struct sk_buff *skb,
|
2005-04-16 22:20:36 +00:00
|
|
|
const struct net_device *in,
|
|
|
|
const struct net_device *out,
|
|
|
|
int (*okfn)(struct sk_buff *))
|
|
|
|
{
|
2007-10-15 07:53:15 +00:00
|
|
|
struct arphdr *arp = arp_hdr(skb);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct arp_payload *payload;
|
|
|
|
struct clusterip_config *c;
|
|
|
|
|
|
|
|
/* we don't care about non-ethernet and non-ipv4 ARP */
|
2009-11-23 22:17:06 +00:00
|
|
|
if (arp->ar_hrd != htons(ARPHRD_ETHER) ||
|
|
|
|
arp->ar_pro != htons(ETH_P_IP) ||
|
|
|
|
arp->ar_pln != 4 || arp->ar_hln != ETH_ALEN)
|
2005-04-16 22:20:36 +00:00
|
|
|
return NF_ACCEPT;
|
|
|
|
|
2005-06-28 19:49:30 +00:00
|
|
|
/* we only want to mangle arp requests and replies */
|
2009-11-23 22:17:06 +00:00
|
|
|
if (arp->ar_op != htons(ARPOP_REPLY) &&
|
|
|
|
arp->ar_op != htons(ARPOP_REQUEST))
|
2005-04-16 22:20:36 +00:00
|
|
|
return NF_ACCEPT;
|
|
|
|
|
|
|
|
payload = (void *)(arp+1);
|
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
/* if there is no clusterip configuration for the arp reply's
|
2005-04-16 22:20:36 +00:00
|
|
|
* source ip, we don't want to mangle it */
|
2005-09-16 23:59:46 +00:00
|
|
|
c = clusterip_config_find_get(payload->src_ip, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!c)
|
|
|
|
return NF_ACCEPT;
|
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
/* normally the linux kernel always replies to arp queries of
|
2005-04-16 22:20:36 +00:00
|
|
|
* addresses on different interfacs. However, in the CLUSTERIP case
|
|
|
|
* this wouldn't work, since we didn't subscribe the mcast group on
|
|
|
|
* other interfaces */
|
|
|
|
if (c->dev != out) {
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_debug("not mangling arp reply on different "
|
2007-07-08 05:39:38 +00:00
|
|
|
"interface: cip'%s'-skb'%s'\n",
|
|
|
|
c->dev->name, out->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
clusterip_config_put(c);
|
|
|
|
return NF_ACCEPT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mangle reply hardware address */
|
|
|
|
memcpy(payload->src_hw, c->clustermac, arp->ar_hln);
|
|
|
|
|
2007-07-08 05:39:38 +00:00
|
|
|
#ifdef DEBUG
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_debug("mangled arp reply: ");
|
2005-04-16 22:20:36 +00:00
|
|
|
arp_print(payload);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
clusterip_config_put(c);
|
|
|
|
|
|
|
|
return NF_ACCEPT;
|
|
|
|
}
|
|
|
|
|
2007-12-05 09:23:00 +00:00
|
|
|
static struct nf_hook_ops cip_arp_ops __read_mostly = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.hook = arp_mangle,
|
2008-10-08 09:35:01 +00:00
|
|
|
.pf = NFPROTO_ARP,
|
2005-04-16 22:20:36 +00:00
|
|
|
.hooknum = NF_ARP_OUT,
|
|
|
|
.priority = -1
|
|
|
|
};
|
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* PROC DIR HANDLING
|
2005-04-16 22:20:36 +00:00
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
struct clusterip_seq_position {
|
|
|
|
unsigned int pos; /* position */
|
|
|
|
unsigned int weight; /* number of bits set == size */
|
|
|
|
unsigned int bit; /* current bit */
|
|
|
|
unsigned long val; /* current value */
|
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static void *clusterip_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
|
{
|
2010-01-22 21:21:18 +00:00
|
|
|
struct clusterip_config *c = s->private;
|
2005-09-17 00:00:04 +00:00
|
|
|
unsigned int weight;
|
|
|
|
u_int32_t local_nodes;
|
|
|
|
struct clusterip_seq_position *idx;
|
|
|
|
|
|
|
|
/* FIXME: possible race */
|
|
|
|
local_nodes = c->local_nodes;
|
|
|
|
weight = hweight32(local_nodes);
|
|
|
|
if (*pos >= weight)
|
2005-04-16 22:20:36 +00:00
|
|
|
return NULL;
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
idx = kmalloc(sizeof(struct clusterip_seq_position), GFP_KERNEL);
|
|
|
|
if (!idx)
|
2005-04-16 22:20:36 +00:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
idx->pos = *pos;
|
|
|
|
idx->weight = weight;
|
|
|
|
idx->bit = ffs(local_nodes);
|
|
|
|
idx->val = local_nodes;
|
|
|
|
clear_bit(idx->bit - 1, &idx->val);
|
|
|
|
|
|
|
|
return idx;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *clusterip_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
|
{
|
2008-04-14 07:56:05 +00:00
|
|
|
struct clusterip_seq_position *idx = v;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
*pos = ++idx->pos;
|
|
|
|
if (*pos >= idx->weight) {
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(v);
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-09-17 00:00:04 +00:00
|
|
|
idx->bit = ffs(idx->val);
|
|
|
|
clear_bit(idx->bit - 1, &idx->val);
|
|
|
|
return idx;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void clusterip_seq_stop(struct seq_file *s, void *v)
|
|
|
|
{
|
2010-04-01 10:54:09 +00:00
|
|
|
if (!IS_ERR(v))
|
|
|
|
kfree(v);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int clusterip_seq_show(struct seq_file *s, void *v)
|
|
|
|
{
|
2008-04-14 07:56:05 +00:00
|
|
|
struct clusterip_seq_position *idx = v;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
if (idx->pos != 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
seq_putc(s, ',');
|
|
|
|
|
2005-09-17 00:00:04 +00:00
|
|
|
seq_printf(s, "%u", idx->bit);
|
|
|
|
|
|
|
|
if (idx->pos == idx->weight - 1)
|
2005-04-16 22:20:36 +00:00
|
|
|
seq_putc(s, '\n');
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-11 06:07:31 +00:00
|
|
|
static const struct seq_operations clusterip_seq_ops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.start = clusterip_seq_start,
|
|
|
|
.next = clusterip_seq_next,
|
|
|
|
.stop = clusterip_seq_stop,
|
|
|
|
.show = clusterip_seq_show,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int clusterip_proc_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
int ret = seq_open(file, &clusterip_seq_ops);
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
struct seq_file *sf = file->private_data;
|
2013-03-31 22:16:14 +00:00
|
|
|
struct clusterip_config *c = PDE_DATA(inode);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-01-22 21:21:18 +00:00
|
|
|
sf->private = c;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
clusterip_config_get(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clusterip_proc_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
2013-03-31 22:16:14 +00:00
|
|
|
struct clusterip_config *c = PDE_DATA(inode);
|
2005-04-16 22:20:36 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = seq_release(inode, file);
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
clusterip_config_put(c);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t clusterip_proc_write(struct file *file, const char __user *input,
|
|
|
|
size_t size, loff_t *ofs)
|
|
|
|
{
|
2013-03-31 22:16:14 +00:00
|
|
|
struct clusterip_config *c = PDE_DATA(file_inode(file));
|
2005-04-16 22:20:36 +00:00
|
|
|
#define PROC_WRITELEN 10
|
|
|
|
char buffer[PROC_WRITELEN+1];
|
|
|
|
unsigned long nodenum;
|
2012-12-09 23:12:28 +00:00
|
|
|
int rc;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2011-03-20 14:42:52 +00:00
|
|
|
if (size > PROC_WRITELEN)
|
|
|
|
return -EIO;
|
|
|
|
if (copy_from_user(buffer, input, size))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EFAULT;
|
2011-03-20 14:42:52 +00:00
|
|
|
buffer[size] = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (*buffer == '+') {
|
2012-12-09 23:12:28 +00:00
|
|
|
rc = kstrtoul(buffer+1, 10, &nodenum);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (clusterip_add_node(c, nodenum))
|
|
|
|
return -ENOMEM;
|
|
|
|
} else if (*buffer == '-') {
|
2012-12-09 23:12:28 +00:00
|
|
|
rc = kstrtoul(buffer+1, 10, &nodenum);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (clusterip_del_node(c, nodenum))
|
|
|
|
return -ENOENT;
|
|
|
|
} else
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2007-02-12 08:55:35 +00:00
|
|
|
static const struct file_operations clusterip_proc_fops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = clusterip_proc_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.write = clusterip_proc_write,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
.release = clusterip_proc_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
|
2007-12-05 07:24:03 +00:00
|
|
|
static int __init clusterip_tg_init(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2007-12-05 07:24:03 +00:00
|
|
|
ret = xt_register_target(&clusterip_tg_reg);
|
2006-04-06 21:11:30 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-06 21:11:30 +00:00
|
|
|
ret = nf_register_hook(&cip_arp_ops);
|
|
|
|
if (ret < 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
goto cleanup_target;
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2007-09-12 10:01:34 +00:00
|
|
|
clusterip_procdir = proc_mkdir("ipt_CLUSTERIP", init_net.proc_net);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!clusterip_procdir) {
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_err("Unable to proc dir entry\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto cleanup_hook;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("ClusterIP Version %s loaded successfully\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
CLUSTERIP_VERSION);
|
|
|
|
return 0;
|
|
|
|
|
2006-11-29 01:35:42 +00:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2005-04-16 22:20:36 +00:00
|
|
|
cleanup_hook:
|
|
|
|
nf_unregister_hook(&cip_arp_ops);
|
2006-11-29 01:35:42 +00:00
|
|
|
#endif /* CONFIG_PROC_FS */
|
2005-04-16 22:20:36 +00:00
|
|
|
cleanup_target:
|
2007-12-05 07:24:03 +00:00
|
|
|
xt_unregister_target(&clusterip_tg_reg);
|
2006-04-06 21:11:30 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-12-05 07:24:03 +00:00
|
|
|
static void __exit clusterip_tg_exit(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("ClusterIP Version %s unloading\n", CLUSTERIP_VERSION);
|
2006-04-06 21:11:30 +00:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2013-04-12 16:27:28 +00:00
|
|
|
proc_remove(clusterip_procdir);
|
2006-04-06 21:11:30 +00:00
|
|
|
#endif
|
|
|
|
nf_unregister_hook(&cip_arp_ops);
|
2007-12-05 07:24:03 +00:00
|
|
|
xt_unregister_target(&clusterip_tg_reg);
|
2010-06-15 11:08:51 +00:00
|
|
|
|
|
|
|
/* Wait for completion of call_rcu_bh()'s (clusterip_config_rcu_free) */
|
|
|
|
rcu_barrier_bh();
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-12-05 07:24:03 +00:00
|
|
|
module_init(clusterip_tg_init);
|
|
|
|
module_exit(clusterip_tg_exit);
|