Bind a socket to a specific interface with unknown Ip - c++

I have a host with two interfaces. In my specific case I am trying to join a multicast group using boost::asio::ip::multicast::join_group which appears to only work if i use the constructor that includes the local address. However i do not know in advance the ip address of the local interface connected to the remote host that will do the multicasting. I do however know that it will be eth1. Of course, i could make it configurable but that seems like introducing a useless opportunity to misconfigure, seeing how the same address would have to be configured for the interface and my application.
Ideally there would be a glaringly obvious way to create a boost::asio::endpoint or boost::asio::address from an interface instead of an address that i somehow missed. Alternatively i would of course be just as happy with any other way to deduce an interfaces Ip that works both, with and without a DHCP-Server supplying the Ip.
Is there a proper way to do either or should i just trust users to never fumble with the configuration?
To make sure that this is not completely a XY Problem, here is the code i used while testing to join a multicast group:
m_socket.open(boost_ip::udp::v4());
m_socket.bind(boost_ip::udp::endpoint(boost_ip::udp::v4(), listeningPort));
m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
m_socket.set_option(boost::asio::ip::multicast::join_group(
boost::asio::ip::address::from_string("225.x.x.10").to_v4(), // remote
boost::asio::ip::address::from_string("192.x.x.3").to_v4())); // local
This does work but when i discard the last line to not be dependent on the current local address (that might be changed in deployment), i no longer receive any packets.

Multicast uses the IGMP protocol to form a multicast group. Since IGMP operates on the network layer, it requires the local ip address of the end-point that is joining the multicast group.
The application should wait for an event indicating an ip address has been assigned to the ethernet interface and then call the join_group method to join the multicast group.

I came up with an answer to my question myself. This works for me but I will leave the question open in case anyone has a more straightforward solution.
I settled with using <ifaddrs.h> to just find my current ip for the interface and use that to join the multicast group. This is the code i ended up with to determine my ip:
#include <ifaddrs.h>
#include <boost/asio.hpp>
#include <cstring>
std::string getInterfaceAddress(const std::string & interfaceName)
{
ifaddrs* firstNetIf = 0;
getifaddrs(&firstNetIf);
ifaddrs* netIf = 0;
for(netIf = firstNetIf; netIf != 0; netIf = netIf->ifa_next)
{
if(netIf->ifa_addr->sa_family == AF_INET && std::strncmp(netIf->ifa_name, interfaceName.c_str(), interfaceName.length()) == 0)
{
break;
}
}
unsigned long address =
netIf != 0 ? reinterpret_cast<sockaddr_in*>(netIf->ifa_addr)->sin_addr.s_addr : 0;
if(firstNetIf != 0)
{
freeifaddrs(firstNetIf);
}
return boost::asio::ip::address_v4(htonl(address)).to_string();
}
Of course in my case i could compare with "eth1" and return the boost::asio::ip::address directly but it turns out that this code can be used at one other place as well this way.

Related

Receiving Data for Multiple Hosts via Linux Sockets

I have a rather strange question. Lately, I have been tasked with developing software to simulate a large (hundreds of nodes and up) network. To make a long story short, we have a head-end server that communicates with each host through a predictable IP addressing scheme via Linux sockets using a mixture of broadcast and unicast. The head-end will issue a request to a given client and will (sometimes) receive data pertaining to the command executed. All data / commands are sent via UDP on a well-defined port.
Now, for testing purposes, we would like to use the original server binary in a virtual environment an still receive reasonable data. For example, we would like to issue a reset command to a particular node and receive a fake notification back. The broadcast bit is easy, as I simply have to listen in on the proper broadcast address and act accordingly. The unicast is what has me stuck.
The Question
Is it possible to receive UDP requests for a large number of discrete hosts via a single (or a reduced) number of Linux sockets? All hosts are on the same subnet and all IP addresses / hosts / network topology are known ahead of time.
Desired Output
Ultimately, we would like to have an app that runs on a host on the network and responds as if it were each of these discrete 'virtualized' hosts based on input datagrams.
Do note that I am not asking for someone to write me a program. I am just simply looking for some direction as to the 'vehicle' by which this can be accomplished.
Possible Solutions
RAW Sockets: This has promise as I can trap all inbound data via a
single socket and punt it off to a worker thread for processing and
response. Unfortunately, I only receive packets that are
destined for my host IP and none of the 'fake' IPs.
Abuse IP aliases on Linux, one for each host: This seems to be the most direct approach but it feels like duck hunting with a bazooka. It has the added benefit of appearing to 'be' the host for any other forms of communication, I just worry that creating 400+ aliases might be a bit much for our bastard-child of a Linux environment. As an added complication, the hosts do change based on configuration and can be in any manner of states (up, down, command processing, etc.).
The source code of the server is to be treated as immutable for the purpose of our testing. I fully expect this will be impossible with the constraints given, but someone may have an idea of how to accomplish this as, quite frankly, I have never done anything of this sort before.
Thank you in advance for any assistance.
Personally, I would use your second option - add all the IP addresses to the host, then bind to INADDR_ANY address. This would mean you could use just one socket.
An alternative is to set the IP_TRANSPARENT socket option on your socket, which will then allow your application to bind to non-local addresses (you would route the networks containing those addresses through the machine that your application is running on). This method does require one socket per address, though.
So, using a combination of both of caf's solutions, I was able to have my cake and eat it too. I was also heavily influenced by
Python/iptables: Capturing all UDP packets and their original destination
which is a Python example, but does show how I can 'cheat' the packets back to a single interface, negating the need for maintenance of many sockets. That question is well worth the read and contains a lot of good information. For compactness, though, I will restate part of it below.
Hopefully it can help someone else down the road.
Part 1 - Host Configuration
As stated in the above question, we can use a combination of iptables and ip routes to redirect the packets to loopback for processing. This was not stated in my original question, but it is acceptable for the 'simulator' to run on the head-end host itself and not be a discrete node on the network. To do this, we mark each packet via iptables and then route it to lo based on said mark.
iptables -A OUTPUT -t mangle -p udp --dport 27333 -j MARK --set-mark 1
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
In my case, I only need traffic to a certain port so my iptables rule has been adjusted accordingly from the original.
Part 2 - Software
As caf stated in his post, the real trick is to use IP_TRANSPARENT and a raw socket. Raw sockets are necessary in order to get the original source / destination IP addresses. One gotchya that took me a while was the use of IPPROTO_UDP in the call to socket(). Even though this is a raw socket, it will strip out the Ethernet header. A lot of code online shows the calculation of the IP header offset using something similar to the following:
struct iphdr* ipHeader = (struct iphdr *)(buf + sizeof(ethhdr));
Offsetting by ethhdr (which is stripped) will give you some rather entertaining garbage data. With that particular header removed, the necessary IP header is simply the first structure in the buffer.
The Test Code
Below you will find a proof-of-concept example. It is no way fully functional or complete. In particular, no checking in done on the incoming packets for malicious data (ex. format string exploits in the payload, pointer math problems, malformed / malicious packets, etc).
Note that the code binds to lo specifically. This does not mean that we will only get packets destined for one of our 'fake' hosts (other services use loobpack, too). Additional checking / filtering is required to get only the packets we want.
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string>
int main(int argc, char *argv[]) {
//Set up listening socket
struct sockaddr_in serverAddr;
struct iphdr* ipHeader;
struct udphdr* udpHeader;
int listenSock = 0;
char data[65536];
static int is_transparent = 1;
std::string device = "lo";
//Initialize listening socket
if ((listenSock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0) {
printf("Error creating socket\n");
return 1;
}
setsockopt(listenSock, SOL_IP, IP_TRANSPARENT, &is_transparent, sizeof(is_transparent));
setsockopt(listensock, SOL_SOCKET, SO_BINDTO_DEVICE, device.c_str(), device.size());
memset(&serverAddr, 0x00, sizeof(serverAddr));
memset(&data, 0x00, sizeof(data));
//Setup server address
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(27333);
//Bind and listen
if (bind(listenSock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) {
printf("Error binding socket\n");
return 1;
}
while (1) {
//Accept connection
recv(listenSock, data, 65536, 0);
//Get IP header
ipHeader = (struct iphdr*)(data);
//Only grab UDP packets (17 is the magic number for UDP protocol)
if ((unsigned int)ipHeader->protocol == 17) {
//Get UDP header information
udpHeader = (struct udphdr*)(data + (ipHeader->ihl * 4));
//DEBUG
struct sockaddr_in tempDest;
struct sockaddr_in tempSource;
char* payload = (char*)(data + ipHeader->ihl * 4) + sizeof(struct udphdr));
memset(&tempSource, 0x00, sizeof(tempSource));
memset(&tempDest, 0x00, sizeof(tempDest));
tempSource.sin_addr.s_addr = ipHeader->saddr;
tempDest.sin_addr.s_addr = ipHeader->daddr;
printf("Datagram received\n");
printf("Source IP: %s\n", inet_ntoa(tempSource.sin_addr));
printf("Dest IP : %s\n", inet_ntoa(tempDest.sin_addr));
printf("Data : %s\n", payload);
printf("Port : %d\n\n", ntohs(udpHeader->dest));
}
}
}
Further Reading
Some very helpful links are below.
http://www.binarytides.com/packet-sniffer-code-in-c-using-linux-sockets-bsd-part-2/
http://bert-hubert.blogspot.com/2012/10/on-binding-datagram-udp-sockets-to-any.html

Get socket's IP address before accepting connection C++

I need to get the IP address of a connection to see if it has already connected previously (checking against a list of ips, if it has connected previously but isnt connected anymore, it will say offline). (using nonblocking sockets)
How can I get the IP without first accepting it.
///
case FD_ACCEPT:
int W;
for(W = 0;W <= ListView_GetItemCount(GetDlgItem(HwND,IDC_IPLIST));W++){
So then im just gonna check the IP against the list view to see if it had connected before. If it has, I want to use the same socket number it was using last time.
This is how I'm accepting connections right now
case FD_ACCEPT:
while(Client[F] != NULL)
{
F++;
}
Client[F]=accept(wParam,(LPSOCKADDR)&ServAdr,&AdrLen);
break;
so to break it down...
I want to check incoming connections against an IP list of previous connections. This list will have the IP and whether its online/offline (connected/not connected). If it has connected before I want it to show Online when I accept the new connection, and use the same socket number it used last time instead of using a new one all together. If it hasn't I want it to be added to the list. (the list will have the socket number)
If this doesnt make much sense I'll try and clarify a bit more.
What you are asking for cannot be done with accept(). You do not have access to a connection's information until after it has been accepted and a new SOCKET handle allocated. To get the connection info pre-acceptance, you have to use the callback functionality of WSAAccept() instead.
Either way, there is no way to reuse an existing SOCKET handle for a new connection. Each accepted connection must have its own unique SOCKET handle. You can certainly associate the new connection from a previously-seen IP with an existing slot in your ListView, though.
If by socket number you mean the number returned by accept(), you can't rely on it's value at all. I mean, if the remote host disconnects and connects again the value returned by accept() will most probably be different. It does not make sense to rely on this number.
If by socket number you mean the position in your array, you can assign the value returned by accept() to temporary variable:
SOCKET tmpSock;
sockaddr_in tmpAddr;
int namelen;
typedef struct { /*...*/ } TClient;
TClient Client[MAX_CLIENTS];
/*...*/
tmpSock = accept(/*...*/);
namelen = sizeof(tmpAddr);
getpeername(tmpSock, (sockaddr*)&tmpAddr,&namelen);
/*...*/
//looking for tmpAddr.sin_addr in your list and calculating
//the list position - F
/*...*/
Client[F].Socket = tmpSock;
Client[F].IsConnected = true;
Client[F].Address = tmpAddr.sin_addr;
Have in mind that after the listen() call the OS kernel will accept all incoming connection to the port/local IP set by you. It means that the connect() of remote host will return successfully whether you call accept() or not (provided you have space in listen queue). Calling accept() will only allow you to interact with the socket. It will not change the connection state seen by the remote host.
I'm not sure the is possible nor an efficient specification to achieve what you want. I would either:
Accept any connection and then check the IP address, disconnecting connections which are not in the list
(This probably isn't suitable for you) Configure an upstream firewall, such that only allowed IP addresses are allowed through.
If you bind to a wildcard address (INADDR_ANY), then the IP address used for communication isn't determined until a connection comes in (it will be one from the interface the packets are passing through). The same listening socket can result in accepted connections on more than one IP address.
If you bind to a specific address, then you already know the address you bound to.

Is ->h_addr_list[0] the address I need?

I am working on implementing UpNP on C++, and I need to get the local internal IP address assigned by the router to make the sockets works. The address I need is the one that appears on routers where it shows the computers connected to the router and the local IP assigned to each computer. I am using this:
PHOSTENT Addr = NULL;
char Host[MAX_PATH];
if( gethostname(Host, sizeof(Host)) == 0 )
{
Address = gethostbyname( Host );
if( Address != NULL )
{
//*(struct in_addr *)Address->h_addr_list[0]) <- this is my address
}
}
This works fine on the computer I am testing, but that computer has only one network card, so I was wondering if maybe when a computer has more than one card or network device, Address->h_addr_list[0] may not be the one I need and it could be in another index of that array.
Will [0] always retrieve the IP assigned by the router?
(Assuming winsock here, as per previous question)
You shouldn't assume that the first address is the correct one (as there may be multiple interfaces, and more than one may be active)
I'd recommend enumerating addresses using either getaddrinfo with an empty pNodeName argument, or GetAdaptersAddresses.
Both of these return a linked lists with your system's registered addresses
... get the local internal IP address assigned by the router ...
Note that in some cases, the machine's IP address will be manually assigned, but the user will still want to use UPnP.
On Linux, it is suggested to use getaddrinfo(3) instead of gethostbyname(3), perhaps Winsocks has made a similar transition?
On Linux, it is common for /etc/hosts to have loopback entries also accessible by hostname; /etc/gai.conf can be used to configure the sort order of returned addresses, and possibly a loopback address will be returned. Does Winsock make it that easy for sysadmins to change the order of returned addresses?
Don't forget that a system may legitimately have multiple upstream routers: a laptop with an EV-DO or EDGE or similar cellular data connection and a wireless or wired Ethernet will have multiple IPs, multiple upstream routers, and the routing table will be consulted to figure out which one should be used to send each packet.
Can you use either (a) the address used by clients to contact you? (getsockname(2) will return the local address used on a specific socket.) (b) ask the user to select among the list of IP addresses, if there are several? Binding to N of M interfaces would be nice, so users can select which networks get the services and which networks are left alone.

libpcap: how to get active network interface (Mac OSX)

I am using libpcap to sniff traffic. I would like to do it on the currently active network device (e.g. one that has an assigned IP address, etc). What's the best way to do this? I'm assuming I would have to do the following:
pcap_findalldevs(&alldevs, errbuf)
to get all the network devices, then loop through and check which one is currently active.
Edit: The following function
(pcap_lookupnet(dev, &net, &mask, errbuf)
returns the network address and subnet mask for a network device. I ran some tests with the different ethernet adapters on my computer and it returns -1 when I call it on an adapter that is not connected to a network. Would this be the bulletproof way to get an active interface? Are there any edge cases it would miss?
the API pcap has for looking up interfaces conforming to some user defined rules is trivial. You could indeed use pcap_findalldevs() to interate over all suitable-to-use network devices or use pcap_lookupdev() to get the next network device that you can use with pcap. Defining what is the interface you want to use with your sniffer may be problematic (code wise) on systems with multiple network devices and you would want to define more explicit rules for choosing such an interface. Such rules are usually statically defined (like "the active interface with the default route installed"). However, you may have multiple default routes (think load balancing) and here you may either want to sniff on all of them or (for example) only on the ppp interface. So choosing the target interface I would say is a task to be resolved outside the sniffer rather than at runtime in the sniffer code.
For example:
If by "active interface" we understand the interface on which the default route is installed (i assume a linux system here):
ip route show 0.0.0.0/0 | awk ' { print $5 ; } ' | xargs ./sniffer
if you want to get the active interface which has the default route installed on from your sniffer code, you would rather use netlink(7) or proc(5) (/proc/net/route) than pcap's device lookup api but the complexity is high.
In conclusion, the interface lookup logic can be easily scripted into some wrapper program on any system and the result(s) passed as parameter(s) to your sniffer.
Why don't you capture on 'any' device (Pseudo-device that captures on all interfaces) ?
Any way, here is a little snippet that will help you find 'active' interfaces
#include <stdio.h>
#include <pcap.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static void
dump_addresses (pcap_addr_t *addresses)
{
pcap_addr_t *addr = addresses;
printf("(");
while (addr) {
struct sockaddr_in *ip = (struct sockaddr_in *)addr->addr;
struct sockaddr_in *nm = (struct sockaddr_in *)addr->netmask;
if (ip && nm)
printf("%s/%s ",
inet_ntoa(ip->sin_addr), inet_ntoa(nm->sin_addr));
addr = addr->next;
}
printf(")");
}
static void
devs_dump (pcap_if_t *devs)
{
pcap_if_t *dev = devs;
while (dev) {
printf("dev: %s - %s - ",
dev->name, dev->description);
dump_addresses(dev->addresses);
printf("\n");
dev = dev->next;
}
}
int
main(int argc, char *argv[])
{
int r;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *devs;
r = pcap_findalldevs (&devs, errbuf);
if (r) {
printf("Findalldevs: %d (%s)\n", r, errbuf);
return -1;
}
devs_dump(devs);
pcap_freealldevs (devs);
return 0;
}
I've been down this road several times before, and usually find myself falling back to adding a -i switch to allow the user to precisely identify the interface.
It makes your job simpler and avoids any edge conditions.
According to pcap_open_live (3):
DESCRIPTION
pcap_open_live() is used to obtain a packet capture handle to
look at packets on the network. device is a string that
specifies the network device to open; on Linux systems with
2.2 or later kernels, a device argument of "any" or NULL can
be used to capture packets from all interfaces.
But it seems it's deprecated now, you should use pcap_create(3)

How to get Current System IP in C under Windows XP [duplicate]

For a communication between two hosts, I need to send the IP address of my host to the other site. The problem is that if I request my IP address, it might be that I get back my local loopback IP addres (127.x.x.x) , not the network (ethernet) IP address.
I use the following code:
char myhostname[32];
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
if( (my_ip % 256) == 127) {
/* Wrong IP adress as it's 127.x.x.x */
printf("Error, local IP address!");
return;
}
The only way to solve it is to make sure my hostname in /etc/hosts is behind the real network address, not the local loopback (the default for e.g. Ubuntu).
Is there a way to solve this without relying on the content of /etc/hosts?
Edit: I changed the above code so it makes use of getaddrinfo, but I still get back the loopback device's number (127.0,0,1) instead of the real IP address:
struct addrinfo hint = {0};
struct addrinfo *aip = NULL;
unsigned ip = 0;
struct sockaddr_in *sinp = NULL;
hint.ai_family = AF_INET; /* IPv4 */
hint.ai_socktype = SOCK_STREAM;
if(getaddrinfo(hostname, NULL, &hint, &aip) != 0) {
return 0;
}
sinp = (struct sockaddr_in *) aip->ai_addr;
ip = *(unsigned *) &sinp->sin_addr;
(I used to get back a list of 3 addrinfo's with the three SOCK_STREAM,SOCK_DGRAM and SOCK_RAW, but the hint prevents that)
So my question still stands...
There is POSIX function getaddrinfo() that returns linked list of addresses for given hostname, so you just need to go through that list and find non-loopback address.
See man getaddrinfo.
Not an answer, but a relevant comment: be aware that as soon as you start sending addressing information in the content of packets, you run the risk of making your application unable to work across NAT:ing routers and/or through firewalls.
These technologies rely on the information in IP packet headers to keep track of the traffic, and if applications exchange addressing information inside packets, where they remain invisible to this inspection, they might break.
Of course, this might be totally irrelevant to your application, but I thought it worth pointing out in this context.
The originating address will be included in the packet sent... there's no need to duplicate this information. It's obtained when accepting the communication from the remote host (see beej's guide to networking, specifically the part on accept())
I just ran into a situation where when only /etc/hosts has information in it and when I used getaddrinfo to get the IP address list, it returned 127.0.0.1 each time. As it turned out, the hostname was aliased to localhost...something often easy to overlook. Here's what happened:
The /etc/hosts file:
127.0.0.1 localhost.localdomain localhost foo
::1 localhost6.localdomain6 localhost6
172.16.1.248 foo
172.16.1.249 bie
172.16.1.250 bletch
So, now, when you call getaddrinfo with host="foo", it returns 127.0.0.1 3 times. The error here, is that foo appears both on the line with "127.0.0.1" and "172.16.1.248". Once I removed foo from the line with "127.0.0.1" things worked fine.
Hope this helps someone.
Look at this:
Discovering public IP programmatically
Note that in some cases a computer can have more than one non-loopback IP address, and in that case the answers to that question tell you how to get the one that is exposed to the internet.
Even if the computer has only one physical network interface (an assumption that may or may not hold, even netbooks have two - ethernet and WLAN), VPNs can add even more IP adresses. Anyway, the host on the other side should be able to determine the IP your host used to contact it.
Use getaddrinfo()
You're almost there. I'm not sure how you're getting my_ip from hp.
gethostbyname() returns a pointer to a hostent structure which has an h_addr_list field.
The h_addr_list field is a null-terminated list of all the ip addresses bound to that host.
I think you're getting the loopback address because it's the first entry in h_addr_list.
EDIT: It should work something like this:
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
for (int i = 0; hp->h_addr_list[i] != 0; ++i) {
if (hp->h_addr_list[i] != INADDR_LOOPBACK) {
// hp->addr_list[i] is a non-loopback address
}
}
// no address found
If /etc/hosts is still there and still the same, looking for all entries of h_addr_list won't help.
Your new code hardwires the use of IPv4 (in the hint.ai_family field) which is a terrible idea.
Apart from that, you're close, you just should loop through the results of getaddrinfo. Your code just gets the first IP address but there is an aip->ai_next field to follow...
struct addrinfo {
...
struct addrinfo *ai_next; /* next structure in linked list */
};