COMMENTS

  • james@rounter: Hey okay thx. :)
  • james@router: Does my mac adresse change if I upgrade my computer with some...
  • Firm: 这方面没接触过 8-O
  • ontogma: итак: восхитительно… Киев лучшие секс знакомства...
  • 一路阳光: 现在再看刚毕业时的简历肯定感觉有点心虚,当年要不是初生牛犊估 计也没有那份胆量写那样的简历了
  • licream: 我也离了。0 0。不过还没找上。郁闷哦
  • Firm: 公司面试都喜欢考链表 确实,俺上次也考这个了
  • bokit: 今天第一天开工,祝心想事成,顺顺利利 PS:恭喜博主,新的一年新的开始。

ioctl及getifaddrs读取IPV4,IPV6网卡信息

使用ioctl的SIOCGIFCONF可以读取所有网卡信息。ioctl调用后返回指向ifconf的结构链表,其中包含了指向ifreq的结构指针。ifconf及ifreq定义在net/if.h中。

《UNIX网络编程》中提供了get_ifi_info函数的实现方法,使用这种方式来获取网络信息。在LINUX下,这种方式不能获得IPV6的网卡信息。《UNIX网络编程》中有如下描述:

在支持IPV6的系统中,没有关于对SIOCGIFCONF请求是否返回IPV6地址的标准。我们给支持IPV6的新系统增加了一个case语句, 这是为了预防万一。问题在于ifreq中的联合把返回的地址定义成一个通用的16字节套接口地址结构,适合16字节的IPV4 socket_in结构,但对于24字节的IPV6 socket_in6结构太小了。如果返回IPV6地址,将可能破环现有的在每个ifreq结构中采用固定大小的套接口地址结构的代码。

经测试,在fedor6-2.6.18kernel中无法返回ipv6地址,事实上,返回的地址簇总是AF_INET,而并非AF_INET6。
这种方法的实现代码如下:

net_if.h

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
#ifndef __NET_INF_H
#define __NET_INF_H
 
#include <net/if.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
#define IFI_NAME 16
#define IFI_HADDR 8
 
typedef struct ifi_info
{
  char ifi_name[IFI_NAME];
  u_char ifi_haddr[IFI_HADDR];
  u_short ifi_hlen;
  short ifi_flags;
  short ifi_myflags;
  struct sockaddr *ifi_addr;
  struct sockaddr *ifi_brdaddr;
  struct sockaddr *ifi_dstaddr;
  struct ifi_info *ifi_next;
}ifi_info;
 
#define IFI_ALIAS 1
 
struct ifi_info *get_ifi_info(int, int);
 
void free_ifi_info(struct ifi_info *);
 
#endif

net_if.c

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#include "net_if.h"
 
ifi_info *get_ifi_info(int family, int doaliases)
{
  ifi_info *ifi, *ifihead, **ifipnext;
  int sockfd, len, lastlen, flags, myflags;
  char *ptr, *buf, lastname[IFNAMSIZ], *cptr;
  struct ifconf ifc;
  struct ifreq *ifr, ifrcopy;
  struct sockaddr_in *sinptr;
 
  if ((sockfd=socket(family, SOCK_DGRAM, 0))<0)
  {
    printf("socket error.\n");
    exit(1);
  }
 
  lastlen = 0;
 
  len = 10*sizeof(struct ifreq);
  while (1)
  {
    buf = (char*)malloc(len);
    ifc.ifc_len = len;
    ifc.ifc_buf = buf;
    if (ioctl(sockfd, SIOCGIFCONF, &ifc)<0)
    {
      if (errno!=EINVAL||lastlen!=0)
      {
        printf("ioctl error.\n");
      }
    }
    else
    {
      if (ifc.ifc_len == lastlen)
        break;
      lastlen = ifc.ifc_len;
    }
    len += 10*sizeof(struct ifreq);
    free(buf);
  }
 
  ifihead = NULL;
  ifipnext = &ifihead;
  lastname[0] = 0;
 
  for (ptr = buf; ptr<buf+ifc.ifc_len;)
  {
    ifr = (struct ifreq*)ptr;
#ifdef HAVE_SOCKADDR_SA_LEN
    len = sizeof(struct sockaddr)>ifr->ifr_addr.sa_len?sizeof(struct sockaddr):ifr->ifr_addr.sa_len;
#else
    switch (ifr->ifr_addr.sa_family)
    {
#ifdef IPV6
    case AF_INET6:
      len = sizeof(struct sockaddr_in6);
      break;
#endif
    case AF_INET:
    default:
      len = sizeof(struct sockaddr);
      break;
    }
#endif
 
    ptr += sizeof(ifr->ifr_name) + len;
 
    if (ifr->ifr_addr.sa_family != family)
      continue;
 
    myflags = 0;
    if ((cptr=strchr(ifr->ifr_name, ':'))!=NULL)
      *cptr = 0;
    if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ)==0)
    {
      if (doaliases == 0)
        continue;
      myflags = IFI_ALIAS;
    }
 
    memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
 
    ifrcopy = *ifr;
    ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
    flags = ifrcopy.ifr_flags;
    if ((flags&IFF_UP)==0)
      continue;
    /*
    if ((flags&IFF_BROADCAST)==0)
      continue;
    */
    ifi = calloc(1, sizeof(struct ifi_info));
    *ifipnext = ifi;
    ifipnext = &ifi->ifi_next;
    ifi->ifi_flags = flags;
    ifi->ifi_myflags = myflags;
    memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
    ifi->ifi_name[IFI_NAME-1] = '\0';
 
    switch (ifr->ifr_addr.sa_family)
    {
    case AF_INET:
      sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
      if (ifi->ifi_addr == NULL)
      {
        ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
        memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
#ifdef SIOCGIFBRDADDR
        if (flags & IFF_BROADCAST)
        {
          ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
          sinptr = (struct sockaddr_in *)&ifrcopy.ifr_broadaddr;
          ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
          memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
        }
#endif
#ifdef SIOCGIFDSTADDR
        if (flags & IFF_POINTOPOINT)
        {
          ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
          sinptr = (struct sockaddr_in*)&ifrcopy.ifr_dstaddr;
          ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in));
          memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
        }
#endif
      }
      break;
    default:
      break;
    }
  }
  free(buf);
  return(ifihead);
}
 
void free_ifi_info(ifi_info *ifihead)
{
  ifi_info *ifi, *ifinext;
  for (ifi=ifihead; ifi!=NULL; ifi=ifinext)
  {
    if (ifi->ifi_addr!=NULL)
      free(ifi->ifi_addr);
 
    if (ifi->ifi_brdaddr!=NULL)
      free(ifi->ifi_brdaddr);
    if (ifi->ifi_dstaddr!=NULL)
      free(ifi->ifi_dstaddr);
    ifinext = ifi->ifi_next;
 
    free(ifi);
  }
 
}
 
char *sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
  char portstr[7];
  static char str[128];
 
  switch (sa->sa_family)
  {
  case AF_INET:
    {
      struct sockaddr_in *sin = (struct sockaddr_in *)sa;
 
      if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))==NULL)
        return NULL;
 
      if (ntohs(sin->sin_port)!=0)
      {
        snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin_port));
        strcat(str, portstr);
      }
      return str;
    }
    break;
  case AF_INET6:
    {
      struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa;
 
      if (inet_ntop(AF_INET6, &sin->sin6_addr, str, sizeof(str))==NULL)
        return NULL;
 
      if (ntohs(sin->sin6_port)!=0)
      {
        snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin6_port));
        strcat(str, portstr);
      }
      return str;
    }
    break;
  default:
    return NULL;
    break;
  }
 
 
}
 
int main(int argc, char *argv[])
{
  ifi_info *ifi, *ifihead;
  struct sockaddr *sa;
  u_char *ptr;
  int i, family, doaliases;
  if (argc!=3)
  {
    printf("usage: ./prifinfo <inet4 | inet 6> <doaliases>");
    exit(1);
  }
 
  if (strcmp(argv[1], "inet4") == 0)
    family = AF_INET;
#ifdef IPV6
  else if (strcmp(argv[1], "inet6") == 0)
    family =AF_INET6;
#endif
  else
  {
    printf("invalid <address-family>");
    exit(1);
  }
 
  doaliases = atoi(argv[2]);
 
  for(ifihead = ifi = get_ifi_info(family, doaliases);
      ifi!=NULL;ifi=ifi->ifi_next)
  {
    printf("%s:<", ifi->ifi_name);
    if (ifi->ifi_flags&IFF_UP) printf("UP");
    if (ifi->ifi_flags&IFF_BROADCAST) printf("BCAST");
    if (ifi->ifi_flags&IFF_MULTICAST) printf("MCAST");
    if (ifi->ifi_flags&IFF_LOOPBACK) printf("LOOP");
    if (ifi->ifi_flags&IFF_POINTOPOINT) printf("P2P");
    printf(">\n");
 
    if ((i=ifi->ifi_hlen)>0)
    {
      ptr = ifi->ifi_haddr;
      do
      {
        printf("%s%x", (i==ifi->ifi_hlen)?" ":":", *ptr++);
      }while(--i>0);
 
      printf("\n");
    }
 
    if ((sa=ifi->ifi_addr)!=NULL)
      printf(" IP addr: %s\n",
             sock_ntop(sa, sizeof(*sa)));
    if ((sa=ifi->ifi_brdaddr)!=NULL)
      printf(" broadcast addr: %s\n",
             sock_ntop(sa, sizeof(*sa)));
    if ((sa=ifi->ifi_dstaddr)!=NULL)
      printf(" destnation addr: %s\n",
             sock_ntop(sa, sizeof(*sa)));
  }
 
  free_ifi_info(ifihead);
 
  exit(0);
}

使用gcc net_if.c -o net_if -DIPV6编译,在IPV4模式下运行输出为:

[root@localhost net_if]./net_if inet4 1
lo:
IP addr: 127.0.0.1
eth1:
IP addr: 192.168.1.2
broadcast addr: 192.168.1.255
eth0:
IP addr: 192.168.125.99
broadcast addr: 192.168.125.255

执行./net_if inet6 1在输出为空。

第二种方式是使用getifaddrs函数获取,需要包含ifaddrs.h头文件,这种方式可以获得IPV6地址,改写的《UNIX网络编程》中的get_ifi_info函数如下所示:

znet.h

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
#ifndef __ZNET_H__
#define __ZNET_H__
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <ifaddrs.h>
 
#define	IFI_NAME	16			/* same as IFNAMSIZ in <net/if.h> */
#define	IFI_HADDR	 8			/* allow for 64-bit EUI-64 in future */
 
struct ifi_info {
  char    ifi_name[IFI_NAME];	/* interface name, null-terminated */
  short   ifi_index;			/* interface index */
  short   ifi_flags;			/* IFF_xxx constants from <net/if.h> */
  struct sockaddr  *ifi_addr;	/* primary address */
  struct sockaddr  *ifi_brdaddr;/* broadcast address */
  struct ifi_info  *ifi_next;	/* next of these structures */
};
 
struct ifi_info* get_ifi_info(int, int);
void free_ifi_info(struct ifi_info *);
 
#endif

znet.c

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include "znet.h"
 
struct ifi_info* get_ifi_info(int family, int doaliases) {
	struct ifi_info		*ifi, *ifihead, **ifipnext,*p;
	struct sockaddr_in	*sinptr;
	struct sockaddr_in6	*sin6ptr;
	struct ifaddrs *ifas;
//	char addr[128];
	int sockfd;
 
	ifihead = NULL;
	ifipnext = &ifihead;
 
	if(getifaddrs(&ifas)!=0)
		return ;
 
	for(;ifas!=NULL;ifas=(*ifas).ifa_next) {	
		if (((*ifas).ifa_addr)->sa_family != family)
			continue;	// ignore if not desired address family
/*
		printf("%s %d\n",(*ifas).ifa_name,((*ifas).ifa_addr)->sa_family);
		if(((*ifas).ifa_addr)->sa_family!=AF_INET6)
			inet_ntop(AF_INET,&(((struct sockaddr_in *)((*ifas).ifa_addr))->sin_addr),addr,sizeof(addr));
		else
			inet_ntop(AF_INET6,&(((struct sockaddr_in6 *)((*ifas).ifa_addr))->sin6_addr),addr,sizeof(addr));
		printf("%s\t",addr);
		printf("\n");
*/		
		ifi = (struct ifi_info*)calloc(1,sizeof(struct ifi_info));
		*ifipnext = ifi;			
		ifipnext = &ifi->ifi_next;	
 
		ifi->ifi_flags = (*ifas).ifa_flags;
		memcpy(ifi->ifi_name, (*ifas).ifa_name, IFI_NAME);
		ifi->ifi_name[IFI_NAME-1] = '\0';
 
		switch (((*ifas).ifa_addr)->sa_family) {
			case AF_INET: 
				sinptr = (struct sockaddr_in *) (*ifas).ifa_addr;
				ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
				memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));			
#ifdef	SIOCGIFBRDADDR
				if (ifi->ifi_flags & IFF_BROADCAST) {
					sinptr = (struct sockaddr_in *) (*ifas).ifa_broadaddr;
					ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
					memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
				}
#endif
				break; 
 
			case AF_INET6: 
				sin6ptr = (struct sockaddr_in6 *) (*ifas).ifa_addr;
				ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6));
				memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6));
				break; 
 
			default:
				break;
		}
	}
	freeifaddrs(ifas);	
 
	return(ifihead);	
}
 
int main(int argc, char *argv[]) {
	int family;
	if (argc!=2) {
	    printf("usage: ./znet <inet4 | inet 6>\n");
	    exit(1);
	}
	if (strcmp(argv[1], "inet4") == 0)
		family = AF_INET;
	else if (strcmp(argv[1], "inet6") == 0)
		family =AF_INET6;
	else {
    printf("invalid <address-family>\n");
    exit(1);
    	}
 
	char addr[128];
	struct ifi_info	*ifi, *ifihead;
	printf("name\tflag\tIP\t\tbroadcastaddr\n");
	for (ifihead = ifi = get_ifi_info(family,1); ifi != NULL; ifi = ifi->ifi_next) {
 
		printf("%s\t",ifi->ifi_name); 
		printf("%d\t",ifi->ifi_flags);
		if((ifi->ifi_addr)->sa_family!=AF_INET6)
			inet_ntop(AF_INET,&(((struct sockaddr_in *)(ifi->ifi_addr))->sin_addr),addr,sizeof(addr));
		else
			inet_ntop(AF_INET6,&(((struct sockaddr_in6 *)(ifi->ifi_addr))->sin6_addr),addr,sizeof(addr));
		printf("%s\t",addr);
#ifdef	SIOCGIFBRDADDR
		if ((ifi->ifi_flags & IFF_BROADCAST) && (ifi->ifi_addr)->sa_family!=AF_INET6) {
			inet_ntop(AF_INET,&(((struct sockaddr_in *) (ifi->ifi_brdaddr))->sin_addr),addr,sizeof(addr));
			printf("%s\t",addr);
		}
#endif			
		printf("\n+++++++++++++++++++++++++++++++++++++++++++\n");
	}
	return 0;
}

这段代码输出如下:

[root@localhost net_if]./znet inet4
name flag IP broadcastaddr
lo 73 127.0.0.1
++++++++++++++++++++++++++++++
eth1 4099 192.168.1.2 192.168.1.255
++++++++++++++++++++++++++++++
eth0 4163 192.168.125.99 192.168.125.255
++++++++++++++++++++++++++++++
[root@localhost net_if]./znet inet6
name flag IP broadcastaddr
lo 73 ::1
++++++++++++++++++++++++++++++
eth1 4163 2001:250:1800:1::1
++++++++++++++++++++++++++++++
eth0 4163 2001:250:1888:1::1
++++++++++++++++++++++++++++++

相关文章:
  1. 使用原始套接字SOCK_RAW捕捉网络数据包并简单分析
  2. 自写一则单链表小程序
  3. 一种IP改变唤醒SNMP TRAP的实现源码
  4. 使用setitimer和signal创建一个计时器
  5. Linux多线程(1)

Leave a Reply

 

 

 

:wink: :twisted: :roll: :oops: :mrgreen: :lol: :idea: :evil: :cry: :arrow: :?: :-| :-x :-o :-P :-D :-? :) :( :!: 8-O 8)