1
0
Эх сурвалжийг харах

Merge pull request #1443 from domosekai/win32

Add IPv6 route management for Windows client
Yihong Wu 4 жил өмнө
parent
commit
03859eb515
3 өөрчлөгдсөн 259 нэмэгдсэн , 326 устгасан
  1. 66 57
      src/Cedar/VLanWin32.c
  2. 183 257
      src/Mayaqua/Network.c
  3. 10 12
      src/Mayaqua/Network.h

+ 66 - 57
src/Cedar/VLanWin32.c

@@ -162,7 +162,6 @@ void RouteTrackingMain(SESSION *s)
 					char ip_str2[64];
 
 					Copy(&e->DestIP, &nat_t_ip, sizeof(IP));
-					e->Metric = e->OldIfMetric;
 
 					IPToStr(ip_str, sizeof(ip_str), &e->DestIP);
 					IPToStr(ip_str2, sizeof(ip_str2), &e->GatewayIP);
@@ -190,9 +189,12 @@ void RouteTrackingMain(SESSION *s)
 	{
 		UINT i;
 		bool route_to_server_erased = true;
-		bool is_vlan_want_to_be_default_gateway = false;
-		UINT vlan_default_gateway_metric = 0;
-		UINT other_if_default_gateway_metric_min = INFINITE;
+		bool is_vlan_want_to_be_default_gateway_v4 = false;
+		bool is_vlan_want_to_be_default_gateway_v6 = false;
+		UINT vlan_default_gateway_metric_v4 = 0;
+		UINT vlan_default_gateway_metric_v6 = 0;
+		UINT other_if_default_gateway_metric_min_v4 = INFINITE;
+		UINT other_if_default_gateway_metric_min_v6 = INFINITE;
 
 		// Get whether the routing table have been changed
 		if (t->LastRoutingTableHash != table->HashedValue)
@@ -224,59 +226,77 @@ void RouteTrackingMain(SESSION *s)
 			}
 
 			// Search for the default gateway
-			if (IPToUINT(&e->DestIP) == 0 &&
-				IPToUINT(&e->DestMask) == 0)
+			if (IsZeroIP(&e->DestIP) && IsZeroIP(&e->DestMask))
 			{
 				Debug("e->InterfaceID = %u, t->VLanInterfaceId = %u\n",
 					e->InterfaceID, t->VLanInterfaceId);
 
 				if (e->InterfaceID == t->VLanInterfaceId)
 				{
-					// The virtual LAN card think that he want to be a default gateway
-					is_vlan_want_to_be_default_gateway = true;
-					vlan_default_gateway_metric = e->Metric;
-
-					if (vlan_default_gateway_metric >= 2 &&
-						t->OldDefaultGatewayMetric == (vlan_default_gateway_metric - 1))
+					if (IsIP4(&e->DestIP))
 					{
-						// Restore because the PPP server rewrites
-						// the routing table selfishly
-						DeleteRouteEntry(e);
-						e->Metric--;
-						AddRouteEntry(e);
-						Debug("** Restore metric destroyed by PPP.\n");
+						// The virtual LAN card think that he want to be a default gateway
+						is_vlan_want_to_be_default_gateway_v4 = true;
+						vlan_default_gateway_metric_v4 = e->Metric;
 
-						any_modified = true;
-					}
+						// PPP route fix
+						if (vlan_default_gateway_metric_v4 >= 2 &&
+							t->OldDefaultGatewayMetric == (vlan_default_gateway_metric_v4 - 1))
+						{
+							// Restore because the PPP server rewrites
+							// the routing table selfishly
+							DeleteRouteEntry(e);
+							e->Metric--;
+							AddRouteEntry(e);
+							Debug("** Restore metric destroyed by PPP.\n");
+
+							any_modified = true;
+						}
 
-					// Keep this entry
-					if (t->DefaultGatewayByVLan != NULL)
-					{
-						// Delete if there is one added last time
-						FreeRouteEntry(t->DefaultGatewayByVLan);
-					}
+						// Keep this entry
+						if (t->DefaultGatewayByVLan != NULL)
+						{
+							// Delete if there is one added last time
+							FreeRouteEntry(t->DefaultGatewayByVLan);
+						}
 
-					t->DefaultGatewayByVLan = ZeroMalloc(sizeof(ROUTE_ENTRY));
-					Copy(t->DefaultGatewayByVLan, e, sizeof(ROUTE_ENTRY));
+						t->DefaultGatewayByVLan = ZeroMalloc(sizeof(ROUTE_ENTRY));
+						Copy(t->DefaultGatewayByVLan, e, sizeof(ROUTE_ENTRY));
 
-					t->OldDefaultGatewayMetric = vlan_default_gateway_metric;
+						t->OldDefaultGatewayMetric = vlan_default_gateway_metric_v4;
+					}
+					else
+					{
+						is_vlan_want_to_be_default_gateway_v6 = true;
+						vlan_default_gateway_metric_v6 = e->Metric;
+					}
 				}
 				else
 				{
-					// There are default gateway other than the virtual LAN card
-					// Save the metric value of the default gateway
-					if (other_if_default_gateway_metric_min > e->Metric)
+					if (IsIP4(&e->DestIP))
 					{
-						// Ignore the metric value of all PPP connection in the case of Windows Vista
-						if (e->PPPConnection == false)
+						// There are default gateway other than the virtual LAN card
+						// Save the metric value of the default gateway
+						if (other_if_default_gateway_metric_min_v4 > e->Metric)
 						{
-							other_if_default_gateway_metric_min = e->Metric;
+							// Ignore the metric value of all PPP connection in the case of Windows Vista
+							if (e->PPPConnection == false)
+							{
+								other_if_default_gateway_metric_min_v4 = e->Metric;
+							}
+							else
+							{
+								// a PPP is used to Connect to the network
+								// in using Windows Vista
+								t->VistaAndUsingPPP = true;
+							}
 						}
-						else
+					}
+					else
+					{
+						if (other_if_default_gateway_metric_min_v6 > e->Metric)
 						{
-							// a PPP is used to Connect to the network
-							// in using Windows Vista
-							t->VistaAndUsingPPP = true;
+							other_if_default_gateway_metric_min_v6 = e->Metric;
 						}
 					}
 				}
@@ -287,7 +307,7 @@ void RouteTrackingMain(SESSION *s)
 		{
 			if (t->DefaultGatewayByVLan != NULL)
 			{
-				if (is_vlan_want_to_be_default_gateway)
+				if (is_vlan_want_to_be_default_gateway_v4)
 				{
 					if (t->VistaOldDefaultGatewayByVLan == NULL || Cmp(t->VistaOldDefaultGatewayByVLan, t->DefaultGatewayByVLan, sizeof(ROUTE_ENTRY)) != 0)
 					{
@@ -362,8 +382,9 @@ void RouteTrackingMain(SESSION *s)
 		// to elect the virtual LAN card as the default gateway
 //		Debug("is_vlan_want_to_be_default_gateway = %u, rs = %u, route_to_server_erased = %u, other_if_default_gateway_metric_min = %u, vlan_default_gateway_metric = %u\n",
 //			is_vlan_want_to_be_default_gateway, rs, route_to_server_erased, other_if_default_gateway_metric_min, vlan_default_gateway_metric);
-		if (is_vlan_want_to_be_default_gateway && (rs != NULL && route_to_server_erased == false) &&
-			other_if_default_gateway_metric_min >= vlan_default_gateway_metric)
+		if ((is_vlan_want_to_be_default_gateway_v4 && other_if_default_gateway_metric_min_v4 >= vlan_default_gateway_metric_v4 ||
+			is_vlan_want_to_be_default_gateway_v6 && other_if_default_gateway_metric_min_v6 >= vlan_default_gateway_metric_v6)
+			&& rs != NULL && route_to_server_erased == false)
 		{
 			// Scan the routing table again
 			for (i = 0;i < table->NumEntry;i++)
@@ -372,8 +393,7 @@ void RouteTrackingMain(SESSION *s)
 
 				if (e->InterfaceID != t->VLanInterfaceId)
 				{
-					if (IPToUINT(&e->DestIP) == 0 &&
-					IPToUINT(&e->DestMask) == 0)
+					if (IsZeroIP(&e->DestIP) && IsZeroIP(&e->DestMask))
 					{
 						char str[64];
 						// Default gateway is found
@@ -486,8 +506,6 @@ void RouteTrackingStart(SESSION *s)
 	Debug("GetBestRouteEntry() Succeed. [Gateway: %s]\n", tmp);
 
 	// Add a route
-	e->Metric = e->OldIfMetric;
-
 	if (AddRouteEntryEx(e, &already_exists) == false)
 	{
 		FreeRouteEntry(e);
@@ -549,8 +567,6 @@ void RouteTrackingStart(SESSION *s)
 			else
 			{
 				// Add a route
-				dns->Metric = dns->OldIfMetric;
-
 				if (AddRouteEntry(dns) == false)
 				{
 					FreeRouteEntry(dns);
@@ -569,8 +585,6 @@ void RouteTrackingStart(SESSION *s)
 
 			if (route_to_real_server_global != NULL)
 			{
-				route_to_real_server_global->Metric = route_to_real_server_global->OldIfMetric;
-
 				if (AddRouteEntry(route_to_real_server_global) == false)
 				{
 					FreeRouteEntry(route_to_real_server_global);
@@ -633,11 +647,6 @@ void RouteTrackingStart(SESSION *s)
 			MsFreeAdapter(a);
 		}
 	}
-	else
-	{
-		// For Win9x
-		Win32RenewDhcp9x(if_id);
-	}
 
 	// Clear the DNS cache
 	Win32FlushDnsCache();
@@ -782,12 +791,12 @@ void RouteTrackingStop(SESSION *s, ROUTE_TRACKING *t)
 		// If the restoring routing entry is a default gateway and
 		// the existing routing table contains another default gateway
 		// on the interface, give up restoring the entry
-		if (IPToUINT(&e->DestIP) == 0 && IPToUINT(&e->DestMask) == 0)
+		if (IsZeroIP(&e->DestIP) && IsZeroIP(&e->DestMask))
 		{
 			for (i = 0;i < table->NumEntry;i++)
 			{
 				ROUTE_ENTRY *r = table->Entry[i];
-				if (IPToUINT(&r->DestIP) == 0 && IPToUINT(&r->DestMask) == 0)
+				if (IsZeroIP(&r->DestIP) && IsZeroIP(&r->DestMask))
 				{
 					if (r->InterfaceID == e->InterfaceID)
 					{

+ 183 - 257
src/Mayaqua/Network.c

@@ -59,9 +59,9 @@
 
 struct ROUTE_CHANGE_DATA
 {
-	OVERLAPPED Overlapped;
 	HANDLE Handle;
 	UINT NumCalled;
+	bool Changed;
 };
 #endif
 
@@ -6124,7 +6124,7 @@ ICMP_RESULT *IcmpApiEchoSend(IP *dest_ip, UCHAR ttl, UCHAR *data, UINT size, UIN
 ROUTE_CHANGE *NewRouteChange()
 {
 #ifdef	OS_WIN32
-	return Win32NewRouteChange();
+	return Win32NewRouteChange2(true, true, NULL);
 #else	// OS_WIN32
 	return NULL;
 #endif	// OS_WIN32
@@ -6134,7 +6134,7 @@ ROUTE_CHANGE *NewRouteChange()
 void FreeRouteChange(ROUTE_CHANGE *r)
 {
 #ifdef	OS_WIN32
-	Win32FreeRouteChange(r);
+	Win32FreeRouteChange2(r);
 #endif	// OS_WIN32
 }
 
@@ -6142,27 +6142,54 @@ void FreeRouteChange(ROUTE_CHANGE *r)
 bool IsRouteChanged(ROUTE_CHANGE *r)
 {
 #ifdef	OS_WIN32
-	return Win32IsRouteChanged(r);
+	return Win32IsRouteChanged2(r);
 #else	// OS_WIN32
 	return false;
 #endif	// OS_WIN32
 }
 
-// Routing table change detector function (Win32)
 #ifdef	OS_WIN32
-ROUTE_CHANGE *Win32NewRouteChange()
+void Win32RouteChangeCallback(void *context, MIB_IPFORWARD_ROW2 *row, MIB_NOTIFICATION_TYPE nt)
+{
+	ROUTE_CHANGE_DATA *data = context;
+	data->Changed = true;
+}
+
+// Routing table change detector function (For Vista and later)
+ROUTE_CHANGE *Win32NewRouteChange2(bool ipv4, bool ipv6, void *callback)
 {
 	ROUTE_CHANGE *r;
 	BOOL ret;
+	ADDRESS_FAMILY family;
 
 	r = ZeroMalloc(sizeof(ROUTE_CHANGE));
 
 	r->Data = ZeroMalloc(sizeof(ROUTE_CHANGE_DATA));
 
-	r->Data->Overlapped.hEvent = CreateEventA(NULL, false, true, NULL);
+	if (ipv4 && ipv6)
+	{
+		family = AF_UNSPEC;
+	}
+	else if (ipv6)
+	{
+		family = AF_INET6;
+	}
+	else
+	{
+		family = AF_INET;
+	}
 
-	ret = NotifyRouteChange(&r->Data->Handle, &r->Data->Overlapped);
-	if (!(ret == NO_ERROR || ret == WSA_IO_PENDING || WSAGetLastError() == WSA_IO_PENDING))
+	if (callback != NULL)
+	{
+		ret = NotifyRouteChange2(family, (PIPFORWARD_CHANGE_CALLBACK)callback, r->Data, false, &r->Data->Handle);
+	}
+	else
+	{
+		// Use default callback if not provided
+		ret = NotifyRouteChange2(family, (PIPFORWARD_CHANGE_CALLBACK)Win32RouteChangeCallback, r->Data, false, &r->Data->Handle);
+	}
+
+	if (ret != NO_ERROR)
 	{
 		Free(r->Data);
 		Free(r);
@@ -6173,7 +6200,7 @@ ROUTE_CHANGE *Win32NewRouteChange()
 	return r;
 }
 
-void Win32FreeRouteChange(ROUTE_CHANGE *r)
+void Win32FreeRouteChange2(ROUTE_CHANGE *r)
 {
 	// Validate arguments
 	if (r == NULL)
@@ -6181,14 +6208,13 @@ void Win32FreeRouteChange(ROUTE_CHANGE *r)
 		return;
 	}
 
-	CancelIPChangeNotify(&r->Data->Overlapped);
-	CloseHandle(r->Data->Overlapped.hEvent);
+	CancelMibChangeNotify2(r->Data->Handle);
 
 	Free(r->Data);
 	Free(r);
 }
 
-bool Win32IsRouteChanged(ROUTE_CHANGE *r)
+bool Win32IsRouteChanged2(ROUTE_CHANGE *r)
 {
 	// Validate arguments
 	if (r == NULL)
@@ -6201,9 +6227,9 @@ bool Win32IsRouteChanged(ROUTE_CHANGE *r)
 		return true;
 	}
 
-	if (WaitForSingleObject(r->Data->Overlapped.hEvent, 0) == WAIT_OBJECT_0)
+	if (r->Data->Changed)
 	{
-		NotifyRouteChange(&r->Data->Handle, &r->Data->Overlapped);
+		r->Data->Changed = false;
 		return true;
 	}
 
@@ -6481,6 +6507,17 @@ void GenerateEui64Address6(UCHAR *dst, UCHAR *mac)
 }
 
 // Examine whether two IP addresses are in the same network
+bool IsInSameNetwork(IP *a1, IP *a2, IP *subnet)
+{
+	if (IsIP4(a1))
+	{
+		return IsInSameNetwork4(a1, a2, subnet);
+	}
+	else
+	{
+		return IsInSameNetwork6(a1, a2, subnet);
+	}
+}
 bool IsInSameNetwork6ByStr(char *ip1, char *ip2, char *subnet)
 {
 	IP p1, p2, s;
@@ -8972,118 +9009,6 @@ void Win32FlushDnsCache()
 	Run("ipconfig.exe", "/flushdns", true, false);
 }
 
-// Update the DHCP address of the specified LAN card
-void Win32RenewDhcp9x(UINT if_id)
-{
-	IP_INTERFACE_INFO *info;
-	ULONG size;
-	int i;
-	LIST *o;
-	// Validate arguments
-	if (if_id == 0)
-	{
-		return;
-	}
-
-	size = sizeof(IP_INTERFACE_INFO);
-	info = ZeroMallocFast(size);
-
-	if (GetInterfaceInfo(info, &size) == ERROR_INSUFFICIENT_BUFFER)
-	{
-		Free(info);
-		info = ZeroMallocFast(size);
-	}
-
-	if (GetInterfaceInfo(info, &size) != NO_ERROR)
-	{
-		Free(info);
-		return;
-	}
-
-	o = NewListFast(CompareIpAdapterIndexMap);
-
-	for (i = 0; i < info->NumAdapters; i++)
-	{
-		IP_ADAPTER_INDEX_MAP *a = &info->Adapter[i];
-
-		Add(o, a);
-	}
-
-	Sort(o);
-
-	for (i = 0; i < (int)(LIST_NUM(o)); i++)
-	{
-		IP_ADAPTER_INDEX_MAP *a = LIST_DATA(o, i);
-
-		if (a->Index == if_id)
-		{
-			char arg[MAX_PATH];
-			Format(arg, sizeof(arg), "/renew %u", i);
-			Run("ipconfig.exe", arg, true, false);
-		}
-	}
-
-	ReleaseList(o);
-
-	Free(info);
-}
-
-// Release the DHCP address of the specified LAN card
-void Win32ReleaseDhcp9x(UINT if_id, bool wait)
-{
-	IP_INTERFACE_INFO *info;
-	ULONG size;
-	int i;
-	LIST *o;
-	// Validate arguments
-	if (if_id == 0)
-	{
-		return;
-	}
-
-	size = sizeof(IP_INTERFACE_INFO);
-	info = ZeroMallocFast(size);
-
-	if (GetInterfaceInfo(info, &size) == ERROR_INSUFFICIENT_BUFFER)
-	{
-		Free(info);
-		info = ZeroMallocFast(size);
-	}
-
-	if (GetInterfaceInfo(info, &size) != NO_ERROR)
-	{
-		Free(info);
-		return;
-	}
-
-	o = NewListFast(CompareIpAdapterIndexMap);
-
-	for (i = 0; i < info->NumAdapters; i++)
-	{
-		IP_ADAPTER_INDEX_MAP *a = &info->Adapter[i];
-
-		Add(o, a);
-	}
-
-	Sort(o);
-
-	for (i = 0; i < (int)(LIST_NUM(o)); i++)
-	{
-		IP_ADAPTER_INDEX_MAP *a = LIST_DATA(o, i);
-
-		if (a->Index == if_id)
-		{
-			char arg[MAX_PATH];
-			Format(arg, sizeof(arg), "/release %u", i);
-			Run("ipconfig.exe", arg, true, wait);
-		}
-	}
-
-	ReleaseList(o);
-
-	Free(info);
-}
-
 // Enumerate a list of virtual LAN cards that contains the specified string
 char **Win32EnumVLan(char *tag_name)
 {
@@ -9342,41 +9267,29 @@ bool Win32GetDefaultDns(IP *ip, char *domain, UINT size)
 	return true;
 }
 
-// IP conversion function for Win32
-void Win32UINTToIP(IP *ip, UINT i)
-{
-	UINTToIP(ip, i);
-}
-
-// IP conversion function for Win32
-UINT Win32IPToUINT(IP *ip)
-{
-	return IPToUINT(ip);
-}
-
-// Remove a routing entry from the routing table
-void Win32DeleteRouteEntry(ROUTE_ENTRY *e)
+// Remove a routing entry from the routing table (For Vista and later)
+void Win32DeleteRouteEntry2(ROUTE_ENTRY *e)
 {
-	MIB_IPFORWARDROW *p;
+	MIB_IPFORWARD_ROW2 *p;
 	// Validate arguments
 	if (e == NULL)
 	{
 		return;
 	}
 
-	p = ZeroMallocFast(sizeof(MIB_IPFORWARDROW));
-	Win32RouteEntryToIpForwardRow(p, e);
+	p = ZeroMallocFast(sizeof(MIB_IPFORWARD_ROW2));
+	Win32RouteEntryToIpForwardRow2(p, e);
 
-	DeleteIpForwardEntry(p);
+	DeleteIpForwardEntry2(p);
 	Free(p);
 }
 
-// Add a routing entry to the routing table
-bool Win32AddRouteEntry(ROUTE_ENTRY *e, bool *already_exists)
+// Add a routing entry to the routing table (For Vista and later)
+bool Win32AddRouteEntry2(ROUTE_ENTRY *e, bool *already_exists)
 {
 	bool ret = false;
 	bool dummy = false;
-	MIB_IPFORWARDROW *p;
+	MIB_IPFORWARD_ROW2 *p;
 	UINT err = 0;
 	// Validate arguments
 	if (e == NULL)
@@ -9390,21 +9303,21 @@ bool Win32AddRouteEntry(ROUTE_ENTRY *e, bool *already_exists)
 
 	*already_exists = false;
 
-	p = ZeroMallocFast(sizeof(MIB_IPFORWARDROW));
-	Win32RouteEntryToIpForwardRow(p, e);
+	p = ZeroMallocFast(sizeof(MIB_IPFORWARD_ROW2));
+	Win32RouteEntryToIpForwardRow2(p, e);
 
-	err = CreateIpForwardEntry(p);
+	err = CreateIpForwardEntry2(p);
 	if (err != 0)
 	{
 		if (err == ERROR_OBJECT_ALREADY_EXISTS)
 		{
-			Debug("CreateIpForwardEntry: Already Exists\n");
+			Debug("CreateIpForwardEntry2: Already Exists\n");
 			*already_exists = true;
 			ret = true;
 		}
 		else
 		{
-			Debug("CreateIpForwardEntry Error: %u\n", err);
+			Debug("CreateIpForwardEntry2 Error: %u\n", err);
 			ret = false;
 		}
 	}
@@ -9418,61 +9331,56 @@ bool Win32AddRouteEntry(ROUTE_ENTRY *e, bool *already_exists)
 	return ret;
 }
 
-// Get the routing table
-ROUTE_TABLE *Win32GetRouteTable()
+// Get the routing table (For Vista and later)
+ROUTE_TABLE *Win32GetRouteTable2(bool ipv4, bool ipv6)
 {
 	ROUTE_TABLE *t = ZeroMallocFast(sizeof(ROUTE_TABLE));
-	MIB_IPFORWARDTABLE *p;
+	MIB_IPFORWARD_TABLE2 *p = NULL;
 	UINT ret;
-	ULONG size_needed;
 	UINT num_retry = 0;
 	LIST *o;
 	UINT i;
 	ROUTE_ENTRY *e;
+	ADDRESS_FAMILY family;
 
-RETRY:
-	p = ZeroMallocFast(sizeof(MIB_IFTABLE));
-	size_needed = 0;
-
-	// Examine the needed size
-	ret = GetIpForwardTable(p, &size_needed, 0);
-	if (ret == ERROR_INSUFFICIENT_BUFFER)
+	if (ipv4 && ipv6)
 	{
-		// Re-allocate the memory block of the needed size
-		Free(p);
-		p = ZeroMallocFast(size_needed);
+		family = AF_UNSPEC;
 	}
-	else if (ret != NO_ERROR)
+	else if (ipv6)
 	{
-		// Acquisition failure
-FAILED:
-		Free(p);
-		t->Entry = MallocFast(0);
-		return t;
+		family = AF_INET6;
+	}
+	else
+	{
+		family = AF_INET;
 	}
 
+RETRY:
 	// Actually get
-	ret = GetIpForwardTable(p, &size_needed, FALSE);
+	ret = GetIpForwardTable2(family, &p);
 	if (ret != NO_ERROR)
 	{
 		// Acquisition failure
 		if ((++num_retry) >= 5)
 		{
-			goto FAILED;
+			FreeMibTable(p);
+			t->Entry = MallocFast(0);
+			return t;
 		}
-		Free(p);
+		FreeMibTable(p);
 		goto RETRY;
 	}
 
 	// Add to the list along
 	o = NewListFast(Win32CompareRouteEntryByMetric);
-	for (i = 0; i < p->dwNumEntries; i++)
+	for (i = 0; i < p->NumEntries; i++)
 	{
 		e = ZeroMallocFast(sizeof(ROUTE_ENTRY));
-		Win32IpForwardRowToRouteEntry(e, &p->table[i]);
+		Win32IpForwardRow2ToRouteEntry(e, &p->Table[i]);
 		Add(o, e);
 	}
-	Free(p);
+	FreeMibTable(p);
 
 	// Sort by metric
 	Sort(o);
@@ -9516,83 +9424,100 @@ int Win32CompareRouteEntryByMetric(void *p1, void *p2)
 	}
 }
 
-// Convert the ROUTE_ENTRY to a MIB_IPFORWARDROW
-void Win32RouteEntryToIpForwardRow(void *ip_forward_row, ROUTE_ENTRY *entry)
+// Convert the ROUTE_ENTRY to a MIB_IPFORWARD_ROW2 (For Vista and later)
+void Win32RouteEntryToIpForwardRow2(void *ip_forward_row, ROUTE_ENTRY *entry)
 {
-	MIB_IPFORWARDROW *r;
+	MIB_IPFORWARD_ROW2 *r;
 	// Validate arguments
 	if (entry == NULL || ip_forward_row == NULL)
 	{
 		return;
 	}
 
-	r = (MIB_IPFORWARDROW *)ip_forward_row;
-	Zero(r, sizeof(MIB_IPFORWARDROW));
+	r = (MIB_IPFORWARD_ROW2 *)ip_forward_row;
+	InitializeIpForwardEntry(r);
 
-	// IP address
-	r->dwForwardDest = Win32IPToUINT(&entry->DestIP);
-	// Subnet mask
-	r->dwForwardMask = Win32IPToUINT(&entry->DestMask);
-	// Gateway IP address
-	r->dwForwardNextHop = Win32IPToUINT(&entry->GatewayIP);
-	// Local routing flag
-	if (entry->LocalRouting)
+	if (IsIP4(&entry->DestIP))
 	{
-		// Local
-		r->dwForwardType = 3;
+		// IP address
+		r->DestinationPrefix.Prefix.Ipv4.sin_family = AF_INET;
+		IPToInAddr(&r->DestinationPrefix.Prefix.Ipv4.sin_addr, &entry->DestIP);
+		// Subnet mask
+		r->DestinationPrefix.PrefixLength = SubnetMaskToInt4(&entry->DestMask);
+		// Gateway IP address
+		r->NextHop.Ipv4.sin_family = AF_INET;
+		IPToInAddr(&r->NextHop.Ipv4.sin_addr, &entry->GatewayIP);
 	}
 	else
 	{
-		// Remote router
-		r->dwForwardType = 4;
+		// IP address
+		r->DestinationPrefix.Prefix.Ipv6.sin6_family = AF_INET6;
+		IPToInAddr6(&r->DestinationPrefix.Prefix.Ipv6.sin6_addr, &entry->DestIP);
+		// Subnet mask
+		r->DestinationPrefix.PrefixLength = SubnetMaskToInt6(&entry->DestMask);
+		// Gateway IP address
+		r->NextHop.Ipv6.sin6_family = AF_INET6;
+		IPToInAddr6(&r->NextHop.Ipv6.sin6_addr, &entry->GatewayIP);
 	}
-	// Protocol
-	r->dwForwardProto = r->dwForwardType - 1;	// Subtract by 1 in most cases
-	if (entry->PPPConnection)
+
+	// Metric offset
+	if (entry->Metric >= entry->IfMetric)
 	{
-		// Isn't this a PPP? Danger!
-		r->dwForwardProto++;
+		r->Metric = entry->Metric - entry->IfMetric;
+	}
+	else
+	{
+		r->Metric = 0;
 	}
-	// Metric
-	r->dwForwardMetric1 = entry->Metric;
-	r->dwForwardMetric2 = r->dwForwardMetric3 = r->dwForwardMetric4 = r->dwForwardMetric5 = 0;
-	r->dwForwardAge = 163240;
 
 	// Interface ID
-	r->dwForwardIfIndex = entry->InterfaceID;
+	r->InterfaceIndex = entry->InterfaceID;
 
-	Debug("Win32RouteEntryToIpForwardRow()\n");
-	Debug(" r->dwForwardDest=%X\n", r->dwForwardDest);
-	Debug(" r->dwForwardMask=%X\n", r->dwForwardMask);
-	Debug(" r->dwForwardNextHop=%X\n", r->dwForwardNextHop);
-	Debug(" r->dwForwardType=%u\n", r->dwForwardType);
-	Debug(" r->dwForwardProto=%u\n", r->dwForwardProto);
-	Debug(" r->dwForwardMetric1=%u\n", r->dwForwardMetric1);
-	Debug(" r->dwForwardMetric2=%u\n", r->dwForwardMetric2);
-	Debug(" r->dwForwardIfIndex=%u\n", r->dwForwardIfIndex);
+	Debug("Win32RouteEntryToIpForwardRow2()\n");
 }
 
-// Convert the MIB_IPFORWARDROW to a ROUTE_ENTRY
-void Win32IpForwardRowToRouteEntry(ROUTE_ENTRY *entry, void *ip_forward_row)
+// Convert the MIB_IPFORWARD_ROW2 to a ROUTE_ENTRY (For Vista and later)
+void Win32IpForwardRow2ToRouteEntry(ROUTE_ENTRY *entry, void *ip_forward_row)
 {
-	MIB_IPFORWARDROW *r;
+	MIB_IPFORWARD_ROW2 *r;
 	// Validate arguments
 	if (entry == NULL || ip_forward_row == NULL)
 	{
 		return;
 	}
 
-	r = (MIB_IPFORWARDROW *)ip_forward_row;
+	r = (MIB_IPFORWARD_ROW2 *)ip_forward_row;
 
 	Zero(entry, sizeof(ROUTE_ENTRY));
-	// IP address
-	Win32UINTToIP(&entry->DestIP, r->dwForwardDest);
-	// Subnet mask
-	Win32UINTToIP(&entry->DestMask, r->dwForwardMask);
-	// Gateway IP address
-	Win32UINTToIP(&entry->GatewayIP, r->dwForwardNextHop);
+
+	MIB_IPINTERFACE_ROW *p;
+	p = ZeroMallocFast(sizeof(MIB_IPINTERFACE_ROW));
+
+	if (((struct sockaddr *)&r->DestinationPrefix.Prefix)->sa_family != AF_INET6)
+	{
+		// IP address
+		InAddrToIP(&entry->DestIP, &r->DestinationPrefix.Prefix.Ipv4.sin_addr);
+		// Subnet mask
+		IntToSubnetMask4(&entry->DestMask, r->DestinationPrefix.PrefixLength);
+		// Gateway IP address
+		InAddrToIP(&entry->GatewayIP, &r->NextHop.Ipv4.sin_addr);
+		// Interface
+		p->Family = AF_INET;
+	}
+	else
+	{
+		// IP address
+		InAddrToIP6(&entry->DestIP, &r->DestinationPrefix.Prefix.Ipv6.sin6_addr);
+		// Subnet mask
+		IntToSubnetMask6(&entry->DestMask, r->DestinationPrefix.PrefixLength);
+		// Gateway IP address
+		InAddrToIP6(&entry->GatewayIP, &r->NextHop.Ipv6.sin6_addr);
+		// Interface
+		p->Family = AF_INET6;
+	}
+
 	// Local routing flag
-	if (r->dwForwardType == 3)
+	if (IsZeroIP(&entry->GatewayIP))
 	{
 		entry->LocalRouting = true;
 	}
@@ -9600,15 +9525,27 @@ void Win32IpForwardRowToRouteEntry(ROUTE_ENTRY *entry, void *ip_forward_row)
 	{
 		entry->LocalRouting = false;
 	}
-	if (entry->LocalRouting && r->dwForwardProto == 3)
+	if (entry->LocalRouting && r->Protocol == 3)
 	{
 		// PPP. Danger!
 		entry->PPPConnection = true;
 	}
+
 	// Metric
-	entry->Metric = r->dwForwardMetric1;
+	p->InterfaceIndex = r->InterfaceIndex;
+	if (GetIpInterfaceEntry(p) == NO_ERROR)
+	{
+		entry->IfMetric = p->Metric;
+		entry->Metric = r->Metric + p->Metric;
+	}
+	else
+	{
+		entry->Metric = r->Metric;
+	}
+	Free(p);
+
 	// Interface ID
-	entry->InterfaceID = r->dwForwardIfIndex;
+	entry->InterfaceID = r->InterfaceIndex;
 }
 
 // Initializing the socket library
@@ -10233,23 +10170,12 @@ ROUTE_ENTRY *GetBestRouteEntryFromRouteTableEx(ROUTE_TABLE *table, IP *ip, UINT
 		return NULL;
 	}
 
-	if (IsIP6(ip))
-	{
-		// IPv6 is not supported
-		return NULL;
-	}
-
 	// Select routing table entry by following rule
 	// 1. Largest subnet mask
 	// 2. Smallest metric value
 	for (i = 0; i < table->NumEntry; i++)
 	{
 		ROUTE_ENTRY *e = table->Entry[i];
-		UINT dest, net, mask;
-
-		dest = IPToUINT(ip);
-		net = IPToUINT(&e->DestIP);
-		mask = IPToUINT(&e->DestMask);
 
 		if (exclude_if_id != 0)
 		{
@@ -10260,10 +10186,10 @@ ROUTE_ENTRY *GetBestRouteEntryFromRouteTableEx(ROUTE_TABLE *table, IP *ip, UINT
 		}
 
 		// Mask test
-		if ((dest & mask) == (net & mask))
+		if (IsInSameNetwork(ip, &e->DestIP, &e->DestMask))
 		{
 			// Calculate the score
-			UINT score_high32 = mask;
+			UINT score_high32 = SubnetMaskToInt(&e->DestMask);
 			UINT score_low32 = 0xFFFFFFFF - e->Metric;
 			UINT64 score64 = (UINT64)score_high32 * (UINT64)0x80000000 * (UINT64)2 + (UINT64)score_low32;
 			if (score64 == 0)
@@ -10294,24 +10220,24 @@ ROUTE_ENTRY *GetBestRouteEntryFromRouteTableEx(ROUTE_TABLE *table, IP *ip, UINT
 
 	if (tmp != NULL)
 	{
-		UINT dest, gateway, mask;
-
 		// Generate an entry
 		ret = ZeroMallocFast(sizeof(ROUTE_ENTRY));
 
 		Copy(&ret->DestIP, ip, sizeof(IP));
-		SetIP(&ret->DestMask, 255, 255, 255, 255);
+		if (IsIP4(ip))
+		{
+			IntToSubnetMask4(&ret->DestMask, 32);
+		}
+		else
+		{
+			IntToSubnetMask6(&ret->DestMask, 128);
+		}
 		Copy(&ret->GatewayIP, &tmp->GatewayIP, sizeof(IP));
 		ret->InterfaceID = tmp->InterfaceID;
 		ret->LocalRouting = tmp->LocalRouting;
-		ret->OldIfMetric = tmp->Metric;
-		ret->Metric = 1;
+		ret->Metric = tmp->Metric;
+		ret->IfMetric = tmp->IfMetric;
 		ret->PPPConnection = tmp->PPPConnection;
-
-		// Calculation related to routing control
-		dest = IPToUINT(&tmp->DestIP);
-		gateway = IPToUINT(&tmp->GatewayIP);
-		mask = IPToUINT(&tmp->DestMask);
 	}
 
 	return ret;
@@ -10468,9 +10394,9 @@ void RouteToStr(char *str, UINT str_size, ROUTE_ENTRY *e)
 	IPToStr(dest_mask, sizeof(dest_mask), &e->DestMask);
 	IPToStr(gateway_ip, sizeof(gateway_ip), &e->GatewayIP);
 
-	Format(str, str_size, "%s/%s %s m=%u oif=%u if=%u lo=%u p=%u",
+	Format(str, str_size, "%s/%s %s m=%u ifm=%u if=%u lo=%u p=%u",
 	       dest_ip, dest_mask, gateway_ip,
-	       e->Metric, e->OldIfMetric, e->InterfaceID,
+	       e->Metric, e->IfMetric, e->InterfaceID,
 	       e->LocalRouting, e->PPPConnection);
 }
 
@@ -10479,7 +10405,7 @@ void DeleteRouteEntry(ROUTE_ENTRY *e)
 {
 	Debug("DeleteRouteEntry();\n");
 #ifdef	OS_WIN32
-	Win32DeleteRouteEntry(e);
+	Win32DeleteRouteEntry2(e);
 #else	// OS_WIN32
 	UnixDeleteRouteEntry(e);
 #endif
@@ -10496,7 +10422,7 @@ bool AddRouteEntryEx(ROUTE_ENTRY *e, bool *already_exists)
 	bool ret = false;
 	Debug("AddRouteEntryEx();\n");
 #ifdef	OS_WIN32
-	ret = Win32AddRouteEntry(e, already_exists);
+	ret = Win32AddRouteEntry2(e, already_exists);
 #else	// OS_WIN32
 	ret = UnixAddRouteEntry(e, already_exists);
 #endif
@@ -10512,7 +10438,7 @@ ROUTE_TABLE *GetRouteTable()
 	UCHAR hash[MD5_SIZE];
 
 #ifdef	OS_WIN32
-	t = Win32GetRouteTable();
+	t = Win32GetRouteTable2(true, true);
 #else	//OS_WIN32
 	t = UnixGetRouteTable();
 #endif	// OS_WIN32

+ 10 - 12
src/Mayaqua/Network.h

@@ -285,7 +285,7 @@ struct ROUTE_ENTRY
 	bool LocalRouting;
 	bool PPPConnection;
 	UINT Metric;
-	UINT OldIfMetric;
+	UINT IfMetric;
 	UINT InterfaceID;
 	UINT64 InnerScore;
 };
@@ -958,14 +958,12 @@ void Win32Select(SOCKSET *set, UINT timeout, CANCEL *c1, CANCEL *c2);
 void Win32InitAsyncSocket(SOCK *sock);
 void Win32JoinSockToSockEvent(SOCK *sock, SOCK_EVENT *event);
 void Win32FreeAsyncSocket(SOCK *sock);
-void Win32IpForwardRowToRouteEntry(ROUTE_ENTRY *entry, void *ip_forward_row);
-void Win32RouteEntryToIpForwardRow(void *ip_forward_row, ROUTE_ENTRY *entry);
+void Win32IpForwardRow2ToRouteEntry(ROUTE_ENTRY *entry, void *ip_forward_row);
+void Win32RouteEntryToIpForwardRow2(void *ip_forward_row, ROUTE_ENTRY *entry);
 int Win32CompareRouteEntryByMetric(void *p1, void *p2);
-ROUTE_TABLE *Win32GetRouteTable();
-bool Win32AddRouteEntry(ROUTE_ENTRY *e, bool *already_exists);
-void Win32DeleteRouteEntry(ROUTE_ENTRY *e);
-void Win32UINTToIP(IP *ip, UINT i);
-UINT Win32IPToUINT(IP *ip);
+ROUTE_TABLE *Win32GetRouteTable2(bool ipv4, bool ipv6);
+bool Win32AddRouteEntry2(ROUTE_ENTRY *e, bool *already_exists);
+void Win32DeleteRouteEntry2(ROUTE_ENTRY *e);
 UINT Win32GetVLanInterfaceID(char *instance_name);
 char **Win32EnumVLan(char *tag_name);
 void Win32Cancel(CANCEL *c);
@@ -977,12 +975,11 @@ void Win32CleanupSockEvent(SOCK_EVENT *event);
 bool Win32WaitSockEvent(SOCK_EVENT *event, UINT timeout);
 bool Win32GetDefaultDns(IP *ip, char *domain, UINT size);
 bool Win32GetDnsSuffix(char *domain, UINT size);
-void Win32ReleaseDhcp9x(UINT if_id, bool wait);
 void Win32FlushDnsCache();
 int CompareIpAdapterIndexMap(void *p1, void *p2);
-ROUTE_CHANGE *Win32NewRouteChange();
-void Win32FreeRouteChange(ROUTE_CHANGE *r);
-bool Win32IsRouteChanged(ROUTE_CHANGE *r);
+ROUTE_CHANGE *Win32NewRouteChange2(bool ipv4, bool ipv6, void *callback);
+void Win32FreeRouteChange2(ROUTE_CHANGE *r);
+bool Win32IsRouteChanged2(ROUTE_CHANGE *r);
 bool Win32GetAdapterFromGuid(void *a, char *guid);
 SOCKET Win32Accept(SOCK *sock, SOCKET s, struct sockaddr *addr, int *addrlen, bool ipv6);
 
@@ -1225,6 +1222,7 @@ void GetLoopbackAddress6(IP *ip);
 UINT GetIPAddrType6(IP *ip);
 UINT GetIPv6AddrType(IPV6_ADDR *addr);
 void GetPrefixAddress6(IP *dst, IP *ip, IP *subnet);
+bool IsInSameNetwork(IP *a1, IP *a2, IP *subnet);
 bool IsInSameNetwork6(IP *a1, IP *a2, IP *subnet);
 bool IsInSameNetwork6ByStr(char *ip1, char *ip2, char *subnet);
 void GenerateEui64Address6(UCHAR *dst, UCHAR *mac);