From 00780f0238c7e598269fc05dec4c4d7813e73cbb Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 12 Dec 2013 09:35:36 +0100 Subject: kernel-iph: Add a stub for a Windows IP Helper based networking backend --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 115 ++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/libcharon/plugins/kernel_iph/kernel_iph_net.c (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c new file mode 100644 index 000000000..8cb4aec2b --- /dev/null +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -0,0 +1,115 @@ +/* + * 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 . + * + * 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. + */ + +#include "kernel_iph_net.h" + +#include + +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; +}; + +METHOD(kernel_net_t, get_interface_name, bool, + private_kernel_iph_net_t *this, host_t* ip, char **name) +{ + return FALSE; +} + +METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, + private_kernel_iph_net_t *this, kernel_address_type_t which) +{ + return enumerator_create_empty(); +} + +METHOD(kernel_net_t, get_source_addr, host_t*, + private_kernel_iph_net_t *this, host_t *dest, host_t *src) +{ + return NULL; +} + +METHOD(kernel_net_t, get_nexthop, host_t*, + private_kernel_iph_net_t *this, host_t *dest, host_t *src) +{ + 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; +} + +METHOD(kernel_net_t, add_route, status_t, + private_kernel_iph_net_t *this, chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, host_t *src_ip, char *if_name) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_net_t, del_route, status_t, + private_kernel_iph_net_t *this, chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, host_t *src_ip, char *if_name) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_net_t, destroy, void, + private_kernel_iph_net_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +kernel_iph_net_t *kernel_iph_net_create() +{ + private_kernel_iph_net_t *this; + + 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, + }, + }, + ); + + return &this->public; +} -- cgit v1.2.3 From 96f1978d0e5decb5280cdbf757d6a787a12162c2 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 12 Dec 2013 15:37:21 +0100 Subject: kernel-iph: Create and maintain a cache of interfaces and associated addresses --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 328 ++++++++++++++++++++++ 1 file changed, 328 insertions(+) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 8cb4aec2b..28c110b35 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -13,9 +13,21 @@ * for more details. */ +/* Windows 7, for some iphlpapi.h functionality */ +#define _WIN32_WINNT 0x0601 +#include +#include +#include +#include +#include +#include + #include "kernel_iph_net.h" #include +#include +#include + typedef struct private_kernel_iph_net_t private_kernel_iph_net_t; @@ -28,8 +40,305 @@ struct private_kernel_iph_net_t { * Public interface. */ kernel_iph_net_t public; + + /** + * NotifyIpInterfaceChange() handle + */ + HANDLE changes; + + /** + * Mutex to access interface list + */ + mutex_t *mutex; + + /** + * Known interfaces, as iface_t + */ + linked_list_t *ifaces; }; +/** + * 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", +); + +/** + * 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; + + 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); + } + } + } + + while (list->remove_first(list, (void**)&old) == SUCCESS) + { + if (log) + { + DBG1(DBG_KNL, "%H disappeared from interface %u '%s'", + old, entry->ifindex, entry->ifdesc); + } + old->destroy(old); + } + list->destroy(list); +} + +/** + * 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); + } + } + 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; + } + 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 change_interface(private_kernel_iph_net_t *this, + MIB_IPINTERFACE_ROW_FIXUP *row, MIB_NOTIFICATION_TYPE type) +{ + 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); + } + } +} + METHOD(kernel_net_t, get_interface_name, bool, private_kernel_iph_net_t *this, host_t* ip, char **name) { @@ -85,6 +394,12 @@ METHOD(kernel_net_t, del_route, status_t, METHOD(kernel_net_t, destroy, void, private_kernel_iph_net_t *this) { + if (this->changes) + { + CancelMibChangeNotify2(this->changes); + } + this->mutex->destroy(this->mutex); + this->ifaces->destroy_function(this->ifaces, (void*)iface_destroy); free(this); } @@ -94,6 +409,7 @@ METHOD(kernel_net_t, destroy, void, kernel_iph_net_t *kernel_iph_net_create() { private_kernel_iph_net_t *this; + ULONG res; INIT(this, .public = { @@ -109,7 +425,19 @@ kernel_iph_net_t *kernel_iph_net_create() .destroy = _destroy, }, }, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .ifaces = linked_list_create(), ); + 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; } -- cgit v1.2.3 From 322c341f901f4370c4607268ad0b5d4c36b5df66 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 12 Dec 2013 15:41:39 +0100 Subject: kernel-iph: Implement get_interface() method --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 39 ++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 28c110b35..4c870608c 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -339,10 +339,47 @@ static void change_interface(private_kernel_iph_net_t *this, } } +/** + * 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) { - return FALSE; + 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; } METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, -- cgit v1.2.3 From f9e6200d066f210e7c5a49048f5c7ec5c002ff7a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 12 Dec 2013 16:29:31 +0100 Subject: kernel-iph: Implement address enumeration --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 80 ++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 4c870608c..3d7fd7b88 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -382,10 +382,88 @@ METHOD(kernel_net_t, get_interface_name, bool, 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) { - return enumerator_create_empty(); + 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*, -- cgit v1.2.3 From 0cefd9400734fea388b86d8d2a1075907f96d769 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 12 Dec 2013 17:28:51 +0100 Subject: kernel-iph: Implement get_source_addr() --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 3d7fd7b88..56c458fdb 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -469,7 +469,29 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, METHOD(kernel_net_t, get_source_addr, host_t*, private_kernel_iph_net_t *this, host_t *dest, host_t *src) { - return NULL; + 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*, -- cgit v1.2.3 From 13e18cb2fc52661152c570a5be3e7cddce388902 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 12 Dec 2013 17:34:06 +0100 Subject: kernel-iph: Implement get_nexthop() --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 56c458fdb..36e3c4c56 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -497,6 +497,38 @@ METHOD(kernel_net_t, get_source_addr, host_t*, 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; } -- cgit v1.2.3 From 0ef0493b4aae7b19ce5f81a9e376566546ee49f6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 13 Dec 2013 12:52:06 +0100 Subject: kernel-iph: Implement add/del_route() --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 86 +++++++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 36e3c4c56..e0652ce6f 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -546,18 +546,92 @@ METHOD(kernel_net_t, del_ip, status_t, 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; + } + return SUCCESS; +} + METHOD(kernel_net_t, add_route, status_t, - private_kernel_iph_net_t *this, chunk_t dst_net, u_int8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + private_kernel_iph_net_t *this, chunk_t dst, u_int8_t prefixlen, + host_t *gateway, host_t *src, char *name) { - return NOT_SUPPORTED; + 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_net, u_int8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + private_kernel_iph_net_t *this, chunk_t dst, u_int8_t prefixlen, + host_t *gateway, host_t *src, char *name) { - return NOT_SUPPORTED; + return manage_route(this, FALSE, dst, prefixlen, gateway, name); } METHOD(kernel_net_t, destroy, void, -- cgit v1.2.3 From 3551fdbbdfcd4a97415d87a8e14912a562bcf4f9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 13 Dec 2013 17:12:38 +0100 Subject: kernel-iph: Fire roam events for detected address changes --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index e0652ce6f..71c595ba7 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -27,8 +27,12 @@ #include #include #include +#include +/** delay before firing roam events (ms) */ +#define ROAM_DELAY 500 + typedef struct private_kernel_iph_net_t private_kernel_iph_net_t; /** @@ -55,6 +59,16 @@ struct private_kernel_iph_net_t { * 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; }; /** @@ -99,6 +113,42 @@ ENUM(if_oper_names, IfOperStatusUp, IfOperStatusLowerLayerDown, "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 */ @@ -109,6 +159,7 @@ static void update_addrs(private_kernel_iph_net_t *this, iface_t *entry, enumerator_t *enumerator; linked_list_t *list; host_t *host, *old; + bool changes = FALSE; list = entry->addrs; entry->addrs = linked_list_create(); @@ -149,6 +200,7 @@ static void update_addrs(private_kernel_iph_net_t *this, iface_t *entry, { DBG1(DBG_KNL, "%H appeared on interface %u '%s'", host, entry->ifindex, entry->ifdesc); + changes = TRUE; } } } @@ -159,10 +211,16 @@ static void update_addrs(private_kernel_iph_net_t *this, iface_t *entry, { 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); + } } /** @@ -234,6 +292,7 @@ static void remove_interface(private_kernel_iph_net_t *this, NET_IFINDEX index) DBG1(DBG_KNL, "interface %u '%s' disappeared", entry->ifindex, entry->ifdesc); iface_destroy(entry); + fire_roam_event(this, TRUE); } } enumerator->destroy(enumerator); @@ -261,6 +320,7 @@ static void update_interface(private_kernel_iph_net_t *this, 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); } -- cgit v1.2.3 From 5e6e214ab41000b3eb89ab08b1bc09364ea39408 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 24 Dec 2013 10:40:09 +0100 Subject: kernel-iph: Implicitly enable IP forwarding when installing routes --- src/libcharon/plugins/kernel_iph/kernel_iph_net.c | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src/libcharon/plugins/kernel_iph/kernel_iph_net.c') diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 71c595ba7..68b753792 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -50,6 +50,11 @@ struct private_kernel_iph_net_t { */ HANDLE changes; + /** + * EnableRouter() OVERLAPPED + */ + OVERLAPPED router; + /** * Mutex to access interface list */ @@ -677,6 +682,23 @@ static status_t manage_route(private_kernel_iph_net_t *this, bool add, 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; } @@ -701,6 +723,7 @@ METHOD(kernel_net_t, destroy, void, { CancelMibChangeNotify2(this->changes); } + CloseHandle(this->router.hEvent); this->mutex->destroy(this->mutex); this->ifaces->destroy_function(this->ifaces, (void*)iface_destroy); free(this); @@ -728,6 +751,9 @@ kernel_iph_net_t *kernel_iph_net_create() .destroy = _destroy, }, }, + .router = { + .hEvent = CreateEvent(NULL, FALSE, FALSE, NULL), + }, .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .ifaces = linked_list_create(), ); -- cgit v1.2.3