背景:
我正在编写一个建立传出 TCP/IP 连接的守护程序。它将在具有多个(非环回)IP 地址的计算机上运行。我希望用户能够在守护程序的配置文件中指定用于传出连接的 IP 地址,或者*
使用全部。
这些地址将轮流使用,每个连接都从最近最少使用的 IP 地址发出。这种行为很重要,因为*
作为“all”的替代,因此在多台计算机上运行的守护程序可以指向文件共享上的同一个配置文件,并且每个守护程序都使用自己的一组 IP 地址。
Problem:
如何获取计算机可以进行传出(即到任何其他计算机)连接的所有 IP 地址的列表?给定所有 IP 地址的列表,我如何过滤掉环回地址?
我使用 C,如果可能的话,我只想使用 POSIX,但守护进程可能只在 Linux 机器上运行,所以我接受以 Linux 为中心的答案。
每个 IP 地址将在一个(可能是虚拟的)网络设备上可用,反之亦然,因此一种枚举网络设备并获取关联 IP 地址的方法也足够了,尽管我对此并不满意。 (附带问题:是否有可能将多个 IP 地址与单个设备相关联?多个设备下的同一个 IP 怎么样?并不重要。)
解决方案不足:
-
gethostname()
/gethostbyname()
(as 这个问题)。使用该方法,我只能返回 127.0.0.1(或 Debian 中的 .1.1)。我怀疑这是因为机器的主机名位于hosts
文件,就这样gethostbyname()
检查。 (我相信这就是为什么在 Debian 中我总是得到 127.0.1.1:Debian 默认将 localhost 添加为 127.0.0.1,将机器的主机名添加为 127.0.1.1hosts
文件,对吧?)我想要一个忽略的解决方案hosts
并给了我实际存在的一切。
- 我再也没有运气了
getaddrinfo()
than gethostname()
/gethostbyname()
。它似乎受到同样的问题的束缚。我测试了这个传递机器的主机名和NULL
服务(端口)进入其中;文档说通过NULL
主机名和NULL
服务是非法的,这是有测试支持的。不知道如何向它询问机器上的所有内容,但我愿意接受这方面的建议。
- EDIT: 这个答案显示如何从设备名称获取 IP 地址,但不显示如何枚举设备名称。有任何想法吗?
最终编辑:我已经接受卡斯基的回答感谢他为我指明了如何完成这件事的方向。我已经发布了我的自己的答案列出了具体如何执行此操作的源代码,以防其他人需要它。
这是我使用的概念验证代码卡斯基接受的答案,为了后代:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static const char * flags(int sd, const char * name)
{
static char buf[1024];
static struct ifreq ifreq;
strcpy(ifreq.ifr_name, name);
int r = ioctl(sd, SIOCGIFFLAGS, (char *)&ifreq);
assert(r == 0);
int l = 0;
#define FLAG(b) if(ifreq.ifr_flags & b) l += snprintf(buf + l, sizeof(buf) - l, #b " ")
FLAG(IFF_UP);
FLAG(IFF_BROADCAST);
FLAG(IFF_DEBUG);
FLAG(IFF_LOOPBACK);
FLAG(IFF_POINTOPOINT);
FLAG(IFF_RUNNING);
FLAG(IFF_NOARP);
FLAG(IFF_PROMISC);
FLAG(IFF_NOTRAILERS);
FLAG(IFF_ALLMULTI);
FLAG(IFF_MASTER);
FLAG(IFF_SLAVE);
FLAG(IFF_MULTICAST);
FLAG(IFF_PORTSEL);
FLAG(IFF_AUTOMEDIA);
FLAG(IFF_DYNAMIC);
#undef FLAG
return buf;
}
int main(void)
{
static struct ifreq ifreqs[32];
struct ifconf ifconf;
memset(&ifconf, 0, sizeof(ifconf));
ifconf.ifc_req = ifreqs;
ifconf.ifc_len = sizeof(ifreqs);
int sd = socket(PF_INET, SOCK_STREAM, 0);
assert(sd >= 0);
int r = ioctl(sd, SIOCGIFCONF, (char *)&ifconf);
assert(r == 0);
for(int i = 0; i < ifconf.ifc_len/sizeof(struct ifreq); ++i)
{
printf("%s: %s\n", ifreqs[i].ifr_name, inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr));
printf(" flags: %s\n", flags(sd, ifreqs[i].ifr_name));
}
close(sd);
return 0;
}
奇迹般有效!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)