pktgen有很多函数可以作为很好的网络相关的工具函数,这里列出ipv6中1:0:0:0:0:0:0:1和1::1这两种地址形式相互转化的工具函数。
第一个函数,用于把一个1:0:0:0:0:0:0:1形式的地址转化为1::1
/* * scan_ip6, fmt_ip taken from dietlibc-0.21 * Author Felix von Leitner <felix-dietlibc@fefe.de> * * Slightly modified for kernel. * Should be candidate for net/ipv4/utils.c * --ro */ static unsigned int scan_ip6(const char *s, char ip[16]) { unsigned int i; unsigned int len = 0; unsigned long u; char suffix[16]; unsigned int prefixlen = 0; unsigned int suffixlen = 0; __be32 tmp; char *pos; for (i = 0; i < 16; i++) ip[i] = 0; for (;;) { if (*s == ':') { len++; if (s[1] == ':') { /* Found "::", skip to part 2 */ s += 2; len++; break; } s++; } u = simple_strtoul(s, &pos, 16); i = pos - s; if (!i) return 0; if (prefixlen == 12 && s[i] == '.') { /* the last 4 bytes may be written as IPv4 address */ tmp = in_aton(s); memcpy((struct in_addr *)(ip + 12), &tmp, sizeof(tmp)); return i + len; } ip[prefixlen++] = (u >> 8); ip[prefixlen++] = (u & 255); s += i; len += i; if (prefixlen == 16) return len; } /* part 2, after "::" */ for (;;) { if (*s == ':') { if (suffixlen == 0) break; s++; len++; } else if (suffixlen != 0) break; u = simple_strtol(s, &pos, 16); i = pos - s; if (!i) { if (*s) len--; break; } if (suffixlen + prefixlen <= 12 && s[i] == '.') { tmp = in_aton(s); memcpy((struct in_addr *)(suffix + suffixlen), &tmp, sizeof(tmp)); suffixlen += 4; len += strlen(s); break; } suffix[suffixlen++] = (u >> 8); suffix[suffixlen++] = (u & 255); s += i; len += i; if (prefixlen + suffixlen == 16) break; } for (i = 0; i < suffixlen; i++) ip[16 - suffixlen + i] = suffix[i]; return len; }
第二个函数完成反向的转换,把1::1恢复成完成的地址:
static unsigned int fmt_ip6(char *s, const char ip[16]) { unsigned int len; unsigned int i; unsigned int temp; unsigned int compressing; int j; len = 0; compressing = 0; for (j = 0; j < 16; j += 2) { #ifdef V4MAPPEDPREFIX if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) { inet_ntoa_r(*(struct in_addr *)(ip + 12), s); temp = strlen(s); return len + temp; } #endif temp = ((unsigned long)(unsigned char)ip[j] << 8) + (unsigned long)(unsigned char)ip[j + 1]; if (temp == 0) { if (!compressing) { compressing = 1; if (j == 0) { *s++ = ':'; ++len; } } } else { if (compressing) { compressing = 0; *s++ = ':'; ++len; } i = fmt_xlong(s, temp); len += i; s += i; if (j < 14) { *s++ = ':'; ++len; } } } if (compressing) { *s++ = ':'; ++len; } *s = 0; return len; }
下面附上ipv6的一些相关的基础知识,看懂这些也更有助于理解上面的两个函数,ps:这好像是我之前在一些网站上截的,我当时也记了源地址,但是源网站已经挂掉了:
IPv4地址是类似 A.B.C.D 的格式,它是32位,用.分成四段,用10进制表示;而IPv6地址类似X:X:X:X:X:X:X:X的格式,它是128位的,用:分 成8段,用16进制表示,每一段的值从8位扩充到16位。
RFC2373 中定义了完整的IPv6地址表示:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
例如: 2001:0000: 1F 1F :0000:0000:0100: 11A 0:ADDF
为简化表示, rfc2373提出每段中前面的0可以省略,连续的0可省略为::,但只能出现一次。例如:
1080:0:0:0:8:800: 200C : 417A 可简写为 1080::8:800: 200C : 417A
FF01:0:0:0:0:0:0:101 可简写为 FF01::101
0:0:0:0:0:0:0:1 可简写为 ::1
0:0:0:0:0:0:0:0 可简写为 ::
这里给出一个表格,以比较IPv4和IPv6地址对应关系和区别。
IPv4 |
IPv6 |
组播地址(224.0.0.0/4) |
IPv6组播地址(FF00::/8) |
有广播地址 |
无广播,只有任播(anycast) |
未指定地址为 0.0.0 .0 |
未指定地址为 :: |
回路地址为 127.0.0.1 |
回路地址为 ::1 |
私有地址( 10.0.0 .0/8、172.16.0.0/12和192.168.0.0/16) |
本地站点地址( FEC0::/48) |
Microsoft自动专用IP寻址自动配置的地址(169.254.0.0/16) |
本地链路地址( FE80::/64) |
表达方式:点分十进制 |
表达方式:冒号十六进制式 |
子网掩码表示:以点阵十进制表示法或前缀长度表示法( CIDR) |
子网掩码表示:仅使用前缀长度表示法( CIDR) |
常见的IPv6地址及其前缀:
::/128 即0:0:0:0:0:0:0:0,只能作为尚未获得正式地址的主机的源地址,不能作为目的地址,不能分配给真实的网络接口。
::1/128 即0:0:0:0:0:0:0:1,回环地址,相当于IPv4中的localhost(127.0.0.1),ping locahost可得到此地址。
2001::/16 全球可聚合地址,由 IANA 按地域和ISP进行分配,是最常用的IPv6地址,属于单播地址。
2002::/16 6 to 4 地址,用于6to4自动构造隧道技术的地址,属于单播地址。
3ffe::/16 早期开始的IPv6 6bone试验网 地址,属于单播地址。
fe80::/10 本地链路地址,用于单一链路,适用于自动配置、邻机发现等,路由器不转发以fe80开头的地址。
ff00::/8 组播地址。
::A.B.C.D 兼容IPv4的IPv6地址,其中<A.B.C.D>代表IPv4地址。自动将IPv6包以隧道方式在IPv4网络中传送的IPv4/IPv6节点将使用这些地址。
::FFFF:A.B.C.D 是IPv4映射过来的IPv6地址,其中<A.B.C.D>代表IPv4地址,例如 ::ffff:202.120.2.30 ,它是在不支持IPv6的网上用于表示IPv4节点。