Add Cisco Unity client support for Split-Include and Local-LAN
[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 <utils/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 * Rempve 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 = {};
174 ike_sa_t *ike_sa;
175 char name[128];
176 host_t *host;
177 bool has_vip = FALSE;
178
179 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
180 entry->sa, FALSE);
181 if (ike_sa)
182 {
183 create_shunt_name(ike_sa, entry->ts, name, sizeof(name));
184
185 child_cfg = child_cfg_create(name, &lft, NULL, TRUE, MODE_PASS,
186 ACTION_NONE, ACTION_NONE, ACTION_NONE,
187 FALSE, 0, 0, NULL, NULL, FALSE);
188 child_cfg->add_traffic_selector(child_cfg, FALSE,
189 entry->ts->clone(entry->ts));
190 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
191 while (enumerator->enumerate(enumerator, &host))
192 {
193 has_vip = TRUE;
194 child_cfg->add_traffic_selector(child_cfg, TRUE,
195 traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0));
196 }
197 enumerator->destroy(enumerator);
198
199 if (!has_vip)
200 {
201 host = ike_sa->get_my_host(ike_sa);
202 child_cfg->add_traffic_selector(child_cfg, TRUE,
203 traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0));
204 }
205 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
206
207 charon->shunts->install(charon->shunts, child_cfg);
208 child_cfg->destroy(child_cfg);
209
210 DBG1(DBG_IKE, "installed %N bypass policy for %R",
211 configuration_attribute_type_names, UNITY_LOCAL_LAN, entry->ts);
212 }
213 return JOB_REQUEUE_NONE;
214 }
215
216 /**
217 * Add a bypass policy for a given subnet
218 */
219 static bool add_exclude(private_unity_handler_t *this, chunk_t subnet)
220 {
221 traffic_selector_t *ts;
222 ike_sa_t *ike_sa;
223 entry_t *entry;
224
225 ike_sa = charon->bus->get_sa(charon->bus);
226 if (!ike_sa)
227 {
228 return FALSE;
229 }
230 ts = create_ts(subnet);
231 if (!ts)
232 {
233 return FALSE;
234 }
235 INIT(entry,
236 .sa = ike_sa->get_unique_id(ike_sa),
237 .ts = ts,
238 );
239
240 /* we can't install the shunt policy yet, as we don't know the virtual IP.
241 * Defer installation using an async callback. */
242 lib->processor->queue_job(lib->processor, (job_t*)
243 callback_job_create((void*)add_exclude_async, entry,
244 (void*)entry_destroy, NULL));
245 return TRUE;
246 }
247
248 /**
249 * Remove a bypass policy for a given subnet
250 */
251 static bool remove_exclude(private_unity_handler_t *this, chunk_t subnet)
252 {
253 traffic_selector_t *ts;
254 ike_sa_t *ike_sa;
255 char name[128];
256
257 ike_sa = charon->bus->get_sa(charon->bus);
258 if (!ike_sa)
259 {
260 return FALSE;
261 }
262 ts = create_ts(subnet);
263 if (!ts)
264 {
265 return FALSE;
266 }
267 create_shunt_name(ike_sa, ts, name, sizeof(name));
268 DBG1(DBG_IKE, "uninstalling %N bypass policy for %R",
269 configuration_attribute_type_names, UNITY_LOCAL_LAN, ts);
270 ts->destroy(ts);
271 return charon->shunts->uninstall(charon->shunts, name);
272 }
273
274 METHOD(attribute_handler_t, handle, bool,
275 private_unity_handler_t *this, identification_t *id,
276 configuration_attribute_type_t type, chunk_t data)
277 {
278 switch (type)
279 {
280 case UNITY_SPLIT_INCLUDE:
281 return add_include(this, data);
282 case UNITY_LOCAL_LAN:
283 return add_exclude(this, data);
284 default:
285 return FALSE;
286 }
287 }
288
289 METHOD(attribute_handler_t, release, void,
290 private_unity_handler_t *this, identification_t *server,
291 configuration_attribute_type_t type, chunk_t data)
292 {
293 switch (type)
294 {
295 case UNITY_SPLIT_INCLUDE:
296 remove_include(this, data);
297 break;
298 case UNITY_LOCAL_LAN:
299 remove_exclude(this, data);
300 break;
301 default:
302 break;
303 }
304 }
305
306 /**
307 * Configuration attributes to request
308 */
309 static configuration_attribute_type_t attributes[] = {
310 UNITY_SPLIT_INCLUDE,
311 UNITY_LOCAL_LAN,
312 };
313
314 /**
315 * Attribute enumerator implementation
316 */
317 typedef struct {
318 /** implements enumerator_t */
319 enumerator_t public;
320 /** position in attributes[] */
321 int i;
322 } attribute_enumerator_t;
323
324 METHOD(enumerator_t, enumerate_attributes, bool,
325 attribute_enumerator_t *this, configuration_attribute_type_t *type,
326 chunk_t *data)
327 {
328 if (this->i < countof(attributes))
329 {
330 *type = attributes[this->i++];
331 *data = chunk_empty;
332 return TRUE;
333 }
334 return FALSE;
335 }
336
337 METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
338 unity_handler_t *this, identification_t *id, linked_list_t *vips)
339 {
340 attribute_enumerator_t *enumerator;
341
342 INIT(enumerator,
343 .public = {
344 .enumerate = (void*)_enumerate_attributes,
345 .destroy = (void*)free,
346 },
347 );
348 return &enumerator->public;
349 }
350
351 typedef struct {
352 /** mutex to unlock */
353 mutex_t *mutex;
354 /** IKE_SA ID to filter for */
355 u_int32_t id;
356 } include_filter_t;
357
358 /**
359 * Include enumerator filter function
360 */
361 static bool include_filter(include_filter_t *data,
362 entry_t **entry, traffic_selector_t **ts)
363 {
364 if ((*entry)->sa == data->id)
365 {
366 *ts = (*entry)->ts;
367 return TRUE;
368 }
369 return FALSE;
370 }
371
372 /**
373 * Destroy include filter data, unlock mutex
374 */
375 static void destroy_filter(include_filter_t *data)
376 {
377 data->mutex->unlock(data->mutex);
378 free(data);
379 }
380
381 METHOD(unity_handler_t, create_include_enumerator, enumerator_t*,
382 private_unity_handler_t *this, u_int32_t id)
383 {
384 include_filter_t *data;
385
386 INIT(data,
387 .mutex = this->mutex,
388 .id = id,
389 );
390 data->mutex->lock(data->mutex);
391 return enumerator_create_filter(
392 this->include->create_enumerator(this->include),
393 (void*)include_filter, data, (void*)destroy_filter);
394 }
395
396 METHOD(unity_handler_t, destroy, void,
397 private_unity_handler_t *this)
398 {
399 this->include->destroy(this->include);
400 this->mutex->destroy(this->mutex);
401 free(this);
402 }
403
404 /**
405 * See header
406 */
407 unity_handler_t *unity_handler_create()
408 {
409 private_unity_handler_t *this;
410
411 INIT(this,
412 .public = {
413 .handler = {
414 .handle = _handle,
415 .release = _release,
416 .create_attribute_enumerator = _create_attribute_enumerator,
417 },
418 .create_include_enumerator = _create_include_enumerator,
419 .destroy = _destroy,
420 },
421 .include = linked_list_create(),
422 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
423 );
424
425 return &this->public;
426 }