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

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)
}

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

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);
}

What is sin6_scope_id for the IPv6 loopback address?

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);

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!

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