0e054cab676e7d767971e82ae8304a38614c2393
[strongswan.git] / src / charon / plugins / updown / updown_plugin.c
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 #include "updown_plugin.h"
19
20 #include <daemon.h>
21 #include <config/child_cfg.h>
22
23 typedef struct private_updown_plugin_t private_updown_plugin_t;
24
25 /**
26 * private data of updown plugin
27 */
28 struct private_updown_plugin_t {
29
30 /**
31 * implements plugin interface
32 */
33 updown_plugin_t public;
34
35 /**
36 * Listener interface, listens to CHILD_SA state changes
37 */
38 listener_t listener;
39 };
40
41 /**
42 * Run the up/down script
43 */
44 static void updown(ike_sa_t *ike_sa, child_sa_t *child_sa, bool up)
45 {
46 traffic_selector_t *my_ts, *other_ts;
47 enumerator_t *enumerator;
48 child_cfg_t *config;
49 host_t *vip, *me, *other;
50 char *script;
51
52 config = child_sa->get_config(child_sa);
53 vip = ike_sa->get_virtual_ip(ike_sa, TRUE);
54 script = config->get_updown(config);
55 me = ike_sa->get_my_host(ike_sa);
56 other = ike_sa->get_other_host(ike_sa);
57
58 if (script == NULL)
59 {
60 return;
61 }
62
63 enumerator = child_sa->create_policy_enumerator(child_sa);
64 while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
65 {
66 char command[1024];
67 char *my_client, *other_client, *my_client_mask, *other_client_mask;
68 char *pos, *virtual_ip, *iface;
69 FILE *shell;
70
71 /* get subnet/bits from string */
72 asprintf(&my_client, "%R", my_ts);
73 pos = strchr(my_client, '/');
74 *pos = '\0';
75 my_client_mask = pos + 1;
76 pos = strchr(my_client_mask, '[');
77 if (pos)
78 {
79 *pos = '\0';
80 }
81 asprintf(&other_client, "%R", other_ts);
82 pos = strchr(other_client, '/');
83 *pos = '\0';
84 other_client_mask = pos + 1;
85 pos = strchr(other_client_mask, '[');
86 if (pos)
87 {
88 *pos = '\0';
89 }
90
91 if (vip)
92 {
93 asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip);
94 }
95 else
96 {
97 asprintf(&virtual_ip, "");
98 }
99
100 iface = charon->kernel_interface->get_interface(
101 charon->kernel_interface, me);
102
103 /* build the command with all env variables.
104 * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
105 */
106 snprintf(command, sizeof(command),
107 "2>&1 "
108 "PLUTO_VERSION='1.1' "
109 "PLUTO_VERB='%s%s%s' "
110 "PLUTO_CONNECTION='%s' "
111 "PLUTO_INTERFACE='%s' "
112 "PLUTO_REQID='%u' "
113 "PLUTO_ME='%H' "
114 "PLUTO_MY_ID='%D' "
115 "PLUTO_MY_CLIENT='%s/%s' "
116 "PLUTO_MY_CLIENT_NET='%s' "
117 "PLUTO_MY_CLIENT_MASK='%s' "
118 "PLUTO_MY_PORT='%u' "
119 "PLUTO_MY_PROTOCOL='%u' "
120 "PLUTO_PEER='%H' "
121 "PLUTO_PEER_ID='%D' "
122 "PLUTO_PEER_CLIENT='%s/%s' "
123 "PLUTO_PEER_CLIENT_NET='%s' "
124 "PLUTO_PEER_CLIENT_MASK='%s' "
125 "PLUTO_PEER_PORT='%u' "
126 "PLUTO_PEER_PROTOCOL='%u' "
127 "%s"
128 "%s"
129 "%s",
130 up ? "up" : "down",
131 my_ts->is_host(my_ts, me) ? "-host" : "-client",
132 me->get_family(me) == AF_INET ? "" : "-v6",
133 config->get_name(config),
134 iface ? iface : "unknown",
135 child_sa->get_reqid(child_sa),
136 me, ike_sa->get_my_id(ike_sa),
137 my_client, my_client_mask,
138 my_client, my_client_mask,
139 my_ts->get_from_port(my_ts),
140 my_ts->get_protocol(my_ts),
141 other, ike_sa->get_other_id(ike_sa),
142 other_client, other_client_mask,
143 other_client, other_client_mask,
144 other_ts->get_from_port(other_ts),
145 other_ts->get_protocol(other_ts),
146 virtual_ip,
147 config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
148 script);
149 free(my_client);
150 free(other_client);
151 free(virtual_ip);
152 free(iface);
153
154 DBG3(DBG_CHD, "running updown script: %s", command);
155 shell = popen(command, "r");
156
157 if (shell == NULL)
158 {
159 DBG1(DBG_CHD, "could not execute updown script '%s'", script);
160 return;
161 }
162
163 while (TRUE)
164 {
165 char resp[128];
166
167 if (fgets(resp, sizeof(resp), shell) == NULL)
168 {
169 if (ferror(shell))
170 {
171 DBG1(DBG_CHD, "error reading output from updown script");
172 return;
173 }
174 else
175 {
176 break;
177 }
178 }
179 else
180 {
181 char *e = resp + strlen(resp);
182 if (e > resp && e[-1] == '\n')
183 { /* trim trailing '\n' */
184 e[-1] = '\0';
185 }
186 DBG1(DBG_CHD, "updown: %s", resp);
187 }
188 }
189 pclose(shell);
190 }
191 enumerator->destroy(enumerator);
192 }
193
194 /**
195 * Listener implementation
196 */
197 static bool child_state_change(listener_t *this, ike_sa_t *ike_sa,
198 child_sa_t *child_sa, child_sa_state_t state)
199 {
200 child_sa_state_t old;
201
202 if (ike_sa)
203 {
204 old = child_sa->get_state(child_sa);
205
206 if ((old == CHILD_INSTALLED && state != CHILD_REKEYING ) ||
207 (old == CHILD_DELETING && state == CHILD_DESTROYING))
208 {
209 updown(ike_sa, child_sa, FALSE);
210 }
211 else if (state == CHILD_INSTALLED)
212 {
213 updown(ike_sa, child_sa, TRUE);
214 }
215 }
216 return TRUE;
217 }
218
219 /**
220 * Implementation of plugin_t.destroy
221 */
222 static void destroy(private_updown_plugin_t *this)
223 {
224 charon->bus->remove_listener(charon->bus, &this->listener);
225 free(this);
226 }
227
228 /*
229 * see header file
230 */
231 plugin_t *plugin_create()
232 {
233 private_updown_plugin_t *this = malloc_thing(private_updown_plugin_t);
234
235 this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
236
237 memset(&this->listener, 0, sizeof(listener_t));
238 this->listener.child_state_change = child_state_change;
239
240 charon->bus->add_listener(charon->bus, &this->listener);
241
242 return &this->public.plugin;
243 }
244