f04b417844a3c5f32a3f78eeba459c99d803a8e8
[strongswan.git] / src / charon / plugins / ha / ha_kernel.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "ha_kernel.h"
17
18 typedef u_int32_t u32;
19 typedef u_int8_t u8;
20
21 #include <linux/jhash.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28
29 typedef struct private_ha_kernel_t private_ha_kernel_t;
30
31 /**
32 * Private data of an ha_kernel_t object.
33 */
34 struct private_ha_kernel_t {
35
36 /**
37 * Public ha_kernel_t interface.
38 */
39 ha_kernel_t public;
40
41 /**
42 * Init value for jhash
43 */
44 u_int initval;
45
46 /**
47 * Total number of ClusterIP segments
48 */
49 u_int count;
50
51 /**
52 * List of virtual addresses, as host_t*
53 */
54 linked_list_t *virtuals;
55 };
56
57 /**
58 * Implementation of ha_kernel_t.in_segment
59 */
60 static bool in_segment(private_ha_kernel_t *this, host_t *host, u_int segment)
61 {
62 if (host->get_family(host) == AF_INET)
63 {
64 unsigned long hash;
65 u_int32_t addr;
66
67 addr = *(u_int32_t*)host->get_address(host).ptr;
68 hash = jhash_1word(ntohl(addr), this->initval);
69
70 if ((((u_int64_t)hash * this->count) >> 32) + 1 == segment)
71 {
72 return TRUE;
73 }
74 }
75 return FALSE;
76 }
77 /**
78 * Activate/Deactivate a segment
79 */
80 static void activate_deactivate(private_ha_kernel_t *this,
81 u_int segment, char op)
82 {
83 enumerator_t *enumerator;
84 host_t *host;
85 char cmd[8], file[256];
86 int fd;
87
88 enumerator = this->virtuals->create_enumerator(this->virtuals);
89 while (enumerator->enumerate(enumerator, &host))
90 {
91 snprintf(file, sizeof(file), "/proc/net/ipt_CLUSTERIP/%H", host);
92 snprintf(cmd, sizeof(cmd), "%c%d\n", op, segment);
93
94 fd = open(file, O_WRONLY);
95 if (fd == -1)
96 {
97 DBG1(DBG_CFG, "opening CLUSTERIP file '%s' failed: %s",
98 file, strerror(errno));
99 continue;
100 }
101 if (write(fd, cmd, strlen(cmd) == -1))
102 {
103 DBG1(DBG_CFG, "writing to CLUSTERIP file '%s' failed: %s",
104 file, strerror(errno));
105 }
106 close(fd);
107 }
108 enumerator->destroy(enumerator);
109 }
110
111 /**
112 * Implementation of ha_kernel_t.activate
113 */
114 static void activate(private_ha_kernel_t *this, u_int segment)
115 {
116 activate_deactivate(this, segment, '+');
117 }
118
119 /**
120 * Implementation of ha_kernel_t.deactivate
121 */
122 static void deactivate(private_ha_kernel_t *this, u_int segment)
123 {
124 activate_deactivate(this, segment, '-');
125 }
126
127 /**
128 * Mangle IPtable rules for virtual addresses
129 */
130 static bool mangle_rules(private_ha_kernel_t *this, bool add)
131 {
132 enumerator_t *enumerator;
133 host_t *host;
134 u_char i, mac = 0x20;
135 char *iface, buf[256];
136
137 enumerator = this->virtuals->create_enumerator(this->virtuals);
138 while (enumerator->enumerate(enumerator, &host))
139 {
140 iface = charon->kernel_interface->get_interface(
141 charon->kernel_interface, host);
142 if (!iface)
143 {
144 DBG1(DBG_CFG, "cluster address %H not installed, ignored", host);
145 this->virtuals->remove_at(this->virtuals, enumerator);
146 host->destroy(host);
147 continue;
148 }
149 /* iptables insists of a local node specification, enable node 1 */
150 snprintf(buf, sizeof(buf),
151 "/sbin/iptables -%c INPUT -i %s -d %H -j CLUSTERIP --new "
152 "--hashmode sourceip --clustermac 01:00:5e:00:00:%2x "
153 "--total-nodes %d --local-node 1",
154 add ? 'A' : 'D', iface, host, mac++, this->count);
155 free(iface);
156 if (system(buf) != 0)
157 {
158 DBG1(DBG_CFG, "installing CLUSTERIP rule '%s' failed", buf);
159 }
160 }
161 enumerator->destroy(enumerator);
162
163 if (add)
164 {
165 for (i = 2; i <= this->count; i++)
166 {
167 activate(this, i);
168 }
169 }
170 return TRUE;
171 }
172
173 /**
174 * Parse the list of virtual cluster addresses
175 */
176 static void parse_virtuals(private_ha_kernel_t *this, char *virtual)
177 {
178 enumerator_t *enumerator;
179 host_t *host;
180
181 enumerator = enumerator_create_token(virtual, ",", " ");
182 while (enumerator->enumerate(enumerator, &virtual))
183 {
184 host = host_create_from_string(virtual, 0);
185 if (host)
186 {
187 this->virtuals->insert_last(this->virtuals, host);
188 }
189 else
190 {
191 DBG1(DBG_CFG, "virtual cluster address '%s' invalid, ignored",
192 virtual);
193 }
194 }
195 enumerator->destroy(enumerator);
196 }
197
198 /**
199 * Implementation of ha_kernel_t.destroy.
200 */
201 static void destroy(private_ha_kernel_t *this)
202 {
203 mangle_rules(this, FALSE);
204 this->virtuals->destroy_offset(this->virtuals, offsetof(host_t, destroy));
205 free(this);
206 }
207
208 /**
209 * See header
210 */
211 ha_kernel_t *ha_kernel_create(u_int count, char *virtuals)
212 {
213 private_ha_kernel_t *this = malloc_thing(private_ha_kernel_t);
214
215 this->public.in_segment = (bool(*)(ha_kernel_t*, host_t *host, u_int segment))in_segment;
216 this->public.activate = (void(*)(ha_kernel_t*, u_int segment))activate;
217 this->public.deactivate = (void(*)(ha_kernel_t*, u_int segment))deactivate;
218 this->public.destroy = (void(*)(ha_kernel_t*))destroy;
219
220 this->initval = 0;
221 this->count = count;
222 this->virtuals = linked_list_create();
223
224 parse_virtuals(this, virtuals);
225
226 if (!mangle_rules(this, TRUE))
227 {
228 destroy(this);
229 return NULL;
230 }
231
232 return &this->public;
233 }
234