776 lines
17 KiB
C
776 lines
17 KiB
C
/*
|
|
* Copyright (C) 2013 Martin Willi
|
|
* Copyright (C) 2013 revosec AG
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
/* Windows 7, for some iphlpapi.h functionality */
|
|
#define _WIN32_WINNT 0x0601
|
|
#include <winsock2.h>
|
|
#include <ws2ipdef.h>
|
|
#include <windows.h>
|
|
#include <ntddndis.h>
|
|
#include <naptypes.h>
|
|
#include <iphlpapi.h>
|
|
|
|
#include "kernel_iph_net.h"
|
|
|
|
#include <hydra.h>
|
|
#include <threading/mutex.h>
|
|
#include <collections/linked_list.h>
|
|
#include <processing/jobs/callback_job.h>
|
|
|
|
|
|
/** delay before firing roam events (ms) */
|
|
#define ROAM_DELAY 500
|
|
|
|
typedef struct private_kernel_iph_net_t private_kernel_iph_net_t;
|
|
|
|
/**
|
|
* Private data of kernel_iph_net implementation.
|
|
*/
|
|
struct private_kernel_iph_net_t {
|
|
|
|
/**
|
|
* Public interface.
|
|
*/
|
|
kernel_iph_net_t public;
|
|
|
|
/**
|
|
* NotifyIpInterfaceChange() handle
|
|
*/
|
|
HANDLE changes;
|
|
|
|
/**
|
|
* EnableRouter() OVERLAPPED
|
|
*/
|
|
OVERLAPPED router;
|
|
|
|
/**
|
|
* Mutex to access interface list
|
|
*/
|
|
mutex_t *mutex;
|
|
|
|
/**
|
|
* Known interfaces, as iface_t
|
|
*/
|
|
linked_list_t *ifaces;
|
|
|
|
/**
|
|
* Earliest time of the next roam event
|
|
*/
|
|
timeval_t roam_next;
|
|
|
|
/**
|
|
* Roam event due to address change?
|
|
*/
|
|
bool roam_address;
|
|
};
|
|
|
|
/**
|
|
* Interface entry
|
|
*/
|
|
typedef struct {
|
|
/** interface index */
|
|
DWORD ifindex;
|
|
/** interface name */
|
|
char *ifname;
|
|
/** interface description */
|
|
char *ifdesc;
|
|
/** type of interface */
|
|
DWORD iftype;
|
|
/** interface status */
|
|
IF_OPER_STATUS status;
|
|
/** list of known addresses, as host_t */
|
|
linked_list_t *addrs;
|
|
} iface_t;
|
|
|
|
/**
|
|
* Clean up an iface_t
|
|
*/
|
|
static void iface_destroy(iface_t *this)
|
|
{
|
|
this->addrs->destroy_offset(this->addrs, offsetof(host_t, destroy));
|
|
free(this->ifname);
|
|
free(this->ifdesc);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* Enum names for Windows IF_OPER_STATUS
|
|
*/
|
|
ENUM(if_oper_names, IfOperStatusUp, IfOperStatusLowerLayerDown,
|
|
"Up",
|
|
"Down",
|
|
"Testing",
|
|
"Unknown",
|
|
"Dormant",
|
|
"NotPresent",
|
|
"LowerLayerDown",
|
|
);
|
|
|
|
/**
|
|
* Callback function that raises the delayed roam event
|
|
*/
|
|
static job_requeue_t roam_event(private_kernel_iph_net_t *this)
|
|
{
|
|
bool address;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
address = this->roam_address;
|
|
this->roam_address = FALSE;
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
hydra->kernel_interface->roam(hydra->kernel_interface, address);
|
|
return JOB_REQUEUE_NONE;
|
|
}
|
|
|
|
/**
|
|
* Fire delayed roam event, caller should hold mutex
|
|
*/
|
|
static void fire_roam_event(private_kernel_iph_net_t *this, bool address)
|
|
{
|
|
timeval_t now;
|
|
|
|
time_monotonic(&now);
|
|
this->roam_address |= address;
|
|
if (timercmp(&now, &this->roam_next, >))
|
|
{
|
|
timeval_add_ms(&now, ROAM_DELAY);
|
|
this->roam_next = now;
|
|
lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)
|
|
callback_job_create((callback_job_cb_t)roam_event,
|
|
this, NULL, NULL),
|
|
ROAM_DELAY);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update addresses for an iface entry
|
|
*/
|
|
static void update_addrs(private_kernel_iph_net_t *this, iface_t *entry,
|
|
IP_ADAPTER_ADDRESSES *addr, bool log)
|
|
{
|
|
IP_ADAPTER_UNICAST_ADDRESS *current;
|
|
enumerator_t *enumerator;
|
|
linked_list_t *list;
|
|
host_t *host, *old;
|
|
bool changes = FALSE;
|
|
|
|
list = entry->addrs;
|
|
entry->addrs = linked_list_create();
|
|
|
|
for (current = addr->FirstUnicastAddress; current; current = current->Next)
|
|
{
|
|
if (current->Address.lpSockaddr->sa_family == AF_INET6)
|
|
{
|
|
struct sockaddr_in6 *sin;
|
|
|
|
sin = (struct sockaddr_in6*)current->Address.lpSockaddr;
|
|
if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
host = host_create_from_sockaddr(current->Address.lpSockaddr);
|
|
if (host)
|
|
{
|
|
bool found = FALSE;
|
|
|
|
enumerator = list->create_enumerator(list);
|
|
while (enumerator->enumerate(enumerator, &old))
|
|
{
|
|
if (host->ip_equals(host, old))
|
|
{
|
|
list->remove_at(list, enumerator);
|
|
old->destroy(old);
|
|
found = TRUE;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
entry->addrs->insert_last(entry->addrs, host);
|
|
|
|
if (!found && log)
|
|
{
|
|
DBG1(DBG_KNL, "%H appeared on interface %u '%s'",
|
|
host, entry->ifindex, entry->ifdesc);
|
|
changes = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (list->remove_first(list, (void**)&old) == SUCCESS)
|
|
{
|
|
if (log)
|
|
{
|
|
DBG1(DBG_KNL, "%H disappeared from interface %u '%s'",
|
|
old, entry->ifindex, entry->ifdesc);
|
|
changes = TRUE;
|
|
}
|
|
old->destroy(old);
|
|
}
|
|
list->destroy(list);
|
|
|
|
if (changes)
|
|
{
|
|
fire_roam_event(this, TRUE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an interface entry
|
|
*/
|
|
static void add_interface(private_kernel_iph_net_t *this,
|
|
IP_ADAPTER_ADDRESSES *addr, bool log)
|
|
{
|
|
enumerator_t *enumerator;
|
|
iface_t *entry;
|
|
bool exists = FALSE;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->ifindex == addr->IfIndex)
|
|
{
|
|
exists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (!exists)
|
|
{
|
|
char desc[128] = "";
|
|
|
|
wcstombs(desc, addr->Description, sizeof(desc));
|
|
|
|
INIT(entry,
|
|
.ifindex = addr->IfIndex,
|
|
.ifname = strdup(addr->AdapterName),
|
|
.ifdesc = strdup(desc),
|
|
.iftype = addr->IfType,
|
|
.status = addr->OperStatus,
|
|
.addrs = linked_list_create(),
|
|
);
|
|
|
|
if (log)
|
|
{
|
|
DBG1(DBG_KNL, "interface %u '%s' appeared",
|
|
entry->ifindex, entry->ifdesc);
|
|
}
|
|
|
|
this->mutex->lock(this->mutex);
|
|
update_addrs(this, entry, addr, log);
|
|
this->ifaces->insert_last(this->ifaces, entry);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove an interface entry that is gone
|
|
*/
|
|
static void remove_interface(private_kernel_iph_net_t *this, NET_IFINDEX index)
|
|
{
|
|
enumerator_t *enumerator;
|
|
iface_t *entry;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->ifindex == index)
|
|
{
|
|
this->ifaces->remove_at(this->ifaces, enumerator);
|
|
DBG1(DBG_KNL, "interface %u '%s' disappeared",
|
|
entry->ifindex, entry->ifdesc);
|
|
iface_destroy(entry);
|
|
fire_roam_event(this, TRUE);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
/**
|
|
* Update an interface entry changed
|
|
*/
|
|
static void update_interface(private_kernel_iph_net_t *this,
|
|
IP_ADAPTER_ADDRESSES *addr)
|
|
{
|
|
enumerator_t *enumerator;
|
|
iface_t *entry;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->ifindex == addr->IfIndex)
|
|
{
|
|
if (entry->status != addr->OperStatus)
|
|
{
|
|
DBG1(DBG_KNL, "interface %u '%s' changed state from %N to %N",
|
|
entry->ifindex, entry->ifdesc, if_oper_names,
|
|
entry->status, if_oper_names, addr->OperStatus);
|
|
entry->status = addr->OperStatus;
|
|
fire_roam_event(this, TRUE);
|
|
}
|
|
update_addrs(this, entry, addr, TRUE);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
/**
|
|
* MinGW gets MIB_IPINTERFACE_ROW wrong, as it packs InterfaceLuid just after
|
|
* Family. Fix that with our own version of the struct header.
|
|
*/
|
|
typedef struct {
|
|
ADDRESS_FAMILY Family;
|
|
union {
|
|
ULONG64 Value;
|
|
struct {
|
|
ULONG64 Reserved :24;
|
|
ULONG64 NetLuidIndex :24;
|
|
ULONG64 IfType :16;
|
|
} Info;
|
|
} InterfaceLuid;
|
|
NET_IFINDEX InterfaceIndex;
|
|
/* more would go here if needed */
|
|
} MIB_IPINTERFACE_ROW_FIXUP;
|
|
|
|
/**
|
|
* NotifyIpInterfaceChange() callback
|
|
*/
|
|
static void WINAPI change_interface(void *user, PMIB_IPINTERFACE_ROW row_badal,
|
|
MIB_NOTIFICATION_TYPE type)
|
|
{
|
|
private_kernel_iph_net_t *this = user;
|
|
MIB_IPINTERFACE_ROW_FIXUP* row = (MIB_IPINTERFACE_ROW_FIXUP*)row_badal;
|
|
IP_ADAPTER_ADDRESSES addrs[64], *current;
|
|
ULONG res, size = sizeof(addrs);
|
|
|
|
if (row && type == MibDeleteInstance)
|
|
{
|
|
remove_interface(this, row->InterfaceIndex);
|
|
}
|
|
else
|
|
{
|
|
res = GetAdaptersAddresses(AF_UNSPEC,
|
|
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
|
|
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
|
|
NULL, addrs, &size);
|
|
if (res == NO_ERROR)
|
|
{
|
|
current = addrs;
|
|
while (current)
|
|
{
|
|
/* row is NULL only on MibInitialNotification */
|
|
if (!row || row->InterfaceIndex == current->IfIndex)
|
|
{
|
|
switch (type)
|
|
{
|
|
case MibParameterNotification:
|
|
update_interface(this, current);
|
|
break;
|
|
case MibInitialNotification:
|
|
add_interface(this, current, FALSE);
|
|
break;
|
|
case MibAddInstance:
|
|
add_interface(this, current, TRUE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
current = current->Next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_KNL, "getting IPH adapter addresses failed: 0x%08lx", res);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get an iface entry for a local address, does no locking
|
|
*/
|
|
static iface_t* address2entry(private_kernel_iph_net_t *this, host_t *ip)
|
|
{
|
|
enumerator_t *ifaces, *addrs;
|
|
iface_t *entry, *found = NULL;
|
|
host_t *host;
|
|
|
|
ifaces = this->ifaces->create_enumerator(this->ifaces);
|
|
while (!found && ifaces->enumerate(ifaces, &entry))
|
|
{
|
|
addrs = entry->addrs->create_enumerator(entry->addrs);
|
|
while (!found && addrs->enumerate(addrs, &host))
|
|
{
|
|
if (host->ip_equals(host, ip))
|
|
{
|
|
found = entry;
|
|
}
|
|
}
|
|
addrs->destroy(addrs);
|
|
}
|
|
ifaces->destroy(ifaces);
|
|
|
|
return found;
|
|
}
|
|
|
|
METHOD(kernel_net_t, get_interface_name, bool,
|
|
private_kernel_iph_net_t *this, host_t* ip, char **name)
|
|
{
|
|
iface_t *entry;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
entry = address2entry(this, ip);
|
|
if (entry && name)
|
|
{
|
|
*name = strdup(entry->ifname);
|
|
}
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
return entry != NULL;
|
|
}
|
|
|
|
/**
|
|
* Address enumerator
|
|
*/
|
|
typedef struct {
|
|
/** implements enumerator_t */
|
|
enumerator_t public;
|
|
/** what kind of address should we enumerate? */
|
|
kernel_address_type_t which;
|
|
/** enumerator over interfaces */
|
|
enumerator_t *ifaces;
|
|
/** current enumerator over addresses, or NULL */
|
|
enumerator_t *addrs;
|
|
/** mutex to unlock on destruction */
|
|
mutex_t *mutex;
|
|
} addr_enumerator_t;
|
|
|
|
METHOD(enumerator_t, addr_enumerate, bool,
|
|
addr_enumerator_t *this, host_t **host)
|
|
{
|
|
iface_t *entry;
|
|
|
|
while (TRUE)
|
|
{
|
|
while (!this->addrs)
|
|
{
|
|
if (!this->ifaces->enumerate(this->ifaces, &entry))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (entry->iftype == IF_TYPE_SOFTWARE_LOOPBACK &&
|
|
!(this->which & ADDR_TYPE_LOOPBACK))
|
|
{
|
|
continue;
|
|
}
|
|
if (entry->status != IfOperStatusUp &&
|
|
!(this->which & ADDR_TYPE_DOWN))
|
|
{
|
|
continue;
|
|
}
|
|
this->addrs = entry->addrs->create_enumerator(entry->addrs);
|
|
}
|
|
if (this->addrs->enumerate(this->addrs, host))
|
|
{
|
|
return TRUE;
|
|
}
|
|
this->addrs->destroy(this->addrs);
|
|
this->addrs = NULL;
|
|
}
|
|
}
|
|
|
|
METHOD(enumerator_t, addr_destroy, void,
|
|
addr_enumerator_t *this)
|
|
{
|
|
DESTROY_IF(this->addrs);
|
|
this->ifaces->destroy(this->ifaces);
|
|
this->mutex->unlock(this->mutex);
|
|
free(this);
|
|
}
|
|
|
|
METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
|
|
private_kernel_iph_net_t *this, kernel_address_type_t which)
|
|
{
|
|
addr_enumerator_t *enumerator;
|
|
|
|
if (!(which & ADDR_TYPE_REGULAR))
|
|
{
|
|
/* we currently have no virtual, but regular IPs only */
|
|
return enumerator_create_empty();
|
|
}
|
|
|
|
this->mutex->lock(this->mutex);
|
|
|
|
INIT(enumerator,
|
|
.public = {
|
|
.enumerate = (void*)_addr_enumerate,
|
|
.destroy = _addr_destroy,
|
|
},
|
|
.which = which,
|
|
.ifaces = this->ifaces->create_enumerator(this->ifaces),
|
|
.mutex = this->mutex,
|
|
);
|
|
return &enumerator->public;
|
|
}
|
|
|
|
METHOD(kernel_net_t, get_source_addr, host_t*,
|
|
private_kernel_iph_net_t *this, host_t *dest, host_t *src)
|
|
{
|
|
MIB_IPFORWARD_ROW2 route;
|
|
SOCKADDR_INET best, *sai_dst, *sai_src = NULL;
|
|
DWORD res, index = 0;
|
|
|
|
res = GetBestInterfaceEx(dest->get_sockaddr(dest), &index);
|
|
if (res != NO_ERROR)
|
|
{
|
|
DBG1(DBG_KNL, "getting interface to %H failed: 0x%08x", dest, res);
|
|
return NULL;
|
|
}
|
|
|
|
sai_dst = (SOCKADDR_INET*)dest->get_sockaddr(dest);
|
|
if (src)
|
|
{
|
|
sai_src = (SOCKADDR_INET*)src->get_sockaddr(src);
|
|
}
|
|
res = GetBestRoute2(0, index, sai_src, sai_dst, 0, &route, &best);
|
|
if (res != NO_ERROR)
|
|
{
|
|
DBG2(DBG_KNL, "getting src address to %H failed: 0x%08x", dest, res);
|
|
return NULL;
|
|
}
|
|
return host_create_from_sockaddr((struct sockaddr*)&best);
|
|
}
|
|
|
|
METHOD(kernel_net_t, get_nexthop, host_t*,
|
|
private_kernel_iph_net_t *this, host_t *dest, host_t *src)
|
|
{
|
|
MIB_IPFORWARD_ROW2 route;
|
|
SOCKADDR_INET best, *sai_dst, *sai_src = NULL;
|
|
DWORD res, index = 0;
|
|
host_t *nexthop;
|
|
|
|
res = GetBestInterfaceEx(dest->get_sockaddr(dest), &index);
|
|
if (res != NO_ERROR)
|
|
{
|
|
DBG1(DBG_KNL, "getting interface to %H failed: 0x%08x", dest, res);
|
|
return NULL;
|
|
}
|
|
|
|
sai_dst = (SOCKADDR_INET*)dest->get_sockaddr(dest);
|
|
if (src)
|
|
{
|
|
sai_src = (SOCKADDR_INET*)src->get_sockaddr(src);
|
|
}
|
|
res = GetBestRoute2(0, index, sai_src, sai_dst, 0, &route, &best);
|
|
if (res != NO_ERROR)
|
|
{
|
|
DBG2(DBG_KNL, "getting nexthop to %H failed: 0x%08x", dest, res);
|
|
return NULL;
|
|
}
|
|
nexthop = host_create_from_sockaddr((struct sockaddr*)&route.NextHop);
|
|
if (nexthop)
|
|
{
|
|
if (!nexthop->is_anyaddr(nexthop))
|
|
{
|
|
return nexthop;
|
|
}
|
|
nexthop->destroy(nexthop);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
METHOD(kernel_net_t, add_ip, status_t,
|
|
private_kernel_iph_net_t *this, host_t *virtual_ip, int prefix,
|
|
char *iface_name)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_net_t, del_ip, status_t,
|
|
private_kernel_iph_net_t *this, host_t *virtual_ip, int prefix,
|
|
bool wait)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
/**
|
|
* Add or remove a route
|
|
*/
|
|
static status_t manage_route(private_kernel_iph_net_t *this, bool add,
|
|
chunk_t dst, u_int8_t prefixlen, host_t *gtw, char *name)
|
|
{
|
|
MIB_IPFORWARD_ROW2 row = {
|
|
.DestinationPrefix = {
|
|
.PrefixLength = prefixlen,
|
|
},
|
|
.SitePrefixLength = prefixlen,
|
|
.ValidLifetime = INFINITE,
|
|
.PreferredLifetime = INFINITE,
|
|
.Metric = 10,
|
|
.Protocol = MIB_IPPROTO_NETMGMT,
|
|
};
|
|
enumerator_t *enumerator;
|
|
iface_t *entry;
|
|
ULONG ret;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (streq(name, entry->ifname))
|
|
{
|
|
row.InterfaceIndex = entry->ifindex;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (!row.InterfaceIndex)
|
|
{
|
|
return NOT_FOUND;
|
|
}
|
|
switch (dst.len)
|
|
{
|
|
case 4:
|
|
row.DestinationPrefix.Prefix.si_family = AF_INET;
|
|
memcpy(&row.DestinationPrefix.Prefix.Ipv4.sin_addr,
|
|
dst.ptr, dst.len);
|
|
break;
|
|
case 16:
|
|
row.DestinationPrefix.Prefix.si_family = AF_INET6;
|
|
memcpy(&row.DestinationPrefix.Prefix.Ipv6.sin6_addr,
|
|
dst.ptr, dst.len);
|
|
break;
|
|
default:
|
|
return FAILED;
|
|
}
|
|
if (gtw)
|
|
{
|
|
memcpy(&row.NextHop, gtw->get_sockaddr(gtw),
|
|
*gtw->get_sockaddr_len(gtw));
|
|
}
|
|
|
|
if (add)
|
|
{
|
|
ret = CreateIpForwardEntry2(&row);
|
|
}
|
|
else
|
|
{
|
|
ret = DeleteIpForwardEntry2(&row);
|
|
}
|
|
if (ret != NO_ERROR)
|
|
{
|
|
DBG1(DBG_KNL, "%sing route failed: 0x%08lx", add ? "add" : "remov", ret);
|
|
return FAILED;
|
|
}
|
|
|
|
if (add)
|
|
{
|
|
ret = EnableRouter(NULL, &this->router);
|
|
if (ret != ERROR_IO_PENDING)
|
|
{
|
|
DBG1(DBG_KNL, "EnableRouter router failed: 0x%08lx", ret);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = UnenableRouter(&this->router, NULL);
|
|
if (ret != NO_ERROR)
|
|
{
|
|
DBG1(DBG_KNL, "UnenableRouter router failed: 0x%08lx", ret);
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
METHOD(kernel_net_t, add_route, status_t,
|
|
private_kernel_iph_net_t *this, chunk_t dst, u_int8_t prefixlen,
|
|
host_t *gateway, host_t *src, char *name)
|
|
{
|
|
return manage_route(this, TRUE, dst, prefixlen, gateway, name);
|
|
}
|
|
|
|
METHOD(kernel_net_t, del_route, status_t,
|
|
private_kernel_iph_net_t *this, chunk_t dst, u_int8_t prefixlen,
|
|
host_t *gateway, host_t *src, char *name)
|
|
{
|
|
return manage_route(this, FALSE, dst, prefixlen, gateway, name);
|
|
}
|
|
|
|
METHOD(kernel_net_t, destroy, void,
|
|
private_kernel_iph_net_t *this)
|
|
{
|
|
if (this->changes)
|
|
{
|
|
CancelMibChangeNotify2(this->changes);
|
|
}
|
|
CloseHandle(this->router.hEvent);
|
|
this->mutex->destroy(this->mutex);
|
|
this->ifaces->destroy_function(this->ifaces, (void*)iface_destroy);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* Described in header.
|
|
*/
|
|
kernel_iph_net_t *kernel_iph_net_create()
|
|
{
|
|
private_kernel_iph_net_t *this;
|
|
ULONG res;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.interface = {
|
|
.get_interface = _get_interface_name,
|
|
.create_address_enumerator = _create_address_enumerator,
|
|
.get_source_addr = _get_source_addr,
|
|
.get_nexthop = _get_nexthop,
|
|
.add_ip = _add_ip,
|
|
.del_ip = _del_ip,
|
|
.add_route = _add_route,
|
|
.del_route = _del_route,
|
|
.destroy = _destroy,
|
|
},
|
|
},
|
|
.router = {
|
|
.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL),
|
|
},
|
|
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
|
.ifaces = linked_list_create(),
|
|
);
|
|
/* PIPINTERFACE_CHANGE_CALLBACK is not using WINAPI in MinGW, which seems
|
|
* to be wrong. Force a cast to our WINAPI call */
|
|
res = NotifyIpInterfaceChange(AF_UNSPEC, (void*)change_interface,
|
|
this, TRUE, &this->changes);
|
|
if (res != NO_ERROR)
|
|
{
|
|
DBG1(DBG_KNL, "registering for IPH interface changes failed: 0x%08lx",
|
|
res);
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
|
|
return &this->public;
|
|
}
|