I'm writing a program in C and it needs to look up the IP & MAC address of an interface. I'm using IOCTL calls. Until recently I was still using custom data structure to store theses addresses, and everything was working perfectly.
Then I moved to using standards struct like
struct in_addr
struct ether_addr.
On my OS (Archlinux), it is still working properly,so that's good. But usually I run my program into a virtualized environment (Slitaz linux) in VirtualBox.I do this so I can run a virtualized network with GNS3. Since these modifications, impossible to get the IOCTL call working properly in Slitaz. When I call
printf("%s\n",strerror(errno));
I just get
No Such Device
If it wasn't working properly on both architecture, I would search more in-depth, but here i'm completely lost, it works fine on Arch but not on Slitaz. it WORKED in Slitaz BEFORE these change, I can still use the older version ( 2 branches in git, one for the old version and one for the current).
Thank you for your help.
Nikko
Here are the relevant part of code (I show for MAC oinyl since IP problem is the same):
/*
* Return a ifreq structure for this interface
* */
struct ifreq
get_ifreq ( const char * interface )
{
struct ifreq ifr;
size_t if_len;
if_len = strlen(interface);
if (if_len >= sizeof(ifr.ifr_name)){
fprintf(stderr,"Interface name too long to open descriptor.\nAbort.");
exit(EXIT_FAILURE);
}
strncpy(ifr.ifr_name,interface,if_len);
return ifr;
}
int
get_mac_address(const char * interface, struct ether_addr * ether) {
int fd ;
struct ifreq ifr = get_ifreq(interface);
if((fd = get_socketudp()) == -1) {
fprintf(stderr,"Unable to get mac address.\n");
return -1;
};
if(ioctl(fd,SIOCGIFHWADDR,&ifr) == -1) {
fprintf(stderr,"%s\n",strerror(fd));
fprintf(stderr,"Error while operating IOCTL (MAC resolving).\n");
close(fd);
return -1;
}
close(fd);
memcpy(ether,&ifr.ifr_hwaddr.sa_data,ETH_ALEN);
return 0;
}
And in the main.c, where I call this function :
char * interface = NULL;
/*----------------------------------------------------------------------
* Our OWN mac address & ip address
*-----------------------------------------------------------------------*/
struct ether_addr mac;
struct in_addr ip;
int
main ( int argc, char *argv[] )
{
char * operation = NULL;
char * hostA = NULL;
char * hostB = NULL;
int c = 0;
if (argc < 2) {
usage();
exit(EXIT_FAILURE);
}
while((c = getopt(argc,argv,"m:i:a:b:f:l:")) != -1){
switch(c){
case 'm':
operation = optarg;
if (strncmp(operation,"mitm",4) != 0 &&
strncmp(operation,"flood",5) != 0) {
fprintf(stderr,"Operation %s is unknown.Abort\n",operation);
abort();
}
break;
case 'i':
interface = optarg;
break;
case '?':
fprintf(stderr,"Option %c requires an argument",optopt);
abort();
}
}
/* Check options consistency */
if(operation == NULL) {
fprintf(stderr,"No Operations given. Abort.\n");
exit(EXIT_FAILURE);
} else if (interface == NULL) {
fprintf(stderr,"No interface given. Abort.\n");
exit(EXIT_FAILURE);
}
/* Store our own mac address */
if (get_mac_address(interface,mac) == -1) {
fprintf(stderr,"Abort.\n");
exit(EXIT_FAILURE);
}
SOLUTION
Thanks to the anser I changed my get_ifreq method to :
struct ifreq
get_ifreq ( const char * interface )
{
struct ifreq ifr;
size_t if_len;
memset(ifr.ifr_name,0x00,IFNAMSIZ);
if_len = strlen(interface);
if (if_len >= IFNAMSIZ){
fprintf(stderr,"Interface name too long to open descriptor.\nAbort.");
exit(EXIT_FAILURE);
}
strncpy(ifr.ifr_name,interface,if_len);
return ifr;
}
It seems likely that there is additional garbage in the ifreq structure that you're not clearing out here:
struct ifreq ifr;
if_len = strlen(interface);
strncpy(ifr.ifr_name,interface,if_len);
You declare the struct ifreq on the stack but don't initialize it so the bytes in that structure are potentially random garbage. You then copy exactly if_len bytes into it, but what about the bytes immediately following that. Assuming if_len is less than IFNAMSIZ, how will the kernel know to stop at if_len in interpreting the interface name?
I would clear the structure prior to the strncpy.
Related
I need to obtain the real_dev (f.e. ID) of the given VLAN-inteface.
I wrote some test snippet using libnl:
int main(void) {
struct nl_sock *sock;
struct nl_cache *cache;
char iface[] = "eno1.10";
//char iface[] = "eno1";
if (!(sock = nl_socket_alloc())) {
perror("nl_socket_alloc");
return -1;
}
if (nl_connect(sock, NETLINK_ROUTE) < 0) {
perror("nl_connect");
nl_socket_free( sock );
return -1;
}
if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache) < 0) {
perror("rtnl_link_alloc_cache");
nl_socket_free( sock );
nl_close( sock );
return -1;
}
{
int ifindex;
struct rtnl_link *link = NULL;
if (!(ifindex = rtnl_link_name2i(cache, iface))) {
perror("rtnl_link_name2i");
return -1;
}
printf("ind: %d\n", ifindex);
if (!(link = rtnl_link_get(cache, ifindex))) {
perror("rtnl_link_get");
return -1;
}
if (rtnl_link_is_vlan(link)) {
puts("It's VLAN link");
/* alas it's not about the 'real' device */
printf("master: %d\n", rtnl_link_get_master(link));
} else
puts("It's 'real' link");
}
return 0;
}
So I have some interface ID and I can check if it's a VLAN-interface, but I have no idea how to obtain the interface the vlan is attached to.
It seems that libnl's API does not provide such possibility.
Is there a way to obtain the VLAN's "parent" interface ID through the libnl or the native netlink API?
It's all about IFLA_LINK:
/* IFLA_LINK.
For usual devices it is equal ifi_index.
If it is a "virtual interface" (f.e. tunnel), ifi_link
can point to real physical interface (f.e. for bandwidth calculations),
or maybe 0, what means, that real media is unknown (usual
for IPIP tunnels, when route to endpoint is allowed to change)
*/
Thus through the native netlink API it could be done such a way:
/* some preparation code */
struct rtattr *rta = IFLA_RTA(msg);
int len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*msg));
for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len))
if (rta->rta_type == IFLA_LINK) {
printf("Real device ID:%u\n",
*(unsigned short *)((char *) rta + NLA_HDRLEN));
break;
}
Full example on github.
I've been working from Beejs Network examples, introducing a few customizations. In particular, I'm trying to use a single structure to store the necessary information related to communications/sockets. I think I'm having trouble populating an addrinfo structure and using it with sendto for a UDP socket. Bellow is my code, which compiles fine, but it fails with the message outlined below
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// Definitions
#define COM_MSG_SIZE 1024
#define COM_HOST_SIZE 128
struct com_socket
{
char *type;
int descriptor;
struct addrinfo addr;
};
void COM_error(char *msg) {
perror(msg);
exit(0);
}
int main()
{
int status;
struct com_socket COM_client;
char addr_str[COM_HOST_SIZE];
// ---------------------------------------------
// Initialize socket
COM_client.type = "UDP";
char *hostname = "192.168.0.110";
char *port_num = "4000";
printf("Creating socket...");
if(strcmp(COM_client.type, "UDP") == 0)
{
COM_client.descriptor = socket(AF_INET, SOCK_DGRAM, 0);
}
// Error check
if(COM_client.descriptor < 0)
{
COM_error(" ERROR opening socket");
}
printf(" Success\n");
//------------------------------------------------------------------------------------------
// Define hints
struct addrinfo hints;
hints.ai_family = AF_INET; // AF_UNSPEC "unspecified" or can use IPv6 = AF_INET6, IPv4 = AF_INET
hints.ai_socktype = SOCK_DGRAM; // Socket type: SOCK_STREAM or SOCK_DGRAM or 0 = auto
hints.ai_flags = AI_CANONNAME;
hints.ai_protocol = 0; // 0 = auto
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_addrlen = 0;
hints.ai_next = NULL;
// Get the linked list of address info
struct addrinfo *host_list;
printf("Building host address list...");
status = getaddrinfo(hostname,port_num,&hints,&host_list);
// returns 0 if succeeds
if (status != 0)
{
COM_error(" ERROR getaddrinfo: %s\n");
}
printf(" Success\n");
//------------------------------------------------------------------------------------------
// Select address
int count = 1;
struct addrinfo *entry;
// Loop through each entry in the "linked list" and pull the necessary one
for (entry = host_list; entry != NULL; entry = entry->ai_next)
{
// Print the list of potential IP addresses
if( NULL == inet_ntop( AF_INET, &((struct sockaddr_in *) entry->ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) )
{
COM_error(" ERROR with inet_ntop\n");
}
printf(" Address entry %d: %s",count,addr_str);
// Update counter
count = count + 1;
// Choose which one to copy
if(strncmp(addr_str,"192.",(size_t) 4) == 0)
{
//memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
COM_client.addr = *entry;
// COM_client.addr.ai_addr = entry->ai_addr;
// COM_client.addr.ai_addrlen = entry->ai_addrlen;
// COM_client.addr.ai_canonname = entry->ai_canonname;
// COM_client.addr.ai_family = entry->ai_family;
// COM_client.addr.ai_flags = entry->ai_flags;
// COM_client.addr.ai_protocol = entry->ai_protocol;
// COM_client.addr.ai_socktype = entry->ai_socktype;
if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
{
COM_error(" ERROR with arguments to inet_ntop\n");
}
printf(" <--------- selected* (%s) \n",addr_str);
break;
}
else
{
printf("\n");
}
}
// Clean
freeaddrinfo(host_list);
//-------------------------------------------------------
char *buffer;
char msg[COM_MSG_SIZE];
strncpy(msg,"BEGIN",COM_MSG_SIZE);
printf("ENTER `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);
buffer = calloc(COM_MSG_SIZE+1, sizeof(char));
printf("AFTER calloc `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);
// Check to see if we were successful
if (buffer == NULL)
{
printf("ERROR Could not allocate required memory\n");
exit(1);
}
// Copy message to buffer
strncpy(buffer,msg,COM_MSG_SIZE);
printf("Message input: %s Message to be sent: %s\n",msg,buffer);
if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
{
COM_error(" ERROR with arguments to inet_ntop\n");
}
printf("SEND to address (%s) \n",addr_str);
// Send the buffer to the destination address
if(strcmp(COM_client.type, "UDP") == 0)
{
status = sendto(COM_client.descriptor, buffer, strlen(buffer), 0, COM_client.addr.ai_addr, COM_client.addr.ai_addrlen);
// Error check
if (status < 0)
{
COM_error("ERROR could not send message");
}
}
// Free buffer memory
free(buffer);
//---------------------------------------------------------
close(COM_client.descriptor);
return 0;
}
Here is the output showing messages from the print statements as well as the failure
Creating socket... Success
Building host address list... Success
Address entry 1: 192.168.0.110 <--------- selected* (192.168.0.110)
ENTER `COM_msg_send` address length 16
AFTER calloc `COM_msg_send` address length 16
Message input: BEGIN Message to be sent: BEGIN
L1 = 16 L2 = 16
SEND to address (0.0.0.0)
ERROR could not send message: Invalid argument
Showing SEND to address (0.0.0.0), it appears that something is wrong with the address stored in the structure COM_client. Specifically, I believe I'm having trouble with this part
//memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
COM_client.addr = *entry;
// COM_client.addr.ai_addr = entry->ai_addr;
// COM_client.addr.ai_addrlen = entry->ai_addrlen;
// COM_client.addr.ai_canonname = entry->ai_canonname;
// COM_client.addr.ai_family = entry->ai_family;
// COM_client.addr.ai_flags = entry->ai_flags;
// COM_client.addr.ai_protocol = entry->ai_protocol;
// COM_client.addr.ai_socktype = entry->ai_socktype;
As you can see, I've tried various things, all of which fail. I want to continue to use the COM_client structure approach as my intention is to make the code more modular in which I can pass the structure containing all the necessary communication information.
This line
COM_client.addr = *entry;
"tries" to copy a struct addrinfo, which it in fact does, but as it contains pointers and "only" copies the pointers' values. The memory those pointers point to had been allocated by getaddrinfo() and thus will be deallocates by the call to freeaddrinfo() leaving the pointers inside the copy dangle afterwards.
To get around this you need to perform a "deep copy" of a struct addrinfo.
This for example can be done like so:
/* Does a deep copy to where pdst point from where pscr points to.
Returns 0 on success and -1 on error. Sets errno. */
int addrinfo_copy(struct addrinfo * pdst, struct addrinfo * psrc)
{
int result = 0; /* Be optimistic. */
assert(pdst);
assert(psrc);
*pdst = *pscr; /* Copy all. Note that the pointer elements copied
need to be recreated. See below ... */
do
{
pdst->ai_addr = malloc(psrc->ai_addrlen);
if (!pdst->ai_addr)
{
result = -1;
break;
}
memcpy(pdst->ai_addr, psrc->ai_addr, psrc->ai_addrlen);
pdst->ai_canonname = strdup(psrc->ai_canonname); /* Assumes POSIX. */
if (!pdst->ai_canonname)
{
result = -1;
break;
}
} while (0);
return result;
}
To get rid of such a copy you need something like this:
/* Deallocates and sets to a 0/NULL what had been created by
addrinfo_copy(). */
void addrinfo_free(struct addrinfo * p)
{
assert(p);
free(p->ai_addr);
free(p->canonname);
memset(p, 0, sizeof *p); /* In fact not necessary. */
}
Use it like this:
struct addrinfo * entry, * entry_copy;
/* Set entry to something returned by getaddrinfo (). */
...
if (-1 = addrinfo_copy(entry_copy, entry))
{
perror("addrinfo_copy() failed"):
exit(EXIT_FAILURE);
}
/* Deallocate all results returned by getaddrinfo(). */
freeaddrinfo(...);
/* Still use entry_copy here. */
...
/* Clean up. */
addrinfo_free(entry_copy);
As a final note:
If when doing C you observe obscure sudden/unexpected changes in memory content this all most ever dues to having messed up memory management by writing and/or reading to "wrong" memory. This some times happened way long before those changes in memory become obvious and/or in code (seemingly) completely unrelated to where you observe such changes in memory.
I have the following problem. I have to write a Plugin for Pidgin in the Language C. I am completely new to C.
I found the following Code.
WORD wVersionRequested;
WSADATA wsaData;
char name[255];
char* ip;
PHOSTENT hostinfo;
wVersionRequested = MAKEWORD( 2, 0 );
if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) {
if( gethostname ( name, sizeof(name)) == 0) {
if((hostinfo = gethostbyname(name)) != NULL) {
ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
}
}
WSACleanup( );
}
I have the IP-Address
172.28.52.220
But because of my VMWare I also have the IP 10.0.1.3.
In my Plugin now the IP 10.0.1.3 is assigned to my variable.
i need the IP to find out in which location of my company I am. I need hte 172...
Now I could find in the winsock2.h that *hostinfo->h_addr_list contains the list of Ip addresses. How can I assign the 172. Address to my IP_Variable?
Thank you in advance for your help!
Edit:
Just to clarify: I donĀ“t want to have my external IP address. I need my internals.
Here is an example I tested on on linux. I dont have access to a Windows system until tomorrow, but can test and update the answer if required.
It is comparable to the Windows version only without the WSAStartup call at the beginning.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
int main()
{
char hostnamebuff[100];
if(gethostname(hostnamebuff, 100) == 0)
{
struct hostent* hostinfo = gethostbyname(hostnamebuff);
printf("host name is %s\n", hostnamebuff);
if(hostinfo != NULL)
{
char** paddrlist = hostinfo->h_addr_list;
printf("host list is\n");
while(*paddrlist != NULL)
{
char addrbuff[INET6_ADDRSTRLEN];
if(inet_ntop(hostinfo->h_addrtype, *paddrlist, addrbuff, hostinfo->h_addrtype == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN))
{
printf("%s\n", addrbuff);
if(strncmp(addrbuff, "172.", 4) == 0)
{
printf("its a match\n");
break;
}
} else
{
printf("failed to convert an address\n");
}
paddrlist++;
}
} else
{
printf("failed on gethostbyname\n");
}
} else
{
printf("failed on gethostname errno=%d\n", errno);
}
}
The h_addr_list member of hostent is a NULL terminated array of pointers to char* (so its double pointer). My example shows how to traverse this. Hope it helps.
As for this distinctly smelly bit of code
ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
This is a highly unreadable way of getting the first entry in the address list.
I have looked around like crazy but don't get a real answer. I got one example, but that depended on the individuals own library so not much good.
At first I wanted to get the default gateway of an interface, but since different IP's could be routed differently I quickly understood that what I want it get the gateway to use for a given destination IP by using an AF_ROUTE socket and the rtm_type RTM_GET.
Does anyone have an example where I actually end up with a string containing the gateways IP (or mac address)? The gateway entry seem to be in hex but also encoded in /proc/net/route, where I guess the AF_ROUTE socket get's it info from (but via the kernel I guess).
Thanx in advance
and p.s.
I just started using stack overflow and I must say, all of you guys are great! Fast replies and good ones! You are my new best friends ;)
This is OS specific, there's no unified(or ANSI C) API for this.
Assuming Linux, the best way is to just parse /proc/net/route , look for the entry where Destination is 00000000 , the default gateway is in the Gateway column , where you can read the hex representation of the gateway IP address (in big endian , I believe)
If you want to do this via more specific API calls, you'll have to go through quite some hoops, here's an example program:
#include <netinet/in.h>
#include <net/if.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFSIZE 8192
char gateway[255];
struct route_info {
struct in_addr dstAddr;
struct in_addr srcAddr;
struct in_addr gateWay;
char ifName[IF_NAMESIZE];
};
int readNlSock(int sockFd, char *bufPtr, int seqNum, int pId)
{
struct nlmsghdr *nlHdr;
int readLen = 0, msgLen = 0;
do {
/* Recieve response from the kernel */
if ((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0) {
perror("SOCK READ: ");
return -1;
}
nlHdr = (struct nlmsghdr *) bufPtr;
/* Check if the header is valid */
if ((NLMSG_OK(nlHdr, readLen) == 0)
|| (nlHdr->nlmsg_type == NLMSG_ERROR)) {
perror("Error in recieved packet");
return -1;
}
/* Check if the its the last message */
if (nlHdr->nlmsg_type == NLMSG_DONE) {
break;
} else {
/* Else move the pointer to buffer appropriately */
bufPtr += readLen;
msgLen += readLen;
}
/* Check if its a multi part message */
if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) {
/* return if its not */
break;
}
} while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId));
return msgLen;
}
/* For printing the routes. */
void printRoute(struct route_info *rtInfo)
{
char tempBuf[512];
/* Print Destination address */
if (rtInfo->dstAddr.s_addr != 0)
strcpy(tempBuf, inet_ntoa(rtInfo->dstAddr));
else
sprintf(tempBuf, "*.*.*.*\t");
fprintf(stdout, "%s\t", tempBuf);
/* Print Gateway address */
if (rtInfo->gateWay.s_addr != 0)
strcpy(tempBuf, (char *) inet_ntoa(rtInfo->gateWay));
else
sprintf(tempBuf, "*.*.*.*\t");
fprintf(stdout, "%s\t", tempBuf);
/* Print Interface Name*/
fprintf(stdout, "%s\t", rtInfo->ifName);
/* Print Source address */
if (rtInfo->srcAddr.s_addr != 0)
strcpy(tempBuf, inet_ntoa(rtInfo->srcAddr));
else
sprintf(tempBuf, "*.*.*.*\t");
fprintf(stdout, "%s\n", tempBuf);
}
void printGateway()
{
printf("%s\n", gateway);
}
/* For parsing the route info returned */
void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo)
{
struct rtmsg *rtMsg;
struct rtattr *rtAttr;
int rtLen;
rtMsg = (struct rtmsg *) NLMSG_DATA(nlHdr);
/* If the route is not for AF_INET or does not belong to main routing table
then return. */
if ((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN))
return;
/* get the rtattr field */
rtAttr = (struct rtattr *) RTM_RTA(rtMsg);
rtLen = RTM_PAYLOAD(nlHdr);
for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) {
switch (rtAttr->rta_type) {
case RTA_OIF:
if_indextoname(*(int *) RTA_DATA(rtAttr), rtInfo->ifName);
break;
case RTA_GATEWAY:
rtInfo->gateWay.s_addr= *(u_int *) RTA_DATA(rtAttr);
break;
case RTA_PREFSRC:
rtInfo->srcAddr.s_addr= *(u_int *) RTA_DATA(rtAttr);
break;
case RTA_DST:
rtInfo->dstAddr .s_addr= *(u_int *) RTA_DATA(rtAttr);
break;
}
}
//printf("%s\n", inet_ntoa(rtInfo->dstAddr));
if (rtInfo->dstAddr.s_addr == 0)
sprintf(gateway, (char *) inet_ntoa(rtInfo->gateWay));
//printRoute(rtInfo);
return;
}
int main()
{
struct nlmsghdr *nlMsg;
struct rtmsg *rtMsg;
struct route_info *rtInfo;
char msgBuf[BUFSIZE];
int sock, len, msgSeq = 0;
/* Create Socket */
if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
perror("Socket Creation: ");
memset(msgBuf, 0, BUFSIZE);
/* point the header and the msg structure pointers into the buffer */
nlMsg = (struct nlmsghdr *) msgBuf;
rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg);
/* Fill in the nlmsg header*/
nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message.
nlMsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
nlMsg->nlmsg_seq = msgSeq++; // Sequence of the message packet.
nlMsg->nlmsg_pid = getpid(); // PID of process sending the request.
/* Send the request */
if (send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0) {
printf("Write To Socket Failed...\n");
return -1;
}
/* Read the response */
if ((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) {
printf("Read From Socket Failed...\n");
return -1;
}
/* Parse and print the response */
rtInfo = (struct route_info *) malloc(sizeof(struct route_info));
//fprintf(stdout, "Destination\tGateway\tInterface\tSource\n");
for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) {
memset(rtInfo, 0, sizeof(struct route_info));
parseRoutes(nlMsg, rtInfo);
}
free(rtInfo);
close(sock);
printGateway();
return 0;
}
Maybe this is very old question but I had same problem and I can't find better result. Finally I solved my problem with these code that it has a few changes. So I decide to share it.
char* GetGatewayForInterface(const char* interface)
{
char* gateway = NULL;
char cmd [1000] = {0x0};
sprintf(cmd,"route -n | grep %s | grep 'UG[ \t]' | awk '{print $2}'", interface);
FILE* fp = popen(cmd, "r");
char line[256]={0x0};
if(fgets(line, sizeof(line), fp) != NULL)
gateway = string(line);
pclose(fp);
}
I decided to go the "quick-and-dirty" way to start with and read out the ip from /proc/net/route using netstat -rm.
I thought I'd share my function... Note however that there is some error in it and prehaps you could help me find it and I'll edit this to be without faults. The function take a iface name like eth0 and returns the ip of the gateway used by that iface.
char* GetGatewayForInterface(const char* interface) {
char* gateway = NULL;
FILE* fp = popen("netstat -rn", "r");
char line[256]={0x0};
while(fgets(line, sizeof(line), fp) != NULL)
{
/*
* Get destination.
*/
char* destination;
destination = strndup(line, 15);
/*
* Extract iface to compare with the requested one
* todo: fix for iface names longer than eth0, eth1 etc
*/
char* iface;
iface = strndup(line + 73, 4);
// Find line with the gateway
if(strcmp("0.0.0.0 ", destination) == 0 && strcmp(iface, interface) == 0) {
// Extract gateway
gateway = strndup(line + 16, 15);
}
free(destination);
free(iface);
}
pclose(fp);
return gateway;
}
The problem with this function is that when I leave pclose in there it causes a memory corruption chrash. But it works if I remove the pclose call (but that would not be a good solution beacuse the stream would remain open.. hehe). So if anyone can spot the error I'll edit the function with the correct version. I'm no C guru and gets a bit confused about all the memory fiddling ;)
Problem description:
I have a IP address (can be either IPv4/IPv6) and NIC address, how can I check if the IP address is added to the given NIC(or any NIC) using C.
I know it is simple to do the same on command line/using scripts, however I need to check the same in C Program.
Example:
IP - 192.168.0.1
NIC - eth0
Using command line(linux platform) the below command would tell me if the IP is added or not:
ip addr show | grep "192.168.0.1"
p.s.: Is there any library function which can be used to get similar outputs?
You want to use getifaddrs, which returns a list of network interfaces and the addresses associated with them.
From the man page:
int getifaddrs(struct ifaddrs **ifap);
The getifaddrs() function creates a linked list of structures
describing the network interfaces of the local system, and stores
the address of the first item of the list in *ifap. The list
consists of ifaddrs structures, defined as follows:
struct ifaddrs {
struct ifaddrs *ifa_next; /* Next item in list */
char *ifa_name; /* Name of interface */
unsigned int ifa_flags; /* Flags from SIOCGIFFLAGS */
struct sockaddr *ifa_addr; /* Address of interface */
struct sockaddr *ifa_netmask; /* Netmask of interface */
union {
struct sockaddr *ifu_broadaddr;
/* Broadcast address of interface */
struct sockaddr *ifu_dstaddr;
/* Point-to-point destination address */
} ifa_ifu;
#define ifa_broadaddr ifa_ifu.ifu_broadaddr
#define ifa_dstaddr ifa_ifu.ifu_dstaddr
void *ifa_data; /* Address-specific data */
};
Here's an example of how I've used it in one of my programs:
union sockaddr_u {
struct sockaddr_storage ss;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
};
struct iflist {
char name[IFNAME_LEN];
union sockaddr_u su;
int isloopback;
int ismulti;
int ifidx;
};
void getiflist(struct iflist *list, int *len)
{
struct ifaddrs *ifa, *ifa_tmp;
int count;
unsigned ifidx;
if (getifaddrs(&ifa) == -1) {
perror("getifaddrs failed");
*len = 0;
return;
}
ifa_tmp = ifa;
count = *len;
*len = 0;
while (ifa_tmp && (*len < count)) {
if ((ifidx = if_nametoindex(ifa_tmp->ifa_name)) == 0) {
perror("Error getting interface index for interface %s",
ifa_tmp->ifa_name);
continue;
}
if (ifa_tmp->ifa_addr && ((ifa_tmp->ifa_addr->sa_family == AF_INET) ||
(ifa_tmp->ifa_addr->sa_family == AF_INET6)) &&
((ifa_tmp->ifa_flags & IFF_UP) != 0)) {
memset(&list[*len], 0, sizeof(struct iflist));
strncpy(list[*len].name, ifa_tmp->ifa_name,
sizeof(list[*len].name) - 1);
memcpy(&list[*len].su, ifa_tmp->ifa_addr,
sizeof(struct sockaddr_storage));
list[*len].isloopback = (ifa_tmp->ifa_flags & IFF_LOOPBACK) != 0;
list[*len].ismulti = (ifa_tmp->ifa_flags & IFF_MULTICAST) != 0;
list[*len].ifidx = ifidx;
(*len)++;
}
ifa_tmp = ifa_tmp->ifa_next;
}
freeifaddrs(ifa);
}
You may want to use GETIFADDRS http://man7.org/linux/man-pages/man3/getifaddrs.3.html
This should Work:
#define _BSD_SOURCE
#include <ifaddrs.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netdb.h>
int main(void){
struct ifaddrs *ip, *hosst;
int s;
char host[NI_MAXHOST];
if (getifaddrs(&ip) == -1){
perror("getifaddrs");
return 1;
}
for (hosst = ip; hosst != NULL; hosst = hosst->ifa_next){
if (hosst->ifa_addr == NULL){
continue;
}
s=getnameinfo(hosst->ifa_addr,sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if((strcmp(hosst->ifa_name,"wlan0")==0)&&(hosst->ifa_addr->sa_family==AF_INET)){
if (s != 0){
printf("getnameinfo() failed: %s\n", gai_strerror(s));
return 1;
}
printf("IP - %s\n", host);
printf("NIC - %s\n",hosst->ifa_name );
}
}
free(ip);
return 0;
}
Output:
IP - 192.168.0.110
NIC - wlan0
The method(GETIFADDRS) suggested above is correct solution for the stated problem description. The getifaddrs browses through all the IP's on the NIC.
However in my case there are many IP's addresses added on the NIC/system and I am OK with knowing if the IP is present in any of the NIC. I found a easier/faster solution.
To check if the IP is added on any of the NIC card of the machine, just open a TCP socket and bind with port=zero(0) and the IP address to be checked. The successful bind will suggest that the IP is available/present.
Note: This works if IP is added on any of the NIC card in the system. The port zero should be used instead of hard-coding as it selects any available port on in the system
(ref. http://compnetworking.about.com/od/tcpip/p/port-numbers-0.htm)
This method is tested for both IPv4/IPv6 in UNIX environment(rhel)
PS:Do not forget to close the socket after verifying the presence of IP