IPv6地址的文本表示的最大长度是多少?

544

我想将PHP中$_SERVER["REMOTE_ADDR"]返回的数据存储到数据库字段中,这是一个非常简单的任务。问题在于,我找不到任何关于IPv6地址文本表示的最大长度的正确信息,而这正是Web服务器通过$_SERVER["REMOTE_ADDR"]提供的。

我不想将文本表示转换为通常编码128位地址的格式,我只想知道存储任何由$_SERVER["REMOTE_ADDR"]返回的IPv6地址所需的最大字符数。


6
区域索引怎么样? - Robert Tupelo-Schneck
6
#define INET_ADDRSTRLEN (16) => INET_ADDRSTRLEN的定义为16 #define INET6_ADDRSTRLEN (48) => INET6_ADDRSTRLEN的定义为48 - user1687619
源代码:lxr.free-electrons.com/source/include/linux/inet.h - user1687619
1
这个问题https://dev59.com/BXNA5IYBdhLWcg3wIqTr有一些相似但有用的答案。 - Edward
不要忘记子网掩码,IPv4为32位,IPv6为8位(CIDR格式)。但最好使用数据库的本机格式来存储IP地址等信息,而不是以文本格式进行存储。 - Anders
显示剩余3条评论
6个回答

771

45个字符.

你可能期望地址是什么。

0000:0000:0000:0000:0000:0000:0000:0000

8 * 4 + 7 = 39

有8组由4个数字组成的,在它们之间有7个:

但如果你有一个IPv4-mapped IPv6地址,最后两组可以用以.分隔的十进制表示,例如:[::ffff:192.168.100.228]。完整写出:

0000:0000:0000:0000:0000:ffff:192.168.100.228

(6 * 4 + 5) + 1 + (4 * 3 + 3) = 29 + 1 + 15 = 45

注意,这只是一种输入/显示约定——它仍然是一个128位的地址,对于存储最好使用原始的冒号分隔格式,即对于上面的地址使用 [0000:0000:0000:0000:0000:ffff:c0a8:64e4]


282
头文件定义INET6_ADDRSTRLEN为46,这与45个字符加上一个结尾的空字符相符。 - wisnij
9
值得注意的是,IPv6地址也可能包含范围域(https://dev59.com/G2025IYBdhLWcg3w4aCu),例如,您可以从RemoteEndpointMessageProperty.Address中获取它。 - Rory
1
这是否考虑了IPv6地址中的百分号?请参考-https://superuser.com/questions/99746/why-is-there-a-percent-sign-in-the-ipv6-address - Gaurav
1
@Ellipticalview 无论你将缓冲区设置多大,恶意的攻击者总能想出一个更长的畸形字符串。更明智的做法是将缓冲区设置为足够大以容纳最长的有效值,并通过其他方式处理不良输入。 - kasperd
1
@Ellipticalview 如果您记录客户端IP地址,则应在自己的端使用受信任的代码将其从线路格式转换为文本表示形式。在这种情况下,恶意行为者无法使畸形的IP地址出现在您的日志中。验证输入以防止过长的字符串仍然是明智的,但此时您正在防御自己的错误而不是恶意行为者。 - kasperd
显示剩余7条评论

121
在Linux上,可查看常量INET6_ADDRSTRLEN(包括<arpa/inet.h>,请参见man inet_ntop)。在我的系统上(使用头文件"in.h"):
#define INET6_ADDRSTRLEN 46

我认为最后一个字符是用于终止 NULL 的,所以最大长度为45,与其他答案相同。


7
那么我看到的这个“48”是什么?https://github.com/torvalds/linux/blob/master/include/linux/inet.h#L50 - eis
1
@eis 可能是在2013年。可能是为了结构体对齐而增加的。 - Iiridayn
我认为这只是Linux存储IPv6地址和链路本地附加网络设备名称的方式,例如:fffe::1234:5678:9abc:def0%eth0。为了解释@eis从46到48的变化,但我只是猜测。你应该在内部不存储除二进制形式以外的任何东西,并通过toString()方法或类似方法来传输到/从字符串和内部格式进行转换。然后,如果你的程序不支持该格式或者它是一个错误,你可以抛出异常。(不在结构中存储子网掩码,大多数情况下它是128、64或更小。) - Anders
所以对于IPv4,子网掩码需要存储32位,但对于IPv6,您只需要存储一个字节(0-128)。 - Anders

20

如所示,标准IPv6地址最多为45个字符,但IPv6地址也可以包括以%结尾的“范围”或“区域”字符串,该字符串没有固定的长度,但通常是一个小正整数或网络接口名称,因此实际上可能会比45个字符更大。网络接口名称通常是“eth0”、“eth1”、“wlan0”,仅有少量字符。在Linux中,最大接口名称长度为15个字符,因此选择61个字节将覆盖Linux上的所有接口名称。


6
没错,但这只是本地连接地址,在网站在互联网上运行时(希望即使在开发中也不会看到)你将不会看到任何此类地址。 - Michael Hampton
这个答案值得更多的关注。最好增加5个字符,而不是为了找到这样的异常而调试一个小时。因此,对于想要知道真正的最大字符数的人来说,这个答案是最好的。 - Akito
3
网络接口命名规范规定名称不能超过15个字符。最好加上45 + 1(终止空字符)+ 15 = 61个字符。再加3个字符(用于结构字节对齐),我们得到64个字符。 - tom_mai78101
更好的做法是在从外部源读取数据时添加检查,以确保不超过最大字段大小。然后当它在系统中时,您可以依赖地址是正确的。因此,仅对于IPv6地址,您只需要39个字符(不计算一些行尾字符等8组4个十六进制字符+7个分隔符字符)。 (好的,每次计算新地址时也应进行检查,并在不正确的字符串格式时抛出错误。最简单的方法是使用二进制域结构,具有执行检查的toString()方法)。 - Anders

13

回答了我自己的问题:

IPv6地址通常写作八组四个十六进制数字,每组之间用冒号(:)分隔。

因此最多只有39个字符。


14
然而,显然有一个要注意的地方,请参见https://dev59.com/BXNA5IYBdhLWcg3wIqTr#7477384(“客户端IP地址的最大长度”) - 引用:“对于IPv4映射的IPv6地址,字符串可以比39个字符更长。”。它说“IPv4映射的IPv6”是45个字符,格式为“NNNN:NNNN:NNNN:NNNN:NNNN:NNNN:192.168.158.190”。因此,最大长度应该是45个字符。同样,https://dev59.com/y3VC5IYBdhLWcg3w2k-I#166157(本问题)的答案似乎也证明了这一点。 - Edward
IPv4映射在IPv6地址中仍然只是8组4个十六进制数字的方式。这只是打印IPv6地址的一种方式。 是的,您还可以添加设备名称并带有“%dev”后缀,以增加大小。我会以相同的格式存储它,并在向用户显示/打印时转换为适当的格式。设备也是如此,实际上仅用于链路本地地址。 因此,有8个16位数字,并添加一些其他格式和可选设备名称的标志。 - Anders
更不用说子网掩码了,IPv4的子网掩码是32位,但IPv6只需要一个字节(因为它使用CIDR格式)。 - Anders
似乎谷歌随机选择了这个错误答案,因为“ipv6最大长度”会响应“39个字符”。我想知道有多少人被这个问题影响了... - Glenn Maynard

8

我认为这个链接中的@Deepak回答更接近正确答案:客户端IP地址的最大长度。因此正确的大小是45而不是39。有时我们会尝试在字段大小上节省空间,但如果我们准备足够的存储空间似乎更好。


这只适用于Linux。现在是47,而不是45。 但如果您要存储在数据库中,请使用本机地址格式进行存储。不要忘记存储子网掩码,IPv4需要32位,IPv6仅需要8位等等。 这表明在数据库中以字符格式存储数据是一种不好的做法。 - Anders
@Anders 需要重新检查,并且我想准备关于这个主题的好信息。 - QMaster

3
注意某些标题,例如 HTTP_X_FORWARDED_FOR,它们似乎包含单个 IP 地址。实际上,它们可能包含多个地址(我假设是代理链)。
它们看起来会 以逗号分隔 - 并且可能比总共 45 个字符长得多 - 因此在存储到数据库之前请检查。

4
略有矛盾的结论为-1(因为我之前曾陷入这个陷阱,我想这个回答包含了对那些即将访问这个页面的人有用的信息),但毫无疑问这并不是对所提问题的答案。把它作为评论写在问题下面可能更好。 - Mark Amery
1
加1只是为了抵消无意义的-1。 - Glenn Maynard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接