互联网私有IP地址列表

最近因为业务需要,要判断用户的IP是否私有IP,

以前知道的私有IP,基本上只有如下几个(注意:这不是正确答案):

  • 10.0.0.0/8(10.0.0.0-10.255.255.255)
  • 172.16.0.0/12(172.16.0.0-172.31.255.255)
  • 192.168.0.0/16(192.168.0.0-192.168.255.255)
  • 再加一个本地IP:
    127.0.0.0/8(127.0.0.0-127.255.255.255)
    localhost
  • 以前考MCSE的时候,还知道一个Windows在DHCP未获取到IP时,会自动分配的:
    169.254.0.0/16(169.254.0.0-169.254.255.255)

之前知道的就这些了,问了一下ChatGPT老师,说还有一些,如图:

基于以往印象中,GPT老师的不靠谱情况,去google查了一些资料,最后搜索到官方的定义文档,对互联网保留地址做了一个清单说明,还贴心的提供了csv格式下载:
https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml

这个网址是iana的官方网站,iana负责全球的IP地址分配,具体说明参考百度百科

IANA ( Internet Assigned Numbers Authority,互联网号码分配机构)负责协调全球DNS的根域名、IP地址分配和其他互联网协议资源。

不过iana给的清单还有一些重复,维基百科帮它做了一下合并,整理为17项,参考地址:
https://en.wikipedia.org/wiki/Reserved_IP_addresses

IP地址段 IP范围 IP个数 范围 说明
0.0.0.0/8 0.0.0.0--0.255.255.255 16,777,216 Software 一般用于当前网络或路由等特殊用途
10.0.0.0/8 10.0.0.0--10.255.255.255 16,777,216 Private network 用于大型的私有网络本地通信
100.64.0.0/10 100.64.0.0--100.127.255.255 4,194,304 Private network 运营商NAT用,即ISP与客户间通信的地址
127.0.0.0/8 127.0.0.0--127.255.255.255 16,777,216 Host 本地主机环回地址。
169.254.0.0/16 169.254.0.0--169.254.255.255 65,536 Subnet 一般在DHCP无法获取到地址时,为本机分配的地址
172.16.0.0/12 172.16.0.0--172.31.255.255 1,048,576 Private network 用于中型的私有网络本地通信。
192.0.0.0/24 192.0.0.0--192.0.0.255 256 Private network IETF协议分配。
192.0.2.0/24 192.0.2.0--192.0.2.255 256 Documentation 分配为TEST-NET-1,做文档和示例
192.88.99.0/24 192.88.99.0--192.88.99.255 256 Internet 保留。以前用于从IPv6到IPv4中继
192.168.0.0/16 192.168.0.0--192.168.255.255 65,536 Private network 用于小型的私有网络本地通信。
198.18.0.0/15 198.18.0.0--198.19.255.255 131,072 Private network 用于对2个独立子网间的通信通过基准测试
198.51.100.0/24 198.51.100.0--198.51.100.255 256 Documentation 分配为TEST-NET-2,做文档和示例
203.0.113.0/24 203.0.113.0--203.0.113.255 256 Documentation 分配为TEST-NET-2,做文档和示例
224.0.0.0/4 224.0.0.0--239.255.255.255 268,435,456 Internet 用于IP多播(以前的D类网络)
233.252.0.0/24 233.252.0.0--233.252.0.255 256 Documentation 指定为 MCAST-TEST-NET,文档和示例
240.0.0.0/4 240.0.0.0--255.255.255.254 268,435,455 Internet 保留以备将来使用(以前的E类网络)
255.255.255.255/32 255.255.255.255 1 Subnet 保留,用于"有限广播"目标地址

注:

IPv6的保留地址参考这里:
https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml


转换代码

文章的最后,贴一些转换用的代码吧:

Javascript版本的代码

有5个方法,可以看下面代码的注释:

javascript 复制代码
/**
 * 判断给定的ip,是否属于 IANA定义的保留地址(即私有地址)
 * @param ip 给定的ip
 * @returns {boolean} 是否私有地址
 */
function isPrivateIpAddr(ip) {
    const privateIp = [
        ['0.0.0.0', '0.255.255.255'],  // 0.0.0.0/8
        ['10.0.0.0', '10.255.255.255'],  // 10.0.0.0/8
        ['100.64.0.0', '100.127.255.255'],  // 100.64.0.0/10
        ['127.0.0.0', '127.255.255.255'],  // 127.0.0.0/8
        ['169.254.0.0', '169.254.255.255'],  // 169.254.0.0/16
        ['172.16.0.0', '172.31.255.255'],  // 172.16.0.0/12
        ['192.0.0.0', '192.0.0.255'],  // 192.0.0.0/24
        ['192.0.2.0', '192.0.2.255'],  // 192.0.2.0/24
        ['192.88.99.0', '192.88.99.255'],  // 192.88.99.0/24
        ['192.168.0.0', '192.168.255.255'],  // 192.168.0.0/16
        ['198.18.0.0', '198.19.255.255'],  // 198.18.0.0/15
        ['198.51.100.0', '198.51.100.255'],  // 198.51.100.0/24
        ['203.0.113.0', '203.0.113.255'],  // 203.0.113.0/24
        ['224.0.0.0', '239.255.255.255'],  // 224.0.0.0/4
        ['233.252.0.0', '233.252.0.255'],  // 233.252.0.0/24
        ['240.0.0.0', '255.255.255.254'],  // 240.0.0.0/4
        ['255.255.255.255', '255.255.255.254'],  // 255.255.255.255/32
    ];
    for (const [startIP, endIP] of privateIp) {
        if (inIpAddrRange(ip, startIP, endIP)) {
            return true;
        }
    }
    return false;
}

/**
 * 把 x.x.x.x形式的IP地址,转换为整数返回
 * 注:转换为无符号整型,如 '255.255.255.255'转为4294967295
 * 即: (255*256*256*256) + (255*256*256) + (255*256) + 255
 * @param ip 字符串格式的ip
 * @returns {number} 无符号整数
 */
function ipaddrToNumber(ip) {
    //return (ip.split('.').reduce((acc, cur) => (acc << 8) + parseInt(cur), 0)) >>> 0;
    const ipArr = ip.split('.');
    if (ipArr.length !== 4)
        throw new Error('IP地址格式不对,应该有3个小数点');
    let ret = 0;
    // 验证每个项是否小于等于255
    for (let i = 0; i < 4; i++) {
        const number = parseInt(ipArr[i], 10);
        if (isNaN(number) || number > 255 || number < 0)
            throw new Error('IP地址中的每个项都应在0~255之间');
        ret = (ret << 8) + number;
    }
    // 转无符号数,避免负数返回
    return ret >>> 0;
}

/**
 * 把一个无符号整数,转换为ip地址返回
 * @param number ipaddrToNumber方法计算得出的无符号整数
 * @returns {string} ip地址
 */
function numberToIpAddr(number) {
    if (number < 0 || number > 4294967295) {
        throw new Error('参数应在0~4294967295之间');
    }
    const ipArr = [];
    for (let i = 3; i >= 0; i--) {
        ipArr[i] = (number >>> (8 * (3 - i))) & 255;
    }
    return ipArr.join('.');
}

/**
 * 给定的ip,是否在给定的ip起止范围内
 * @param ip 要判断的ip
 * @param startIP ip范围起始值
 * @param endIp ip范围结束值
 * @returns {boolean} 是否在范围内
 */
function inIpAddrRange(ip, startIP, endIp) {
    const ipNum = ipaddrToNumber(ip);
    const startIPNum = ipaddrToNumber(startIP);
    const endIPNum = ipaddrToNumber(endIp);
    return ipNum >= startIPNum && ipNum <= endIPNum;
}

/**
 * 给定的ip,是否在给定的CIDR ip地址范围内
 * CIDR是用ip网址+子网掩码的表示法,如 192.168.0.0/16
 * @param ip 要判断的ip
 * @param startIP ip网址起始值
 * @param ipMaskNum 子网掩码
 * @returns {boolean} 是否在范围内
 */
function inIpAddrCIDR(ip, startIP, ipMaskNum) {
    const ipMask = parseInt(ipMaskNum, 10);
    if (isNaN(ipMask) || ipMask < 1 || ipMask > 32)
        throw new Error('子网掩码应在1~32之间');
    const ipNum = ipaddrToNumber(ip);
    const startIPNum = ipaddrToNumber(startIP);
    const endIPNum = startIPNum + (Math.pow(2, 32 - ipMask) - 1);
    console.log(startIPNum + ':' + endIPNum);
    return ipNum >= startIPNum && ipNum <= endIPNum;
}

测试代码:
isPrivateIpAddr('192.168.0.1') 结果为 true
ipaddrToNumber('192.168.0.1') 结果为 3232235521
ipaddrToNumber('1.2.3.4') 结果为 16909060
ipaddrToNumber('255.255.255.255') 结果为 4294967295

MySQL的代码

查了一下官方文档,发现MySQL内置了 ip 和 number 互转的函数,文档链接

mysql 复制代码
SELECT INET_ATON('192.168.0.1') ip1,
              INET_ATON('1.2.3.4') ip2, 
              INET_ATON('255.255.255.255') ip3, 
              INET_NTOA(16909060) ip4;

上面sql得到的结果如下:

ip1 ip2 ip3 ip4
3232235521 16909060 4294967295 1.2.3.4

跟上面Javascript代码的结果是一致的。

我们只要把私有地址写入一张表,再把某个ip转数字后,去这张表查询是否有范围内的记录即可。

  • 建表:
sql 复制代码
CREATE TABLE `sysprivateips` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `ipStart` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'ip段起始值',
  `ipEnd` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'ip段结束值',
  `ipMask` tinyint NOT NULL DEFAULT '0' COMMENT '掩码,暂时未用',
  `createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `ipStartNum` bigint GENERATED ALWAYS AS (inet_aton(`ipStart`)) VIRTUAL,
  `ipEndNum` bigint GENERATED ALWAYS AS (inet_aton(`ipEnd`)) VIRTUAL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uqi_ip` (`ipStartNum`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='私有ip列表'
  • 插入所有的私有IP数据:
sql 复制代码
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('0.0.0.0', '0.255.255.255', '8');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('10.0.0.0', '10.255.255.255', '8');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('100.64.0.0', '100.127.255.255', '10');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('127.0.0.0', '127.255.255.255', '8');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('169.254.0.0', '169.254.255.255', '16');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('172.16.0.0', '172.31.255.255', '12');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('192.0.0.0', '192.0.0.255', '24');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('192.0.2.0', '192.0.2.255', '24');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('192.88.99.0', '192.88.99.255', '24');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('192.168.0.0', '192.168.255.255', '16');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('198.18.0.0', '198.19.255.255', '15');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('198.51.100.0', '198.51.100.255', '24');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('203.0.113.0', '203.0.113.255', '24');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('224.0.0.0', '239.255.255.255', '4');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('233.252.0.0', '233.252.0.255', '24');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('240.0.0.0', '255.255.255.254', '4');
INSERT INTO `sysprivateips`(`ipStart`,`ipEnd`,`ipMask`)VALUES('255.255.255.255', '255.255.255.254', '32');
  • OK,现在可以直接用sql,判断是否私有IP了:
    假设要查看 192.168.0.1对应的CIDR地址段,用这个sql:
sql 复制代码
SELECT CONCAT(ipStart, '/', ipMask) AS CIDR 
  FROM `sysprivateips`
 WHERE ipStartNum<=INET_ATON('192.168.0.1') AND INET_ATON('192.168.0.1')<=ipEndNum
相关推荐
sunfove1 小时前
光网络的立交桥:光开关 (Optical Switch) 原理与主流技术解析
网络
Kevin Wang7274 小时前
欧拉系统服务部署注意事项
网络·windows
min1811234564 小时前
深度伪造内容的检测与溯源技术
大数据·网络·人工智能
汤愈韬4 小时前
NAT策略
网络协议·网络安全·security·huawei
汤愈韬4 小时前
Full Cone Nat
网络·网络协议·网络安全·security·huawei
zbtlink5 小时前
现在还需要带电池的路由器吗?是用来干嘛的?
网络·智能路由器
桌面运维家5 小时前
vDisk配置漂移怎么办?VOI/IDV架构故障快速修复
网络·架构
dalerkd5 小时前
忙里偷闲叙-谈谈最近两年
网络·安全·web安全
汤愈韬6 小时前
NAT ALG (应用层网关)
网络·网络协议·网络安全·security·huawei
运维栈记7 小时前
虚拟化网络的根基-网络命名空间
网络·docker·容器