您的位置:首页 > 运维架构 > Linux

Linux下网关地址的获取

2012-02-08 12:31 1716 查看
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");   
    …   
}

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

March 18th,2009 | Category:Software Development
原文地址:http://www.penna.cn/blog/?p=255
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: