如何将 IP 地址与子网匹配并获取 SUM

Posted

技术标签:

【中文标题】如何将 IP 地址与子网匹配并获取 SUM【英文标题】:How to match IP addresses to a Subnets and get SUM 【发布时间】:2021-02-04 22:29:12 【问题描述】:

你能帮我在 Clickhouse 中做这个 SELECT 吗?

我想测量来自某些网络的流量统计。我正在使用带有 2 个表的 clickhouse db:

select * from network_account_db  

┌─network───────----─┬─source─┬─category─┐
│ 192.168.200.0/29   │ server │ general  │
│ 192.168.200.11/30  │ server │ general  │
│ 192.168.200.22/32  │ server │ general  │
└───────────────----─┴────────┴──────────┘

 select  packetDate,packetDateTime,sampleRatio,srcIp,dstIp,length from traffic  
 
┌─packetDate─┬──────packetDateTime─┬─sampleRatio─┬─────srcIp─┬──────dstIp─┬─length─┐
│ 2021-02-04 │ 2021-02-04 22:15:20 │           1 │ 232998210 │  767413237 │   1280 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │  918211986 │     40 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1150185814 │  30088 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1168387235 │     52 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1169107244 │    104 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1169107244 │     52 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1224157376 │    617 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1476066034 │   1425 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1600411769 │   4656 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1743465996 │     52 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1746016762 │    108 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 1746284673 │    901 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 3194642526 │   1976 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 2315259109 │   2403 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 2540034693 │     52 │
│ 2021-02-04 │ 2021-02-04 22:15:19 │           1 │ 767413237 │ 2540034693 │     52 │

我想测量类似的流量

select sum(length * sampleRatio ) AS total, category  
from ( select network as net from network_account_db where source='server'   ) 
where  srcIp=IPv4StringToNum(net)

我需要得到,例如:

category=general
total=242422

你能帮我得到正确的选择吗?我有一个带网络的表和另一个带 IP 的表。

更新

嗨。我需要在选择中添加另一个字段,例如:

SELECT dictGet('network_account_db.reputation_rbl_db', 'category', tuple(srcIp)) AS category, dictGet('network_account_db.reputation_rbl_db', 'source', tuple(srcIp)) AS source,   sum(length * sampleRatio) AS total_bytes,   sum(numberOfPackets * sampleRatio) AS total_pps, IPv4StringToNum(srcIp), IPv4StringToNum(dstIp)   FROM traffic  WHERE dictHas('network_account_db.reputation_rbl_db', tuple(srcIp))  GROUP BY category   order by packetDateTime desc```
but appear an error like:

srcIp 不在聚合函数下,也不在 GROUP BY 中:```

如果我将它添加到 GROUP BY,我会在响应中丢失一些行。

有没有办法用 UNION 或类似的方式添加这些字段?

【问题讨论】:

【参考方案1】:

试试这个直接的方法:

SELECT
    category,
    sum(length * sampleRatio) AS total
FROM
(
    WITH
        (
            SELECT groupArray((category, ip_num_from, ip_num_to))
            FROM
            (
                /* get the lower range and the higher range of the subnet */
                SELECT
                    category,
                    splitByChar('/', network) AS ip_parts,
                    IPv4CIDRToRange(IPv4StringToNum(ip_parts[1]), toUInt8(ip_parts[2])) AS ip_ranges,
                    toUInt32(ip_ranges.1) AS ip_num_from,
                    toUInt32(ip_ranges.2) AS ip_num_to
                FROM network_account_db
            )
        ) AS networks
    SELECT
        /* find the first subnet which covers IP  */
        arrayFirst(n -> srcIp BETWEEN n.2 AND n.3, networks) AS network,
        network.1 AS category,
        length,
        sampleRatio
    FROM traffic
    /* exclude orphans IPs */
    WHERE category != ''
)
GROUP BY category

考虑使用ip-trie-dictionary 来简化覆盖IP 的搜索子网:

CREATE DICTIONARY networks_dict (
  network String,
  source String,
  category String
)
PRIMARY KEY network
SOURCE(CLICKHOUSE(host 'localhost' port 9000 db 'test' table 'network_account_db' user 'default'))
LAYOUT(IP_TRIE())
LIFETIME(3600);
SELECT
    dictGet('test.networks_dict', 'category', tuple(srcIp)) AS category,
    sum(length * sampleRatio) AS total
FROM traffic
WHERE dictHas('test.networks_dict', tuple(srcIp))
GROUP BY category

这些想法在这里借用Add function to check if an IPv4/6 is in a list of subnets #6808。

【讨论】:

谢谢!!。有了字典就更简单了。我能做到 !!!。再次感谢。 Clickhouse 对我来说是新的,它就是力量! @maxid np - 我很乐意提供帮助。只需考虑到字典会定期刷新(请参阅 LIFETIME-settings ),并且在常见的原因中,它不包含实际数据。当您需要将 URL 匹配到多个子网时,基于字典的方式不起作用(因为它只返回一个任何第一个匹配项)并且需要使用第一个“直接方式”。

以上是关于如何将 IP 地址与子网匹配并获取 SUM的主要内容,如果未能解决你的问题,请参考以下文章

如何使用python获取ip和子网掩码地址[关闭]

在MFC中如何获取电脑IP 主机名 网关 MAC 子网掩码等

delphi 获取IP,子网,网关,DNS

将IP地址匹配到子网掩码表中……Java的最佳方法是什么?

获取mac地址所有者

如何用python脚本获取ip,掩码,地址,网段