Finding MAC address from IP address [duplicate] - c++

This question already has answers here:
Getting the MAC address of the remote host
(5 answers)
Closed 4 years ago.
I'm working on a module which has a client-server architecture. And I have to authenticate the connecting peer machine on the basis of the MAC address.
At the time of installation I store an encrypted list of valid MAC addresses.
Please note that I have no control on this peer machine and some third-party application will be running on this and I can not install any application on this peer machine.
At run time when a socket connection happens I need to know the MAC address for the IP address used in the connection, so that I can validate it against the previously stored MAC address.
Are there any C/C++ APIs available which can help me to get the MAC address from the IP address. This machine is usually to be connected in a LAN, but it can be on different subnets.
My module is multiplatform and runs on Windows, Solaris and Linux and similarly the peer machine can be running on any OS.

Unfortunately, the simple answer is really "don't do that".
If your peer machines are on a different subnet (and therefore traffic between you and the peer is passing through a router), then there's no way to do what you want -- the MAC address of the packets you receive will be the MAC address of the router which forwarded them to you, as that's the link-layer device you're communicating with. Only the router nearest to the peer machine will know what the MAC address of the peer is -- and this information isn't passed on.
If the peer machines are on the same subnet, then the incoming packets will contain the MAC address of the peer in the Ethernet header... but this gets stripped off before the packet is delivered to your application. Your options are pretty much limited to either packet-capturing on the network interface you're listening on to get the entire packet, or using whatever tools your system provides to check the local ARP table. Both of these options are very platform-dependent!
Also, neither option is likely to continue working without modification if the interfaces you're dealing with aren't Ethernet interfaces (perhaps they're PPP links, or WiFi, or a funky virtualized interface of some sort, ...), nor will they work with IPv6.
If after all that, you're still determined, look into libpcap/WinPCap for packet capturing, which is the most portable option which comes to mind. To check the local ARP tables, Linux, OS X, Solaris and Windows all provide an arp command-line utility, but the syntax is likely to vary. If an API is available, I wouldn't expect any commonality between the platforms -- there's no standard for this sort of thing, because you really shouldn't be doing it!

This is impossible to do. There is no guarantee that your connecting peer even has a MAC address. It is entirely possible that the peer connects to the network via dial-up (PPP) or some other non-Ethernet interface.

Do not authenticate by MAC address. It is easy to spoof them. Use an HTTP authentication library like Chilkat.

As mcandre mentioned, it's incredibly easy to spoof MAC addresses, but to still answer your question, I think you can do this on all OS's through BSD-style sockets if you're on the same LAN:
Here's some example code from this forum post
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]) {
struct ifreq ifr;
int sock, j, k;
char *p, addr[32], mask[32], mac[32];
if (argc<2) {
fprintf(stderr,"missing argument, example: eth0\n");
return 1;
}
sock=socket(PF_INET, SOCK_STREAM, 0);
if (-1==sock) {
perror("socket() ");
return 1;
}
strncpy(ifr.ifr_name,argv[1],sizeof(ifr.ifr_name)-1);
ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0';
if (-1==ioctl(sock, SIOCGIFADDR, &ifr)) {
perror("ioctl(SIOCGIFADDR) ");
return 1;
}
p=inet_ntoa(((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr);
strncpy(addr,p,sizeof(addr)-1);
addr[sizeof(addr)-1]='\0';
if (-1==ioctl(sock, SIOCGIFNETMASK, &ifr)) {
perror("ioctl(SIOCGIFNETMASK) ");
return 1;
}
p=inet_ntoa(((struct sockaddr_in *)(&ifr.ifr_netmask))->sin_addr);
strncpy(mask,p,sizeof(mask)-1);
mask[sizeof(mask)-1]='\0';
if (-1==ioctl(sock, SIOCGIFHWADDR, &ifr)) {
perror("ioctl(SIOCGIFHWADDR) ");
return 1;
}
for (j=0, k=0; j<6; j++) {
k+=snprintf(mac+k, sizeof(mac)-k-1, j ? ":%02X" : "%02X",
(int)(unsigned int)(unsigned char)ifr.ifr_hwaddr.sa_data[j]);
}
mac[sizeof(mac)-1]='\0';
printf("\n");
printf("name: %s\n",ifr.ifr_name);
printf("address: %s\n",addr);
printf("netmask: %s\n",mask);
printf("macaddr: %s\n",mac);
printf("\n");
close(sock);
return 0;
}

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

Sending files between computers with C++

I am trying to write a program that sends basic text files from one computer to another using C++ over the internet (or a LAN). I was looking into Winsock but everything that I read made it seem like it was only for communication between a server and a client. I am not sure if this is what I am trying to do or if I am looking at a different problem.
EDIT: Thanks for the great answers guys. I kind of feel bad for having to choose a best one out of the lot.
A client-server architecture should be fine for sending files. The "server" is simply the program that starts first, and waits for the other (the client) to connect to it. Past there, there's not a lot of difference between the two. In a lot of cases, it's easiest to write the code so it automatically attempts to contact a server, and if it can't find one, it sets itself up as the server.
Also note that making things work across the internet with (particularly) NAT routers involved can make the code a little trickier. It's not all that tough if you pre-configure the firewall to allow the connection(s), but otherwise you typically end up using UPnP to establish the connection through the firewall. That can easily double the work compared to doing the job locally without NAT involved.
Here is an example of how to copy/transfer files using C/C++ in Linux.The server listens in port 8080. The client transmits a filename. The server receives the filename, open the file and sends its contents to the client. I intentionally used odd or small buffer sizes for illustrative purposes.
The server.c file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
int main (int argc,char *argv[])
{
struct sockaddr_in p;
struct sockaddr_in q;
int z,s,t,x,n,b = sizeof(q);
char buf[23];
int fd;
s = socket(AF_INET,SOCK_STREAM,0);
bzero (&p,sizeof(p));
p.sin_family = AF_INET;
p.sin_addr.s_addr = INADDR_ANY;
p.sin_port = htons(8080);
bind (s,(struct sockaddr*)&p,sizeof(p));
listen(s,13);
while (1) {
t = accept (s,(struct sockaddr*)&q, &b);
n = read(t,buf,23);
buf[n] = 0;
printf("%s sent: ",buf);
fd = open (buf,O_RDONLY);
z = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
x = 0;
do {
n = read (fd,buf,23);
write (t,buf,n);
x += n;
} while (x < z);
printf("%d/%d\n",x,z);
close(fd);
close(t);
}
}
The client.c file:
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
void main (int argc, char *argv[])
{
int ret;
char buf[5];
int s,n,fd;
struct sockaddr_in p;
struct hostent *h;
char *str = argv[1];
fd = open (str,O_CREAT | O_WRONLY,S_IRUSR|S_IWUSR);
h = gethostbyname("127.0.0.1");
bzero(&p,sizeof(p));
p.sin_family = AF_INET;
p.sin_addr.s_addr = htonl(INADDR_ANY);
p.sin_addr.s_addr = ((struct in_addr*)(h->h_addr))->s_addr;
p.sin_port = htons(8080);
s = socket (AF_INET,SOCK_STREAM,0);
connect (s,(void*)&p,sizeof(p));
n = write (s,str,strlen(str));
int idx = 0;
do {
n = read (s,buf,5);
idx += n;
printf(".");
fflush(stdout);
write (fd,buf,n);
} while (n > 0);
close (fd);
close (s);
}
To compile:
Open terminal
gcc -o server server.c
gcc -o client client.c
Run the server as ./server. And then to copy rngoidiot.txt, run the client as ./client rngoidiot.txt and the file will be copied using TCP/IP. Simple combination ;-)
In short: Yes, sockets are the way to go.
"Server" and "client" in the sockets sense are generic terms - your system is running a variety of both at any given time. For example, your web browser operates as an HTTP client (where HTTP is a text-bounded mostly-synchronous protocol running over TCP/IP); Windows file sharing and remote desktop are "servers" that other systems connect into.
The distinction between server and client in the sockets sense (really in the TCP/IP sense) is that a "server" socket listens for incoming connections from remote systems (refer to MSDN or man pages on bind(), listen(), accept()) whereas a "client" socket creates outgoing connections to remote systems (connect()).
Beware that sockets programming can be a bit tricky. What you describe is a good learning exercise, but if you're trying to get something done quickly, you might look at existing tools such as netcat.
it seem like it was only for communication between a server and a client
So what? Make one computer (that is about to receive the file) the server. On that computer create a socket and make it listen for a connection on a port. Once a connection arrives, get the data according so some defined structure (see below)
The sender computer (the client, sort of) will connect to the other computer, and it knows the IP address and the port no. (The two must be known to the client for the connection). Once connection is made, it will send the data as a sequence of bytes to the server.
The structure:
The two computers that are communicating here must have a well defined structure that is known and accepted by both. The simplest structure will be:
|Length-Of-Data | Data ...... |
<-- 4 bytes---> <-- n bytes-->
Read the n from the first 4 bytes. The 4 bytes (n) here represent the length of the data and could be different, its your definition.
You could have many more 'fields' (depending on the functionality) for filename, file-type etc..
If you want to send files over internet, you have to use sockets, or better yet other high level library implemented with sockets.
In the TCP sockets terminology, there is no big difference between client and server: the communications is two way. The only difference is in who is listening for incoming connections (bind, listen, accept), and who is initiating the connection (open).
You mentioned Winsock, it means that you are working on Windows. Take a look into the Windows API, probably you can find some high level library that handles the file transfer (like a FTP or HTTP library).
Maybe doesn't fit your requirements, but you can also use a shared folder for a LAN and a folder synchronized by Dropbox for the Internet... then just use standard IO.
Speaking generally, any file transfer requires some sort of server/client connection. One computer needs to initiate a connection, and the other computer needs to be listening for the connection at the other end, and then take the actions it is instructed to by the first computer.
While you can certainly roll out your own client and server software using sockets, depending on what sorts of computers you want to transfer files between, it might make the most sense to leverage some existing client/server protocol. Some of the more popular possibilities include ssh, http, or ftp. I think C/C++ libraries should exist for all of these protocols.
Without more information I would be inclined to use ssh. Installing an ssh server shouldn't be too hard, so you'll only have to write the client software, and I think some pretty good open source libraries exist for that.
EDIT: libssh looks pretty good!

Poco C++ 1.4.3p1's DatagramSocket ReceiveBytes() never returns. Am I misusing the function?

I have just started using the Poco library. I am having issues getting two computers to communicate using Poco's DatagramSocket objects. Specifically, the receiveBytes function does not seem to return (despite running Wireshark and seeing that the UDP packets I am sending ARE arriving at the destination machine). I assume I am omitting something simple and this is all due to a dumb mistake on my part. I have compiled Poco 1.4.3p1 on Windows 7 using Visual Studio Express 2010. Below are code snippets showing how I am trying to use Poco. Any advise would be appreciated.
Sending
#include "Poco\Net\DatagramSocket.h"
#include "Serializer.h" //A library used for serializing data
int main()
{
Poco::Net::SocketAddress remoteAddr("192.168.1.140", 5678); //The IP address of the remote (receiving) machine
Poco::Net::DatagramSocket mSock; //We make our socket (its not connected currently)
mSock.connect(remoteAddr); //Sends/Receives are restricted to the inputted IPAddress and port
unsigned char float_bytes[4];
FloatToBin(1234.5678, float_bytes); //Serializing the float and storing it in float_bytes
mSock.sendBytes((void*)float_bytes, 4); //Bytes AWAY!
return 0;
}
Receiving (where I am having issues)
#include "Poco\Net\DatagramSocket.h"
#include "Poco\Net\SocketAddress.h"
#include "Serializer.h"
#include <iostream>
int main()
{
Poco::Net::SocketAddress remoteAddr("192.168.1.116", 5678); //The IP address of the remote (sending) machine
Poco::Net::DatagramSocket mSock; //We make our socket (its not connected currently)
mSock.connect(remoteAddr); //Sends/Receives are restricted to the inputted IPAddress and port
//Now lets try to get some datas
std::cout << "Waiting for float" << std::endl;
unsigned char float_bytes[4];
mSock.receiveBytes((void*)float_bytes, 4); //The code is stuck here waiting for a packet. It never returns...
//Finally, lets convert it to a float and print to the screen
float net_float;
BinToFloat(float_bytes, &net_float); //Converting the binary data to a float and storing it in net_float
std::cout << net_float << std::endl;
system("PAUSE");
return 0;
}
Thank you for your time.
The POCO sockets are modeled on the Berkeley sockets. You should read a basic tutorial on the Berkeley socket API, this will make it easier to understand the POCO OOP socket abstractions.
You cannot connect() on both client and server. You connect() on the client only. With UDP, connect() is optional, and can be skipped (then you have to use sendTo() instead of SendBytes()).
On the server, either you bind() on the wildcard IP address (meaning: will then receive on all the available network interfaces on the host), or to a specific IP address (meaning: will then receive only on that IP address).
Looking at your receiver/server code, it seems you want to filter on the address of the remote client. You cannot do it with connect(), you have to read with receiveFrom(buffer, length, address) and then filter yourself on "address".
Security-wise, be careful with the assumptions you make with the source address of the UDP packets you receive. Spoofing a UDP packet is trivial. Said in another way: do not make authentication or authorization decisions based on an IP address (or on anything not secured by proper cryptography).
The POCO presentation http://pocoproject.org/slides/200-Network.pdf explains, with code snippets, how to do network programming with POCO. See slides 15, 16 for DatagramSocket. Note that on slide 15 there is a typo, replace msg.data(), msg.size() with syslogMsg.data(), syslogMsg.size() to compile :-)
Have a look also at the "poco/net/samples" directory for short examples that show also the best practices in using POCO.

How to know if a TCP connection is between two processes on the same machine?

Using socket programming APIs (e.g., socket(), connect(), accept() ...), how can I know if a TCP connection is between two processes on the same machine? Say, I have the socket file descriptor, and the remote ip. Can I simply inspect if the remote ip is 127.0.0.1?
There's no really reliable way to determine this - you can connect to local processes using a globally routed IP address (ie, local processes can use IPs other than 127.0.0.1). It's also possible for a process to run in a different virtual machine on the same physical hardware, if you're in a virtualized environment.
Note, however, that if the remote IP (via getpeername) or local IP (via getsockname) starts with 127 (including 127.0.0.1), then it is indeed a local connection; however, you can't rule out the possibility that it might be a local connection if it's a different pair of addresses.
Use getsockname() and getpeername() to retreive the two IPs associated with the connection, then use gethostname() and gethostbyname() (or other platform-specific APIs, like GetAdaptersInfo() and GetAdapterAddresses() on Windows) to determine the IPs that belong to the local machine, then you can compare the connection IPs to the local machine IPs to see if they both match. A machine can have multiple IPs assigned to it, and multiple IPs on the same machine can communicate with each other.
Here is the approach I have used. The idea is to attempt to bind a listener to that IP address and use the failure/success codes to decide whether the address is local.
I am not claiming this is particularly efficient, but it should be fairly reliable, and for my application it was appropriate.
#include <sys/socket.h>
#include <errno.h>
/* ...probably need some other headers I am forgetting... */
int
is_local(const struct sockaddr *addr, socklen_t addr_len)
{
const char *func = "is_local()";
int result = 0;
int tmp = socket(addr->sa_family, SOCK_STREAM, 0);
if (tmp < 0) {
printf("%s: socket(%d,SOCK_STREAM,0) failed, errno %d\n",
func, addr->sa_family);
goto out;
}
/* If bind() succeeds, or if it fails because the address is in
use, then the address must be local to this system. */
if (bind(tmp, addr, addr_len) < 0) {
if (errno == EADDRINUSE)
result = 1;
else if (errno == EADDRNOTAVAIL)
; /* do nothing; address is remote */
else
printf("%s: bind() unexpected error %d\n", func, errno);
}
else {
result = 1;
}
close(tmp);
out:
return result;
}
You call it like this:
struct sockaddr_storage client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int fd = accept(listener, &client_addr, &client_addr_len);
if (is_local((struct sockaddr *)&client_addr, client_addr_len))
/* peer is local */
If you already have the remote ip address, you can check if it is the loopback address or if it is the ip address of the host, because, as cnicutar points out, it doesn't have to be over the loopback address to be a local connection.

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)