使用Go语言计算网络IP地址的CIDR

duhf_think · · 6869 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  使用了Java、C#等许多语言好多年了,一直没怎么用过这些语言中的位移运算符,最近为了实现了一个小功能,才体会到这几个运算符的用途。在介绍如何用GO语言计算CIDR地址段之前,先介绍一下什么是网络地址的CIDR。

  CIDR全称Classless Inter-Domain Routing(无类域间路由),相比CIDR,现在全球的IPV4地址(通过32位比特位表示)早在上世纪80年代就被划分成了A、B、C、D、E五大类地址,其中A类地址的比特位以0开头,B类地址以10打头,C类地址以110打头,D类地址以1110打头,E类地址以1111打头。其中D类地址主要用于路由组播,E类地址暂时保留。每一类地址剩余的比特部分又被划分为网络和主机两部分,具体划分请见下图:

IP地址传统分类

  每一类IP地址具体范围如下图所示,其中A、B、C三类地址各自预留了一块内网保留地址,这几块保留地址是不是看起来很眼熟:

IP保留地址段

  虽然有了这五类地址(实际可利用的只有三类)的划分,但是IPV4地址资源的分配要比这个细得多,毕竟全球那么多组织和企业在伸手要,为了对这几大类IP地址更加细分,相关组织引入了通过掩码来细化IP段,从而进行更细粒度的分配。IPV4的掩码与IP地址一样也是用32位比特位进行表示,我们可以在IP地址后面加一个“/XX”来指明用于CIDR的掩码,其中XX为一个不大于32的数字,例如如果指定XX=22,那么就表明要使用“11111111.11111111.11111100.00000000”(22个比特1+10个比特0)作为IP细分的掩码,这种做法相当于不再只单独使用原来的ABCDE五类地址来划分IP地址,因此名字中使用了Classless这个词。在Linux下有个ipcalc第三方命令可以用于计算CIDR,大家可以自己安装体会一下。

ipcalc

  虽然有类似ipcalc的工具可用,但是开发网络程序时,还是得有个可以直接调用的lib,在Go下面搜寻未果的情况下,我决定自己实现一下,代码思路是非常简单的,主要是计算好掩码的位数即可,下面贴出来代码供同学们参考(为了演示,这里只计算了低16位的IP):

package main

import (
	"fmt"
	"strings"
	"strconv"
)

func main() {
	minIp, maxIp := getCidrIpRange("100.111.111.111/22")
	fmt.Println("CIDR最小IP:", minIp, " CIDR最大IP:", maxIp)
	fmt.Println("掩码:", getCidrIpMask(22))
	fmt.Println("主机数量", getCidrHostNum(22))
}

func getCidrIpRange(cidr string) (string, string) {
	ip := strings.Split(cidr, "/")[0]
	ipSegs := strings.Split(ip, ".")
	maskLen, _ := strconv.Atoi(strings.Split(cidr, "/")[1])
	seg3MinIp, seg3MaxIp := getIpSeg3Range(ipSegs, maskLen)
	seg4MinIp, seg4MaxIp := getIpSeg4Range(ipSegs, maskLen)
	ipPrefix := ipSegs[0] + "." + ipSegs[1] + "."

	return ipPrefix + strconv.Itoa(seg3MinIp) + "." + strconv.Itoa(seg4MinIp),
				ipPrefix + strconv.Itoa(seg3MaxIp) + "." + strconv.Itoa(seg4MaxIp)
}

//计算得到CIDR地址范围内可拥有的主机数量
func getCidrHostNum(maskLen int) uint {
	cidrIpNum := uint(0)
	var i uint = uint(32 - maskLen - 1)
	for ; i >= 1; i-- {
		cidrIpNum += 1 << i
	}
	return cidrIpNum
}

//获取Cidr的掩码
func getCidrIpMask(maskLen int) string {
	// ^uint32(0)二进制为32个比特1,通过向左位移,得到CIDR掩码的二进制
	cidrMask := ^uint32(0) << uint(32 - maskLen)
	fmt.Println(fmt.Sprintf("%b \n", cidrMask))
	//计算CIDR掩码的四个片段,将想要得到的片段移动到内存最低8位后,将其强转为8位整型,从而得到
	cidrMaskSeg1 := uint8(cidrMask >> 24)
	cidrMaskSeg2 := uint8(cidrMask >> 16)
	cidrMaskSeg3 := uint8(cidrMask >> 8)
	cidrMaskSeg4 := uint8(cidrMask & uint32(255))

	return fmt.Sprint(cidrMaskSeg1) + "." + fmt.Sprint(cidrMaskSeg2) + "." + fmt.Sprint(cidrMaskSeg3) + "." + fmt.Sprint(cidrMaskSeg4)
}

//得到第三段IP的区间(第一片段.第二片段.第三片段.第四片段)
func getIpSeg3Range(ipSegs []string, maskLen int) (int, int) {
	if maskLen > 24 {
		segIp, _ := strconv.Atoi(ipSegs[2])
		return segIp, segIp
	}
	ipSeg, _ := strconv.Atoi(ipSegs[2])
	return getIpSegRange(uint8(ipSeg), uint8(24 - maskLen))
}

//得到第四段IP的区间(第一片段.第二片段.第三片段.第四片段)
func getIpSeg4Range(ipSegs []string, maskLen int) (int, int) {
	ipSeg, _ := strconv.Atoi(ipSegs[3])
	segMinIp, segMaxIp := getIpSegRange(uint8(ipSeg), uint8(32 - maskLen))
	return segMinIp + 1, segMaxIp
}

//根据用户输入的基础IP地址和CIDR掩码计算一个IP片段的区间
func getIpSegRange(userSegIp, offset uint8) (int, int) {
	var ipSegMax uint8 = 255
	netSegIp := ipSegMax << offset
	segMinIp := netSegIp & userSegIp
	segMaxIp := userSegIp & (255 << offset) | ^(255 << offset)
	return int(segMinIp), int(segMaxIp)
}





有疑问加站长微信联系

本文来自:CSDN博客

感谢作者:duhf_think

查看原文:使用Go语言计算网络IP地址的CIDR

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:812540095

6869 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传