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
21 #include "updown_listener.h"
24 #include <config/child_cfg.h>
26 typedef struct private_updown_listener_t private_updown_listener_t
;
29 * Private data of an updown_listener_t object.
31 struct private_updown_listener_t
{
34 * Public updown_listener_t interface.
36 updown_listener_t
public;
39 * List of cached interface names
41 linked_list_t
*iface_cache
;
44 typedef struct cache_entry_t cache_entry_t
;
47 * Cache line in the interface name cache.
49 struct cache_entry_t
{
50 /** requid of the CHILD_SA */
52 /** cached interface name */
57 * Insert an interface name to the cache
59 static void cache_iface(private_updown_listener_t
*this, u_int32_t reqid
,
62 cache_entry_t
*entry
= malloc_thing(cache_entry_t
);
65 entry
->iface
= strdup(iface
);
67 this->iface_cache
->insert_first(this->iface_cache
, entry
);
71 * Remove a cached interface name and return it.
73 static char* uncache_iface(private_updown_listener_t
*this, u_int32_t reqid
)
75 enumerator_t
*enumerator
;
79 enumerator
= this->iface_cache
->create_enumerator(this->iface_cache
);
80 while (enumerator
->enumerate(enumerator
, &entry
))
82 if (entry
->reqid
== reqid
)
84 this->iface_cache
->remove_at(this->iface_cache
, enumerator
);
90 enumerator
->destroy(enumerator
);
95 * Run the up/down script
97 static void updown(private_updown_listener_t
*this, ike_sa_t
*ike_sa
,
98 child_sa_t
*child_sa
, bool up
)
100 traffic_selector_t
*my_ts
, *other_ts
;
101 enumerator_t
*enumerator
;
103 host_t
*vip
, *me
, *other
;
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
);
117 enumerator
= child_sa
->create_policy_enumerator(child_sa
);
118 while (enumerator
->enumerate(enumerator
, &my_ts
, &other_ts
))
121 char *my_client
, *other_client
, *my_client_mask
, *other_client_mask
;
122 char *pos
, *virtual_ip
, *iface
;
125 /* get subnet/bits from string */
126 if (asprintf(&my_client
, "%R", my_ts
) < 0)
130 pos
= strchr(my_client
, '/');
132 my_client_mask
= pos
+ 1;
133 pos
= strchr(my_client_mask
, '[');
138 if (asprintf(&other_client
, "%R", other_ts
) < 0)
142 pos
= strchr(other_client
, '/');
144 other_client_mask
= pos
+ 1;
145 pos
= strchr(other_client_mask
, '[');
153 if (asprintf(&virtual_ip
, "PLUTO_MY_SOURCEIP='%H' ", vip
) < 0)
160 if (asprintf(&virtual_ip
, "") < 0)
168 iface
= charon
->kernel_interface
->get_interface(
169 charon
->kernel_interface
, me
);
172 cache_iface(this, child_sa
->get_reqid(child_sa
), iface
);
177 iface
= uncache_iface(this, child_sa
->get_reqid(child_sa
));
180 /* build the command with all env variables.
181 * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
183 snprintf(command
, sizeof(command
),
185 "PLUTO_VERSION='1.1' "
186 "PLUTO_VERB='%s%s%s' "
187 "PLUTO_CONNECTION='%s' "
188 "PLUTO_INTERFACE='%s' "
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' "
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' "
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
),
224 config
->get_hostaccess(config
) ?
"PLUTO_HOST_ACCESS='1' " : "",
231 DBG3(DBG_CHD
, "running updown script: %s", command
);
232 shell
= popen(command
, "r");
236 DBG1(DBG_CHD
, "could not execute updown script '%s'", script
);
244 if (fgets(resp
, sizeof(resp
), shell
) == NULL
)
248 DBG1(DBG_CHD
, "error reading output from updown script");
258 char *e
= resp
+ strlen(resp
);
259 if (e
> resp
&& e
[-1] == '\n')
260 { /* trim trailing '\n' */
263 DBG1(DBG_CHD
, "updown: %s", resp
);
268 enumerator
->destroy(enumerator
);
272 * Listener implementation
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
)
277 child_sa_state_t old
;
281 old
= child_sa
->get_state(child_sa
);
283 if ((old
== CHILD_INSTALLED
&& state
!= CHILD_REKEYING
) ||
284 (old
== CHILD_DELETING
&& state
== CHILD_DESTROYING
))
286 updown(this, ike_sa
, child_sa
, FALSE
);
288 else if (state
== CHILD_INSTALLED
)
290 updown(this, ike_sa
, child_sa
, TRUE
);
297 * Implementation of updown_listener_t.destroy.
299 static void destroy(private_updown_listener_t
*this)
301 this->iface_cache
->destroy(this->iface_cache
);
308 updown_listener_t
*updown_listener_create()
310 private_updown_listener_t
*this = malloc_thing(private_updown_listener_t
);
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
;
316 this->iface_cache
= linked_list_create();
318 return &this->public;