背景:
我正在编写一个进行TCP/IP连接的守护进程.它将在具有多个(非环回)IP地址的计算机上运行.我希望用户能够在守护程序的配置文件中指定用于传出连接的IP地址,或者*
使用all.
地址将用于轮换,每个连接从最近最少使用的IP地址发出.这种行为很重要,因为它*
是"all"的替代品,因此在多台机器上运行的守护程序可以指向文件共享上的相同配置文件,并且每个守护程序都使用自己的一组IP地址.
问题:
如何获取机器可以传出(即连接到任何其他计算机)连接的所有IP地址列表?给定所有IP地址的列表,我将如何过滤掉环回地址?
我在C中,如果可能的话我只想使用POSIX,但守护进程可能只能在Linux机器上运行,所以我接受一个以Linux为中心的答案.
每个IP地址都可以在一个(可能是虚拟的)网络设备上使用,反之亦然,因此枚举网络设备和获取相关IP地址的方法也足够了,尽管我对此并不满意.(附带问题:甚至可以将多个IP地址与单个设备关联起来吗?多个设备下的相同IP怎么样?不重要.)
解决方案不足:
gethostname()
/ gethostbyname()
(作为这个问题).使用该方法,我只返回127.0.0.1(或Debian中的.1.1).我怀疑这是因为机器的主机名在hosts
文件中,这就是gethostbyname()
检查.(我相信这就是为什么在Debian中我总是得到127.0.1.1:Debian默认将localhost添加为127.0.0.1并将机器的主机名添加为127.0.1.1到hosts
文件中,对吧?)我想要一个忽略hosts
并给我一切的解决方案实际上在那里
我没有getaddrinfo()
比gethostname()
/ 更幸运了gethostbyname()
.它似乎受到同样问题的约束.我测试了这个,将机器的主机名和NULL
服务(端口)传递给它; 文档说传递NULL
主机名和NULL
服务是非法的,这是通过测试来支持的.不知道怎么回答机器上的所有问题,但是我愿意接受这方面的建议.
编辑:此答案显示如何从设备名称获取IP地址,但不显示如何枚举设备名称.有任何想法吗?
最后的编辑:我已经接受了卡斯基的答案给了他一个功劳,因为我指的是如何做到这一点的方向.我已经发布了自己的答案,列出了如果有其他人需要它的确切方法的源代码.
这是我使用caskey接受的答案的概念代码证明,为了后人的缘故:
#include#include #include #include #include #include #include #include #include #include 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; }
奇迹般有效!
这只能以依赖于操作系统的方式完成.您可以尝试解析'iptables'的输出,但linux 的正确答案是使用ioctl.
SIOCGIFCONF takes a struct ifconf *. The ifc_buf field points to a buffer of length ifc_len bytes, into which the kernel writes a list of type struct ifreq [].
struct ifreq在linux/if.h中有记录:
struct ifreq { #define IFHWADDRLEN 6 union { char ifrn_name[IFNAMSIZ]; /* if 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 ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; void * ifru_data; struct if_settings ifru_settings; } ifr_ifru; };
如您所见,它包含您想要的地址信息.