Linux下网关地址的获取

Linux的网关信息保存在路由表中,获取网关实际上就是路由表的查询。

 用户空间获取网关地址

有两种方法,一个是从/proc/net/route中读取,这是最简单,最直接的,route命令就是这么做的,可以参考net-tools包中route的源码实现。

另一种是用Netlink来实现。利用NETLINK_ROUTE(rtnetlink.c: Routing netlink socket interface)的RTM_GETROUTE指令查找路由,这是从网上找的代码,在Debian (2.6.26内核)下测试通过。

C++代码
  1. #include <arpa/inet.h>  //for in_addr  
  2. #include <linux/rtnetlink.h>    //for rtnetlink  
  3. #include <net/if.h> //for IF_NAMESIZ, route_info  
  4. #include <stdlib.h> //for malloc(), free()  
  5. #include <string.h> //for strstr(), memset()  
  6.   
  7. #include <string>  
  8.   
  9. #define BUFSIZE 8192  
  10.    
  11. struct route_info{  
  12.  u_int dstAddr;  
  13.  u_int srcAddr;  
  14.  u_int gateWay;  
  15.  char ifName[IF_NAMESIZE];  
  16. };  
  17. int readNlSock(int sockFd, char *bufPtr, int seqNum, int pId)  
  18. {  
  19.   struct nlmsghdr *nlHdr;  
  20.   int readLen = 0, msgLen = 0;  
  21.   do{  
  22.     //收到内核的应答  
  23.     if((readLen = recv(sockFd, bufPtr, BUFSIZE – msgLen, 0)) < 0)  
  24.     {  
  25.       perror("SOCK READ: ");  
  26.       return -1;  
  27.     }  
  28.      
  29.     nlHdr = (struct nlmsghdr *)bufPtr;  
  30.     //检查header是否有效  
  31.     if((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR))  
  32.     {  
  33.       perror("Error in recieved packet");  
  34.       return -1;  
  35.     }  
  36.      
  37.     /* Check if the its the last message */  
  38.     if(nlHdr->nlmsg_type == NLMSG_DONE)   
  39.     {  
  40.       break;  
  41.     }  
  42.     else  
  43.     {  
  44.       /* Else move the pointer to buffer appropriately */  
  45.       bufPtr += readLen;  
  46.       msgLen += readLen;  
  47.     }  
  48.      
  49.     /* Check if its a multi part message */  
  50.     if((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0)   
  51.     {  
  52.       /* return if its not */  
  53.      break;  
  54.     }  
  55.   } while((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId));  
  56.   return msgLen;  
  57. }  
  58. //分析返回的路由信息  
  59. void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo,char *gateway)  
  60. {  
  61.   struct rtmsg *rtMsg;  
  62.   struct rtattr *rtAttr;  
  63.   int rtLen;  
  64.   char *tempBuf = NULL;  
  65.   struct in_addr dst;  
  66.   struct in_addr gate;  
  67.     
  68.   tempBuf = (char *)malloc(100);  
  69.   rtMsg = (struct rtmsg *)NLMSG_DATA(nlHdr);  
  70.   // If the route is not for AF_INET or does not belong to main routing table  
  71.   //then return.   
  72.   if((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN))  
  73.   return;  
  74.   /* get the rtattr field */  
  75.   rtAttr = (struct rtattr *)RTM_RTA(rtMsg);  
  76.   rtLen = RTM_PAYLOAD(nlHdr);  
  77.   for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen)){  
  78.    switch(rtAttr->rta_type) {  
  79.    case RTA_OIF:  
  80.     if_indextoname(*(int *)RTA_DATA(rtAttr), rtInfo->ifName);  
  81.     break;  
  82.    case RTA_GATEWAY:  
  83.     rtInfo->gateWay = *(u_int *)RTA_DATA(rtAttr);  
  84.     break;  
  85.    case RTA_PREFSRC:  
  86.     rtInfo->srcAddr = *(u_int *)RTA_DATA(rtAttr);  
  87.     break;  
  88.    case RTA_DST:  
  89.     rtInfo->dstAddr = *(u_int *)RTA_DATA(rtAttr);  
  90.     break;  
  91.    }  
  92.   }  
  93.   dst.s_addr = rtInfo->dstAddr;  
  94.   if (strstr((char *)inet_ntoa(dst), "0.0.0.0"))  
  95.   {  
  96.     printf("oif:%s",rtInfo->ifName);  
  97.     gate.s_addr = rtInfo->gateWay;  
  98.     sprintf(gateway, (char *)inet_ntoa(gate));  
  99.     printf("%sn",gateway);  
  100.     gate.s_addr = rtInfo->srcAddr;  
  101.     printf("src:%sn",(char *)inet_ntoa(gate));  
  102.     gate.s_addr = rtInfo->dstAddr;  
  103.     printf("dst:%sn",(char *)inet_ntoa(gate));   
  104.   }  
  105.   free(tempBuf);  
  106.   return;  
  107. }  
  108.   
  109. int get_gateway(char *gateway)  
  110. {  
  111.  struct nlmsghdr *nlMsg;  
  112.  struct rtmsg *rtMsg;  
  113.  struct route_info *rtInfo;  
  114.  char msgBuf[BUFSIZE];  
  115.    
  116.  int sock, len, msgSeq = 0;  
  117.   
  118.  if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)  
  119.  {  
  120.   perror("Socket Creation: ");  
  121.   return -1;  
  122.  }  
  123.    
  124.  /* Initialize the buffer */  
  125.  memset(msgBuf, 0, BUFSIZE);  
  126.    
  127.  /* point the header and the msg structure pointers into the buffer */  
  128.  nlMsg = (struct nlmsghdr *)msgBuf;  
  129.  rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg);  
  130.    
  131.  /* Fill in the nlmsg header*/  
  132.  nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message.  
  133.  nlMsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .  
  134.    
  135.  nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.  
  136.  nlMsg->nlmsg_seq = msgSeq++; // Sequence of the message packet.  
  137.  nlMsg->nlmsg_pid = getpid(); // PID of process sending the request.  
  138.    
  139.  /* Send the request */  
  140.  if(send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0){  
  141.   printf("Write To Socket Failed…n");  
  142.   return -1;  
  143.  }  
  144.    
  145.  /* Read the response */  
  146.  if((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) {  
  147.   printf("Read From Socket Failed…n");  
  148.   return -1;  
  149.  }  
  150.  /* Parse and print the response */  
  151.  rtInfo = (struct route_info *)malloc(sizeof(struct route_info));  
  152.  for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len)){  
  153.   memset(rtInfo, 0, sizeof(struct route_info));  
  154.   parseRoutes(nlMsg, rtInfo,gateway);  
  155.  }  
  156.  free(rtInfo);  
  157.  close(sock);  
  158.  return 0;  
  159. }  
  160.   
  161. int main()  
  162. {  
  163.     char buff[256];  
  164.     get_gateway(buff);  
  165.     return 0;  
  166. }  

内核空间获取网关地址

用户空间的实现,其本质上是内核空间的支持,因此内核空间获取应该更直接点。我参考了NETLINK_ROUTE中的实现来做,即执行一个从本机IP到外网IP的路由查询,获得的路由记录中自然包括网关地址,主要用到ip_route_output_key()函数。下面是我的代码:

C++代码
  1. …  
  2. extern struct net init_net;  
  3. …  
  4. inline void printIP(__u32 uip)  
  5. {  
  6.     printk(NIPQUAD_FMT,NIPQUAD(uip));  
  7. }  
  8. …  
  9. int xxxxxx()  
  10. {  
  11.     …  
  12.     int err;  
  13.     struct rtable * rt = NULL;  
  14.     struct flowi fl = {  
  15.         .nl_u = {  
  16.             .ip4_u = {  
  17.                 .daddr = 0,  
  18.                 .saddr = 0,  
  19.                 .tos = 0,  
  20.             },  
  21.         },  
  22.         .oif = 0,  
  23.     };  
  24.     fl.nl_u.ip4_u.daddr = in_aton("182.168.1.1");  
  25.     fl.nl_u.ip4_u.saddr = in_aton("192.168.0.186");  
  26.     err = ip_route_output_key(&init_net, &rt, &fl);  
  27.     if(rt)  
  28.     {  
  29.         if(rt->idev&&rt->idev->dev&&rt->idev->dev->name)  
  30.             printk(" if:%sn",rt->idev->dev->name);  
  31.         printk(" gw:");  
  32.         printIP(rt->rt_gateway);  
  33.         printk("n dst:");  
  34.         printIP(rt->rt_dst);  
  35.         printk("n src:");  
  36.         printIP(rt->rt_src);  
  37.         printk("n");  
  38.     }  
  39.     else  
  40.         printk("rt = NULL!n");  
  41.     …  
  42. }  

 暂时只找到这种实现方式,有新的发现再来更新:)

发表评论

电子邮件地址不会被公开。 必填项已用*标注