libhydra: Move kernel interface to libcharon
[strongswan.git] / src / libcharon / plugins / kernel_libipsec / kernel_libipsec_router.c
1 /*
2 * Copyright (C) 2013 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 *
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
16 #include <unistd.h>
17 #include <fcntl.h>
18
19 #include "kernel_libipsec_router.h"
20
21 #include <daemon.h>
22 #include <ipsec.h>
23 #include <collections/hashtable.h>
24 #include <networking/tun_device.h>
25 #include <threading/rwlock.h>
26 #include <threading/thread.h>
27 #include <processing/jobs/callback_job.h>
28
29 typedef struct private_kernel_libipsec_router_t private_kernel_libipsec_router_t;
30
31 /**
32 * Entry in the TUN device map
33 */
34 typedef struct {
35 /** virtual IP (points to internal data of tun) */
36 host_t *addr;
37 /** underlying TUN file descriptor (cached from tun) */
38 int fd;
39 /** TUN device */
40 tun_device_t *tun;
41 } tun_entry_t;
42
43 /**
44 * Single instance of the router
45 */
46 kernel_libipsec_router_t *router;
47
48 /**
49 * Private data
50 */
51 struct private_kernel_libipsec_router_t {
52
53 /**
54 * Public interface
55 */
56 kernel_libipsec_router_t public;
57
58 /**
59 * Default TUN device if kernel interface does not require separate TUN
60 * devices per VIP or for tunnels without VIP.
61 */
62 tun_entry_t tun;
63
64 /**
65 * Hashtable that maps virtual IPs to TUN devices (tun_entry_t).
66 */
67 hashtable_t *tuns;
68
69 /**
70 * Lock for TUN device map
71 */
72 rwlock_t *lock;
73
74 /**
75 * Pipe to signal handle_plain() about changes regarding TUN devices
76 */
77 int notify[2];
78 };
79
80 /**
81 * Hash function for TUN device map
82 */
83 static u_int tun_entry_hash(tun_entry_t *entry)
84 {
85 return chunk_hash(entry->addr->get_address(entry->addr));
86 }
87
88 /**
89 * Comparison function for TUN device map
90 */
91 static bool tun_entry_equals(tun_entry_t *a, tun_entry_t *b)
92 {
93 return a->addr->ip_equals(a->addr, b->addr);
94 }
95
96 /**
97 * Outbound callback
98 */
99 static void send_esp(void *data, esp_packet_t *packet)
100 {
101 charon->sender->send_no_marker(charon->sender, (packet_t*)packet);
102 }
103
104 /**
105 * Receiver callback
106 */
107 static void receiver_esp_cb(void *data, packet_t *packet)
108 {
109 ipsec->processor->queue_inbound(ipsec->processor,
110 esp_packet_create_from_packet(packet));
111 }
112
113 /**
114 * Inbound callback
115 */
116 static void deliver_plain(private_kernel_libipsec_router_t *this,
117 ip_packet_t *packet)
118 {
119 tun_device_t *tun;
120 tun_entry_t *entry, lookup = {
121 .addr = packet->get_destination(packet),
122 };
123
124 this->lock->read_lock(this->lock);
125 entry = this->tuns->get(this->tuns, &lookup);
126 tun = entry ? entry->tun : this->tun.tun;
127 tun->write_packet(tun, packet->get_encoding(packet));
128 this->lock->unlock(this->lock);
129 packet->destroy(packet);
130 }
131
132 /**
133 * Read and process outbound plaintext packet for the given TUN device
134 */
135 static void process_plain(tun_device_t *tun)
136 {
137 chunk_t raw;
138
139 if (tun->read_packet(tun, &raw))
140 {
141 ip_packet_t *packet;
142
143 packet = ip_packet_create(raw);
144 if (packet)
145 {
146 ipsec->processor->queue_outbound(ipsec->processor, packet);
147 }
148 else
149 {
150 DBG1(DBG_KNL, "invalid IP packet read from TUN device");
151 }
152 }
153 }
154
155 /**
156 * Find flagged revents in a pollfd set by fd
157 */
158 static int find_revents(struct pollfd *pfd, int count, int fd)
159 {
160 int i;
161
162 for (i = 0; i < count; i++)
163 {
164 if (pfd[i].fd == fd)
165 {
166 return pfd[i].revents;
167 }
168 }
169 return 0;
170 }
171
172 /**
173 * Job handling outbound plaintext packets
174 */
175 static job_requeue_t handle_plain(private_kernel_libipsec_router_t *this)
176 {
177 enumerator_t *enumerator;
178 tun_entry_t *entry;
179 bool oldstate;
180 int count = 0;
181 char buf[1];
182 struct pollfd *pfd;
183
184 this->lock->read_lock(this->lock);
185
186 pfd = alloca(sizeof(*pfd) * (this->tuns->get_count(this->tuns) + 2));
187 pfd[count].fd = this->notify[0];
188 pfd[count].events = POLLIN;
189 count++;
190 pfd[count].fd = this->tun.fd;
191 pfd[count].events = POLLIN;
192 count++;
193
194 enumerator = this->tuns->create_enumerator(this->tuns);
195 while (enumerator->enumerate(enumerator, NULL, &entry))
196 {
197 pfd[count].fd = entry->fd;
198 pfd[count].events = POLLIN;
199 count++;
200 }
201 enumerator->destroy(enumerator);
202 this->lock->unlock(this->lock);
203
204 oldstate = thread_cancelability(TRUE);
205 if (poll(pfd, count, -1) <= 0)
206 {
207 thread_cancelability(oldstate);
208 return JOB_REQUEUE_FAIR;
209 }
210 thread_cancelability(oldstate);
211
212 if (pfd[0].revents & POLLIN)
213 {
214 /* list of TUN devices changed, read notification data, rebuild FDs */
215 while (read(this->notify[0], &buf, sizeof(buf)) == sizeof(buf))
216 {
217 /* nop */
218 }
219 return JOB_REQUEUE_DIRECT;
220 }
221
222 if (pfd[1].revents & POLLIN)
223 {
224 process_plain(this->tun.tun);
225 }
226
227 this->lock->read_lock(this->lock);
228 enumerator = this->tuns->create_enumerator(this->tuns);
229 while (enumerator->enumerate(enumerator, NULL, &entry))
230 {
231 if (find_revents(pfd, count, entry->fd) & POLLIN)
232 {
233 process_plain(entry->tun);
234 }
235 }
236 enumerator->destroy(enumerator);
237 this->lock->unlock(this->lock);
238
239 return JOB_REQUEUE_DIRECT;
240 }
241
242 METHOD(kernel_listener_t, tun, bool,
243 private_kernel_libipsec_router_t *this, tun_device_t *tun, bool created)
244 {
245 tun_entry_t *entry, lookup;
246 char buf[] = {0x01};
247
248 this->lock->write_lock(this->lock);
249 if (created)
250 {
251 INIT(entry,
252 .addr = tun->get_address(tun, NULL),
253 .fd = tun->get_fd(tun),
254 .tun = tun,
255 );
256 this->tuns->put(this->tuns, entry, entry);
257 }
258 else
259 {
260 lookup.addr = tun->get_address(tun, NULL);
261 entry = this->tuns->remove(this->tuns, &lookup);
262 free(entry);
263 }
264 /* notify handler thread to recreate FD set */
265 ignore_result(write(this->notify[1], buf, sizeof(buf)));
266 this->lock->unlock(this->lock);
267 return TRUE;
268 }
269
270 METHOD(kernel_libipsec_router_t, get_tun_name, char*,
271 private_kernel_libipsec_router_t *this, host_t *vip)
272 {
273 tun_entry_t *entry, lookup = {
274 .addr = vip,
275 };
276 tun_device_t *tun;
277 char *name;
278
279 if (!vip)
280 {
281 return strdup(this->tun.tun->get_name(this->tun.tun));
282 }
283 this->lock->read_lock(this->lock);
284 entry = this->tuns->get(this->tuns, &lookup);
285 tun = entry ? entry->tun : this->tun.tun;
286 name = strdup(tun->get_name(tun));
287 this->lock->unlock(this->lock);
288 return name;
289 }
290
291 METHOD(kernel_libipsec_router_t, destroy, void,
292 private_kernel_libipsec_router_t *this)
293 {
294 charon->receiver->del_esp_cb(charon->receiver,
295 (receiver_esp_cb_t)receiver_esp_cb);
296 ipsec->processor->unregister_outbound(ipsec->processor,
297 (ipsec_outbound_cb_t)send_esp);
298 ipsec->processor->unregister_inbound(ipsec->processor,
299 (ipsec_inbound_cb_t)deliver_plain);
300 charon->kernel->remove_listener(charon->kernel, &this->public.listener);
301 this->lock->destroy(this->lock);
302 this->tuns->destroy(this->tuns);
303 close(this->notify[0]);
304 close(this->notify[1]);
305 router = NULL;
306 free(this);
307 }
308
309 /**
310 * Set O_NONBLOCK on the given socket.
311 */
312 static bool set_nonblock(int socket)
313 {
314 int flags = fcntl(socket, F_GETFL);
315 return flags != -1 && fcntl(socket, F_SETFL, flags | O_NONBLOCK) != -1;
316 }
317
318 /*
319 * See header file
320 */
321 kernel_libipsec_router_t *kernel_libipsec_router_create()
322 {
323 private_kernel_libipsec_router_t *this;
324
325 INIT(this,
326 .public = {
327 .listener = {
328 .tun = _tun,
329 },
330 .get_tun_name = _get_tun_name,
331 .destroy = _destroy,
332 },
333 .tun = {
334 .tun = lib->get(lib, "kernel-libipsec-tun"),
335 }
336 );
337
338 if (pipe(this->notify) != 0 ||
339 !set_nonblock(this->notify[0]) || !set_nonblock(this->notify[1]))
340 {
341 DBG1(DBG_KNL, "creating notify pipe for kernel-libipsec router failed");
342 free(this);
343 return NULL;
344 }
345
346 this->tun.fd = this->tun.tun->get_fd(this->tun.tun);
347
348 this->tuns = hashtable_create((hashtable_hash_t)tun_entry_hash,
349 (hashtable_equals_t)tun_entry_equals, 4);
350 this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
351
352 charon->kernel->add_listener(charon->kernel, &this->public.listener);
353 ipsec->processor->register_outbound(ipsec->processor, send_esp, NULL);
354 ipsec->processor->register_inbound(ipsec->processor,
355 (ipsec_inbound_cb_t)deliver_plain, this);
356 charon->receiver->add_esp_cb(charon->receiver,
357 (receiver_esp_cb_t)receiver_esp_cb, NULL);
358 lib->processor->queue_job(lib->processor,
359 (job_t*)callback_job_create((callback_job_cb_t)handle_plain, this,
360 NULL, (callback_job_cancel_t)return_false));
361
362 router = &this->public;
363 return &this->public;
364 }