之前有写过一篇文章,介绍如何在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