zeus00456

导航

ipv6的cidr和地址池校验(js实现,不是校验ipv6的格式)

要求

用户填写:
一个ipV6的cidr
若干对ipv6地址,每一对地址作为起始/截止地址,可以夹出一个地址池来

要求校验
用户填写的地址池都在上述cidr的范围内,且互相不重复。

思路说明

一般ipv4的cidr校验是基于数值的。流程大约如下:

  • ip按 '.' 切分
  • 数组中元素转二进制
  • 上一个元素左移8位加下一个元素
  • 最后得到一个值,这个值就是个ip对应的值
  • 把 cidr 中 /x 解析成高x位为1剩余位为0的二进制
  • 两个二进制按位与,然后低位全置0为ip的最小值,全置1为ip的最大值,这就是cidr的范围,只需要校验ip在这个范围中即可

这个思路直接套用在ipv6中明显是行不通的,因为这个校验最终是将ip转换为一个数字进行比大小,一个数字比大小的前提是得现有一个数字可以精确的表示ip。ipv6,是128 bit的,很明显不可以。
我们可以使用比较直接的替代思路,既然一个数不能表示,那就换若干个数。一种可行方案是将ipv6表示成一个长度为8的数组,每个元素是一个 0-2^16 的数字(四位16进制)。这种方式可以通过比较数组中元素的大小判断对应ip的大小。

当然,我们可以使用其他思路。如何判断一个ip是在cidr中的?只需要ip和cidr的前x位二进制是一致的。如何达成这个判断?只需要二者使用同样的标准进行表达,都转成二进制是可以的,都转成16进制也是可以的。至于为什么转16进制,因为ipv6默认使用16进制表达,并且它比较短。
那么比较思路分为两个部分:
1,我们把用:分割的一部分ipv6叫做一段,那么用/x中的x,除16,可以得到如果此ip在cidr中,则16进制表示的ipv6应该有几段是一致的
2,使用/x 中的x,对16取模,可以获取“若ip在cidr中,则开始允许不一样的段中前几位应该是一样的”
解释“开始允许不一样的段”。若1111:1111:1111:1111:1111:1111:1111:1111/100为cidr,要求前100bit一样,100/16=6,则此cidr的前6段和ip的前6段应一模一样,第7段开始可以不一样。但第7段还需要校验前100%16=4比特位,要求保持一致。其他位不用验证,他们都是可以变的。

第二个问题,我们用两个ipv6地址可以夹出一个地址池来,如何判断地址池相互不重叠?
这个相对比较简单,
首先,所有地址转为ipv6的完整形态,即8段4位16进制
然后,字典顺序排序
最后,用每一对地址池的起始地址去排序结果中查找,只要起始地址在排序中的后一个地址是与之相对的结束地址即可
举例说明,若有3对地址,分别为ab,cd,ef,排序后的数组为z,那么分别使用ace去z中查找,只要z中ace后面的元素分别为bdf即可。结合下图理解,比较直观
在这里插入图片描述

代码实现

因为最初就是给前端提供的,所以是js实现

//补全4位16进制
function toFullHexSegment(part){
	return part.padStart(4,'0');
}
//ip(不是cidr) 转标准ipV6
function toStandardIpv6(ip){
	if(-1 != ip.indexOf('.')){//ipv4/6融合的写法
     var head = ip.substring(0,ip.lastIndexOf(':'));
     var tail = ip.substring(ip.lastIndexOf(':')+1,ip.length);
     tail = tail.split('.');
     head = head+ ':' +toFullHexSegment((parseInt(tail[0],10)<<8+parseInt(tail[1],10)).toString(16));
     head = head+ ':' +toFullHexSegment((parseInt(tail[2],10)<<8+parseInt(tail[3],10)).toString(16));
     ip = head;
    }
    //按32bit补0
    ip = ip.split(/:/);
    while(8!=ip.length) ip.splice(ip.indexOf(''),0,'0');
    for(var i=0;i<ip.length;i++) {
        if(4 != ip[i].length) {
            ip[i]=toFullHexSegment(ip[i]);
        }
    }
    return ip.join(':');
}
//从标准ipv6中获取第n个部分的二进制 [的前x位] 的字符串表现形式
function checkoutPartInIpv6(ip,n,x){
	if(x >= 0)
		return parseInt(ip.split(':')[n-1],16).toString(2).padStart(16,'0').substring(0,x);
	else
		return parseInt(ip.split(':')[n-1],16).toString(2).padStart(16,'0');
}
//校验ip是不是在cidr中
function checkInCidr4Ipv6(cidr,ip){
	cidr = cidr.split('/');
	var targetIp = toStandardIpv6(cidr[0]);//cidr中解析的靶ipv6的标准化

	var checkIp = toStandardIpv6(ip);//待比较的ip的标准化
	var samePartNum = ~~(cidr[1]/16);//有几段ipv6一模一样
	var sameIpStrNum = samePartNum * 4 + samePartNum -1;//有几位字符串一致
	var sameBinNum = cidr[1]%16;//需要比较几位二进制一致

	if(targetIp.substring(0,sameIpStrNum) != checkIp.substring(0,sameIpStrNum)){
		//alert('字符串比较部分不一致');
		return false;
	}
	//alert(checkoutPartInIpv6(targetIp,samePartNum+1));
	if(checkoutPartInIpv6(targetIp,samePartNum+1,sameBinNum)!=checkoutPartInIpv6(checkIp,samePartNum+1,sameBinNum)){
		//alert('二进制比较部分不一致');
		return false;
	}
	return true;
}

function checkAddressPool4Ipv6(cidr,addresspools){
	var addresses = addresspools.slice();//复制数组
	addresses.forEach((value,index)=>{addresses[index]=toStandardIpv6(value)});
	var addressesSorted = addresses.slice().sort();//复制数组,并排序

	//地址池全是合法的
	for(var i=0; i<addresses.length; i++)
		if(!checkInCidr4Ipv6(cidr,addresspools[i]))
			return false;
	for(var i=0; i<addresses.length; i+=2)
		if( addresses[i+1] != addressesSorted[addressesSorted.indexOf(addresses[i])+1] )
			return false;
	return true;
}

//demo
alert(checkInCidr4Ipv6('fd00::1007:1:0/112','fd00::1007:1:1'));

posted on 2022-07-28 16:39  问仙长何方蓬莱  阅读(638)  评论(0编辑  收藏  举报