OLD | NEW |
1 /* | 1 /* |
2 * | 2 * |
3 * Connection Manager | 3 * Connection Manager |
4 * | 4 * |
5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. | 5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. |
6 * | 6 * |
7 * This program is free software; you can redistribute it and/or modify | 7 * This program is free software; you can redistribute it and/or modify |
8 * it under the terms of the GNU General Public License version 2 as | 8 * it under the terms of the GNU General Public License version 2 as |
9 * published by the Free Software Foundation. | 9 * published by the Free Software Foundation. |
10 * | 10 * |
11 * This program is distributed in the hope that it will be useful, | 11 * This program is distributed in the hope that it will be useful, |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 * GNU General Public License for more details. | 14 * GNU General Public License for more details. |
15 * | 15 * |
16 * You should have received a copy of the GNU General Public License | 16 * You should have received a copy of the GNU General Public License |
17 * along with this program; if not, write to the Free Software | 17 * along with this program; if not, write to the Free Software |
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
19 * | 19 * |
20 */ | 20 */ |
21 | 21 |
22 /* | 22 /* |
23 * Support for vpn plugins. Common code to manage a provider object, | 23 * Support for vpn plugins. Common code to manage a provider object, |
24 * tun device and a task associated with an external vpn process. The | 24 * tun device and a task associated with an external vpn process. The |
25 * vpn plugin is responsible for launching the external process and | 25 * vpn plugin is responsible for launching the external process and |
26 * handling notification callbacks to clock the provider state machine. | 26 * handling notification callbacks to clock the provider state machine. |
27 * | 27 * |
28 * TODO(sleffler) currently assumes extern vpn service uses tun but not all do | |
29 * TODO(sleffler) seems to make more sense in src than plugins | 28 * TODO(sleffler) seems to make more sense in src than plugins |
30 */ | 29 */ |
31 | 30 |
32 #ifdef HAVE_CONFIG_H | 31 #ifdef HAVE_CONFIG_H |
33 #include <config.h> | 32 #include <config.h> |
34 #endif | 33 #endif |
35 | 34 |
36 #include <string.h> | 35 #include <string.h> |
37 #include <fcntl.h> | 36 #include <fcntl.h> |
38 #include <unistd.h> | 37 #include <unistd.h> |
(...skipping 11 matching lines...) Expand all Loading... |
50 #include <glib/gprintf.h> | 49 #include <glib/gprintf.h> |
51 | 50 |
52 #include <connman/provider.h> | 51 #include <connman/provider.h> |
53 #include <connman/log.h> | 52 #include <connman/log.h> |
54 #include <connman/rtnl.h> | 53 #include <connman/rtnl.h> |
55 #include <connman/task.h> | 54 #include <connman/task.h> |
56 #include <connman/inet.h> | 55 #include <connman/inet.h> |
57 | 56 |
58 #include "vpn.h" | 57 #include "vpn.h" |
59 | 58 |
60 #define»_DBG_VPN(fmt, arg...)» DBG(DBG_VPN, fmt, ## arg) | 59 #define _DBG_VPN(fmt, arg...) DBG(DBG_VPN, fmt, ## arg) |
61 | 60 |
62 struct vpn_data { | 61 struct vpn_data { |
63 struct connman_provider *provider; | 62 struct connman_provider *provider; |
64 char *if_name; | 63 char *if_name; |
65 unsigned flags; | 64 unsigned flags; |
66 unsigned int watch; | 65 unsigned int watch; |
67 unsigned int state; | 66 unsigned int state; |
68 struct connman_task *task; | 67 struct connman_task *task; |
69 }; | 68 }; |
70 | 69 |
71 struct vpn_driver_data { | 70 struct vpn_driver_data { |
72 const char *name; | 71 const char *name; |
73 const char *program; | 72 const char *program; |
74 struct vpn_driver *vpn_driver; | 73 struct vpn_driver *vpn_driver; |
75 struct connman_provider_driver provider_driver; | 74 struct connman_provider_driver provider_driver; |
76 }; | 75 }; |
77 | 76 |
78 static GHashTable *driver_hash = NULL; | 77 static GHashTable *driver_hash = NULL; |
79 | 78 |
80 static int kill_tun(char *tun_name) | 79 static int kill_tun(struct connman_provider *provider) |
81 { | 80 { |
| 81 struct vpn_data *data = connman_provider_get_data(provider); |
| 82 struct vpn_driver_data *vpn_driver_data; |
| 83 const char *name; |
82 struct ifreq ifr; | 84 struct ifreq ifr; |
83 int fd, err; | 85 int fd, err; |
84 | 86 |
| 87 if (data == NULL) |
| 88 return -1; |
| 89 |
| 90 name = connman_provider_get_driver_name(provider); |
| 91 vpn_driver_data = g_hash_table_lookup(driver_hash, name); |
| 92 |
| 93 if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver !=NULL && |
| 94 vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN) |
| 95 return 0; |
| 96 |
85 memset(&ifr, 0, sizeof(ifr)); | 97 memset(&ifr, 0, sizeof(ifr)); |
86 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; | 98 ifr.ifr_flags = IFF_TUN | IFF_NO_PI; |
87 » strncpy(ifr.ifr_name, tun_name, sizeof(ifr.ifr_name)); | 99 » strncpy(ifr.ifr_name, data->if_name, sizeof(ifr.ifr_name)); |
88 | 100 |
89 fd = open("/dev/net/tun", O_RDWR); | 101 fd = open("/dev/net/tun", O_RDWR); |
90 if (fd < 0) { | 102 if (fd < 0) { |
91 err = -errno; | 103 err = -errno; |
92 connman_error("Failed to open /dev/net/tun to device %s: %s", | 104 connman_error("Failed to open /dev/net/tun to device %s: %s", |
93 » » » tun_name, strerror(errno)); | 105 » » » data->if_name, strerror(errno)); |
94 return err; | 106 return err; |
95 } | 107 } |
96 | 108 |
97 if (ioctl(fd, TUNSETIFF, (void *)&ifr)) { | 109 if (ioctl(fd, TUNSETIFF, (void *)&ifr)) { |
98 err = -errno; | 110 err = -errno; |
99 connman_error("Failed to TUNSETIFF for device %s to it: %s", | 111 connman_error("Failed to TUNSETIFF for device %s to it: %s", |
100 » » » tun_name, strerror(errno)); | 112 » » » data->if_name, strerror(errno)); |
101 close(fd); | 113 close(fd); |
102 return err; | 114 return err; |
103 } | 115 } |
104 | 116 |
105 if (ioctl(fd, TUNSETPERSIST, 0)) { | 117 if (ioctl(fd, TUNSETPERSIST, 0)) { |
106 err = -errno; | 118 err = -errno; |
107 connman_error("Failed to set tun device %s nonpersistent: %s", | 119 connman_error("Failed to set tun device %s nonpersistent: %s", |
108 » » » tun_name, strerror(errno)); | 120 » » » data->if_name, strerror(errno)); |
109 close(fd); | 121 close(fd); |
110 return err; | 122 return err; |
111 } | 123 } |
112 close(fd); | 124 close(fd); |
113 » _DBG_VPN("Killed tun device %s", tun_name); | 125 » _DBG_VPN("Killed tun device %s", data->if_name); |
114 return 0; | 126 return 0; |
115 } | 127 } |
116 | 128 |
117 void vpn_died(struct connman_task *task, void *user_data) | 129 void vpn_died(struct connman_task *task, void *user_data) |
118 { | 130 { |
119 struct connman_provider *provider = user_data; | 131 struct connman_provider *provider = user_data; |
120 struct vpn_data *data = connman_provider_get_data(provider); | 132 struct vpn_data *data = connman_provider_get_data(provider); |
121 int state = data->state; | 133 int state = data->state; |
122 | 134 |
123 _DBG_VPN("provider %p data %p", provider, data); | 135 _DBG_VPN("provider %p data %p", provider, data); |
124 | 136 |
125 if (!data) | 137 if (!data) |
126 goto vpn_exit; | 138 goto vpn_exit; |
127 | 139 |
128 » kill_tun(data->if_name); | 140 » kill_tun(provider); |
129 connman_provider_set_data(provider, NULL); | 141 connman_provider_set_data(provider, NULL); |
130 connman_rtnl_remove_watch(data->watch); | 142 connman_rtnl_remove_watch(data->watch); |
131 | 143 |
132 vpn_exit: | 144 vpn_exit: |
133 if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) | 145 if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) |
134 connman_provider_set_state(provider, | 146 connman_provider_set_state(provider, |
135 CONNMAN_PROVIDER_STATE_FAILURE); | 147 CONNMAN_PROVIDER_STATE_FAILURE); |
136 else | 148 else |
137 connman_provider_set_state(provider, | 149 connman_provider_set_state(provider, |
138 CONNMAN_PROVIDER_STATE_IDLE); | 150 CONNMAN_PROVIDER_STATE_IDLE); |
139 | 151 |
140 connman_provider_set_index(provider, -1); | 152 connman_provider_set_index(provider, -1); |
141 connman_provider_unref(data->provider); | 153 connman_provider_unref(data->provider); |
142 g_free(data); | 154 g_free(data); |
143 | 155 |
144 connman_task_destroy(task); | 156 connman_task_destroy(task); |
145 } | 157 } |
146 | 158 |
147 static void vpn_newlink(const char *ifname, unsigned flags, unsigned change,»
» » » void *user_data) | 159 int vpn_set_ifname(struct connman_provider *provider, const char *ifname) |
| 160 { |
| 161 » struct vpn_data *data = connman_provider_get_data(provider); |
| 162 » int index; |
| 163 |
| 164 » if (data == NULL) { |
| 165 » » _DBG_VPN("%s: provider data not accessible", __func__); |
| 166 » » return -EIO; |
| 167 » } |
| 168 |
| 169 » if (ifname == NULL) { |
| 170 » » _DBG_VPN("%s: ifname not provided", __func__); |
| 171 » » return -EIO; |
| 172 » } |
| 173 |
| 174 » index = connman_inet_ifindex(ifname); |
| 175 » if (index < 0) { |
| 176 » » _DBG_VPN("%s: could not get ifindex from %s", __func__, ifname); |
| 177 » » return -EIO; |
| 178 » } |
| 179 |
| 180 » data->if_name = (char *)g_strdup(ifname); |
| 181 » connman_provider_set_index(provider, index); |
| 182 |
| 183 » /* Set connect state to retry creating ipconfig with index above. */ |
| 184 » connman_provider_set_state(provider, CONNMAN_PROVIDER_STATE_CONNECT); |
| 185 |
| 186 » return 0; |
| 187 } |
| 188 |
| 189 static void vpn_newlink(const char *ifname, unsigned flags, unsigned change, |
| 190 » » » void *user_data) |
148 { | 191 { |
149 struct connman_provider *provider = user_data; | 192 struct connman_provider *provider = user_data; |
150 struct vpn_data *data = connman_provider_get_data(provider); | 193 struct vpn_data *data = connman_provider_get_data(provider); |
151 | 194 |
152 if ((data->flags & IFF_UP) != (flags & IFF_UP)) { | 195 if ((data->flags & IFF_UP) != (flags & IFF_UP)) { |
153 if (flags & IFF_UP) { | 196 if (flags & IFF_UP) { |
154 data->state = VPN_STATE_READY; | 197 data->state = VPN_STATE_READY; |
155 connman_provider_set_state(provider, | 198 connman_provider_set_state(provider, |
156 CONNMAN_PROVIDER_STATE_READY); | 199 CONNMAN_PROVIDER_STATE_READY); |
157 } | 200 } |
158 } | 201 } |
159 data->flags = flags; | 202 data->flags = flags; |
160 } | 203 } |
161 | 204 |
162 static void vpn_notify(struct connman_task *task, | 205 static DBusMessage *vpn_notify(struct connman_task *task, |
163 DBusMessage *msg, void *user_data) | 206 DBusMessage *msg, void *user_data) |
164 { | 207 { |
165 struct connman_provider *provider = user_data; | 208 struct connman_provider *provider = user_data; |
166 struct vpn_data *data; | 209 struct vpn_data *data; |
167 struct vpn_driver_data *vpn_driver_data; | 210 struct vpn_driver_data *vpn_driver_data; |
168 const char *name; | 211 const char *name; |
169 int state, index; | 212 int state, index; |
170 | 213 |
171 data = connman_provider_get_data(provider); | 214 data = connman_provider_get_data(provider); |
172 | 215 |
173 name = connman_provider_get_driver_name(provider); | 216 name = connman_provider_get_driver_name(provider); |
174 vpn_driver_data = g_hash_table_lookup(driver_hash, name); | 217 vpn_driver_data = g_hash_table_lookup(driver_hash, name); |
175 if (vpn_driver_data == NULL) | 218 if (vpn_driver_data == NULL) |
176 » » return; | 219 » » return NULL; |
177 | 220 |
178 state = vpn_driver_data->vpn_driver->notify(msg, provider); | 221 state = vpn_driver_data->vpn_driver->notify(msg, provider); |
179 switch (state) { | 222 switch (state) { |
180 case VPN_STATE_CONNECT: | 223 case VPN_STATE_CONNECT: |
181 case VPN_STATE_READY: | 224 case VPN_STATE_READY: |
182 index = connman_provider_get_index(provider); | 225 index = connman_provider_get_index(provider); |
183 data->watch = connman_rtnl_add_newlink_watch(index, | 226 data->watch = connman_rtnl_add_newlink_watch(index, |
184 vpn_newlink, provider); | 227 vpn_newlink, provider); |
185 connman_inet_ifup(index); | 228 connman_inet_ifup(index); |
186 break; | 229 break; |
187 | 230 |
188 case VPN_STATE_UNKNOWN: | 231 case VPN_STATE_UNKNOWN: |
189 case VPN_STATE_IDLE: | 232 case VPN_STATE_IDLE: |
190 case VPN_STATE_DISCONNECT: | 233 case VPN_STATE_DISCONNECT: |
191 case VPN_STATE_FAILURE: | 234 case VPN_STATE_FAILURE: |
192 connman_provider_set_state(provider, | 235 connman_provider_set_state(provider, |
193 CONNMAN_PROVIDER_STATE_DISCONNECT); | 236 CONNMAN_PROVIDER_STATE_DISCONNECT); |
194 break; | 237 break; |
195 } | 238 } |
| 239 |
| 240 return NULL; |
196 } | 241 } |
197 | 242 |
198 static int vpn_connect(struct connman_provider *provider) | 243 static int vpn_create_tun(struct connman_provider *provider) |
199 { | 244 { |
200 struct vpn_data *data = connman_provider_get_data(provider); | 245 struct vpn_data *data = connman_provider_get_data(provider); |
201 struct vpn_driver_data *vpn_driver_data; | |
202 struct ifreq ifr; | 246 struct ifreq ifr; |
203 const char *name; | |
204 int i, fd, index; | 247 int i, fd, index; |
205 int ret = 0; | 248 int ret = 0; |
206 | 249 |
207 » if (data != NULL) | 250 » if (data == NULL) { |
208 » » return -EISCONN; | 251 » » connman_error("%s: called out of order", __func__); |
209 | 252 » » return -EIO; |
210 » data = g_try_new0(struct vpn_data, 1); | 253 » } |
211 » if (data == NULL) | |
212 » » return -ENOMEM; | |
213 | |
214 » data->provider = connman_provider_ref(provider); | |
215 » data->watch = 0; | |
216 » data->flags = 0; | |
217 » data->task = NULL; | |
218 » data->state = VPN_STATE_IDLE; | |
219 | |
220 » connman_provider_set_data(provider, data); | |
221 | |
222 » name = connman_provider_get_driver_name(provider); | |
223 » vpn_driver_data = g_hash_table_lookup(driver_hash, name); | |
224 | 254 |
225 fd = open("/dev/net/tun", O_RDWR); | 255 fd = open("/dev/net/tun", O_RDWR); |
226 if (fd < 0) { | 256 if (fd < 0) { |
227 i = -errno; | 257 i = -errno; |
228 connman_error("%s: failed to open /dev/net/tun: %s", | 258 connman_error("%s: failed to open /dev/net/tun: %s", |
229 __func__, strerror(errno)); | 259 __func__, strerror(errno)); |
230 ret = i; | 260 ret = i; |
231 goto exist_err; | 261 goto exist_err; |
232 } | 262 } |
233 | 263 |
(...skipping 28 matching lines...) Expand all Loading... |
262 close(fd); | 292 close(fd); |
263 ret = i; | 293 ret = i; |
264 goto exist_err; | 294 goto exist_err; |
265 } | 295 } |
266 | 296 |
267 close(fd); | 297 close(fd); |
268 | 298 |
269 index = connman_inet_ifindex(data->if_name); | 299 index = connman_inet_ifindex(data->if_name); |
270 if (index < 0) { | 300 if (index < 0) { |
271 connman_error("%s: failed to get tun ifindex", __func__); | 301 connman_error("%s: failed to get tun ifindex", __func__); |
272 » » kill_tun(data->if_name); | 302 » » kill_tun(provider); |
273 ret = -EIO; | 303 ret = -EIO; |
274 goto exist_err; | 304 goto exist_err; |
275 } | 305 } |
276 connman_provider_set_index(provider, index); | 306 connman_provider_set_index(provider, index); |
277 | 307 |
| 308 return 0; |
| 309 |
| 310 exist_err: |
| 311 return ret; |
| 312 } |
| 313 |
| 314 static int vpn_connect(struct connman_provider *provider) |
| 315 { |
| 316 struct vpn_data *data = connman_provider_get_data(provider); |
| 317 struct vpn_driver_data *vpn_driver_data; |
| 318 const char *name; |
| 319 int ret = 0; |
| 320 |
| 321 if (data != NULL) { |
| 322 _DBG_VPN("%s: data != NULL", __func__); |
| 323 return -EISCONN; |
| 324 } |
| 325 |
| 326 data = g_try_new0(struct vpn_data, 1); |
| 327 if (data == NULL) |
| 328 return -ENOMEM; |
| 329 |
| 330 data->provider = connman_provider_ref(provider); |
| 331 data->watch = 0; |
| 332 data->flags = 0; |
| 333 data->task = NULL; |
| 334 data->state = VPN_STATE_IDLE; |
| 335 |
| 336 connman_provider_set_data(provider, data); |
| 337 |
| 338 name = connman_provider_get_driver_name(provider); |
| 339 vpn_driver_data = g_hash_table_lookup(driver_hash, name); |
| 340 |
| 341 if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL && |
| 342 vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) { |
| 343 |
| 344 ret = vpn_create_tun(provider); |
| 345 if (ret < 0) |
| 346 goto exist_err; |
| 347 } |
| 348 |
278 data->task = connman_task_create(vpn_driver_data->program); | 349 data->task = connman_task_create(vpn_driver_data->program); |
279 | 350 |
280 if (data->task == NULL) { | 351 if (data->task == NULL) { |
281 ret = -ENOMEM; | 352 ret = -ENOMEM; |
282 » » kill_tun(data->if_name); | 353 » » kill_tun(provider); |
283 goto exist_err; | 354 goto exist_err; |
284 } | 355 } |
285 | 356 |
286 if (connman_task_set_notify(data->task, "notify", | 357 if (connman_task_set_notify(data->task, "notify", |
287 vpn_notify, provider)) { | 358 vpn_notify, provider)) { |
288 ret = -ENOMEM; | 359 ret = -ENOMEM; |
289 » » kill_tun(data->if_name); | 360 » » kill_tun(provider); |
290 connman_task_destroy(data->task); | 361 connman_task_destroy(data->task); |
291 data->task = NULL; | 362 data->task = NULL; |
292 goto exist_err; | 363 goto exist_err; |
293 } | 364 } |
294 | 365 |
295 ret = vpn_driver_data->vpn_driver->connect(provider, data->task, | 366 ret = vpn_driver_data->vpn_driver->connect(provider, data->task, |
296 data->if_name); | 367 data->if_name); |
297 if (ret < 0) { | 368 if (ret < 0) { |
298 » » kill_tun(data->if_name); | 369 » » kill_tun(provider); |
299 connman_task_destroy(data->task); | 370 connman_task_destroy(data->task); |
300 data->task = NULL; | 371 data->task = NULL; |
301 goto exist_err; | 372 goto exist_err; |
302 } | 373 } |
303 | 374 |
304 _DBG_VPN("%s started with dev %s", | 375 _DBG_VPN("%s started with dev %s", |
305 vpn_driver_data->provider_driver.name, data->if_name); | 376 vpn_driver_data->provider_driver.name, data->if_name); |
306 | 377 |
307 data->state = VPN_STATE_CONNECT; | 378 data->state = VPN_STATE_CONNECT; |
308 | 379 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 connman_task_stop(data->task); | 419 connman_task_stop(data->task); |
349 | 420 |
350 return 0; | 421 return 0; |
351 } | 422 } |
352 | 423 |
353 static int vpn_remove(struct connman_provider *provider) | 424 static int vpn_remove(struct connman_provider *provider) |
354 { | 425 { |
355 struct vpn_data *data; | 426 struct vpn_data *data; |
356 | 427 |
357 data = connman_provider_get_data(provider); | 428 data = connman_provider_get_data(provider); |
358 connman_provider_set_data(provider, NULL); | |
359 if (data == NULL) | 429 if (data == NULL) |
360 return 0; | 430 return 0; |
361 | 431 |
362 if (data->watch != 0) | 432 if (data->watch != 0) |
363 connman_rtnl_remove_watch(data->watch); | 433 connman_rtnl_remove_watch(data->watch); |
364 data->watch = 0; | 434 data->watch = 0; |
365 connman_task_stop(data->task); | 435 connman_task_stop(data->task); |
366 | 436 |
367 g_usleep(G_USEC_PER_SEC); | 437 g_usleep(G_USEC_PER_SEC); |
368 » kill_tun(data->if_name); | 438 » kill_tun(provider); |
| 439 » connman_provider_set_data(provider, NULL); |
369 return 0; | 440 return 0; |
370 } | 441 } |
371 | 442 |
372 int vpn_register(const char *name, struct vpn_driver *vpn_driver, | 443 int vpn_register(const char *name, struct vpn_driver *vpn_driver, |
373 const char *program) | 444 const char *program) |
374 { | 445 { |
375 struct vpn_driver_data *data; | 446 struct vpn_driver_data *data; |
376 | 447 |
377 _DBG_VPN("name %s program %s", name, program); | 448 _DBG_VPN("name %s program %s", name, program); |
378 | 449 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 if (data == NULL) | 483 if (data == NULL) |
413 return; | 484 return; |
414 | 485 |
415 connman_provider_driver_unregister(&data->provider_driver); | 486 connman_provider_driver_unregister(&data->provider_driver); |
416 | 487 |
417 g_hash_table_remove(driver_hash, name); | 488 g_hash_table_remove(driver_hash, name); |
418 | 489 |
419 if (g_hash_table_size(driver_hash) == 0) | 490 if (g_hash_table_size(driver_hash) == 0) |
420 g_hash_table_destroy(driver_hash); | 491 g_hash_table_destroy(driver_hash); |
421 } | 492 } |
OLD | NEW |