Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_kernel.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2009-2011 Martin Willi
3: * HSR 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 uint32_t u32;
19: typedef uint8_t u8;
20:
21: #include <sys/utsname.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: #define CLUSTERIP_DIR "/proc/net/ipt_CLUSTERIP"
30:
31: /**
32: * Versions of jhash used in the Linux kernel
33: */
34: typedef enum {
35: /* old variant, http://burtleburtle.net/bob/c/lookup2.c */
36: JHASH_LOOKUP2,
37: /* new variant, http://burtleburtle.net/bob/c/lookup3.c, since 2.6.37 */
38: JHASH_LOOKUP3,
39: /* variant with different init values, since 4.1 */
40: JHASH_LOOKUP3_1,
41: } jhash_version_t;
42:
43: typedef struct private_ha_kernel_t private_ha_kernel_t;
44:
45: /**
46: * Private data of an ha_kernel_t object.
47: */
48: struct private_ha_kernel_t {
49:
50: /**
51: * Public ha_kernel_t interface.
52: */
53: ha_kernel_t public;
54:
55: /**
56: * Total number of ClusterIP segments
57: */
58: u_int count;
59:
60: /**
61: * jhash version the kernel uses
62: */
63: jhash_version_t version;
64: };
65:
66: /**
67: * Get the jhash version based on the uname().release
68: */
69: static jhash_version_t get_jhash_version()
70: {
71: struct utsname utsname;
72: int a, b, c;
73:
74: if (uname(&utsname) == 0)
75: {
76: switch (sscanf(utsname.release, "%d.%d.%d", &a, &b, &c))
77: {
78: case 3:
79: if (a == 2 && b == 6)
80: {
81: if (c < 37)
82: {
83: DBG1(DBG_CFG, "detected Linux %d.%d.%d, using old "
84: "jhash", a, b, c);
85: return JHASH_LOOKUP2;
86: }
87: DBG1(DBG_CFG, "detected Linux %d.%d.%d, using new "
88: "jhash", a, b, c);
89: return JHASH_LOOKUP3;
90: }
91: /* FALL */
92: case 2:
93: if (a < 4 || (a == 4 && b == 0))
94: {
95: DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash",
96: a, b);
97: return JHASH_LOOKUP3;
98: }
99: DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash with "
100: "updated init values", a, b);
101: return JHASH_LOOKUP3_1;
102: default:
103: break;
104: }
105: }
106: DBG1(DBG_CFG, "detecting Linux version failed, using new jhash");
107: return JHASH_LOOKUP3;
108: }
109:
110: /**
111: * Rotate 32 bit word x by k bits
112: */
113: #define jhash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
114:
115: /**
116: * jhash algorithm of two words, as used in kernel (using 0 as initval)
117: */
118: static uint32_t jhash(jhash_version_t version, uint32_t a, uint32_t b)
119: {
120: uint32_t c = 0;
121:
122: switch (version)
123: {
124: case JHASH_LOOKUP2:
125: a += 0x9e3779b9;
126: b += 0x9e3779b9;
127:
128: a -= b; a -= c; a ^= (c >> 13);
129: b -= c; b -= a; b ^= (a << 8);
130: c -= a; c -= b; c ^= (b >> 13);
131: a -= b; a -= c; a ^= (c >> 12);
132: b -= c; b -= a; b ^= (a << 16);
133: c -= a; c -= b; c ^= (b >> 5);
134: a -= b; a -= c; a ^= (c >> 3);
135: b -= c; b -= a; b ^= (a << 10);
136: c -= a; c -= b; c ^= (b >> 15);
137: break;
138: case JHASH_LOOKUP3_1:
139: /* changed with 4.1: # of 32-bit words shifted by 2 and c is
140: * initialized. we only use the two word variant with SPIs, so it's
141: * unlikely that b is 0 in that case */
142: c += ((b ? 2 : 1) << 2) + 0xdeadbeef;
143: a += ((b ? 2 : 1) << 2);
144: b += ((b ? 2 : 1) << 2);
145: /* FALL */
146: case JHASH_LOOKUP3:
147: a += 0xdeadbeef;
148: b += 0xdeadbeef;
149:
150: c ^= b; c -= jhash_rot(b, 14);
151: a ^= c; a -= jhash_rot(c, 11);
152: b ^= a; b -= jhash_rot(a, 25);
153: c ^= b; c -= jhash_rot(b, 16);
154: a ^= c; a -= jhash_rot(c, 4);
155: b ^= a; b -= jhash_rot(a, 14);
156: c ^= b; c -= jhash_rot(b, 24);
157: break;
158: }
159: return c;
160: }
161:
162: /**
163: * Segment a calculated hash
164: */
165: static u_int hash2segment(private_ha_kernel_t *this, uint64_t hash)
166: {
167: return ((hash * this->count) >> 32) + 1;
168: }
169:
170: /**
171: * Get a host as an integer for hashing
172: */
173: static uint32_t host2int(host_t *host)
174: {
175: if (host->get_family(host) == AF_INET)
176: {
177: return *(uint32_t*)host->get_address(host).ptr;
178: }
179: return 0;
180: }
181:
182: METHOD(ha_kernel_t, get_segment, u_int,
183: private_ha_kernel_t *this, host_t *host)
184: {
185: unsigned long hash;
186: uint32_t addr;
187:
188: addr = host2int(host);
189: hash = jhash(this->version, ntohl(addr), 0);
190:
191: return hash2segment(this, hash);
192: }
193:
194: METHOD(ha_kernel_t, get_segment_spi, u_int,
195: private_ha_kernel_t *this, host_t *host, uint32_t spi)
196: {
197: unsigned long hash;
198: uint32_t addr;
199:
200: addr = host2int(host);
201: hash = jhash(this->version, ntohl(addr), ntohl(spi));
202:
203: return hash2segment(this, hash);
204: }
205:
206: METHOD(ha_kernel_t, get_segment_int, u_int,
207: private_ha_kernel_t *this, int n)
208: {
209: unsigned long hash;
210:
211: hash = jhash(this->version, ntohl(n), 0);
212:
213: return hash2segment(this, hash);
214: }
215:
216: /**
217: * Activate/Deactivate a segment for a given clusterip file
218: */
219: static void enable_disable(private_ha_kernel_t *this, u_int segment,
220: char *file, bool enable)
221: {
222: char cmd[8];
223: int fd;
224:
225: snprintf(cmd, sizeof(cmd), "%c%d\n", enable ? '+' : '-', segment);
226:
227: fd = open(file, O_WRONLY);
228: if (fd == -1)
229: {
230: DBG1(DBG_CFG, "opening CLUSTERIP file '%s' failed: %s",
231: file, strerror(errno));
232: return;
233: }
234: if (write(fd, cmd, strlen(cmd)) == -1)
235: {
236: DBG1(DBG_CFG, "writing to CLUSTERIP file '%s' failed: %s",
237: file, strerror(errno));
238: }
239: close(fd);
240: }
241:
242: /**
243: * Get the currently active segments in the kernel for a clusterip file
244: */
245: static segment_mask_t get_active(private_ha_kernel_t *this, char *file)
246: {
247: char buf[256];
248: segment_mask_t mask = 0;
249: ssize_t len;
250: int fd;
251:
252: fd = open(file, O_RDONLY);
253: if (fd == -1)
254: {
255: DBG1(DBG_CFG, "opening CLUSTERIP file '%s' failed: %s",
256: file, strerror(errno));
257: return 0;
258: }
259: len = read(fd, buf, sizeof(buf)-1);
260: close(fd);
261: if (len == -1)
262: {
263: DBG1(DBG_CFG, "reading from CLUSTERIP file '%s' failed: %s",
264: file, strerror(errno));
265: }
266: else
267: {
268: enumerator_t *enumerator;
269: u_int segment;
270: char *token;
271:
272: buf[len] = '\0';
273: enumerator = enumerator_create_token(buf, ",", " ");
274: while (enumerator->enumerate(enumerator, &token))
275: {
276: segment = atoi(token);
277: if (segment)
278: {
279: mask |= SEGMENTS_BIT(segment);
280: }
281: }
282: enumerator->destroy(enumerator);
283: }
284: return mask;
285: }
286:
287: METHOD(ha_kernel_t, activate, void,
288: private_ha_kernel_t *this, u_int segment)
289: {
290: enumerator_t *enumerator;
291: char *file;
292:
293: enumerator = enumerator_create_directory(CLUSTERIP_DIR);
294: if (enumerator)
295: {
296: while (enumerator->enumerate(enumerator, NULL, &file, NULL))
297: {
298: enable_disable(this, segment, file, TRUE);
299: }
300: enumerator->destroy(enumerator);
301: }
302: }
303:
304: METHOD(ha_kernel_t, deactivate, void,
305: private_ha_kernel_t *this, u_int segment)
306: {
307: enumerator_t *enumerator;
308: char *file;
309:
310: enumerator = enumerator_create_directory(CLUSTERIP_DIR);
311: if (enumerator)
312: {
313: while (enumerator->enumerate(enumerator, NULL, &file, NULL))
314: {
315: enable_disable(this, segment, file, FALSE);
316: }
317: enumerator->destroy(enumerator);
318: }
319: }
320:
321: /**
322: * Disable all not-yet disabled segments on all clusterip addresses
323: */
324: static void disable_all(private_ha_kernel_t *this)
325: {
326: enumerator_t *enumerator;
327: segment_mask_t active;
328: char *file;
329: int i;
330:
331: enumerator = enumerator_create_directory(CLUSTERIP_DIR);
332: if (enumerator)
333: {
334: while (enumerator->enumerate(enumerator, NULL, &file, NULL))
335: {
336: if (chown(file, lib->caps->get_uid(lib->caps),
337: lib->caps->get_gid(lib->caps)) != 0)
338: {
339: DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s",
340: strerror(errno));
341: }
342: active = get_active(this, file);
343: for (i = 1; i <= this->count; i++)
344: {
345: if (active & SEGMENTS_BIT(i))
346: {
347: enable_disable(this, i, file, FALSE);
348: }
349: }
350: }
351: enumerator->destroy(enumerator);
352: }
353: }
354:
355: METHOD(ha_kernel_t, destroy, void,
356: private_ha_kernel_t *this)
357: {
358: free(this);
359: }
360:
361: /**
362: * See header
363: */
364: ha_kernel_t *ha_kernel_create(u_int count)
365: {
366: private_ha_kernel_t *this;
367:
368: INIT(this,
369: .public = {
370: .get_segment = _get_segment,
371: .get_segment_spi = _get_segment_spi,
372: .get_segment_int = _get_segment_int,
373: .activate = _activate,
374: .deactivate = _deactivate,
375: .destroy = _destroy,
376: },
377: .version = get_jhash_version(),
378: .count = count,
379: );
380:
381: disable_all(this);
382:
383: return &this->public;
384: }
385:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>