changing trunk/trunk to trunk
[iptables.git] / extensions / libipt_CLUSTERIP.c
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 <stdio.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <getopt.h>
10 #include <stddef.h>
11
12 #if defined(__GLIBC__) && __GLIBC__ == 2
13 #include <net/ethernet.h>
14 #else
15 #include <linux/if_ether.h>
16 #endif
17
18 #include <iptables.h>
19 #include <linux/netfilter_ipv4/ip_tables.h>
20 #include "../include/linux/netfilter_ipv4/ipt_CLUSTERIP.h"
21
22 static 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
44 static const struct option CLUSTERIP_opts[] = {
45         { "new", 0, NULL, '1' },
46         { "hashmode", 1, NULL, '2' },
47         { "clustermac", 1, NULL, '3' },
48         { "total-nodes", 1, NULL, '4' },
49         { "local-node", 1, NULL, '5' },
50         { "hash-init", 1, NULL, '6' },
51         { .name = NULL }
52 };
53
54 static void
55 parse_mac(const char *mac, char *macbuf)
56 {
57         unsigned int i = 0;
58
59         if (strlen(mac) != ETH_ALEN*3-1)
60                 exit_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                         exit_error(PARAMETER_PROBLEM,
74                                    "Bad mac address `%s'", mac);
75         }
76 }
77
78 static 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                         exit_error(PARAMETER_PROBLEM, "Can only specify `--new' once\n");
90                 *flags |= PARAM_NEW;
91                 break;
92         case '2':
93                 if (!(*flags & PARAM_NEW))
94                         exit_error(PARAMETER_PROBLEM, "Can only specify hashmode combined with `--new'\n");
95                 if (*flags & PARAM_HMODE)
96                         exit_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                         exit_error(PARAMETER_PROBLEM, "Unknown hashmode `%s'\n",
105                                    optarg);
106                 *flags |= PARAM_HMODE;
107                 break;
108         case '3':
109                 if (!(*flags & PARAM_NEW))
110                         exit_error(PARAMETER_PROBLEM, "Can only specify MAC combined with `--new'\n");
111                 if (*flags & PARAM_MAC)
112                         exit_error(PARAMETER_PROBLEM, "Can only specify MAC once\n");
113                 parse_mac(optarg, (char *)cipinfo->clustermac);
114                 if (!(cipinfo->clustermac[0] & 0x01))
115                         exit_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                         exit_error(PARAMETER_PROBLEM, "Can only specify node number combined with `--new'\n");
121                 if (*flags & PARAM_TOTALNODE)
122                         exit_error(PARAMETER_PROBLEM, "Can only specify total node number once\n");
123                 if (string_to_number(optarg, 1, CLUSTERIP_MAX_NODES, &num) < 0)
124                         exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
125                 cipinfo->num_total_nodes = (u_int16_t)num;
126                 *flags |= PARAM_TOTALNODE;
127                 break;
128         case '5':
129                 if (!(*flags & PARAM_NEW))
130                         exit_error(PARAMETER_PROBLEM, "Can only specify node number combined with `--new'\n");
131                 if (*flags & PARAM_LOCALNODE)
132                         exit_error(PARAMETER_PROBLEM, "Can only specify local node number once\n");
133                 if (string_to_number(optarg, 1, CLUSTERIP_MAX_NODES, &num) < 0)
134                         exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
135                 cipinfo->num_local_nodes = 1;
136                 cipinfo->local_nodes[0] = (u_int16_t)num;
137                 *flags |= PARAM_LOCALNODE;
138                 break;
139         case '6':
140                 if (!(*flags & PARAM_NEW))
141                         exit_error(PARAMETER_PROBLEM, "Can only specify hash init value combined with `--new'\n");
142                 if (*flags & PARAM_HASHINIT)
143                         exit_error(PARAMETER_PROBLEM, "Can specify hash init value only once\n");
144                 if (string_to_number(optarg, 0, UINT_MAX, &num) < 0)
145                         exit_error(PARAMETER_PROBLEM, "Unable to parse `%s'\n", optarg);
146                 cipinfo->hash_initval = num;
147                 *flags |= PARAM_HASHINIT;
148                 break;
149         default:
150                 return 0;
151         }
152
153         return 1;
154 }
155
156 static void CLUSTERIP_check(unsigned int flags)
157 {
158         if (flags == 0)
159                 return;
160
161         if ((flags & (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE))
162                 == (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE))
163                 return;
164
165         exit_error(PARAMETER_PROBLEM, "CLUSTERIP target: Invalid parameter combination\n");
166 }
167
168 static char *hashmode2str(enum clusterip_hashmode mode)
169 {
170         char *retstr;
171         switch (mode) {
172                 case CLUSTERIP_HASHMODE_SIP:
173                         retstr = "sourceip";
174                         break;
175                 case CLUSTERIP_HASHMODE_SIP_SPT:
176                         retstr = "sourceip-sourceport";
177                         break;
178                 case CLUSTERIP_HASHMODE_SIP_SPT_DPT:
179                         retstr = "sourceip-sourceport-destport";
180                         break;
181                 default:
182                         retstr = "unknown-error";
183                         break;
184         }
185         return retstr;
186 }
187
188 static char *mac2str(const u_int8_t mac[ETH_ALEN])
189 {
190         static char buf[ETH_ALEN*3];
191         sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
192                 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
193         return buf;
194 }
195                         
196
197 /* Prints out the targinfo. */
198 static void CLUSTERIP_print(const void *ip,
199                             const struct xt_entry_target *target, int numeric)
200 {
201         const struct ipt_clusterip_tgt_info *cipinfo =
202                 (const struct ipt_clusterip_tgt_info *)target->data;
203         
204         if (!cipinfo->flags & CLUSTERIP_FLAG_NEW) {
205                 printf("CLUSTERIP");
206                 return;
207         }
208
209         printf("CLUSTERIP hashmode=%s clustermac=%s total_nodes=%u local_node=%u hash_init=%u", 
210                 hashmode2str(cipinfo->hash_mode),
211                 mac2str(cipinfo->clustermac),
212                 cipinfo->num_total_nodes,
213                 cipinfo->local_nodes[0],
214                 cipinfo->hash_initval);
215 }
216
217 /* Saves the union ipt_targinfo in parsable form to stdout. */
218 static void CLUSTERIP_save(const void *ip, const struct xt_entry_target *target)
219 {
220         const struct ipt_clusterip_tgt_info *cipinfo =
221                 (const struct ipt_clusterip_tgt_info *)target->data;
222
223         /* if this is not a new entry, we don't need to save target
224          * parameters */
225         if (!cipinfo->flags & CLUSTERIP_FLAG_NEW)
226                 return;
227
228         printf("--new --hashmode %s --clustermac %s --total-nodes %d --local-node %d --hash-init %u",
229                hashmode2str(cipinfo->hash_mode),
230                mac2str(cipinfo->clustermac),
231                cipinfo->num_total_nodes,
232                cipinfo->local_nodes[0],
233                cipinfo->hash_initval);
234 }
235
236 static struct xtables_target clusterip_tg_reg = {
237         .name           = "CLUSTERIP",
238         .version        = XTABLES_VERSION,
239         .family         = PF_INET,
240         .size           = XT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)),
241         .userspacesize  = offsetof(struct ipt_clusterip_tgt_info, config),
242         .help           = CLUSTERIP_help,
243         .parse          = CLUSTERIP_parse,
244         .final_check    = CLUSTERIP_check,
245         .print          = CLUSTERIP_print,
246         .save           = CLUSTERIP_save,
247         .extra_opts     = CLUSTERIP_opts,
248 };
249
250 void _init(void)
251 {
252         xtables_register_target(&clusterip_tg_reg);
253 }