added HA resync option to (re-)integrate nodes to a cluster
[strongswan.git] / src / charon / plugins / ha_sync / ha_sync_segments.c
1 /*
2 * Copyright (C) 2008 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 * $Id$
16 */
17
18 #include "ha_sync_segments.h"
19
20 #include <utils/linked_list.h>
21
22 typedef u_int32_t u32;
23 typedef u_int8_t u8;
24
25 #include <linux/jhash.h>
26
27 #define MAX_SEGMENTS 16
28
29 typedef struct private_ha_sync_segments_t private_ha_sync_segments_t;
30
31 /**
32 * Private data of an ha_sync_segments_t object.
33 */
34 struct private_ha_sync_segments_t {
35
36 /**
37 * Public ha_sync_segments_t interface.
38 */
39 ha_sync_segments_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 segment_count;
50
51 /**
52 * mask of active segments
53 */
54 u_int16_t active;
55 };
56
57 /**
58 * Check if a host address is in the CLUSTERIP segment
59 */
60 static bool in_segment(private_ha_sync_segments_t *this,
61 host_t *host, u_int segment)
62 {
63 if (host->get_family(host) == AF_INET)
64 {
65 unsigned long hash;
66 u_int32_t addr;
67
68 addr = *(u_int32_t*)host->get_address(host).ptr;
69 hash = jhash_1word(ntohl(addr), this->initval);
70
71 if ((((u_int64_t)hash * this->segment_count) >> 32) + 1 == segment)
72 {
73 return TRUE;
74 }
75 }
76 return FALSE;
77 }
78
79 /**
80 * Log currently active segments
81 */
82 static void log_segments(private_ha_sync_segments_t *this, bool activated,
83 u_int segment)
84 {
85 char buf[64], *pos = buf;
86 int i;
87 bool first = TRUE;
88
89 for (i = 0; i < this->segment_count; i++)
90 {
91 if (this->active & 0x01 << i)
92 {
93 if (first)
94 {
95 first = FALSE;
96 }
97 else
98 {
99 pos += snprintf(pos, buf + sizeof(buf) - pos, ",");
100 }
101 pos += snprintf(pos, buf + sizeof(buf) - pos, "%d", i+1);
102 }
103 }
104 DBG1(DBG_CFG, "HA sync segment %d %sactivated, now active: %s",
105 segment, activated ? "" : "de", buf);
106 }
107
108 /**
109 * Implementation of ha_sync_segments_t.activate
110 */
111 static void activate(private_ha_sync_segments_t *this, u_int segment)
112 {
113 ike_sa_t *ike_sa;
114 enumerator_t *enumerator;
115 u_int16_t mask = 0x01 << (segment - 1);
116
117 if (segment > 0 && segment <= this->segment_count && !(this->active & mask))
118 {
119 this->active |= mask;
120
121 enumerator = charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager);
122 while (enumerator->enumerate(enumerator, &ike_sa))
123 {
124 if (ike_sa->get_state(ike_sa) == IKE_PASSIVE &&
125 in_segment(this, ike_sa->get_other_host(ike_sa), segment))
126 {
127 ike_sa->set_state(ike_sa, IKE_ESTABLISHED);
128 }
129 }
130 enumerator->destroy(enumerator);
131
132 log_segments(this, TRUE, segment);
133 }
134 }
135
136 /**
137 * Implementation of ha_sync_segments_t.deactivate
138 */
139 static void deactivate(private_ha_sync_segments_t *this, u_int segment)
140 {
141 ike_sa_t *ike_sa;
142 enumerator_t *enumerator;
143 u_int16_t mask = 0x01 << (segment - 1);
144
145 if (segment > 0 && segment <= this->segment_count && (this->active & mask))
146 {
147 this->active &= ~mask;
148
149 enumerator = charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager);
150 while (enumerator->enumerate(enumerator, &ike_sa))
151 {
152 if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
153 in_segment(this, ike_sa->get_other_host(ike_sa), segment))
154 {
155 ike_sa->set_state(ike_sa, IKE_PASSIVE);
156 }
157 }
158 enumerator->destroy(enumerator);
159
160 log_segments(this, FALSE, segment);
161 }
162 }
163
164 /**
165 * Rekey all children of an IKE_SA
166 */
167 static status_t rekey_children(ike_sa_t *ike_sa)
168 {
169 iterator_t *iterator;
170 child_sa_t *child_sa;
171 status_t status = SUCCESS;
172
173 iterator = ike_sa->create_child_sa_iterator(ike_sa);
174 while (iterator->iterate(iterator, (void**)&child_sa))
175 {
176 DBG1(DBG_CFG, "resyncing CHILD_SA");
177 status = ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa),
178 child_sa->get_spi(child_sa, TRUE));
179 if (status == DESTROY_ME)
180 {
181 break;
182 }
183 }
184 iterator->destroy(iterator);
185 return status;
186 }
187
188 /**
189 * Implementation of ha_sync_segments_t.resync
190 */
191 static void resync(private_ha_sync_segments_t *this, u_int segment)
192 {
193 ike_sa_t *ike_sa;
194 enumerator_t *enumerator;
195 linked_list_t *list;
196 ike_sa_id_t *id;
197 u_int16_t mask = 0x01 << (segment - 1);
198
199
200 if (segment > 0 && segment <= this->segment_count && (this->active & mask))
201 {
202 this->active &= ~mask;
203 list = linked_list_create();
204
205 DBG1(DBG_CFG, "resyncing HA sync segment %d", segment);
206
207 /* we do the actual rekeying in a seperate loop to avoid rekeying
208 * an SA twice. */
209 enumerator = charon->ike_sa_manager->create_enumerator(
210 charon->ike_sa_manager);
211 while (enumerator->enumerate(enumerator, &ike_sa))
212 {
213 if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
214 in_segment(this, ike_sa->get_other_host(ike_sa), segment))
215 {
216 id = ike_sa->get_id(ike_sa);
217 list->insert_last(list, id->clone(id));
218 }
219 }
220 enumerator->destroy(enumerator);
221
222 while (list->remove_last(list, (void**)&id) == SUCCESS)
223 {
224 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
225 id->destroy(id);
226 if (ike_sa)
227 {
228 DBG1(DBG_CFG, "resyncing IKE_SA");
229 if (ike_sa->rekey(ike_sa) != DESTROY_ME)
230 {
231 if (rekey_children(ike_sa) != DESTROY_ME)
232 {
233 charon->ike_sa_manager->checkin(
234 charon->ike_sa_manager, ike_sa);
235 continue;
236 }
237 }
238 charon->ike_sa_manager->checkin_and_destroy(
239 charon->ike_sa_manager, ike_sa);
240 }
241 }
242 list->destroy(list);
243 }
244 }
245
246 /**
247 * Implementation of ha_sync_segments_t.destroy.
248 */
249 static void destroy(private_ha_sync_segments_t *this)
250 {
251 free(this);
252 }
253
254 /**
255 * See header
256 */
257 ha_sync_segments_t *ha_sync_segments_create()
258 {
259 private_ha_sync_segments_t *this = malloc_thing(private_ha_sync_segments_t);
260 enumerator_t *enumerator;
261 u_int segment;
262 char *str;
263
264 this->public.activate = (void(*)(ha_sync_segments_t*, u_int segment))activate;
265 this->public.deactivate = (void(*)(ha_sync_segments_t*, u_int segment))deactivate;
266 this->public.resync = (void(*)(ha_sync_segments_t*, u_int segment))resync;
267 this->public.destroy = (void(*)(ha_sync_segments_t*))destroy;
268
269 this->initval = 0;
270 this->active = 0;
271 this->segment_count = lib->settings->get_int(lib->settings,
272 "charon.plugins.ha_sync.segment_count", 1);
273 this->segment_count = min(this->segment_count, MAX_SEGMENTS);
274 str = lib->settings->get_str(lib->settings,
275 "charon.plugins.ha_sync.active_segments", "1");
276 enumerator = enumerator_create_token(str, ",", " ");
277 while (enumerator->enumerate(enumerator, &str))
278 {
279 segment = atoi(str);
280 if (segment > 0 && segment < MAX_SEGMENTS)
281 {
282 this->active |= 0x01 << (segment - 1);
283 }
284 }
285 enumerator->destroy(enumerator);
286
287 return &this->public;
288 }
289