]>
Commit | Line | Data |
---|---|---|
3ebebc5e MW |
1 | /** |
2 | * @file kernel_interface.c | |
3 | * | |
4 | * @brief Implementation of kernel_interface_t. | |
5 | * | |
6 | */ | |
7 | ||
8 | /* | |
9 | * Copyright (C) 2005 Jan Hutter, Martin Willi | |
10 | * Hochschule fuer Technik Rapperswil | |
fb2b73da MW |
11 | * Copyright (C) 2003 Herbert Xu. |
12 | * | |
13 | * Contains modified parts from pluto. | |
3ebebc5e MW |
14 | * |
15 | * This program is free software; you can redistribute it and/or modify it | |
16 | * under the terms of the GNU General Public License as published by the | |
17 | * Free Software Foundation; either version 2 of the License, or (at your | |
18 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, but | |
21 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
22 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
23 | * for more details. | |
24 | */ | |
25 | ||
26 | #include <sys/types.h> | |
27 | #include <sys/socket.h> | |
28 | #include <linux/netlink.h> | |
3ebebc5e | 29 | #include <pthread.h> |
ba425b87 MW |
30 | #include <unistd.h> |
31 | #include <fcntl.h> | |
32 | #include <errno.h> | |
33 | #include <string.h> | |
3ebebc5e MW |
34 | |
35 | #include "kernel_interface.h" | |
36 | ||
ba425b87 | 37 | #include <daemon.h> |
3ebebc5e | 38 | #include <utils/linked_list.h> |
3ebebc5e MW |
39 | |
40 | ||
30b5b412 MW |
41 | #define KERNEL_ESP 50 |
42 | #define KERNEL_AH 51 | |
43 | ||
a527a426 MW |
44 | #define SPD_PRIORITY 1024 |
45 | ||
aa5a35a0 MW |
46 | #define XFRM_DATA_LENGTH 512 |
47 | ||
a527a426 MW |
48 | |
49 | typedef struct xfrm_data_t xfrm_data_t; | |
50 | ||
51 | /** | |
52 | * Lenght/Type/data struct for userdata in xfrm | |
aa5a35a0 MW |
53 | * We dont use the "I-don't-know-where-they-come-from"-structs |
54 | * used in the kernel. | |
a527a426 MW |
55 | */ |
56 | struct xfrm_data_t { | |
57 | /** | |
58 | * length of the data | |
59 | */ | |
60 | u_int16_t length; | |
aa5a35a0 | 61 | |
a527a426 MW |
62 | /** |
63 | * type of data | |
64 | */ | |
65 | u_int16_t type; | |
aa5a35a0 | 66 | |
a527a426 MW |
67 | /** |
68 | * and the data itself, for different purposes | |
69 | */ | |
70 | union { | |
aa5a35a0 | 71 | /** algorithm */ |
a527a426 | 72 | struct xfrm_algo algo; |
aa5a35a0 | 73 | /** policy tmpl */ |
a527a426 MW |
74 | struct xfrm_user_tmpl tmpl[2]; |
75 | }; | |
76 | }; | |
77 | ||
30b5b412 | 78 | |
3ebebc5e MW |
79 | typedef struct netlink_message_t netlink_message_t; |
80 | ||
2ed133a8 MW |
81 | /** |
82 | * Representation of ANY netlink message used | |
83 | */ | |
3ebebc5e MW |
84 | struct netlink_message_t { |
85 | ||
86 | /** | |
87 | * header of the netlink message | |
88 | */ | |
89 | struct nlmsghdr hdr; | |
90 | ||
91 | union { | |
aa5a35a0 | 92 | /** error message */ |
3ebebc5e | 93 | struct nlmsgerr e; |
aa5a35a0 | 94 | /** message for spi allocation */ |
a527a426 | 95 | struct xfrm_userspi_info spi; |
aa5a35a0 | 96 | /** message for SA manipulation */ |
5d187bd2 | 97 | struct xfrm_usersa_id sa_id; |
aa5a35a0 | 98 | /** message for SA installation */ |
5d187bd2 | 99 | struct xfrm_usersa_info sa; |
aa5a35a0 | 100 | /** message for policy manipulation */ |
5d187bd2 | 101 | struct xfrm_userpolicy_id policy_id; |
aa5a35a0 | 102 | /** message for policy installation */ |
5d187bd2 | 103 | struct xfrm_userpolicy_info policy; |
3ebebc5e | 104 | }; |
aa5a35a0 | 105 | u_int8_t data[XFRM_DATA_LENGTH]; |
3ebebc5e MW |
106 | }; |
107 | ||
108 | ||
109 | typedef struct private_kernel_interface_t private_kernel_interface_t; | |
110 | ||
111 | /** | |
3febcf15 | 112 | * @brief Private Variables and Functions of kernel_interface class. |
3ebebc5e MW |
113 | * |
114 | */ | |
115 | struct private_kernel_interface_t { | |
116 | /** | |
3febcf15 | 117 | * Public part of the kernel_interface_t object. |
3ebebc5e | 118 | */ |
30b5b412 MW |
119 | kernel_interface_t public; |
120 | ||
121 | /** | |
122 | * Netlink communication socket. | |
123 | */ | |
124 | int socket; | |
125 | ||
8f1c27ba MW |
126 | /** |
127 | * Process id of kernel thread | |
128 | */ | |
ba425b87 | 129 | pid_t pid; |
8f1c27ba | 130 | |
30b5b412 MW |
131 | /** |
132 | * Sequence number for messages. | |
133 | */ | |
134 | u_int32_t seq; | |
135 | ||
136 | /** | |
137 | * List of responded messages. | |
138 | */ | |
139 | linked_list_t *responses; | |
140 | ||
141 | /** | |
142 | * Thread which receives messages. | |
143 | */ | |
144 | pthread_t thread; | |
145 | ||
146 | /** | |
147 | * Mutex locks access to replies list. | |
148 | */ | |
149 | pthread_mutex_t mutex; | |
150 | ||
151 | /** | |
152 | * Condvar allows signaling of threads waiting for a reply. | |
153 | */ | |
154 | pthread_cond_t condvar; | |
155 | ||
8f1c27ba MW |
156 | /** |
157 | * Logger for XFRM stuff | |
158 | */ | |
159 | logger_t *logger; | |
160 | ||
30b5b412 MW |
161 | /** |
162 | * Function for the thread, receives messages. | |
163 | */ | |
164 | void (*receive_messages) (private_kernel_interface_t *this); | |
165 | ||
166 | /** | |
167 | * Sends a netlink_message_t down to the kernel and wait for reply. | |
168 | */ | |
169 | status_t (*send_message) (private_kernel_interface_t *this, netlink_message_t *request, netlink_message_t **response); | |
ba425b87 MW |
170 | }; |
171 | ||
aa5a35a0 MW |
172 | /** |
173 | * In the kernel, algorithms are identified as strings, we use our | |
174 | * mapping functions... | |
175 | * Algorithms for encryption. | |
176 | * TODO: Add missing algorithm strings | |
177 | */ | |
ba425b87 MW |
178 | mapping_t kernel_encryption_algs_m[] = { |
179 | {ENCR_DES_IV64, ""}, | |
180 | {ENCR_DES, "des"}, | |
fb2b73da | 181 | {ENCR_3DES, "des3_ede"}, |
ba425b87 | 182 | {ENCR_RC5, ""}, |
fb2b73da MW |
183 | {ENCR_IDEA, "idea"}, |
184 | {ENCR_CAST, "cast128"}, | |
185 | {ENCR_BLOWFISH, "blowfish"}, | |
ba425b87 MW |
186 | {ENCR_3IDEA, ""}, |
187 | {ENCR_DES_IV32, ""}, | |
188 | {ENCR_NULL, ""}, | |
189 | {ENCR_AES_CBC, "aes"}, | |
190 | {ENCR_AES_CTR, ""}, | |
191 | {MAPPING_END, NULL} | |
192 | }; | |
aa5a35a0 MW |
193 | /** |
194 | * In the kernel, algorithms are identified as strings, we use our | |
195 | * mapping functions... | |
196 | * Algorithms for integrity protection. | |
197 | * TODO: Add missing algorithm strings | |
198 | */ | |
ba425b87 MW |
199 | mapping_t kernel_integrity_algs_m[] = { |
200 | {AUTH_HMAC_MD5_96, "md5"}, | |
201 | {AUTH_HMAC_SHA1_96, "sha1"}, | |
202 | {AUTH_DES_MAC, ""}, | |
203 | {AUTH_KPDK_MD5, ""}, | |
204 | {AUTH_AES_XCBC_96, ""}, | |
205 | {MAPPING_END, NULL} | |
3ebebc5e MW |
206 | }; |
207 | ||
8f1c27ba | 208 | |
aa5a35a0 MW |
209 | /** |
210 | * Implementation of kernel_interface_t.get_spi. | |
211 | */ | |
a527a426 MW |
212 | static status_t get_spi(private_kernel_interface_t *this, |
213 | host_t *src, host_t *dest, | |
214 | protocol_id_t protocol, u_int32_t reqid, | |
215 | u_int32_t *spi) | |
ba425b87 MW |
216 | { |
217 | netlink_message_t request, *response; | |
a527a426 | 218 | status_t status = SUCCESS; |
ba425b87 | 219 | |
8f1c27ba MW |
220 | |
221 | this->logger->log(this->logger, CONTROL|LEVEL2, "getting spi"); | |
222 | ||
30b5b412 MW |
223 | memset(&request, 0, sizeof(request)); |
224 | request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.spi))); | |
225 | request.hdr.nlmsg_flags = NLM_F_REQUEST; | |
226 | request.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI; | |
ba425b87 MW |
227 | request.spi.info.saddr = src->get_xfrm_addr(src); |
228 | request.spi.info.id.daddr = dest->get_xfrm_addr(dest); | |
a527a426 MW |
229 | request.spi.info.mode = TRUE; /* tunnel mode */ |
230 | request.spi.info.reqid = reqid; | |
dec59822 | 231 | request.spi.info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; |
30b5b412 | 232 | request.spi.info.family = PF_INET; |
aa5a35a0 MW |
233 | request.spi.min = 0xc0000000; |
234 | request.spi.max = 0xcFFFFFFF; | |
30b5b412 MW |
235 | |
236 | if (this->send_message(this, &request, &response) != SUCCESS) | |
237 | { | |
8f1c27ba MW |
238 | this->logger->log(this->logger, ERROR, "netlink communication failed"); |
239 | return FAILED; | |
30b5b412 | 240 | } |
a527a426 | 241 | else if (response->hdr.nlmsg_type == NLMSG_ERROR) |
30b5b412 | 242 | { |
8f1c27ba MW |
243 | this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got an error: %s", |
244 | strerror(-response->e.error)); | |
a527a426 | 245 | status = FAILED; |
30b5b412 | 246 | } |
a527a426 | 247 | else if (response->hdr.nlmsg_type != XFRM_MSG_NEWSA) |
30b5b412 | 248 | { |
8f1c27ba | 249 | this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got a unknown reply"); |
a527a426 | 250 | status = FAILED; |
30b5b412 MW |
251 | } |
252 | else if (response->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(response->sa))) | |
253 | { | |
8f1c27ba | 254 | this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got an invalid reply"); |
a527a426 | 255 | status = FAILED; |
30b5b412 | 256 | } |
8f1c27ba MW |
257 | else |
258 | { | |
259 | *spi = response->sa.id.spi; | |
260 | } | |
5113680f | 261 | free(response); |
30b5b412 | 262 | |
a527a426 | 263 | return status; |
ba425b87 | 264 | } |
3ebebc5e | 265 | |
aa5a35a0 MW |
266 | /** |
267 | * Implementation of kernel_interface_t.add_sa. | |
268 | */ | |
ba425b87 | 269 | static status_t add_sa( private_kernel_interface_t *this, |
fc186705 MW |
270 | host_t *me, |
271 | host_t *other, | |
272 | u_int32_t spi, | |
273 | int protocol, | |
a527a426 | 274 | u_int32_t reqid, |
ba425b87 | 275 | encryption_algorithm_t enc_alg, |
fc186705 | 276 | chunk_t encryption_key, |
ba425b87 | 277 | integrity_algorithm_t int_alg, |
fc186705 | 278 | chunk_t integrity_key, |
ba425b87 MW |
279 | bool replace) |
280 | { | |
30b5b412 MW |
281 | netlink_message_t request, *response; |
282 | memset(&request, 0, sizeof(request)); | |
8f1c27ba MW |
283 | status_t status = SUCCESS; |
284 | ||
285 | this->logger->log(this->logger, CONTROL|LEVEL2, "adding SA"); | |
30b5b412 MW |
286 | |
287 | request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | |
288 | request.hdr.nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; | |
289 | ||
290 | request.sa.saddr = me->get_xfrm_addr(me); | |
291 | request.sa.id.daddr = other->get_xfrm_addr(other); | |
292 | ||
293 | request.sa.id.spi = spi; | |
dec59822 | 294 | request.sa.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; |
30b5b412 | 295 | request.sa.family = me->get_family(me); |
a527a426 | 296 | request.sa.mode = TRUE; /* tunnel mode */ |
8a491129 | 297 | request.sa.replay_window = 32; |
a527a426 | 298 | request.sa.reqid = reqid; |
30b5b412 MW |
299 | request.sa.lft.soft_byte_limit = XFRM_INF; |
300 | request.sa.lft.soft_packet_limit = XFRM_INF; | |
301 | request.sa.lft.hard_byte_limit = XFRM_INF; | |
302 | request.sa.lft.hard_packet_limit = XFRM_INF; | |
303 | ||
304 | request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.sa))); | |
305 | ||
306 | if (enc_alg != ENCR_UNDEFINED) | |
307 | { | |
a527a426 | 308 | xfrm_data_t *data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); |
30b5b412 | 309 | |
a527a426 MW |
310 | data->type = XFRMA_ALG_CRYPT; |
311 | data->length = 4 + sizeof(data->algo) + encryption_key.len; | |
312 | data->algo.alg_key_len = encryption_key.len * 8; | |
aa5a35a0 MW |
313 | request.hdr.nlmsg_len += data->length; |
314 | if (request.hdr.nlmsg_len > sizeof(request)) | |
315 | { | |
316 | return FAILED; | |
317 | } | |
a527a426 MW |
318 | strcpy(data->algo.alg_name, mapping_find(kernel_encryption_algs_m, enc_alg)); |
319 | memcpy(data->algo.alg_key, encryption_key.ptr, encryption_key.len); | |
30b5b412 MW |
320 | } |
321 | ||
322 | if (int_alg != AUTH_UNDEFINED) | |
323 | { | |
a527a426 | 324 | xfrm_data_t *data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); |
ba425b87 | 325 | |
a527a426 MW |
326 | data->type = XFRMA_ALG_AUTH; |
327 | data->length = 4 + sizeof(data->algo) + integrity_key.len; | |
328 | data->algo.alg_key_len = integrity_key.len * 8; | |
aa5a35a0 MW |
329 | request.hdr.nlmsg_len += data->length; |
330 | if (request.hdr.nlmsg_len > sizeof(request)) | |
331 | { | |
332 | return FAILED; | |
333 | } | |
a527a426 MW |
334 | strcpy(data->algo.alg_name, mapping_find(kernel_integrity_algs_m, int_alg)); |
335 | memcpy(data->algo.alg_key, integrity_key.ptr, integrity_key.len); | |
30b5b412 MW |
336 | } |
337 | ||
aa5a35a0 | 338 | /* TODO: add IPComp here*/ |
30b5b412 MW |
339 | |
340 | if (this->send_message(this, &request, &response) != SUCCESS) | |
341 | { | |
8f1c27ba MW |
342 | this->logger->log(this->logger, ERROR, "netlink communication failed"); |
343 | return FAILED; | |
a527a426 MW |
344 | } |
345 | else if (response->hdr.nlmsg_type != NLMSG_ERROR) | |
346 | { | |
8f1c27ba | 347 | this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWSA not acknowledged"); |
a527a426 MW |
348 | status = FAILED; |
349 | } | |
350 | else if (response->e.error) | |
351 | { | |
8f1c27ba MW |
352 | this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWSA got error %s", |
353 | strerror(-response->e.error)); | |
a527a426 | 354 | status = FAILED; |
30b5b412 MW |
355 | } |
356 | ||
5113680f | 357 | free(response); |
8f1c27ba | 358 | return status; |
ba425b87 MW |
359 | } |
360 | ||
5d187bd2 MW |
361 | static status_t del_sa( private_kernel_interface_t *this, |
362 | host_t *dst, | |
363 | u_int32_t spi, | |
364 | protocol_id_t protocol) | |
365 | { | |
366 | netlink_message_t request, *response; | |
367 | memset(&request, 0, sizeof(request)); | |
8f1c27ba MW |
368 | status_t status = SUCCESS; |
369 | ||
370 | this->logger->log(this->logger, CONTROL|LEVEL2, "deleting SA"); | |
5d187bd2 MW |
371 | |
372 | request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | |
373 | request.hdr.nlmsg_type = XFRM_MSG_DELSA; | |
374 | ||
375 | request.sa_id.daddr = dst->get_xfrm_addr(dst); | |
376 | ||
377 | request.sa_id.spi = spi; | |
dec59822 | 378 | request.sa_id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; |
5d187bd2 MW |
379 | request.sa_id.family = dst->get_family(dst); |
380 | ||
381 | request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.sa_id))); | |
382 | ||
383 | if (this->send_message(this, &request, &response) != SUCCESS) | |
384 | { | |
8f1c27ba | 385 | return FAILED; |
5d187bd2 MW |
386 | } |
387 | else if (response->hdr.nlmsg_type != NLMSG_ERROR) | |
388 | { | |
389 | status = FAILED; | |
390 | } | |
391 | else if (response->e.error) | |
392 | { | |
393 | status = FAILED; | |
394 | } | |
395 | ||
5113680f | 396 | free(response); |
8f1c27ba | 397 | return status; |
5d187bd2 MW |
398 | } |
399 | ||
aa5a35a0 MW |
400 | /** |
401 | * Implementation of kernel_interface_t.add_policy. | |
402 | */ | |
a527a426 MW |
403 | static status_t add_policy(private_kernel_interface_t *this, |
404 | host_t *me, host_t *other, | |
405 | host_t *src, host_t *dst, | |
406 | u_int8_t src_hostbits, u_int8_t dst_hostbits, | |
407 | int direction, int upper_proto, | |
408 | bool ah, bool esp, | |
409 | u_int32_t reqid) | |
410 | { | |
411 | netlink_message_t request, *response; | |
412 | status_t status = SUCCESS; | |
413 | ||
8f1c27ba MW |
414 | this->logger->log(this->logger, CONTROL|LEVEL2, "adding policy"); |
415 | ||
a527a426 MW |
416 | memset(&request, 0, sizeof(request)); |
417 | request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | |
418 | ||
419 | request.policy.sel.sport = htons(src->get_port(src)); | |
420 | request.policy.sel.dport = htons(dst->get_port(dst)); | |
421 | request.policy.sel.sport_mask = (request.policy.sel.sport) ? ~0 : 0; | |
422 | request.policy.sel.dport_mask = (request.policy.sel.dport) ? ~0 : 0; | |
423 | request.policy.sel.saddr = src->get_xfrm_addr(src); | |
424 | request.policy.sel.daddr = dst->get_xfrm_addr(dst); | |
425 | request.policy.sel.prefixlen_s = src_hostbits; | |
426 | request.policy.sel.prefixlen_d = dst_hostbits; | |
427 | request.policy.sel.proto = upper_proto; | |
428 | request.policy.sel.family = src->get_family(src); | |
5d187bd2 MW |
429 | |
430 | request.hdr.nlmsg_type = XFRM_MSG_NEWPOLICY; | |
431 | request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.policy))); | |
432 | ||
a527a426 MW |
433 | request.policy.dir = direction; |
434 | request.policy.priority = SPD_PRIORITY; | |
435 | request.policy.action = XFRM_POLICY_ALLOW; | |
436 | request.policy.share = XFRM_SHARE_ANY; | |
437 | ||
438 | request.policy.lft.soft_byte_limit = XFRM_INF; | |
439 | request.policy.lft.soft_packet_limit = XFRM_INF; | |
440 | request.policy.lft.hard_byte_limit = XFRM_INF; | |
441 | request.policy.lft.hard_packet_limit = XFRM_INF; | |
a527a426 MW |
442 | |
443 | if (esp || ah) | |
444 | { | |
445 | xfrm_data_t *data; | |
446 | int tmpl_pos = 0; | |
447 | data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); | |
448 | data->type = XFRMA_TMPL; | |
449 | if (esp) | |
450 | { | |
451 | data->tmpl[tmpl_pos].reqid = reqid; | |
452 | data->tmpl[tmpl_pos].id.proto = KERNEL_ESP; | |
453 | data->tmpl[tmpl_pos].aalgos = data->tmpl[tmpl_pos].ealgos = data->tmpl[tmpl_pos].calgos = ~0; | |
454 | data->tmpl[tmpl_pos].mode = TRUE; | |
5d187bd2 | 455 | |
a527a426 MW |
456 | data->tmpl[tmpl_pos].saddr = me->get_xfrm_addr(me); |
457 | data->tmpl[tmpl_pos].id.daddr = me->get_xfrm_addr(other); | |
458 | ||
459 | tmpl_pos++; | |
460 | } | |
461 | if (ah) | |
462 | { | |
463 | data->tmpl[tmpl_pos].reqid = reqid; | |
464 | data->tmpl[tmpl_pos].id.proto = KERNEL_AH; | |
465 | data->tmpl[tmpl_pos].aalgos = data->tmpl[tmpl_pos].ealgos = data->tmpl[tmpl_pos].calgos = ~0; | |
466 | data->tmpl[tmpl_pos].mode = TRUE; | |
5d187bd2 | 467 | |
a527a426 MW |
468 | data->tmpl[tmpl_pos].saddr = me->get_xfrm_addr(me); |
469 | data->tmpl[tmpl_pos].id.daddr = other->get_xfrm_addr(other); | |
470 | ||
471 | tmpl_pos++; | |
472 | } | |
473 | data->length = 4 + sizeof(struct xfrm_user_tmpl) * tmpl_pos; | |
474 | request.hdr.nlmsg_len += data->length; | |
475 | } | |
476 | ||
477 | if (this->send_message(this, &request, &response) != SUCCESS) | |
478 | { | |
8f1c27ba MW |
479 | this->logger->log(this->logger, ERROR, "netlink communication failed"); |
480 | return FAILED; | |
a527a426 MW |
481 | } |
482 | else if (response->hdr.nlmsg_type != NLMSG_ERROR) | |
483 | { | |
8f1c27ba | 484 | this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY not acknowledged"); |
a527a426 MW |
485 | status = FAILED; |
486 | } | |
487 | else if (response->e.error) | |
488 | { | |
8f1c27ba MW |
489 | this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY got error %s", |
490 | strerror(-response->e.error)); | |
a527a426 MW |
491 | status = FAILED; |
492 | } | |
493 | ||
5113680f | 494 | free(response); |
a527a426 MW |
495 | return status; |
496 | } | |
497 | ||
aa5a35a0 MW |
498 | /** |
499 | * Implementation of kernel_interface_t.del_policy. | |
500 | */ | |
5d187bd2 MW |
501 | static status_t del_policy(private_kernel_interface_t *this, |
502 | host_t *me, host_t *other, | |
503 | host_t *src, host_t *dst, | |
504 | u_int8_t src_hostbits, u_int8_t dst_hostbits, | |
505 | int direction, int upper_proto) | |
506 | { | |
507 | netlink_message_t request, *response; | |
508 | status_t status = SUCCESS; | |
509 | ||
8f1c27ba MW |
510 | |
511 | this->logger->log(this->logger, CONTROL|LEVEL2, "deleting policy"); | |
512 | ||
5d187bd2 MW |
513 | memset(&request, 0, sizeof(request)); |
514 | request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | |
515 | ||
516 | request.policy_id.sel.sport = htons(src->get_port(src)); | |
517 | request.policy_id.sel.dport = htons(dst->get_port(dst)); | |
518 | request.policy_id.sel.sport_mask = (request.policy.sel.sport) ? ~0 : 0; | |
519 | request.policy_id.sel.dport_mask = (request.policy.sel.dport) ? ~0 : 0; | |
520 | request.policy_id.sel.saddr = src->get_xfrm_addr(src); | |
521 | request.policy_id.sel.daddr = dst->get_xfrm_addr(dst); | |
522 | request.policy_id.sel.prefixlen_s = src_hostbits; | |
523 | request.policy_id.sel.prefixlen_d = dst_hostbits; | |
524 | request.policy_id.sel.proto = upper_proto; | |
525 | request.policy_id.sel.family = src->get_family(src); | |
526 | ||
527 | request.policy_id.dir = direction; | |
528 | ||
529 | request.hdr.nlmsg_type = XFRM_MSG_DELPOLICY; | |
530 | request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.policy_id))); | |
531 | ||
532 | if (this->send_message(this, &request, &response) != SUCCESS) | |
533 | { | |
8f1c27ba | 534 | return FAILED; |
5d187bd2 MW |
535 | } |
536 | else if (response->hdr.nlmsg_type != NLMSG_ERROR) | |
537 | { | |
538 | status = FAILED; | |
539 | } | |
540 | else if (response->e.error) | |
541 | { | |
542 | status = FAILED; | |
543 | } | |
544 | ||
5113680f | 545 | free(response); |
5d187bd2 MW |
546 | return status; |
547 | } | |
548 | ||
aa5a35a0 MW |
549 | /** |
550 | * Implementation of private_kernel_interface_t.send_message. | |
551 | */ | |
ba425b87 MW |
552 | static status_t send_message(private_kernel_interface_t *this, netlink_message_t *request, netlink_message_t **response) |
553 | { | |
554 | size_t length; | |
555 | struct sockaddr_nl addr; | |
556 | ||
557 | request->hdr.nlmsg_seq = ++this->seq; | |
558 | request->hdr.nlmsg_pid = this->pid; | |
30b5b412 | 559 | |
ba425b87 MW |
560 | memset(&addr, 0, sizeof(struct sockaddr_nl)); |
561 | addr.nl_family = AF_NETLINK; | |
562 | addr.nl_pid = 0; | |
563 | addr.nl_groups = 0; | |
564 | ||
565 | length = sendto(this->socket,(void *)request, request->hdr.nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); | |
566 | ||
567 | if (length < 0) | |
568 | { | |
569 | return FAILED; | |
570 | } | |
571 | else if (length != request->hdr.nlmsg_len) | |
572 | { | |
573 | return FAILED; | |
574 | } | |
575 | ||
576 | pthread_mutex_lock(&(this->mutex)); | |
577 | ||
578 | while (TRUE) | |
579 | { | |
580 | iterator_t *iterator; | |
581 | bool found = FALSE; | |
582 | /* search list, break if found */ | |
583 | iterator = this->responses->create_iterator(this->responses, TRUE); | |
584 | while (iterator->has_next(iterator)) | |
585 | { | |
586 | netlink_message_t *listed_response; | |
587 | iterator->current(iterator, (void**)&listed_response); | |
588 | if (listed_response->hdr.nlmsg_seq == request->hdr.nlmsg_seq) | |
589 | { | |
590 | /* matches our request, this is the reply */ | |
30b5b412 MW |
591 | *response = listed_response; |
592 | found = TRUE; | |
593 | break; | |
594 | } | |
595 | } | |
596 | iterator->destroy(iterator); | |
597 | ||
598 | if (found) | |
599 | { | |
600 | break; | |
601 | } | |
aa5a35a0 | 602 | /* TODO: we should time out, if something goes wrong!??? */ |
ba425b87 MW |
603 | pthread_cond_wait(&(this->condvar), &(this->mutex)); |
604 | } | |
605 | ||
606 | pthread_mutex_unlock(&(this->mutex)); | |
607 | ||
608 | return SUCCESS; | |
609 | } | |
610 | ||
aa5a35a0 MW |
611 | /** |
612 | * Implementation of private_kernel_interface_t.receive_messages. | |
613 | */ | |
ba425b87 | 614 | static void receive_messages(private_kernel_interface_t *this) |
30b5b412 | 615 | { |
ba425b87 MW |
616 | while(TRUE) |
617 | { | |
618 | netlink_message_t response, *listed_response; | |
619 | while (TRUE) | |
620 | { | |
30b5b412 | 621 | struct sockaddr_nl addr; |
ba425b87 MW |
622 | socklen_t addr_length; |
623 | size_t length; | |
30b5b412 | 624 | |
ba425b87 MW |
625 | addr_length = sizeof(addr); |
626 | ||
627 | response.hdr.nlmsg_type = XFRM_MSG_NEWSA; | |
628 | length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length); | |
629 | if (length < 0) | |
630 | { | |
631 | if (errno == EINTR) | |
30b5b412 MW |
632 | { |
633 | /* interrupted, try again */ | |
ba425b87 | 634 | continue; |
30b5b412 MW |
635 | } |
636 | charon->kill(charon, "receiving from netlink socket failed"); | |
ba425b87 MW |
637 | } |
638 | if (!NLMSG_OK(&response.hdr, length)) | |
639 | { | |
640 | /* bad netlink message */ | |
641 | continue; | |
642 | } | |
643 | if (addr.nl_pid != 0) | |
644 | { | |
2ed133a8 | 645 | /* not from kernel. not interested, try another one */ |
30b5b412 | 646 | continue; |
ba425b87 MW |
647 | } |
648 | break; | |
649 | } | |
650 | ||
651 | /* got a valid message. | |
2ed133a8 | 652 | * requests are handled on our own, |
ba425b87 MW |
653 | * responses are listed for the requesters |
654 | */ | |
655 | if (response.hdr.nlmsg_flags & NLM_F_REQUEST) | |
656 | { | |
657 | /* handle request */ | |
658 | } | |
659 | else | |
660 | { | |
661 | /* add response to queue */ | |
5113680f | 662 | listed_response = malloc(sizeof(response)); |
ba425b87 MW |
663 | memcpy(listed_response, &response, sizeof(response)); |
664 | ||
665 | pthread_mutex_lock(&(this->mutex)); | |
666 | this->responses->insert_last(this->responses, (void*)listed_response); | |
667 | pthread_mutex_unlock(&(this->mutex)); | |
2ed133a8 | 668 | /* signal ALL waiting threads */ |
ba425b87 MW |
669 | pthread_cond_broadcast(&(this->condvar)); |
670 | } | |
671 | /* get the next one */ | |
672 | } | |
673 | } | |
674 | ||
ba425b87 | 675 | /** |
3febcf15 | 676 | * Implementation of kernel_interface_t.destroy. |
3ebebc5e | 677 | */ |
ba425b87 | 678 | static void destroy(private_kernel_interface_t *this) |
3ebebc5e | 679 | { |
ba425b87 MW |
680 | pthread_cancel(this->thread); |
681 | pthread_join(this->thread, NULL); | |
682 | close(this->socket); | |
683 | this->responses->destroy(this->responses); | |
5113680f | 684 | free(this); |
3ebebc5e MW |
685 | } |
686 | ||
687 | /* | |
3febcf15 | 688 | * Described in header. |
3ebebc5e MW |
689 | */ |
690 | kernel_interface_t *kernel_interface_create() | |
691 | { | |
5113680f | 692 | private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t); |
3ebebc5e MW |
693 | |
694 | /* public functions */ | |
a527a426 MW |
695 | this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi; |
696 | this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,encryption_algorithm_t,chunk_t,integrity_algorithm_t,chunk_t,bool))add_sa; | |
697 | this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*, host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int,bool,bool,u_int32_t))add_policy; | |
aa5a35a0 MW |
698 | this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; |
699 | this->public.del_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int))del_policy; | |
5d187bd2 | 700 | |
3ebebc5e MW |
701 | this->public.destroy = (void(*)(kernel_interface_t*)) destroy; |
702 | ||
703 | /* private members */ | |
ba425b87 MW |
704 | this->receive_messages = receive_messages; |
705 | this->send_message = send_message; | |
706 | this->pid = getpid(); | |
707 | this->responses = linked_list_create(); | |
8f1c27ba | 708 | this->logger = logger_manager->get_logger(logger_manager, XFRM); |
ba425b87 MW |
709 | pthread_mutex_init(&(this->mutex),NULL); |
710 | pthread_cond_init(&(this->condvar),NULL); | |
711 | this->seq = 0; | |
3ebebc5e | 712 | this->socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM); |
ba425b87 MW |
713 | if (this->socket <= 0) |
714 | { | |
8f1c27ba | 715 | this->responses->destroy(this->responses); |
5113680f | 716 | free(this); |
ba425b87 MW |
717 | charon->kill(charon, "Unable to create netlink socket"); |
718 | } | |
719 | ||
720 | if (pthread_create(&(this->thread), NULL, (void*(*)(void*))this->receive_messages, this) != 0) | |
721 | { | |
8f1c27ba | 722 | this->responses->destroy(this->responses); |
ba425b87 | 723 | close(this->socket); |
5113680f | 724 | free(this); |
ba425b87 MW |
725 | charon->kill(charon, "Unable to create netlink thread"); |
726 | } | |
3ebebc5e MW |
727 | |
728 | return (&this->public); | |
729 | } |