c语言如何输出ipv6header

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c语言如何输出ipv6header相关的知识,希望对你有一定的参考价值。

从文件/proc/net/if_inet6中获取ipv6地址

我们先来看看文件/proc/net/if_inet6中有什么内容:
图2:文件/proc/net/if_inet6内容
这个文件中,每行为一个网络接口的数据,每行数据分成 6 个字段
序号
字段名称
字段说明
1
ipv6address
ipv6地址,16位(4个字符)一组,16进制,中间没有分隔符
2
ifindex
接口设备号,每个设备不同
3
prefixlen
前缀长度,类似子网掩码
4
scopeid
scope id
5
flags
接口标志,标识这个接口的特性
6
devname
接口设备名称
所以从这个文件中可以很容易地获得所有接口的 ipv6 地址:
#include <stdio.h>
#include <linux/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void)
FILE *f;
int scope, prefix;
unsigned char _ipv6[16];
char dname[IFNAMSIZ];
char address[INET6_ADDRSTRLEN];

f = fopen("/proc/net/if_inet6", "r");
if (f == NULL)
return -1;

while (19 == fscanf(f,
" %2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx %*x %x %x %*x %s",
&_ipv6[0], &_ipv6[1], &_ipv6[2], &_ipv6[3], &_ipv6[4], &_ipv6[5], &_ipv6[6], &_ipv6[7],
&_ipv6[8], &_ipv6[9], &_ipv6[10], &_ipv6[11], &_ipv6[12], &_ipv6[13], &_ipv6[14], &_ipv6[15],
&prefix, &scope, dname))
if (inet_ntop(AF_INET6, _ipv6, address, sizeof(address)) == NULL)
continue;

printf("%s: %s\n", dname, address);

fclose(f);

return 0;

fscanf中的%2hhx是一种不多见的用法,hhx表示后面的指针&_ipv6[x]指向一个unsigned char *,2表示从文件中读取的长度,这个是常用的;
关于fscanf中的hh和h的用法,可以查看在线手册man fscanf了解更多的内容;
ipv6地址一共128位,16位一组,一共8组,但是这里为什么不一次从文件中读入4个字符(16 位),读8次,而要一次读入2个字符读16次呢?
这个要去看inet_ntop的参数,我们先使用命令man inet_ntop看一下inet_ntop的在线手册:

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
当第1个参数af=AF_INET6时,对于第2个参数,还有说明:

AF_INET6
src points to a struct in6_addr (in network byte order) which is converted to a representation of
this address in the most appropriate IPv6 network address format for this address. The buffer dst
must be at least INET6_ADDRSTRLEN bytes long.
很显然,需要第2个参数指向一个struct in6_addr,这个结构在netinet/in.h中定义:

/* IPv6 address */
struct in6_addr

union

uint8_t __u6_addr8[16];
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
__in6_u;
#define s6_addr __in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
;
这个结构在一般情况下使用的是uint8_t __u6_addr8[16],也就是16个unsigned char的数组,只有在"混杂模式"时才使用8个unsigned short int或者4个unsigned int的数组;

所以,实际上struct in6_addr的结构如下:

struct in6_addr
unsigned char __u6_addr8[16];

这就是我们在读文件时为什么要一次读入2个字符,读16次,要保证读出的内容符合struct in6_addr的定义;

一次从文件中读入4个字符(16位),读8次,和一次读入2个字符读16次有什么不同呢?我们以16进制的f8e9为例;
当我们每次读入 2 个字符,读 2 次时,在内存中的排列是这样的:

unsigned char _ipv6[16];
fscanf(f, "%2hhx2hhx", &_ipv6[0], &_ipv6[1]);
unsigned char *p = _ipv6
f8 e9
-+ -+
| |
| +------- p + 1
+----------- p
当我们每次读入 4 个字符,读 1 次时,在内存中的排列是这样的:

unsigned int _ipv6[8]
fscanf(f, "%4x", &_ipv6[0])
unsigned char *p = (unsigned char *)_ipv6
e9 f8
-+ -+
| |
| +------- p + 1
+----------- p
这是因为X86系列CPU的存储模式是小端模式,也就是高位字节要存放在高地址上,f8e9这个数,f8是高位字节,e9是低位字节,所以当我们把f8e9作为一个整数读出的时候,e9 将存储在低地址,f8存储在高地址,这和struct in6_addr的定义是不相符的;

所以如果我们一次读4个字符,读8次,我们就不能使用inet_ntop()去把ipv6地址转换成我们所需要的字符串,当然我们可以自己转换,但有些麻烦,参考下面代码:

unsigned short int _ipv6[8];
int zero_flag = 0;
while (11 == fscanf(f,
" %4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx %*x %x %x %*x %s",
&_ipv6[0], &_ipv6[1], &_ipv6[2], &_ipv6[3], &_ipv6[4], &_ipv6[5], &_ipv6[6], &_ipv6[7],
&prefix, &scope, dname))
printf("%s: ", dname);
for (int i = 0; i < 8; ++i)
if (_ipv6[i] != 0)
if (i) putc(':', stdout);
printf("%x", _ipv6[i]);
zero_flag = 0;
else
if (!zero_flag) putc(':', stdout);
zero_flag = 1;


putc('\n', stdout);

和上面的代码比较,多了不少麻烦,自己去体会吧。
参考技术A 写个for循环

以上是关于c语言如何输出ipv6header的主要内容,如果未能解决你的问题,请参考以下文章

C语言输出如何右对齐?

C语言中如何对齐输出

c语言如何输出彩色字

如何将C语言的输出倒入Excel工作表

C语言中如何实现输入输出多组数据,该如何结束输入

请问在c语言中如何输出百分数?