944c46c6c4578dc18d298c1c4c5aac688dbc1532
[strongswan.git] / src / libcharon / plugins / updown / updown_listener.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * 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 #define _GNU_SOURCE
17 #include <stdio.h>
18
19 #include "updown_listener.h"
20
21 #include <hydra.h>
22 #include <daemon.h>
23 #include <config/child_cfg.h>
24
25 typedef struct private_updown_listener_t private_updown_listener_t;
26
27 /**
28 * Private data of an updown_listener_t object.
29 */
30 struct private_updown_listener_t {
31
32 /**
33 * Public updown_listener_t interface.
34 */
35 updown_listener_t public;
36
37 /**
38 * List of cached interface names
39 */
40 linked_list_t *iface_cache;
41
42 /**
43 * DNS attribute handler
44 */
45 updown_handler_t *handler;
46 };
47
48 typedef struct cache_entry_t cache_entry_t;
49
50 /**
51 * Cache line in the interface name cache.
52 */
53 struct cache_entry_t {
54 /** requid of the CHILD_SA */
55 u_int32_t reqid;
56 /** cached interface name */
57 char *iface;
58 };
59
60 /**
61 * Insert an interface name to the cache
62 */
63 static void cache_iface(private_updown_listener_t *this, u_int32_t reqid,
64 char *iface)
65 {
66 cache_entry_t *entry = malloc_thing(cache_entry_t);
67
68 entry->reqid = reqid;
69 entry->iface = strdup(iface);
70
71 this->iface_cache->insert_first(this->iface_cache, entry);
72 }
73
74 /**
75 * Remove a cached interface name and return it.
76 */
77 static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid)
78 {
79 enumerator_t *enumerator;
80 cache_entry_t *entry;
81 char *iface = NULL;
82
83 enumerator = this->iface_cache->create_enumerator(this->iface_cache);
84 while (enumerator->enumerate(enumerator, &entry))
85 {
86 if (entry->reqid == reqid)
87 {
88 this->iface_cache->remove_at(this->iface_cache, enumerator);
89 iface = entry->iface;
90 free(entry);
91 break;
92 }
93 }
94 enumerator->destroy(enumerator);
95 return iface;
96 }
97
98 /**
99 * Create variables for handled DNS attributes
100 */
101 static char *make_dns_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
102 {
103 enumerator_t *enumerator;
104 host_t *host;
105 int v4 = 0, v6 = 0;
106 char total[512] = "", current[64];
107
108 if (!this->handler)
109 {
110 return strdup("");
111 }
112
113 enumerator = this->handler->create_dns_enumerator(this->handler,
114 ike_sa->get_unique_id(ike_sa));
115 while (enumerator->enumerate(enumerator, &host))
116 {
117 switch (host->get_family(host))
118 {
119 case AF_INET:
120 snprintf(current, sizeof(current),
121 "PLUTO_DNS4_%d='%H' ", ++v4, host);
122 break;
123 case AF_INET6:
124 snprintf(current, sizeof(current),
125 "PLUTO_DNS6_%d='%H' ", ++v6, host);
126 break;
127 default:
128 continue;
129 }
130 strncat(total, current, sizeof(total) - strlen(total) - 1);
131 }
132 enumerator->destroy(enumerator);
133
134 return strdup(total);
135 }
136
137 METHOD(listener_t, child_updown, bool,
138 private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
139 bool up)
140 {
141 traffic_selector_t *my_ts, *other_ts;
142 enumerator_t *enumerator;
143 child_cfg_t *config;
144 host_t *vip, *me, *other;
145 char *script;
146
147 config = child_sa->get_config(child_sa);
148 vip = ike_sa->get_virtual_ip(ike_sa, TRUE);
149 script = config->get_updown(config);
150 me = ike_sa->get_my_host(ike_sa);
151 other = ike_sa->get_other_host(ike_sa);
152
153 if (script == NULL)
154 {
155 return TRUE;
156 }
157
158 enumerator = child_sa->create_policy_enumerator(child_sa);
159 while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
160 {
161 char command[1024];
162 host_t *my_client, *other_client;
163 u_int8_t my_client_mask, other_client_mask;
164 char *virtual_ip, *iface, *mark_in, *mark_out, *udp_enc, *dns;
165 mark_t mark;
166 bool is_host, is_ipv6;
167 FILE *shell;
168
169 my_ts->to_subnet(my_ts, &my_client, &my_client_mask);
170 other_ts->to_subnet(other_ts, &other_client, &other_client_mask);
171
172 if (vip)
173 {
174 if (asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip) < 0)
175 {
176 virtual_ip = NULL;
177 }
178 }
179 else
180 {
181 if (asprintf(&virtual_ip, "") < 0)
182 {
183 virtual_ip = NULL;
184 }
185 }
186
187 /* check for the presence of an inbound mark */
188 mark = config->get_mark(config, TRUE);
189 if (mark.value)
190 {
191 if (asprintf(&mark_in, "PLUTO_MARK_IN='%u/0x%08x' ",
192 mark.value, mark.mask ) < 0)
193 {
194 mark_in = NULL;
195 }
196 }
197 else
198 {
199 if (asprintf(&mark_in, "") < 0)
200 {
201 mark_in = NULL;
202 }
203 }
204
205 /* check for the presence of an outbound mark */
206 mark = config->get_mark(config, FALSE);
207 if (mark.value)
208 {
209 if (asprintf(&mark_out, "PLUTO_MARK_OUT='%u/0x%08x' ",
210 mark.value, mark.mask ) < 0)
211 {
212 mark_out = NULL;
213 }
214 }
215 else
216 {
217 if (asprintf(&mark_out, "") < 0)
218 {
219 mark_out = NULL;
220 }
221 }
222
223 /* check for a NAT condition causing ESP_IN_UDP encapsulation */
224 if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
225 {
226 if (asprintf(&udp_enc, "PLUTO_UDP_ENC='%u' ",
227 other->get_port(other)) < 0)
228 {
229 udp_enc = NULL;
230 }
231
232 }
233 else
234 {
235 if (asprintf(&udp_enc, "") < 0)
236 {
237 udp_enc = NULL;
238 }
239
240 }
241
242 if (up)
243 {
244 iface = hydra->kernel_interface->get_interface(
245 hydra->kernel_interface, me);
246 if (iface)
247 {
248 cache_iface(this, child_sa->get_reqid(child_sa), iface);
249 }
250 }
251 else
252 {
253 iface = uncache_iface(this, child_sa->get_reqid(child_sa));
254 }
255
256 dns = make_dns_vars(this, ike_sa);
257
258 /* determine IPv4/IPv6 and client/host situation */
259 is_host = my_ts->is_host(my_ts, me);
260 is_ipv6 = is_host ? (me->get_family(me) == AF_INET6) :
261 (my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE);
262
263 /* build the command with all env variables.
264 * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
265 */
266 snprintf(command, sizeof(command),
267 "2>&1 "
268 "PLUTO_VERSION='1.1' "
269 "PLUTO_VERB='%s%s%s' "
270 "PLUTO_CONNECTION='%s' "
271 "PLUTO_INTERFACE='%s' "
272 "PLUTO_REQID='%u' "
273 "PLUTO_ME='%H' "
274 "PLUTO_MY_ID='%Y' "
275 "PLUTO_MY_CLIENT='%H/%u' "
276 "PLUTO_MY_PORT='%u' "
277 "PLUTO_MY_PROTOCOL='%u' "
278 "PLUTO_PEER='%H' "
279 "PLUTO_PEER_ID='%Y' "
280 "PLUTO_PEER_CLIENT='%H/%u' "
281 "PLUTO_PEER_PORT='%u' "
282 "PLUTO_PEER_PROTOCOL='%u' "
283 "%s"
284 "%s"
285 "%s"
286 "%s"
287 "%s"
288 "%s"
289 "%s",
290 up ? "up" : "down",
291 is_host ? "-host" : "-client",
292 is_ipv6 ? "-v6" : "",
293 config->get_name(config),
294 iface ? iface : "unknown",
295 child_sa->get_reqid(child_sa),
296 me, ike_sa->get_my_id(ike_sa),
297 my_client, my_client_mask,
298 my_ts->get_from_port(my_ts),
299 my_ts->get_protocol(my_ts),
300 other, ike_sa->get_other_id(ike_sa),
301 other_client, other_client_mask,
302 other_ts->get_from_port(other_ts),
303 other_ts->get_protocol(other_ts),
304 virtual_ip,
305 mark_in,
306 mark_out,
307 udp_enc,
308 config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
309 dns,
310 script);
311 my_client->destroy(my_client);
312 other_client->destroy(other_client);
313 free(virtual_ip);
314 free(mark_in);
315 free(mark_out);
316 free(udp_enc);
317 free(dns);
318 free(iface);
319
320 DBG3(DBG_CHD, "running updown script: %s", command);
321 shell = popen(command, "r");
322
323 if (shell == NULL)
324 {
325 DBG1(DBG_CHD, "could not execute updown script '%s'", script);
326 return TRUE;
327 }
328
329 while (TRUE)
330 {
331 char resp[128];
332
333 if (fgets(resp, sizeof(resp), shell) == NULL)
334 {
335 if (ferror(shell))
336 {
337 DBG1(DBG_CHD, "error reading output from updown script");
338 }
339 break;
340 }
341 else
342 {
343 char *e = resp + strlen(resp);
344 if (e > resp && e[-1] == '\n')
345 { /* trim trailing '\n' */
346 e[-1] = '\0';
347 }
348 DBG1(DBG_CHD, "updown: %s", resp);
349 }
350 }
351 pclose(shell);
352 }
353 enumerator->destroy(enumerator);
354 return TRUE;
355 }
356
357 METHOD(updown_listener_t, destroy, void,
358 private_updown_listener_t *this)
359 {
360 this->iface_cache->destroy(this->iface_cache);
361 free(this);
362 }
363
364 /**
365 * See header
366 */
367 updown_listener_t *updown_listener_create(updown_handler_t *handler)
368 {
369 private_updown_listener_t *this;
370
371 INIT(this,
372 .public = {
373 .listener = {
374 .child_updown = _child_updown,
375 },
376 .destroy = _destroy,
377 },
378 .iface_cache = linked_list_create(),
379 .handler = handler,
380 );
381
382 return &this->public;
383 }
384