2011年7月17日 星期日

getting both IPv4 and IPv6 addresses of all local network interfaces via "getifaddrs"

In the old days, we often use ioctl to get the IPv4 address  of our local interfaces via :
ioctl(socketfd, SIOCGIFCONF, (struct ifconf)&buffer);
if we are interested in the IPv6 local addresses, we can always check them out via "/proc", for example, this is what we might do:

[sauron@icomserver ~]$ cat /proc/net/if_inet6
fe8000000000000002d0c9fffebb030b      03 40 20 80       eth1
00000000000000000000000000000001 01 80 10 80       lo
  1. IPv6 address displayed in 32 hexadecimal chars without colons as separator
  2. Netlink device number (interface index) in hexadecimal (see “ip addr” , too)
  3. Prefix length in hexadecimal
  4. Scope value (see kernel source “ include/net/ipv6.h” and “net/ipv6/addrconf.c” for more)
  5. Interface flags (see “include/linux/rtnetlink.h” and “net/ipv6/addrconf.c” for more)
  6. Device name
Quite easy, right? But what should we do to get both IPv4 and IPv6 addresses via the same API?
if you are seeking for a IPv4 and IPv6 total solution, then "getifaddrs"  is the answer that satisfies all your needs.

reference:
man 3 getifaddrs, man  3 getaddrinfo, man 3 getnameinfo




#include <arpa/inet.h>
#include <sys/socket.h>

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

int

main(int argc, char *argv[])
{
   struct ifaddrs *ifaddr, *ifa;

   int family, s;
   char host[NI_MAXHOST];

   if (getifaddrs(&ifaddr) == -1) {
       perror("getifaddrs");

       exit(EXIT_FAILURE);
   }

/* Walk through linked list, maintaining head pointer so we
*               can free list later */

   for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {

       family = ifa->ifa_addr->sa_family;

/*   Display interface name and family (including symbolic
*               form of the latter for the common families) */

       printf("%s  address family: %d%s\n",

        ifa->ifa_name, family,
        (family == AF_PACKET) ? " (AF_PACKET)" :
        (family == AF_INET) ?   " (AF_INET)" :
        (family == AF_INET6) ?  " (AF_INET6)" : "");

       /* For an AF_INET* interface address, display the address */

       if (family == AF_INET || family == AF_INET6) {

    s = getnameinfo(ifa->ifa_addr,
     (family == AF_INET) ? sizeof(struct sockaddr_in) :

      sizeof(struct sockaddr_in6),
     host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);

    if (s != 0) {
        printf("getnameinfo() failed: %s\n", gai_strerror(s));

        exit(EXIT_FAILURE);
    }
    printf("\taddress: <%s>\n", host);
       }
   }

   freeifaddrs(ifaddr);
   exit(EXIT_SUCCESS);
}

2011年7月6日 星期三

how to get default gateway via rtnetlink (c code example)

this is a example code showing you how to get the ip of the default gateway via netlink socket. I've also attached some useful information if you want to study netlink
further reading:

Sample Code To Learn Netlink Infrastructure

 understanding and programming with netlink sockets(PDF)

reference:
man 3 rtnetlink, man 7 rtnetlink, man 3 netlink, man 7 netlink


example code:
#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

 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.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", (char *)inet_ntoa(rtInfo->dstAddr));
  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));

  /* 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);

  printGateway();

  return 0;
 }