Annotation of embedaddon/strongswan/src/libcharon/plugins/kernel_iph/kernel_iph_net.c, revision 1.1.1.1
1.1 misho 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 <daemon.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: charon->kernel->roam(charon->kernel, 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, va_list args)
470: {
471: iface_t *entry;
472: host_t **host;
473:
474: VA_ARGS_VGET(args, host);
475:
476: while (TRUE)
477: {
478: while (!this->addrs)
479: {
480: if (!this->ifaces->enumerate(this->ifaces, &entry))
481: {
482: return FALSE;
483: }
484: if (entry->iftype == IF_TYPE_SOFTWARE_LOOPBACK &&
485: !(this->which & ADDR_TYPE_LOOPBACK))
486: {
487: continue;
488: }
489: if (entry->status != IfOperStatusUp &&
490: !(this->which & ADDR_TYPE_DOWN))
491: {
492: continue;
493: }
494: this->addrs = entry->addrs->create_enumerator(entry->addrs);
495: }
496: if (this->addrs->enumerate(this->addrs, host))
497: {
498: return TRUE;
499: }
500: this->addrs->destroy(this->addrs);
501: this->addrs = NULL;
502: }
503: }
504:
505: METHOD(enumerator_t, addr_destroy, void,
506: addr_enumerator_t *this)
507: {
508: DESTROY_IF(this->addrs);
509: this->ifaces->destroy(this->ifaces);
510: this->mutex->unlock(this->mutex);
511: free(this);
512: }
513:
514: METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
515: private_kernel_iph_net_t *this, kernel_address_type_t which)
516: {
517: addr_enumerator_t *enumerator;
518:
519: if (!(which & ADDR_TYPE_REGULAR))
520: {
521: /* we currently have no virtual, but regular IPs only */
522: return enumerator_create_empty();
523: }
524:
525: this->mutex->lock(this->mutex);
526:
527: INIT(enumerator,
528: .public = {
529: .enumerate = enumerator_enumerate_default,
530: .venumerate = _addr_enumerate,
531: .destroy = _addr_destroy,
532: },
533: .which = which,
534: .ifaces = this->ifaces->create_enumerator(this->ifaces),
535: .mutex = this->mutex,
536: );
537: return &enumerator->public;
538: }
539:
540: METHOD(kernel_net_t, get_source_addr, host_t*,
541: private_kernel_iph_net_t *this, host_t *dest, host_t *src)
542: {
543: MIB_IPFORWARD_ROW2 route;
544: SOCKADDR_INET best, *sai_dst, *sai_src = NULL;
545: DWORD res, index = 0;
546:
547: res = GetBestInterfaceEx(dest->get_sockaddr(dest), &index);
548: if (res != NO_ERROR)
549: {
550: DBG1(DBG_KNL, "getting interface to %H failed: 0x%08x", dest, res);
551: return NULL;
552: }
553:
554: sai_dst = (SOCKADDR_INET*)dest->get_sockaddr(dest);
555: if (src)
556: {
557: sai_src = (SOCKADDR_INET*)src->get_sockaddr(src);
558: }
559: res = GetBestRoute2(0, index, sai_src, sai_dst, 0, &route, &best);
560: if (res != NO_ERROR)
561: {
562: DBG2(DBG_KNL, "getting src address to %H failed: 0x%08x", dest, res);
563: return NULL;
564: }
565: return host_create_from_sockaddr((struct sockaddr*)&best);
566: }
567:
568: METHOD(kernel_net_t, get_nexthop, host_t*,
569: private_kernel_iph_net_t *this, host_t *dest, int prefix, host_t *src,
570: char **iface)
571: {
572: MIB_IPFORWARD_ROW2 route;
573: SOCKADDR_INET best, *sai_dst, *sai_src = NULL;
574: DWORD res, index = 0;
575: host_t *nexthop;
576:
577: res = GetBestInterfaceEx(dest->get_sockaddr(dest), &index);
578: if (res != NO_ERROR)
579: {
580: DBG1(DBG_KNL, "getting interface to %H failed: 0x%08x", dest, res);
581: return NULL;
582: }
583:
584: sai_dst = (SOCKADDR_INET*)dest->get_sockaddr(dest);
585: if (src)
586: {
587: sai_src = (SOCKADDR_INET*)src->get_sockaddr(src);
588: }
589: res = GetBestRoute2(0, index, sai_src, sai_dst, 0, &route, &best);
590: if (res != NO_ERROR)
591: {
592: DBG2(DBG_KNL, "getting nexthop to %H failed: 0x%08x", dest, res);
593: return NULL;
594: }
595: nexthop = host_create_from_sockaddr((struct sockaddr*)&route.NextHop);
596: if (nexthop)
597: {
598: if (!nexthop->is_anyaddr(nexthop))
599: {
600: if (iface)
601: {
602: *iface = NULL;
603: }
604: return nexthop;
605: }
606: nexthop->destroy(nexthop);
607: }
608: return NULL;
609: }
610:
611: METHOD(kernel_net_t, add_ip, status_t,
612: private_kernel_iph_net_t *this, host_t *virtual_ip, int prefix,
613: char *iface_name)
614: {
615: return NOT_SUPPORTED;
616: }
617:
618: METHOD(kernel_net_t, del_ip, status_t,
619: private_kernel_iph_net_t *this, host_t *virtual_ip, int prefix,
620: bool wait)
621: {
622: return NOT_SUPPORTED;
623: }
624:
625: /**
626: * Add or remove a route
627: */
628: static status_t manage_route(private_kernel_iph_net_t *this, bool add,
629: chunk_t dst, uint8_t prefixlen, host_t *gtw, char *name)
630: {
631: MIB_IPFORWARD_ROW2 row = {
632: .DestinationPrefix = {
633: .PrefixLength = prefixlen,
634: },
635: .SitePrefixLength = prefixlen,
636: .ValidLifetime = INFINITE,
637: .PreferredLifetime = INFINITE,
638: .Metric = 10,
639: .Protocol = MIB_IPPROTO_NETMGMT,
640: };
641: enumerator_t *enumerator;
642: iface_t *entry;
643: ULONG ret;
644:
645: this->mutex->lock(this->mutex);
646: enumerator = this->ifaces->create_enumerator(this->ifaces);
647: while (enumerator->enumerate(enumerator, &entry))
648: {
649: if (streq(name, entry->ifname))
650: {
651: row.InterfaceIndex = entry->ifindex;
652: break;
653: }
654: }
655: enumerator->destroy(enumerator);
656: this->mutex->unlock(this->mutex);
657:
658: if (!row.InterfaceIndex)
659: {
660: return NOT_FOUND;
661: }
662: switch (dst.len)
663: {
664: case 4:
665: row.DestinationPrefix.Prefix.si_family = AF_INET;
666: memcpy(&row.DestinationPrefix.Prefix.Ipv4.sin_addr,
667: dst.ptr, dst.len);
668: break;
669: case 16:
670: row.DestinationPrefix.Prefix.si_family = AF_INET6;
671: memcpy(&row.DestinationPrefix.Prefix.Ipv6.sin6_addr,
672: dst.ptr, dst.len);
673: break;
674: default:
675: return FAILED;
676: }
677: if (gtw)
678: {
679: memcpy(&row.NextHop, gtw->get_sockaddr(gtw),
680: *gtw->get_sockaddr_len(gtw));
681: }
682:
683: if (add)
684: {
685: ret = CreateIpForwardEntry2(&row);
686: }
687: else
688: {
689: ret = DeleteIpForwardEntry2(&row);
690: }
691: if (ret != NO_ERROR)
692: {
693: DBG1(DBG_KNL, "%sing route failed: 0x%08lx", add ? "add" : "remov", ret);
694: return FAILED;
695: }
696:
697: if (add)
698: {
699: ret = EnableRouter(NULL, &this->router);
700: if (ret != ERROR_IO_PENDING)
701: {
702: DBG1(DBG_KNL, "EnableRouter router failed: 0x%08lx", ret);
703: }
704: }
705: else
706: {
707: ret = UnenableRouter(&this->router, NULL);
708: if (ret != NO_ERROR)
709: {
710: DBG1(DBG_KNL, "UnenableRouter router failed: 0x%08lx", ret);
711: }
712: }
713: return SUCCESS;
714: }
715:
716: METHOD(kernel_net_t, add_route, status_t,
717: private_kernel_iph_net_t *this, chunk_t dst, uint8_t prefixlen,
718: host_t *gateway, host_t *src, char *name, bool pass)
719: {
720: return manage_route(this, TRUE, dst, prefixlen, gateway, name);
721: }
722:
723: METHOD(kernel_net_t, del_route, status_t,
724: private_kernel_iph_net_t *this, chunk_t dst, uint8_t prefixlen,
725: host_t *gateway, host_t *src, char *name, bool pass)
726: {
727: return manage_route(this, FALSE, dst, prefixlen, gateway, name);
728: }
729:
730: METHOD(kernel_net_t, destroy, void,
731: private_kernel_iph_net_t *this)
732: {
733: if (this->changes)
734: {
735: CancelMibChangeNotify2(this->changes);
736: }
737: CloseHandle(this->router.hEvent);
738: this->mutex->destroy(this->mutex);
739: this->ifaces->destroy_function(this->ifaces, (void*)iface_destroy);
740: free(this);
741: }
742:
743: /*
744: * Described in header.
745: */
746: kernel_iph_net_t *kernel_iph_net_create()
747: {
748: private_kernel_iph_net_t *this;
749: ULONG res;
750:
751: INIT(this,
752: .public = {
753: .interface = {
754: .get_interface = _get_interface_name,
755: .create_address_enumerator = _create_address_enumerator,
756: .get_source_addr = _get_source_addr,
757: .get_nexthop = _get_nexthop,
758: .add_ip = _add_ip,
759: .del_ip = _del_ip,
760: .add_route = _add_route,
761: .del_route = _del_route,
762: .destroy = _destroy,
763: },
764: },
765: .router = {
766: .hEvent = CreateEvent(NULL, FALSE, FALSE, NULL),
767: },
768: .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
769: .ifaces = linked_list_create(),
770: );
771: /* PIPINTERFACE_CHANGE_CALLBACK is not using WINAPI in MinGW, which seems
772: * to be wrong. Force a cast to our WINAPI call */
773: res = NotifyIpInterfaceChange(AF_UNSPEC, (void*)change_interface,
774: this, TRUE, &this->changes);
775: if (res != NO_ERROR)
776: {
777: DBG1(DBG_KNL, "registering for IPH interface changes failed: 0x%08lx",
778: res);
779: destroy(this);
780: return NULL;
781: }
782:
783: return &this->public;
784: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>