How to add Multicast Group under a specific interface (Windows) - windows

I can use the command "netsh interface ip show joins" in cmd to show multicast group under each interface. But I really don't know how to add a group to a
interface, like adding a IP address 239.39.188.188 to "Interface 8: VirtualBox Host-Only Network". The simplest way would be appreciated.
Interface 3: Ethernet
Scope References Last Address
---------- ---------- ---- ---------------------
0 0 Yes 224.0.0.1
Interface 1: Loopback Interface
Scope References Last Address
---------- ---------- ---- ---------------------
0 2 Yes 239.255.255.250
Interface 8: VirtualBox Host-Only Network
Scope References Last Address
---------- ---------- ---- ---------------------
0 0 Yes 224.0.0.1
0 1 Yes 224.0.0.251
239.39.188.188 // this is what I want to add
Btw, I tried with some methods, like opening UDP socket and setting IP_ADD_MEMBERSHIP (How to add my host to Multicast Group...!). Also, I tried with a command on linux "ip maddr [ add | del ] MULTIADDR dev STRING".
After that, I observed that IP_ADD_MEMBERSHIP was set successfully. But as the result, in the above table, I cannot add a group under a specific interface.
For opening UDP socket and setting IP_ADD_MEMBERSHIP part, I coded in linux as belows.
ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(_outIP.c_str()); // _outIP is destination address(group address), interface is ethernet interface
mreq.imr_interface.s_addr = _interface.length() > 0 ? inet_addr(_interface.c_str()) : htonl(INADDR_ANY);
if (setsockopt(_udpSock,IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) {
cout << "Fail to add ip membership!!!!" << endl;
}
else {
cout << "Success to add ip membership!!!!" << endl;
s = sprintf(warnmsg, "Success to add ip membership!!!!");
_ofile->write(warnmsg, s);
}

You must keep the socket with which you joined the group open forever. In other words, your program must not terminate. Add a for (;;) { sleep(1000000); } or so at the end.
When you program terminates, the socket gets closed automatically and your OS (Windows or Linux, it does not matter) leaves the group again.
What happens in the OS is slightly more complex as multiple programs may join the same multicast group, so the OS keeps a reference count and the machine only leaves the group when the group is no longer references by any socket.

Related

How to implement kernel module for ip forwarding

I want to implement my own kernel module to replace the need of IPtables (since I need to free the space IPtables takes)
I was easily able to filter packets with my custom module but am not successful in IP forwarding.
I want the specific forwarding: I want to send a packet from A to C via B. B has my cutom module and the rule that any packet received on port 3050 it forwards to C on port 80.
I did the following:
I hooked a function to the PRE_ROUTING hook of Netfilter. In this hook I check if the packet is from port 3050, if so I save the source IP address and port and change the destination IP to C's IP address and the destination port to 80.
I hooked a function to the POST_ROUTING hook of Netfilter. In this hook I check if the destination address is C's IP address I change the source IP address to B's IP address. I then recalculate the checksum of the IP and TCP headers.
PRE_ROUTING code:
'''
if ((3050 == getDestPort(skb, ipHeader->protocol))) {
realSrcAddr = ipHeader->saddr;
realSrcPort = tcpHeader->dest;
ipHeader->daddr = DEST_ADDR;
tcpHeader->dest = 80;
return NF_ACCEPT;
}
if (ipHeader->saddr == DEST_ADDR) {
ipHeader->daddr = realSrcAddr;
tcpHeader->dest = realSrcPort;
return NF_ACCEPT;
}
'''
POST_ROUTING code:
'''
if ((ipHeader->daddr == DEST_ADDR) || (ipHeader->saddr == DEST_ADDR)) {
printk(KERN_INFO "src switching in fwding...");
ipHeader->saddr = MY_ADDR;
tcpHeader->source = MY_ADDR;
ipHeader->check = 0;
ipHeader->check = compute_checksum((unsigned short*)ipHeader, ipHeader->ihl<<2);
tcpHeader->check = compute_tcp_checksum((unsigned short*)tcpHeader);
}
'''
I see the packet is sent from B to C but C doesn't respond to it. am I missing a CRC/checksum to update? is there other stuff I need to do to implement ip forwarding? is there a HOW TO guide anywhere?
Thanks,
Robbie

VLAN Priority, DEI and ID are missing in UDP Package

I am building an ethernet simulation project to send and receive UDP packages to an external device (let's call it A).
I am supposed to simulate multiple devices, some of them send UDP packages (let's call them B) and some receive UDP packages (let's call them C), B and C are on two different VLANs with two different IDs.
I used an external ETH/Adapter for B and C, which both are connected to a switch alongside the main device A (which can see both the VLANs). then I configured the two eth/adp on windows by setting the "VLAN and Priority" to Enabled and Set VLAN ID with the correct ID for each B and C, finally, I set static IPs for each one of them.
I then used QT to create the simulation project, The Receiving parts are perfect Device A is transmitting UDP packages to Multicast and I join with VLAN C on the Multicast and start reading these frames.
The problem is with sending, I am able to send the frames correctly however the 4 bytes that define the Priority, DEI, and ID are missing (which means device A is not receiving and dumping these frames)
You can see in the below screenshot, on the right the healthy packages that are accepted by device A and on the left the simulated frames that are not accepted
Comaprison between accepted and unaccepted packages
Here is the Code I use to bind and Join Multicast
socket_1 = new QUdpSocket(this);
qDebug() << "Binding UDP Socket ..." ;
bool bind_res = socket_1->bind(QHostAddress("192.168.11.4"), 51011 , QUdpSocket::ShareAddress);
if(!bind_res)
{
qDebug() << "Faild to bind with Error: " +socket_1->errorString() ;
QApplication::quit();
}
bool join_res = socket_1->joinMulticastGroup(interface->GRP_IP,interface->Qinterface);
if(!join_res)
{
qDebug() << "Failed to join with error: "+ socket_1->errorString() ;
QApplication::quit();
}
connect(socket_1, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
qDebug() << "UDP Socket initialized successfully ..." ;
and here is the function to send (interface->GRP_IP is the Multicast IP)
void UDPSocket_VLAN11::sendUDP_1(QByteArray data)
{
qint64 res = socket_1->writeDatagram(data, interface->GRP_IP, 50011);
qDebug() << " --- Sending UDP Packet ---";
qDebug() << "Sending to: " << interface->GRP_IP;
qDebug() << "Sending port: " << port;
qDebug() << "Sending Size: " << data.size();
qDebug() << "Sending: " << data.toHex().toLower();
qDebug() << "Sending Result: " << res;
}
Can someone please point how to set these values weather it's in the configuration of the VLAN or the socket in QT ?
So yes, as #Zac67 mentioned, the main issue was that the eth/usb adapters weren't supporting this protocol and I had a choice of either keep looking for the right adapters or, as I finally did, to change the HW setup and get ride of the adapters and instead I used the native NIC ethernet port on the machine and configured it using Hyper-V to simulate the VLAN

Make IPv6 UDP socket on windows to receive from any interface

I want to have an IPv6 UDP socket that can receive broadcast/multicast messages from any local interface using Link-Local addresses.
In Linux it is enough to bind it to in6addr_any, but in Windows you will not receive any multicast until you join a multicast group using setsockopt() + IPV6_JOIN_GROUP. The problem that an interface index must be provided during this option. But this is inconvenient. Is there a way to receive multicast from any interface in Windows?
UPD: I use destination address ff02::1 (All Nodes Address)
For IPv4, the index of the network interface is the IP address; for IPv6 the index of the network interface is returned by the method socket.getaddrinfo.
The code below shows how to listen to multicast on all network interfaces:
from socket import AF_INET6, AF_INET
import socket
import struct
# Bugfix for Python 3.6 for Windows ... missing IPPROTO_IPV6 constant
if not hasattr(socket, 'IPPROTO_IPV6'):
socket.IPPROTO_IPV6 = 41
multicast_address = {
AF_INET: ["224.0.1.187"],
AF_INET6: ["FF00::FD"]
}
multicast_port = 5683
addr_info = socket.getaddrinfo('', None) # get all ip
for addr in addr_info:
family = addr[0]
local_address = addr[4][0]
sock = socket.socket(family, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((local_address, multicast_port))
if family == AF_INET:
for multicast_group in multicast_address[family]:
sock.setsockopt(
socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(multicast_group) + socket.inet_aton(local_address)
)
elif family == AF_INET6:
for multicast_group in multicast_address[family]:
ipv6mr_interface = struct.pack('i', addr[4][3])
ipv6_mreq = socket.inet_pton(socket.AF_INET6, multicast_group) + ipv6mr_interface
sock.setsockopt(
socket.IPPROTO_IPV6,
socket.IPV6_JOIN_GROUP,
ipv6_mreq
)
# _transport, _protocol = await loop.create_datagram_endpoint(
# lambda: protocol_factory(), sock=sock)

Why does pcap_sendpacket fail on Thunderbolt interface?

In a multi platform project I am using pcap to get a list of all network interfaces, open each (user cannot select which interfaces to use) and send/receive packets (Ethernet type 0x88e1/HomePlugAV) on each. This works fine on Windows and on Mac OS X, but sometimes on Mac OS X pcap_sendpacket fails after some time on the interface that networksetup -listallhardwareports lists as "Hardware Port: Thunderbolt 1". The error is:
send: No buffer space available
When the program is run after the machine was booted, then it takes some time until the error occurs. When the error occurred once and I stop my program, the error occurs immediately when I restart my program without rebooting the machine.
ifconfig -v en9:
en9: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500 index 8
eflags=80<TXSTART>
options=60<TSO4,TSO6>
ether b2:00:1e:94:9b:c1
media: autoselect <full-duplex>
status: inactive
type: Ethernet
scheduler: QFQ
networksetup -listallhardwareports (only the relevant parts):
Hardware Port: Thunderbolt 1
Device: en9
Ethernet Address: b2:00:1e:94:9b:c1
Tests show that on OS X 10.9 the interface is not up initially, but on OS X 10.9.2 and 10.9.3 the interface is up and running after booting.
On OS X 10.9 ifconfig initially says:
en5: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500 index 8
After ifconfig en5 up the problematic behavior is the same on OS X 10.9.
Why does pcap_sendpacket fail on the Thunderbolt adapter?
How can my program detect that this is a troubling interface before opening it? I know I could open the interface and try to send one packet, but I'ld prefer to do a clean detection beforehand.
As a workaround, you can ignore the "Thunderbolt 1" interface:
#include <stdio.h>
#include <pcap/pcap.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SCNetworkConfiguration.h>
const char thunderbolt[] = "Thunderbolt 1";
// Build with -framework CoreFoundation -framework SystemConfiguration
int main(int argc, char * argv[])
{
// See: https://opensource.apple.com/source/configd/configd-596.13/SystemConfiguration.fproj/SCNetworkInterface.c
// get Ethernet, Firewire, Thunderbolt, and AirPort interfaces
CFArrayRef niArrayRef = SCNetworkInterfaceCopyAll();
// Find out the thunderbolt iface
char thunderboltInterface[4] = "";
if(niArrayRef) {
CFIndex cnt = CFArrayGetCount(niArrayRef);
for(CFIndex idx = 0; idx < cnt; ++idx) {
SCNetworkInterfaceRef tSCNetworkInterfaceRef = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(niArrayRef, idx);
if(tSCNetworkInterfaceRef) {
CFStringRef BSDName = SCNetworkInterfaceGetBSDName(tSCNetworkInterfaceRef);
const char * interfaceName = (BSDName == NULL) ? "none" : CFStringGetCStringPtr(BSDName, kCFStringEncodingUTF8);
CFStringRef localizedDisplayName = SCNetworkInterfaceGetLocalizedDisplayName(tSCNetworkInterfaceRef);
const char * interfaceType = (localizedDisplayName == NULL) ? "none" : CFStringGetCStringPtr(localizedDisplayName, kCFStringEncodingUTF8);
printf("%s : %s\n", interfaceName, interfaceType);
if(strcmp(interfaceType, thunderbolt) == 0) {
// Make a copy this time
CFStringGetCString(BSDName, thunderboltInterface, sizeof(thunderboltInterface), kCFStringEncodingUTF8);
}
}
}
}
printf("%s => %s\n", thunderbolt, thunderboltInterface);
CFRelease(niArrayRef);
return 0;
}
I'm guessing from
When the program is run after the machine was booted, then it takes some time until the error occurs. When the error occurred once and I stop my program, the error occurs immediately when I restart my program without rebooting the machine.
that what's probably happening here is that the interface isn't active, so packets given to it to send aren't transmitted (and the mbuf(s) for them freed), and aren't discarded, but are, instead, just left in the interface's queue to be transmitted. Eventually either the queue fills up or an attempt to allocate some resource for the packet fails, and the interface's driver returns an ENOBUFS error.
This is arguably an OS X bug.
From
In a multi platform project I am using pcap to get a list of all network interfaces, open each (user cannot select which interfaces to use) and send/receive packets (Ethernet type 0x88e1/HomePlugAV) on each.
I suspect you aren't sending on all interfaces; not all interfaces have a link-layer header type that has an Ethernet type field - for example, lo0 doesn't.
If you're constructing Ethernet packets, you would only want to send on interfaces with a link-layer header type (as returned by pcap_datalink()) of DLT_EN10MB ("10MB" is a historical artifact; it refers to all Ethernet types except for the old experimental 3MB Xerox Ethernet, which had a different link-layer header).
You probably also don't want to bother with interfaces that aren't "active" in some sense (some sense other than "is up"); unfortunately, there's no platform-independent API to determine that, so you're going to have to fall back on #ifdefs here. That would probably rule out interfaces where the packets would pile up unsent and eventually cause an ENOBUFS error.

Using a specific network interface for a socket in windows

Is there a reliable way in Windows, apart from changing the routing table, to force a newly created socket to use a specific network interface? I understand that bind() to the interface's IP address does not guarantee this.
(Ok second time lucky..)
FYI there's another question here perform connect() on specific network adapter along the same lines...
According to The Cable Guy
Windows XP and Windows ServerĀ® 2003
use the weak host model for sends and
receives for all IPv4 interfaces and
the strong host model for sends and
receives for all IPv6 interfaces. You
cannot configure this behavior. The
Next Generation TCP/IP stack in
Windows Vista and Windows Server 2008
supports strong host sends and
receives for both IPv4 and IPv6 by
default on all interfaces except the
Teredo tunneling interface for a
Teredo host-specific relay.
So to answer your question (properly, this time) in Windows XP and Windows Server 2003 IP4 no, but for IP6 yes. And for Windows Vista and Windows 2008 yes (except for certain circumstances).
Also from http://www.codeguru.com/forum/showthread.php?t=487139
On Windows, a call to bind() affects
card selection only incoming traffic,
not outgoing traffic. Thus, on a
client running in a multi-homed system
(i.e., more than one interface card),
it's the network stack that selects
the card to use, and it makes its
selection based solely on the
destination IP, which in turn is based
on the routing table. A call to bind()
will not affect the choice of the card
in any way.
It's got something to do with
something called a "Weak End System"
("Weak E/S") model. Vista changed to a
strong E/S model, so the issue might
not arise under Vista. But all prior
versions of Windows used the weak E/S
model.
With a weak E/S model, it's the
routing table that decides which card
is used for outgoing traffic in a
multihomed system.
See if these threads offer some
insight:
"Local socket binding on multihomed
host in Windows XP does not work" at
http://www.codeguru.com/forum/showthread.php?t=452337
"How to connect a port to a specified
Networkcard?" at
http://www.codeguru.com/forum/showthread.php?t=451117.
This thread mentions the
CreateIpForwardEntry() function, which
(I think) can be used to create an
entry in the routing table so that all
outgoing IP traffic with a specified
server is routed via a specified
adapter.
"Working with 2 Ethernet cards" at
http://www.codeguru.com/forum/showthread.php?t=448863
"Strange bind behavior on multihomed
system" at
http://www.codeguru.com/forum/showthread.php?t=452368
Hope that helps!
I'm not sure why you say bind is not working reliably. Granted I have not done exhaustive testing, but the following solution worked for me (Win10, Visual Studio 2019). I needed to send a broadcast message via a particular NIC, where multiple NICs might be present on a computer. In the snippet below, I want the broadcast message to go out on the NIC with IP of .202.106.
In summary:
create a socket
create a sockaddr_in address with the IP address of the NIC you want to send FROM
bind the socket to that FROM sockaddr_in
create another sockaddr_in with the IP of your broadcast address (255.255.255.255)
do a sendto, passing the socket created is step 1, and the sockaddr of the broadcast address.
`
static WSADATA wsaData;
static int ServoSendPort = 8888;
static char ServoSendNetwork[] = "192.168.202.106";
static char ServoSendBroadcast[] = "192.168.255.255";
`
... < snip >
if ( WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR )
return false;
// Make a UDP socket
SOCKET ServoSendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int iOptVal = TRUE;
int iOptLen = sizeof(int);
int RetVal = setsockopt(ServoSendSocket, SOL_SOCKET, SO_BROADCAST, (char*)&iOptVal, iOptLen);
// Bind it to a particular interface
sockaddr_in ServoBindAddr={0};
ServoBindAddr.sin_family = AF_INET;
ServoBindAddr.sin_addr.s_addr = inet_addr( ServoSendNetwork ); // target NIC
ServoBindAddr.sin_port = htons( ServoSendPort );
int bindRetVal = bind( ServoSendSocket, (sockaddr*) &ServoBindAddr, sizeof(ServoBindAddr) );
if (bindRetVal == SOCKET_ERROR )
{
int ErrorCode = WSAGetLastError();
CString errMsg;
errMsg.Format ( _T("rats! bind() didn't work! Error code %d\n"), ErrorCode );
OutputDebugString( errMsg );
}
// now create the address to send to...
sockaddr_in ServoSendAddr={0};
ServoSendAddr.sin_family = AF_INET;
ServoSendAddr.sin_addr.s_addr = inet_addr( ServoSendBroadcast ); //
ServoSendAddr.sin_port = htons( ServoSendPort );
...
#define NUM_BYTES_SERVO_SEND 20
unsigned char sendBuf[NUM_BYTES_SERVO_SEND];
int BufLen = NUM_BYTES_SERVO_SEND;
ServoSocketStatus = sendto(ServoSendSocket, (char*)sendBuf, BufLen, 0, (SOCKADDR *) &ServoSendAddr, sizeof(ServoSendAddr));
if(ServoSocketStatus == SOCKET_ERROR)
{
ServoUdpSendBytes = WSAGetLastError();
CString message;
message.Format(_T("Error transmitting UDP message to Servo Controller: %d."), ServoSocketStatus);
OutputDebugString(message);
return false;
}

Resources