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