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

 

  1. 内核空间获取网关地址

用户空间的实现,其本质上是内核空间的支持,因此内核空间获取应该更直接点。我参考了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");   
    …   
}  
 暂时只找到这种实现方式,有新的发现再来更新:)
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
获取Linux操作系统的IP地址可以通过使用命令行工具来实现,以下是获取IP地址的命令: 1. 获取IP地址: ``` ifconfig ``` 该命令可以列出络接口信息,其中包括IP地址以及其他络相的信息。 2. 获取: ``` route -n ``` 该命令可以列出当前系统的路由表信息,其中包括的IP地址以及其他络相的信息。 如果你是使用Java程序来获取IP地址的话,可以通过使用NetworkInterface和InetAddress类来实现,以下是示例代码: 获取IP地址: ```java import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; public class GetIPAddress { public static void main(String[] args) throws Exception { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface iface = interfaces.nextElement(); if (iface.isLoopback() || !iface.isUp()) { continue; } Enumeration<InetAddress> addresses = iface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress addr = addresses.nextElement(); if (addr.isLinkLocalAddress() || addr.isLoopbackAddress() || addr.isMulticastAddress()) { continue; } System.out.println("IP地址:" + addr.getHostAddress()); } } } } ``` 获取: ```java import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; public class GetGateway { public static void main(String[] args) throws SocketException { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface iface = interfaces.nextElement(); Enumeration<InetAddress> inetAddresses = iface.getInetAddresses(); while (inetAddresses.hasMoreElements()) { InetAddress inetAddress = inetAddresses.nextElement(); if (!inetAddress.isLinkLocalAddress() && !inetAddress.isLoopbackAddress() && inetAddress instanceof java.net.Inet4Address) { Enumeration<NetworkInterface> subInterfaces = iface.getSubInterfaces(); while (subInterfaces.hasMoreElements()) { NetworkInterface subInterface = subInterfaces.nextElement(); Enumeration<InetAddress> subInetAddresses = subInterface.getInetAddresses(); while (subInetAddresses.hasMoreElements()) { InetAddress subInetAddress = subInetAddresses.nextElement(); if (!subInetAddress.isLinkLocalAddress() && !subInetAddress.isLoopbackAddress() && subInetAddress instanceof java.net.Inet4Address) { System.out.println("地址:" + subInetAddress.getHostAddress()); } } } } } } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值