一、背景

一般在CMDB(Configuration Management Database,配置管理数据库)里会存储一台服务器的内网IP、私有IP、管理IP、电信IP、联通IP、BAT弹性IP,在使用的时候只需要拿到其中一个外网IP地址即可。那么我们就需要判断并剔除掉内网IP、私有IP、管理IP,获取第一个外网IP(电信IP、联通IP、BAT弹性IP)。

例如三线机房服务器:'10.20.0.111'(内网IP), '10.10.42.1'(管理IP), '100.64.228.13'(运营商IP), '221.222.222.33', '8.8.8.8(DNS解析)', '114.114.144.114', '192.168.31.46'(内网IP)

 

二、判断内网ip的原理

内网IP可分为三类:

A类地址:10.0.0.0--10.255.255.255

B类地址:172.16.0.0--172.31.255.255

C类地址:192.168.0.0--192.168.255.255

运营商:100.64.0.0/10     100.64.0.0--100.127.255.255

DHCP: 169.254.0.0/16    169.254.0.0--169.254.255.255

局域网在选取使用私有地址时,一般会按照实际需要容纳的主机数来选择私有地址段。

常见的局域网由于容量小,一般选择C类的192.168.0.0作为地址段使用,一些大型企业就需要使用B类甚至A类地址段作为内部网络的地址段。

首地址 尾地址 所属IP分类 无分类掩码 示意
0.0.0.0 0.255.255.255 Class A network 0.x.x.x 0.0.0.0/8 A类保留地址
10.0.0.0 10.255.255.255 Class A network 10.x.x.x 10.0.0.0/8 A类私有地址
100.64.0.0 100.127.255.255 Class B network 100.x.x.x 100.64.0.0/10 64个B类私有地址(运营商)
127.0.0.0 127.255.255.255 Class A network 127.x.x.x 127.0.0.0/8 回环IP,表示本机
128.0.0.0 128.0.255.255 Class B network 128.0.x.x 128.0.0.0/16 保留
169.254.0.0 169.254.255.255 Class B network 169.254.x.x 169.254.0.0/16 DHCP自动分配B类私有IP地址
172.16.0.0 172.31.255.255 172.16.x.x 到 172.31.x.x共16共B类网络 172.16.0.0/12 B类私有地址
191.255.0.0 191.255.255.255 Class B network 191.255.x.x 191.255.0.0/16 C类保留地址
192.0.0.0 192.0.0.255 Class C network 192.0.0.x 192.0.0.0/24 C类保留地址
192.168.0.0 192.168.255.255 256 个C类网络 192.168.0.0/16 C类私有IP
223.255.255.0 223.255.255.255 Class C network 223.255.255.x 223.255.255/24 保留
255.255.255.255 255.255.255.255 Class C network 255.255.255.255 255.255.255.255/32 广播地址

更完整的保留IP:维基百科的Reserved IP Address词条

Address block Address range Number of addresses Scope Description
0.0.0.0/8 0.0.0.0–0.255.255.255 16777216 Software Current network (only valid as source address).
10.0.0.0/8 10.0.0.0–10.255.255.255 16777216 Private network Used for local communications within a private network.
100.64.0.0/10 100.64.0.0–100.127.255.255 4194304 Private network Shared address space for communications between a service provider and its subscribers when using a carrier-grade NAT.
127.0.0.0/8 127.0.0.0–127.255.255.255 16777216 Host Used for loopback addresses to the local host.
169.254.0.0/16 169.254.0.0–169.254.255.255 65536 Subnet Used for link-local addresses between two hosts on a single link when no IP address is otherwise specified, such as would have normally been retrieved from a DHCP server.
172.16.0.0/12 172.16.0.0–172.31.255.255 1048576 Private network Used for local communications within a private network.
192.0.0.0/24 192.0.0.0–192.0.0.255 256 Private network IETF Protocol Assignments.
192.0.2.0/24 192.0.2.0–192.0.2.255 256 Documentation Assigned as TEST-NET-1, documentation and examples.
192.88.99.0/24 192.88.99.0–192.88.99.255 256 Internet Reserved. Formerly used for IPv6 to IPv4 relay (included IPv6 address block 2002::/16).
192.168.0.0/16 192.168.0.0–192.168.255.255 65536 Private network Used for local communications within a private network.
198.18.0.0/15 198.18.0.0–198.19.255.255 131072 Private network Used for benchmark testing of inter-network communications between two separate subnets.
198.51.100.0/24 198.51.100.0–198.51.100.255 256 Documentation Assigned as TEST-NET-2, documentation and examples.
203.0.113.0/24 203.0.113.0–203.0.113.255 256 Documentation Assigned as TEST-NET-3, documentation and examples.
224.0.0.0/4 224.0.0.0–239.255.255.255 268435456 Internet In use for IP multicast. (Former Class D network).
240.0.0.0/4 240.0.0.0–255.255.255.254 268435456 Internet Reserved for future use. (Former Class E network).
255.255.255.255/32 255.255.255.255 1 Subnet Reserved for the "limited broadcast" destination address.

 

三、代码实现步骤

实现方法一:掩码对比

def ip_into_int(ip):
    # 先把 192.168.31.46 用map分割'.'成数组,然后用reduuce+lambda转成10进制的 3232243502
    # (((((192 * 256) + 168) * 256) + 31) * 256) + 46
    return reduce(lambda x,y:(x<<8)+y,map(int,ip.split('.')))

# 方法1:掩码对比
def is_internal_ip(ip_str):
    ip_int = ip_into_int(ip_str)
    net_A = ip_into_int('10.255.255.255') >> 24
    net_B = ip_into_int('172.31.255.255') >> 20
    net_C = ip_into_int('192.168.255.255') >> 16
    net_ISP = ip_into_int('100.127.255.255') >> 22
    net_DHCP = ip_into_int('169.254.255.255') >> 16
    return ip_int >> 24 == net_A or ip_int >>20 == net_B or ip_int >> 16 == net_C or ip_int >> 22 == net_ISP or ip_int >> 16 == net_DHCP

def main():
    for ip in ['10.20.0.111', '10.10.42.1', '100.64.228.13', '221.222.222.33', '8.8.8.8', '114.114.144.114', '192.168.31.46']:
        print("is_internal_ip: ip: %s, \t result: %s" % (ip, is_internal_ip(ip)))       # 方法1:掩码对比

运行结果:

is_internal_ip: ip: 10.20.0.111, 	 result: True
is_internal_ip: ip: 10.10.42.1, 	 result: True
is_internal_ip: ip: 100.64.228.13, 	 result: False
is_internal_ip: ip: 221.222.222.33,  result: False
is_internal_ip: ip: 8.8.8.8, 	 	 result: False
is_internal_ip: ip: 114.114.144.114, result: False
is_internal_ip: ip: 192.168.31.46, 	 result: True

 

其中,map、reduce、lambda 函数的用法示例:

>>> map(int, '12.34'.split('.'))
[12, 34]
>>> reduce(lambda x,y:(x<<8)+y, [12, 34])
3106
>>> 12 * 256 + 34
3106
>>> 
>>> map(int, '192.168.31.46'.split('.'))
[192, 168, 31, 46]
>>> reduce(lambda x,y:(x<<8)+y, [192, 168, 31, 46])
3232243502

 

实现方法二:字符串对比

# 方法2:字符串对比
def is_internal_ip2(ip_str):
    ip_str2 = ''
    for item in ip_str.split('.'):
        if(len(item)) == 1: item = "00" + item
        if(len(item)) == 2: item = "0" + item
        ip_str2 = ip_str2 + '.' + item
    ip_str2 = ip_str2.lstrip('.')
    
    inet_A_array = ['010.000.000.000','010.255.255.255']
    inet_B_array = ['172.016.000.000','172.031.255.255']
    inet_C_array = ['192.168.000.000','192.168.255.255']
    inet_ISP_array = ['100.064.000.000','100.127.255.255']
    inet_DHCP_array = ['169.254.000.000','169.254.255.255']
#     print("ip_int: %s, ip_str2: %s" % (ip_str, ip_str2))
    return (ip_str2 >= inet_A_array[0] and ip_str2 < = inet_A_array[1]) or (ip_str2 >= inet_B_array[0] and ip_str2 < = inet_B_array[1]) or (ip_str2 >= inet_C_array[0] and ip_str2 < = inet_C_array[1]) \
            or (ip_str2 >= inet_ISP_array[0] and ip_str2 <= inet_ISP_array[1]) or (ip_str2 >= inet_DHCP_array[0] and ip_str2 < = inet_DHCP_array[1])


def main():
    for ip in ['10.20.0.111', '10.10.42.1', '100.64.228.13', '221.222.222.33', '8.8.8.8', '114.114.144.114', '192.168.31.46']:
        print("is_internal_ip: ip: %s, \t result: %s" % (ip, is_internal_ip2(ip)))      # 方法2:字符串对比

运行结果:

is_internal_ip: ip: 10.20.0.111, 	 result: True
is_internal_ip: ip: 10.10.42.1, 	 result: True
is_internal_ip: ip: 100.64.228.13, 	 result: True
is_internal_ip: ip: 221.222.222.33, 	 result: False
is_internal_ip: ip: 8.8.8.8, 	 result: False
is_internal_ip: ip: 114.114.144.114, 	 result: False
is_internal_ip: ip: 192.168.31.46, 	 result: True

 

四、应用实例

Python 实现的完整代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 
# Copyright 2015 by mimvp.com

'''
判断ip地址是否内网ip,参考米扑博客 https://blog.mimvp.com/article/30447.html
A类地址: 10.0.0.0/8        10.0.0.0--10.255.255.255
B类地址: 172.16.0.0/12     172.16.0.0--172.31.255.255
C类地址: 192.168.0.0/16    192.168.0.0--192.168.255.255
运营商:  100.64.0.0/10     100.64.0.0--100.127.255.255
DHCP:   169.254.0.0/16    169.254.0.0--169.254.255.255

测试ip:  '10.50.42.1', '100.64.228.13', '192.168.31.46', '8.8.8.8'

2015-02-05
'''


import sys
reload(sys)
sys.setdefaultencoding('utf-8')


def ip_into_int(ip):
    # 先把 192.168.31.46 用map分割'.'成数组,然后用reduuce+lambda转成10进制的 3232243502
    # (((((192 * 256) + 168) * 256) + 31) * 256) + 46
    return reduce(lambda x,y:(x<<8)+y,map(int,ip.split('.')))

# 方法1:掩码对比
def is_internal_ip(ip_str):
    ip_int = ip_into_int(ip_str)
    net_A = ip_into_int('10.255.255.255') >> 24
    net_B = ip_into_int('172.31.255.255') >> 20
    net_C = ip_into_int('192.168.255.255') >> 16
    net_ISP = ip_into_int('100.127.255.255') >> 22
    net_DHCP = ip_into_int('169.254.255.255') >> 16
    return ip_int >> 24 == net_A or ip_int >>20 == net_B or ip_int >> 16 == net_C or ip_int >> 22 == net_ISP or ip_int >> 16 == net_DHCP

# 方法2:字符串对比
def is_internal_ip2(ip_str):
    ip_str2 = ''
    for item in ip_str.split('.'):
        if(len(item)) == 1: item = "00" + item
        if(len(item)) == 2: item = "0" + item
        ip_str2 = ip_str2 + '.' + item
    ip_str2 = ip_str2.lstrip('.')
    
    inet_A_array = ['010.000.000.000','010.255.255.255']
    inet_B_array = ['172.016.000.000','172.031.255.255']
    inet_C_array = ['192.168.000.000','192.168.255.255']
    inet_ISP_array = ['100.064.000.000','100.127.255.255']
    inet_DHCP_array = ['169.254.000.000','169.254.255.255']
#     print("ip_int: %s, ip_str2: %s" % (ip_str, ip_str2))
    return (ip_str2 >= inet_A_array[0] and ip_str2 < = inet_A_array[1]) or (ip_str2 >= inet_B_array[0] and ip_str2 < = inet_B_array[1]) or (ip_str2 >= inet_C_array[0] and ip_str2 < = inet_C_array[1]) \
            or (ip_str2 >= inet_ISP_array[0] and ip_str2 <= inet_ISP_array[1]) or (ip_str2 >= inet_DHCP_array[0] and ip_str2 < = inet_DHCP_array[1])


def main():
    for ip in ['10.20.0.111', '10.10.42.1', '100.64.228.13', '221.222.222.33', '8.8.8.8', '114.114.144.114', '192.168.31.46']:
        print("is_internal_ip: ip: %s, \t result: %s" % (ip, is_internal_ip(ip)))       # 方法1:掩码对比
        print("is_internal_ip: ip: %s, \t result: %s" % (ip, is_internal_ip2(ip)))      # 方法2:字符串对比
    

if __name__ == '__main__':
    main()
#     print(map(int,'192.168.31.46'.split('.')))      # [192, 168, 31, 46]
#     print(ip_into_int('192.168.31.46'))             # 3232243502
#     print(100 >> 4)                                 # 6

运行结果:

is_internal_ip: ip: 10.20.0.111, 	 result: True
is_internal_ip: ip: 10.20.0.111, 	 result: True
is_internal_ip: ip: 10.10.42.1, 	 result: True
is_internal_ip: ip: 10.10.42.1, 	 result: True
is_internal_ip: ip: 100.64.228.13, 	 result: True
is_internal_ip: ip: 100.64.228.13, 	 result: True
is_internal_ip: ip: 221.222.222.33, 	 result: False
is_internal_ip: ip: 221.222.222.33, 	 result: False
is_internal_ip: ip: 8.8.8.8, 	 result: False
is_internal_ip: ip: 8.8.8.8, 	 result: False
is_internal_ip: ip: 114.114.144.114, 	 result: False
is_internal_ip: ip: 114.114.144.114, 	 result: False
is_internal_ip: ip: 192.168.31.46, 	 result: True
is_internal_ip: ip: 192.168.31.46, 	 result: True

 

Linux Shell 脚本实现

#!/bin/bash
# Copyright 2015 by mimvp.com

## 检测ip是否内网ip, 1-内网ip, 0-非内网ip,用法: result=$(mimvp_is_internal_ip "100.64.228.13")
function mimvp_is_internal_ip() {
    ip_str="$1"
    ip_str2=''

    ip_array=(${ip_str//./ })
    for item in ${ip_array[@]}
    do
        item_length=${#item}    # 获取item字符串的长度
        if [ "${item_length}" -eq "1" ]; then
            item="00${item}"
        fi
        if [ "${item_length}" -eq "2" ]; then
            item="0${item}"
        fi
#        echo "item: $item  item_length: ${item_length}" 
        ip_str2="${ip_str2}.${item}"
    done
    ip_str2=${ip_str2:1}        # 起始下标0,第2个字段(下标1)截取
#    echo "ip_str2: ${ip_str2}"


    inet_A_array=('010.000.000.000' '010.255.255.255')
    inet_B_array=('172.016.000.000' '172.031.255.255')
    inet_C_array=('192.168.000.000' '192.168.255.255')
    inet_ISP_array=('100.064.000.000' '100.127.255.255')
    inet_DHCP_array=('169.254.000.000' '169.254.255.255')

#    echo "${inet_A_array[0]}  ${inet_A_array[1]}"
#    echo "${inet_B_array[0]}  ${inet_B_array[1]}"
#    echo "${inet_C_array[0]}  ${inet_C_array[1]}"
#    echo "${inet_ISP_array[0]}  ${inet_ISP_array[1]}"
#    echo "${inet_DHCP_array[0]}  ${inet_DHCP_array[1]}"

    result=0
    if [[ "${ip_str2}" > "${inet_A_array[0]}" && "${ip_str2}" < "${inet_A_array[1]}" ]]; then
        result=1
    fi
    if [[ "${ip_str2}" > "${inet_B_array[0]}" && "${ip_str2}" < "${inet_B_array[1]}" ]]; then
        result=1
    fi
    if [[ "${ip_str2}" > "${inet_C_array[0]}" && "${ip_str2}" < "${inet_C_array[1]}" ]]; then
        result=1
    fi
    if [[ "${ip_str2}" > "${inet_ISP_array[0]}" && "${ip_str2}" < "${inet_ISP_array[1]}" ]]; then
        result=1
    fi
    if [[ "${ip_str2}" > "${inet_DHCP_array[0]}" && "${ip_str2}" < "${inet_DHCP_array[1]}" ]]; then
        result=1
    fi
    echo $result
}

result=$(mimvp_is_internal_ip "100.64.228.13")
echo "result: $result"  	## 1

 

 

参考推荐

Linux 命令 ipcalc 计算IP地址

IPv4 保留地址、私有地址、运营商级IP地址

公共 DNS 服务器 IP 地址

Linux 之 shell 比较运算符

IPv4 与 IPv6 的比较

IP地址段与子网掩码

IPv6 理解的十个误区

网络DNS域名转换成IP地址

2018年中国互联网络发展状况统计报告

常见路由器的默认网关 IP 是 192.168.1.1 的由来

IPv6 地址数量有多少,能够分配到地球上的每一粒尘埃吗

Nginx 限制单个IP的并发连接数/速度防止恶意攻击/蜘蛛爬虫采集

Linux iptables 防火墙常用规则

米扑代理:爬虫代理IP哪家好