Linux下获取本机IP地址(不需要知道interface name的方式)

之前有写过一篇文章,介绍如何在linux下编程实现获取本机的IP地址,详见下文地址
http://blog.csdn.net/timsley/article/details/51062342

但是这种方法有一种限制,就是需要知道你的interface name,在阅读libupnp代码时,发现如果不知道interface name,也是有方法可以拿到本机的IP地址。

实现思路

通过struct ifconf拿到interface的configure信息,然后遍历struct ifreq,拿到合法的IP地址

struct ifconf是用来保存所有接口信息的一个结构体,具体定义可以在/usr/include/net/if.h文件中找到

struct ifconf
  {
    int ifc_len;            /* Size of buffer.  */
    union
      {
    __caddr_t ifcu_buf;
    struct ifreq *ifcu_req;
      } ifc_ifcu;
  };
# define ifc_buf    ifc_ifcu.ifcu_buf   /* Buffer address.  */
# define ifc_req    ifc_ifcu.ifcu_req   /* Array of structures.  */
# define _IOT_ifconf _IOT(_IOTS(struct ifconf),1,0,0,0,0) /* not right */

struct ifreq是用来保存某个接口信息的,它的定义也是在/usr/include/net/if.h文件中找到,具体定义如下

struct ifreq
  {
# define IFHWADDRLEN    6
# define IFNAMSIZ   IF_NAMESIZE
    union
      {
    char ifrn_name[IFNAMSIZ];   /* Interface name, e.g. "en0".  */
      } ifr_ifrn;

    union
      {
    struct sockaddr ifru_addr;
    struct sockaddr ifru_dstaddr;
    struct sockaddr ifru_broadaddr;
    struct sockaddr ifru_netmask;
    struct sockaddr ifru_hwaddr;
    short int ifru_flags;
    int ifru_ivalue;
    int ifru_mtu;
    struct ifmap ifru_map;
    char ifru_slave[IFNAMSIZ];  /* Just fits the size */
    char ifru_newname[IFNAMSIZ];
    __caddr_t ifru_data;
      } ifr_ifru;
  };
# define ifr_name   ifr_ifrn.ifrn_name  /* interface name   */
# define ifr_hwaddr ifr_ifru.ifru_hwaddr    /* MAC address      */
# define ifr_addr   ifr_ifru.ifru_addr  /* address      */
# define ifr_dstaddr    ifr_ifru.ifru_dstaddr   /* other end of p-p lnk */
# define ifr_broadaddr  ifr_ifru.ifru_broadaddr /* broadcast address    */
# define ifr_netmask    ifr_ifru.ifru_netmask   /* interface net mask   */
# define ifr_flags  ifr_ifru.ifru_flags /* flags        */
# define ifr_metric ifr_ifru.ifru_ivalue    /* metric       */
# define ifr_mtu    ifr_ifru.ifru_mtu   /* mtu          */
# define ifr_map    ifr_ifru.ifru_map   /* device map       */
# define ifr_slave  ifr_ifru.ifru_slave /* slave device     */
# define ifr_data   ifr_ifru.ifru_data  /* for use by interface */
# define ifr_ifindex    ifr_ifru.ifru_ivalue    /* interface index      */
# define ifr_bandwidth  ifr_ifru.ifru_ivalue    /* link bandwidth   */
# define ifr_qlen   ifr_ifru.ifru_ivalue    /* queue length     */
# define ifr_newname    ifr_ifru.ifru_newname   /* New name     */
# define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)
# define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)
# define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)

你可以通过struct ifreq里的ifr_name去获取Interface的名字,也可以通过ifr_addr去拿到对应interface的IP地址,还可以拿到netmask、broadaddr等,通过ifr_flags可以判断当前interface是否up或者是否是回环等等

具体代码实现如下,详细可以参考libupnp里的getlocalhostname函数

int get_local_ip(char *out, size_t out_len)
{
    const char *p = NULL;
    char szBuffer[256 * sizeof (struct ifreq)];
    struct ifconf ifConf;
    struct ifreq ifReq;
    int nResult;
    long unsigned int i;
    int LocalSock;
    struct sockaddr_in LocalAddr;
    int j = 0;

    /* purify */
    memset(&ifConf,  0, sizeof(ifConf));
    memset(&ifReq,   0, sizeof(ifReq));
    memset(szBuffer, 0, sizeof(szBuffer));
    memset(&LocalAddr, 0, sizeof(LocalAddr));

    /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on.  */
    LocalSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (LocalSock == -1) 
    {
        printf("Can't create addrlist socket\n");
        return -1;
    }
    /* Get the interface configuration information... */
    ifConf.ifc_len = (int)sizeof szBuffer;
    ifConf.ifc_ifcu.ifcu_buf = (caddr_t) szBuffer;
    nResult = ioctl(LocalSock, SIOCGIFCONF, &ifConf);
    if (nResult < 0) 
    {
        printf("DiscoverInterfaces: SIOCGIFCONF returned error\n");
        close(LocalSock);
        return -1;
    }

    /* Cycle through the list of interfaces looking for IP addresses. */
    for (i = 0lu; i < (long unsigned int)ifConf.ifc_len && j < 1; ) 
    {
        struct ifreq *pifReq = (struct ifreq *)((caddr_t)ifConf.ifc_req + i);
        i += sizeof *pifReq;
        /* See if this is the sort of interface we want to deal with. */

        memset(ifReq.ifr_name, 0, sizeof(ifReq.ifr_name));
        strncpy(ifReq.ifr_name, pifReq->ifr_name, sizeof(ifReq.ifr_name) - 1);
        if (ioctl(LocalSock, SIOCGIFFLAGS, &ifReq) < 0)
        {
            printf("Can't get interface flags for %s:\n", ifReq.ifr_name);
        }

        /* Skip loopback, point-to-point and down interfaces,
        * except don't skip down interfaces
        * if we're trying to get a list of configurable interfaces. */        
        if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP)))
        {
            continue;
        }

        if (pifReq->ifr_addr.sa_family == AF_INET) 
        {
            /* Get a pointer to the address...*/
            memcpy(&LocalAddr, &pifReq->ifr_addr, sizeof pifReq->ifr_addr);
            /* We don't want the loopback interface. */
            if (LocalAddr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) 
            {
                continue;
            }
        }
        /* increment j if we found an address which is not loopback
        * and is up */
        j++;
    }
    close(LocalSock);

    p = inet_ntoa(LocalAddr.sin_addr);  
    if (p) 
    {
        strncpy(out, p, out_len);
    } 
    else
    {
        printf("getlocalhostname: inet_ntop returned error\n" );
        return -1;
    }

    printf("Inside getlocalhostname: after strncpy %s\n", out);

    return 0;
}

关于struct ifreq里的ifr_flags变量设定,可以参考以下链接
SIOCGIFFLAGS, SIOCSIFFLAGS

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值