libipt_CLUSTERIP.c revision d09b6d591ca7d7d7575cb6aa20384c9830f777ab
1/* Shared library add-on to iptables to add CLUSTERIP target support.
2 * (C) 2003 by Harald Welte <laforge@gnumonks.org>
3 *
4 * Development of this code was funded by SuSE AG, http://www.suse.com/
5 */
6#include <stdbool.h>
7#include <stdio.h>
8#include <string.h>
9#include <stdlib.h>
10#include <getopt.h>
11#include <stddef.h>
12
13#if defined(__GLIBC__) && __GLIBC__ == 2
14#include <net/ethernet.h>
15#else
16#include <linux/if_ether.h>
17#endif
18
19#include <xtables.h>
20#include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
21
22static void CLUSTERIP_help(void)
23{
24	printf(
25"CLUSTERIP target options:\n"
26"  --new			 Create a new ClusterIP\n"
27"  --hashmode <mode>		 Specify hashing mode\n"
28"					sourceip\n"
29"					sourceip-sourceport\n"
30"					sourceip-sourceport-destport\n"
31"  --clustermac <mac>		 Set clusterIP MAC address\n"
32"  --total-nodes <num>		 Set number of total nodes in cluster\n"
33"  --local-node <num>		 Set the local node number\n"
34"  --hash-init <num>		 Set init value of the Jenkins hash\n");
35}
36
37#define	PARAM_NEW	0x0001
38#define PARAM_HMODE	0x0002
39#define PARAM_MAC	0x0004
40#define PARAM_TOTALNODE	0x0008
41#define PARAM_LOCALNODE	0x0010
42#define PARAM_HASHINIT	0x0020
43
44static const struct option CLUSTERIP_opts[] = {
45	{.name = "new",         .has_arg = false, .val = '1'},
46	{.name = "hashmode",    .has_arg = true,  .val = '2'},
47	{.name = "clustermac",  .has_arg = true,  .val = '3'},
48	{.name = "total-nodes", .has_arg = true,  .val = '4'},
49	{.name = "local-node",  .has_arg = true,  .val = '5'},
50	{.name = "hash-init",   .has_arg = true,  .val = '6'},
51	XT_GETOPT_TABLEEND,
52};
53
54static void
55parse_mac(const char *mac, char *macbuf)
56{
57	unsigned int i = 0;
58
59	if (strlen(mac) != ETH_ALEN*3-1)
60		xtables_error(PARAMETER_PROBLEM, "Bad mac address \"%s\"", mac);
61
62	for (i = 0; i < ETH_ALEN; i++) {
63		long number;
64		char *end;
65
66		number = strtol(mac + i*3, &end, 16);
67
68		if (end == mac + i*3 + 2
69		    && number >= 0
70		    && number <= 255)
71			macbuf[i] = number;
72		else
73			xtables_error(PARAMETER_PROBLEM,
74				   "Bad mac address `%s'", mac);
75	}
76}
77
78static int CLUSTERIP_parse(int c, char **argv, int invert, unsigned int *flags,
79                           const void *entry, struct xt_entry_target **target)
80{
81	struct ipt_clusterip_tgt_info *cipinfo
82		= (struct ipt_clusterip_tgt_info *)(*target)->data;
83
84	switch (c) {
85		unsigned int num;
86	case '1':
87		cipinfo->flags |= CLUSTERIP_FLAG_NEW;
88		if (*flags & PARAM_NEW)
89			xtables_error(PARAMETER_PROBLEM, "Can only specify \"--new\" once\n");
90		*flags |= PARAM_NEW;
91		break;
92	case '2':
93		if (!(*flags & PARAM_NEW))
94			xtables_error(PARAMETER_PROBLEM, "Can only specify hashmode combined with \"--new\"\n");
95		if (*flags & PARAM_HMODE)
96			xtables_error(PARAMETER_PROBLEM, "Can only specify hashmode once\n");
97		if (!strcmp(optarg, "sourceip"))
98			cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP;
99		else if (!strcmp(optarg, "sourceip-sourceport"))
100			cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT;
101		else if (!strcmp(optarg, "sourceip-sourceport-destport"))
102			cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT_DPT;
103		else
104			xtables_error(PARAMETER_PROBLEM, "Unknown hashmode \"%s\"\n",
105				   optarg);
106		*flags |= PARAM_HMODE;
107		break;
108	case '3':
109		if (!(*flags & PARAM_NEW))
110			xtables_error(PARAMETER_PROBLEM, "Can only specify MAC combined with \"--new\"\n");
111		if (*flags & PARAM_MAC)
112			xtables_error(PARAMETER_PROBLEM, "Can only specify MAC once\n");
113		parse_mac(optarg, (char *)cipinfo->clustermac);
114		if (!(cipinfo->clustermac[0] & 0x01))
115			xtables_error(PARAMETER_PROBLEM, "MAC has to be a multicast ethernet address\n");
116		*flags |= PARAM_MAC;
117		break;
118	case '4':
119		if (!(*flags & PARAM_NEW))
120			xtables_error(PARAMETER_PROBLEM, "Can only specify node number combined with \"--new\"\n");
121		if (*flags & PARAM_TOTALNODE)
122			xtables_error(PARAMETER_PROBLEM, "Can only specify total node number once\n");
123		if (!xtables_strtoui(optarg, NULL, &num, 1, CLUSTERIP_MAX_NODES))
124			xtables_error(PARAMETER_PROBLEM, "Unable to parse \"%s\"\n", optarg);
125		cipinfo->num_total_nodes = num;
126		*flags |= PARAM_TOTALNODE;
127		break;
128	case '5':
129		if (!(*flags & PARAM_NEW))
130			xtables_error(PARAMETER_PROBLEM, "Can only specify node number combined with \"--new\"\n");
131		if (*flags & PARAM_LOCALNODE)
132			xtables_error(PARAMETER_PROBLEM, "Can only specify local node number once\n");
133		if (!xtables_strtoui(optarg, NULL, &num, 1, CLUSTERIP_MAX_NODES))
134			xtables_error(PARAMETER_PROBLEM, "Unable to parse \"%s\"\n", optarg);
135		cipinfo->num_local_nodes = 1;
136		cipinfo->local_nodes[0] = num;
137		*flags |= PARAM_LOCALNODE;
138		break;
139	case '6':
140		if (!(*flags & PARAM_NEW))
141			xtables_error(PARAMETER_PROBLEM, "Can only specify hash init value combined with \"--new\"\n");
142		if (*flags & PARAM_HASHINIT)
143			xtables_error(PARAMETER_PROBLEM, "Can specify hash init value only once\n");
144		if (!xtables_strtoui(optarg, NULL, &num, 0, UINT_MAX))
145			xtables_error(PARAMETER_PROBLEM, "Unable to parse \"%s\"\n", optarg);
146		cipinfo->hash_initval = num;
147		*flags |= PARAM_HASHINIT;
148		break;
149	}
150
151	return 1;
152}
153
154static void CLUSTERIP_check(unsigned int flags)
155{
156	if (flags == 0)
157		return;
158
159	if ((flags & (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE))
160		== (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE))
161		return;
162
163	xtables_error(PARAMETER_PROBLEM, "CLUSTERIP target: Invalid parameter combination\n");
164}
165
166static char *hashmode2str(enum clusterip_hashmode mode)
167{
168	char *retstr;
169	switch (mode) {
170		case CLUSTERIP_HASHMODE_SIP:
171			retstr = "sourceip";
172			break;
173		case CLUSTERIP_HASHMODE_SIP_SPT:
174			retstr = "sourceip-sourceport";
175			break;
176		case CLUSTERIP_HASHMODE_SIP_SPT_DPT:
177			retstr = "sourceip-sourceport-destport";
178			break;
179		default:
180			retstr = "unknown-error";
181			break;
182	}
183	return retstr;
184}
185
186static char *mac2str(const uint8_t mac[ETH_ALEN])
187{
188	static char buf[ETH_ALEN*3];
189	sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
190		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
191	return buf;
192}
193
194static void CLUSTERIP_print(const void *ip,
195                            const struct xt_entry_target *target, int numeric)
196{
197	const struct ipt_clusterip_tgt_info *cipinfo =
198		(const struct ipt_clusterip_tgt_info *)target->data;
199
200	if (!cipinfo->flags & CLUSTERIP_FLAG_NEW) {
201		printf("CLUSTERIP");
202		return;
203	}
204
205	printf("CLUSTERIP hashmode=%s clustermac=%s total_nodes=%u local_node=%u hash_init=%u",
206		hashmode2str(cipinfo->hash_mode),
207		mac2str(cipinfo->clustermac),
208		cipinfo->num_total_nodes,
209		cipinfo->local_nodes[0],
210		cipinfo->hash_initval);
211}
212
213static void CLUSTERIP_save(const void *ip, const struct xt_entry_target *target)
214{
215	const struct ipt_clusterip_tgt_info *cipinfo =
216		(const struct ipt_clusterip_tgt_info *)target->data;
217
218	/* if this is not a new entry, we don't need to save target
219	 * parameters */
220	if (!cipinfo->flags & CLUSTERIP_FLAG_NEW)
221		return;
222
223	printf("--new --hashmode %s --clustermac %s --total-nodes %d --local-node %d --hash-init %u",
224	       hashmode2str(cipinfo->hash_mode),
225	       mac2str(cipinfo->clustermac),
226	       cipinfo->num_total_nodes,
227	       cipinfo->local_nodes[0],
228	       cipinfo->hash_initval);
229}
230
231static struct xtables_target clusterip_tg_reg = {
232	.name		= "CLUSTERIP",
233	.version	= XTABLES_VERSION,
234	.family		= NFPROTO_IPV4,
235	.size		= XT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)),
236	.userspacesize	= offsetof(struct ipt_clusterip_tgt_info, config),
237 	.help		= CLUSTERIP_help,
238	.parse		= CLUSTERIP_parse,
239	.final_check	= CLUSTERIP_check,
240	.print		= CLUSTERIP_print,
241	.save		= CLUSTERIP_save,
242	.extra_opts	= CLUSTERIP_opts,
243};
244
245void _init(void)
246{
247	xtables_register_target(&clusterip_tg_reg);
248}
249