1. 程式人生 > >Linux下閘道器地址的獲取

Linux下閘道器地址的獲取

 使用者空間獲取閘道器地址

有兩種方法,一個是從/proc/net/route中讀取,這是最簡單,最直接的,route命令就是這麼做的,可以參考net-tools包中route的原始碼實現。

另一種是用Netlink來實現。利用NETLINK_ROUTE(rtnetlink.c: Routing netlink socket interface)的RTM_GETROUTE指令查詢路由,這是從網上找的程式碼,在Debian (2.6.26核心)下測試通過。

#include <arpa/inet.h>  //for in_addr   
#include <linux/rtnetlink.h>    //for rtnetlink   
#include <net/if.h> //for IF_NAMESIZ, route_info   
#include <stdlib.h> //for malloc(), free()   
#include <string.h> //for strstr(), memset()   
   
#include <string>   
   
#define BUFSIZE 8192   
    
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{   
    //收到核心的應答   
    if((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0)   
    {   
      perror("SOCK READ: ");   
      return -1;   
    }   
      
    nlHdr = (struct nlmsghdr *)bufPtr;   
    //檢查header是否有效   
    if((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR))   
    {   
      perror("Error in recieved packet");   
      return -1;   
    }   
      
       
    if(nlHdr->nlmsg_type == NLMSG_DONE)    
    {   
      break;   
    }   
    else   
    {   
         
      bufPtr += readLen;   
      msgLen += readLen;   
    }   
      
       
    if((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0)    
    {   
         
     break;   
    }   
  } while((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId));   
  return msgLen;   
}   
//分析返回的路由資訊   
void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo,char *gateway)   
{   
  struct rtmsg *rtMsg;   
  struct rtattr *rtAttr;   
  int rtLen;   
  char *tempBuf = NULL;   
  struct in_addr dst;   
  struct in_addr gate;   
     
  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;   
     
  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;   
   }   
  }   
  dst.s_addr = rtInfo->dstAddr;   
  if (strstr((char *)inet_ntoa(dst), "0.0.0.0"))   
  {   
    printf("oif:%s",rtInfo->ifName);   
    gate.s_addr = rtInfo->gateWay;   
    sprintf(gateway, (char *)inet_ntoa(gate));   
    printf("%s\n",gateway);   
    gate.s_addr = rtInfo->srcAddr;   
    printf("src:%s\n",(char *)inet_ntoa(gate));   
    gate.s_addr = rtInfo->dstAddr;   
    printf("dst:%s\n",(char *)inet_ntoa(gate));    
  }   
  free(tempBuf);   
  return;   
}   
   
int get_gateway(char *gateway)   
{   
 struct nlmsghdr *nlMsg;   
 struct rtmsg *rtMsg;   
 struct route_info *rtInfo;   
 char msgBuf[BUFSIZE];   
    
 int sock, len, msgSeq = 0;   
   
 if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)   
 {   
  perror("Socket Creation: ");   
  return -1;   
 }   
    
    
 memset(msgBuf, 0, BUFSIZE);   
    
    
 nlMsg = (struct nlmsghdr *)msgBuf;   
 rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg);   
    
    
 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.   
    
    
 if(send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0){   
  printf("Write To Socket Failed…\n");   
  return -1;   
 }   
    
    
 if((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) {   
  printf("Read From Socket Failed…\n");   
  return -1;   
 }   
    
 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,gateway);   
 }   
 free(rtInfo);   
 close(sock);   
 return 0;   
}   
   
int main()   
{   
    char buff[256];   
    get_gateway(buff);   
    return 0;   
}  
核心空間獲取閘道器地址

使用者空間的實現,其本質上是核心空間的支援,因此核心空間獲取應該更直接點。我參考了NETLINK_ROUTE中的實現來做,即執行一個從本機IP到外網IP的路由查詢,獲得的路由記錄中自然包括閘道器地址,主要用到ip_route_output_key()函式。下面是我的程式碼:

…   
extern struct net init_net;   
…   
inline void printIP(__u32 uip)   
{   
    printk(NIPQUAD_FMT,NIPQUAD(uip));   
}   
…   
int xxxxxx()   
{   
    …   
    int err;   
    struct rtable * rt = NULL;   
    struct flowi fl = {   
        .nl_u = {   
            .ip4_u = {   
                .daddr = 0,   
                .saddr = 0,   
                .tos = 0,   
            },   
        },   
        .oif = 0,   
    };   
    fl.nl_u.ip4_u.daddr = in_aton("182.168.1.1");   
    fl.nl_u.ip4_u.saddr = in_aton("192.168.0.186");   
    err = ip_route_output_key(&init_net, &rt, &fl);   
    if(rt)   
    {   
        if(rt->idev&&rt->idev->dev&&rt->idev->dev->name)   
            printk(" if:%s\n",rt->idev->dev->name);   
        printk(" gw:");   
        printIP(rt->rt_gateway);   
        printk("\n dst:");   
        printIP(rt->rt_dst);   
        printk("\n src:");   
        printIP(rt->rt_src);   
        printk("\n");   
    }   
    else   
        printk("rt = NULL!\n");   
    …   
}  
 暫時只找到這種實現方式,有新的發現再來更新:)