暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

IP 地址段到 CIDR 格式的转换

生有可恋 2024-01-15
181

CIDR 是无类别域间路由的意思,以前会用A类、B类、C类地址来表示一个网段。在 CIDR 的概念里没有ABC类地址的概念,所有地址段都可以用 a.b.c.d/m 的形式来表示。其中 a.b.c.d 是 IP 地址,m 是子网掩码。这类IP段的表示法就叫 CIDR 表示法。

如何将 IP 地址范围(以开始和结束 IP 地址的形式)转换成 CIDR 表示法,这是一个常见需求。有些防火墙和 ipset 操作不支持使用不连续的网段来表示一个集合,比如 192.168.1.1-192.168.1.10 是没法转换成单一的 CIDR 的,它如果转换成 CIDR 格式,会转换成多个 CIDR 地址段,比如:

    $ python3 ip_merge2.py -t ip 192.168.1.1-192.168.1.10
    192.168.1.1/32
    192.168.1.2/31
    192.168.1.4/30
    192.168.1.8/31
    192.168.1.10/32
    复制

    即一个 IP 段是由多个 CIDR 段组成。Github 上有一个 Go 语言写的工具可以处理这种 IP 段到 CIDR 的转换,项目地址为:

      https://github.com/zhanhb/cidr-merger
      复制

      之前也写过文章介绍过这款工具,文章地址:

      这里介绍一种在 Python 下的解决方案,这样就不需要调用第三方工具了,直接在代码中即可处理 IP 段到 CIDR 的转换。后面会贴出代码,将函数包装成命令行工具后,使用方法下:

        $ python3 ip_merge2.py
        usage: ip_merge2.py [-h] -t {cidr,ip} input
        ip_merge2.py: error: the following arguments are required: input, -t/--type




        $ python3 ip_merge2.py -t ip 0.0.0.0-9.255.255.255
        0.0.0.0/5
        8.0.0.0/7
        复制

        使用前要先安装 netaddr 模块:

          $ pip install netaddr
          复制

          用到的关键函数如下:

            >>> from netaddr import IPRange
            >>> def ip_range_to_cidr(ip_start, ip_end):
            ... cidr_list = IPRange(ip_start, ip_end)
            ... return cidr_list
            ...
            >>> ip_range_to_cidr('192.168.1.1', '192.168.1.10')
            IPRange('192.168.1.1', '192.168.1.10')
            >>> from netaddr import IPSet
            >>> IPSet(ip_range_to_cidr('192.168.1.1', '192.168.1.10'))
            IPSet(['192.168.1.1/32', '192.168.1.2/31', '192.168.1.4/30', '192.168.1.8/31', '192.168.1.10/32'])
            >>>




            复制

            Python 代码如下:

              import argparse
              import ipaddress
              from netaddr import IPSet, IPRange


              def debug(func):
              def wrapper(*args, **kwargs):
              print(f"Function: {func.__name__}")
              print(f"Arguments: {args}")
              print(f"Keyword Arguments: {kwargs}")
              print()
              result = func(*args, **kwargs)
              return result
              return wrapper




              def ip_to_cidr(ip):
              ip_network = ipaddress.ip_network(ip, strict=False)
              return str(ip_network)




              # @debug
              def ip_range_to_cidr(ip_start, ip_end):
              cidr_list = IPRange(ip_start, ip_end)
              return cidr_list




              def cidr_to_ip_range(cidr):
              ip_network = ipaddress.ip_network(cidr, strict=False)
              ip_range = (str(ip_network.network_address), str(ip_network.broadcast_address))
              return ip_range




              def main():
              parser = argparse.ArgumentParser(description="Convert between CIDR and IP range")


              parser.add_argument("input", help="Input CIDR, IP address, or IP range")
              parser.add_argument("-t", "--type", choices=["cidr", "ip"], help="Specify input type (cidr or ip)", required=True)


              args = parser.parse_args()


              if args.type == "cidr":
              if "/" not in args.input:
              print("Error: Invalid CIDR input. Make sure to include the subnet mask.")
              return
              result = cidr_to_ip_range(args.input)
              print(f"CIDR to IP Range: {args.input} -> {result}")
              elif args.type == "ip":
              if "-" in args.input:
              ip_start, ip_end = map(str.strip, args.input.split("-"))
              result = ip_range_to_cidr(ip_start, ip_end)
              # print(f"IP Range to CIDR: {args.input} -> {IPSet(result)}")
              for i in result.cidrs():
              print(i)
              elif "/" in args.input:
              print("Error: Invalid IP input. Remove the subnet mask for IP address input.")
              return
              else:
              result = ip_to_cidr(args.input)
              print(f"IP to CIDR: {args.input} -> {result}")


              if __name__ == "__main__":
              main()


              复制

              全文完。

              如果转发本文,文末务必注明:“转自微信公众号:生有可恋”。

              文章转载自生有可恋,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论