android: Add a custom kernel-net implementation to replace kernel-netlink
[strongswan.git] / src / frontends / android / 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 "../charonservice.h"
22 #include <hydra.h>
23 #include <processing/jobs/callback_job.h>
24 #include <threading/mutex.h>
25
26 /** delay before firing roam events (ms) */
27 #define ROAM_DELAY 100
28
29 typedef struct private_android_net_t private_android_net_t;
30
31 struct private_android_net_t {
32
33 /**
34 * Public kernel interface
35 */
36 kernel_net_t public;
37
38 /**
39 * Reference to NetworkManager object
40 */
41 network_manager_t *network_manager;
42
43 /**
44 * Earliest time of the next roam event
45 */
46 timeval_t next_roam;
47
48 /**
49 * Mutex to check and update roam event time, and other private members
50 */
51 mutex_t *mutex;
52
53 /**
54 * List of virtual IPs
55 */
56 linked_list_t *vips;
57
58 /**
59 * Socket used to determine source address
60 */
61 int socket_v4;
62 };
63
64 /**
65 * callback function that raises the delayed roam event
66 */
67 static job_requeue_t roam_event()
68 {
69 /* this will fail if no connection is up */
70 charonservice->bypass_socket(charonservice, -1, 0);
71 hydra->kernel_interface->roam(hydra->kernel_interface, TRUE);
72 return JOB_REQUEUE_NONE;
73 }
74
75 /**
76 * Listen for connectivity change events and queue a roam event
77 */
78 static void connectivity_cb(private_android_net_t *this,
79 bool disconnected)
80 {
81 timeval_t now;
82 job_t *job;
83
84 time_monotonic(&now);
85 this->mutex->lock(this->mutex);
86 if (!timercmp(&now, &this->next_roam, >))
87 {
88 this->mutex->unlock(this->mutex);
89 return;
90 }
91 timeval_add_ms(&now, ROAM_DELAY);
92 this->next_roam = now;
93 this->mutex->unlock(this->mutex);
94
95 job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, NULL,
96 NULL, NULL);
97 lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
98 }
99
100 METHOD(kernel_net_t, get_source_addr, host_t*,
101 private_android_net_t *this, host_t *dest, host_t *src)
102 {
103 union {
104 struct sockaddr sockaddr;
105 struct sockaddr_in sin;
106 struct sockaddr_in6 sin6;
107 } addr;
108 socklen_t addrlen;
109
110 addrlen = *dest->get_sockaddr_len(dest);
111 addr.sockaddr.sa_family = AF_UNSPEC;
112 if (connect(this->socket_v4, &addr.sockaddr, addrlen) < 0)
113 {
114 DBG1(DBG_KNL, "failed to disconnect socket: %s", strerror(errno));
115 return NULL;
116 }
117 if (connect(this->socket_v4, dest->get_sockaddr(dest), addrlen) < 0)
118 {
119 /* don't report an error if we are not connected (ENETUNREACH) */
120 if (errno != ENETUNREACH)
121 {
122 DBG1(DBG_KNL, "failed to connect socket: %s", strerror(errno));
123 }
124 return NULL;
125 }
126 if (getsockname(this->socket_v4, &addr.sockaddr, &addrlen) < 0)
127 {
128 DBG1(DBG_KNL, "failed to determine src address: %s", strerror(errno));
129 return NULL;
130 }
131 return host_create_from_sockaddr((sockaddr_t*)&addr);
132 }
133
134 METHOD(kernel_net_t, get_nexthop, host_t*,
135 private_android_net_t *this, host_t *dest, int prefix, host_t *src)
136 {
137 return NULL;
138 }
139
140 METHOD(kernel_net_t, get_interface, bool,
141 private_android_net_t *this, host_t *host, char **name)
142 {
143 if (name)
144 { /* the actual name does not matter in our case */
145 *name = strdup("strongswan");
146 }
147 return TRUE;
148 }
149
150 METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
151 private_android_net_t *this, kernel_address_type_t which)
152 {
153 /* return virtual IPs if requested, nothing otherwise */
154 if (which & ADDR_TYPE_VIRTUAL)
155 {
156 this->mutex->lock(this->mutex);
157 return enumerator_create_cleaner(
158 this->vips->create_enumerator(this->vips),
159 (void*)this->mutex->unlock, this->mutex);
160 }
161 return enumerator_create_empty();
162 }
163
164 METHOD(kernel_net_t, add_ip, status_t,
165 private_android_net_t *this, host_t *virtual_ip, int prefix, char *iface)
166 {
167 this->mutex->lock(this->mutex);
168 this->vips->insert_last(this->vips, virtual_ip->clone(virtual_ip));
169 this->mutex->unlock(this->mutex);
170 return SUCCESS;
171 }
172
173 METHOD(kernel_net_t, del_ip, status_t,
174 private_android_net_t *this, host_t *virtual_ip, int prefix, bool wait)
175 {
176 host_t *vip;
177
178 this->mutex->lock(this->mutex);
179 if (this->vips->find_first(this->vips, (void*)virtual_ip->ip_equals,
180 (void**)&vip, virtual_ip) == SUCCESS)
181 {
182 this->vips->remove(this->vips, vip, NULL);
183 vip->destroy(vip);
184 }
185 this->mutex->unlock(this->mutex);
186 return SUCCESS;
187 }
188
189 METHOD(kernel_net_t, add_route, status_t,
190 private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
191 host_t *gateway, host_t *src_ip, char *if_name)
192 {
193 return NOT_SUPPORTED;
194 }
195
196 METHOD(kernel_net_t, del_route, status_t,
197 private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
198 host_t *gateway, host_t *src_ip, char *if_name)
199 {
200 return NOT_SUPPORTED;
201 }
202
203 METHOD(kernel_net_t, destroy, void,
204 private_android_net_t *this)
205 {
206 this->network_manager->remove_connectivity_cb(this->network_manager,
207 (void*)connectivity_cb);
208 this->mutex->destroy(this->mutex);
209 this->vips->destroy(this->vips);
210 close(this->socket_v4);
211 free(this);
212 }
213
214 kernel_net_t *kernel_android_net_create()
215 {
216 private_android_net_t *this;
217
218 INIT(this,
219 .public = {
220 .get_source_addr = _get_source_addr,
221 .get_nexthop = _get_nexthop,
222 .get_interface = _get_interface,
223 .create_address_enumerator = _create_address_enumerator,
224 .add_ip = _add_ip,
225 .del_ip = _del_ip,
226 .add_route = _add_route,
227 .del_route = _del_route,
228 .destroy = _destroy,
229 },
230 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
231 .vips = linked_list_create(),
232 .network_manager = charonservice->get_network_manager(charonservice),
233 );
234 timerclear(&this->next_roam);
235
236 this->socket_v4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
237 if (this->socket_v4 < 0)
238 {
239 DBG1(DBG_KNL, "failed to create socket to lookup src addresses: %s",
240 strerror(errno));
241 }
242 charonservice->bypass_socket(charonservice, this->socket_v4, AF_INET);
243
244 this->network_manager->add_connectivity_cb(this->network_manager,
245 (void*)connectivity_cb, this);
246 return &this->public;
247 }