在实际工作中,我终于遇到了一些实实在在的面试题:

  • 算法题:一个包含海量节点的无序链表,已知里面有多个重复元素,找出重复次数最多的那个,给出时间复杂度。比如20-1-2-3-5-7-3-20-12-3,重复元素有3个3,2个20答案显然是3。

在进行流量分析,DDoS检测与防护,流量清洗等动作时,一个很常见的需求就是“求top N”,与之相关的算法可谓汗牛充栋:

  • 排序
  • 最大堆
  • LRU
  • Bitmap counter

理论算法很多,但在实战中总是会碰到各种工程问题,比如说并发操作的锁开销不可避免,那么设计一个装置而不是设计一个算法就是自然而然的想法了。

另外值得注意的的是,检测异常流量是一种模糊操作,未必需要精确的运算和精确的匹配,只需要定位到异常即可,因此还有一种思路就是使用percpu数据结构,读取数据时容忍精度的损失,用精度换效率。

下面是我在下班路上想到的两种方法,求top 1非常适合,稍微也能求top N。这两种方法实现起来非常简单通透。

方法1:多hash链表(类bloom filter的思路)

两个(或多个)使用不同hash算法的hash表链接IP地址结构体,并为per bucket计数:

数据结构如下:

struct bucket {int hash;atomic_t count;spinlock_t lock;struct list_head hlist;
};struct IP_item {struct list_head list;u32 ipaddr;
};#define HSIZE    8192struct bucket hlist[2][HSIZE];

操作:

  • IP进入:以IP地址(源或者目标,取决于配置)分别计算hash1和hash2,分别链入相应的list,递增bucket计数。

  • timer到期或者conntrack销毁:摘除相应的IP结构体,递减bucket计数。

异常检测:

  • 两个hash表同时存在某个bucket计数超过平均计数器的β\betaβ倍,且两个最大bucket计数器相差不超过α\alphaα,视为异常:

    Lmax>βLmeanL_{max}>\beta L_{mean}Lmax​>βLmean​

    其中平均长度计算如下:

    Lmean=Σn=0Nbuckets−1Ln−LmaxNbuckets−1L_{mean}=\dfrac{\Sigma_{n=0}^{N_{buckets}-1}{Ln}-L_{max}}{N_{buckets}-1}Lmean​=Nbuckets​−1Σn=0Nbuckets​−1​Ln−Lmax​​

  • 遍历两个最大计数器的bucket链表的开始γ\gammaγ个元素(γ\gammaγ为平均长度的两倍),取两个链表最大重复交集。即为异常IP地址。力扣349:https://leetcode-cn.com/problems/intersection-of-two-arrays/

评价:

  • IP进入需要hash计算操作,O(1)时间复杂度。

  • IP结构体插入hash bucket链表,O(1)时间复杂度。(可能spinlock开销大,可percpu优化)

  • 异常检测冒泡两个hash表最大bucket计数器(bucket固定),O(1)时间复杂度。

  • 遍历两个链表KaTeX parse error: Undefined control sequence: \gama at position 1: \̲g̲a̲m̲a̲个元素,O(n)时间复杂度,假设hash算法是均匀的,LmeanL_{mean}Lmean​是很小的。

方法2:hash计数器(同样是bloom filter的思路)
将IP地址按照每8位拆分,每8位为256个counter,同时设置两个hash counter:

数据结构如下:

struct bucket {int hash;atomic_t count;
};struct bcounter {atomic_t counter;
};#define HSIZE    8192struct bucket hlist[2][HSIZE];
struct bcounter counter[4][256]

操作:

  • IP进入:以IP地址(源或者目标,取决于配置)分别计算hash1和hash2,分别递增两个hash counter对应的计数器,同时递增per octet counter组对应bit的计数器。

  • timer到期或者conntrack销毁:递减相应计数器。

异常检测:

  • 两个hash表同时存在某个bucket计数超过平均计数器的β\betaβ倍,且两个最大bucket计数器相差不超过α\alphaα,视为异常:

    Lmax>βLmeanL_{max}>\beta L_{mean}Lmax​>βLmean​

    其中平均长度计算如下:

    Lmean=Σn=0Nbuckets−1Ln−LmaxNbuckets−1L_{mean}=\dfrac{\Sigma_{n=0}^{N_{buckets}-1}{Ln}-L_{max}}{N_{buckets}-1}Lmean​=Nbuckets​−1Σn=0Nbuckets​−1​Ln−Lmax​​

  • 取per octet counter组的最大计数器,按其位置作索引拼接成IP地址,用两个hash表验算。如果同时落到了最大计数器的bucket(即第一步发现的那两个bucket),则该拼接的地址就是异常地址,否则选择次小继续。

不是per octet counter组最大值拼接出来的概率很低,我们假设IP地址是均匀的长尾分布。

评价:

  • 省去了hash链表的维护,特别是spinlock的使用。

优化版本

可以多加两个重叠per octet counter组,提高准确性,如此一来,从低位到高位,对于每一个octet,只要能high 4bit和下一个octet的low 4bit能对应拼接的,超级大概率就是top 1地址,除此之外,还能求top N地址:

背后的思路是,正常无突刺的情况下,IP地址的足够散列的,异常情况下,32位IP地址的任何子区间均会出现突刺,把所有的突刺拼接起来,就是一个完整的IP地址。

然而,如果出现了两个或者多个flood程度相当的IP地址出现,便难以检测了,需要排列组合来验算。一种应对这种情景的思路就是对每一个子区间进行top N排序,然后再验算。

总结

大家建议我用bitmap直接做,对于IPv4地址而言,耗费4G的地址空间。然而我是在内核里做这个功能,4G实在是玩不起啊(其实也没什么,但还是觉得不优雅)。bitmap是一个好方法,但如何能少占些内存呢?

于是想到了模糊的求解方法,也就是方法一,这个方法在极端情况下可能会失效,比如hash算法被攻破,hash散列产生了严重的畸变,但大多数情况下是好使的。在review的时候,per bucket的spinlock依然是心头一阵痛。

方法二是直接根据bitmap的思路改造的,好使,无锁。事实上,如果仔细观察和分析这些拆分出来的bits子区间,还是能挖出更多东西的,求top N完全不在话下,只是说目的达到了,也就没有再继续了。

BTW,以后面试千万不要再让人写个查找算法了,应该考察工程实现上的问题,比如并发操作如何优化锁的开销等等,写一个实际可用的spinlock要比写一个算法有用的多!大部分程序员职业生涯中除了面试根本不会再写快速排序,但就我这个成年不写几行代码的不会编程的人,这不也必须写一个在Linux内核里查找top 1 IP地址的实现了么?


浙江温州皮鞋湿,下雨进水不会胖。

在Linux内核接收路径查找top 1的IP地址相关推荐

  1. 【Windows 逆向】CE 地址遍历工具 ( CE 结构剖析工具 | 从内存结构中根据寻址路径查找子弹数据的内存地址 )

    文章目录 一.CE 结构剖析工具 二.从内存结构中根据寻址路径查找子弹数据的内存地址 一.CE 结构剖析工具 游戏中的数据结构 , 需要靠调试和观察 , 才能发现其中的规律 ; 之前发现的 静态地址 ...

  2. 无盘服务器万象收费,万象收费机崩溃,客户机忘记解锁密码怎样查找原收费机IP地址?...

    今天给大家带来万象收费机崩溃,客户机忘记解锁密码怎样查找原收费机IP地址?,让您轻松解决问题. 万象收费机崩溃,客户机忘记解锁密码,如何查找原收费机IP地址? 现场情况: 1.万象收费机电脑系统崩溃无 ...

  3. Linux内核协议栈分析之网卡初始化——tcp/ip通信并不神秘(1)

    写在代码前: 写技术类文章的一个痛苦之处在于--写简单了,看的人觉得没意思:写难了,又看不懂是什么意思.例如--<redis源代码分析--事件机制>http://blog.csdn.net ...

  4. 一文讲解Linux 内核网络协议栈-数据从接收到ip层

    [推荐阅读] 一文了解Linux上TCP的几个内核参数调优 一文剖析Linux内核中内存管理 分析linux启动内核源码 此处主要讲的是从数据来到,中断到最终数据包被处理的过程. 0:首先来介绍一下I ...

  5. linux ssh ip地址命令,关于Linux:在ssh会话中查找客户机的IP地址

    我有一个脚本,由使用ssh登录到服务器的人运行. 有没有办法自动找出用户连接的IP地址? 当然,我可以问用户(这是一个程序员的工具,所以没问题),但如果我刚刚发现的话,会更酷. 建议转到服务器故障,不 ...

  6. linux上查找某个IP,grep正则表达式实现查找某个特定的IP地址

    下面会举几个在grep命令中使用正则表达式从一个文件中匹配到特定的IP地址.下面的正则表达式将会匹配IPV4的地址. 正则表达式匹配IP地址: 使用下面的正则表达式来匹配IPV4的地址,先来匹配从0. ...

  7. linux查看ip地址特定信息,grep正则表达式实现查找某个特定的IP地址

    下面会举几个在grep命令中使用正则表达式从一个文件中匹配到特定的IP地址.下面的正则表达式将会匹配IPV4的地址. 正则表达式匹配IP地址: 使用下面的正则表达式来匹配IPV4的地址,先来匹配从0. ...

  8. LInux初学者 必学 三种配置网卡IP地址的方式(非常详细)

    弱智选择安逸,强者选择永往向前.本文讲解Linux图形化.shell.命令三种方式配置IP地址静态网卡信息 基础 文章目录 目录 一.IP基础 二.三种网卡配置方式 1.图形化界面 2.nmtui:在 ...

  9. Linux/CentOS7给一个网卡设定多个IP地址, Linux网卡配置虚拟IP

    此文章摘自书籍: (跟阿铭学Linux第十三章 13.3.2部分内容) 案发现场 login as: root root@127.1.1.2's password: Last login: Tue M ...

最新文章

  1. c语言从stdin读入
  2. 动态规划4--最佳加法表达式
  3. LSASRV事件ID:40960
  4. faster rcnn的源码理解(一)SmoothL1LossLayer论文与代码的结合理解
  5. 推荐一款基于SpringBoot+Vue开发的分布式网盘系统(附源码)
  6. 经营升级渐成影院运营主课题,怎样才能交出技术改造好答卷?
  7. 看完这篇还不清楚Netty的内存管理,那我就哭了!
  8. 科幻电影系列-计算机技术
  9. WPF初学——自定义样式
  10. 关于GDAL计算图像坐标的几个问题
  11. Java 并发 —— Thread、Executor、线程池
  12. 2015蓝桥杯C++A:饮料换购
  13. C++ STL list 学习一
  14. 施耐德 m340 编程手册_施耐德PLC漏洞历险记
  15. hibernate 如何安装_python如何搭建WEB服务?
  16. 《世界历史》—史前时期的分期
  17. 区块链开源代码什么意思_区块链和开源社区有什么共同点
  18. 球与球的碰撞检测java测试_cocos 躲避球游戏(2) --资源导入和碰撞检测
  19. 此spoolsv.exe(木马程序)非彼spoolsv.exe(系统进程)
  20. 奥利给 之 【优学院自定义速度】

热门文章

  1. Transformer的PyTorch实现(超详细)
  2. 如何免费开通微信公众号留言功能(上)
  3. 20221227英语学习
  4. p5.js 交互应用实战 —— 音乐可视化(案例)
  5. 泉州数字平台让城市管理更“智慧” 数分钟“搞掂”
  6. WinGate 6.0 build 984铪铪铪
  7. Android:RecyclerView滑动到边缘时的光晕效果
  8. ARM7开发板模拟器Skyeye安装设置全攻略
  9. 解决win10更新后vmware无法启动问题
  10. 27岁技术总监,收入太高,心头慌得一比。。。