IPv6 作为可比较的 JavaScript 字符串?
IPv6 as a comparable JavaScript string?
前言
在IPv4时代很容易,因为IPv4地址可以转换成一个简单的32位整数,然后用于各种比较计算。
对于 IPv6,这有点尴尬,因为一方面,JavaScript 本身不支持 128 位整数,而且它们的转换一点也不简单。这只留下处理 IPv6 字符串表示的选项。
问题
如何将任何已知格式的 IPv6 地址转换为可比较的字符串?
要求
- 对于任何可比较的字符串,如果地址 A 在地址 B 之前,则条件
A < B
必须在 JavaScript 中产生 true
。类似的逻辑必须对其余比较有效:===
、<=
、>
和 >=
.
- 对于每个 IPv6,必须生成尽可能多的字符串以覆盖地址中的每个范围,即每个范围的起始地址 + 结束地址。
您没有说明您的解决方案是否可以接受第三方库,但如果可以,我相信您可以使用 ip-address library and its dependency jsbn 将每个地址解析为 v6 对象,调用 v6.bigInteger()
获取地址作为 jsbn BigInteger 对象,然后使用 BigInteger.compareTo
比较地址。
将简化的 IPv6 地址格式转换为完整格式并不难。只有 3 条规则可以简化地址。以下是按顺序列出的规则,必须撤消这些规则才能将地址转换回完整格式:
点分四组表示法(IPv4 地址嵌入在 IPv6 地址中)
可以省略前导零
零组可以缩写为::
从技术上讲,根据您的处理方式,2 和 3 可能会互换。
所以这里有一个简单的转换器,它只转换有效的 IPv6 地址(如果你输入无效的 IPv6 地址,它肯定会失败,因为我没有做任何验证):
function full_IPv6 (ip_string) {
// replace ipv4 address if any
var ipv4 = ip_string.match(/(.*:)([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$)/);
if (ipv4) {
var ip_string = ipv4[1];
ipv4 = ipv4[2].match(/[0-9]+/g);
for (var i = 0;i < 4;i ++) {
var byte = parseInt(ipv4[i],10);
ipv4[i] = ("0" + byte.toString(16)).substr(-2);
}
ip_string += ipv4[0] + ipv4[1] + ':' + ipv4[2] + ipv4[3];
}
// take care of leading and trailing ::
ip_string = ip_string.replace(/^:|:$/g, '');
var ipv6 = ip_string.split(':');
for (var i = 0; i < ipv6.length; i ++) {
var hex = ipv6[i];
if (hex != "") {
// normalize leading zeros
ipv6[i] = ("0000" + hex).substr(-4);
}
else {
// normalize grouped zeros ::
hex = [];
for (var j = ipv6.length; j <= 8; j ++) {
hex.push('0000');
}
ipv6[i] = hex.join(':');
}
}
return ipv6.join(':');
}
您可能可以在 .split(':')
之后进行嵌入式 IPv4 处理,但我已经在考虑正则表达式的情况下编写了它。从上面的代码可以看出,这个过程的每一步都相当简单。唯一让我绊倒的是最后一个 for 循环中 j<=8
条件中的一个差错。
只需将一个 IPv6 地址转换为四个 32 位无符号整数,然后循环这四个整数。我一直这样做:
操作 IPv4 或 IPv6 地址只需要两件事:地址和掩码。每个协议的长度都相同(IPv4=32 位,IPv6=128 位)。由于我没有 128 位无符号整数,因此我使用一个包含四个 32 位无符号整数的数组作为 IPv6 地址和掩码。其他一切都可以从这两个值构建。
IPv6 在查找第一个和最后一个地址方面比 IPv4 更容易,因为在 IPv6 中,第一个地址是子网,最后一个地址是子网加上反掩码。
使用ip6
npm包规范化IPv6地址,然后直接比较。
let ip6 = require('ip6')
console.log(ip6.normalize('2404:6800:4003:808::200e'));
// 2404:6800:4003:0808:0000:0000:0000:200e
前言
在IPv4时代很容易,因为IPv4地址可以转换成一个简单的32位整数,然后用于各种比较计算。
对于 IPv6,这有点尴尬,因为一方面,JavaScript 本身不支持 128 位整数,而且它们的转换一点也不简单。这只留下处理 IPv6 字符串表示的选项。
问题
如何将任何已知格式的 IPv6 地址转换为可比较的字符串?
要求
- 对于任何可比较的字符串,如果地址 A 在地址 B 之前,则条件
A < B
必须在 JavaScript 中产生true
。类似的逻辑必须对其余比较有效:===
、<=
、>
和>=
. - 对于每个 IPv6,必须生成尽可能多的字符串以覆盖地址中的每个范围,即每个范围的起始地址 + 结束地址。
您没有说明您的解决方案是否可以接受第三方库,但如果可以,我相信您可以使用 ip-address library and its dependency jsbn 将每个地址解析为 v6 对象,调用 v6.bigInteger()
获取地址作为 jsbn BigInteger 对象,然后使用 BigInteger.compareTo
比较地址。
将简化的 IPv6 地址格式转换为完整格式并不难。只有 3 条规则可以简化地址。以下是按顺序列出的规则,必须撤消这些规则才能将地址转换回完整格式:
点分四组表示法(IPv4 地址嵌入在 IPv6 地址中)
可以省略前导零
零组可以缩写为
::
从技术上讲,根据您的处理方式,2 和 3 可能会互换。
所以这里有一个简单的转换器,它只转换有效的 IPv6 地址(如果你输入无效的 IPv6 地址,它肯定会失败,因为我没有做任何验证):
function full_IPv6 (ip_string) {
// replace ipv4 address if any
var ipv4 = ip_string.match(/(.*:)([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$)/);
if (ipv4) {
var ip_string = ipv4[1];
ipv4 = ipv4[2].match(/[0-9]+/g);
for (var i = 0;i < 4;i ++) {
var byte = parseInt(ipv4[i],10);
ipv4[i] = ("0" + byte.toString(16)).substr(-2);
}
ip_string += ipv4[0] + ipv4[1] + ':' + ipv4[2] + ipv4[3];
}
// take care of leading and trailing ::
ip_string = ip_string.replace(/^:|:$/g, '');
var ipv6 = ip_string.split(':');
for (var i = 0; i < ipv6.length; i ++) {
var hex = ipv6[i];
if (hex != "") {
// normalize leading zeros
ipv6[i] = ("0000" + hex).substr(-4);
}
else {
// normalize grouped zeros ::
hex = [];
for (var j = ipv6.length; j <= 8; j ++) {
hex.push('0000');
}
ipv6[i] = hex.join(':');
}
}
return ipv6.join(':');
}
您可能可以在 .split(':')
之后进行嵌入式 IPv4 处理,但我已经在考虑正则表达式的情况下编写了它。从上面的代码可以看出,这个过程的每一步都相当简单。唯一让我绊倒的是最后一个 for 循环中 j<=8
条件中的一个差错。
只需将一个 IPv6 地址转换为四个 32 位无符号整数,然后循环这四个整数。我一直这样做:
操作 IPv4 或 IPv6 地址只需要两件事:地址和掩码。每个协议的长度都相同(IPv4=32 位,IPv6=128 位)。由于我没有 128 位无符号整数,因此我使用一个包含四个 32 位无符号整数的数组作为 IPv6 地址和掩码。其他一切都可以从这两个值构建。
IPv6 在查找第一个和最后一个地址方面比 IPv4 更容易,因为在 IPv6 中,第一个地址是子网,最后一个地址是子网加上反掩码。
使用ip6
npm包规范化IPv6地址,然后直接比较。
let ip6 = require('ip6')
console.log(ip6.normalize('2404:6800:4003:808::200e'));
// 2404:6800:4003:0808:0000:0000:0000:200e