Moving charon to libcharon.
[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 <daemon.h>
22 #include <config/child_cfg.h>
23
24 typedef struct private_updown_listener_t private_updown_listener_t;
25
26 /**
27 * Private data of an updown_listener_t object.
28 */
29 struct private_updown_listener_t {
30
31 /**
32 * Public updown_listener_t interface.
33 */
34 updown_listener_t public;
35
36 /**
37 * List of cached interface names
38 */
39 linked_list_t *iface_cache;
40 };
41
42 typedef struct cache_entry_t cache_entry_t;
43
44 /**
45 * Cache line in the interface name cache.
46 */
47 struct cache_entry_t {
48 /** requid of the CHILD_SA */
49 u_int32_t reqid;
50 /** cached interface name */
51 char *iface;
52 };
53
54 /**
55 * Insert an interface name to the cache
56 */
57 static void cache_iface(private_updown_listener_t *this, u_int32_t reqid,
58 char *iface)
59 {
60 cache_entry_t *entry = malloc_thing(cache_entry_t);
61
62 entry->reqid = reqid;
63 entry->iface = strdup(iface);
64
65 this->iface_cache->insert_first(this->iface_cache, entry);
66 }
67
68 /**
69 * Remove a cached interface name and return it.
70 */
71 static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid)
72 {
73 enumerator_t *enumerator;
74 cache_entry_t *entry;
75 char *iface = NULL;
76
77 enumerator = this->iface_cache->create_enumerator(this->iface_cache);
78 while (enumerator->enumerate(enumerator, &entry))
79 {
80 if (entry->reqid == reqid)
81 {
82 this->iface_cache->remove_at(this->iface_cache, enumerator);
83 iface = entry->iface;
84 free(entry);
85 break;
86 }
87 }
88 enumerator->destroy(enumerator);
89 return iface;
90 }
91
92 METHOD(listener_t, child_updown, bool,
93 private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
94 bool up)
95 {
96 traffic_selector_t *my_ts, *other_ts;
97 enumerator_t *enumerator;
98 child_cfg_t *config;
99 host_t *vip, *me, *other;
100 char *script;
101
102 config = child_sa->get_config(child_sa);
103 vip = ike_sa->get_virtual_ip(ike_sa, TRUE);
104 script = config->get_updown(config);
105 me = ike_sa->get_my_host(ike_sa);
106 other = ike_sa->get_other_host(ike_sa);
107
108 if (script == NULL)
109 {
110 return TRUE;
111 }
112
113 enumerator = child_sa->create_policy_enumerator(child_sa);
114 while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
115 {
116 char command[1024];
117 char *my_client, *other_client, *my_client_mask, *other_client_mask;
118 char *pos, *virtual_ip, *iface;
119 bool is_host, is_ipv6;
120 FILE *shell;
121
122 /* get subnet/bits from string */
123 if (asprintf(&my_client, "%R", my_ts) < 0)
124 {
125 my_client = NULL;
126 }
127 pos = strchr(my_client, '/');
128 *pos = '\0';
129 my_client_mask = pos + 1;
130 pos = strchr(my_client_mask, '[');
131 if (pos)
132 {
133 *pos = '\0';
134 }
135 if (asprintf(&other_client, "%R", other_ts) < 0)
136 {
137 other_client = NULL;
138 }
139 pos = strchr(other_client, '/');
140 *pos = '\0';
141 other_client_mask = pos + 1;
142 pos = strchr(other_client_mask, '[');
143 if (pos)
144 {
145 *pos = '\0';
146 }
147
148 if (vip)
149 {
150 if (asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip) < 0)
151 {
152 virtual_ip = NULL;
153 }
154 }
155 else
156 {
157 if (asprintf(&virtual_ip, "") < 0)
158 {
159 virtual_ip = NULL;
160 }
161 }
162
163 if (up)
164 {
165 iface = charon->kernel_interface->get_interface(
166 charon->kernel_interface, me);
167 if (iface)
168 {
169 cache_iface(this, child_sa->get_reqid(child_sa), iface);
170 }
171 }
172 else
173 {
174 iface = uncache_iface(this, child_sa->get_reqid(child_sa));
175 }
176
177 /* determine IPv4/IPv6 and client/host situation */
178 is_host = my_ts->is_host(my_ts, me);
179 is_ipv6 = is_host ? (me->get_family(me) == AF_INET6) :
180 (my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE);
181
182 /* build the command with all env variables.
183 * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
184 */
185 snprintf(command, sizeof(command),
186 "2>&1 "
187 "PLUTO_VERSION='1.1' "
188 "PLUTO_VERB='%s%s%s' "
189 "PLUTO_CONNECTION='%s' "
190 "PLUTO_INTERFACE='%s' "
191 "PLUTO_REQID='%u' "
192 "PLUTO_ME='%H' "
193 "PLUTO_MY_ID='%Y' "
194 "PLUTO_MY_CLIENT='%s/%s' "
195 "PLUTO_MY_CLIENT_NET='%s' "
196 "PLUTO_MY_CLIENT_MASK='%s' "
197 "PLUTO_MY_PORT='%u' "
198 "PLUTO_MY_PROTOCOL='%u' "
199 "PLUTO_PEER='%H' "
200 "PLUTO_PEER_ID='%Y' "
201 "PLUTO_PEER_CLIENT='%s/%s' "
202 "PLUTO_PEER_CLIENT_NET='%s' "
203 "PLUTO_PEER_CLIENT_MASK='%s' "
204 "PLUTO_PEER_PORT='%u' "
205 "PLUTO_PEER_PROTOCOL='%u' "
206 "%s"
207 "%s"
208 "%s",
209 up ? "up" : "down",
210 is_host ? "-host" : "-client",
211 is_ipv6 ? "-v6" : "",
212 config->get_name(config),
213 iface ? iface : "unknown",
214 child_sa->get_reqid(child_sa),
215 me, ike_sa->get_my_id(ike_sa),
216 my_client, my_client_mask,
217 my_client, my_client_mask,
218 my_ts->get_from_port(my_ts),
219 my_ts->get_protocol(my_ts),
220 other, ike_sa->get_other_id(ike_sa),
221 other_client, other_client_mask,
222 other_client, other_client_mask,
223 other_ts->get_from_port(other_ts),
224 other_ts->get_protocol(other_ts),
225 virtual_ip,
226 config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
227 script);
228 free(my_client);
229 free(other_client);
230 free(virtual_ip);
231 free(iface);
232
233 DBG3(DBG_CHD, "running updown script: %s", command);
234 shell = popen(command, "r");
235
236 if (shell == NULL)
237 {
238 DBG1(DBG_CHD, "could not execute updown script '%s'", script);
239 return TRUE;
240 }
241
242 while (TRUE)
243 {
244 char resp[128];
245
246 if (fgets(resp, sizeof(resp), shell) == NULL)
247 {
248 if (ferror(shell))
249 {
250 DBG1(DBG_CHD, "error reading output from updown script");
251 }
252 break;
253 }
254 else
255 {
256 char *e = resp + strlen(resp);
257 if (e > resp && e[-1] == '\n')
258 { /* trim trailing '\n' */
259 e[-1] = '\0';
260 }
261 DBG1(DBG_CHD, "updown: %s", resp);
262 }
263 }
264 pclose(shell);
265 }
266 enumerator->destroy(enumerator);
267 return TRUE;
268 }
269
270 METHOD(updown_listener_t, destroy, void,
271 private_updown_listener_t *this)
272 {
273 this->iface_cache->destroy(this->iface_cache);
274 free(this);
275 }
276
277 /**
278 * See header
279 */
280 updown_listener_t *updown_listener_create()
281 {
282 private_updown_listener_t *this;
283
284 INIT(this,
285 .public = {
286 .listener.child_updown = _child_updown,
287 .destroy = _destroy,
288 },
289 .iface_cache = linked_list_create(),
290 );
291
292 return &this->public;
293 }
294