Linux C 获取本机IPV4和IPV6地址列表
有时候设备网卡上有多个IPv6,其中只有一个是可用的,另外一个是内网地址,无法使用,如果程序需要绑定一个V6地址的时候,需要获取网卡上的V6地址,并且要求是可用的。
通过ifconfig可用看到,eth0网卡上有2个IP地址,其中只有第一个V6地址的Scope
为Global
:
eth0 Link encap:Ethernet HWaddr 52:54:00:1D:79:D1
inet addr:192.168.121.120 Bcast:192.168.121.255 Mask:255.255.255.0
inet6 addr: 2001:250:250:250:250:250:250:222/64 Scope:Global
inet6 addr: fe80::5054:ff:fe1d:79d1/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:5999959 errors:0 dropped:0 overruns:0 frame:0
TX packets:104610 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:317521212 (302.8 MiB) TX bytes:8564391 (8.1 MiB)
那么如何用C获取IPv6地址,并且过滤其中Scope为Global的地址:
#define _GNU_SOURCE # required for NI_NUMERICHOST
#include <arpa/inet.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <netdb.h>
int main ()
{
struct ifaddrs *ifap, *ifa;
struct sockaddr_in6 *sa;
struct sockaddr_in *sa4;
char addr[INET6_ADDRSTRLEN];
char addr4[INET_ADDRSTRLEN];
getifaddrs (&ifap);
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family==AF_INET6) {
sa = (struct sockaddr_in6 *) ifa->ifa_addr;
getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr,
sizeof(addr), NULL, 0, NI_NUMERICHOST);
printf("Interface: %5s\tAddress: %s, scope: ", ifa->ifa_name, addr);
// struct sockaddr_in6 sa;
if (IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
printf ("link-local ");
}
if (IN6_IS_ADDR_SITELOCAL(&sa->sin6_addr)) {
printf ("site-local ");
}
if (IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr)) {
printf ("v4mapped ");
}
if (IN6_IS_ADDR_V4COMPAT(&sa->sin6_addr)) {
printf ("v4compat ");
}
if (IN6_IS_ADDR_LOOPBACK(&sa->sin6_addr)) {
printf ("host ");
}
if (IN6_IS_ADDR_UNSPECIFIED(&sa->sin6_addr)) {
printf ("unspecified ");
}
if (IN6_IS_ADDR_MC_GLOBAL(&sa->sin6_addr)){
printf ("global ");
}
printf("\n");
} else if(ifa->ifa_addr->sa_family == AF_INET){
sa4 = (struct sockaddr_in *)ifa->ifa_addr;
getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), addr4,
sizeof(addr4), NULL, 0, NI_NUMERICHOST);
/*inet_ntop(AF_INET, sa4, addr4, INET_ADDRSTRLEN);*/
printf("Interface: %5s\tAddress: %s\n", ifa->ifa_name, addr4);
}
}
freeifaddrs(ifap);
return 0;
}
编译下:
gcc -o ipa ipa.c
运行输出:
# ./ipa
Interface: lo Address: 127.0.0.1
Interface: eth0 Address: 192.168.121.120
Interface: eth1 Address: 10.24.0.114
Interface: lo Address: ::1, scope: host
Interface: eth0 Address: 2001:250:250:250:250:250:250:222, scope:
Interface: eth0 Address: fe80::5054:ff:fe1d:79d1%eth0, scope: link-local
Interface: eth1 Address: fe80::5054:ff:fedb:bdd7%eth1, scope: link-local
啊,可见IN6_IS_ADDR_MC_GLOBAL
无法判断global的地址,我们只能用排除法了,将IN6_IS_ADDR_MC_GLOBAL
判断修改为如下代码:
// if (IN6_IS_ADDR_MC_GLOBAL(&sa->sin6_addr)){
// printf ("global ");
// }
if (!(IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) && !(IN6_IS_ADDR_SITELOCAL(&sa->sin6_addr)) &&
!(IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr)) && !(IN6_IS_ADDR_LOOPBACK(&sa->sin6_addr)) &&
!(IN6_IS_ADDR_UNSPECIFIED(&sa->sin6_addr))
) {
printf ("global ");
}
重新编译输出:
# ./ipa
Interface: lo Address: 127.0.0.1
Interface: eth0 Address: 192.168.121.120
Interface: eth1 Address: 10.24.0.114
Interface: lo Address: ::1, scope: host
Interface: eth0 Address: 2001:250:250:250:250:250:250:222, scope: global
Interface: eth0 Address: fe80::5054:ff:fe1d:79d1%eth0, scope: link-local
Interface: eth1 Address: fe80::5054:ff:fedb:bdd7%eth1, scope: link-local