unity: Allow UNITY_LOCAL_LAN to be longer than 8 bytes
[strongswan.git] / src / libcharon / plugins / unity / unity_handler.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
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 "unity_handler.h"
17
18 #include <daemon.h>
19 #include <threading/mutex.h>
20 #include <collections/linked_list.h>
21 #include <processing/jobs/callback_job.h>
22
23 typedef struct private_unity_handler_t private_unity_handler_t;
24
25 /**
26 * Private data of an unity_handler_t object.
27 */
28 struct private_unity_handler_t {
29
30 /**
31 * Public unity_handler_t interface.
32 */
33 unity_handler_t public;
34
35 /**
36 * List of subnets to include, as entry_t
37 */
38 linked_list_t *include;
39
40 /**
41 * Mutex for concurrent access to lists
42 */
43 mutex_t *mutex;
44 };
45
46 /**
47 * Traffic selector entry for networks to include under a given IKE_SA
48 */
49 typedef struct {
50 /** associated IKE_SA, unique ID */
51 u_int32_t sa;
52 /** traffic selector to include/exclude */
53 traffic_selector_t *ts;
54 } entry_t;
55
56 /**
57 * Clean up an entry
58 */
59 static void entry_destroy(entry_t *this)
60 {
61 this->ts->destroy(this->ts);
62 free(this);
63 }
64
65 /**
66 * Create a traffic selector from a unity subnet definition
67 */
68 static traffic_selector_t *create_ts(chunk_t subnet)
69 {
70 chunk_t net, mask;
71 int i;
72
73 if (subnet.len < 8)
74 {
75 return NULL;
76 }
77 net = chunk_create(subnet.ptr, 4);
78 mask = chunk_clonea(chunk_skip(subnet, 4));
79 for (i = 0; i < net.len; i++)
80 {
81 mask.ptr[i] = (mask.ptr[i] ^ 0xFF) | net.ptr[i];
82 }
83 return traffic_selector_create_from_bytes(0, TS_IPV4_ADDR_RANGE,
84 net, 0, mask, 65535);
85 }
86
87 /**
88 * Store a subnet to include in tunnels under this IKE_SA
89 */
90 static bool add_include(private_unity_handler_t *this, chunk_t subnet)
91 {
92 traffic_selector_t *ts;
93 ike_sa_t *ike_sa;
94 entry_t *entry;
95
96 ike_sa = charon->bus->get_sa(charon->bus);
97 if (!ike_sa)
98 {
99 return FALSE;
100 }
101 ts = create_ts(subnet);
102 if (!ts)
103 {
104 return FALSE;
105 }
106 INIT(entry,
107 .sa = ike_sa->get_unique_id(ike_sa),
108 .ts = ts,
109 );
110
111 this->mutex->lock(this->mutex);
112 this->include->insert_last(this->include, entry);
113 this->mutex->unlock(this->mutex);
114 return TRUE;
115 }
116
117 /**
118 * Remove a subnet from the inclusion list for this IKE_SA
119 */
120 static bool remove_include(private_unity_handler_t *this, chunk_t subnet)
121 {
122 enumerator_t *enumerator;
123 traffic_selector_t *ts;
124 ike_sa_t *ike_sa;
125 entry_t *entry;
126
127 ike_sa = charon->bus->get_sa(charon->bus);
128 if (!ike_sa)
129 {
130 return FALSE;
131 }
132 ts = create_ts(subnet);
133 if (!ts)
134 {
135 return FALSE;
136 }
137
138 this->mutex->lock(this->mutex);
139 enumerator = this->include->create_enumerator(this->include);
140 while (enumerator->enumerate(enumerator, &entry))
141 {
142 if (entry->sa == ike_sa->get_unique_id(ike_sa) &&
143 ts->equals(ts, entry->ts))
144 {
145 this->include->remove_at(this->include, enumerator);
146 entry_destroy(entry);
147 break;
148 }
149 }
150 enumerator->destroy(enumerator);
151 this->mutex->unlock(this->mutex);
152 ts->destroy(ts);
153 return TRUE;
154 }
155
156 /**
157 * Create a unique shunt name for a bypass policy
158 */
159 static void create_shunt_name(ike_sa_t *ike_sa, traffic_selector_t *ts,
160 char *buf, size_t len)
161 {
162 snprintf(buf, len, "Unity (%s[%u]: %R)", ike_sa->get_name(ike_sa),
163 ike_sa->get_unique_id(ike_sa), ts);
164 }
165
166 /**
167 * Install entry as a shunt policy
168 */
169 static job_requeue_t add_exclude_async(entry_t *entry)
170 {
171 enumerator_t *enumerator;
172 child_cfg_t *child_cfg;
173 lifetime_cfg_t lft = { .time = { .life = 0 } };
174 ike_sa_t *ike_sa;
175 char name[128];
176 host_t *host;
177
178 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
179 entry->sa, FALSE);
180 if (ike_sa)
181 {
182 create_shunt_name(ike_sa, entry->ts, name, sizeof(name));
183
184 child_cfg = child_cfg_create(name, &lft, NULL, TRUE, MODE_PASS,
185 ACTION_NONE, ACTION_NONE, ACTION_NONE,
186 FALSE, 0, 0, NULL, NULL, FALSE);
187 child_cfg->add_traffic_selector(child_cfg, FALSE,
188 entry->ts->clone(entry->ts));
189 host = ike_sa->get_my_host(ike_sa);
190 child_cfg->add_traffic_selector(child_cfg, TRUE,
191 traffic_selector_create_from_subnet(host->clone(host),
192 32, 0, 0, 65535));
193 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
194 while (enumerator->enumerate(enumerator, &host))
195 {
196 child_cfg->add_traffic_selector(child_cfg, TRUE,
197 traffic_selector_create_from_subnet(host->clone(host),
198 32, 0, 0, 65535));
199 }
200 enumerator->destroy(enumerator);
201 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
202
203 charon->shunts->install(charon->shunts, child_cfg);
204 child_cfg->destroy(child_cfg);
205
206 DBG1(DBG_IKE, "installed %N bypass policy for %R",
207 configuration_attribute_type_names, UNITY_LOCAL_LAN, entry->ts);
208 }
209 return JOB_REQUEUE_NONE;
210 }
211
212 /**
213 * Add a bypass policy for a given subnet
214 */
215 static bool add_exclude(private_unity_handler_t *this, chunk_t subnet)
216 {
217 traffic_selector_t *ts;
218 ike_sa_t *ike_sa;
219 entry_t *entry;
220
221 ike_sa = charon->bus->get_sa(charon->bus);
222 if (!ike_sa)
223 {
224 return FALSE;
225 }
226 ts = create_ts(subnet);
227 if (!ts)
228 {
229 return FALSE;
230 }
231 INIT(entry,
232 .sa = ike_sa->get_unique_id(ike_sa),
233 .ts = ts,
234 );
235
236 /* we can't install the shunt policy yet, as we don't know the virtual IP.
237 * Defer installation using an async callback. */
238 lib->processor->queue_job(lib->processor, (job_t*)
239 callback_job_create((void*)add_exclude_async, entry,
240 (void*)entry_destroy, NULL));
241 return TRUE;
242 }
243
244 /**
245 * Remove a bypass policy for a given subnet
246 */
247 static bool remove_exclude(private_unity_handler_t *this, chunk_t subnet)
248 {
249 traffic_selector_t *ts;
250 ike_sa_t *ike_sa;
251 char name[128];
252
253 ike_sa = charon->bus->get_sa(charon->bus);
254 if (!ike_sa)
255 {
256 return FALSE;
257 }
258 ts = create_ts(subnet);
259 if (!ts)
260 {
261 return FALSE;
262 }
263 create_shunt_name(ike_sa, ts, name, sizeof(name));
264 DBG1(DBG_IKE, "uninstalling %N bypass policy for %R",
265 configuration_attribute_type_names, UNITY_LOCAL_LAN, ts);
266 ts->destroy(ts);
267 return charon->shunts->uninstall(charon->shunts, name);
268 }
269
270 METHOD(attribute_handler_t, handle, bool,
271 private_unity_handler_t *this, identification_t *id,
272 configuration_attribute_type_t type, chunk_t data)
273 {
274 switch (type)
275 {
276 case UNITY_SPLIT_INCLUDE:
277 return add_include(this, data);
278 case UNITY_LOCAL_LAN:
279 return add_exclude(this, data);
280 default:
281 return FALSE;
282 }
283 }
284
285 METHOD(attribute_handler_t, release, void,
286 private_unity_handler_t *this, identification_t *server,
287 configuration_attribute_type_t type, chunk_t data)
288 {
289 switch (type)
290 {
291 case UNITY_SPLIT_INCLUDE:
292 remove_include(this, data);
293 break;
294 case UNITY_LOCAL_LAN:
295 remove_exclude(this, data);
296 break;
297 default:
298 break;
299 }
300 }
301
302 /**
303 * Configuration attributes to request
304 */
305 static configuration_attribute_type_t attributes[] = {
306 UNITY_SPLIT_INCLUDE,
307 UNITY_LOCAL_LAN,
308 };
309
310 /**
311 * Attribute enumerator implementation
312 */
313 typedef struct {
314 /** implements enumerator_t */
315 enumerator_t public;
316 /** position in attributes[] */
317 int i;
318 } attribute_enumerator_t;
319
320 METHOD(enumerator_t, enumerate_attributes, bool,
321 attribute_enumerator_t *this, configuration_attribute_type_t *type,
322 chunk_t *data)
323 {
324 if (this->i < countof(attributes))
325 {
326 *type = attributes[this->i++];
327 *data = chunk_empty;
328 return TRUE;
329 }
330 return FALSE;
331 }
332
333 METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
334 unity_handler_t *this, identification_t *id, linked_list_t *vips)
335 {
336 attribute_enumerator_t *enumerator;
337 ike_sa_t *ike_sa;
338
339 ike_sa = charon->bus->get_sa(charon->bus);
340 if (!ike_sa || ike_sa->get_version(ike_sa) != IKEV1 ||
341 !ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
342 {
343 return enumerator_create_empty();
344 }
345 INIT(enumerator,
346 .public = {
347 .enumerate = (void*)_enumerate_attributes,
348 .destroy = (void*)free,
349 },
350 );
351 return &enumerator->public;
352 }
353
354 typedef struct {
355 /** mutex to unlock */
356 mutex_t *mutex;
357 /** IKE_SA ID to filter for */
358 u_int32_t id;
359 } include_filter_t;
360
361 /**
362 * Include enumerator filter function
363 */
364 static bool include_filter(include_filter_t *data,
365 entry_t **entry, traffic_selector_t **ts)
366 {
367 if ((*entry)->sa == data->id)
368 {
369 *ts = (*entry)->ts;
370 return TRUE;
371 }
372 return FALSE;
373 }
374
375 /**
376 * Destroy include filter data, unlock mutex
377 */
378 static void destroy_filter(include_filter_t *data)
379 {
380 data->mutex->unlock(data->mutex);
381 free(data);
382 }
383
384 METHOD(unity_handler_t, create_include_enumerator, enumerator_t*,
385 private_unity_handler_t *this, u_int32_t id)
386 {
387 include_filter_t *data;
388
389 INIT(data,
390 .mutex = this->mutex,
391 .id = id,
392 );
393 data->mutex->lock(data->mutex);
394 return enumerator_create_filter(
395 this->include->create_enumerator(this->include),
396 (void*)include_filter, data, (void*)destroy_filter);
397 }
398
399 METHOD(unity_handler_t, destroy, void,
400 private_unity_handler_t *this)
401 {
402 this->include->destroy(this->include);
403 this->mutex->destroy(this->mutex);
404 free(this);
405 }
406
407 /**
408 * See header
409 */
410 unity_handler_t *unity_handler_create()
411 {
412 private_unity_handler_t *this;
413
414 INIT(this,
415 .public = {
416 .handler = {
417 .handle = _handle,
418 .release = _release,
419 .create_attribute_enumerator = _create_attribute_enumerator,
420 },
421 .create_include_enumerator = _create_include_enumerator,
422 .destroy = _destroy,
423 },
424 .include = linked_list_create(),
425 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
426 );
427
428 return &this->public;
429 }