What is sin6_scope_id for the IPv6 loopback address? - windows

I want to create a socket bound to any port on the local machine so I can simulate socketpair() on Windows. When I want this socket to be IPv6, what value should I set for sin6_scope_id? Do I actually have to enumerate the adapters and find the loopback adapter in order to fill in that field?
In other words, what do I need to do with this in order to bind to any local port?
struct sockaddr_in6 addr;
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(0);
addr.sin6_flowinfo = 0;
addr.sin6_addr = in6addr_loopback;
addr.sin6_scope_id = ????;

Some socket gurus might know a direct way to set it. But it's easier to just query the system for it. This will correctly set both "flowinfo" and "scope_id" for you.
General approach is this:
Invoke getifaddrs to get a list of ifaddrs instances.
Enumerate the list of ifaddrs returned from this api until you find the AF_INET6 adapter with the IFF_LOOPBACK bit set on the ifa_flags member. This is the adapter you want to bind to.
Now that you have a pointer to the ifaddrs instance, cast its ifa_addr member to a (sockaddr_in6*). Copy this structure into your own addr and set the port.
Some sample code below:
struct sockaddr_in6 addr = {0};
ifaddrs* pList = NULL;
ifaddrs* pAdapterFound = NULL;
ifaddrs* pAdapter = NULL;
getifaddrs(&pList);
pAdapter = pList;
while (pAdapter)
{
if ((pAdapter->ifa_addr != NULL) &&
(family == pAdapter->ifa_addr->sa_family == AF_INET6) &&
(pAdapter->ifa_flags & IFF_LOOPBACK))
{
pAdapterFound = pAdapter;
break;
}
pAdapter = pAdapter->ifa_next;
}
if (pAdapterFound)
{
// C++ way to copy the sockaddr_in6 struct
addr = *(reinterpret_cast<sockaddr_in6*>(pAdapterFound->ifa_addr)); // C++
// If you are coding in C instead of C++
// memcpy(&addr, pAdapterFound->ifa_addr, sizeof(addr));
addr.sin6_port = htons(0); // or whatever port you want to bind to
}
freeifaddrs(pList);
You are welcome to reference a helper function I have called GetSocketAddressForAdapter. You could invoke it as follows:
GetSocketAddressforAdapter(AF_INET6, "::1", 0, &address);

Related

winsock2: How to get the ipv4/ipv6 address of a connected client after server side code calls `accept()`

There are other similar questions on this site, but they either do not related to winsock2 or they are suitable only for use with ipv4 address spaces. The default compiler for Visual Studio 2019 produces an error when the ntoa function is used, hence an ipv4 and ipv6 solution is required.
I did once produce the code to do this for a Linux system however I am currently at work and do not have access to that. It may or may not be "copy and paste"-able into a windows environment with winsock2. (Edit: I will of course add that code later this evening, but of course it might not be useful.)
The following contains an example, however this is an example for client side code, not server side code.
https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3c.html
Here, the getaddrinfo() function is used to obtain a structure containing matching ipv4 and ipv6 addresses. To obtain this information there is some interaction with DNS, which is not required in this case.
I have some server code which calls accept() (after bind and listen) to accept a client connection. I want to be able to print the client ip address and port to stdout.
The most closely related question on this site is here. However the answer uses ntoa and is only ipv4 compatible.
What I have so far:
So far I have something sketched out like this:
SOCKET acceptSocket = INVALID_SOCKET;
SOCKADDR_IN addr; // both of these are NOT like standard unix sockets
// I don't know how they differ and if they can be used with standard
// unix like function calls (eg: inet_ntop)
int addrlen = sizeof addr;
acceptSocket = accept(listenSocket, (SOCKADDR*)&addr, &addrlen);
if(acceptSocket == INVALID_SOCKET)
{
// some stuff
}
else
{
const std::size_t addrbuflen = INET6_ADDRSRTLEN;
char addrbuf[addrbuflen] = '\0'
inet_ntop(AF_INET, (void*)addr.sin_addr, (PSTR)addrbuf, addrbuflen);
// above line does not compile and mixes unix style function calls
// with winsock2 structures
std::cout << addrbuf << ':' << addr.sin_port << std::endl;
}
getpeername()
int ret = getpeername(acceptSocket, addrbuf, &addrbuflen);
// addrbuf cannot convert from char[65] to sockaddr*
if(ret == ???)
{
// TODO
}
You need to access the SOCKADDR. This is effectively a discriminated union. The first field tells you whether its an IPv4 (==AF_INET) or IPv6 (==AF_INET6) address. Depending on that you cast the addr pointer to be either struct sockaddr_in* or struct sockaddr_in6*, and then read off the IP address from the relevant field.
C++ code snippet in vs2019:
char* CPortListener::get_ip_str(struct sockaddr* sa, char* s, size_t maxlen)
{
switch (sa->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr),
s, maxlen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr),
s, maxlen);
break;
default:
strncpy(s, "Unknown AF", maxlen);
return NULL;
}
return s;
}
Example:
{
...
char s[INET6_ADDRSTRLEN];
sockaddr_storage ca;
socklen_t al = sizeof(ca);
SOCKET recv = accept(sd, (sockaddr*)&ca, &al);
pObj->m_ip = get_ip_str(((sockaddr*)&ca),s,sizeof(s));
}

Windows Network Device Driver: Set link UP/DOWN from within the driver

I am writing a network driver for Windows. I want to do something like the pseudocode below:
Init_interface_link_status = disconnected (Equivalent to DOWN in Linux)
Repeat using delayed workitem:
if (condition is true)
interface_link_status = connected (UP)
break
else
interface_link_status = disconnected (DOWN)
All this happens inside the driver code.
I am using Windows Driver Samples as reference. I have found something that looks promising: https://github.com/microsoft/Windows-driver-samples/blob/master/network/ndis/netvmini/6x/adapter.c#L353
AdapterGeneral.MediaConnectState = HWGetMediaConnectStatus(Adapter);
I can set this MediaConnectSate to MediaConnectStateDisconnected here and the driver initialises in Disconnected state which is what I want.
But I can't find a way to change this state elsewhere after the driver is initialised.
Found inspiration from a proprietary network driver code.
This function turns interface on/off:
VOID NSUChangeAdapterLinkState(
_In_ PMP_ADAPTER Adapter,
_In_ BOOLEAN TurnInterfaceUP)
/*++
Routine Description:
Change Adapter Link's state. This is equivalent to doing ifup/ifdown on Linux.
Arguments:
Adapter - Pointer to our adapter
TurnInterfaceUP - Pass TRUE to turn interface UP, FALSE to turn DOWN
Return Value:
None
--*/
{
NDIS_LINK_STATE LinkState;
NDIS_STATUS_INDICATION StatusIndication;
RtlZeroMemory(&LinkState, sizeof(LinkState));
LinkState.Header.Revision = NDIS_LINK_STATE_REVISION_1;
LinkState.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
LinkState.Header.Size = NDIS_SIZEOF_LINK_STATE_REVISION_1;
if (TurnInterfaceUP)
{
LinkState.MediaConnectState = MediaConnectStateConnected;
MP_CLEAR_FLAG(Adapter, fMP_DISCONNECTED);
} else
{
LinkState.MediaConnectState = MediaConnectStateDisconnected;
MP_SET_FLAG(Adapter, fMP_DISCONNECTED);
}
LinkState.RcvLinkSpeed = Adapter->ulLinkRecvSpeed;
LinkState.XmitLinkSpeed = Adapter->ulLinkSendSpeed;
LinkState.MediaDuplexState = MediaDuplexStateFull;
RtlZeroMemory(&StatusIndication, sizeof(StatusIndication));
StatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
StatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
StatusIndication.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1;
StatusIndication.SourceHandle = Adapter->AdapterHandle;
StatusIndication.StatusCode = NDIS_STATUS_LINK_STATE;
StatusIndication.StatusBuffer = &LinkState;
StatusIndication.StatusBufferSize = sizeof(NDIS_LINK_STATE);
NdisMIndicateStatusEx(Adapter->AdapterHandle, &StatusIndication);
}

How to get IP and Port from net.Addr when it could be a net.UDPAddr or net.TCPAddr

I'm running a hybrid server which listens on both TCP and UDP and need to get the local port, remote IP address and remote port. Currently the way I'm checking if the underlying type is net.UDPAddr or a net.TCPAddr is the following:
// BAD: but not sure a better way
switch reflect.TypeOf(remoteAddr).String() {
case "*net.UDPAddr":
p.SrcIP = remoteAddr.(*net.UDPAddr).IP.String()
p.SrcPort = uint(remoteAddr.(*net.UDPAddr).Port)
p.DstPort = uint(localAddr.(*net.UDPAddr).Port)
case "*net.TCPAddr":
p.SrcIP = remoteAddr.(*net.TCPAddr).IP.String()
p.SrcPort = uint(remoteAddr.(*net.TCPAddr).Port)
p.DstPort = uint(localAddr.(*net.TCPAddr).Port)
}
I'm not the greatest fan of this, if anyone has any cleaner looking solutions that would be greatly appreciated
No need for reflection, just do a proper type assertion switch instead:
switch addr := remoteAddr.(type) {
case *net.UDPAddr:
p.SrcIP = addr.IP.String()
p.SrcPort = uint(addr.Port)
p.DstPort = uint(localAddr.(*net.UDPAddr).Port)
case *net.TCPAddr:
p.SrcIP = addr.IP.String()
p.SrcPort = uint(addr.Port)
p.DstPort = uint(localAddr.(*net.TCPAddr).Port)
}

Set CONNMARK in netfilter module

I have a netfilter module which hooks into NF_INET_LOCAL_OUT. In the hook function, I set the mark as follows:
if (tcp_header->syn && dport == 80) {
skb->mark = 0x1;
}
However, this seems to set the mark only for this packet and not the connection mark.
How do I set the connmark to 0x1 so that all packets in this flow
have this mark?
How do I access connmark for a packet in the
hook function?
Thanks for your help.
NB: I am not a kernel network guru; I'm just good at reading the network code itself :)
Cribbing entirely from net/netfilter/xt_connmark.c in the latest version (4.15.8), try the following code:
if (tcp_header->syn && dport == 80) {
skb->mark = 0x1;
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
if (ct != NULL) {
u_int32_t newmark;
newmark = 0x01;
if (ct->mark != newmark) {
ct->mark = newmark;
nf_conntrack_event_cache(IPCT_MARK, ct);
}
}
}
Basically, in order to set the CONNMARK itself, you need to first get the actual conntrack entry for the flow. Once you've done that, you see if the current mark is already set to your new mark of 0x01. If it isn't, you set the mark and fire an event that the mark has been set.
You may want to look a bit more into connmark_tg, which is where this snippet is modified from; it may give you a bit more insight than just this code block alone.
Best of luck!

Convert Network Interface Name

I am trying to get a list of active network interfaces with end user understandable names. Like the names listed in System Preferences instead of en0 en5.
I have the raw interfaces using getifaddrs but haven't been able to find how to take those and get the system names of Ethernet or Wifi.
Anyone know how to do this? This would be for macOS.
What I have now:
struct ifaddrs *ifap;
if( getifaddrs(&ifap) == 0 ){
struct ifaddrs *interface;
for (interface = ifap; interface != NULL; interface = interface->ifa_next) {
unsigned int flags = interface->ifa_flags;
struct sockaddr *addr = interface->ifa_addr;
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if ((flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING)) {
if (addr->sa_family == AF_INET || addr->sa_family == AF_INET6) {
// Convert interface address to a human readable string:
char host[NI_MAXHOST];
getnameinfo(addr, addr->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
printf("interface:%s, address:%s\n", interface->ifa_name, host);
// MAGIC HERE TO CONVERT ifa_name to "Ethernet" or something
}
}
}
freeifaddrs(ifap);
This is possible with System Configuration on macOS. In Objective-C like so:
CFArrayRef ref = SCNetworkInterfaceCopyAll();
NSArray* networkInterfaces = (__bridge NSArray *)(ref);
for(int i = 0; i < networkInterfaces.count; i += 1) {
SCNetworkInterfaceRef interface = (__bridge SCNetworkInterfaceRef)(networkInterfaces[i]);
CFStringRef displayName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
CFStringRef bsdName = SCNetworkInterfaceGetBSDName(interface);
NSLog(#"Name:%# \ninterface: %#\nbsd:%#",displayName, SCNetworkInterfaceGetInterfaceType(interface), bsdName);
}
The localized display name will be something like Display Ethernet or WiFi and the BSD name will be something like en5 which will allow matching to the above code.
This approach doesn't work on iOS, but there aren't really any other configurations on iOS anyway.

Resources