LinuxQuestions.org

LinuxQuestions.org (http://www.linuxquestions.org/questions/index.php)
-   Linux - Networking (http://www.linuxquestions.org/questions/forumdisplay.php?f=3)
-   -   Howto find gateway address through code (http://www.linuxquestions.org/questions/showthread.php?t=397078)

/usr/src 12-28-2005 12:37 AM

Howto find gateway address through code
 
I need to find the gateway IP address of my Linux machine from within a C program. The machine gets its address via DHCP.
I know that I can see the gateway thru 'route' and 'netstat -rn', but I need to do this thru C code.
Does anyone know of a C function which does that?
Or do I definitely have to do this by reading /proc/net/ and juggling with the formatting :( .

tincuchalice 12-29-2005 10:44 AM

try this if you don't mind file i/o

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define CMD "netstat -rn > gateway.txt"
#define FILENAME "gateway.txt"
#define DEFAULT "default" // this may be "0.0.0.0" or something else try cmd the less gateway.txt
char line[255];
char *p, *q;
FILE *fp;

int main()
{
system(CMD);
fp = fopen(FILENAME, "rb");
if (!fp)
return -1;
while (fgets(line, 255, fp))
if (p = strstr(line, DEFAULT))
break;
int i = 0;
while (!isdigit(p[i]))
i++;
q = p + i;
p = strstr(q, ".");
p++;
p = strstr(p, ".");
p++;
p = strstr(p, ".");
p++;
i = 0;
while (isdigit(p[i]))
i++;
q[strlen(q) - strlen(p) + i] = 0;
printf("GATEWAY:%s\n", q);
fclose(fp);
return 0;
}

live_dont_exist 12-29-2005 11:10 AM

Thats some neat C code....but theres a much simpler way if you do it through shell scripting :and I just strtd using shell scripts so heres what I think:-

Find out if ur card is eth0 , eth1 or eth2 .... u shd be able to find stuff out...

then

grep GATEWAY /etc/sysconfig/network-scripts/ifcfg-ethX ( x=0,1,2..watevr)

once thats done...u can do what u want using C using strstr as suggested...or start a pointer at the start of the gateway file and do a ptr = ptr + 9 after opening that file in read mode using fopen...u get teh picture...

hope it helps
cheers
Arvind

/usr/src 12-30-2005 12:03 AM

@tincuchalice : Thanks for the wonderful code snippet - however, I am still interested to know whether there is a linux C function to do this (i.e. one can get the interface IP address by making an ioctl call with the command SIOCGIFADDR, don't we have something like that for gateway too?).

@live_dont_exist : The scheme you suggest will work only for statically configured interfaces and not for all cases because in majority of the cases, the IP, Gateway etc is gotten thru dhcp.

tincuchalice 12-31-2005 11:53 AM

well, i did some research, but it involves opening sockets

socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)

gettting rtg tables from the kernel and working with nlmsghdrs

the code is extensive but here it is...it is not my code, and i modified it to only print the gateway

essentially it mocks the netstat -rl in code w/o FILE *'s all stuff I did says //ADDED BY BOB

#include <asm/types.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/types.h>

#define BUFSIZE 8192
// ADDED BY BOB
char gateway[255];

struct route_info{
u_int dstAddr;
u_int srcAddr;
u_int 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 != 0)
strcpy(tempBuf, (char *)inet_ntoa(rtInfo->dstAddr));
else
sprintf(tempBuf,"*.*.*.*\t");
fprintf(stdout,"%s\t", tempBuf);

/* Print Gateway address */
if(rtInfo->gateWay != 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 != 0)
strcpy(tempBuf, (char *)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;
char *tempBuf = NULL;

tempBuf = (char *)malloc(100);
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 = *(u_int *)RTA_DATA(rtAttr);
break;
case RTA_PREFSRC:
rtInfo->srcAddr = *(u_int *)RTA_DATA(rtAttr);
break;
case RTA_DST:
rtInfo->dstAddr = *(u_int *)RTA_DATA(rtAttr);
break;
}
}
//printf("%s\n", (char *)inet_ntoa(rtInfo->dstAddr));
// ADDED BY BOB - ALSO COMMENTED printRoute
if (strstr((char *)inet_ntoa(rtInfo->dstAddr), "0.0.0.0"))
sprintf(gateway, (char *)inet_ntoa(rtInfo->gateWay));
//printRoute(rtInfo);
free(tempBuf);
return;
}

int main()
{
struct nlmsghdr *nlMsg;
struct rtmsg *rtMsg;
struct route_info *rtInfo;
char msgBuf[BUFSIZE];

int sock, len, msgSeq = 0;
char buff[1024];

/* Create Socket */
if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
perror("Socket Creation: ");

/* Initialize the buffer */
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));
// ADDED BY BOB
/* THIS IS THE NETTSTAT -RL code I commented out the printing here and in parse routes */
//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);
// ADDED BY BOB
printGateway();
return 0;
}

tincuchalice 12-31-2005 11:55 AM

I also think new releases could merit a function getgateway() that actually does this, but i don't see a direct UNIX fcn to get this

/usr/src 01-03-2006 06:48 AM

@tinuchalice : Man that is some code!! Thanks!!!

Ephracis 07-14-2008 01:41 PM

inet_ntoa() takes a struct in_addr as argument while the gateWay inside the structure is a u_int. I can't get the code to compile because of that. Shouldn't you put it into a in_addr structure and then give it to inet_ntoa()? Or am I missing something?

Anyone tried to compile the code?

gibbon1 05-27-2009 04:30 PM

Example cleaned and working
 
Quote:

Originally Posted by Ephracis (Post 3214289)
inet_ntoa() takes a struct in_addr as argument while the gateWay inside the structure is a u_int. I can't get the code to compile because of that. Shouldn't you put it into a in_addr structure and then give it to inet_ntoa()? Or am I missing something?

Anyone tried to compile the code?

I cleaned it up and got it to work. I replaced inet_ntoa() with something more generic and did some clean up of unused variables etc. This appears to work fine under FC8 and IPV4. Not sure under IPV6.

----
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <net/if.h>
#include <linux/rtnetlink.h>

#include <unistd.h>
#include <arpa/inet.h>

#define BUFSIZE 8192

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

/* parse 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:
memcpy(&rtInfo->gateWay, RTA_DATA(rtAttr), sizeof(rtInfo->gateWay));
break;

case RTA_PREFSRC:
memcpy(&rtInfo->srcAddr, RTA_DATA(rtAttr), sizeof(rtInfo->srcAddr));
break;

case RTA_DST:
memcpy(&rtInfo->dstAddr, RTA_DATA(rtAttr), sizeof(rtInfo->dstAddr));
break;
}
}

return;
}

// meat
int get_gatewayip(char *gatewayip, socklen_t size)
{
int found_gatewayip = 0;

struct nlmsghdr *nlMsg;
struct rtmsg *rtMsg;
struct route_info *rtInfo;
char msgBuf[BUFSIZE]; // pretty large buffer

int sock, len, msgSeq = 0;

/* Create Socket */
if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
{
perror("Socket Creation: ");
return(-1);
}

/* Initialize the buffer */
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)
{
fprintf(stderr, "Write To Socket Failed...\n");
return -1;
}

/* Read the response */
if((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0)
{
fprintf(stderr, "Read From Socket Failed...\n");
return -1;
}

/* Parse and print the response */
rtInfo = (struct route_info *)malloc(sizeof(struct route_info));

for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len))
{
memset(rtInfo, 0, sizeof(struct route_info));
parseRoutes(nlMsg, rtInfo);

// Check if default gateway
if (strstr((char *)inet_ntoa(rtInfo->dstAddr), "0.0.0.0"))
{
// copy it over
inet_ntop(AF_INET, &rtInfo->gateWay, gatewayip, size);
found_gatewayip = 1;
break;
}
}

free(rtInfo);
close(sock);

return found_gatewayip;
}

mburnicki 03-18-2013 12:39 PM

More enhancements
 
Just for the records: I recently stumbled about this example code which basically works great but contains a bug.

The route_info structure is always cleared before parseRoutes() is called, and the original code just checked if dstAddr was 0.0.0.0 after parseRoutes() had been called. If it was then the code assumed the the default route had been found, and stopped evaluation of the rest of the routing message.

However, if parseRoutes() hadn't evaluated a message segment then it had not filled up route_info at all, and thus route_info was still all zero after return, in which case the code assumed the default route (dstAddr 0.0.0.0) had been found, but of course gateWay field was also still always 0.0.0.0 in such case, and so was the returned default gateway.

So I modified parseRoutes() to return -1 if a message segment has not been evaluated, and let the evaluation loop just continue instead of checking for the default route if the return code from parseRoutes() is < 0.

Then, in the original code an 8k buffer was used as a local variable, but a simple "struct route_info" (which has just a few bytes) was allocate by calling malloc without checking if malloc succeeded. The current code just uses another local variable for "struct route_info".

Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <net/if.h>
#include <linux/rtnetlink.h>

#include <unistd.h>
#include <arpa/inet.h>

#define BUFSIZE 8192

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, size_t buf_size, int seqNum, int pId)
{
  struct nlmsghdr *nlHdr;
  int readLen = 0, msgLen = 0;

  do
  {
    /* Recieve response from the kernel */
    if((readLen = recv(sockFd, bufPtr, buf_size - 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;
}

/* parse the route info returned */
int 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 -1;

  /* 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:
      memcpy(&rtInfo->gateWay, RTA_DATA(rtAttr), sizeof(rtInfo->gateWay));
      break;

    case RTA_PREFSRC:
      memcpy(&rtInfo->srcAddr, RTA_DATA(rtAttr), sizeof(rtInfo->srcAddr));
      break;

    case RTA_DST:
      memcpy(&rtInfo->dstAddr, RTA_DATA(rtAttr), sizeof(rtInfo->dstAddr));
      break;
    }
  }

  return 0;
}

// meat
int get_gatewayip(char *gatewayip, socklen_t size)
{
  int found_gatewayip = 0;

  struct nlmsghdr *nlMsg;
  struct rtmsg *rtMsg;
  struct route_info route_info;
  char msgBuf[BUFSIZE]; // pretty large buffer

  int sock, len, msgSeq = 0;

  /* Create Socket */
  if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
  {
    perror("Socket Creation: ");
    return(-1);
  }

  /* Initialize the buffer */
  memset(msgBuf, 0, sizeof(msgBuf));

  /* 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)
  {
    fprintf(stderr, "Write To Socket Failed...\n");
    return -1;
  }

  /* Read the response */
  if((len = readNlSock(sock, msgBuf, sizeof(msgBuf), msgSeq, getpid())) < 0)
  {
    fprintf(stderr, "Read From Socket Failed...\n");
    return -1;
  }

  /* Parse and print the response */
  for(; NLMSG_OK(nlMsg,len); nlMsg = NLMSG_NEXT(nlMsg,len))
  {
    memset(&route_info, 0, sizeof(route_info));
    if ( parseRoutes(nlMsg, &route_info) < 0 )
      continue;  // don't check route_info if it has not been set up

    // Check if default gateway
    if (strstr((char *)inet_ntoa(route_info.dstAddr), "0.0.0.0"))
    {
      // copy it over
      inet_ntop(AF_INET, &route_info.gateWay, gatewayip, size);
      found_gatewayip = 1;
      break;
    }
  }

  close(sock);

  return found_gatewayip;
}



All times are GMT -5. The time now is 03:47 PM.