a4be4041e69f942f32b73405f17fdb12e5131020
[strongswan.git] / src / libcharon / plugins / kernel_iph / kernel_iph_net.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
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 /* Windows 7, for some iphlpapi.h functionality */
17 #define _WIN32_WINNT 0x0601
18 #include <winsock2.h>
19 #include <ws2ipdef.h>
20 #include <windows.h>
21 #include <ntddndis.h>
22 #include <naptypes.h>
23 #include <iphlpapi.h>
24
25 #include "kernel_iph_net.h"
26
27 #include <hydra.h>
28 #include <threading/mutex.h>
29 #include <collections/linked_list.h>
30 #include <processing/jobs/callback_job.h>
31
32
33 /** delay before firing roam events (ms) */
34 #define ROAM_DELAY 500
35
36 typedef struct private_kernel_iph_net_t private_kernel_iph_net_t;
37
38 /**
39 * Private data of kernel_iph_net implementation.
40 */
41 struct private_kernel_iph_net_t {
42
43 /**
44 * Public interface.
45 */
46 kernel_iph_net_t public;
47
48 /**
49 * NotifyIpInterfaceChange() handle
50 */
51 HANDLE changes;
52
53 /**
54 * EnableRouter() OVERLAPPED
55 */
56 OVERLAPPED router;
57
58 /**
59 * Mutex to access interface list
60 */
61 mutex_t *mutex;
62
63 /**
64 * Known interfaces, as iface_t
65 */
66 linked_list_t *ifaces;
67
68 /**
69 * Earliest time of the next roam event
70 */
71 timeval_t roam_next;
72
73 /**
74 * Roam event due to address change?
75 */
76 bool roam_address;
77 };
78
79 /**
80 * Interface entry
81 */
82 typedef struct {
83 /** interface index */
84 DWORD ifindex;
85 /** interface name */
86 char *ifname;
87 /** interface description */
88 char *ifdesc;
89 /** type of interface */
90 DWORD iftype;
91 /** interface status */
92 IF_OPER_STATUS status;
93 /** list of known addresses, as host_t */
94 linked_list_t *addrs;
95 } iface_t;
96
97 /**
98 * Clean up an iface_t
99 */
100 static void iface_destroy(iface_t *this)
101 {
102 this->addrs->destroy_offset(this->addrs, offsetof(host_t, destroy));
103 free(this->ifname);
104 free(this->ifdesc);
105 free(this);
106 }
107
108 /**
109 * Enum names for Windows IF_OPER_STATUS
110 */
111 ENUM(if_oper_names, IfOperStatusUp, IfOperStatusLowerLayerDown,
112 "Up",
113 "Down",
114 "Testing",
115 "Unknown",
116 "Dormant",
117 "NotPresent",
118 "LowerLayerDown",
119 );
120
121 /**
122 * Callback function that raises the delayed roam event
123 */
124 static job_requeue_t roam_event(private_kernel_iph_net_t *this)
125 {
126 bool address;
127
128 this->mutex->lock(this->mutex);
129 address = this->roam_address;
130 this->roam_address = FALSE;
131 this->mutex->unlock(this->mutex);
132
133 hydra->kernel_interface->roam(hydra->kernel_interface, address);
134 return JOB_REQUEUE_NONE;
135 }
136
137 /**
138 * Fire delayed roam event, caller should hold mutex
139 */
140 static void fire_roam_event(private_kernel_iph_net_t *this, bool address)
141 {
142 timeval_t now;
143
144 time_monotonic(&now);
145 this->roam_address |= address;
146 if (timercmp(&now, &this->roam_next, >))
147 {
148 timeval_add_ms(&now, ROAM_DELAY);
149 this->roam_next = now;
150 lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)
151 callback_job_create((callback_job_cb_t)roam_event,
152 this, NULL, NULL),
153 ROAM_DELAY);
154 }
155 }
156
157 /**
158 * Update addresses for an iface entry
159 */
160 static void update_addrs(private_kernel_iph_net_t *this, iface_t *entry,
161 IP_ADAPTER_ADDRESSES *addr, bool log)
162 {
163 IP_ADAPTER_UNICAST_ADDRESS *current;
164 enumerator_t *enumerator;
165 linked_list_t *list;
166 host_t *host, *old;
167 bool changes = FALSE;
168
169 list = entry->addrs;
170 entry->addrs = linked_list_create();
171
172 for (current = addr->FirstUnicastAddress; current; current = current->Next)
173 {
174 if (current->Address.lpSockaddr->sa_family == AF_INET6)
175 {
176 struct sockaddr_in6 *sin;
177
178 sin = (struct sockaddr_in6*)current->Address.lpSockaddr;
179 if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
180 {
181 continue;
182 }
183 }
184
185 host = host_create_from_sockaddr(current->Address.lpSockaddr);
186 if (host)
187 {
188 bool found = FALSE;
189
190 enumerator = list->create_enumerator(list);
191 while (enumerator->enumerate(enumerator, &old))
192 {
193 if (host->ip_equals(host, old))
194 {
195 list->remove_at(list, enumerator);
196 old->destroy(old);
197 found = TRUE;
198 }
199 }
200 enumerator->destroy(enumerator);
201
202 entry->addrs->insert_last(entry->addrs, host);
203
204 if (!found && log)
205 {
206 DBG1(DBG_KNL, "%H appeared on interface %u '%s'",
207 host, entry->ifindex, entry->ifdesc);
208 changes = TRUE;
209 }
210 }
211 }
212
213 while (list->remove_first(list, (void**)&old) == SUCCESS)
214 {
215 if (log)
216 {
217 DBG1(DBG_KNL, "%H disappeared from interface %u '%s'",
218 old, entry->ifindex, entry->ifdesc);
219 changes = TRUE;
220 }
221 old->destroy(old);
222 }
223 list->destroy(list);
224
225 if (changes)
226 {
227 fire_roam_event(this, TRUE);
228 }
229 }
230
231 /**
232 * Add an interface entry
233 */
234 static void add_interface(private_kernel_iph_net_t *this,
235 IP_ADAPTER_ADDRESSES *addr, bool log)
236 {
237 enumerator_t *enumerator;
238 iface_t *entry;
239 bool exists = FALSE;
240
241 this->mutex->lock(this->mutex);
242 enumerator = this->ifaces->create_enumerator(this->ifaces);
243 while (enumerator->enumerate(enumerator, &entry))
244 {
245 if (entry->ifindex == addr->IfIndex)
246 {
247 exists = TRUE;
248 break;
249 }
250 }
251 enumerator->destroy(enumerator);
252 this->mutex->unlock(this->mutex);
253
254 if (!exists)
255 {
256 char desc[128] = "";
257
258 wcstombs(desc, addr->Description, sizeof(desc));
259
260 INIT(entry,
261 .ifindex = addr->IfIndex,
262 .ifname = strdup(addr->AdapterName),
263 .ifdesc = strdup(desc),
264 .iftype = addr->IfType,
265 .status = addr->OperStatus,
266 .addrs = linked_list_create(),
267 );
268
269 if (log)
270 {
271 DBG1(DBG_KNL, "interface %u '%s' appeared",
272 entry->ifindex, entry->ifdesc);
273 }
274
275 this->mutex->lock(this->mutex);
276 update_addrs(this, entry, addr, log);
277 this->ifaces->insert_last(this->ifaces, entry);
278 this->mutex->unlock(this->mutex);
279 }
280 }
281
282 /**
283 * Remove an interface entry that is gone
284 */
285 static void remove_interface(private_kernel_iph_net_t *this, NET_IFINDEX index)
286 {
287 enumerator_t *enumerator;
288 iface_t *entry;
289
290 this->mutex->lock(this->mutex);
291 enumerator = this->ifaces->create_enumerator(this->ifaces);
292 while (enumerator->enumerate(enumerator, &entry))
293 {
294 if (entry->ifindex == index)
295 {
296 this->ifaces->remove_at(this->ifaces, enumerator);
297 DBG1(DBG_KNL, "interface %u '%s' disappeared",
298 entry->ifindex, entry->ifdesc);
299 iface_destroy(entry);
300 fire_roam_event(this, TRUE);
301 }
302 }
303 enumerator->destroy(enumerator);
304 this->mutex->unlock(this->mutex);
305 }
306
307 /**
308 * Update an interface entry changed
309 */
310 static void update_interface(private_kernel_iph_net_t *this,
311 IP_ADAPTER_ADDRESSES *addr)
312 {
313 enumerator_t *enumerator;
314 iface_t *entry;
315
316 this->mutex->lock(this->mutex);
317 enumerator = this->ifaces->create_enumerator(this->ifaces);
318 while (enumerator->enumerate(enumerator, &entry))
319 {
320 if (entry->ifindex == addr->IfIndex)
321 {
322 if (entry->status != addr->OperStatus)
323 {
324 DBG1(DBG_KNL, "interface %u '%s' changed state from %N to %N",
325 entry->ifindex, entry->ifdesc, if_oper_names,
326 entry->status, if_oper_names, addr->OperStatus);
327 entry->status = addr->OperStatus;
328 fire_roam_event(this, TRUE);
329 }
330 update_addrs(this, entry, addr, TRUE);
331 }
332 }
333 enumerator->destroy(enumerator);
334 this->mutex->unlock(this->mutex);
335 }
336
337 /**
338 * MinGW gets MIB_IPINTERFACE_ROW wrong, as it packs InterfaceLuid just after
339 * Family. Fix that with our own version of the struct header.
340 */
341 typedef struct {
342 ADDRESS_FAMILY Family;
343 union {
344 ULONG64 Value;
345 struct {
346 ULONG64 Reserved :24;
347 ULONG64 NetLuidIndex :24;
348 ULONG64 IfType :16;
349 } Info;
350 } InterfaceLuid;
351 NET_IFINDEX InterfaceIndex;
352 /* more would go here if needed */
353 } MIB_IPINTERFACE_ROW_FIXUP;
354
355 /**
356 * NotifyIpInterfaceChange() callback
357 */
358 static void WINAPI change_interface(void *user, PMIB_IPINTERFACE_ROW row_badal,
359 MIB_NOTIFICATION_TYPE type)
360 {
361 private_kernel_iph_net_t *this = user;
362 MIB_IPINTERFACE_ROW_FIXUP* row = (MIB_IPINTERFACE_ROW_FIXUP*)row_badal;
363 IP_ADAPTER_ADDRESSES addrs[64], *current;
364 ULONG res, size = sizeof(addrs);
365
366 if (row && type == MibDeleteInstance)
367 {
368 remove_interface(this, row->InterfaceIndex);
369 }
370 else
371 {
372 res = GetAdaptersAddresses(AF_UNSPEC,
373 GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
374 GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
375 NULL, addrs, &size);
376 if (res == NO_ERROR)
377 {
378 current = addrs;
379 while (current)
380 {
381 /* row is NULL only on MibInitialNotification */
382 if (!row || row->InterfaceIndex == current->IfIndex)
383 {
384 switch (type)
385 {
386 case MibParameterNotification:
387 update_interface(this, current);
388 break;
389 case MibInitialNotification:
390 add_interface(this, current, FALSE);
391 break;
392 case MibAddInstance:
393 add_interface(this, current, TRUE);
394 break;
395 default:
396 break;
397 }
398 }
399 current = current->Next;
400 }
401 }
402 else
403 {
404 DBG1(DBG_KNL, "getting IPH adapter addresses failed: 0x%08lx", res);
405 }
406 }
407 }
408
409 /**
410 * Get an iface entry for a local address, does no locking
411 */
412 static iface_t* address2entry(private_kernel_iph_net_t *this, host_t *ip)
413 {
414 enumerator_t *ifaces, *addrs;
415 iface_t *entry, *found = NULL;
416 host_t *host;
417
418 ifaces = this->ifaces->create_enumerator(this->ifaces);
419 while (!found && ifaces->enumerate(ifaces, &entry))
420 {
421 addrs = entry->addrs->create_enumerator(entry->addrs);
422 while (!found && addrs->enumerate(addrs, &host))
423 {
424 if (host->ip_equals(host, ip))
425 {
426 found = entry;
427 }
428 }
429 addrs->destroy(addrs);
430 }
431 ifaces->destroy(ifaces);
432
433 return found;
434 }
435
436 METHOD(kernel_net_t, get_interface_name, bool,
437 private_kernel_iph_net_t *this, host_t* ip, char **name)
438 {
439 iface_t *entry;
440
441 this->mutex->lock(this->mutex);
442 entry = address2entry(this, ip);
443 if (entry && name)
444 {
445 *name = strdup(entry->ifname);
446 }
447 this->mutex->unlock(this->mutex);
448
449 return entry != NULL;
450 }
451
452 /**
453 * Address enumerator
454 */
455 typedef struct {
456 /** implements enumerator_t */
457 enumerator_t public;
458 /** what kind of address should we enumerate? */
459 kernel_address_type_t which;
460 /** enumerator over interfaces */
461 enumerator_t *ifaces;
462 /** current enumerator over addresses, or NULL */
463 enumerator_t *addrs;
464 /** mutex to unlock on destruction */
465 mutex_t *mutex;
466 } addr_enumerator_t;
467
468 METHOD(enumerator_t, addr_enumerate, bool,
469 addr_enumerator_t *this, host_t **host)
470 {
471 iface_t *entry;
472
473 while (TRUE)
474 {
475 while (!this->addrs)
476 {
477 if (!this->ifaces->enumerate(this->ifaces, &entry))
478 {
479 return FALSE;
480 }
481 if (entry->iftype == IF_TYPE_SOFTWARE_LOOPBACK &&
482 !(this->which & ADDR_TYPE_LOOPBACK))
483 {
484 continue;
485 }
486 if (entry->status != IfOperStatusUp &&
487 !(this->which & ADDR_TYPE_DOWN))
488 {
489 continue;
490 }
491 this->addrs = entry->addrs->create_enumerator(entry->addrs);
492 }
493 if (this->addrs->enumerate(this->addrs, host))
494 {
495 return TRUE;
496 }
497 this->addrs->destroy(this->addrs);
498 this->addrs = NULL;
499 }
500 }
501
502 METHOD(enumerator_t, addr_destroy, void,
503 addr_enumerator_t *this)
504 {
505 DESTROY_IF(this->addrs);
506 this->ifaces->destroy(this->ifaces);
507 this->mutex->unlock(this->mutex);
508 free(this);
509 }
510
511 METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
512 private_kernel_iph_net_t *this, kernel_address_type_t which)
513 {
514 addr_enumerator_t *enumerator;
515
516 if (!(which & ADDR_TYPE_REGULAR))
517 {
518 /* we currently have no virtual, but regular IPs only */
519 return enumerator_create_empty();
520 }
521
522 this->mutex->lock(this->mutex);
523
524 INIT(enumerator,
525 .public = {
526 .enumerate = (void*)_addr_enumerate,
527 .destroy = _addr_destroy,
528 },
529 .which = which,
530 .ifaces = this->ifaces->create_enumerator(this->ifaces),
531 .mutex = this->mutex,
532 );
533 return &enumerator->public;
534 }
535
536 METHOD(kernel_net_t, get_source_addr, host_t*,
537 private_kernel_iph_net_t *this, host_t *dest, host_t *src)
538 {
539 MIB_IPFORWARD_ROW2 route;
540 SOCKADDR_INET best, *sai_dst, *sai_src = NULL;
541 DWORD res, index = 0;
542
543 res = GetBestInterfaceEx(dest->get_sockaddr(dest), &index);
544 if (res != NO_ERROR)
545 {
546 DBG1(DBG_KNL, "getting interface to %H failed: 0x%08x", dest, res);
547 return NULL;
548 }
549
550 sai_dst = (SOCKADDR_INET*)dest->get_sockaddr(dest);
551 if (src)
552 {
553 sai_src = (SOCKADDR_INET*)src->get_sockaddr(src);
554 }
555 res = GetBestRoute2(0, index, sai_src, sai_dst, 0, &route, &best);
556 if (res != NO_ERROR)
557 {
558 DBG2(DBG_KNL, "getting src address to %H failed: 0x%08x", dest, res);
559 return NULL;
560 }
561 return host_create_from_sockaddr((struct sockaddr*)&best);
562 }
563
564 METHOD(kernel_net_t, get_nexthop, host_t*,
565 private_kernel_iph_net_t *this, host_t *dest, int prefix, host_t *src)
566 {
567 MIB_IPFORWARD_ROW2 route;
568 SOCKADDR_INET best, *sai_dst, *sai_src = NULL;
569 DWORD res, index = 0;
570 host_t *nexthop;
571
572 res = GetBestInterfaceEx(dest->get_sockaddr(dest), &index);
573 if (res != NO_ERROR)
574 {
575 DBG1(DBG_KNL, "getting interface to %H failed: 0x%08x", dest, res);
576 return NULL;
577 }
578
579 sai_dst = (SOCKADDR_INET*)dest->get_sockaddr(dest);
580 if (src)
581 {
582 sai_src = (SOCKADDR_INET*)src->get_sockaddr(src);
583 }
584 res = GetBestRoute2(0, index, sai_src, sai_dst, 0, &route, &best);
585 if (res != NO_ERROR)
586 {
587 DBG2(DBG_KNL, "getting nexthop to %H failed: 0x%08x", dest, res);
588 return NULL;
589 }
590 nexthop = host_create_from_sockaddr((struct sockaddr*)&route.NextHop);
591 if (nexthop)
592 {
593 if (!nexthop->is_anyaddr(nexthop))
594 {
595 return nexthop;
596 }
597 nexthop->destroy(nexthop);
598 }
599 return NULL;
600 }
601
602 METHOD(kernel_net_t, add_ip, status_t,
603 private_kernel_iph_net_t *this, host_t *virtual_ip, int prefix,
604 char *iface_name)
605 {
606 return NOT_SUPPORTED;
607 }
608
609 METHOD(kernel_net_t, del_ip, status_t,
610 private_kernel_iph_net_t *this, host_t *virtual_ip, int prefix,
611 bool wait)
612 {
613 return NOT_SUPPORTED;
614 }
615
616 /**
617 * Add or remove a route
618 */
619 static status_t manage_route(private_kernel_iph_net_t *this, bool add,
620 chunk_t dst, u_int8_t prefixlen, host_t *gtw, char *name)
621 {
622 MIB_IPFORWARD_ROW2 row = {
623 .DestinationPrefix = {
624 .PrefixLength = prefixlen,
625 },
626 .SitePrefixLength = prefixlen,
627 .ValidLifetime = INFINITE,
628 .PreferredLifetime = INFINITE,
629 .Metric = 10,
630 .Protocol = MIB_IPPROTO_NETMGMT,
631 };
632 enumerator_t *enumerator;
633 iface_t *entry;
634 ULONG ret;
635
636 this->mutex->lock(this->mutex);
637 enumerator = this->ifaces->create_enumerator(this->ifaces);
638 while (enumerator->enumerate(enumerator, &entry))
639 {
640 if (streq(name, entry->ifname))
641 {
642 row.InterfaceIndex = entry->ifindex;
643 break;
644 }
645 }
646 enumerator->destroy(enumerator);
647 this->mutex->unlock(this->mutex);
648
649 if (!row.InterfaceIndex)
650 {
651 return NOT_FOUND;
652 }
653 switch (dst.len)
654 {
655 case 4:
656 row.DestinationPrefix.Prefix.si_family = AF_INET;
657 memcpy(&row.DestinationPrefix.Prefix.Ipv4.sin_addr,
658 dst.ptr, dst.len);
659 break;
660 case 16:
661 row.DestinationPrefix.Prefix.si_family = AF_INET6;
662 memcpy(&row.DestinationPrefix.Prefix.Ipv6.sin6_addr,
663 dst.ptr, dst.len);
664 break;
665 default:
666 return FAILED;
667 }
668 if (gtw)
669 {
670 memcpy(&row.NextHop, gtw->get_sockaddr(gtw),
671 *gtw->get_sockaddr_len(gtw));
672 }
673
674 if (add)
675 {
676 ret = CreateIpForwardEntry2(&row);
677 }
678 else
679 {
680 ret = DeleteIpForwardEntry2(&row);
681 }
682 if (ret != NO_ERROR)
683 {
684 DBG1(DBG_KNL, "%sing route failed: 0x%08lx", add ? "add" : "remov", ret);
685 return FAILED;
686 }
687
688 if (add)
689 {
690 ret = EnableRouter(NULL, &this->router);
691 if (ret != ERROR_IO_PENDING)
692 {
693 DBG1(DBG_KNL, "EnableRouter router failed: 0x%08lx", ret);
694 }
695 }
696 else
697 {
698 ret = UnenableRouter(&this->router, NULL);
699 if (ret != NO_ERROR)
700 {
701 DBG1(DBG_KNL, "UnenableRouter router failed: 0x%08lx", ret);
702 }
703 }
704 return SUCCESS;
705 }
706
707 METHOD(kernel_net_t, add_route, status_t,
708 private_kernel_iph_net_t *this, chunk_t dst, u_int8_t prefixlen,
709 host_t *gateway, host_t *src, char *name)
710 {
711 return manage_route(this, TRUE, dst, prefixlen, gateway, name);
712 }
713
714 METHOD(kernel_net_t, del_route, status_t,
715 private_kernel_iph_net_t *this, chunk_t dst, u_int8_t prefixlen,
716 host_t *gateway, host_t *src, char *name)
717 {
718 return manage_route(this, FALSE, dst, prefixlen, gateway, name);
719 }
720
721 METHOD(kernel_net_t, destroy, void,
722 private_kernel_iph_net_t *this)
723 {
724 if (this->changes)
725 {
726 CancelMibChangeNotify2(this->changes);
727 }
728 CloseHandle(this->router.hEvent);
729 this->mutex->destroy(this->mutex);
730 this->ifaces->destroy_function(this->ifaces, (void*)iface_destroy);
731 free(this);
732 }
733
734 /*
735 * Described in header.
736 */
737 kernel_iph_net_t *kernel_iph_net_create()
738 {
739 private_kernel_iph_net_t *this;
740 ULONG res;
741
742 INIT(this,
743 .public = {
744 .interface = {
745 .get_interface = _get_interface_name,
746 .create_address_enumerator = _create_address_enumerator,
747 .get_source_addr = _get_source_addr,
748 .get_nexthop = _get_nexthop,
749 .add_ip = _add_ip,
750 .del_ip = _del_ip,
751 .add_route = _add_route,
752 .del_route = _del_route,
753 .destroy = _destroy,
754 },
755 },
756 .router = {
757 .hEvent = CreateEvent(NULL, FALSE, FALSE, NULL),
758 },
759 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
760 .ifaces = linked_list_create(),
761 );
762 /* PIPINTERFACE_CHANGE_CALLBACK is not using WINAPI in MinGW, which seems
763 * to be wrong. Force a cast to our WINAPI call */
764 res = NotifyIpInterfaceChange(AF_UNSPEC, (void*)change_interface,
765 this, TRUE, &this->changes);
766 if (res != NO_ERROR)
767 {
768 DBG1(DBG_KNL, "registering for IPH interface changes failed: 0x%08lx",
769 res);
770 destroy(this);
771 return NULL;
772 }
773
774 return &this->public;
775 }