Enumerating each IP address assigned to network interfaces - c

I think there is no way to enumerate each network interface on my system and their assigned IP address using just sockets. Is this correct?
I mean, in Linux this could be:
eth0: 192.168.1.5
wlan0: 192.168.0.5
lo: 127.0.0.1
I don't care about interface names, just the IP addresses assigned.
I recall to have done this in the past in Windows, using Win32 (though I don't remember how). But is there a method to do this in a portable way?

Here's a good start:
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <stdio.h>
void
print_sockaddr(struct sockaddr* addr,const char *name)
{
char addrbuf[128] ;
addrbuf[0] = 0;
if(addr->sa_family == AF_UNSPEC)
return;
switch(addr->sa_family) {
case AF_INET:
inet_ntop(addr->sa_family,&((struct sockaddr_in*)addr)->sin_addr,addrbuf,sizeof(addrbuf));
break;
case AF_INET6:
inet_ntop(addr->sa_family,&((struct sockaddr_in6*)addr)->sin6_addr,addrbuf,sizeof(addrbuf));
break;
default:
sprintf(addrbuf,"Unknown (%d)",(int)addr->sa_family);
break;
}
printf("%-16s %s\n",name,addrbuf);
}
void
print_ifaddr(struct ifaddrs *addr)
{
char addrbuf[128] ;
addrbuf[0] = 0;
printf("%-16s %s\n","Name",addr->ifa_name);
if(addr->ifa_addr != NULL)
print_sockaddr(addr->ifa_addr,"Address");
if(addr->ifa_netmask != NULL)
print_sockaddr(addr->ifa_netmask,"Netmask");
if(addr->ifa_broadaddr != NULL)
print_sockaddr(addr->ifa_broadaddr,"Broadcast addr.");
if(addr->ifa_dstaddr != NULL)
print_sockaddr(addr->ifa_dstaddr,"Peer addr.");
puts("");
}
int main(int argc,char *argv[])
{
struct ifaddrs *addrs,*tmp;
if(getifaddrs(&addrs) != 0) {
perror("getifaddrs");
return 1;
}
for(tmp = addrs; tmp ; tmp = tmp->ifa_next) {
print_ifaddr(tmp);
}
freeifaddrs(addrs);
return 0;
}

Use gethostname() to retreive the machine's local DNS name, and then pass that name to gethostbyname() to get its local IP addresses.
If the OS supports IPv6, then look at the getaddrinfo() function instead of gethostbyname().

Take a look at the ioctl() function. If I recall correctly, you can use it to obtain information on any interface, as well as obtaining the available interfaces.
I don't remember the correct invocation though. As with fcntl(), it takes a request argument plus variable parameters that determine its behaviour.

On Linux, that information is accessed through NETLINK sockets. See the manual pages for libnetlink(3), netlink(7) and rtnetlink(7).
A somewhat more portable way of doing this is via ioctl()'s. SIOCGIFCONF is the one you want. See the manual page for netdevice(7). This one works on some other *nixes.

Related

How to check if the IP address that a socket is bound to is still one of the valid machine's addresses?

Once I call socket(); bind() (with a specific IP address, not INADDR_ANY); listen(), there seems to be no way of determining if the IP address is still a valid address of one of the system's interfaces.
What I looked into using:
Checking error with getsockopt(SO_ERROR);
Using epoll()-ing on some EPOLLERR, EPOLL{,RD}HUP events;
Hoping that accept() would return an error if the IP address is deleted when process is blocked on this syscall;
Non of those above seem to detect the IP address vanishing and/or change of interface state at all.
Calling bind() in some timer callback to periodically check if the IP address may be bound, but this requires another socket has to be created it is not feasible.
I did not test these:
Setting SO_BINDTODEVICE in the hope that this will change behavior of the facilities from the first triple in case interface goes down/IP address is removed.
Calling some ioctl() like SIOCSPGRP or FIOASYNC since they promise to signal process about asynchronous events that hopefully include disappearance of an IP address.
Using netlink to get routing table events, but this is very Linux-specific.
I'm hopping for a more portable way.
What I needing is some event similar to RDMA_CM_EVENT_DEVICE_REMOVAL, but with AF_INET sockets that would notify me when there is no bound interface with that IP address. Even this may be impossible to fulfill, because even bind() completes without error if the interface is down.
You do not need to close a socket when the link goes down with BSD sockets.
This other questions shows how to listen for network routing changes mentioned by Remy.
A simpler way may be to check if the IP is currently bound before using the socket. This is a way to verify that 127.0.0.1 is still valid:
% cat checksocket.c && cc -o checksocket checksocket.c ; ./checksocket
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <net/if.h>
int main (void)
{
struct ifaddrs *ifa, *i;
char host[NI_MAXHOST];
int ret = getifaddrs(&ifa);
if (ret) {
perror("getifaddrs:");
exit(EXIT_FAILURE);
}
for (i = ifa; i != NULL; i = i->ifa_next) {
ret = getnameinfo(i->ifa_addr, sizeof(*(i->ifa_addr)),
host, sizeof(host), NULL, 0, NI_NUMERICHOST);
char find[] = "127.0.0.1";
int len = strlen(host);
if (!ret && len && !strncmp(host, find, len)) {
printf("Still valid: %s %s\n", i->ifa_name, ret ? "" : host);
}
}
freeifaddrs(ifa);
return 0;
}
Still valid: lo0 127.0.0.1

How gethostbyname() or getnameinfo() work in background?

How gethostbyname() or getnameinfo() work in background?
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
/* paddr: print the IP address in a standard decimal dotted format */
void
paddr(unsigned char *a)
{
printf("%d.%d.%d.%d\n", a[0], a[1], a[2], a[3]);
}
main(int argc, char **argv) {
struct hostent *hp;
char *host = "google.com";
int i;
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "could not obtain address of %s\n", host);
return 0;
}
for (i=0; hp->h_addr_list[i] != 0; i++)
paddr((unsigned char*) hp->h_addr_list[i]);
exit(0);
}
output for google.com:
74.125.236.198
74.125.236.199
74.125.236.206
74.125.236.201
74.125.236.200
74.125.236.196
74.125.236.193
74.125.236.197
74.125.236.194
74.125.236.195
74.125.236.192
output for www.google.com:
74.125.236.210
74.125.236.209
74.125.236.212
74.125.236.208
74.125.236.211
Will the above program do a check in the internet to resolve into IP?
Why its showing less IP addresses for www.google.com and more for just google.com?
On a Linux system the gethostbyname() call implemented in the glibc performs lookups according to the configuration files /etc/host.conf and /etc/nsswitch.conf.
Typically in a default configuration it will first look in the /etc/hosts file if a local entry for the given name exists and if so, returns that. Otherwise it will proceed with the DNS protocol that is in turn configured by /etc/resolv.conf where the nameservers are stated.
Much more complex setups can be configured that lookup LDAP servers, databases etc.
You can also look into some man pages like man 5 nsswitch.conf.

Similar to getifaddrs on old glibc version

There is very useful funcion call getifaddrs which retrieves all machin network addresses. The problem is that I'm using old glibc version which doesn't have this function. Is there any replacement for it? I was looking and found getipnodebyname but it is unuseful when address isn't mapped in /etc/hosts file.
To add to the previous answer, here is an example for the SIOCGIFCONF-approach. You have to do something like this:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
int fd;
int get_iface_list(struct ifconf *ifconf)
{
int rval;
if((rval = ioctl(fd, SIOCGIFCONF , (char*) ifconf )) < 0 )
perror("ioctl(SIOGIFCONF)");
return rval;
}
int main()
{
static struct ifreq ifreqs[100];
static struct ifconf ifc;
char *ptr;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return 1;
ifc.ifc_buf = (char*) (ifreqs);
ifc.ifc_len = sizeof(ifreqs);
if(get_iface_list(&ifc) < 0) return -1;
/* Go through the list of interfaces */
for (ptr = ifc.ifc_buf; ptr < ifc.ifc_buf + ifc.ifc_len;)
{
struct ifreq *ifr = (struct ifreq*)ptr;
int len = (sizeof(struct sockaddr) > ifr->ifr_addr.sa_len) ?
sizeof(struct sockaddr) : ifr->ifr_addr.sa_len;
ptr += sizeof(ifr->ifr_name) + len;
/* Do what you need with the ifr-structure.
* ifr->ifr_addr contains either sockaddr_dl,
* sockaddr_in or sockaddr_in6 depending on
* what addresses and L2 protocols the interface
* has associated in it.
*/
}
close(fd);
return 0;
}
There are some gotchas, of course. According to Unix Network Programming chapter 17.6 ioctl(fd, SIOCGIFCONF, array) may not return an error on some platforms if the array pointed in the argument is too small. The data will then be concatenated. Only way to work around this is to call ioctl() in a loop until you get same result length twice while increasing the size of the array. Of course, since this is 2012, I'm not sure how relevant this is anymore.
Size of ifreqs array is purely a guess in this case. Keep in mind though that the array will contain one struct ifreq for every L2 and L3 address associated with a interface. For example, assuming you have also IPv6 addresses, for lo-interface you'd get three entries: ethernet, IPv4 and IPv6. Therefore reserve enough space or apply the kludge.
To get broadcast addresses and other additional information, you will need to additional ioctl() calls in the loop. All possible options depends on what your OS provides, of course.
For more information I'd recommend reading Unix Network Programming by W. Richard Stevens. It is the most comprehesive book about this subject.
The traditional way to do the equivalent was with the SIOCGIFCONF operation to ioctl. Any socket can be used for the operation. It's not as easy as a single function call though.

get LAN ip and print it

Does anyone know how do I get my LAN IP and print it on the screen.
*I don't mean on shell, but in c programming.
**I will appreciate if you'll post me a sample code.
There's a few approaches; first, you could set up a connection to a known peer using connect(2) and then read the local socket 'name' with getsockname(2). This is a pretty poor mechanism, but it is easy.
But, getsockname(2) will only report one IP address, when a machine may have thousands of IP addresses, and which IP is returned will depend in part upon the peer that you chose! So, not very reliable.
A much better answer is to use rtnetlink(7) to read the IP addresses for the machine directly from the kernel: you would send RTM_GETADDR messages to the kernel for each interface on the machine and read the answers back. Your best bet for understanding how to use this is probably to read the source code for the ip program.
Another option is to use the SIOCGIFCONF ioctl(2) on an IP socket. This interface isn't as flexible as the rtnetlink(7) interface, so it might not always be correct, but it'll be a mid-way point. The ifconfig(8) utility uses this approach for displaying ifconfig -a output. Again, your best bet would be to read the source. (There is some slight documentation in ioctl_list(2).)
The getifaddrs() function from <ifaddrs.h> is the simplest way to get the current interfaces and corresponding addresses:
#include <stdio.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct ifaddrs *iflist, *iface;
if (getifaddrs(&iflist) < 0) {
perror("getifaddrs");
return 1;
}
for (iface = iflist; iface; iface = iface->ifa_next) {
int af = iface->ifa_addr->sa_family;
const void *addr;
char addrp[INET6_ADDRSTRLEN];
switch (af) {
case AF_INET:
addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr;
break;
case AF_INET6:
addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr;
break;
default:
addr = NULL;
}
if (addr) {
if (inet_ntop(af, addr, addrp, sizeof addrp) == NULL) {
perror("inet_ntop");
continue;
}
printf("Interface %s has address %s\n", iface->ifa_name, addrp);
}
}
freeifaddrs(iflist);
return 0;
}
gethostbyname should help you to do this. Example:
GetLocalAddress()
{
char ac[80];
// Get my host name
if (gethostname(ac, sizeof(ac)) != -1)
{
printf("Host name: %s\n", ac);
// Find IP addresses
struct hostent* p_he = gethostbyname(ac);
if (p_he != 0)
{
for (int i=0; p_he->h_addr_list[i] != 0; ++i)
{
struct in_addr addr;
memcpy(&addr, p_he->h_addr_list[i], sizeof(struct in_addr));
printf("IP address %d: %s\n", i, inet_ntoa(addr));
}
}
}
You might want to filter to remove 127.0.0.1 from the list.

How to check Ethernet in Linux?

I need the test case for Ethernet in Linux using C code to check eth0. If eth0 is down, we enable the net then check if up and the test is passed.
To check if the link is up, try something like this. It works without root privileges.
#include <stdio.h> // printf
#include <string.h> // strncpy
//#include <sys/socket.h> // AF_INET
#include <sys/ioctl.h> // SIOCGIFFLAGS
#include <errno.h> // errno
#include <netinet/in.h> // IPPROTO_IP
#include <net/if.h> // IFF_*, ifreq
#define ERROR(fmt, ...) do { printf(fmt, __VA_ARGS__); return -1; } while(0)
int CheckLink(char *ifname) {
int state = -1;
int socId = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (socId < 0) ERROR("Socket failed. Errno = %d\n", errno);
struct ifreq if_req;
(void) strncpy(if_req.ifr_name, ifname, sizeof(if_req.ifr_name));
int rv = ioctl(socId, SIOCGIFFLAGS, &if_req);
close(socId);
if ( rv == -1) ERROR("Ioctl failed. Errno = %d\n", errno);
return (if_req.ifr_flags & IFF_UP) && (if_req.ifr_flags & IFF_RUNNING);
}
int main() {
printf("%d\n", CheckLink("eth0"));
}
If IFF_UP is set, it means interface is up (see ifup). If IFF_RUNNING is set then interface is plugged.
I also tried using the ethtool ioctl call, but it failed when the gid was not root. But just for the log:
...
#include <asm/types.h> // __u32
#include <linux/ethtool.h> // ETHTOOL_GLINK
#include <linux/sockios.h> // SIOCETHTOOL
...
int CheckLink(char *ifname) {
...
struct ifreq if_req;
(void) strncpy( if_req.ifr_name, ifname, sizeof(if_req.ifr_name) );
struct ethtool_value edata;
edata.cmd = ETHTOOL_GLINK;
if_req.ifr_data = (char*) &edata;
int rv = ioctl(socId, SIOCETHTOOL, &if_req);
...
return !!edata.data;
}
The network interfaces can be seen in sysfs: /sys/class/net/eth[x]. There you can check the link, interface status, and more.
You may want to take advantage of libudev to get around in /sys:
http://www.signal11.us/oss/udev/
I simply check if an Ip address is assigned to the network card.
You can use something like this to check if lan is up in the given network card (say eth0) :
/sbin/ifconfig eth0| grep 'inet addr:' | wc -l
This should simply return 0 or 1 based on whether or not an ip address is assigned to the nic.
Also you may use Yann Ramin's method to list out all the nic's & perform the check.
I missed out noticing you are looking for a c code. Maybe adding a tag would be good.
Either ways, I think you can look at the same file (ifconfig) manually in c by reading it for an ip.
As this question is somewhat important, I'll add another answer despite its extreme age. You can read the contents of /sys/class/net/eth0/operstate which will simply contain the string "up\n" or "down\n".

Resources