2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
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>.
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
16 /* Windows 7, for some iphlpapi.h functionality */
17 #define _WIN32_WINNT 0x0601
25 #include "kernel_iph_net.h"
28 #include <threading/mutex.h>
29 #include <collections/linked_list.h>
30 #include <processing/jobs/callback_job.h>
33 /** delay before firing roam events (ms) */
34 #define ROAM_DELAY 500
36 typedef struct private_kernel_iph_net_t private_kernel_iph_net_t
;
39 * Private data of kernel_iph_net implementation.
41 struct private_kernel_iph_net_t
{
46 kernel_iph_net_t
public;
49 * NotifyIpInterfaceChange() handle
54 * EnableRouter() OVERLAPPED
59 * Mutex to access interface list
64 * Known interfaces, as iface_t
66 linked_list_t
*ifaces
;
69 * Earliest time of the next roam event
74 * Roam event due to address change?
83 /** interface index */
87 /** interface description */
89 /** type of interface */
91 /** interface status */
92 IF_OPER_STATUS status
;
93 /** list of known addresses, as host_t */
100 static void iface_destroy(iface_t
*this)
102 this->addrs
->destroy_offset(this->addrs
, offsetof(host_t
, destroy
));
109 * Enum names for Windows IF_OPER_STATUS
111 ENUM(if_oper_names
, IfOperStatusUp
, IfOperStatusLowerLayerDown
,
122 * Callback function that raises the delayed roam event
124 static job_requeue_t
roam_event(private_kernel_iph_net_t
*this)
128 this->mutex
->lock(this->mutex
);
129 address
= this->roam_address
;
130 this->roam_address
= FALSE
;
131 this->mutex
->unlock(this->mutex
);
133 hydra
->kernel_interface
->roam(hydra
->kernel_interface
, address
);
134 return JOB_REQUEUE_NONE
;
138 * Fire delayed roam event, caller should hold mutex
140 static void fire_roam_event(private_kernel_iph_net_t
*this, bool address
)
144 time_monotonic(&now
);
145 this->roam_address
|= address
;
146 if (timercmp(&now
, &this->roam_next
, >))
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
,
158 * Update addresses for an iface entry
160 static void update_addrs(private_kernel_iph_net_t
*this, iface_t
*entry
,
161 IP_ADAPTER_ADDRESSES
*addr
, bool log
)
163 IP_ADAPTER_UNICAST_ADDRESS
*current
;
164 enumerator_t
*enumerator
;
167 bool changes
= FALSE
;
170 entry
->addrs
= linked_list_create();
172 for (current
= addr
->FirstUnicastAddress
; current
; current
= current
->Next
)
174 if (current
->Address
.lpSockaddr
->sa_family
== AF_INET6
)
176 struct sockaddr_in6
*sin
;
178 sin
= (struct sockaddr_in6
*)current
->Address
.lpSockaddr
;
179 if (IN6_IS_ADDR_LINKLOCAL(&sin
->sin6_addr
))
185 host
= host_create_from_sockaddr(current
->Address
.lpSockaddr
);
190 enumerator
= list
->create_enumerator(list
);
191 while (enumerator
->enumerate(enumerator
, &old
))
193 if (host
->ip_equals(host
, old
))
195 list
->remove_at(list
, enumerator
);
200 enumerator
->destroy(enumerator
);
202 entry
->addrs
->insert_last(entry
->addrs
, host
);
206 DBG1(DBG_KNL
, "%H appeared on interface %u '%s'",
207 host
, entry
->ifindex
, entry
->ifdesc
);
213 while (list
->remove_first(list
, (void**)&old
) == SUCCESS
)
217 DBG1(DBG_KNL
, "%H disappeared from interface %u '%s'",
218 old
, entry
->ifindex
, entry
->ifdesc
);
227 fire_roam_event(this, TRUE
);
232 * Add an interface entry
234 static void add_interface(private_kernel_iph_net_t
*this,
235 IP_ADAPTER_ADDRESSES
*addr
, bool log
)
237 enumerator_t
*enumerator
;
241 this->mutex
->lock(this->mutex
);
242 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
243 while (enumerator
->enumerate(enumerator
, &entry
))
245 if (entry
->ifindex
== addr
->IfIndex
)
251 enumerator
->destroy(enumerator
);
252 this->mutex
->unlock(this->mutex
);
258 wcstombs(desc
, addr
->Description
, sizeof(desc
));
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(),
271 DBG1(DBG_KNL
, "interface %u '%s' appeared",
272 entry
->ifindex
, entry
->ifdesc
);
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
);
283 * Remove an interface entry that is gone
285 static void remove_interface(private_kernel_iph_net_t
*this, NET_IFINDEX index
)
287 enumerator_t
*enumerator
;
290 this->mutex
->lock(this->mutex
);
291 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
292 while (enumerator
->enumerate(enumerator
, &entry
))
294 if (entry
->ifindex
== index
)
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
);
303 enumerator
->destroy(enumerator
);
304 this->mutex
->unlock(this->mutex
);
308 * Update an interface entry changed
310 static void update_interface(private_kernel_iph_net_t
*this,
311 IP_ADAPTER_ADDRESSES
*addr
)
313 enumerator_t
*enumerator
;
316 this->mutex
->lock(this->mutex
);
317 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
318 while (enumerator
->enumerate(enumerator
, &entry
))
320 if (entry
->ifindex
== addr
->IfIndex
)
322 if (entry
->status
!= addr
->OperStatus
)
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
);
330 update_addrs(this, entry
, addr
, TRUE
);
333 enumerator
->destroy(enumerator
);
334 this->mutex
->unlock(this->mutex
);
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.
342 ADDRESS_FAMILY Family
;
346 ULONG64 Reserved
:24;
347 ULONG64 NetLuidIndex
:24;
351 NET_IFINDEX InterfaceIndex
;
352 /* more would go here if needed */
353 } MIB_IPINTERFACE_ROW_FIXUP
;
356 * NotifyIpInterfaceChange() callback
358 static void WINAPI
change_interface(void *user
, PMIB_IPINTERFACE_ROW row_badal
,
359 MIB_NOTIFICATION_TYPE type
)
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
);
366 if (row
&& type
== MibDeleteInstance
)
368 remove_interface(this, row
->InterfaceIndex
);
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
,
381 /* row is NULL only on MibInitialNotification */
382 if (!row
|| row
->InterfaceIndex
== current
->IfIndex
)
386 case MibParameterNotification
:
387 update_interface(this, current
);
389 case MibInitialNotification
:
390 add_interface(this, current
, FALSE
);
393 add_interface(this, current
, TRUE
);
399 current
= current
->Next
;
404 DBG1(DBG_KNL
, "getting IPH adapter addresses failed: 0x%08lx", res
);
410 * Get an iface entry for a local address, does no locking
412 static iface_t
* address2entry(private_kernel_iph_net_t
*this, host_t
*ip
)
414 enumerator_t
*ifaces
, *addrs
;
415 iface_t
*entry
, *found
= NULL
;
418 ifaces
= this->ifaces
->create_enumerator(this->ifaces
);
419 while (!found
&& ifaces
->enumerate(ifaces
, &entry
))
421 addrs
= entry
->addrs
->create_enumerator(entry
->addrs
);
422 while (!found
&& addrs
->enumerate(addrs
, &host
))
424 if (host
->ip_equals(host
, ip
))
429 addrs
->destroy(addrs
);
431 ifaces
->destroy(ifaces
);
436 METHOD(kernel_net_t
, get_interface_name
, bool,
437 private_kernel_iph_net_t
*this, host_t
* ip
, char **name
)
441 this->mutex
->lock(this->mutex
);
442 entry
= address2entry(this, ip
);
445 *name
= strdup(entry
->ifname
);
447 this->mutex
->unlock(this->mutex
);
449 return entry
!= NULL
;
456 /** implements enumerator_t */
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 */
464 /** mutex to unlock on destruction */
468 METHOD(enumerator_t
, addr_enumerate
, bool,
469 addr_enumerator_t
*this, host_t
**host
)
477 if (!this->ifaces
->enumerate(this->ifaces
, &entry
))
481 if (entry
->iftype
== IF_TYPE_SOFTWARE_LOOPBACK
&&
482 !(this->which
& ADDR_TYPE_LOOPBACK
))
486 if (entry
->status
!= IfOperStatusUp
&&
487 !(this->which
& ADDR_TYPE_DOWN
))
491 this->addrs
= entry
->addrs
->create_enumerator(entry
->addrs
);
493 if (this->addrs
->enumerate(this->addrs
, host
))
497 this->addrs
->destroy(this->addrs
);
502 METHOD(enumerator_t
, addr_destroy
, void,
503 addr_enumerator_t
*this)
505 DESTROY_IF(this->addrs
);
506 this->ifaces
->destroy(this->ifaces
);
507 this->mutex
->unlock(this->mutex
);
511 METHOD(kernel_net_t
, create_address_enumerator
, enumerator_t
*,
512 private_kernel_iph_net_t
*this, kernel_address_type_t which
)
514 addr_enumerator_t
*enumerator
;
516 if (!(which
& ADDR_TYPE_REGULAR
))
518 /* we currently have no virtual, but regular IPs only */
519 return enumerator_create_empty();
522 this->mutex
->lock(this->mutex
);
526 .enumerate
= (void*)_addr_enumerate
,
527 .destroy
= _addr_destroy
,
530 .ifaces
= this->ifaces
->create_enumerator(this->ifaces
),
531 .mutex
= this->mutex
,
533 return &enumerator
->public;
536 METHOD(kernel_net_t
, get_source_addr
, host_t
*,
537 private_kernel_iph_net_t
*this, host_t
*dest
, host_t
*src
)
539 MIB_IPFORWARD_ROW2 route
;
540 SOCKADDR_INET best
, *sai_dst
, *sai_src
= NULL
;
541 DWORD res
, index
= 0;
543 res
= GetBestInterfaceEx(dest
->get_sockaddr(dest
), &index
);
546 DBG1(DBG_KNL
, "getting interface to %H failed: 0x%08x", dest
, res
);
550 sai_dst
= (SOCKADDR_INET
*)dest
->get_sockaddr(dest
);
553 sai_src
= (SOCKADDR_INET
*)src
->get_sockaddr(src
);
555 res
= GetBestRoute2(0, index
, sai_src
, sai_dst
, 0, &route
, &best
);
558 DBG2(DBG_KNL
, "getting src address to %H failed: 0x%08x", dest
, res
);
561 return host_create_from_sockaddr((struct sockaddr
*)&best
);
564 METHOD(kernel_net_t
, get_nexthop
, host_t
*,
565 private_kernel_iph_net_t
*this, host_t
*dest
, host_t
*src
)
567 MIB_IPFORWARD_ROW2 route
;
568 SOCKADDR_INET best
, *sai_dst
, *sai_src
= NULL
;
569 DWORD res
, index
= 0;
572 res
= GetBestInterfaceEx(dest
->get_sockaddr(dest
), &index
);
575 DBG1(DBG_KNL
, "getting interface to %H failed: 0x%08x", dest
, res
);
579 sai_dst
= (SOCKADDR_INET
*)dest
->get_sockaddr(dest
);
582 sai_src
= (SOCKADDR_INET
*)src
->get_sockaddr(src
);
584 res
= GetBestRoute2(0, index
, sai_src
, sai_dst
, 0, &route
, &best
);
587 DBG2(DBG_KNL
, "getting nexthop to %H failed: 0x%08x", dest
, res
);
590 nexthop
= host_create_from_sockaddr((struct sockaddr
*)&route
.NextHop
);
593 if (!nexthop
->is_anyaddr(nexthop
))
597 nexthop
->destroy(nexthop
);
602 METHOD(kernel_net_t
, add_ip
, status_t
,
603 private_kernel_iph_net_t
*this, host_t
*virtual_ip
, int prefix
,
606 return NOT_SUPPORTED
;
609 METHOD(kernel_net_t
, del_ip
, status_t
,
610 private_kernel_iph_net_t
*this, host_t
*virtual_ip
, int prefix
,
613 return NOT_SUPPORTED
;
617 * Add or remove a route
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
)
622 MIB_IPFORWARD_ROW2 row
= {
623 .DestinationPrefix
= {
624 .PrefixLength
= prefixlen
,
626 .SitePrefixLength
= prefixlen
,
627 .ValidLifetime
= INFINITE
,
628 .PreferredLifetime
= INFINITE
,
630 .Protocol
= MIB_IPPROTO_NETMGMT
,
632 enumerator_t
*enumerator
;
636 this->mutex
->lock(this->mutex
);
637 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
638 while (enumerator
->enumerate(enumerator
, &entry
))
640 if (streq(name
, entry
->ifname
))
642 row
.InterfaceIndex
= entry
->ifindex
;
646 enumerator
->destroy(enumerator
);
647 this->mutex
->unlock(this->mutex
);
649 if (!row
.InterfaceIndex
)
656 row
.DestinationPrefix
.Prefix
.si_family
= AF_INET
;
657 memcpy(&row
.DestinationPrefix
.Prefix
.Ipv4
.sin_addr
,
661 row
.DestinationPrefix
.Prefix
.si_family
= AF_INET6
;
662 memcpy(&row
.DestinationPrefix
.Prefix
.Ipv6
.sin6_addr
,
670 memcpy(&row
.NextHop
, gtw
->get_sockaddr(gtw
),
671 *gtw
->get_sockaddr_len(gtw
));
676 ret
= CreateIpForwardEntry2(&row
);
680 ret
= DeleteIpForwardEntry2(&row
);
684 DBG1(DBG_KNL
, "%sing route failed: 0x%08lx", add ?
"add" : "remov", ret
);
690 ret
= EnableRouter(NULL
, &this->router
);
691 if (ret
!= ERROR_IO_PENDING
)
693 DBG1(DBG_KNL
, "EnableRouter router failed: 0x%08lx", ret
);
698 ret
= UnenableRouter(&this->router
, NULL
);
701 DBG1(DBG_KNL
, "UnenableRouter router failed: 0x%08lx", ret
);
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
)
711 return manage_route(this, TRUE
, dst
, prefixlen
, gateway
, name
);
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
)
718 return manage_route(this, FALSE
, dst
, prefixlen
, gateway
, name
);
721 METHOD(kernel_net_t
, destroy
, void,
722 private_kernel_iph_net_t
*this)
726 CancelMibChangeNotify2(this->changes
);
728 CloseHandle(this->router
.hEvent
);
729 this->mutex
->destroy(this->mutex
);
730 this->ifaces
->destroy_function(this->ifaces
, (void*)iface_destroy
);
735 * Described in header.
737 kernel_iph_net_t
*kernel_iph_net_create()
739 private_kernel_iph_net_t
*this;
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
,
751 .add_route
= _add_route
,
752 .del_route
= _del_route
,
757 .hEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
),
759 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
760 .ifaces
= linked_list_create(),
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
);
768 DBG1(DBG_KNL
, "registering for IPH interface changes failed: 0x%08lx",
774 return &this->public;