2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
19 #include "updown_listener.h"
23 #include <config/child_cfg.h>
25 typedef struct private_updown_listener_t private_updown_listener_t
;
28 * Private data of an updown_listener_t object.
30 struct private_updown_listener_t
{
33 * Public updown_listener_t interface.
35 updown_listener_t
public;
38 * List of cached interface names
40 linked_list_t
*iface_cache
;
43 * DNS attribute handler
45 updown_handler_t
*handler
;
48 typedef struct cache_entry_t cache_entry_t
;
51 * Cache line in the interface name cache.
53 struct cache_entry_t
{
54 /** requid of the CHILD_SA */
56 /** cached interface name */
61 * Insert an interface name to the cache
63 static void cache_iface(private_updown_listener_t
*this, u_int32_t reqid
,
66 cache_entry_t
*entry
= malloc_thing(cache_entry_t
);
69 entry
->iface
= strdup(iface
);
71 this->iface_cache
->insert_first(this->iface_cache
, entry
);
75 * Remove a cached interface name and return it.
77 static char* uncache_iface(private_updown_listener_t
*this, u_int32_t reqid
)
79 enumerator_t
*enumerator
;
83 enumerator
= this->iface_cache
->create_enumerator(this->iface_cache
);
84 while (enumerator
->enumerate(enumerator
, &entry
))
86 if (entry
->reqid
== reqid
)
88 this->iface_cache
->remove_at(this->iface_cache
, enumerator
);
94 enumerator
->destroy(enumerator
);
99 * Create variables for handled DNS attributes
101 static char *make_dns_vars(private_updown_listener_t
*this, ike_sa_t
*ike_sa
)
103 enumerator_t
*enumerator
;
106 char total
[512] = "", current
[64];
113 enumerator
= this->handler
->create_dns_enumerator(this->handler
,
114 ike_sa
->get_unique_id(ike_sa
));
115 while (enumerator
->enumerate(enumerator
, &host
))
117 switch (host
->get_family(host
))
120 snprintf(current
, sizeof(current
),
121 "PLUTO_DNS4_%d='%H' ", ++v4
, host
);
124 snprintf(current
, sizeof(current
),
125 "PLUTO_DNS6_%d='%H' ", ++v6
, host
);
130 strncat(total
, current
, sizeof(total
) - strlen(total
) - 1);
132 enumerator
->destroy(enumerator
);
134 return strdup(total
);
138 * Create variables for local virtual IPs
140 static char *make_vip_vars(private_updown_listener_t
*this, ike_sa_t
*ike_sa
)
142 enumerator_t
*enumerator
;
145 char total
[512] = "", current
[64];
148 enumerator
= ike_sa
->create_virtual_ip_enumerator(ike_sa
, TRUE
);
149 while (enumerator
->enumerate(enumerator
, &host
))
152 { /* legacy variable for first VIP */
153 snprintf(current
, sizeof(current
),
154 "PLUTO_MY_SOURCEIP='%H' ", host
);
155 strncat(total
, current
, sizeof(total
) - strlen(total
) - 1);
157 switch (host
->get_family(host
))
160 snprintf(current
, sizeof(current
),
161 "PLUTO_MY_SOURCEIP4_%d='%H' ", ++v4
, host
);
164 snprintf(current
, sizeof(current
),
165 "PLUTO_MY_SOURCEIP6_%d='%H' ", ++v6
, host
);
170 strncat(total
, current
, sizeof(total
) - strlen(total
) - 1);
172 enumerator
->destroy(enumerator
);
174 return strdup(total
);
177 METHOD(listener_t
, child_updown
, bool,
178 private_updown_listener_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
181 traffic_selector_t
*my_ts
, *other_ts
;
182 enumerator_t
*enumerator
;
187 config
= child_sa
->get_config(child_sa
);
188 script
= config
->get_updown(config
);
189 me
= ike_sa
->get_my_host(ike_sa
);
190 other
= ike_sa
->get_other_host(ike_sa
);
197 enumerator
= child_sa
->create_policy_enumerator(child_sa
);
198 while (enumerator
->enumerate(enumerator
, &my_ts
, &other_ts
))
201 host_t
*my_client
, *other_client
;
202 u_int8_t my_client_mask
, other_client_mask
;
203 char *virtual_ip
, *iface
, *mark_in
, *mark_out
, *udp_enc
, *dns
;
205 bool is_host
, is_ipv6
;
208 my_ts
->to_subnet(my_ts
, &my_client
, &my_client_mask
);
209 other_ts
->to_subnet(other_ts
, &other_client
, &other_client_mask
);
211 virtual_ip
= make_vip_vars(this, ike_sa
);
213 /* check for the presence of an inbound mark */
214 mark
= config
->get_mark(config
, TRUE
);
217 if (asprintf(&mark_in
, "PLUTO_MARK_IN='%u/0x%08x' ",
218 mark
.value
, mark
.mask
) < 0)
225 if (asprintf(&mark_in
, "") < 0)
231 /* check for the presence of an outbound mark */
232 mark
= config
->get_mark(config
, FALSE
);
235 if (asprintf(&mark_out
, "PLUTO_MARK_OUT='%u/0x%08x' ",
236 mark
.value
, mark
.mask
) < 0)
243 if (asprintf(&mark_out
, "") < 0)
249 /* check for a NAT condition causing ESP_IN_UDP encapsulation */
250 if (ike_sa
->has_condition(ike_sa
, COND_NAT_ANY
))
252 if (asprintf(&udp_enc
, "PLUTO_UDP_ENC='%u' ",
253 other
->get_port(other
)) < 0)
261 if (asprintf(&udp_enc
, "") < 0)
270 if (hydra
->kernel_interface
->get_interface(hydra
->kernel_interface
,
273 cache_iface(this, child_sa
->get_reqid(child_sa
), iface
);
278 iface
= uncache_iface(this, child_sa
->get_reqid(child_sa
));
281 dns
= make_dns_vars(this, ike_sa
);
283 /* determine IPv4/IPv6 and client/host situation */
284 is_host
= my_ts
->is_host(my_ts
, me
);
285 is_ipv6
= is_host ?
(me
->get_family(me
) == AF_INET6
) :
286 (my_ts
->get_type(my_ts
) == TS_IPV6_ADDR_RANGE
);
288 /* build the command with all env variables.
289 * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
291 snprintf(command
, sizeof(command
),
293 "PLUTO_VERSION='1.1' "
294 "PLUTO_VERB='%s%s%s' "
295 "PLUTO_CONNECTION='%s' "
296 "PLUTO_INTERFACE='%s' "
300 "PLUTO_MY_CLIENT='%H/%u' "
301 "PLUTO_MY_PORT='%u' "
302 "PLUTO_MY_PROTOCOL='%u' "
304 "PLUTO_PEER_ID='%Y' "
305 "PLUTO_PEER_CLIENT='%H/%u' "
306 "PLUTO_PEER_PORT='%u' "
307 "PLUTO_PEER_PROTOCOL='%u' "
316 is_host ?
"-host" : "-client",
317 is_ipv6 ?
"-v6" : "",
318 config
->get_name(config
),
319 iface ? iface
: "unknown",
320 child_sa
->get_reqid(child_sa
),
321 me
, ike_sa
->get_my_id(ike_sa
),
322 my_client
, my_client_mask
,
323 my_ts
->get_from_port(my_ts
),
324 my_ts
->get_protocol(my_ts
),
325 other
, ike_sa
->get_other_id(ike_sa
),
326 other_client
, other_client_mask
,
327 other_ts
->get_from_port(other_ts
),
328 other_ts
->get_protocol(other_ts
),
333 config
->get_hostaccess(config
) ?
"PLUTO_HOST_ACCESS='1' " : "",
336 my_client
->destroy(my_client
);
337 other_client
->destroy(other_client
);
345 DBG3(DBG_CHD
, "running updown script: %s", command
);
346 shell
= popen(command
, "r");
350 DBG1(DBG_CHD
, "could not execute updown script '%s'", script
);
358 if (fgets(resp
, sizeof(resp
), shell
) == NULL
)
362 DBG1(DBG_CHD
, "error reading output from updown script");
368 char *e
= resp
+ strlen(resp
);
369 if (e
> resp
&& e
[-1] == '\n')
370 { /* trim trailing '\n' */
373 DBG1(DBG_CHD
, "updown: %s", resp
);
378 enumerator
->destroy(enumerator
);
382 METHOD(updown_listener_t
, destroy
, void,
383 private_updown_listener_t
*this)
385 this->iface_cache
->destroy(this->iface_cache
);
392 updown_listener_t
*updown_listener_create(updown_handler_t
*handler
)
394 private_updown_listener_t
*this;
399 .child_updown
= _child_updown
,
403 .iface_cache
= linked_list_create(),
407 return &this->public;