]>
Commit | Line | Data |
---|---|---|
7a5dd544 | 1 | /* |
d80055ba | 2 | * Copyright (C) 2016-2017 Tobias Brunner |
7a5dd544 | 3 | * Copyright (C) 2008 Martin Willi |
19ef2aec TB |
4 | * |
5 | * Copyright (C) secunet Security Networks AG | |
7a5dd544 TB |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
16 | */ | |
17 | ||
18 | #include "mock_ipsec.h" | |
19 | ||
d80055ba TB |
20 | #include <daemon.h> |
21 | #include <collections/hashtable.h> | |
2b581b59 | 22 | #include <collections/array.h> |
d80055ba TB |
23 | |
24 | #include <assert.h> | |
25 | ||
7a5dd544 TB |
26 | typedef struct private_kernel_ipsec_t private_kernel_ipsec_t; |
27 | ||
28 | /** | |
29 | * Private data | |
30 | */ | |
31 | struct private_kernel_ipsec_t { | |
32 | ||
33 | /** | |
34 | * Public interface | |
35 | */ | |
36 | kernel_ipsec_t public; | |
37 | ||
2b581b59 TB |
38 | /** |
39 | * Rekey listener | |
40 | */ | |
41 | listener_t listener; | |
42 | ||
7a5dd544 TB |
43 | /** |
44 | * Allocated SPI | |
45 | */ | |
46 | refcount_t spi; | |
d80055ba TB |
47 | |
48 | /** | |
49 | * Installed SAs | |
50 | */ | |
51 | hashtable_t *sas; | |
7a5dd544 TB |
52 | }; |
53 | ||
d80055ba TB |
54 | /** |
55 | * Global instance | |
56 | */ | |
57 | static private_kernel_ipsec_t *instance; | |
58 | ||
59 | /** | |
60 | * Data about installed IPsec SAs | |
61 | */ | |
62 | typedef struct { | |
63 | /** | |
64 | * SPI of the SA | |
65 | */ | |
66 | uint32_t spi; | |
67 | ||
68 | /** | |
69 | * Associated IKE_SA | |
70 | */ | |
71 | ike_sa_t *ike_sa; | |
72 | ||
73 | /** | |
74 | * TRUE if this was an allocated SPI | |
75 | */ | |
76 | bool alloc; | |
77 | ||
78 | } entry_t; | |
79 | ||
80 | /** | |
81 | * Hash an IPsec SA entry | |
82 | */ | |
83 | static u_int entry_hash(const void *key) | |
84 | { | |
85 | entry_t *entry = (entry_t*)key; | |
86 | return chunk_hash_inc(chunk_from_thing(entry->spi), | |
87 | chunk_hash(chunk_from_thing(entry->ike_sa))); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Compare an IPsec SA entry | |
92 | */ | |
93 | static bool entry_equals(const void *key, const void *other_key) | |
94 | { | |
95 | entry_t *a = (entry_t*)key, *b = (entry_t*)other_key; | |
96 | return a->spi == b->spi && a->ike_sa == b->ike_sa; | |
97 | } | |
98 | ||
7a5dd544 TB |
99 | METHOD(kernel_ipsec_t, get_spi, status_t, |
100 | private_kernel_ipsec_t *this, host_t *src, host_t *dst, uint8_t protocol, | |
101 | uint32_t *spi) | |
102 | { | |
d80055ba TB |
103 | entry_t *entry; |
104 | ||
7a5dd544 | 105 | *spi = (uint32_t)ref_get(&this->spi); |
d80055ba TB |
106 | INIT(entry, |
107 | .spi = *spi, | |
108 | .ike_sa = charon->bus->get_sa(charon->bus), | |
109 | .alloc = TRUE, | |
110 | ); | |
111 | entry = this->sas->put(this->sas, entry, entry); | |
112 | assert(!entry); | |
7a5dd544 TB |
113 | return SUCCESS; |
114 | } | |
115 | ||
116 | METHOD(kernel_ipsec_t, get_cpi, status_t, | |
117 | private_kernel_ipsec_t *this, host_t *src, host_t *dst, uint16_t *cpi) | |
118 | { | |
119 | return FAILED; | |
120 | } | |
121 | ||
122 | METHOD(kernel_ipsec_t, add_sa, status_t, | |
123 | private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, | |
124 | kernel_ipsec_add_sa_t *data) | |
125 | { | |
d80055ba TB |
126 | entry_t *entry; |
127 | ||
128 | INIT(entry, | |
129 | .spi = id->spi, | |
130 | .ike_sa = charon->bus->get_sa(charon->bus), | |
131 | ); | |
132 | if (data->inbound) | |
133 | { | |
134 | entry = this->sas->put(this->sas, entry, entry); | |
135 | assert(entry && entry->alloc); | |
136 | free(entry); | |
137 | } | |
138 | else | |
139 | { | |
140 | entry = this->sas->put(this->sas, entry, entry); | |
141 | assert(!entry); | |
142 | } | |
7a5dd544 TB |
143 | return SUCCESS; |
144 | } | |
145 | ||
146 | METHOD(kernel_ipsec_t, update_sa, status_t, | |
147 | private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, | |
148 | kernel_ipsec_update_sa_t *data) | |
149 | { | |
150 | return SUCCESS; | |
151 | } | |
152 | ||
153 | METHOD(kernel_ipsec_t, query_sa, status_t, | |
154 | private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, | |
155 | kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets, | |
156 | time_t *time) | |
157 | { | |
158 | return NOT_SUPPORTED; | |
159 | } | |
160 | ||
161 | METHOD(kernel_ipsec_t, del_sa, status_t, | |
162 | private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, | |
163 | kernel_ipsec_del_sa_t *data) | |
164 | { | |
d80055ba TB |
165 | entry_t *entry, lookup = { |
166 | .spi = id->spi, | |
167 | .ike_sa = charon->bus->get_sa(charon->bus), | |
168 | }; | |
169 | ||
170 | entry = this->sas->remove(this->sas, &lookup); | |
171 | assert(entry); | |
172 | free(entry); | |
7a5dd544 TB |
173 | return SUCCESS; |
174 | } | |
175 | ||
2b581b59 TB |
176 | METHOD(listener_t, ike_rekey, bool, |
177 | listener_t *listener, ike_sa_t *old, ike_sa_t *new) | |
178 | { | |
179 | enumerator_t *enumerator; | |
180 | array_t *sas = NULL; | |
181 | entry_t *entry; | |
182 | ||
183 | enumerator = instance->sas->create_enumerator(instance->sas); | |
184 | while (enumerator->enumerate(enumerator, &entry, NULL)) | |
185 | { | |
186 | if (entry->ike_sa == old) | |
187 | { | |
188 | instance->sas->remove_at(instance->sas, enumerator); | |
189 | array_insert_create(&sas, ARRAY_TAIL, entry); | |
190 | } | |
191 | } | |
192 | enumerator->destroy(enumerator); | |
193 | enumerator = array_create_enumerator(sas); | |
194 | while (enumerator->enumerate(enumerator, &entry)) | |
195 | { | |
196 | array_remove_at(sas, enumerator); | |
197 | entry->ike_sa = new; | |
198 | entry = instance->sas->put(instance->sas, entry, entry); | |
199 | assert(!entry); | |
200 | } | |
201 | enumerator->destroy(enumerator); | |
202 | array_destroy(sas); | |
203 | return TRUE; | |
204 | } | |
205 | ||
7a5dd544 TB |
206 | METHOD(kernel_ipsec_t, add_policy, status_t, |
207 | private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, | |
208 | kernel_ipsec_manage_policy_t *data) | |
209 | { | |
210 | return SUCCESS; | |
211 | } | |
212 | ||
213 | METHOD(kernel_ipsec_t, query_policy, status_t, | |
214 | private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, | |
215 | kernel_ipsec_query_policy_t *data, time_t *use_time) | |
216 | { | |
217 | *use_time = 1; | |
218 | return SUCCESS; | |
219 | } | |
220 | ||
221 | METHOD(kernel_ipsec_t, del_policy, status_t, | |
222 | private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, | |
223 | kernel_ipsec_manage_policy_t *data) | |
224 | { | |
225 | return SUCCESS; | |
226 | } | |
227 | ||
610745e7 TB |
228 | CALLBACK(destroy_spis, void, |
229 | entry_t *entry, const void* key) | |
230 | { | |
231 | /* only free allocated SPIs, other SAs that were not properly deleted will | |
232 | * cause a leak */ | |
233 | if (entry->alloc) | |
234 | { | |
235 | free(entry); | |
236 | } | |
237 | } | |
238 | ||
d80055ba TB |
239 | METHOD(kernel_ipsec_t, destroy, void, |
240 | private_kernel_ipsec_t *this) | |
241 | { | |
2b581b59 | 242 | charon->bus->remove_listener(charon->bus, &this->listener); |
610745e7 | 243 | this->sas->destroy_function(this->sas, destroy_spis); |
d80055ba TB |
244 | free(this); |
245 | } | |
246 | ||
7a5dd544 TB |
247 | /* |
248 | * Described in header | |
249 | */ | |
250 | kernel_ipsec_t *mock_ipsec_create() | |
251 | { | |
252 | private_kernel_ipsec_t *this; | |
253 | ||
254 | INIT(this, | |
255 | .public = { | |
256 | .get_spi = _get_spi, | |
257 | .get_cpi = _get_cpi, | |
258 | .add_sa = _add_sa, | |
259 | .update_sa = _update_sa, | |
260 | .query_sa = _query_sa, | |
261 | .del_sa = _del_sa, | |
262 | .flush_sas = (void*)return_failed, | |
263 | .add_policy = _add_policy, | |
264 | .query_policy = _query_policy, | |
265 | .del_policy = _del_policy, | |
266 | .flush_policies = (void*)return_failed, | |
267 | .bypass_socket = (void*)return_true, | |
268 | .enable_udp_decap = (void*)return_true, | |
d80055ba | 269 | .destroy = _destroy, |
7a5dd544 | 270 | }, |
2b581b59 TB |
271 | .listener = { |
272 | .ike_rekey = _ike_rekey, | |
273 | }, | |
d80055ba | 274 | .sas = hashtable_create(entry_hash, entry_equals, 8), |
7a5dd544 | 275 | ); |
d80055ba TB |
276 | |
277 | instance = this; | |
278 | ||
2b581b59 TB |
279 | charon->bus->add_listener(charon->bus, &this->listener); |
280 | ||
7a5dd544 TB |
281 | return &this->public; |
282 | } | |
d80055ba | 283 | |
525cc46c TB |
284 | |
285 | CALLBACK(filter_sas, bool, | |
286 | void *data, enumerator_t *orig, va_list args) | |
d80055ba | 287 | { |
525cc46c TB |
288 | entry_t *entry; |
289 | ike_sa_t **ike_sa; | |
290 | uint32_t *spi; | |
291 | ||
292 | VA_ARGS_VGET(args, ike_sa, spi); | |
293 | ||
294 | while (orig->enumerate(orig, &entry, NULL)) | |
d80055ba | 295 | { |
525cc46c TB |
296 | if (entry->alloc) |
297 | { | |
298 | continue; | |
299 | } | |
300 | *ike_sa = entry->ike_sa; | |
301 | *spi = entry->spi; | |
302 | return TRUE; | |
d80055ba | 303 | } |
525cc46c | 304 | return FALSE; |
d80055ba TB |
305 | } |
306 | ||
307 | /* | |
308 | * Described in header | |
309 | */ | |
310 | enumerator_t *mock_ipsec_create_sa_enumerator() | |
311 | { | |
312 | return enumerator_create_filter( | |
313 | instance->sas->create_enumerator(instance->sas), | |
525cc46c | 314 | filter_sas, NULL, NULL); |
d80055ba | 315 | } |