/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#define _WIN32_WINNT 0x0600
#include "exception/WinFail.h"
#include "util/platform/netdev/NetPlatform.h"
#include "util/Bits.h"
#include "util/platform/Sockaddr.h"
#include
#include
#define NET_LUID misalligned_NET_LUID
#define PNET_LUID misalligned_PNET_LUID
#define IF_LUID misalligned_IF_LUID
#define PIF_LUID misalligned_PIF_LUID
#include
#undef NET_LUID
#undef PNET_LUID
#undef IF_LUID
#undef PIF_LUID
// mingw-w64 incorrectly pragma pack's this to 1 byte.
typedef union NET_LUID {
ULONG64 Value;
__C89_NAMELESS struct { /* bitfield with 64 bit types. */
ULONG64 Reserved :24;
ULONG64 NetLuidIndex :24;
ULONG64 IfType :16;
} Info;
} NET_LUID, *PNET_LUID;
Assert_compileTime(sizeof(NET_LUID) == 8);
typedef NET_LUID IF_LUID, *PIF_LUID;
#include
#include
#include
#include
#include
#include
#include
#include
static NET_LUID getLuid(const char* name, struct Except* eh)
{
uint16_t ifName[IF_MAX_STRING_SIZE + 1] = {0};
WinFail_check(eh,
(!MultiByteToWideChar(CP_UTF8, 0, name, strlen(name), ifName, IF_MAX_STRING_SIZE + 1))
);
NET_LUID out;
WinFail_check(eh, ConvertInterfaceAliasToLuid(ifName, &out));
return out;
}
static LONG flushAddresses(NET_LUID luid, MIB_UNICASTIPADDRESS_TABLE* table)
{
LONG out = NO_ERROR;
for (int i = 0; i < (int)table->NumEntries; i++) {
if (table->Table[i].InterfaceLuid.Value == luid.Value) {
if ((out = DeleteUnicastIpAddressEntry(&table->Table[i]))) {
return out;
}
}
}
return out;
}
void NetPlatform_flushAddresses(const char* deviceName, struct Except* eh)
{
NET_LUID luid = getLuid(deviceName, eh);
MIB_UNICASTIPADDRESS_TABLE* table;
WinFail_check(eh, GetUnicastIpAddressTable(AF_INET, &table));
LONG ret = flushAddresses(luid, table);
FreeMibTable(table);
if (ret) {
WinFail_fail(eh, "DeleteUnicastIpAddressEntry(&table->Table[i])", ret);
}
WinFail_check(eh, GetUnicastIpAddressTable(AF_INET6, &table));
ret = flushAddresses(luid, table);
FreeMibTable(table);
if (ret) {
WinFail_fail(eh, "DeleteUnicastIpAddressEntry(&table->Table[i])", ret);
}
}
#include "util/Hex.h"
#include
static void setupRoute(const char* deviceName,
const uint8_t* addrBytes,
int prefixLen,
int addrFam,
struct Except* eh)
{
void WINAPI InitializeIpForwardEntry(PMIB_IPFORWARD_ROW2 Row);
MIB_IPFORWARD_ROW2 row = {
.InterfaceLuid = getLuid(deviceName, eh),
.ValidLifetime = WSA_INFINITE,
.PreferredLifetime = WSA_INFINITE,
.Metric = 0xffffffff,
.Protocol = MIB_IPPROTO_NETMGMT,
.SitePrefixLength = 255,
.DestinationPrefix = {
.PrefixLength = prefixLen,
.Prefix = { .si_family = addrFam }
},
.NextHop = { .si_family = addrFam },
.Loopback = false,
.AutoconfigureAddress = false,
.Immortal = false,
.Age = 0,
.Origin = 0
};
if (addrFam == AF_INET6) {
Bits_memcpyConst(&row.DestinationPrefix.Prefix.Ipv6.sin6_addr, addrBytes, 15);
row.DestinationPrefix.Prefix.Ipv6.sin6_family = AF_INET6;
// set the gateway addr to the client's addr +1
uint64_t addr[2];
Bits_memcpyConst(addr, addrBytes, 16);
addr[1] = Endian_hostToBigEndian64(Endian_bigEndianToHost64(addr[1]) + 1);
if (!addr[1]) {
addr[0] = Endian_hostToBigEndian64(Endian_bigEndianToHost64(addr[0]) + 1);
}
// Bits_memcpyConst(&row.NextHop.Ipv6.sin6_addr, addr, 16);
} else {
Bits_memcpyConst(&row.DestinationPrefix.Prefix.Ipv4.sin_addr, addrBytes, 4);
row.DestinationPrefix.Prefix.Ipv4.sin_family = AF_INET;
uint32_t addr;
Bits_memcpyConst(&addr, addrBytes, 4);
addr = Endian_hostToBigEndian32(Endian_bigEndianToHost32(addr) + 1);
Bits_memcpyConst(&row.NextHop.Ipv4.sin_addr, &addr, 4);
}
//InitializeIpForwardEntry(&row);
uint8_t buff[sizeof(row) * 2 + 1];
Hex_encode(buff, sizeof(buff), (uint8_t*) &row, sizeof(row));
printf("%s %d\n", buff, row.SitePrefixLength);
// Hex_encode(buff, sizeof(buff), (uint8_t*) &row, sizeof(row));
// printf("%s %d<\n", buff, row.SitePrefixLength);
WinFail_check(eh, CreateIpForwardEntry2(&row));
}
void NetPlatform_addAddress(const char* name,
const uint8_t* addrBytes,
int prefixLen,
int addrFam,
struct Log* logger,
struct Except* eh)
{
MIB_UNICASTIPADDRESS_ROW ipRow = {
.PrefixOrigin = IpPrefixOriginUnchanged,
.SuffixOrigin = IpSuffixOriginUnchanged,
.ValidLifetime = 0xFFFFFFFF,
.PreferredLifetime = 0xFFFFFFFF,
.OnLinkPrefixLength = 0xFF
};
ipRow.InterfaceLuid = getLuid(name, eh);
ipRow.Address.si_family = addrFam;
if (addrFam == Sockaddr_AF_INET6) {
Bits_memcpyConst(&ipRow.Address.Ipv6.sin6_addr, addrBytes, 16);
} else if (addrFam == Sockaddr_AF_INET) {
Bits_memcpyConst(&ipRow.Address.Ipv4.sin_addr, addrBytes, 4);
} else {
Assert_true(0);
}
ipRow.OnLinkPrefixLength = prefixLen;
WinFail_check(eh, CreateUnicastIpAddressEntry(&ipRow));
return;
setupRoute(name, addrBytes, prefixLen, addrFam, eh);
}
void NetPlatform_setMTU(const char* interfaceName,
uint32_t mtu,
struct Log* logger,
struct Except* eh)
{
// I looked all through the Windows API and setting the MTU is beyond me.
// But I do know how to do it through netsh.
// We know the connection names on Windows can't have any badly-behaved
// characters that would need escaping.
const char* format = ("netsh interface ipv6 set subinterface "
"\"%s\" mtu=%d");
// How much space do we need to fit the pattern substituted with the
// interface name? We ought to use _vscprintf, but the compiler chokes on
// its variable arguiment list. For now we overestimate: pattern length +
// interrface name length + estimated size of the MTU number according to
// + a byte for the null
// terminator.
uint32_t totalSize = strlen(format) + strlen(interfaceName) +
(CHAR_BIT * sizeof(uint32_t)) / 3 + 3 + 1;
// Make a buffer to prepare our command in
char buffer[totalSize];
// Fill in the interface name
snprintf(buffer, totalSize, format, interfaceName, mtu);
Log_debug(logger, "Going to run command: %s", buffer);
// Make the netsh call, and die if it returns the wrong thing.
WinFail_check(eh, system(buffer));
}