From cd958a6c92c84095a439780b53832bb3aae2d512 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 6 May 2009 13:01:20 +0200 Subject: extensions: add `cluster' match support This patch adds support for the cluster match to iptables. Signed-off-by: Pablo Neira Ayuso --- extensions/libxt_cluster.c | 238 +++++++++++++++++++++++++++++++++++++++++++ extensions/libxt_cluster.man | 62 +++++++++++ 2 files changed, 300 insertions(+) create mode 100644 extensions/libxt_cluster.c create mode 100644 extensions/libxt_cluster.man (limited to 'extensions') diff --git a/extensions/libxt_cluster.c b/extensions/libxt_cluster.c new file mode 100644 index 00000000..1659a60e --- /dev/null +++ b/extensions/libxt_cluster.c @@ -0,0 +1,238 @@ +/* + * (C) 2009 by Pablo Neira Ayuso + * + * 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. + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +/* hack to keep for check */ +static unsigned int total_nodes; +static unsigned int node_mask; + +static void +cluster_help(void) +{ + printf( +"cluster match options:\n" +" --cluster-total-nodes Set number of total nodes in cluster\n" +" [!] --cluster-local-node Set the local node number\n" +" [!] --cluster-local-nodemask Set the local node mask\n" +" --cluster-hash-seed Set seed value of the Jenkins hash\n"); +} + +enum { + CLUSTER_OPT_TOTAL_NODES, + CLUSTER_OPT_LOCAL_NODE, + CLUSTER_OPT_NODE_MASK, + CLUSTER_OPT_HASH_SEED, +}; + +static const struct option cluster_opts[] = { + { "cluster-total-nodes", 1, NULL, CLUSTER_OPT_TOTAL_NODES }, + { "cluster-local-node", 1, NULL, CLUSTER_OPT_LOCAL_NODE }, + { "cluster-local-nodemask", 1, NULL, CLUSTER_OPT_NODE_MASK }, + { "cluster-hash-seed", 1, NULL, CLUSTER_OPT_HASH_SEED }, + { .name = NULL } +}; + +static int +cluster_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_cluster_match_info *info = (void *)(*match)->data; + unsigned int num; + + switch (c) { + case CLUSTER_OPT_TOTAL_NODES: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-total-nodes' once"); + } + if (!xtables_strtoui(optarg, NULL, &num, 1, + XT_CLUSTER_NODES_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s' in " + "`--cluster-total-nodes'", optarg); + } + total_nodes = num; + info->total_nodes = total_nodes = num; + *flags |= 1 << c; + break; + case CLUSTER_OPT_LOCAL_NODE: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-local-node' once"); + } + if (*flags & (1 << CLUSTER_OPT_NODE_MASK)) { + xtables_error(PARAMETER_PROBLEM, "You cannot use " + "`--cluster-local-nodemask' and " + "`--cluster-local-node'"); + } + xtables_check_inverse(optarg, &invert, &optind, 0); + + if (!xtables_strtoui(optarg, NULL, &num, 1, + XT_CLUSTER_NODES_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s' in " + "`--cluster-local-node'", optarg); + } + if (invert) + info->flags |= (1 << XT_CLUSTER_F_INV); + + info->node_mask = node_mask = (1 << (num - 1)); + *flags |= 1 << c; + break; + case CLUSTER_OPT_NODE_MASK: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-local-node' once"); + } + if (*flags & (1 << CLUSTER_OPT_LOCAL_NODE)) { + xtables_error(PARAMETER_PROBLEM, "You cannot use " + "`--cluster-local-nodemask' and " + "`--cluster-local-node'"); + } + xtables_check_inverse(optarg, &invert, &optind, 0); + + if (!xtables_strtoui(optarg, NULL, &num, 1, + XT_CLUSTER_NODES_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s' in " + "`--cluster-local-node'", optarg); + } + if (invert) + info->flags |= (1 << XT_CLUSTER_F_INV); + + info->node_mask = node_mask = num; + *flags |= 1 << c; + break; + + case CLUSTER_OPT_HASH_SEED: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-hash-seed' once"); + } + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s'", optarg); + } + info->hash_seed = num; + *flags |= 1 << c; + break; + default: + return 0; + } + + return 1; +} + +static void +cluster_check(unsigned int flags) +{ + if ((flags & ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_LOCAL_NODE) | + (1 << CLUSTER_OPT_HASH_SEED))) + == ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_LOCAL_NODE) | + (1 << CLUSTER_OPT_HASH_SEED))) { + if (node_mask >= (1ULL << total_nodes)) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: " + "`--cluster-local-node' " + "must be <= `--cluster-total-nodes'"); + } + return; + } + if ((flags & ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_NODE_MASK) | + (1 << CLUSTER_OPT_HASH_SEED))) + == ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_NODE_MASK) | + (1 << CLUSTER_OPT_HASH_SEED))) { + if (node_mask >= (1ULL << total_nodes)) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: " + "`--cluster-local-nodemask' too big " + "for `--cluster-total-nodes'"); + } + return; + } + if (!(flags & (1 << CLUSTER_OPT_TOTAL_NODES))) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: `--cluster-total-nodes' " + "is missing"); + } + if (!(flags & (1 << CLUSTER_OPT_HASH_SEED))) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: `--cluster-hash-seed' " + "is missing"); + } + if (!(flags & ((1 << (CLUSTER_OPT_LOCAL_NODE) | + (1 << (CLUSTER_OPT_NODE_MASK)))))) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: `--cluster-local-node' or" + "`--cluster-local-nodemask' is missing"); + } +} + +static void +cluster_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_cluster_match_info *info = (void *)match->data; + + printf("cluster "); + if (info->flags & XT_CLUSTER_F_INV) + printf("!node_mask=0x%08x ", info->node_mask); + else + printf("node_mask=0x%08x ", info->node_mask); + + printf("total_nodes=%u hash_seed=0x%08x ", + info->total_nodes, info->hash_seed); +} + +static void +cluster_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_cluster_match_info *info = (void *)match->data; + + if (info->flags & XT_CLUSTER_F_INV) + printf("! --cluster-local-nodemask 0x%08x ", info->node_mask); + else + printf("--cluster-local-nodemask 0x%08x ", info->node_mask); + + printf("--cluster-total-nodes %u --cluster-hash-seed 0x%08x ", + info->total_nodes, info->hash_seed); +} + +static struct xtables_match cluster_mt_reg = { + .family = AF_UNSPEC, + .name = "cluster", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_cluster_match_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_cluster_match_info)), + .help = cluster_help, + .parse = cluster_parse, + .final_check = cluster_check, + .print = cluster_print, + .save = cluster_save, + .extra_opts = cluster_opts, +}; + +void _init(void) +{ + xtables_register_match(&cluster_mt_reg); +} diff --git a/extensions/libxt_cluster.man b/extensions/libxt_cluster.man new file mode 100644 index 00000000..6081be14 --- /dev/null +++ b/extensions/libxt_cluster.man @@ -0,0 +1,62 @@ +Allows you to deploy gateway and back-end load-sharing clusters without the +need of load-balancers. +.PP +This match requires that all the nodes see the same packets. Thus, the cluster +match decides if this node has to handle a packet given the following options: +.TP +\fB\-\-cluster\-total\-nodes \fInum\fP +Set number of total nodes in cluster. +.TP +[\fB!\fP] \fB\-\-cluster\-local\-node \fInum\fP +Set the local node number ID. +.TP +[\fB!\fP] \fB\-\-cluster\-local\-nodemask \fImask\fP +Set the local node number ID mask. You can use this option instead +of \fB\-\-cluster\-local\-node. +.TP +\fB\-\-cluster\-hash\-seed \fIvalue\fP +Set seed value of the Jenkins hash. +.PP +Example: +.IP +iptables \-A PREROUTING \-t mangle \-i eth1 \-m cluster \ +\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1 \ +\-\-cluster\-hash\-seed 0xdeadbeef \ +\-j MARK \-\-set-mark 0xffff +.IP +iptables \-A PREROUTING \-t mangle \-i eth2 \-m cluster \ +\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1 \ +\-\-cluster\-hash\-seed 0xdeadbeef \ +\-j MARK -\-set\-mark 0xffff +.IP +iptables \-A PREROUTING \-t mangle \-i eth1 \ +\-m mark ! \-\-mark 0xffff \-j DROP +.IP +iptables \-A PREROUTING \-t mangle \-i eth2 \ +\-m mark ! \-\-mark 0xffff \-j DROP +.PP +And the following commands to make all nodes see the same packets: +.IP +ip maddr add 01:00:5e:00:01:01 dev eth1 +.IP +ip maddr add 01:00:5e:00:01:02 dev eth2 +.IP +arptables \-A OUTPUT \-o eth1 \-\-h\-length 6 \ +\-j mangle \-\-mangle-mac-s 01:00:5e:00:01:01 +.IP +arptables \-A INPUT \-i eth1 \-\-h-length 6 \ +\-\-destination-mac 01:00:5e:00:01:01 \ +\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27 +.IP +arptables \-A OUTPUT \-o eth2 \-\-h\-length 6 \ +\-j mangle \-\-mangle\-mac\-s 01:00:5e:00:01:02 +.IP +arptables \-A INPUT \-i eth2 \-\-h\-length 6 \ +\-\-destination\-mac 01:00:5e:00:01:02 \ +\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27 +.PP +In the case of TCP connections, pickup facility has to be disabled +to avoid marking TCP ACK packets coming in the reply direction as +valid. +.IP +echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose -- cgit v1.2.3