2ce1bdfac997b497aee958ce0b244920f705e5ba
[strongswan.git] / src / frontends / android / app / src / main / jni / libandroidbridge / kernel / android_net.c
1 /*
2 * Copyright (C) 2012-2015 Tobias Brunner
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 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <unistd.h>
17 #include <errno.h>
18
19 #include "android_net.h"
20
21 #include "../android_jni.h"
22 #include "../charonservice.h"
23 #include <hydra.h>
24 #include <processing/jobs/callback_job.h>
25 #include <threading/mutex.h>
26
27 /** delay before firing roam events (ms) */
28 #define ROAM_DELAY 100
29 #define ROAM_DELAY_RECHECK 1000
30
31 typedef struct private_android_net_t private_android_net_t;
32
33 struct private_android_net_t {
34
35 /**
36 * Public kernel interface
37 */
38 kernel_net_t public;
39
40 /**
41 * Reference to NetworkManager object
42 */
43 network_manager_t *network_manager;
44
45 /**
46 * Earliest time of the next roam event
47 */
48 timeval_t next_roam;
49
50 /**
51 * Mutex to check and update roam event time, and other private members
52 */
53 mutex_t *mutex;
54
55 /**
56 * List of virtual IPs
57 */
58 linked_list_t *vips;
59
60 /**
61 * Socket used to determine source address
62 */
63 int socket_v4;
64
65 /**
66 * Whether the device is currently connected
67 */
68 bool connected;
69 };
70
71 /**
72 * callback function that raises the delayed roam event
73 */
74 static job_requeue_t roam_event()
75 {
76 /* this will fail if no connection is up */
77 charonservice->bypass_socket(charonservice, -1, 0);
78 hydra->kernel_interface->roam(hydra->kernel_interface, TRUE);
79 return JOB_REQUEUE_NONE;
80 }
81
82 /**
83 * Listen for connectivity change events and queue a roam event
84 */
85 static void connectivity_cb(private_android_net_t *this,
86 bool disconnected)
87 {
88 timeval_t now;
89 job_t *job;
90
91 time_monotonic(&now);
92 this->mutex->lock(this->mutex);
93 this->connected = !disconnected;
94 if (!timercmp(&now, &this->next_roam, >))
95 {
96 this->mutex->unlock(this->mutex);
97 return;
98 }
99 timeval_add_ms(&now, ROAM_DELAY);
100 this->next_roam = now;
101 this->mutex->unlock(this->mutex);
102
103 job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, NULL,
104 NULL, NULL);
105 lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
106 }
107
108 METHOD(kernel_net_t, get_source_addr, host_t*,
109 private_android_net_t *this, host_t *dest, host_t *src)
110 {
111 union {
112 struct sockaddr sockaddr;
113 struct sockaddr_in sin;
114 struct sockaddr_in6 sin6;
115 } addr;
116 socklen_t addrlen;
117 timeval_t now;
118 job_t *job;
119
120 addrlen = *dest->get_sockaddr_len(dest);
121 addr.sockaddr.sa_family = AF_UNSPEC;
122 if (connect(this->socket_v4, &addr.sockaddr, addrlen) < 0)
123 {
124 DBG1(DBG_KNL, "failed to disconnect socket: %s", strerror(errno));
125 return NULL;
126 }
127 if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
128 { /* this seems to help avoiding the VIP, unless there is no connectivity
129 * at all */
130 charonservice->bypass_socket(charonservice, -1, 0);
131 }
132 if (connect(this->socket_v4, dest->get_sockaddr(dest), addrlen) < 0)
133 {
134 /* don't report an error if we are not connected (ENETUNREACH) */
135 if (errno != ENETUNREACH)
136 {
137 DBG1(DBG_KNL, "failed to connect socket: %s", strerror(errno));
138 }
139 else
140 {
141 time_monotonic(&now);
142 this->mutex->lock(this->mutex);
143 if (this->connected && timercmp(&now, &this->next_roam, >))
144 { /* we were not able to find a source address but reportedly are
145 * connected, trigger a recheck in case an IP address appears
146 * delayed but the callback is not triggered again */
147 timeval_add_ms(&now, ROAM_DELAY_RECHECK);
148 this->next_roam = now;
149 this->mutex->unlock(this->mutex);
150 job = (job_t*)callback_job_create((callback_job_cb_t)roam_event,
151 NULL, NULL, NULL);
152 lib->scheduler->schedule_job_ms(lib->scheduler, job,
153 ROAM_DELAY_RECHECK);
154 }
155 else
156 {
157 this->mutex->unlock(this->mutex);
158 }
159 }
160 return NULL;
161 }
162 if (getsockname(this->socket_v4, &addr.sockaddr, &addrlen) < 0)
163 {
164 DBG1(DBG_KNL, "failed to determine src address: %s", strerror(errno));
165 return NULL;
166 }
167 return host_create_from_sockaddr((sockaddr_t*)&addr);
168 }
169
170 METHOD(kernel_net_t, get_source_addr_old, host_t*,
171 private_android_net_t *this, host_t *dest, host_t *src)
172 {
173 host_t *host;
174
175 /* on older Android versions we might get the virtual IP back because
176 * the protect() implementation there and connect() don't properly work
177 * together, on newer releases (using fwmarks) that's not a problem */
178 host = get_source_addr(this, dest, src);
179 if (host)
180 {
181 this->mutex->lock(this->mutex);
182 if (this->vips->find_first(this->vips, (void*)host->ip_equals,
183 NULL, host) == SUCCESS)
184 {
185 host->destroy(host);
186 host = NULL;
187 }
188 this->mutex->unlock(this->mutex);
189 }
190 return host;
191 }
192
193 METHOD(kernel_net_t, get_nexthop, host_t*,
194 private_android_net_t *this, host_t *dest, int prefix, host_t *src)
195 {
196 return NULL;
197 }
198
199 METHOD(kernel_net_t, get_interface, bool,
200 private_android_net_t *this, host_t *host, char **name)
201 {
202 if (name)
203 { /* the actual name does not matter in our case */
204 *name = strdup("strongswan");
205 }
206 return TRUE;
207 }
208
209 METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
210 private_android_net_t *this, kernel_address_type_t which)
211 {
212 /* return virtual IPs if requested, nothing otherwise */
213 if (which & ADDR_TYPE_VIRTUAL)
214 {
215 this->mutex->lock(this->mutex);
216 return enumerator_create_cleaner(
217 this->vips->create_enumerator(this->vips),
218 (void*)this->mutex->unlock, this->mutex);
219 }
220 return enumerator_create_empty();
221 }
222
223 METHOD(kernel_net_t, add_ip, status_t,
224 private_android_net_t *this, host_t *virtual_ip, int prefix, char *iface)
225 {
226 this->mutex->lock(this->mutex);
227 this->vips->insert_last(this->vips, virtual_ip->clone(virtual_ip));
228 this->mutex->unlock(this->mutex);
229 return SUCCESS;
230 }
231
232 METHOD(kernel_net_t, del_ip, status_t,
233 private_android_net_t *this, host_t *virtual_ip, int prefix, bool wait)
234 {
235 host_t *vip;
236
237 this->mutex->lock(this->mutex);
238 if (this->vips->find_first(this->vips, (void*)virtual_ip->ip_equals,
239 (void**)&vip, virtual_ip) == SUCCESS)
240 {
241 this->vips->remove(this->vips, vip, NULL);
242 vip->destroy(vip);
243 }
244 this->mutex->unlock(this->mutex);
245 return SUCCESS;
246 }
247
248 METHOD(kernel_net_t, add_route, status_t,
249 private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
250 host_t *gateway, host_t *src_ip, char *if_name)
251 {
252 return NOT_SUPPORTED;
253 }
254
255 METHOD(kernel_net_t, del_route, status_t,
256 private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
257 host_t *gateway, host_t *src_ip, char *if_name)
258 {
259 return NOT_SUPPORTED;
260 }
261
262 METHOD(kernel_net_t, destroy, void,
263 private_android_net_t *this)
264 {
265 this->network_manager->remove_connectivity_cb(this->network_manager,
266 (void*)connectivity_cb);
267 this->mutex->destroy(this->mutex);
268 this->vips->destroy(this->vips);
269 close(this->socket_v4);
270 free(this);
271 }
272
273 kernel_net_t *kernel_android_net_create()
274 {
275 private_android_net_t *this;
276
277 INIT(this,
278 .public = {
279 .get_source_addr = _get_source_addr,
280 .get_nexthop = _get_nexthop,
281 .get_interface = _get_interface,
282 .create_address_enumerator = _create_address_enumerator,
283 .add_ip = _add_ip,
284 .del_ip = _del_ip,
285 .add_route = _add_route,
286 .del_route = _del_route,
287 .destroy = _destroy,
288 },
289 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
290 .vips = linked_list_create(),
291 .network_manager = charonservice->get_network_manager(charonservice),
292 );
293 timerclear(&this->next_roam);
294
295 if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
296 {
297 this->public.get_source_addr = _get_source_addr_old;
298 }
299
300 this->socket_v4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
301 if (this->socket_v4 < 0)
302 {
303 DBG1(DBG_KNL, "failed to create socket to lookup src addresses: %s",
304 strerror(errno));
305 }
306 charonservice->bypass_socket(charonservice, this->socket_v4, AF_INET);
307
308 this->mutex->lock(this->mutex);
309 this->network_manager->add_connectivity_cb(
310 this->network_manager, (void*)connectivity_cb, this);
311 this->connected = this->network_manager->is_connected(this->network_manager);
312 this->mutex->unlock(this->mutex);
313 return &this->public;
314 }