sqlserver 根据IP和数量,计算应该使用的掩码IP地址段
起因
最近在采集各个IP段的来源,然后,根据来源相同的IP,合并成一个大IP段,用来制作一个IP禁止或同行的策略。
CSDN 文盲老顾的博客,https://blog.csdn.net/superwfei
老顾的个人社区,https://bbs.csdn.net/forums/bfba6c5031e64c13aa7c60eebe858a5f?category=10003&typeId=3364713
但是,在合并了相同来源,并计算相同来源之后的 IP 段之后,出现了一点点小麻烦,需要人工去计算对应的掩码,来简化IP策略。
比如:1.0.1.0这个IP段,来源相同的有768个IP,即:1.0.1.0-1.0.3.255。那么,对应的掩码应该是 1.0.1.0/24 和 1.0.2.0/23 两个掩码地址段。
但是人工计算就太麻烦了,所以,使用 sqlserver 来进行了一下计算。
数据来源
使用采集方式,可以从百度获得IP的来源,以老顾获取的数据为例:
text
1.0.0.0 16777216 美国 Cloudflare IDC
1.0.1.0 16777472 中国 福建省 福州市 未知 未知
1.0.2.0 16777728 中国 福建省 福州市 未知 未知
1.0.3.0 16777984 中国 福建省 福州市 未知 未知
1.0.4.0 16778240 澳大利亚 维多利亚州 墨尔本 Gtelecom P 机构专线
1.0.5.0 16778496 澳大利亚 维多利亚州 墨尔本 Gtelecom P 机构专线
1.0.6.0 16778752 澳大利亚 维多利亚州 墨尔本 Gtelecom P 机构专线
1.0.7.0 16779008 澳大利亚 维多利亚州 墨尔本 Gtelecom P 机构专线
1.0.8.0 16779264 中国 广东省 广州市 未知 未知
1.0.9.0 16779520 中国 广东省 广州市 未知 未知
1.0.10.0 16779776 中国 广东省 广州市 未知 未知
1.0.11.0 16780032 中国 广东省 广州市 未知 未知
1.0.12.0 16780288 中国 广东省 广州市 未知 未知
1.0.13.0 16780544 中国 广东省 广州市 未知 未知
1.0.14.0 16780800 中国 广东省 广州市 未知 未知
1.0.15.0 16781056 中国 广东省 广州市 未知 未知
1.0.16.0 16781312 日本 东京都 东京 works 机构专线
1.0.17.0 16781568 日本 未知 机构专线
1.0.18.0 16781824 日本 未知 机构专线
1.0.19.0 16782080 日本 未知 机构专线
1.0.20.0 16782336 日本 未知 机构专线
1.0.21.0 16782592 日本 未知 机构专线
1.0.22.0 16782848 日本 未知 机构专线
1.0.23.0 16783104 日本 未知 机构专线
1.0.24.0 16783360 日本 未知 机构专线
1.0.25.0 16783616 日本 未知 机构专线
1.0.26.0 16783872 日本 未知 机构专线
1.0.27.0 16784128 日本 未知 机构专线
1.0.28.0 16784384 日本 未知 机构专线
1.0.29.0 16784640 日本 未知 机构专线
1.0.30.0 16784896 日本 未知 机构专线
1.0.31.0 16785152 日本 未知 机构专线
将采集的结果导入到sqlserver 后,可以对连续的相同IP用途、来源进行合并
数据合并
sql
;with t as (
select *
,(case
when ISP in ('亚马逊','微软公司','美国礼来公司','阿里云','华为','腾讯','甲骨文软件系统有限公司','OCULUS NETWORKS INC','Partner Communications') then ISP
when 企业<>'' then 国 + ' ' + 企业
when 国<>'中国' and ISP<>'未知' then 国 + ' ' + ISP
--when 国='中国' and 用途<>'未知' then master.dbo.regexreplace(省,'[省市]','') + master.dbo.regexreplace(市,'[省市]','') + ' ' + 用途
when 国='中国' then
(
case when 省='' and isp<>'未知' and 用途<>'未知' then isp + ' ' + 用途
when 省<>'' then master.dbo.regexreplace(省,'([省市]|((回族)?自治|特别行政|)[区州])','') + master.dbo.regexreplace(市,'([省市]|布依族苗族自治州|苗族侗族自治州)','') + ' ' + 用途
else
国 + ' ' + 用途 end
)
else 国 + ' ' + 用途 end
) txt
,row_number() over(order by ip_l) rid
from IP数据表
--where ip_l <16778240
)
select *
into #t1
from (
select *,0 nid from t where rid=1
union
select * ,row_number() over(order by a.rid) nid
from t a
where exists(select top 1 1 from t where rid=a.rid-1 and txt<>a.txt)
union
select '',min(ip_l) + 65536 * 256,'','','','','','','',99999,count(0) + 1
from t a
where exists(select top 1 1 from t where rid=a.rid-1 and txt<>a.txt)
) a
order by ip_l
在这里,我以数据第一行和最后一行对数据进行了一下补充,避免无法计算IP数量。
其中用途的计算是根据老顾个人的习惯来的,可自行调整,然后将结果存放到临时表 t1 中
计算连续IP数量
sql
select a.ip_l,a.ip,a.txt,(b.ip_l-a.ip_l) nums
into #t2
from #t1 a
left join #t1 b on a.nid+1=b.nid
where b.ip is not null
得到的数据如下:
html
16777216 1.0.0.0 美国 Cloudflare, Inc 256
16777472 1.0.1.0 福建福州 未知 768
16778240 1.0.4.0 澳大利亚 Gtelecom Pty Lt 1024
16779264 1.0.8.0 广东广州 未知 2048
16781312 1.0.16.0 日本 works 256
16781568 1.0.17.0 日本 机构专线 3840
16785408 1.0.32.0 广东广州 IDC 256
16785664 1.0.33.0 广东广州 未知 7680
16793344 1.0.63.0 广东广州 IDC 256
16793600 1.0.64.0 日本 Enecom.Inc. 16384
16809984 1.0.128.0 泰国 TOT Public Compan 17664
16827648 1.0.197.0 泰国 Ministry of Educa 256
16827904 1.0.198.0 泰国 TOT Public Compan 14848
对连续的IP进行数量计算,并将结果保存到临时表 t2 中。
其中第一列的整形为 bigint 类型,与第二列的字符串 IP 是可以互相转换的,为了方便,先将对应函数贴出来。
使用自定义函数,转换IPv4 与 bigint 类型
sql
CREATE FUNCTION [dbo].[fn_BigIntToIPv4]
(
@num bigint
)
RETURNS varchar(15)
AS
BEGIN
DECLARE @result varchar(15)
--select @result = master.dbo.fn_varbintohexstr(@num)
SELECT @result = (
select stuff((
select '.'+convert(varchar(3),convert(int,substring(master.dbo.fn_cdc_hexstrtobin('0x'+match),1,1)))
from master.dbo.regexmatches(substring(master.dbo.fn_varbintohexstr(convert(bigint,@num)),11,18),'[\s\S]{2}')
for xml path('')
),1,1,''))
RETURN @result
END
CREATE FUNCTION [dbo].[fn_IPv4ToBigInt]
(
@ip varchar(15)
)
RETURNS bigint
AS
BEGIN
DECLARE @result bigint
select @result = (select convert(bigint,substring(master.dbo.fn_cdc_hexstrtobin('0x'+(
select
substring(master.dbo.fn_varbintohexstr(convert(int,match)),9,10)
from master.dbo.regexmatches(@ip,'\d+')
for xml path('')
)),1,4)))
RETURN @result
END
关于 regex 相关的函数,可以参考正则表达式使模式匹配和数据提取变得更容易(David Banister)一文的内容。
根据 起始 IP 地址和连续 IP 数量,进行掩码计算
sql
CREATE FUNCTION [dbo].[IPmask]
(
@ip varchar(20),@nums bigint
)
RETURNS TABLE
AS
RETURN
(
with t1 as (
select master.dbo.fn_IPv4ToBigInt(@ip) l,@nums n,@nums s,0 t
union all
select l / 2,n / 2,s,t + 1
from t1
where l % 2 = 0 and n > 0 and s >= power(2,t + 1)
union all
select (l + 1) * power(2,t),s - power(2,t),s - power(2,t),0
from t1
where s > 0 and (s < power(2,t + 1) or l % 2 = 1)
),t2 as (
select *,row_number() over(partition by s order by n) rid
from t1
where s > 0
)
select master.dbo.fn_biginttoIPv4(l * power(2,t)) + '/' + convert(varchar,32 - t) v,row_number() over(order by s desc) nid
from t2
where rid=1
)
创建一个表值函数,用来生成应该使用的 IP 掩码地址段。
在这里使用了 CTE 递归,但是,有很大概率,会出现递归超过100层的情况,所以,我们需要在使用的时候,追加上 OPTION (MAXRECURSION 0) 参数。
需要注意的是,在函数内,是无法使用该参数的,只能在调用该表值函数的查询指令中使用该指令。
同时,为了能保证生成出来的地址掩码是连续的,且从小到大排列,所以追加了开窗排序函数的使用。
最终计算
sql
select convert(varchar(15),ip) ip,ip_l,nums,v,convert(nvarchar(20),txt) txt,nid
from #t2
cross apply master.dbo.IPmask(ip,nums)
where ip_l<=master.dbo.fn_ipv4tobigint('1.0.255.0')
order by ip_l,nid
OPTION (MAXRECURSION 0)
使用 apply ,将初始IP和IP数量作为参数传递给刚才我们创建的函数,就可以得到对应的 IP 掩码结果了。
html
ip ip_l nums v txt nid
--------------- -------------------- -------------------- ---------------------------------------------- -------------------- --------------------
1.0.0.0 16777216 256 1.0.0.0/24 美国 Cloudflare, Inc 1
1.0.1.0 16777472 768 1.0.1.0/24 福建福州 未知 1
1.0.1.0 16777472 768 1.0.2.0/23 福建福州 未知 2
1.0.4.0 16778240 1024 1.0.4.0/22 澳大利亚 Gtelecom Pty Lt 1
1.0.8.0 16779264 2048 1.0.8.0/21 广东广州 未知 1
1.0.16.0 16781312 256 1.0.16.0/24 日本 works 1
1.0.17.0 16781568 3840 1.0.17.0/24 日本 机构专线 1
1.0.17.0 16781568 3840 1.0.18.0/23 日本 机构专线 2
1.0.17.0 16781568 3840 1.0.20.0/22 日本 机构专线 3
1.0.17.0 16781568 3840 1.0.24.0/21 日本 机构专线 4
1.0.32.0 16785408 256 1.0.32.0/24 广东广州 IDC 1
1.0.33.0 16785664 7680 1.0.33.0/24 广东广州 未知 1
1.0.33.0 16785664 7680 1.0.34.0/23 广东广州 未知 2
1.0.33.0 16785664 7680 1.0.36.0/22 广东广州 未知 3
1.0.33.0 16785664 7680 1.0.40.0/21 广东广州 未知 4
1.0.33.0 16785664 7680 1.0.48.0/21 广东广州 未知 5
1.0.33.0 16785664 7680 1.0.56.0/22 广东广州 未知 6
1.0.33.0 16785664 7680 1.0.60.0/23 广东广州 未知 7
1.0.33.0 16785664 7680 1.0.62.0/24 广东广州 未知 8
1.0.63.0 16793344 256 1.0.63.0/24 广东广州 IDC 1
1.0.64.0 16793600 16384 1.0.64.0/18 日本 Enecom.Inc. 1
1.0.128.0 16809984 17664 1.0.128.0/18 泰国 TOT Public Compan 1
1.0.128.0 16809984 17664 1.0.192.0/22 泰国 TOT Public Compan 2
1.0.128.0 16809984 17664 1.0.196.0/24 泰国 TOT Public Compan 3
1.0.197.0 16827648 256 1.0.197.0/24 泰国 Ministry of Educa 1
1.0.198.0 16827904 14848 1.0.198.0/23 泰国 TOT Public Compan 1
1.0.198.0 16827904 14848 1.0.200.0/21 泰国 TOT Public Compan 2
1.0.198.0 16827904 14848 1.0.208.0/20 泰国 TOT Public Compan 3
1.0.198.0 16827904 14848 1.0.224.0/19 泰国 TOT Public Compan 4
后记
这个函数目前是第一版,其实稍微有点不太好用,主要的方面就是,一个大的连续IP段中,出现个别其他用途的IP时,老顾暂时没想到怎么合并大的IP段,并将其中个别IP段标记为异常。
比如刚才的结果中,1.0.128.0-1.0.255.255 最理想的掩码表示,应该是 1.0.128.0/17 用途 泰国 TOT Public Compan,其中 1.0.197.0/24 为异常数据,用途为 泰国 Ministry of Educa。这样就只需要两个 IP 掩码段就能表述清楚了。
PS:这个函数也就是临时写出来的,如果有错误,还请进行指正。
