Commit 2811db0f authored by Jondy Zhao's avatar Jondy Zhao

Patch babeld for cygwin

parent a1b68b4d
...@@ -14,6 +14,20 @@ SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \ ...@@ -14,6 +14,20 @@ SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \
OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \ OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \
route.o xroute.o message.o resend.o configuration.o local.o route.o xroute.o message.o resend.o configuration.o local.o
ifneq "$(WINVER)" ""
SRCS += cyginet.c
OBJS += cyginet.o
LDLIBS += -liphlpapi -lws2_32 -lwlanapi -lole32 -lsetupapi
ifeq "$(WINVER)" "XP"
CFLAGS += -D_WIN32_WINNT=0x0503
else
CLFAGS += -D_WIN32_WINNT=0x0600
endif
endif
babeld: $(OBJS) babeld: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS) $(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS)
...@@ -48,4 +62,9 @@ uninstall: ...@@ -48,4 +62,9 @@ uninstall:
clean: clean:
-rm -f babeld babeld.html *.o *~ core TAGS gmon.out -rm -f babeld babeld.html *.o *~ core TAGS gmon.out
kernel.o: kernel_netlink.c kernel_socket.c kernel.o: kernel_netlink.c kernel_socket.c kernel_cygwin.c
# Usage: ./test.exe
# Verify most of the functions in the cyginet.c
test.exe: cyginet.c
$(CC) $(CFLAGS) $(LDFLAGS) -DTEST_CYGINET -o $@ $< $(LDLIBS)
...@@ -96,6 +96,8 @@ static void init_signals(void); ...@@ -96,6 +96,8 @@ static void init_signals(void);
static void dump_tables(FILE *out); static void dump_tables(FILE *out);
static int reopen_logfile(void); static int reopen_logfile(void);
int cyginet_set_interface_forwards(const char * ifname, int value);
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
...@@ -371,13 +373,17 @@ main(int argc, char **argv) ...@@ -371,13 +373,17 @@ main(int argc, char **argv)
FOR_ALL_INTERFACES(ifp) { FOR_ALL_INTERFACES(ifp) {
/* ifp->ifindex is not necessarily valid at this point */ /* ifp->ifindex is not necessarily valid at this point */
#if defined (_WIN32_WINNT)
int ifindex = if_nametoindex(cyginet_guidname(ifp->name));
#else
int ifindex = if_nametoindex(ifp->name); int ifindex = if_nametoindex(ifp->name);
#endif
if(ifindex > 0) { if(ifindex > 0) {
unsigned char eui[8]; unsigned char eui[8];
rc = if_eui64(ifp->name, ifindex, eui); rc = if_eui64(ifp->name, ifindex, eui);
if(rc < 0) if(rc < 0)
continue; continue;
memcpy(myid, eui, 8); memcpy(myid, eui, 8);
goto have_id; goto have_id;
} }
} }
...@@ -390,7 +396,11 @@ main(int argc, char **argv) ...@@ -390,7 +396,11 @@ main(int argc, char **argv)
ifname = if_indextoname(i, buf); ifname = if_indextoname(i, buf);
if(ifname == NULL) if(ifname == NULL)
continue; continue;
#if defined (_WIN32_WINNT)
rc = if_eui64(cyginet_ifname(ifname), i, eui);
#else
rc = if_eui64(ifname, i, eui); rc = if_eui64(ifname, i, eui);
#endif
if(rc < 0) if(rc < 0)
continue; continue;
memcpy(myid, eui, 8); memcpy(myid, eui, 8);
...@@ -479,6 +489,14 @@ main(int argc, char **argv) ...@@ -479,6 +489,14 @@ main(int argc, char **argv)
if(receive_buffer == NULL) if(receive_buffer == NULL)
goto fail; goto fail;
#if defined (_WIN32_WINNT)
FOR_ALL_INTERFACES(ifp)
if (cyginet_set_interface_forwards(ifp->name, 1) == -1) {
fprintf(stderr, "Cannot enable IPv6 forwarding.\n");
goto fail;
}
#endif
rc = check_xroutes(0); rc = check_xroutes(0);
if(rc < 0) if(rc < 0)
fprintf(stderr, "Warning: couldn't check exported routes.\n"); fprintf(stderr, "Warning: couldn't check exported routes.\n");
......
...@@ -348,7 +348,11 @@ parse_filter(gnc_t gnc, void *closure) ...@@ -348,7 +348,11 @@ parse_filter(gnc_t gnc, void *closure)
if(c < -1) if(c < -1)
goto error; goto error;
filter->ifname = interface; filter->ifname = interface;
#if defined (_WIN32_WINNT)
filter->ifindex = if_nametoindex(cyginet_guidname(interface));
#else
filter->ifindex = if_nametoindex(interface); filter->ifindex = if_nametoindex(interface);
#endif
} else if(strcmp(token, "allow") == 0) { } else if(strcmp(token, "allow") == 0) {
filter->result = 0; filter->result = 0;
} else if(strcmp(token, "deny") == 0) { } else if(strcmp(token, "deny") == 0) {
...@@ -644,7 +648,11 @@ renumber_filter(struct filter *filter) ...@@ -644,7 +648,11 @@ renumber_filter(struct filter *filter)
{ {
while(filter) { while(filter) {
if(filter->ifname) if(filter->ifname)
#if defined (_WIN32_WINNT)
filter->ifindex = if_nametoindex(cyginet_guidname(filter->ifname));
#else
filter->ifindex = if_nametoindex(filter->ifname); filter->ifindex = if_nametoindex(filter->ifname);
#endif
filter = filter->next; filter = filter->next;
} }
} }
......
/* The Win32 select only worked on socket handles. The Cygwin
* implementation allows select to function normally when given
* different types of file descriptors (sockets, pipes, handles,
* etc.).
*/
#if !defined(__INSIDE_CYGWIN__)
#define __INSIDE_CYGWIN__
#define USE_SYS_TYPES_FD_SET
#endif
#include <sys/select.h>
#include <sys/fcntl.h>
/* Headers in the /usr/include/w32api */
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <wlanapi.h>
#include <rtmv2.h>
#include <nldef.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <wchar.h>
#include <assert.h>
#define INSIDE_BABELD_CYGINET
#include "cyginet.h"
#undef INSIDE_BABELD_CYGINET
#if defined (TEST_CYGINET)
#define kdebugf printf
#else
extern int debug;
void do_debugf(int level, const char *format, ...);
#define kdebugf(_args...) \
do { \
if(!!(debug >= 3)) do_debugf(3, _args); \
} while(0)
#endif
static HRESULT (WINAPI *ws_guidfromstring)(LPCTSTR psz, LPGUID pguid) = NULL;
static HANDLE event_notify_monitor_thread = WSA_INVALID_EVENT;
static PLIBWINET_INTERFACE_MAP_TABLE g_interface_map_table = NULL;
static int libwinet_run_command(const char *);
static void
plen2mask(int n, struct in_addr *dest)
{
unsigned char *p;
int i;
static const int pl2m[9] = {
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
};
memset(dest, 0, sizeof(struct in_addr));
p = (u_char *)dest;
for (i = 0; i < 4; i++, p++, n -= 8) {
if (n >= 8) {
*p = 0xff;
continue;
}
*p = pl2m[n];
break;
}
return;
}
static int
mask2len(const unsigned char *p, const int size)
{
int i = 0, j;
for(j = 0; j < size; j++, p++) {
if(*p != 0xff)
break;
i += 8;
}
if(j < size) {
switch(*p) {
#define MASKLEN(m, l) case m: do { i += l; break; } while (0)
MASKLEN(0xfe, 7); break;
MASKLEN(0xfc, 6); break;
MASKLEN(0xf8, 5); break;
MASKLEN(0xf0, 4); break;
MASKLEN(0xe0, 3); break;
MASKLEN(0xc0, 2); break;
MASKLEN(0x80, 1); break;
#undef MASKLEN
}
}
return i;
}
static int
libwinet_set_registry_key(char *key, char * name, int value, int defvalue)
{
HKEY hKey;
unsigned long type;
unsigned long size;
unsigned long old;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WRITE, &hKey) !=
ERROR_SUCCESS)
return -1;
size = sizeof(old);
if (RegQueryValueEx(hKey, name, NULL, &type, (unsigned char *)&old, &size) !=
ERROR_SUCCESS || type != REG_DWORD)
old = defvalue;
if (RegSetValueEx(hKey,
name,
0,
REG_DWORD,
(unsigned char *)&value,
sizeof(value)
)) {
RegCloseKey(hKey);
return -1;
}
RegCloseKey(hKey);
return old;
}
/* return True if success */
static int
libwinet_get_registry_key(char *key, char * name, long * value)
{
HKEY hKey;
int rc;
unsigned long type;
unsigned long size;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey) !=
ERROR_SUCCESS)
return -1;
size = sizeof(*value);
rc = (RegQueryValueEx(hKey, name, NULL, &type, (unsigned char *)value, &size) ==
ERROR_SUCCESS && type == REG_DWORD);
RegCloseKey(hKey);
return rc;
}
static void
libwinet_free_interface_map_table()
{
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
PLIBWINET_INTERFACE_MAP_TABLE s;
while (p) {
if ( p -> AdapterName )
FREE(p -> AdapterName);
if (p -> FriendlyName)
FREE(p -> FriendlyName);
s = p;
p = p -> next;
FREE(s);
}
g_interface_map_table = NULL;
}
static int
get_interface_forwards(char * guid)
{
long value = 0;
/* Location in the Windows XP, not sure in Vista or Windows 7 */
char * key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters"
"\\Interfaces\\%s";
char * name = "Forwards";
char buf[256] = {0};
if (snprintf(buf, 255, key, guid) > 0)
if (libwinet_get_registry_key(buf, name, &value))
return value;
return -1;
}
static int
libwinet_refresh_interface_map_table()
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
dwRet = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_UNICAST \
| GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return 0;
dwRet = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_UNICAST \
| GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
if (g_interface_map_table)
libwinet_free_interface_map_table();
PLIBWINET_INTERFACE_MAP_TABLE p;
size_t len;
g_interface_map_table = NULL;
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
p = MALLOC(sizeof(LIBWINET_INTERFACE_MAP_TABLE));
if (!p) {
dwRet = ERROR_BUFFER_OVERFLOW;
break;
}
p -> next = g_interface_map_table;
p -> IfIndex = pTmpAdaptAddr -> IfIndex;
p -> Ipv6IfIndex = pTmpAdaptAddr -> Ipv6IfIndex;
p -> RouteFlags = get_interface_forwards(pTmpAdaptAddr -> AdapterName);
len = WideCharToMultiByte(CP_ACP,
0,
pTmpAdaptAddr -> FriendlyName,
-1,
NULL,
0,
NULL,
NULL
);
p -> FriendlyName = MALLOC(len + 1);
if (!p -> FriendlyName) {
dwRet = ERROR_BUFFER_OVERFLOW;
break;
}
if (WideCharToMultiByte(CP_ACP,
0,
pTmpAdaptAddr -> FriendlyName,
-1,
p -> FriendlyName,
len,
NULL,
NULL
) == 0) {
dwRet = ERROR_BUFFER_OVERFLOW;
break;
}
len = strlen(pTmpAdaptAddr -> AdapterName);
p -> AdapterName = MALLOC(len + 1);
if (!p -> AdapterName) {
dwRet = ERROR_BUFFER_OVERFLOW;
break;
}
memcpy(p -> AdapterName, pTmpAdaptAddr -> AdapterName, len);
*(p -> AdapterName + len) = 0;
p -> IfType = pTmpAdaptAddr -> IfType;
p -> PhysicalAddressLength = pTmpAdaptAddr -> PhysicalAddressLength;
memcpy(p -> PhysicalAddress,
pTmpAdaptAddr -> PhysicalAddress,
p -> PhysicalAddressLength
);
pTmpAdaptAddr = pTmpAdaptAddr->Next;
g_interface_map_table = p;
}
if (ERROR_BUFFER_OVERFLOW == dwRet)
libwinet_free_interface_map_table();
}
FREE(pAdaptAddr);
return (NO_ERROR == dwRet);
}
/* Map ifindex belong to family to index of another family,
Ipv4 -> Ipv6 or
Ipv6 -> Ipv4
Return 0, if the interface only binds one ip version.
Special case:
If the interface is loopback, it will always return 1.
Notice the parameter family specify the familay of input ifindex,
not output family. Actually,
family == AF_INET, it will map ipv4 to ipv6,
family == AF_INET6, it will map ipv6 to ipv4,
*/
static int
libwinet_map_ifindex(int family, int ifindex)
{
/* Loopback Interface */
if (ifindex == 1)
return 1;
if (g_interface_map_table == NULL)
if (!libwinet_refresh_interface_map_table())
return -1;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
if (family == AF_INET && ifindex == p -> IfIndex && p -> Ipv6IfIndex)
return p -> Ipv6IfIndex;
else if (family == AF_INET6 && ifindex == p -> Ipv6IfIndex && p -> IfIndex)
return p -> IfIndex;
p = p -> next;
}
return 0;
}
static int
libwinet_get_interface_info(const char *ifname,
PLIBWINET_INTERFACE pinfo)
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD dwReturn = 0;
ULONG family = AF_UNSPEC;
WCHAR *friendlyname;
size_t size;
size = MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
NULL,
0
);
friendlyname = MALLOC(size * sizeof(WCHAR));
if (!friendlyname)
return -1;
if (MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
friendlyname,
size
) == 0) {
FREE(friendlyname);
return -1;
}
dwRet = GetAdaptersAddresses(family,
GAA_FLAG_SKIP_UNICAST \
| GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return -1;
dwRet = GetAdaptersAddresses(family,
GAA_FLAG_SKIP_UNICAST \
| GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
if (wcscmp(friendlyname, pTmpAdaptAddr -> FriendlyName) == 0) {
memset(pinfo, 0, sizeof(pinfo));
pinfo -> IfType = pTmpAdaptAddr -> IfType;
pinfo -> Mtu = pTmpAdaptAddr -> Mtu;
pinfo -> OperStatus = pTmpAdaptAddr -> OperStatus;
/* Copy first unicast address */
if (pTmpAdaptAddr -> FirstUnicastAddress)
memcpy(&(pinfo -> Address),
(pTmpAdaptAddr -> FirstUnicastAddress -> Address).lpSockaddr,
sizeof(pinfo -> Address)
);
dwReturn = 1;
break;
}
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
return dwReturn;
}
static int
libwinet_run_command(const char *command)
{
FILE *output;
kdebugf("libwinet_run_command: %s\n", command);
output = popen (command, "r");
if (!output)
return -1;
/* Waiting for subprocess exit and return exit code */
return pclose (output);
}
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
/*
* Before Windows Vista, use netsh command to get ipv6 route table
*
* C:\> netsh interface ipv6 show routes verbose
*
* It will print the following route entries:
*
* Prefix : fe80::1/128
* Interface 1 : Loopback Pseudo-Interface
* Gateway : fe80::1
* Metric : 4
* Publish : no
* Type : System
* Valid Lifetime : infinite
* Preferred Lifetime: infinite
* Site Prefix Length: 0
*
* ....
*
* Type System means that routes used for loopback.
*
* Gateway could be an address or interface name.
*
* In the Windows 7, there is a little difference:
*
* Destination Prefix: ::/0
* Source Prefix: ::/0
* Interface Index: 15
* Gateway/Interface Name: Teredo Tunneling Pseudo-Interface
* Publish: No
* Type: Manual
* Metric: 8
* SitePrefixLength 0
* ValidLifeTime Infinite
* PreferredLifeTime Infinite
*
*
*/
static int
libwinet_dump_ipv6_route_table(struct cyginet_route *routes,
int maxroutes)
{
#define MAX_LINE_SIZE 80
const char * command = "netsh interface ipv6 show routes verbose";
FILE *output;
char buffer[MAX_LINE_SIZE];
char *s, *p;
int count = 0;
int ignored = 0;
struct sockaddr_in6 *dest;
struct sockaddr_in6 *gate;
struct cyginet_route route;
struct cyginet_route * proute = routes;
output = popen (command, "r");
if (!output)
return -1;
dest = (struct sockaddr_in6*)&(route.prefix);
gate = (struct sockaddr_in6*)&(route.gateway);
/* Ignore the first line */
fgets(buffer, MAX_LINE_SIZE, output);
/* Read the output until EOF */
while (fgets(buffer, MAX_LINE_SIZE, output)) {
if (('\n' == buffer[0]) || ('\r' == buffer[0]))
continue;
if (NULL == (s = strchr(buffer, ':')))
break;
*s ++ = 0; /* Split the string */
s ++; /* Skip space */
/* Remove the newline character at the end of line */
p = s + strlen(s) - 1;
while ( (p > s) && (*p == '\n' || *p == '\r'))
*p -- = 0;
/* The first field of route entry
In the Windows XP, field name is "prefix",
Windows 7, "Destination Prefix"
*/
if ((strncmp(buffer, "Prefix", 6) == 0) ||
(strncmp(buffer, "Destination Prefix", 18) == 0)) {
memset(&route, 0, sizeof(struct cyginet_route));
if (NULL == (p = strchr(s, '/')))
break;
*p ++ = 0;
/*
* Maybe it will be "fe80::5efe:10.85.0.127", ignore it
*/
if (inet_pton(AF_INET6, s, &(dest -> sin6_addr)) != 1)
ignored = 1;
dest -> sin6_family = AF_INET6;
route.plen = strtol(p, NULL, 10);
}
else if (strncmp(buffer, "Interface", 9) == 0) {
route.ifindex = strtol(buffer + 9, NULL, 10);
/* In Windows 7 */
if (route.ifindex == 0)
route.ifindex = strtol(s, NULL, 10);
}
else if (strncmp(buffer, "Gateway", 7) == 0) {
if (inet_pton(AF_INET6, s, &(gate -> sin6_addr)) == 1)
gate -> sin6_family = AF_INET6;
}
else if (strncmp(buffer, "Metric", 6) == 0)
route.metric = strtol(s, NULL, 10);
else if ((strncmp(buffer, "Valid Lifetime", 14) == 0) &&
(strncmp(s, "0s", 2) == 0))
ignored = 1;
/* Last field of the route entry */
else if ((strncmp(buffer, "Site Prefix Length", 18) == 0) ||
(strncmp(buffer, "PreferredLifeTime", 17) == 0)) {
if (ignored)
ignored = 0;
else if (!ignored) {
route.proto = MIB_IPPROTO_NETMGMT; /* ?? */
if ((maxroutes > count) && (proute != NULL)) {
memcpy(proute, &route, sizeof(struct cyginet_route));
proute ++;
}
count ++;
}
}
else {
/* Immortal: persistent entry */
/* Age */
/* ... */
}
}
pclose (output);
return count;
}
/* Tell the interface is wireless or not. First we list all wireless
* interfaces in the machine, then search the designated one.
*/
static int
libwinet_is_wireless_interface(const char *ifname)
{
GUID ifguid;
HANDLE hClient = NULL;
DWORD dwMaxClient = 1;
/* 1 Client version for Windows XP with SP3 and Wireless LAN API for
Windows XP with SP2. */
/* 2 Client version for Windows Vista and Windows Server 2008 */
DWORD dwCurVersion = 1;
DWORD dwResult = 0;
int iRet = 0;
int i;
if (NULL == ws_guidfromstring) {
HMODULE lib;
if ((lib = LoadLibraryW(L"shell32.dll"))) {
ws_guidfromstring = (HRESULT (WINAPI *)(LPCTSTR, LPGUID))
GetProcAddress(lib, (LPCSTR)703); /* GUIDFromStringA */
FreeLibrary(lib);
}
}
if (NULL == ws_guidfromstring)
return -1;
if (!(*ws_guidfromstring)(cyginet_guidname(ifname), &ifguid))
return -1;
/* variables used for WlanEnumInterfaces */
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
PWLAN_INTERFACE_INFO pIfInfo = NULL;
dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);
if (dwResult != ERROR_SUCCESS)
return -1;
dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);
if (dwResult != ERROR_SUCCESS)
return -1;
for (i = 0; i < (int) pIfList->dwNumberOfItems; i++) {
pIfInfo = (WLAN_INTERFACE_INFO *) &pIfList->InterfaceInfo[i];
if (0 == memcmp(&pIfInfo->InterfaceGuid, &ifguid, sizeof(GUID))) {
iRet = 1;
break;
}
}
if (pIfList != NULL) {
WlanFreeMemory(pIfList);
pIfList = NULL;
}
return iRet;
}
#endif /* _WIN32_WINNT < _WIN32_WINNT_VISTA */
static DWORD WINAPI
libwinet_monitor_route_thread_proc(LPVOID lpParam)
{
#define EVENT_COUNT 4
DWORD dwBytesReturned = 0;
DWORD dwReturn = 0;
SOCKET s[2] = {INVALID_SOCKET, INVALID_SOCKET};
WSAOVERLAPPED hOverLappeds[EVENT_COUNT];
WSAEVENT hEvents[EVENT_COUNT + 1];
SOCKADDR_IN6 IPv6Addr = {
AF_INET6,
0,
0,
{{IN6ADDR_ANY_INIT}}
};
SOCKADDR_IN IPv4Addr = {
AF_INET,
0,
{{{INADDR_ANY}}},
{0}
};
int mypipe = (int)lpParam;
BOOL bResult = TRUE;
int i;
memset(hOverLappeds, 0, sizeof(WSAOVERLAPPED) * EVENT_COUNT);
memset(hEvents, 0, sizeof(WSAEVENT) * EVENT_COUNT);
hEvents[EVENT_COUNT] = event_notify_monitor_thread;
if (bResult) {
s[0] = socket(AF_INET, SOCK_DGRAM, 0);
s[1] = socket(AF_INET6, SOCK_DGRAM, 0);
if ((INVALID_SOCKET == s[0]) || (INVALID_SOCKET == s[1])) {
SOCKETERR("socket");
bResult = FALSE;
}
}
if (bResult)
for (i = 0; i < EVENT_COUNT; i++)
if (WSA_INVALID_EVENT == (hEvents[i] = WSACreateEvent())) {
SOCKETERR("WSACreateEvent");
bResult = FALSE;
break;
}
/* DestAddrs[0].sa_family = AF_INET; */
/* ((SOCKADDR_IN*)&DestAddrs[0])->sin_addr.S_un.S_addr =
htonl(INADDR_ANY); */
/* DestAddrs[1].sa_family = AF_INET6; */
/* ((SOCKADDR_IN6*)&DestAddrs[1])->sin6_addr = IN6ADDR_ANY_INIT; */
/* Waiting for route and interface changed */
DWORD dwWaitResult;
while (bResult) {
memset(hOverLappeds, 0, sizeof(WSAOVERLAPPED) * EVENT_COUNT);
for (i = 0; i < EVENT_COUNT; i++)
hOverLappeds[i].hEvent = hEvents[i];
for (i = 0; i < 2; i++) {
if (bResult) {
if (SOCKET_ERROR == WSAIoctl(s[i],
SIO_ROUTING_INTERFACE_CHANGE,
i ? (LPVOID)&IPv6Addr:(LPVOID)&IPv4Addr,
i ? sizeof(SOCKADDR_IN6):sizeof(SOCKADDR_IN),
NULL,
0,
&dwBytesReturned,
&hOverLappeds[i * 2],
NULL
)) {
if (WSA_IO_PENDING != WSAGetLastError()) {
SOCKETERR("WSAIoctl");
bResult = FALSE;
}
}
}
if (bResult) {
if (SOCKET_ERROR == WSAIoctl(s[i],
SIO_ADDRESS_LIST_CHANGE,
NULL,
0,
NULL,
0,
&dwBytesReturned,
&hOverLappeds[i * 2 + 1],
NULL
)) {
if (WSA_IO_PENDING != WSAGetLastError()) {
SOCKETERR("WSAIoctl");
bResult = FALSE;
}
}
}
}
if (bResult) {
dwWaitResult = WSAWaitForMultipleEvents(EVENT_COUNT + 1,
hEvents,
FALSE,
WSA_INFINITE,
FALSE
);
switch (dwWaitResult) {
case WSA_WAIT_TIMEOUT:
break;
case WSA_WAIT_EVENT_0:
WSAResetEvent(hEvents[0]);
if (write(mypipe, &"0", 1) == -1)
bResult = FALSE;
break;
case WSA_WAIT_EVENT_0 + 1:
WSAResetEvent(hEvents[1]);
if (write(mypipe, &"1", 1) == -1)
bResult = FALSE;
break;
case WSA_WAIT_EVENT_0 + 2:
WSAResetEvent(hEvents[2]);
if (write(mypipe, &"2", 1) == -1)
bResult = FALSE;
break;
case WSA_WAIT_EVENT_0 + 3:
WSAResetEvent(hEvents[3]);
if (write(mypipe, &"3", 1) == -1)
bResult = FALSE;
break;
case WSA_WAIT_EVENT_0 + 4:
WSAResetEvent(hEvents[4]);
bResult = FALSE;
break;
case WSA_WAIT_IO_COMPLETION:
break;
default:
SOCKETERR("WSAWaitForMultipleEvents");
bResult = FALSE;
}
}
}
for (i = 0; i < EVENT_COUNT; i ++)
CLOSESOCKEVENT(hEvents[i]);
CLOSESOCKET(s[0]);
CLOSESOCKET(s[1]);
return dwReturn;
}
int cyginet_start_monitor_route_changes(int mypipe)
{
if (WSA_INVALID_EVENT == event_notify_monitor_thread)
event_notify_monitor_thread = WSACreateEvent();
if (WSA_INVALID_EVENT == event_notify_monitor_thread)
return -1;
HANDLE hthread;
hthread = CreateThread(NULL, // default security
0, // stack size
libwinet_monitor_route_thread_proc,
(LPVOID)mypipe,
0, // startup flags
NULL
);
if (hthread == NULL) {
CLOSESOCKEVENT(event_notify_monitor_thread);
event_notify_monitor_thread = WSA_INVALID_EVENT;
return -1;
}
return 0;
}
int cyginet_stop_monitor_route_changes()
{
int rc = 0;
/* Notify thread to quit */
WSASetEvent(event_notify_monitor_thread);
CLOSESOCKEVENT(event_notify_monitor_thread);
/* Waiting for thread exit in 5 seconds */
event_notify_monitor_thread = WSA_INVALID_EVENT;
return rc;
}
/* Find an interface which binds both of ipv4 and ipv6, and configured
with at least one unicast address.
Return ipv6 ifindex of this interface, set addr6 and addr as the
ipv4 and ipv6 address respectively.
Return 0 if there is no matched interface found.
*/
int
cyginet_blackhole_index(struct in6_addr* addr6, char * addr)
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD dwReturn = 0;
dwRet = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return 0;
dwRet = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
if (pTmpAdaptAddr -> IfIndex
&& pTmpAdaptAddr -> Ipv6IfIndex
&& (pTmpAdaptAddr -> OperStatus == IfOperStatusUp)
&& (pTmpAdaptAddr -> IfType != IF_TYPE_SOFTWARE_LOOPBACK)
&& !wcscmp(pTmpAdaptAddr -> FriendlyName, L"blackhole")) {
PIP_ADAPTER_UNICAST_ADDRESS p = pTmpAdaptAddr -> FirstUnicastAddress;
while (p) {
SOCKADDR *s;
s = (p -> Address).lpSockaddr;
if (s -> sa_family == AF_INET) {
if (addr)
memcpy(addr, &(((struct sockaddr_in *)s) -> sin_addr), 4);
}
else if (s -> sa_family == AF_INET6) {
if (addr6)
memcpy(addr6, &(((struct sockaddr_in6 *)s) -> sin6_addr), 16);
}
p = p -> Next;
}
dwReturn = pTmpAdaptAddr -> Ipv6IfIndex;
break;
}
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
return dwReturn;
}
/*
* There are 3 ways to change a route:
*
* Before Windows Vista
*
* 1. IPv4 route: CreateIpForwardEntry
* DeleteIpForwardEntry
* SetIpForwardEntry
*
* Or route command
*
* Or netsh routing add persistentroute
*
* Or netsh routing add rtmroute
*
* it need "Routing and Remote Access Service" running on the local
* machine. Use 'net start remoteaccess' on the local machine to
* start the service.
*
* 2. IPv6 route: command "netsh"
*
* C:/> netsh interface ipv6 add route
* prefix=<IPv6 address>/<integer>
* interface=]<string>
* nexthop=<IPv6 address>
* metric=<integer>
*
* Example:
*
* add route prefix=3ffe::/16 interface=1 nexthop=fe80::1
*
* In Windows Vista and later
*
* 3. API: CreateIpForwardEntry2
* DeleteIpForwardEntry2
* SetIpForwardEntry2
*
*/
static int
libwinet_edit_route_entry(const struct sockaddr *dest,
unsigned short plen,
const struct sockaddr *gate,
int ifindex,
unsigned int metric,
int cmdflag)
{
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
/* Add ipv6 route before Windows Vista */
if(dest->sa_family == AF_INET6) {
const int MAX_BUFFER_SIZE = 1024;
const char * cmdformat = "netsh interface ipv6 %s route "
"prefix=%s/%d interface=%d "
"nexthop=%s %s %cmetric=%d";
char cmdbuf[MAX_BUFFER_SIZE];
char sdest[INET6_ADDRSTRLEN];
char sgate[INET6_ADDRSTRLEN];
if (NULL == inet_ntop(AF_INET6,
(const void*)(&(((SOCKADDR_IN6*)dest)->sin6_addr)),
sdest,
INET6_ADDRSTRLEN
))
return -1;
if (NULL == inet_ntop(AF_INET6,
(const void*)(&(((SOCKADDR_IN6*)gate)->sin6_addr)),
sgate,
INET6_ADDRSTRLEN
))
return -1;
/* metric clause results delete route command failed, so we add
'#' to commet this clause when delete route. */
if (snprintf(cmdbuf,
MAX_BUFFER_SIZE,
cmdformat,
cmdflag == RTM_ADD ? "add" :
cmdflag == RTM_DELETE ? "delete" : "set",
sdest,
plen,
ifindex,
sgate,
cmdflag == RTM_ADD ? "publish=yes" : "",
cmdflag == RTM_DELETE ? '#' : ' ',
metric
) >= MAX_BUFFER_SIZE)
return -1;
if (libwinet_run_command(cmdbuf) != 0)
return -1;
}
/* Add ipv4 route before Windows Vista, use IP Helper API */
else {
MIB_IPFORWARDROW Row;
unsigned long Res;
struct in_addr mask;
plen2mask(plen, &mask);
memset(&Row, 0, sizeof(MIB_IPFORWARDROW));
Row.dwForwardDest = (((SOCKADDR_IN*)dest) -> sin_addr).S_un.S_addr;
Row.dwForwardPolicy = 0;
Row.dwForwardNextHop = (((SOCKADDR_IN*)gate) -> sin_addr).S_un.S_addr;
Row.dwForwardIfIndex = libwinet_map_ifindex(AF_INET6, ifindex);
Row.dwForwardMask = mask.S_un.S_addr;
/*
* MIB_IPROUTE_TYPE_DIRECT <==> dwForwardNextHop == dwForwardDest
* MIB_IPROUTE_TYPE_INDIRECT all the others
* Refer to:
* http://technet.microsoft.com/en-us/library/dd379495(v=ws.10).aspx
*/
Row.dwForwardType = (Row.dwForwardNextHop == Row.dwForwardDest) ?
MIB_IPROUTE_TYPE_DIRECT : MIB_IPROUTE_TYPE_INDIRECT;
Row.dwForwardProto = MIB_IPPROTO_NETMGMT;
Row.dwForwardAge = 0;
Row.dwForwardNextHopAS = 0;
Row.dwForwardMetric1 = metric;
Row.dwForwardMetric2 = -1;
Row.dwForwardMetric3 = -1;
Row.dwForwardMetric4 = -1;
Row.dwForwardMetric5 = -1;
switch(cmdflag) {
case RTM_ADD:
Res = CreateIpForwardEntry(&Row);
break;
case RTM_DELETE:
Res = DeleteIpForwardEntry(&Row);
break;
case RTM_CHANGE:
Res = SetIpForwardEntry(&Row);
break;
default:
Res = -1;
break;
}
if (Res != NO_ERROR)
return -1;
}
#if 0 /* Use route command */
else {
/* route ADD dest MASK mask gate METRIC n IF index */
/* route CHANGE dest MASK mask gate METRIC n IF index */
/* route DELETE dest MASK mask gate METRIC n IF index */
const int MAX_BUFFER_SIZE = 1024;
char cmdbuf[MAX_BUFFER_SIZE];
char sdest[INET_ADDRSTRLEN];
char sgate[INET_ADDRSTRLEN];
char smask[INET_ADDRSTRLEN];
struct in_addr mask;
plen2mask(plen, &mask);
if (NULL == inet_ntop(AF_INET,
(const void*)(&(((SOCKADDR_IN*)dest)->sin_addr)),
sdest,
INET_ADDRSTRLEN
))
return -1;
if (NULL == inet_ntop(AF_INET,
(const void*)(&(((SOCKADDR_IN*)gate)->sin_addr)),
sgate,
INET_ADDRSTRLEN
))
return -1;
if (NULL == inet_ntop(AF_INET,
(const void*)(&mask),
smask,
INET_ADDRSTRLEN
))
return -1;
if (snprintf(cmdbuf,
MAX_BUFFER_SIZE,
"route %s %s MASK %s %s METRIC %d IF %d",
cmdflag == RTM_ADD ? "add" :
cmdflag == RTM_DELETE ? "delete" : "change",
sdest,
smask,
sgate,
metric,
libwinet_map_ifindex(AF_INET6, ifindex)
) >= MAX_BUFFER_SIZE)
return -1;
if (libwinet_run_command(cmdbuf) != 0)
return -1;
}
#endif /* if 0 */
#else
/* Add route entry after Windows Vista */
MIB_IPFORWARDROW2 Row2;
unsigned long Res;
memset(&Row2, 0, sizeof(MIB_IPFORWARDROW2));
Row2.InterfaceLuid = NULL;
/* Maybe in the Vista, both of indexs are same. */
Row2.InterfaceIndex = dest->sa_family == AF_INET6 ?
ifindex : libwinet_map_ifindex(AF_INET6, ifindex);
Row2.DestinationPrefix.PrefixLength = plen;
memcpy(&Row2.DestinationPrefix.Prefix, dest, sizeof(SOCKADDR_INET));
memcpy(&Row2.NextHop, gate, sizeof(SOCKADDR_INET)) ;
Row2.SitePrefixLength = 255; /* INVALID */
Row2.ValidLifetime = WSA_INFINITE;;
Row2.PreferredLifetime = WSA_INFINITE;
Row2.Metric = metric;
Row2.Protocol = MIB_IPPROTO_NETMGMT;
Row2.Loopback = gate->sa_family == AF_INET6 ?
IN6_IS_ADDR_LOOPBACK(&(((SOCKADDR_IN6*)gate)->sin6_addr)) :
IN_LOOPBACK(ntohl(((SOCKADDR_IN*)gate)->sin_addr.S_un.S_addr));
Row2.AutoconfigureAddress = FALSE;
Row2.Publish = FALSE;
Row2.Immortal = 0;
Row2.Age = 0;
Row2.Origin = 0; /* NlroManual */
switch(cmdflag) {
case 0:
Res = CreateIpForwardEntry2(&Row);
break;
case 1:
Res = SetIpForwardEntry2(&Row);
break;
case 2:
Res = DeleteIpForwardEntry2(&Row);
break;
}
if (Res != NO_ERROR)
return -1;
}
#endif /* _WIN32_WINNT < _WIN32_WINNT_VISTA */
return 1;
}
int
cyginet_set_interface_forwards(const char * ifname, int value)
{
/* For ipv4 */
/* netsh routing add interface ifname enabled/disabled */
char cmdbuf[255];
if (snprintf(cmdbuf,
255,
"netsh interface ipv6 set interface \"%s\" "
"forwarding=%s > /dev/null",
ifname,
value ? "enabled" : "disabled"
) > 0)
return system(cmdbuf);
return -1;
}
int
cyginet_set_icmp6_redirect_accept(int value)
{
char * key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
char * name = "EnableICMPRedirect";
return libwinet_set_registry_key(key,
name,
value,
1
);
}
static void
libwinet_restore_ipv6_interface()
{
char cmdbuf[255];
int rc=0;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
if (p -> RouteFlags != -1)
if (snprintf(cmdbuf,
255,
"netsh interface ipv6 set interface \"%s\" "
"forwarding=%s > /dev/null",
p -> FriendlyName,
p -> RouteFlags ? "enabled" : "disabled"
) > 0)
rc = system(cmdbuf);
p = p -> next;
}
}
#if 0
int
cyginet_set_ipv6_forwards(int value)
{
char * key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
char * name = "IPEnableRouter";
return libwinet_set_registry_key(key,
name,
value,
0
);
}
static int
libwinet_ipv6_interfaces_forwards(int forward)
{
const int MAX_BUFFER_SIZE = 255;
char cmdbuf[MAX_BUFFER_SIZE];
int result;
struct if_nameindex * p;
struct if_nameindex * ptr;
if (NULL == (ptr = (struct if_nameindex *)if_nameindex()))
return -1;
p = ptr;
while (p -> if_index) {
if (snprintf(cmdbuf,
MAX_BUFFER_SIZE,
"netsh interface ipv6 set interface %d forwarding=%s",
p -> if_index,
forward ? "enabled" : "disabled"
) >= MAX_BUFFER_SIZE)
break;
if (libwinet_run_command(cmdbuf) != 0)
break;
p ++;
}
result = ! (p -> if_index);
if_freenameindex(ptr);
return result;
}
static void
libwinet_init_ipv6_interface()
{
char cmdbuf[255];
int rc=0;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
if (snprintf(cmdbuf,
255,
"netsh interface ipv6 set interface \"%s\" " \
"forwarding=enabled advertise=enabled > /dev/null",
p -> FriendlyName
) > 0) {
rc = system(cmdbuf);
}
p = p -> next;
}
}
#endif /* if 0 */
/*
* On Windows Vista and later, wireless network cards are reported as
* IF_TYPE_IEEE80211 by the GetAdaptersAddresses function.
*
* On earlier versions of Windows, wireless network cards are reported
* as IF_TYPE_ETHERNET_CSMACD. On Windows XP with SP3 and on Windows
* XP with SP2 x86 with the Wireless LAN API for Windows XP with SP2
* installed, the WlanEnumInterfaces function can be used to enumerate
* wireless interfaces on the local computer.
*/
int cyginet_interface_wireless(const char *ifname, int ifindex)
{
LIBWINET_INTERFACE winf;
if (1 == libwinet_get_interface_info(ifname, &winf)) {
if (IF_TYPE_IEEE80211 == winf.IfType)
return 1;
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
if (IF_TYPE_ETHERNET_CSMACD == winf.IfType) {
return libwinet_is_wireless_interface(ifname);
}
#endif
return 0;
}
return -1;
}
int
cyginet_interface_mtu(const char *ifname, int ifindex)
{
LIBWINET_INTERFACE winf;
if (1 == libwinet_get_interface_info(ifname, &winf))
return winf.Mtu;
return -1;
}
int
cyginet_interface_operational(const char *ifname, int ifindex)
{
LIBWINET_INTERFACE winf;
int rc = -1;
if (1 == libwinet_get_interface_info(ifname, &winf)) {
switch (winf.OperStatus) {
case IfOperStatusUp:
rc = IFF_UP;
break;
case IfOperStatusDormant:
rc = IFF_RUNNING;
break;
default:
rc = 0;
}
}
return rc;
}
int
cyginet_interface_ipv4(const char *ifname,
int ifindex,
unsigned char *addr_r)
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD dwReturn = 0;
ULONG family = AF_INET;
WCHAR *friendlyname;
size_t size;
size = MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
NULL,
0
);
friendlyname = MALLOC(size * sizeof(WCHAR));
if (!friendlyname)
return -1;
if (MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
friendlyname,
size
) == 0) {
FREE(friendlyname);
return -1;
}
dwRet = GetAdaptersAddresses(family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return -1;
dwRet = GetAdaptersAddresses(family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
if (wcscmp(friendlyname, pTmpAdaptAddr -> FriendlyName) == 0) {
/* Copy first unicast address */
if (pTmpAdaptAddr -> FirstUnicastAddress) {
SOCKADDR *s;
s = (pTmpAdaptAddr -> FirstUnicastAddress -> Address).lpSockaddr;
memcpy(addr_r, &(((struct sockaddr_in *)s) -> sin_addr), 4);
dwReturn = 1;
if (pTmpAdaptAddr->FirstUnicastAddress->Next)
fprintf(stderr,
"Warning: more than one ipv4 address configured"
"in the interface '%s', but only the first one returned\n",
ifname
);
break;
}
}
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
return dwReturn;
}
int
cyginet_interface_sdl(struct sockaddr_dl *sdl, char *ifname)
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD dwReturn = -1;
DWORD dwFamily = AF_INET6;
size_t size;
WCHAR *friendlyname;
size = MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
NULL,
0
);
friendlyname = MALLOC(size * sizeof(WCHAR));
if (!friendlyname)
return -1;
if (MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
friendlyname,
size
) == 0) {
FREE(friendlyname);
return -1;
}
dwRet = GetAdaptersAddresses(dwFamily,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize))){
FREE(friendlyname);
return -1;
}
dwRet = GetAdaptersAddresses(dwFamily,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
if (wcscmp(pTmpAdaptAddr -> FriendlyName, friendlyname) == 0) {
size = strlen(ifname);
sdl -> sdl_family = 0;
sdl -> sdl_index = pTmpAdaptAddr -> Ipv6IfIndex;
sdl -> sdl_type = pTmpAdaptAddr -> IfType;
sdl -> sdl_nlen = size;
sdl -> sdl_alen = pTmpAdaptAddr -> PhysicalAddressLength;
sdl -> sdl_slen = 0;
memcpy(sdl -> sdl_data, ifname, size);
memcpy(sdl -> sdl_data + size,
pTmpAdaptAddr -> PhysicalAddress,
pTmpAdaptAddr -> PhysicalAddressLength
);
sdl -> sdl_len = ((void*)(sdl->sdl_data) - (void*)sdl) + size \
+ pTmpAdaptAddr -> PhysicalAddressLength;
dwReturn = 0;
break;
}
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
FREE(friendlyname);
return dwReturn;
}
int
cyginet_getifaddresses(char *ifname,
struct cyginet_route *routes,
int maxroutes
)
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD dwReturn = 0;
size_t size;
WCHAR *friendlyname = 0;
if (ifname) {
size = MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
NULL,
0
);
friendlyname = MALLOC(size * sizeof(WCHAR));
if (!friendlyname)
return -1;
if (MultiByteToWideChar(CP_ACP,
0,
ifname,
-1,
friendlyname,
size
) == 0) {
FREE(friendlyname);
return -1;
}
}
dwRet = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return -1;
dwRet = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
if ((pTmpAdaptAddr -> OperStatus == IfOperStatusUp) &&
((ifname == NULL) ||
(wcscmp(pTmpAdaptAddr -> FriendlyName, friendlyname) == 0))) {
PIP_ADAPTER_UNICAST_ADDRESS p = pTmpAdaptAddr -> FirstUnicastAddress;
while (p) {
if (p -> ValidLifetime) {
SOCKET_ADDRESS *s = &(p -> Address);
memcpy(&routes[dwReturn].prefix,
s -> lpSockaddr,
s -> iSockaddrLength
);
dwReturn += 1;
if (dwReturn == maxroutes)
break;
}
p = p -> Next;
}
if (ifname)
break;
}
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
return dwReturn;
}
/* In the windows, loopback interface index is alawys 1 */
int
cyginet_loopback_index(int family)
{
return 1;
}
/*
* There are 3 ways to dump route table in the Windows:
*
* Before Windows Vista
*
* 1. IPv4 route: GetIpForwardTable
*
* 2. IPv6 route: command "netsh"
*
* C:/> netsh interface ipv6 show route verbose
*
* In Windows Vista and later
*
* 3. API: GetIpForwardTable2
*
*/
int
cyginet_dump_route_table(struct cyginet_route *routes, int maxroutes)
{
ULONG NumEntries = -1;
struct cyginet_route *proute;
int i;
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
/* First dump ipv6 route */
NumEntries = libwinet_dump_ipv6_route_table(routes, maxroutes);
if (NumEntries < 0)
return -1;
/* Then ipv4 route table */
SOCKADDR_IN * paddr;
PMIB_IPFORWARDTABLE pIpForwardTable;
PMIB_IPFORWARDROW pRow;
DWORD dwSize = sizeof(MIB_IPFORWARDTABLE);
pIpForwardTable = (PMIB_IPFORWARDTABLE)MALLOC(dwSize);
if (NULL == pIpForwardTable)
return -1;
if (ERROR_INSUFFICIENT_BUFFER == GetIpForwardTable(pIpForwardTable,
&dwSize,
0
)) {
FREE(pIpForwardTable);
pIpForwardTable = (PMIB_IPFORWARDTABLE) MALLOC(dwSize);
if (pIpForwardTable == NULL)
return -1;
}
if (NO_ERROR != GetIpForwardTable(pIpForwardTable,
&dwSize,
0))
return -1;
proute = routes + NumEntries;
NumEntries += pIpForwardTable->dwNumEntries;
if ((routes == NULL) || (NumEntries > maxroutes)) {
FREE(pIpForwardTable);
return NumEntries;
}
pRow = pIpForwardTable->table;
for (i = 0;
i < (int) pIpForwardTable->dwNumEntries;
i++, proute ++, pRow ++) {
/* Map Ipv4 ifindex to Ipv6 Ifindex, maybe return 0 */
proute -> ifindex = libwinet_map_ifindex(AF_INET, pRow -> dwForwardIfIndex);
proute -> metric = pRow -> dwForwardMetric1;
proute -> proto = pRow -> dwForwardProto;
proute -> plen = mask2len((unsigned char*)&(pRow -> dwForwardMask), 4);
/* Note that the IPv4 addresses returned in GetIpForwardTable
* entries are in network byte order
*/
paddr = (struct sockaddr_in*)&(proute -> prefix);
paddr -> sin_family = AF_INET;
(paddr -> sin_addr).S_un.S_addr = pRow -> dwForwardDest;
paddr = (struct sockaddr_in*)&(proute -> gateway);
paddr -> sin_family = AF_INET;
(paddr -> sin_addr).S_un.S_addr = pRow -> dwForwardNextHop;
}
FREE(pIpForwardTable);
#else
PMIB_IPFORWARD_TABLE2 pIpForwardTable2;
PMIB_IPFORWARD_ROW2 pRow2;
/* From Windows Vista later, use GetIpForwardTable2 instead */
if (NO_ERROR == GetIpForwardTable2(family,
pIpForwardTable2
0)) {
NumEntries = pIpForwardTable2->dwNumEntries;
if ((routes == NULL) || (NumEntries > maxroutes)) {
FreeMibTable(pIpForwardTable2);
return NumEntries;
}
proute = routes;
NumEntries = pIpForwardTable2->dwNumEntries;
pRow2 = pIpForwardTable2 -> Table;
for (i = 0; i < NumEntries; i++, proute ++, pRow2 ++) {
proute -> ifindex = pRow2 -> InterfaceIndex;
proute -> metric = pRow2 -> Metric;
proute -> proto = pRow2 -> Protocol;
proute -> plen = (pRow2 -> DestinationPrefix).PrefixLength;
memcpy(proute -> prefix,
(pRow2 -> DestinationPrefix).DestinationPrefix,
sizeof(SOCKADDR_INET)
);
memcpy(proute -> gateway,
pRow2 -> NextHop,
sizeof(SOCKADDR_INET)
);
}
FreeMibTable(pIpForwardTable2);
}
#endif
return NumEntries;
}
int
cyginet_add_route_entry(const struct sockaddr *dest,
unsigned short plen,
const struct sockaddr *gate,
int ifindex,
unsigned int metric)
{
return libwinet_edit_route_entry(dest, plen, gate, ifindex, metric, 1);
}
int
cyginet_delete_route_entry(const struct sockaddr *dest,
unsigned short plen,
const struct sockaddr *gate,
int ifindex,
unsigned int metric)
{
return libwinet_edit_route_entry(dest, plen, gate, ifindex, metric, 2);
}
int
cyginet_update_route_entry(const struct sockaddr *dest,
unsigned short plen,
const struct sockaddr *gate,
int ifindex,
unsigned int metric)
{
return libwinet_edit_route_entry(dest, plen, gate, ifindex, metric, 3);
}
/*
* This function is used to read route socket to get changes of route
* table, and return a struct rt_msghdr in the buffer. However I can't
* find windows API to implement it.
*/
int
cyginet_read_route_socket(void *buffer, size_t size)
{
/* TODO */
return 0;
}
int
cyginet_refresh_interface_table()
{
return libwinet_refresh_interface_map_table();
}
int
cyginet_startup()
{
WORD wVersionRequested;
WSADATA wsaData;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
return WSAStartup(wVersionRequested, &wsaData);
}
void
cyginet_cleanup()
{
libwinet_restore_ipv6_interface();
libwinet_free_interface_map_table();
WSACleanup();
}
char *
cyginet_guidname(const char * ifname)
{
PLIBWINET_INTERFACE_MAP_TABLE p;
if (!g_interface_map_table)
libwinet_refresh_interface_map_table();
p = g_interface_map_table;
while (p) {
if (strcmp(ifname, p -> FriendlyName) == 0)
return p -> AdapterName;
p = p -> next;
}
return NULL;
}
char *
cyginet_ifname(const char * guidname)
{
PLIBWINET_INTERFACE_MAP_TABLE p;
if (!g_interface_map_table)
libwinet_refresh_interface_map_table();
p = g_interface_map_table;
while (p) {
if (strcmp(guidname, p -> AdapterName) == 0)
return p -> FriendlyName;
p = p -> next;
}
return NULL;
}
static int
libwinet_edit_netentry(int operation,
int ifindex,
struct sockaddr *addr,
int type)
{
MIB_IPNETROW row = {0};
DWORD dwRetVal = NO_ERROR;
DWORD dest = (((SOCKADDR_IN*)addr) -> sin_addr).S_un.S_addr;
if (operation == 1) {
/* We need send an arp request to get mac address */
/* TO DO: src should be address assigned to ifindex */
DWORD src = 0;
if (type != MIB_IPNET_TYPE_INVALID) {
dwRetVal = SendARP(dest, src, (PULONG)row.bPhysAddr, &row.dwPhysAddrLen);
if (dwRetVal != NO_ERROR)
return -1;
}
row.dwIndex = ifindex;
row.dwAddr = dest;
row.dwType = MIB_IPNET_TYPE_DYNAMIC;
dwRetVal = CreateIpNetEntry ( &row );
}
else if (operation == 0) {
row.dwIndex = ifindex;
row.dwAddr = dest;
dwRetVal = DeleteIpNetEntry(&row);
}
else assert(0);
return 0 ? dwRetVal == NO_ERROR : dwRetVal;
}
/* The following functions are reserved. */
#if 0
int
cyginet_search_netentry(int add, int ifindex, struct sockaddr *addr)
{
MIB_IPNETROW row = {0};
MIB_IPNETTABLE * ptable;
MIB_IPNETROW * prow = NULL;
DWORD dwSize;
DWORD dwRetVal = NO_ERROR;
DWORD dest = (((SOCKADDR_IN*)addr) -> sin_addr).S_un.S_addr;
DWORD n;
ptable = (MIB_IPNETTABLE *) MALLOC(sizeof (MIB_IPNETTABLE));
if (ptable == NULL)
return -1;
/* Make an initial call to get the necessary size into dwSize */
dwSize = sizeof (MIB_IPNETTABLE);
if (GetIpNetTable(ptable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) {
FREE(ptable);
ptable = (MIB_IPNETTABLE *) MALLOC(dwSize);
if (ptable == NULL)
return -1;
}
/* Make a second call to get the actual data we want. */
if ((dwRetVal = GetIpNetTable(ptable, &dwSize, FALSE)) != NO_ERROR) {
FREE(ptable);
return -1;
}
/* Search entry in the table */
prow = ptable -> table;
for (n = 0; n < ptable -> dwNumEntries; n++, prow++)
if ( prow -> dwAddr == dest)
break;
if (add) {
if (!prow) {
/* We need send an arp request to get mac address */
/* TO DO: src should be address assigned to ifindex */
DWORD src = 0;
dwRetVal = SendARP(dest, src, (PULONG)row.bPhysAddr, &row.dwPhysAddrLen);
if (dwRetVal != NO_ERROR) {
FREE(ptable);
return -1;
}
row.dwIndex = ifindex;
row.dwAddr = dest;
row.dwType = MIB_IPNET_TYPE_DYNAMIC;
dwRetVal = CreateIpNetEntry ( &row );
}
}
else {
if (prow)
dwRetVal = DeleteIpNetEntry(prow);
}
FREE(ptable);
return 0 ? dwRetVal == NO_ERROR : dwRetVal;
}
char *
cyginet_ipv4_index2ifname(int ifindex)
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD dwFamily = AF_INET;
char * ifname = NULL;
dwRet = GetAdaptersAddresses(dwFamily,
GAA_FLAG_SKIP_UNICAST \
| GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return NULL;
dwRet = GetAdaptersAddresses(dwFamily,
GAA_FLAG_SKIP_UNICAST \
| GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
if (pTmpAdaptAddr -> IfIndex == ifindex) {
ifname = cyginet_ifname(pTmpAdaptAddr -> AdapterName);
break;
}
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
return ifname;
}
static int
libwinet_get_loopback_index(int family)
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD dwReturn = 0;
dwRet = GetAdaptersAddresses(family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER \
| GAA_FLAG_SKIP_FRIENDLY_NAME,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return 0;
dwRet = GetAdaptersAddresses(family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
| GAA_FLAG_SKIP_DNS_SERVER \
| GAA_FLAG_SKIP_FRIENDLY_NAME,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
if (IF_TYPE_SOFTWARE_LOOPBACK == pTmpAdaptAddr -> IfType) {
dwReturn = family == AF_INET ?
pTmpAdaptAddr -> IfIndex :
pTmpAdaptAddr -> Ipv6IfIndex;
/* Loopback interface doesn't support both of Ipv4 or Ipv6 */
if (dwReturn)
break;
}
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
return dwReturn;
}
static PMIB_IPFORWARDTABLE
libwinet_get_ipforward_table(int forder)
{
DWORD dwSize = sizeof(MIB_IPFORWARDTABLE);
PMIB_IPFORWARDTABLE pIpForwardTable;
pIpForwardTable = (PMIB_IPFORWARDTABLE)MALLOC(dwSize);
if (NULL == pIpForwardTable)
return NULL;
if (ERROR_INSUFFICIENT_BUFFER == GetIpForwardTable(pIpForwardTable,
&dwSize,
forder
)) {
FREE(pIpForwardTable);
pIpForwardTable = (PMIB_IPFORWARDTABLE) MALLOC(dwSize);
if (pIpForwardTable == NULL)
return NULL;
}
if (NO_ERROR == GetIpForwardTable(pIpForwardTable,
&dwSize,
forder))
return pIpForwardTable;
return NULL;
}
static int
convert_ipv6_route_table2()
{
const MAX_LINE_SIZE = 80;
const char * command = "netsh interface ipv6 show route verbose";
/* One example entry of netsh output
Prefix : fe80::1/128
Interface 1 : Loopback Pseudo-Interface
Gateway : fe80::1
Metric : 4
Publish : no
Type : System
Valid Lifetime : infinite
Preferred Lifetime: infinite
Site Prefix Length: 0
*/
FILE *output;
char buffer[MAX_LINE_SIZE];
int index = -1;
size_t size;
char *s, *p;
int ifindex;
MIB_IPFORWARD_ROW2 iprow;
MIB_IPFORWARD_ROW2 *piprow = &iprow;
output = popen (command, "r");
if (!output)
return -1;
/* Ignore the first line */
fgets(buffer, MAX_LINE_SIZE, output);
memset(piprow, 0, sizeof(MIB_IPFORWARD_ROW2));
piprow -> Protocol = MIB_IPPROTO_OTHER;
/* Read the output until EOF */
while (fgets(buffer, MAX_LINE_SIZE, output)) {
if (('\n' == buffer[0]) || ('\r' == buffer[0]))
continue;
if (NULL == (s = strchr(buffer, ':')))
break;
*s ++ = 0;
s ++;
if (strncmp(buffer, "Prefix", 6) == 0) {
index ++;
if (NULL == (p = strchr(s, '/')))
break;
*p ++ = 0;
if (WSAStringToAddress(s,
AF_INET6,
NULL,
(LPSOCKADDR)(&(piprow -> DestinationPrefix.Prefix)),
&size
) == SOCKET_ERROR)
break;
piprow -> DestinationPrefix.PrefixLength = strtol(p, NULL, 10);
}
else if (strncmp(buffer, "Interface", 9) == 0) {
ifindex = strtol(buffer + 9, NULL, 10);
piprow -> InterfaceIndex = ifindex;
}
else if (strncmp(buffer, "Gateway", 7) == 0) {
/* NextHop */
/* Loopback: A value that specifies if the route is a loopback
route (the gateway is on the local host). */
}
else if (strncmp(buffer, "Metric", 6) == 0)
piprow -> Metric = strtol(s, NULL, 10);
else if (strncmp(buffer, "Publish", 7) == 0)
piprow -> Publish = (strncmp("yes", s, 3) == 0);
else if (strncmp(buffer, "Type", 4) == 0) {
if (strncmp("Manual", s, 6) == 0) {
piprow -> Origin = NlroManual;
}
else if (strncmp("System", s, 6) == 0) {
piprow -> Origin = NlroDHCP;
}
else if (strncmp("Autoconf", s, 8) == 0) {
piprow -> Origin = Nlro6to4;
piprow -> AutoconfigureAddress = 1;
}
}
else if (strncmp(buffer, "Valid Lifetime", 14) == 0)
piprow -> ValidLifetime = convert_time_from_string_to_ulong(s);
else if (strncmp(buffer, "Preferred Lifetime", 18) == 0)
piprow -> PreferredLifetime = convert_time_from_string_to_ulong(s);
else if (strncmp(buffer, "Site Prefix Length", 18) == 0)
piprow -> SitePrefixLength = (UCHAR)strtol(s, NULL, 10);
else {
/* Immortal: persistent entry */
/* Age */
break;
}
}
/* Return the exit code of command */
return pclose (output);
}
/*
* Output format: struct rt_msghdr
*
* rtm_msglen
*
* rtm_type = RTM_GET
*
* rtm_index = IfIndex for Ipv4
* Ipv6IfIndex for Ipv6
*
* rtm_flags = RTF_HOST: All netmask bits are 1.
* RTF_GATEWAY: Destination is a gateway.
*
* rtm_addrs = RTA_DST RTA_GATEWAY RTA_NETMASK
* rtm_errno Set when failed.
*
* RTF_HOST: Set when all netmask bits are 1
*
* RTF_GATEWAY: Set when gateway is not local address.
* dwForwardNextHop is not
*
*/
static int
convert_ipv6_route_table_to_rtm(struct rt_msghdr *rtm,
int maxroutes)
{
const MAX_LINE_SIZE = 80;
const char * command = "netsh interface ipv6 show route verbose";
/* One example entry of netsh output
Prefix : fe80::1/128
Interface 1 : Loopback Pseudo-Interface
Gateway : fe80::1
Metric : 4
Publish : no
Type : System
Valid Lifetime : infinite
Preferred Lifetime: infinite
Site Prefix Length: 0
*/
FILE *output;
char buffer[MAX_LINE_SIZE];
int index = -1;
size_t size;
char *s, *p;
int ifindex;
int start = 0;
SOCKADDR *sa;
int msg_len = sizeof(struct rt_msghdr) + 3 * sizeof(SOCKADDR);
output = popen (command, "r");
if (!output)
return -1;
/* Ignore the first line */
fgets(buffer, MAX_LINE_SIZE, output);
/* Read the output until EOF */
while (fgets(buffer, MAX_LINE_SIZE, output)) {
if (('\n' == buffer[0]) || ('\r' == buffer[0]))
continue;
if (NULL == (s = strchr(buffer, ':')))
break;
*s ++ = 0;
s ++;
if (strncmp(buffer, "Prefix", 6) == 0) {
rtm -> rtm_msglen = msg_len;
rtm -> rtm_version = RTM_VERSION;
rtm -> rtm_type = RTM_GET;
rtm -> rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
sa = (SOCKADDR*)(rtm + 1);
if (NULL == (p = strchr(s, '/')))
break;
*p ++ = 0;
if (WSAStringToAddress(s,
AF_INET6,
NULL,
sa,
&size
) == SOCKET_ERROR)
break;
// PrefixLength = strtol(p, NULL, 10);
sa ++;
}
else if (strncmp(buffer, "Interface", 9) == 0)
rtm -> rtm_index = strtol(buffer + 9, NULL, 10);
else if (strncmp(buffer, "Gateway", 7) == 0) {
/* NextHop */
/* Loopback: A value that specifies if the route is a loopback
route (the gateway is on the local host). */
if (WSAStringToAddress(s,
AF_INET6,
NULL,
sa,
&size
) == SOCKET_ERROR)
break;
sa ++;
/* Netmask */
}
else if (strncmp(buffer, "Site Prefix Length", 18) == 0) {
rtm = (struct rt_msghdr*)((void*)rtm + msg_len);
start ++;
if (start > maxroutes)
break;
}
else {
/* Immortal: persistent entry */
/* Age */
}
}
pclose (output);
return start;
}
static ULONG
convert_time_from_string_to_ulong(const char * stime)
{
char *s;
long k;
long result;
if (strncmp(stime, "infinite", 8) == 0)
return WSA_INFINITE;
result = 0;
s = (char*)stime;
do {
k = strtol(s, &s, 10);
if (*s == 's') {
result += k;
s = NULL;
}
else if (*s == 'm') {
result += 60 * k;
s ++;
}
else if (*s == 'h') {
result += 3600 * k;
s ++;
}
else if (*s == 'd') {
result += 3600 * 24 * k;
s ++;
}
else s = NULL;
} while (s);
return result;
}
/*
* It's another way to tell wireless netcard, query device status by
* DeviceIoControl.
*
*/
#if !defined OID_802_11_CONFIGURATION
#define OID_802_11_CONFIGURATION 0x0d010211
#endif
#if !defined IOCTL_NDIS_QUERY_GLOBAL_STATS
#define IOCTL_NDIS_QUERY_GLOBAL_STATS 0x00170002
#endif
static int
libwinet_is_wireless_device(const wchar_t *pszwAdapterName)
{
const int MAX_DEV_NAME_LEN = 45;
const int MAX_OUT_BUF_SIZE = 100;
wchar_t DevName[MAX_DEV_NAME_LEN];
HANDLE DevHand;
unsigned int ErrNo;
unsigned int Oid;
unsigned char OutBuff[MAX_OUT_BUF_SIZE];
unsigned long OutBytes;
if (swprintf(DevName,
MAX_DEV_NAME_LEN,
L"\\\\.\\%ls",
pszwAdapterName
) >= MAX_DEV_NAME_LEN)
return -1;
DevHand = CreateFileW(DevName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (DevHand == INVALID_HANDLE_VALUE) {
ErrNo = GetLastError();
return -1;
}
Oid = OID_802_11_CONFIGURATION;
if (!DeviceIoControl(DevHand,
IOCTL_NDIS_QUERY_GLOBAL_STATS,
&Oid,
sizeof(Oid),
OutBuff,
sizeof(OutBuff),
&OutBytes,
NULL
)) {
ErrNo = GetLastError();
CloseHandle(DevHand);
/* OID not supported. Device probably not wireless. */
if ((ErrNo == ERROR_GEN_FAILURE)
|| (ErrNo == ERROR_INVALID_PARAMETER)
|| ErrNo == ERROR_NOT_SUPPORTED) {
return 0;
}
/* DeviceIoControl() Error */
return -1;
}
CloseHandle(DevHand);
return 1;
}
BOOL RouteLookup(SOCKADDR *destAddr,
int destLen,
SOCKADDR *localAddr,
int localLen)
{
DWORD dwBytes = 0;
BOOL bRet = TRUE;
CHAR szAddr[MAX_PATH] = {0};
SOCKET s = INVALID_SOCKET;
if (INVALID_SOCKET == (s = socket(destAddr->sa_family,SOCK_DGRAM,0)))
{
SOCKETERR("socket");
return FALSE;
}
if (SOCKET_ERROR == WSAIoctl(s,
SIO_ROUTING_INTERFACE_QUERY,
destAddr,
destLen,
localAddr,
localLen,
&dwBytes,
NULL,
NULL
))
{
SOCKETERR("WSAIoctl");
bRet = FALSE;
}
if (bRet)
{
dwBytes = sizeof(szAddr);
ZeroMemory(szAddr,dwBytes);
WSAAddressToStringA(destAddr,
(DWORD)destLen,
NULL,
szAddr,
&dwBytes
);
dwBytes = sizeof(szAddr);
ZeroMemory(szAddr,dwBytes);
WSAAddressToStringA(localAddr,
(DWORD)localLen,
NULL,
szAddr,
&dwBytes
);
}
CLOSESOCKET(s);
return bRet;
}
DWORD GetInterfaceIndexForAddress(SOCKADDR *pAddr)
{
IP_ADAPTER_UNICAST_ADDRESS *pTmpUniAddr = NULL;
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
BOOL bFound = FALSE;
DWORD dwRet = 0;
DWORD dwReturn = (DWORD) SOCKET_ERROR;
DWORD dwSize = 0x10000;
DWORD Family = AF_UNSPEC;
switch (pAddr->sa_family) {
case AF_INET:
Family = AF_INET;
break;
case AF_INET6:
Family = AF_INET6;
break;
default:
WSASetLastError(WSAEAFNOSUPPORT);
break;
}
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return -1;
dwRet = GetAdaptersAddresses(Family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
|GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
return -1;
dwRet = GetAdaptersAddresses(Family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
|GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
//look at each IP_ADAPTER_UNICAST_ADDRESS node
pTmpUniAddr = pTmpAdaptAddr->FirstUnicastAddress;
while (pTmpUniAddr) {
if (AF_INET == pTmpUniAddr->Address.lpSockaddr->sa_family) {
/* IN4_ADDR_EQUAL */
if (memcmp(&((SOCKADDR_IN*)pAddr)->sin_addr,
&((SOCKADDR_IN*)pTmpUniAddr->Address.lpSockaddr)->sin_addr,
sizeof(SOCKADDR_IN)
) == 0)
{
dwReturn = pTmpAdaptAddr->IfIndex;
bFound = TRUE;
break;
}
}
else {
/* IN6_ADDR_EQUAL */
if (memcmp(&((SOCKADDR_IN6*)pAddr)->sin6_addr,
&((SOCKADDR_IN6*)pTmpUniAddr->Address.lpSockaddr)->sin6_addr,
sizeof(SOCKADDR_IN6)
) == 0)
{
dwReturn = pTmpAdaptAddr->Ipv6IfIndex;
bFound = TRUE;
break;
}
}
pTmpUniAddr = pTmpUniAddr->Next;
}
if (bFound)
break;
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
FREE(pAdaptAddr);
}
return dwReturn;
}
void WaitForNetworkChnages()
{
WSAQUERYSET querySet = {0};
querySet.dwSize = sizeof(WSAQUERYSET);
querySet.dwNameSpace = NS_NLA;
HANDLE LookupHandle = NULL;
WSALookupServiceBegin(&querySet, LUP_RETURN_ALL, &LookupHandle);
DWORD BytesReturned = 0;
WSANSPIoctl(LookupHandle,
SIO_NSP_NOTIFY_CHANGE,
NULL,
0,
NULL,
0,
&BytesReturned,
NULL
);
WSALookupServiceEnd(LookupHandle);
}
DWORD GetConnectedNetworks()
{
WSAQUERYSET qsRestrictions;
DWORD dwControlFlags;
HANDLE hLookup;
DWORD dwCount = 0;
ZeroMemory(&qsRestrictions, sizeof(WSAQUERYSET));
qsRestrictions.dwSize = sizeof(WSAQUERYSET);
qsRestrictions.dwNameSpace = NS_ALL;
dwControlFlags = LUP_RETURN_ALL;
int result = WSALookupServiceBegin(&qsRestrictions,
dwControlFlags, &hLookup);
DWORD dwBufferLength;
WSAQUERYSET qsResult;
while (0 == result)
{
ZeroMemory(&qsResult, sizeof(WSAQUERYSET));
result = WSALookupServiceNext(hLookup,
LUP_RETURN_NAME,
&dwBufferLength,
&qsResult
);
dwCount ++;
}
result = WSALookupServiceEnd(hLookup);
return dwCount;
}
#endif /* Unused */
/* ------------------------------------------------------------- */
/* */
/* The following functions are used to test or verify something. */
/* */
/* ------------------------------------------------------------- */
#ifdef TEST_CYGINET
VOID PrintAllInterfaces()
{
IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
DWORD dwRet = 0;
DWORD dwSize = 0x10000;
DWORD Family = AF_UNSPEC;
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize))) {
printf("Memory error.\n");
return ;
}
dwRet = GetAdaptersAddresses(Family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
|GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
if (ERROR_BUFFER_OVERFLOW == dwRet) {
FREE(pAdaptAddr);
if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize))) {
printf("Memory error.\n");
return ;
}
dwRet = GetAdaptersAddresses(Family,
GAA_FLAG_SKIP_ANYCAST \
| GAA_FLAG_SKIP_MULTICAST \
|GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdaptAddr,
&dwSize
);
}
if (NO_ERROR == dwRet) {
pTmpAdaptAddr = pAdaptAddr;
while (pTmpAdaptAddr) {
printf("If6Index:\t %ld\n", pTmpAdaptAddr -> Ipv6IfIndex);
printf("If4Index:\t %ld\n", pTmpAdaptAddr -> IfIndex);
printf("Friendly Name:\t %S\n", pTmpAdaptAddr -> FriendlyName);
printf("Adapter Name:\t %s\n", pTmpAdaptAddr -> AdapterName);
printf("IfType:\t %ld\n", pTmpAdaptAddr -> IfType);
printf("Oper Status:\t %d\n", pTmpAdaptAddr -> OperStatus);
printf("\n\n");
pTmpAdaptAddr = pTmpAdaptAddr->Next;
}
}
else
printf("GetAdaptersAddresses failed.\n");
FREE(pAdaptAddr);
}
static void
runTestCases()
{
printf("\n\nTest getifaddrs works in the Cygwin:\n\n");
{
struct ifaddrs * piftable, *pif;
getifaddrs(&piftable);
for (pif = piftable; pif != NULL; pif = pif -> ifa_next)
printf("Iterface name is %s\n", pif -> ifa_name);
freeifaddrs(piftable);
}
printf("\n\nTest if_indexname works in the Cygwin:\n\n");
{
struct if_nameindex * ptr = (struct if_nameindex *)if_nameindex();
if (ptr) {
struct if_nameindex * p = ptr;
while (p -> if_index) {
printf("%d\t\t%s\n", p -> if_index, p -> if_name);
p ++;
}
if_freenameindex(ptr);
}
}
printf("\n\nTest if_indextoname works in the Cygwin:\n\n");
{
CHAR ifname[256];
if (if_indextoname(1, ifname))
printf("Interface Index 1: %s\n", ifname);
else
printf("if_indextoname failed\n");
}
printf("\n\nTest cyginet_ifname works in the Cygwin:\n\n");
{
struct if_nameindex * ptr = (struct if_nameindex *)if_nameindex();
if (ptr) {
struct if_nameindex * p = ptr;
while (p -> if_index) {
printf("%s:\t\t%s\n", p -> if_name, cyginet_ifname(p -> if_name));
p ++;
}
if_freenameindex(ptr);
}
}
/*
printf("\n\nTest cyginet_ipv4_index2ifname:\n\n");
{
int ifindex = 1;
printf("The name of ipv4 ifindex %d: %s\n",
ifindex,
cyginet_ipv4_index2ifname(ifindex)
);
}
*/
printf("\n\nTest cyginet_guidname works in the Cygwin:\n\n");
{
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
printf("%s:\t\t%s\n",
p -> FriendlyName,
cyginet_guidname(p -> FriendlyName)
);
p = p -> next;
}
}
printf("\n\nTest cyginet_blackhole_index:\n\n");
{
struct in6_addr addr6;
char addr[4];
printf("The blackhole ifindex is %d\n",
cyginet_blackhole_index(&addr6, addr)
);
}
#if _WIN32_WINNT < _WIN32_WINNT_VISTA
printf("\n\nTest libwinet_dump_ipv6_route_table:\n\n");
{
struct cyginet_route routes[100];
memset(routes, 0, sizeof(struct cyginet_route) * 100);
int n = libwinet_dump_ipv6_route_table(routes, 100);
printf("Get route numbers: %d\n", n);
}
printf("\n\nTest libwinet_run_command:\n\n");
{
printf("ls command return %d\n", libwinet_run_command("ls"));
}
printf("\n\nTest libwinet_is_wireless_interface:\n\n");
{
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
printf("%s is wireless netcard: %d\n",
p -> FriendlyName,
libwinet_is_wireless_interface(p -> FriendlyName)
);
p = p -> next;
}
}
#endif /* _WIN32_WINNT < _WIN32_WINNT_VISTA */
/*
printf("\n\nTest libwinet_get_loopback_index:\n\n");
{
printf("Ipv4 loopback ifindex is %d\n",
libwinet_get_loopback_index(AF_INET)
);
printf("Ipv6 loopback ifindex is %d\n",
libwinet_get_loopback_index(AF_INET6)
);
}
*/
printf("\n\nTest libwinet_map_ifindex:\n\n");
{
int i;
for (i = 1; i < 32; i++) {
printf("Ipv4 ifindex %d map to: Ipv6 index %d\n",
i,
libwinet_map_ifindex(AF_INET, i)
);
printf("Ipv6 ifindex %d map to: Ipv4 index %d\n",
i,
libwinet_map_ifindex(AF_INET6, i)
);
}
}
/*
printf("\n\nTest cyginet_set_ipv6_forwards:\n\n");
{
printf("cyginet_set_ipv6_forwards(1) return %d\n",
cyginet_set_ipv6_forwards(1)
);
printf("cyginet_set_ipv6_forwards(0) return %d\n",
cyginet_set_ipv6_forwards(0)
);
}
printf("\n\nTest cyginet_set_icmp6_redirect_accept:\n\n");
{
printf("cyginet_set_icmp6_redirect_accept(0) return %d\n",
cyginet_set_icmp6_redirect_accept(0)
);
printf("cyginet_set_icmp6_redirect_accept(1) return %d\n",
cyginet_set_icmp6_redirect_accept(1)
);
}
*/
printf("\n\nTest cyginet_interface_wireless:\n\n");
{
int n;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
n = cyginet_interface_wireless(p -> FriendlyName, 1);
printf("%s is wireless netcard: %d\n", p -> FriendlyName, n);
p = p -> next;
}
}
printf("\n\nTest cyginet_interface_mtu:\n\n");
{
int n;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
n = cyginet_interface_mtu(p -> FriendlyName, 1);
printf("mtu of %s is : %d\n", p -> FriendlyName, n);
p = p -> next;
}
}
printf("\n\nTest cyginet_interface_operational:\n\n");
{
int n;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
n = cyginet_interface_operational(p -> FriendlyName, 1);
printf("%s is up: %d\n", p -> FriendlyName, n);
p = p -> next;
}
}
printf("\n\nTest cyginet_interface_ipv4:\n\n");
{
struct sockaddr_in sa;
int n;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
memset(&sa, 0, sizeof(sa));
n = cyginet_interface_ipv4(p -> FriendlyName, 1, (unsigned char*)&sa);
printf("get ipv4 from %s: %d\n", p -> FriendlyName, n);
p = p -> next;
}
}
printf("\n\nTest cyginet_interface_sdl:\n\n");
{
int n;
struct sockaddr_dl sdl;
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
memset(&sdl, 0, sizeof(struct sockaddr_dl));
n = cyginet_interface_sdl(&sdl, p -> FriendlyName);
printf("get sdl from %s: %d\n", p -> FriendlyName, n);
if (0 == n) {
printf("sdl_len is %d\n", sdl.sdl_len);
printf("sdl_nlen is %d\n", sdl.sdl_nlen);
printf("sdl_alen is %d\n", sdl.sdl_alen);
}
p = p -> next;
}
}
printf("\n\nTest cyginet_dump_route_table:\n\n");
do {
#define MAX_ROUTES 120
struct cyginet_route routes[MAX_ROUTES];
memset(routes, 0, sizeof(struct cyginet_route) * MAX_ROUTES);
int n = cyginet_dump_route_table(routes, MAX_ROUTES);
printf("Get route numbers: %d\n", n);
} while (0);
printf("\n\nTest libwinet_monitor_route_thread_proc:\n\n");
do {
int mypipes[2];
int n;
char *cmd1 = "netsh interface ipv6 add route 3ffe::/16 1 fe80::1";
char *cmd2 = "netsh interface ipv6 delete route 3ffe::/16 1 fe80::1";
// char *cmd3 = "netsh interface ipv6 update route 3ffe::/16 1 fe80::1";
if (-1 == pipe(mypipes))
break;
n = cyginet_start_monitor_route_changes(mypipes[1]);
if (n == 0) {
char ch = ' ';
printf("Run command: %s\n", cmd1);
libwinet_run_command(cmd1);
Sleep(100);
if (read(mypipes[0], &ch, 1) == 1)
printf("Event number is %c\n", ch);
printf("Run command: %s\n", cmd2);
libwinet_run_command(cmd2);
Sleep(100);
if (read(mypipes[0], &ch, 1) == 1)
printf("Event number is %c\n", ch);
cyginet_stop_monitor_route_changes();
}
close(mypipes[0]);
close(mypipes[1]);
} while(0);
printf("\n\nTest select and pipe with \n");
printf("\tcyginet_start_monitor_route_changes\n");
printf("\tcyginet_stop_monitor_route_changes\n\n");
do {
break; /* We don't run it beacuse it need
manual intervention. */
int mypipes[2];
int n;
fd_set readfds;
char buf[16];
if (-1 == pipe(mypipes))
break;
if (fcntl(mypipes[0], F_SETFL, O_NONBLOCK) < 0)
printf("Error set NONBLOCK\n");
FD_ZERO(&readfds);
n = cyginet_start_monitor_route_changes(mypipes[1]);
if (n == 0) {
FD_SET(mypipes[0], &readfds);
printf("Please disable/enable your netcard or plug/unplug "
"netting wire so as to change route table.\n");
fflush(NULL);
printf("select return: %d\n",
select(FD_SETSIZE, &readfds, NULL, NULL, NULL)
);
memset(buf, 0, 16);
printf("read pipe, return %d\n",
read(mypipes[0], buf, 16));
printf("Event number is %s\n",buf);
cyginet_stop_monitor_route_changes();
}
close(mypipes[0]);
close(mypipes[1]);
} while(0);
printf("\n\nTest cyginet_dump_route_table:\n\n");
do {
#define MAX_ROUTES 120
struct cyginet_route routes[MAX_ROUTES];
memset(routes, 0, sizeof(struct cyginet_route) * MAX_ROUTES);
int n = cyginet_dump_route_table(routes, MAX_ROUTES);
printf("Get route numbers: %d\n", n);
} while (0);
printf("\n\nTest libwinet_edit_route_entry:\n\n");
do {
SOCKADDR *dest;
SOCKADDR *gate;
SOCKADDR_IN dest4 = { AF_INET, 0, {{{ INADDR_ANY }}}, {0} };
SOCKADDR_IN gate4 = { AF_INET, 0, {{{ INADDR_ANY }}}, {0} };
SOCKADDR_IN6 dest6 = {
AF_INET6,
0,
0,
{{IN6ADDR_ANY_INIT}}
};
SOCKADDR_IN6 gate6 = {
AF_INET6,
0,
0,
{{IN6ADDR_ANY_INIT}}
};
int prefix;
unsigned int metric;
int ifindex;
int n;
if (inet_pton(AF_INET, "192.168.128.119", &dest4.sin_addr) != 1)
break;
if (inet_pton(AF_INET, "192.168.121.200", &gate4.sin_addr) != 1)
break;
ifindex = 5;
metric = 3;
prefix = 32;
dest = (SOCKADDR*)&dest4;
gate = (SOCKADDR*)&gate4;
n = libwinet_edit_route_entry(dest,
prefix,
gate,
ifindex,
metric,
RTM_ADD
);
printf("Add Ipv4 route return %d\n", n);
n = libwinet_edit_route_entry(dest,
prefix,
gate,
ifindex,
metric,
RTM_CHANGE
);
printf("Change Ipv4 route return %d\n", n);
metric = 15;
n = libwinet_edit_route_entry(dest,
prefix,
gate,
ifindex,
metric,
RTM_DELETE
);
printf("Delete Ipv4 route return %d\n", n);
if (inet_pton(AF_INET6, "3ffe::", &dest6.sin6_addr) != 1)
break;
if (inet_pton(AF_INET6, "fe80::1", &gate6.sin6_addr) != 1)
break;
prefix = 112;
metric = 1200;
ifindex = 1;
dest = (SOCKADDR*)&dest6;
gate = (SOCKADDR*)&gate6;
n = libwinet_edit_route_entry(dest,
prefix,
gate,
ifindex,
metric,
RTM_ADD
);
printf("Add Ipv6 route return %d\n", n);
metric = 1100;
n = libwinet_edit_route_entry(dest,
prefix,
gate,
ifindex,
metric,
RTM_CHANGE
);
printf("Change Ipv6 route return %d\n", n);
n = libwinet_edit_route_entry(dest,
prefix,
gate,
ifindex,
metric,
RTM_DELETE
);
printf("Delete Ipv6 route return %d\n", n);
} while(0);
}
int main(int argc, char* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
/* PrintAllInterfaces(); */
printf("\n\nTest libwinet_refresh_interface_map_table:\n\n");
{
if (libwinet_refresh_interface_map_table()) {
PLIBWINET_INTERFACE_MAP_TABLE p = g_interface_map_table;
while (p) {
printf("Friendly Name:\t %s\n", p -> FriendlyName);
printf("Adapter Name:\t %s\n", p -> AdapterName);
printf("Forward flag:\t %d\n", p -> RouteFlags);
printf("IfType:\t %ld\n", p -> IfType);
p = p -> next;
}
}
else
printf("libwinet_refresh_interface_map_table failed\n");
}
/*
printf("\n\nTest ipv4 blackhole route:\n\n");
do {
SOCKADDR_IN dest = { AF_INET, 0, {{{ INADDR_ANY }}}, {0} };
SOCKADDR_IN gate = { AF_INET, 0, {{{ INADDR_ANY }}}, {0} };
int ifindex = 1;
int n;
if (inet_pton(AF_INET, "123.58.180.0", &dest.sin_addr) != 1)
break;
if (inet_pton(AF_INET, "127.0.0.1", &gate.sin_addr) != 1)
break;
n = libwinet_edit_route_entry((struct sockaddr*)&dest,
24,
(struct sockaddr*)&gate,
ifindex,
1,
RTM_ADD
);
printf("Add blackhole route return: %d", n);
} while(0);
*/
printf("\n\nTest myown cyg_getifaddress:\n\n");
do {
struct cyginet_route ptable[255];
int rc;
rc = cyginet_getifaddresses(NULL, ptable, 255);
printf("return %d\n", rc);
while (rc--) {
}
} while(0);
// runTestCases();
/* printf("\n\nTest libwinet_init_ipv6_interface:\n\n"); */
/* libwinet_init_ipv6_interface(); */
/* printf("\n\nTest libwinet_restore_ipv6_interface:\n\n"); */
/* libwinet_restore_ipv6_interface(); */
printf("\n\nTest libwinet_free_interface_map_table:\n\n");
libwinet_free_interface_map_table();
printf("\n\nTest Finished.\n\n");
WSACleanup();
return 0;
}
#endif /* TEST_CYGINET */
/*
Reference List in the MSDN.
NDIS 6.0 Interfaces for Window Vista later
http://msdn.microsoft.com/en-us/library/windows/hardware/ff565740(v=vs.85).aspx
NDIS Versions in Network Drivers (Windows Drivers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff567893(v=vs.85).aspx
For Windows XP, NDIS 5.0
http://msdn.microsoft.com/en-us/library/windows/hardware/ff565849(v=vs.85).aspx
NDIS General-use Interfaces (NDIS 5.1) (Windows Drivers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff556983(v=vs.85).aspx
Routing Protocol Interface Functions (Windows)
http://msdn.microsoft.com/en-us/library/windows/desktop/aa446772(v=vs.85).aspx
Networking (Windows)
http://msdn.microsoft.com/en-us/library/windows/desktop/ee663286(v=vs.85).aspx
NDIS Protocol Drivers (NDIS 5.1) (Windows Drivers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff557149(v=vs.85).aspx
Winsock IOCTLs (Windows)
http://msdn.microsoft.com/zh-cn/library/windows/desktop/bb736550(v=vs.85).aspx
Creating a Basic IP Helper Application (Windows)
http://msdn.microsoft.com/zh-cn/library/windows/desktop/aa365872(v=vs.85).aspx
Network Awareness in Windows XP
http://msdn.microsoft.com/en-us/library/ms700657(v=vs.85).aspx
System-Defined Device Setup Classes (Windows Drivers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553419(v=vs.85).aspx
Device Information Sets (Windows Drivers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff541247(v=vs.85).aspx
Accessing Device Instance SPDRP_Xxx Properties (Windows Drivers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff537737(v=vs.85).aspx
IOCTL_NDIS_QUERY_GLOBAL_STATS (Windows Drivers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff548975(v=vs.85).aspx
IPv6 RFCs and Standards Working Groups
http://www.ipv6now.com.au/RFC.php
Routing Table Manager Version 2
http://msdn.microsoft.com/en-us/library/windows/desktop/bb404201(v=vs.85).aspx
Using Routing Table Manager Version 2, this section contains sample
code that can be used when developing clients such as routing
protocols.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa382335(v=vs.85).aspx
An introduction to the IPv6 protocol along with overviews on
deployment and IPv6 transitioning technologies is available on
Technet at Microsoft Internet Protocol Version 6 (IPv6).
http://go.microsoft.com/fwlink/p/?linkid=194338
http://technet.microsoft.com/en-us/network/bb530961.aspx
Internet Protocol Version 6 (IPv6)
http://msdn.microsoft.com/en-us//library/windows/desktop/ms738570(v=vs.85).aspx
IPv6 Link-local and Site-local Addresses
http://msdn.microsoft.com/zh-cn/library/windows/desktop/ms739166(v=vs.85).aspx
Recommended Configurations for IPv6
http://msdn.microsoft.com/en-us/library/windows/desktop/ms740117(v=vs.85).aspx
IPv6 Support in Home Routers, It looks like a windows re6stnet.
http://msdn.microsoft.com/en-us/windows/hardware/gg463251.aspx
Neighbor Discovery in IPv6
http://tools.ietf.org/html/rfc4861
Default Address Selection for Internet Protocol version 6 (IPv6)
http://tools.ietf.org/html/rfc3484
Path MTU Discovery
http://tools.ietf.org/html/rfc1191
IPv6 Traffic Between Nodes on Different Subnets of an IPv4 Internetwork (6to4)
http://msdn.microsoft.com/zh-cn/library/windows/desktop/ms737598(v=vs.85).aspx
Multicast Listener Discovery (MLD)
http://msdn.microsoft.com/en-us/library/aa916334.aspx
IPv6 Addresses, it explains the relation between link-local address
and interface id
http://msdn.microsoft.com/en-us/library/aa921042.aspx
TCP/IP (v4 and v6) Technical Reference, it shows ipv4 and ipv6 how
to work in the windows. (Recommended)
http://technet.microsoft.com/en-us/library/dd379473(v=ws.10).aspx
*/
#ifndef __CYGIFNET_H__
#define __CYGIFNET_H__
#ifndef IN_LOOPBACK
#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000)
#endif
/* Missing defines in the Cygwin */
#define RTM_ADD 0x1 /* Add Route */
#define RTM_DELETE 0x2 /* Delete Route */
#define RTM_CHANGE 0x3 /* Change Metrics or flags */
#define RTM_GET 0x4 /* Report Metrics */
#define IFF_RUNNING 0x40
/*
* Structure of a Link-Level sockaddr:
*/
struct sockaddr_dl {
u_char sdl_len; /* Total length of sockaddr */
u_char sdl_family; /* AF_LINK */
u_short sdl_index; /* if != 0, system given index for interface */
u_char sdl_type; /* interface type */
u_char sdl_nlen; /* interface name length, no trailing 0 reqd. */
u_char sdl_alen; /* link level address length */
u_char sdl_slen; /* link layer selector length */
char sdl_data[46]; /* minimum work area, can be larger;
contains both if name and ll address */
};
struct cyginet_route {
struct sockaddr_storage prefix;
int plen;
int metric;
unsigned int ifindex;
int proto;
struct sockaddr_storage gateway;
};
#if defined(INSIDE_BABELD_CYGINET)
struct ifaddrs {
struct ifaddrs *ifa_next;
char *ifa_name;
unsigned int ifa_flags;
struct sockaddr *ifa_addr;
union {
struct sockaddr *ifa_netmask;
struct sockaddr *ifa_dstaddr;
};
void *ifa_data;
};
struct if_nameindex {
unsigned if_index;
char *if_name;
};
typedef struct _LIBWINET_INTERFACE_MAP_TABLE {
PCHAR FriendlyName;
PCHAR AdapterName;
BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH];
DWORD PhysicalAddressLength;
DWORD IfType;
int RouteFlags;
DWORD IfIndex;
DWORD Ipv6IfIndex;
VOID *next;
} LIBWINET_INTERFACE_MAP_TABLE, *PLIBWINET_INTERFACE_MAP_TABLE;
typedef struct _LIBWINET_INTERFACE {
DWORD IfType;
IF_OPER_STATUS OperStatus;
DWORD Mtu;
SOCKADDR Address;
} LIBWINET_INTERFACE, *PLIBWINET_INTERFACE;
extern unsigned if_nametoindex (const char *);
extern char *if_indextoname (unsigned, char *);
extern struct if_nameindex *if_nameindex (void);
extern void if_freenameindex (struct if_nameindex *);
extern const char *inet_ntop (int, const void *, char *, socklen_t);
extern int inet_pton (int, const char *, void *);
extern int getifaddrs(struct ifaddrs **);
extern void freeifaddrs(struct ifaddrs *);
#define MALLOC(x) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,x)
#define FREE(p) \
if(NULL != p) {HeapFree(GetProcessHeap(),0,p); p = NULL;}
#define CLOSESOCKET(s) \
if(INVALID_SOCKET != s) {closesocket(s); s = INVALID_SOCKET;}
#define CLOSESOCKEVENT(h) \
if(WSA_INVALID_EVENT != h) {WSACloseEvent(h); h = WSA_INVALID_EVENT;}
#define SOCKETERR(e) \
{ \
printf("%s:%s failed: %d [%s@%d]\n", \
__FUNCTION__, \
e, \
WSAGetLastError(), \
__FILE__, \
__LINE__ \
); \
}
#endif /* INSIDE_BABELD_CYGINET */
/* Export functions from cyginet */
int cyginet_startup();
void cyginet_cleanup();
int cyginet_start_monitor_route_changes(int);
int cyginet_stop_monitor_route_changes();
int cyginet_set_icmp6_redirect_accept(int);
int cyginet_set_interface_forwards(const char * ifname, int value);
int cyginet_interface_sdl(struct sockaddr_dl *, char *);
int cyginet_interface_wireless(const char *, int);
int cyginet_interface_mtu(const char *, int);
int cyginet_interface_operational(const char *, int);
int cyginet_interface_ipv4(const char *, int, unsigned char *);
int cyginet_dump_route_table(struct cyginet_route *, int);
int cyginet_loopback_index(int);
int cyginet_add_route_entry(const struct sockaddr *, unsigned short,
const struct sockaddr *, int , unsigned int);
int cyginet_delete_route_entry(const struct sockaddr *, unsigned short,
const struct sockaddr *, int , unsigned int);
int cyginet_update_route_entry(const struct sockaddr *, unsigned short,
const struct sockaddr *, int , unsigned int);
char * cyginet_ifname(const char *);
char * cyginet_guidname(const char *);
int cyginet_refresh_interface_table();
int cyginet_getifaddresses(char *, struct cyginet_route *, int);
#endif /* __CYGIFNET_H__ */
...@@ -409,7 +409,11 @@ check_interfaces(void) ...@@ -409,7 +409,11 @@ check_interfaces(void)
unsigned int ifindex; unsigned int ifindex;
FOR_ALL_INTERFACES(ifp) { FOR_ALL_INTERFACES(ifp) {
#if defined (_WIN32_WINNT)
ifindex = if_nametoindex(cyginet_guidname(ifp->name));
#else
ifindex = if_nametoindex(ifp->name); ifindex = if_nametoindex(ifp->name);
#endif
if(ifindex != ifp->ifindex) { if(ifindex != ifp->ifindex) {
debugf("Noticed ifindex change for %s.\n", ifp->name); debugf("Noticed ifindex change for %s.\n", ifp->name);
ifp->ifindex = 0; ifp->ifindex = 0;
......
...@@ -111,3 +111,8 @@ void set_timeout(struct timeval *timeout, int msecs); ...@@ -111,3 +111,8 @@ void set_timeout(struct timeval *timeout, int msecs);
int interface_up(struct interface *ifp, int up); int interface_up(struct interface *ifp, int up);
int interface_ll_address(struct interface *ifp, const unsigned char *address); int interface_ll_address(struct interface *ifp, const unsigned char *address);
void check_interfaces(void); void check_interfaces(void);
#if defined (_WIN32_WINNT)
char * cyginet_ifname(const char *);
char * cyginet_guidname(const char *);
#endif
...@@ -28,6 +28,8 @@ THE SOFTWARE. ...@@ -28,6 +28,8 @@ THE SOFTWARE.
#ifdef __linux #ifdef __linux
#include "kernel_netlink.c" #include "kernel_netlink.c"
#elif defined(__CYGWIN__)
#include "kernel_cygwin.c"
#else #else
#include "kernel_socket.c" #include "kernel_socket.c"
#endif #endif
......
/*
Copyright (c) 2007 by Grégoire Henry
Copyright (c) 2008, 2009 by Juliusz Chroboczek
Copyright (c) 2010 by Vincent Gross
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <assert.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if.h>
#include "babeld.h"
#include "neighbour.h"
#include "kernel.h"
#include "util.h"
#include "interface.h"
#include "cyginet.h"
static int get_sdl(struct sockaddr_dl *sdl, char *guidname);
static const unsigned char v4prefix[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
int export_table = -1, import_table = -1;
int
if_eui64(char *ifname, int ifindex, unsigned char *eui)
{
struct sockaddr_dl sdl;
char *tmp = NULL;
memset(&sdl, 0, sizeof(struct sockaddr_dl));
if (get_sdl(&sdl, ifname) < 0) {
return -1;
}
tmp = sdl.sdl_data + sdl.sdl_nlen;
if (sdl.sdl_alen == 8) {
memcpy(eui, tmp, 8);
eui[0] ^= 2;
} else if (sdl.sdl_alen == 6) {
memcpy(eui, tmp, 3);
eui[3] = 0xFF;
eui[4] = 0xFE;
memcpy(eui+5, tmp+3, 3);
} else {
return -1;
}
return 0;
}
/* Fill sdl with the structure corresponding to ifname.
Warning: make a syscall (and get all interfaces).
return -1 if an error occurs, 0 otherwise. */
static int
get_sdl(struct sockaddr_dl *sdl, char *ifname)
{
return cyginet_interface_sdl(sdl, ifname);
}
static int old_forwarding = -1;
static int old_accept_redirects = -1;
static int ifindex_lo = 1;
static int kernel_pipe_handles[2];
/* It enables ip6.forwarding and disable ip6.redirect.
*
* Option 1:
*
* IPV6CTL_FORWARDING (ip6.forwarding) Boolean: enable/disable
* forward- ing of IPv6 packets. Also, identify if the node is
* acting as a router. Defaults to off.
*
* ==> In the Windows, MSDN says in the registry
*
* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
*
* Value Name: IPEnableRouter
* Value type: REG_DWORD
* Value Data: 1
*
* A value of 1 enables TCP/IP forwarding for all network
* connections that are installed and used by this computer.
*
* Refer to: http://support.microsoft.com/kb/315236/en-us
*
* For ipv6, no global options to enable forwarding, but for each
* interface respectively.
*
* Option 2:
*
* ICMPV6CTL_REDIRACCEPT
*
* IPV6CTL_SENDREDIRECTS (ip6.redirect) Boolean: enable/disable
* sending of ICMPv6 redirects in response to unforwardable IPv6
* packets. This option is ignored unless the node is routing
* IPv6 packets, and should normally be enabled on all systems.
* Defaults to on.
*
* ==> MSDN says in the registry
*
* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
*
* EnableICMPRedirect = 0
*
* Regarding ipv6, it's in the registry
*
* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters
*
* Refer to:
* http://technet.microsoft.com/en-us/library/cc766102(v=ws.10).aspx
* http://msdn.microsoft.com/en-us/library/aa915651.aspx
*
* Notice the msdn page of Windows CE, value is "EnableICMPRedirects",
* it's plural. But I'd rather use singluar form "EnableICMPRedirect".
*
*/
int
kernel_setup(int setup)
{
int flags=0;
if (setup) {
if (0 != cyginet_startup())
return -1;
/* We don't disable ICMPv6 redirect in the Windows */
/*
if ((rc = cyginet_set_icmp6_redirect_accept(0)) == -1) {
fprintf(stderr, "Cannot disable ICMPv6 redirect.\n");
return -1;
}
if (rc) {
fprintf(stderr,
"Disable ICMPv6 redirect successfully. Reboot computer "
"to take it effect now.\n\n"
);
return -1;
}
*/
if (pipe(kernel_pipe_handles) == -1)
return -1;
if ((flags = fcntl(kernel_pipe_handles[0], F_GETFL, 0)) < 0)
goto error;
if (fcntl(kernel_pipe_handles[0], F_SETFL, flags | O_NONBLOCK) == -1)
goto error;
}
else {
close(kernel_pipe_handles[0]);
close(kernel_pipe_handles[1]);
cyginet_cleanup();
}
return 1;
error:
return -1;
}
int
kernel_setup_socket(int setup)
{
/* We use a pipe to notify route changed */
if(setup) {
if(kernel_socket < 0) {
kernel_socket = kernel_pipe_handles[0];
if (-1 ==
cyginet_start_monitor_route_changes(kernel_pipe_handles[1])) {
perror("start_monitor_route_changes");
kernel_socket = -1;
return -1;
}
}
} else {
cyginet_stop_monitor_route_changes();
kernel_socket = -1;
}
return 1;
}
int
kernel_setup_interface(int setup, const char *ifname, int ifindex)
{
return 1;
}
int
kernel_interface_operational(const char *ifname, int ifindex)
{
int rc;
int flags = link_detect ? (IFF_UP | IFF_RUNNING) : IFF_UP;
rc = cyginet_interface_operational(ifname, ifindex);
if (rc < 0)
return -1;
return ((rc & flags) == flags);
}
int
kernel_interface_ipv4(const char *ifname, int ifindex, unsigned char *addr_r)
{
return cyginet_interface_ipv4(ifname, ifindex, addr_r);
}
int
kernel_interface_mtu(const char *ifname, int ifindex)
{
return cyginet_interface_mtu(ifname, ifindex);
}
int
kernel_interface_wireless(const char *ifname, int ifindex)
{
return cyginet_interface_wireless(ifname, ifindex);
}
int
kernel_interface_channel(const char *ifname, int ifindex)
{
errno = ENOSYS;
return -1;
}
static void
clear_kernel_socket_event()
{
int ch;
while (read(kernel_pipe_handles[0], &ch, 1) > 0);
}
/*
* RTF_REJECT
*
* Instead of forwarding a packet like a normal route, routes with
* RTF_REJECT cause packets to be dropped and unreachable messages
* to be sent to the packet originators. This flag is only valid on
* routes pointing at the loopback interface.
*
* RTF_BLACKHOLE
*
* Like the RTF_REJECT flag, routes with RTF_BLACKHOLE cause packets
* to be dropped, but unreachable messages are not sent. This flag
* is only valid on routes pointing at the loopback interface.
*
* Nullrouting on Windows
*
* Windows XP/Vista/7 does not support reject or blackhole arguments
* via route, thus an unused IP address (e.g. 192.168.0.205) must be
* used as the target gateway.
*
* RTF_GATEWAY: destination is a gateway
*
* RTF_HOST: host entry (net otherwise)
*
* It means all the netmask bits are 1.
*
* RTF_CLONING: generate new routes on use
*
* Not implemented in the Windows.
*
*/
int
kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *gate, int ifindex, unsigned int metric,
const unsigned char *newgate, int newifindex,
unsigned int newmetric)
{
char local6[1][1][16] = {{IN6ADDR_ANY_INIT}};
char local4[1][1][16] =
{{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}};
int rc, ipv4;
int route_ifindex;
int prefix_len;
struct sockaddr_storage destination = {0};
struct sockaddr_storage gateway = {0};
/* Check that the protocol family is consistent. */
if(plen >= 96 && v4mapped(dest)) {
if(!v4mapped(gate)) {
errno = EINVAL;
return -1;
}
ipv4 = 1;
} else {
if(v4mapped(gate)) {
errno = EINVAL;
return -1;
}
ipv4 = 0;
}
if(operation == ROUTE_MODIFY && newmetric == metric &&
memcmp(newgate, gate, 16) == 0 && newifindex == ifindex)
return 0;
if(operation == ROUTE_MODIFY) {
if((metric == KERNEL_INFINITY) ||
(newmetric == KERNEL_INFINITY) ||
(ipv4 && plen == 128 && memcmp(dest, newgate, 16) == 0)) {
kernel_route(ROUTE_FLUSH, dest, plen,
gate, ifindex, metric,
NULL, 0, 0);
return kernel_route(ROUTE_ADD, dest, plen,
newgate, newifindex, newmetric,
NULL, 0, 0);
} else {
metric = newmetric;
gate = newgate;
ifindex = newifindex;
}
}
kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n",
operation == ROUTE_ADD ? "add" :
operation == ROUTE_FLUSH ? "flush" : "change",
format_address(dest), plen, metric, ifindex,
format_address(gate));
if(kernel_socket < 0) kernel_setup_socket(1);
route_ifindex = ifindex;
prefix_len = ipv4 ? plen - 96 : plen;
if(metric == KERNEL_INFINITY) {
/* We do nothing, windows don't know blackhole. */
assert(operation == ROUTE_ADD || operation == ROUTE_FLUSH);
return 0;
/* It means this route has property: RTF_BLACKHOLE */
if(ifindex_lo < 0) {
ifindex_lo = cyginet_loopback_index(AF_UNSPEC);
if(ifindex_lo <= 0)
return -1;
}
route_ifindex = ifindex_lo;
}
#define PUSHADDR(dst, src) \
do { struct sockaddr_in *sin = (struct sockaddr_in*)&(dst); \
sin->sin_family = AF_INET; \
memcpy(&sin->sin_addr, (src) + 12, 4); \
} while (0)
#define PUSHADDR6(dst, src) \
do { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&(dst); \
sin6->sin6_family = AF_INET6; \
memcpy(&sin6->sin6_addr, (src), 16); \
} while (0)
if(ipv4) {
PUSHADDR(destination, dest);
if (metric == KERNEL_INFINITY) {
assert(0);
/* blackhole route, now it doesn't work */
kdebugf("Error: ipv4 blackhole route doesn't work.\n");
return -1;
assert (operation != ROUTE_MODIFY);
PUSHADDR(gateway, **local4);
}
else if(plen == 128 && memcmp(dest+12, gate+12, 4) == 0) {
assert (operation != ROUTE_MODIFY);
PUSHADDR(gateway, gate); /* MIB_IPROUTE_TYPE_DIRECT */
}
else
PUSHADDR(gateway, gate); /* MIB_IPROUTE_TYPE_INDIRECT */
} else {
PUSHADDR6(destination, dest);
if (metric == KERNEL_INFINITY) {
assert(0);
PUSHADDR6(gateway, **local6);
}
else
PUSHADDR6(gateway, gate);
}
#undef PUSHADDR
#undef PUSHADDR6
/* what if route_ifindex == 0 */
switch(operation) {
case ROUTE_FLUSH:
rc = cyginet_delete_route_entry((struct sockaddr*)&destination,
prefix_len,
(struct sockaddr*)&gateway,
route_ifindex,
metric
);
break;
case ROUTE_ADD:
rc = cyginet_add_route_entry((struct sockaddr*)&destination,
prefix_len,
(struct sockaddr*)&gateway,
route_ifindex,
metric
);
break;
case ROUTE_MODIFY:
rc = cyginet_update_route_entry((struct sockaddr*)&destination,
prefix_len,
(struct sockaddr*)&gateway,
route_ifindex,
metric
);
break;
default:
return -1;
};
/* Monitor thread will write data to kernel pipe when any change
in the route table is happened. Here it's babeld itself to
change the route table, so kernel pipe need to be clean. */
clear_kernel_socket_event();
return rc;
}
static void
print_kernel_route(int add, struct kernel_route *route)
{
char *ifname = NULL;
char guidname[IFNAMSIZ];
if(if_indextoname(route->ifindex, guidname))
ifname = cyginet_ifname(guidname);
fprintf(stderr,
"%s kernel route: dest: %s gw: %s metric: %d if: %s(%d) \n",
add == RTM_ADD ? "Add" :
add == RTM_DELETE ? "Delete" : "Change",
format_prefix(route->prefix, route->plen),
format_address(route->gw),
route->metric,
ifname ? ifname : "unk",
route->ifindex
);
}
static int
parse_kernel_route(struct cyginet_route *src, struct kernel_route *route)
{
struct sockaddr *sa;
if(ifindex_lo < 0) {
ifindex_lo = cyginet_loopback_index(AF_UNSPEC);
if(ifindex_lo <= 0)
return -1;
}
memset(route, 0, sizeof(struct kernel_route));
route -> plen = src -> plen;
route -> metric = src -> metric;
route -> proto = src -> proto;
route -> ifindex = src -> ifindex;
sa = (struct sockaddr*)&(src -> prefix);
if(sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
memcpy(route->prefix, &sin6->sin6_addr, 16);
if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
|| IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr))
return -1;
} else if(sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
#if defined(IN_LINKLOCAL)
if(IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)))
return -1;
#endif
if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
return -1;
v4tov6(route->prefix, (unsigned char *)&sin->sin_addr);
} else {
return -1;
}
/* Gateway */
sa = (struct sockaddr*)&(src -> gateway);
if(sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
memcpy(route->gw, &sin6->sin6_addr, 16);
} else if(sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
v4tov6(route->gw, (unsigned char *)&sin->sin_addr);
}
if(route->ifindex == ifindex_lo)
return -1;
/* Netmask */
if(v4mapped(route->prefix)) route->plen += 96;
return 0;
}
int
kernel_routes(struct kernel_route *routes, int maxroutes)
{
int rc, i;
int count;
struct kernel_route * proute = routes;
struct cyginet_route * ptable;
rc = cyginet_dump_route_table(NULL, 0);
if (rc < 0)
return -1;
if (rc == 0)
return 0;
rc += 10;
if (NULL == (ptable = calloc(rc, sizeof(struct cyginet_route))))
return -1;
rc = cyginet_dump_route_table(ptable, rc);
if (rc < 0) {
free(ptable);
return -1;
} else if (rc >= maxroutes)
return rc;
for (i = 0, count = 0; i < rc; i++) {
if (parse_kernel_route(ptable + i, proute) != 0)
continue;
if(debug > 2)
print_kernel_route(RTM_ADD, proute);
if (maxroutes > rc)
proute++;
count ++;
}
free(ptable);
return count;
}
/* Note: ifname returned by getifaddrs maybe includes a suffix number
in the Cygwin, it looks like:
{C05BAB6E-B82D-4C4D-AF07-EFF7C45C5DB0}_1
{C05BAB6E-B82D-4C4D-AF07-EFF7C45C5DB0}_2
...
*/
static int
compare_ifname(const char * ifapname, const char * ifname)
{
assert(ifname);
char * guidname = cyginet_guidname(ifname);
if (guidname)
return strncmp(guidname, ifapname, strlen(guidname));
return -1;
}
int
cyginet_kernel_addresses(char *ifname, int ifindex, int ll,
struct kernel_route *routes, int maxroutes)
{
int rc, i, n;
struct cyginet_route *ptable, *p;
n = maxroutes * 2;
while (1) {
if (NULL == (ptable = calloc(n, sizeof(struct cyginet_route))))
return -1;
rc = cyginet_getifaddresses(ifname, ptable, n);
if(rc < 0)
return -1;
if (rc < n)
break;
free(ptable);
n += n;
}
i = 0;
p = ptable; p --;
while (p ++, n --) {
struct sockaddr *s = (struct sockaddr*)&(p -> prefix);
if (s -> sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&(p->prefix);
if(!!ll != !!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
continue;
memcpy(routes[i].prefix, &sin6->sin6_addr, 16);
routes[i].plen = 128;
routes[i].metric = 0;
routes[i].ifindex = ifindex;
routes[i].proto = RTPROT_BABEL_LOCAL;
memset(routes[i].gw, 0, 16);
i++;
} else if (s -> sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in*)&(p->prefix);
if(ll)
continue;
#if defined(IN_LINKLOCAL)
if(IN_LINKLOCAL(htonl(sin->sin_addr.s_addr)))
continue;
#endif
memcpy(routes[i].prefix, v4prefix, 12);
memcpy(routes[i].prefix + 12, &sin->sin_addr, 4);
routes[i].plen = 128;
routes[i].metric = 0;
routes[i].ifindex = ifindex;
routes[i].proto = RTPROT_BABEL_LOCAL;
memset(routes[i].gw, 0, 16);
i++;
}
}
free(ptable);
return i;
}
int
kernel_addresses(char *ifname, int ifindex, int ll,
struct kernel_route *routes, int maxroutes)
{
struct ifaddrs *ifa, *ifap;
int rc, i;
rc = getifaddrs(&ifa);
if(rc < 0)
return -1;
ifap = ifa;
i = 0;
while(ifap && i < maxroutes) {
if((ifname != NULL && compare_ifname(ifap->ifa_name, ifname) != 0))
goto next;
if(ifap->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifap->ifa_addr;
if(!!ll != !!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
goto next;
memcpy(routes[i].prefix, &sin6->sin6_addr, 16);
routes[i].plen = 128;
routes[i].metric = 0;
routes[i].ifindex = ifindex;
routes[i].proto = RTPROT_BABEL_LOCAL;
memset(routes[i].gw, 0, 16);
i++;
} else if(ifap->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in*)ifap->ifa_addr;
if(ll)
goto next;
#if defined(IN_LINKLOCAL)
if(IN_LINKLOCAL(htonl(sin->sin_addr.s_addr)))
goto next;
#endif
memcpy(routes[i].prefix, v4prefix, 12);
memcpy(routes[i].prefix + 12, &sin->sin_addr, 4);
routes[i].plen = 128;
routes[i].metric = 0;
routes[i].ifindex = ifindex;
routes[i].proto = RTPROT_BABEL_LOCAL;
memset(routes[i].gw, 0, 16);
i++;
}
next:
ifap = ifap->ifa_next;
}
freeifaddrs(ifa);
return i;
}
int
kernel_callback(int (*fn)(int, void*), void *closure)
{
if (kernel_socket < 0) kernel_setup_socket(1);
/* In the Windows, we can't get the exact changed route, but the
route table is really changed. */
kdebugf("Kernel table changed.\n");
cyginet_refresh_interface_table();
clear_kernel_socket_event();
return fn(~0, closure);
}
/* Local Variables: */
/* c-basic-offset: 4 */
/* indent-tabs-mode: nil */
/* End: */
...@@ -19,7 +19,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ...@@ -19,7 +19,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -50,9 +49,16 @@ babel_socket(int port) ...@@ -50,9 +49,16 @@ babel_socket(int port)
if(s < 0) if(s < 0)
return -1; return -1;
/* When this value is nonzero (the default on Windows), a socket
created for the AF_INET6 address family can be used to send and
receive IPv6 packets only. So it's not require to set in the
Windows XP. Actualy, this socket option is only supported on
Windows Vista or later. */
#if !defined(_WIN32_WINNT) || _WIN32_WINNT >= 0x0600
rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
if(rc < 0) if(rc < 0)
goto fail; goto fail;
#endif
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0) if(rc < 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment