ZyLiu's Blog

IP地址与mac地址获取小结

字数统计: 1.3k阅读时长: 7 min
2018/11/10 Share

Linux平台获取IP地址

首先是使用ioctl获取IP地址的方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <vector>

using namespace std;


void getIPAddr(vector<string>& ipAddrs) {
int fd;
int interfaceNum = 0;
struct ifreq buf[16];
struct ifconf ifc;
struct ifreq ifrcopy;
char ip[32] = {0};

if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
close(fd);
return;
}

ifc.ifc_len = sizeof(buf);
ifc.ifc_req = buf;
if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc)) {
interfaceNum = ifc.ifc_len / sizeof(struct ifreq);
while (interfaceNum-- > 0) {
ifrcopy = buf[interfaceNum];
if (ioctl(fd, SIOCGIFFLAGS, &ifrcopy)) {
printf("ioctl: %s [%s:%d]\n", strerror(errno), __FILE__, __LINE__);
close(fd);
return;
}

//get the IP of this interface
if (!ioctl(fd, SIOCGIFADDR, (char *)&buf[interfaceNum])) {
snprintf(ip, sizeof(ip), "%s", (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_addr))->sin_addr));
ipAddrs.push_back(string(ip));
}
else {
printf("ioctl: %s [%s:%d]\n", strerror(errno), __FILE__, __LINE__);
close(fd);
return;
}
}
}
else {
printf("ioctl: %s [%s:%d]\n", strerror(errno), __FILE__, __LINE__);
close(fd);
return;
}

close(fd);
}

int main() {
vector<string> iplist;
getIPAddr(iplist);

cout << "get " << iplist.size() << " ip." << endl;

int ipnum = iplist.size();

for (int i = 0; i < ipnum; i++) {
string ip = iplist[i];
cout << "ip" << i << ": ";
printf("%s\n", ip.c_str());
}

return 0;
}

以上代码通过ioctl获得本机的ip,核心代码为:

1
2
fd = socket(AF_INET, SOCK_DGRAM, 0);
ioctl(fd, SIOCGIFADDR, (char *)&buf[interfaceNum]);

ioctl第二个参数为SIOCGIFADDR,得到的结果就是ip地址。

Linux获得mac地址

下面是一种最常用的使用ioctl获取mac地址的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include   <stdio.h> 
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <string.h>

int main(int argc, char *argv[]) {

struct ifreq m_ifreq;
int sock;

if(argc!=2) {
printf("Usage: ethname\n ");
return 1;
}
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 2;
}

strcpy(m_ifreq.ifr_name,argv[1]);
if(ioctl(sock, SIOCGIFHWADDR, &m_ifreq) < 0) {
perror("ioctl");
return 3;
}
printf( "%02x:%02x:%02x:%02x:%02x:%02x\n",
(unsigned char)m_ifreq.ifr_hwaddr.sa_data[0],
(unsigned char)m_ifreq.ifr_hwaddr.sa_data[1],
(unsigned char)m_ifreq.ifr_hwaddr.sa_data[2],
(unsigned char)m_ifreq.ifr_hwaddr.sa_data[3],
(unsigned char)m_ifreq.ifr_hwaddr.sa_data[4],
(unsigned char)m_ifreq.ifr_hwaddr.sa_data[5]);
return 0;
}

获得mac地址的核心代码为:

1
2
sock=socket(AF_INET, SOCK_STREAM, 0);
ioctl(sock, SIOCGIFHWADDR, &m_ifreq);

获取到的mac地址放在结构体m_ifreq.ifr_hwaddr.sa_data中。

QNX平台获取mac的方法

在QNX平台中,net/if.h与linux下不同,ifreq结构体的定义中不存在ifr_hwaddr的成员,我估计ioctl的实现方式也不同,因此上述方法不能使用。

这时应该转而使用ifaddrs.h中的方法:getifaddrs。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#ifdef __linux__
#include <arpa/inet.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <net/if_dl.h>
#endif

int listmacaddrs(void) {
struct ifaddrs *ifap, *ifaptr;
unsigned char *ptr;

if (getifaddrs(&ifap) == 0) {
for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
#ifdef __linux__
char macp[INET6_ADDRSTRLEN];
if (((ifaptr)->ifa_addr)->sa_family == AF_PACKET) {
struct sockaddr_ll *s = (struct sockaddr_ll*)(ifaptr->ifa_addr);
int i;
int len = 0;
for (i = 0; i < 6; i++) {
len += sprintf(macp+len, "%02X%s", s->sll_addr[i], i < 5 ? ":":"");
}
printf("%s: %s\n", (ifaptr)->ifa_name, macp);
}
#else
if (((ifaptr)->ifa_addr)->sa_family == AF_LINK) {
ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n",
(ifaptr)->ifa_name,
*ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
}
#endif
}
freeifaddrs(ifap);
return 1;
} else {
return 0;
}
}

使用getifaddrs(&ifap)将mac地址与ip地址同时放到ifap结构体中,该结构体的类型为ifaddrs

其核心代码为:

1
2
3
4
5
6
struct ifaddrs *ifap,  *ifaptr;
getifaddrs(&ifap);
for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n", (ifaptr)->ifa_name, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
}

ifaddrs::ifa_name为网卡名,ifaddrs::ifa_addr中存放mac地址。

通过getnameinfo可获得ip地址,ip地址在host中:

1
s = getnameinfo(ifa->ifa_addr, (family == AF_INET)?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NUMERICHOST);

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
struct ifaddrs *ifaddr, *ifa;
int family, s;
char host[NI_MAXHOST];

if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
exit(EXIT_FAILURE);
}

/* Walk through linked list, maintaining head pointer so we
* can free list later */

for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL)
continue;

family = ifa->ifa_addr->sa_family;

/* Display interface name and family (including symbolic
* form of the latter for the common families) */

printf("%s address family: %d%s\n",
ifa->ifa_name, family,
(family == AF_PACKET) ? " (AF_PACKET)" :
(family == AF_INET) ? " (AF_INET)" :
(family == AF_INET6) ? " (AF_INET6)" : "");

/* For an AF_INET* interface address, display the address */

if (family == AF_INET || family == AF_INET6) {
s = getnameinfo(ifa->ifa_addr,
(family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (s != 0) {
printf("getnameinfo() failed: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
printf("\taddress: <%s>\n", host);
}
}

freeifaddrs(ifaddr);
exit(EXIT_SUCCESS);
}

必须要注意的是getifaddrs的调用必须与freeifaddrs成对出现,如果不进行内存释放,会造成内存泄漏

参考资料

Linxu的struct ifaddrs与getifaddrs()函数:https://blog.csdn.net/g457499940/article/details/13630549
怎么得到带有getifaddrs的mac地址?:https://cloud.tencent.com/developer/ask/148099
Linux下的ioctl()函数详解:https://www.cnblogs.com/tdyizhen1314/p/4896689.html

CATALOG
  1. 1. Linux平台获取IP地址
  2. 2. Linux获得mac地址
  3. 3. QNX平台获取mac的方法
  4. 4. 参考资料