2 * Copyright (C) 2013 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Copyright (C) 2016 Andreas Steffen
5 * HSR Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 #include "updown_listener.h"
24 #include <utils/process.h>
26 #include <config/child_cfg.h>
28 typedef struct private_updown_listener_t private_updown_listener_t
;
31 * Private data of an updown_listener_t object.
33 struct private_updown_listener_t
{
36 * Public updown_listener_t interface.
38 updown_listener_t
public;
41 * List of cached interface names
43 linked_list_t
*iface_cache
;
46 * DNS attribute handler
48 updown_handler_t
*handler
;
51 typedef struct cache_entry_t cache_entry_t
;
54 * Cache line in the interface name cache.
56 struct cache_entry_t
{
57 /** requid of the CHILD_SA */
59 /** cached interface name */
64 * Insert an interface name to the cache
66 static void cache_iface(private_updown_listener_t
*this, uint32_t reqid
,
69 cache_entry_t
*entry
= malloc_thing(cache_entry_t
);
72 entry
->iface
= strdup(iface
);
74 this->iface_cache
->insert_first(this->iface_cache
, entry
);
78 * Remove a cached interface name and return it.
80 static char* uncache_iface(private_updown_listener_t
*this, uint32_t reqid
)
82 enumerator_t
*enumerator
;
86 enumerator
= this->iface_cache
->create_enumerator(this->iface_cache
);
87 while (enumerator
->enumerate(enumerator
, &entry
))
89 if (entry
->reqid
== reqid
)
91 this->iface_cache
->remove_at(this->iface_cache
, enumerator
);
97 enumerator
->destroy(enumerator
);
102 * Allocate and push a format string to the environment
104 static bool push_env(char *envp
[], u_int count
, char *fmt
, ...)
112 if (++i
+ 1 >= count
)
118 if (vasprintf(&str
, fmt
, args
) >= 0)
123 return envp
[i
] != NULL
;
127 * Free all allocated environment strings
129 static void free_env(char *envp
[])
133 for (i
= 0; envp
[i
]; i
++)
140 * Push variables for handled DNS attributes
142 static void push_dns_env(private_updown_listener_t
*this, ike_sa_t
*ike_sa
,
143 char *envp
[], u_int count
)
145 enumerator_t
*enumerator
;
151 enumerator
= this->handler
->create_dns_enumerator(this->handler
,
152 ike_sa
->get_unique_id(ike_sa
));
153 while (enumerator
->enumerate(enumerator
, &host
))
155 switch (host
->get_family(host
))
158 push_env(envp
, count
, "PLUTO_DNS4_%d=%H", ++v4
, host
);
161 push_env(envp
, count
, "PLUTO_DNS6_%d=%H", ++v6
, host
);
167 enumerator
->destroy(enumerator
);
172 * Push variables for local/remote virtual IPs
174 static void push_vip_env(private_updown_listener_t
*this, ike_sa_t
*ike_sa
,
175 char *envp
[], u_int count
, bool local
)
177 enumerator_t
*enumerator
;
182 enumerator
= ike_sa
->create_virtual_ip_enumerator(ike_sa
, local
);
183 while (enumerator
->enumerate(enumerator
, &host
))
186 { /* legacy variable for first VIP */
188 push_env(envp
, count
, "PLUTO_%s_SOURCEIP=%H",
189 local
? "MY" : "PEER", host
);
191 switch (host
->get_family(host
))
194 push_env(envp
, count
, "PLUTO_%s_SOURCEIP4_%d=%H",
195 local
? "MY" : "PEER", ++v4
, host
);
198 push_env(envp
, count
, "PLUTO_%s_SOURCEIP6_%d=%H",
199 local
? "MY" : "PEER", ++v6
, host
);
205 enumerator
->destroy(enumerator
);
208 #define PORT_BUF_LEN 12
211 * Determine proper values for port env variable
213 static char* get_port(traffic_selector_t
*me
, traffic_selector_t
*other
,
214 char *port_buf
, bool local
)
216 uint16_t port
, to
, from
;
218 switch (max(me
->get_protocol(me
), other
->get_protocol(other
)))
223 port
= max(me
->get_from_port(me
), other
->get_from_port(other
));
224 snprintf(port_buf
, PORT_BUF_LEN
, "%u",
225 local
? traffic_selector_icmp_type(port
)
226 : traffic_selector_icmp_code(port
));
232 from
= me
->get_from_port(me
);
233 to
= me
->get_to_port(me
);
237 from
= other
->get_from_port(other
);
238 to
= other
->get_to_port(other
);
240 if (from
== to
|| (from
== 0 && to
== 65535))
242 snprintf(port_buf
, PORT_BUF_LEN
, "%u", from
);
246 snprintf(port_buf
, PORT_BUF_LEN
, "%u:%u", from
, to
);
252 * Invoke the updown script once for given traffic selectors
254 static void invoke_once(private_updown_listener_t
*this, ike_sa_t
*ike_sa
,
255 child_sa_t
*child_sa
, child_cfg_t
*config
, bool up
,
256 traffic_selector_t
*my_ts
, traffic_selector_t
*other_ts
)
258 host_t
*me
, *other
, *host
;
263 bool is_host
, is_ipv6
;
267 char port_buf
[PORT_BUF_LEN
];
268 char *envp
[128] = {};
270 me
= ike_sa
->get_my_host(ike_sa
);
271 other
= ike_sa
->get_other_host(ike_sa
);
273 push_env(envp
, countof(envp
), "PATH=%s", getenv("PATH"));
274 push_env(envp
, countof(envp
), "PLUTO_VERSION=1.1");
275 is_host
= my_ts
->is_host(my_ts
, me
);
278 is_ipv6
= me
->get_family(me
) == AF_INET6
;
282 is_ipv6
= my_ts
->get_type(my_ts
) == TS_IPV6_ADDR_RANGE
;
284 push_env(envp
, countof(envp
), "PLUTO_VERB=%s%s%s",
286 is_host
? "-host" : "-client",
287 is_ipv6
? "-v6" : "");
288 push_env(envp
, countof(envp
), "PLUTO_CONNECTION=%s",
289 config
->get_name(config
));
292 if (charon
->kernel
->get_interface(charon
->kernel
, me
, &iface
))
294 cache_iface(this, child_sa
->get_reqid(child_sa
), iface
);
303 iface
= uncache_iface(this, child_sa
->get_reqid(child_sa
));
305 push_env(envp
, countof(envp
), "PLUTO_INTERFACE=%s",
306 iface
? iface
: "unknown");
307 push_env(envp
, countof(envp
), "PLUTO_REQID=%u",
308 child_sa
->get_reqid(child_sa
));
309 push_env(envp
, countof(envp
), "PLUTO_PROTO=%s",
310 child_sa
->get_protocol(child_sa
) == PROTO_ESP
? "esp" : "ah");
311 push_env(envp
, countof(envp
), "PLUTO_UNIQUEID=%u",
312 ike_sa
->get_unique_id(ike_sa
));
313 push_env(envp
, countof(envp
), "PLUTO_ME=%H", me
);
314 push_env(envp
, countof(envp
), "PLUTO_MY_ID=%Y", ike_sa
->get_my_id(ike_sa
));
315 if (!my_ts
->to_subnet(my_ts
, &host
, &mask
))
317 DBG1(DBG_CHD
, "updown approximates local TS %R "
318 "by next larger subnet", my_ts
);
320 push_env(envp
, countof(envp
), "PLUTO_MY_CLIENT=%+H/%u", host
, mask
);
322 push_env(envp
, countof(envp
), "PLUTO_MY_PORT=%s",
323 get_port(my_ts
, other_ts
, port_buf
, TRUE
));
324 push_env(envp
, countof(envp
), "PLUTO_MY_PROTOCOL=%u",
325 my_ts
->get_protocol(my_ts
));
326 push_env(envp
, countof(envp
), "PLUTO_PEER=%H", other
);
327 push_env(envp
, countof(envp
), "PLUTO_PEER_ID=%Y",
328 ike_sa
->get_other_id(ike_sa
));
329 if (!other_ts
->to_subnet(other_ts
, &host
, &mask
))
331 DBG1(DBG_CHD
, "updown approximates remote TS %R "
332 "by next larger subnet", other_ts
);
334 push_env(envp
, countof(envp
), "PLUTO_PEER_CLIENT=%+H/%u", host
, mask
);
336 push_env(envp
, countof(envp
), "PLUTO_PEER_PORT=%s",
337 get_port(my_ts
, other_ts
, port_buf
, FALSE
));
338 push_env(envp
, countof(envp
), "PLUTO_PEER_PROTOCOL=%u",
339 other_ts
->get_protocol(other_ts
));
340 if (ike_sa
->has_condition(ike_sa
, COND_EAP_AUTHENTICATED
) ||
341 ike_sa
->has_condition(ike_sa
, COND_XAUTH_AUTHENTICATED
))
343 push_env(envp
, countof(envp
), "PLUTO_XAUTH_ID=%Y",
344 ike_sa
->get_other_eap_id(ike_sa
));
346 push_vip_env(this, ike_sa
, envp
, countof(envp
), TRUE
);
347 push_vip_env(this, ike_sa
, envp
, countof(envp
), FALSE
);
348 mark
= child_sa
->get_mark(child_sa
, TRUE
);
351 push_env(envp
, countof(envp
), "PLUTO_MARK_IN=%u/0x%08x",
352 mark
.value
, mark
.mask
);
354 mark
= child_sa
->get_mark(child_sa
, FALSE
);
357 push_env(envp
, countof(envp
), "PLUTO_MARK_OUT=%u/0x%08x",
358 mark
.value
, mark
.mask
);
360 if_id
= child_sa
->get_if_id(child_sa
, TRUE
);
363 push_env(envp
, countof(envp
), "PLUTO_IF_ID_IN=%u", if_id
);
365 if_id
= child_sa
->get_if_id(child_sa
, FALSE
);
368 push_env(envp
, countof(envp
), "PLUTO_IF_ID_OUT=%u", if_id
);
370 if (ike_sa
->has_condition(ike_sa
, COND_NAT_ANY
))
372 push_env(envp
, countof(envp
), "PLUTO_UDP_ENC=%u",
373 other
->get_port(other
));
375 if (child_sa
->get_ipcomp(child_sa
) != IPCOMP_NONE
)
377 push_env(envp
, countof(envp
), "PLUTO_IPCOMP=1");
379 push_dns_env(this, ike_sa
, envp
, countof(envp
));
380 if (config
->has_option(config
, OPT_HOSTACCESS
))
382 push_env(envp
, countof(envp
), "PLUTO_HOST_ACCESS=1");
385 process
= process_start_shell(envp
, NULL
, &out
, NULL
, "2>&1 %s",
386 config
->get_updown(config
));
389 shell
= fdopen(out
, "r");
396 if (fgets(resp
, sizeof(resp
), shell
) == NULL
)
400 DBG1(DBG_CHD
, "error reading from updown script");
406 char *e
= resp
+ strlen(resp
);
407 if (e
> resp
&& e
[-1] == '\n')
411 DBG1(DBG_CHD
, "updown: %s", resp
);
420 process
->wait(process
, NULL
);
426 METHOD(listener_t
, child_updown
, bool,
427 private_updown_listener_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
430 traffic_selector_t
*my_ts
, *other_ts
;
431 enumerator_t
*enumerator
;
434 config
= child_sa
->get_config(child_sa
);
435 if (config
->get_updown(config
))
437 enumerator
= child_sa
->create_policy_enumerator(child_sa
);
438 while (enumerator
->enumerate(enumerator
, &my_ts
, &other_ts
))
440 invoke_once(this, ike_sa
, child_sa
, config
, up
, my_ts
, other_ts
);
442 enumerator
->destroy(enumerator
);
447 METHOD(updown_listener_t
, destroy
, void,
448 private_updown_listener_t
*this)
450 this->iface_cache
->destroy(this->iface_cache
);
457 updown_listener_t
*updown_listener_create(updown_handler_t
*handler
)
459 private_updown_listener_t
*this;
464 .child_updown
= _child_updown
,
468 .iface_cache
= linked_list_create(),
472 return &this->public;