]>
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. | |
49653b6b MW |
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 { | |
7daf5226 | 30 | |
49653b6b MW |
31 | /** |
32 | * Public updown_listener_t interface. | |
33 | */ | |
34 | updown_listener_t public; | |
7daf5226 | 35 | |
49653b6b MW |
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); | |
7daf5226 | 61 | |
49653b6b MW |
62 | entry->reqid = reqid; |
63 | entry->iface = strdup(iface); | |
7daf5226 | 64 | |
49653b6b MW |
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; | |
7daf5226 | 76 | |
49653b6b MW |
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 | ||
7481f964 MW |
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) | |
49653b6b MW |
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; | |
7daf5226 | 101 | |
49653b6b MW |
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); | |
7daf5226 | 107 | |
49653b6b MW |
108 | if (script == NULL) |
109 | { | |
7481f964 | 110 | return TRUE; |
49653b6b | 111 | } |
7daf5226 | 112 | |
49653b6b MW |
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; | |
14665981 AS |
118 | char *pos, *virtual_ip, *iface, *mark_in, *mark_out; |
119 | mark_t mark; | |
9789d3a9 | 120 | bool is_host, is_ipv6; |
49653b6b MW |
121 | FILE *shell; |
122 | ||
123 | /* get subnet/bits from string */ | |
124 | if (asprintf(&my_client, "%R", my_ts) < 0) | |
125 | { | |
126 | my_client = NULL; | |
127 | } | |
128 | pos = strchr(my_client, '/'); | |
129 | *pos = '\0'; | |
130 | my_client_mask = pos + 1; | |
131 | pos = strchr(my_client_mask, '['); | |
132 | if (pos) | |
133 | { | |
134 | *pos = '\0'; | |
135 | } | |
136 | if (asprintf(&other_client, "%R", other_ts) < 0) | |
137 | { | |
138 | other_client = NULL; | |
139 | } | |
140 | pos = strchr(other_client, '/'); | |
141 | *pos = '\0'; | |
142 | other_client_mask = pos + 1; | |
143 | pos = strchr(other_client_mask, '['); | |
144 | if (pos) | |
145 | { | |
146 | *pos = '\0'; | |
147 | } | |
148 | ||
149 | if (vip) | |
150 | { | |
151 | if (asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip) < 0) | |
152 | { | |
153 | virtual_ip = NULL; | |
154 | } | |
155 | } | |
156 | else | |
157 | { | |
158 | if (asprintf(&virtual_ip, "") < 0) | |
159 | { | |
160 | virtual_ip = NULL; | |
161 | } | |
162 | } | |
7daf5226 | 163 | |
14665981 AS |
164 | /* check for the presence of an inbound mark */ |
165 | mark = config->get_mark(config, TRUE); | |
166 | if (mark.value) | |
167 | { | |
168 | if (asprintf(&mark_in, "PLUTO_MARK_IN='%u/0x%08x' ", | |
169 | mark.value, mark.mask ) < 0) | |
170 | { | |
171 | mark_in = NULL; | |
172 | } | |
173 | } | |
174 | else | |
175 | { | |
176 | if (asprintf(&mark_in, "") < 0) | |
177 | { | |
178 | mark_in = NULL; | |
179 | } | |
180 | } | |
181 | ||
182 | /* check for the presence of an outbound mark */ | |
183 | mark = config->get_mark(config, FALSE); | |
184 | if (mark.value) | |
185 | { | |
186 | if (asprintf(&mark_out, "PLUTO_MARK_OUT='%u/0x%08x' ", | |
187 | mark.value, mark.mask ) < 0) | |
188 | { | |
189 | mark_out = NULL; | |
190 | } | |
191 | } | |
192 | else | |
193 | { | |
194 | if (asprintf(&mark_out, "") < 0) | |
195 | { | |
196 | mark_out = NULL; | |
197 | } | |
198 | } | |
199 | ||
49653b6b MW |
200 | if (up) |
201 | { | |
202 | iface = charon->kernel_interface->get_interface( | |
203 | charon->kernel_interface, me); | |
204 | if (iface) | |
205 | { | |
206 | cache_iface(this, child_sa->get_reqid(child_sa), iface); | |
207 | } | |
208 | } | |
209 | else | |
210 | { | |
211 | iface = uncache_iface(this, child_sa->get_reqid(child_sa)); | |
212 | } | |
7daf5226 | 213 | |
9789d3a9 AS |
214 | /* determine IPv4/IPv6 and client/host situation */ |
215 | is_host = my_ts->is_host(my_ts, me); | |
216 | is_ipv6 = is_host ? (me->get_family(me) == AF_INET6) : | |
217 | (my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE); | |
218 | ||
49653b6b MW |
219 | /* build the command with all env variables. |
220 | * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing | |
221 | */ | |
222 | snprintf(command, sizeof(command), | |
223 | "2>&1 " | |
224 | "PLUTO_VERSION='1.1' " | |
225 | "PLUTO_VERB='%s%s%s' " | |
226 | "PLUTO_CONNECTION='%s' " | |
227 | "PLUTO_INTERFACE='%s' " | |
228 | "PLUTO_REQID='%u' " | |
229 | "PLUTO_ME='%H' " | |
d24a74c5 | 230 | "PLUTO_MY_ID='%Y' " |
49653b6b MW |
231 | "PLUTO_MY_CLIENT='%s/%s' " |
232 | "PLUTO_MY_CLIENT_NET='%s' " | |
233 | "PLUTO_MY_CLIENT_MASK='%s' " | |
234 | "PLUTO_MY_PORT='%u' " | |
235 | "PLUTO_MY_PROTOCOL='%u' " | |
236 | "PLUTO_PEER='%H' " | |
d24a74c5 | 237 | "PLUTO_PEER_ID='%Y' " |
49653b6b MW |
238 | "PLUTO_PEER_CLIENT='%s/%s' " |
239 | "PLUTO_PEER_CLIENT_NET='%s' " | |
240 | "PLUTO_PEER_CLIENT_MASK='%s' " | |
241 | "PLUTO_PEER_PORT='%u' " | |
242 | "PLUTO_PEER_PROTOCOL='%u' " | |
243 | "%s" | |
244 | "%s" | |
14665981 AS |
245 | "%s" |
246 | "%s" | |
49653b6b MW |
247 | "%s", |
248 | up ? "up" : "down", | |
9789d3a9 AS |
249 | is_host ? "-host" : "-client", |
250 | is_ipv6 ? "-v6" : "", | |
49653b6b MW |
251 | config->get_name(config), |
252 | iface ? iface : "unknown", | |
253 | child_sa->get_reqid(child_sa), | |
254 | me, ike_sa->get_my_id(ike_sa), | |
255 | my_client, my_client_mask, | |
256 | my_client, my_client_mask, | |
257 | my_ts->get_from_port(my_ts), | |
258 | my_ts->get_protocol(my_ts), | |
259 | other, ike_sa->get_other_id(ike_sa), | |
260 | other_client, other_client_mask, | |
261 | other_client, other_client_mask, | |
262 | other_ts->get_from_port(other_ts), | |
263 | other_ts->get_protocol(other_ts), | |
264 | virtual_ip, | |
14665981 AS |
265 | mark_in, |
266 | mark_out, | |
49653b6b MW |
267 | config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "", |
268 | script); | |
269 | free(my_client); | |
270 | free(other_client); | |
271 | free(virtual_ip); | |
14665981 AS |
272 | free(mark_in); |
273 | free(mark_out); | |
49653b6b | 274 | free(iface); |
7daf5226 | 275 | |
49653b6b MW |
276 | DBG3(DBG_CHD, "running updown script: %s", command); |
277 | shell = popen(command, "r"); | |
278 | ||
279 | if (shell == NULL) | |
280 | { | |
281 | DBG1(DBG_CHD, "could not execute updown script '%s'", script); | |
7481f964 | 282 | return TRUE; |
49653b6b | 283 | } |
7daf5226 | 284 | |
49653b6b MW |
285 | while (TRUE) |
286 | { | |
287 | char resp[128]; | |
7daf5226 | 288 | |
49653b6b MW |
289 | if (fgets(resp, sizeof(resp), shell) == NULL) |
290 | { | |
291 | if (ferror(shell)) | |
292 | { | |
293 | DBG1(DBG_CHD, "error reading output from updown script"); | |
49653b6b | 294 | } |
7481f964 | 295 | break; |
49653b6b MW |
296 | } |
297 | else | |
298 | { | |
299 | char *e = resp + strlen(resp); | |
300 | if (e > resp && e[-1] == '\n') | |
301 | { /* trim trailing '\n' */ | |
302 | e[-1] = '\0'; | |
303 | } | |
304 | DBG1(DBG_CHD, "updown: %s", resp); | |
305 | } | |
306 | } | |
307 | pclose(shell); | |
308 | } | |
309 | enumerator->destroy(enumerator); | |
49653b6b MW |
310 | return TRUE; |
311 | } | |
312 | ||
7481f964 MW |
313 | METHOD(updown_listener_t, destroy, void, |
314 | private_updown_listener_t *this) | |
49653b6b MW |
315 | { |
316 | this->iface_cache->destroy(this->iface_cache); | |
317 | free(this); | |
318 | } | |
319 | ||
320 | /** | |
321 | * See header | |
322 | */ | |
323 | updown_listener_t *updown_listener_create() | |
324 | { | |
7481f964 MW |
325 | private_updown_listener_t *this; |
326 | ||
327 | INIT(this, | |
328 | .public = { | |
6f52d3b0 TB |
329 | .listener = { |
330 | .child_updown = _child_updown, | |
331 | }, | |
7481f964 MW |
332 | .destroy = _destroy, |
333 | }, | |
334 | .iface_cache = linked_list_create(), | |
335 | ); | |
7daf5226 | 336 | |
49653b6b MW |
337 | return &this->public; |
338 | } | |
339 |