LwIP 协议栈源码详解 ——TCP/IP 协议的实现(九:ARP 表查询)

8 ARP 表查询
        ARP 攻击,是针对以太网地址解析协议(ARP)的一种攻击技术。在局域网中,ARP
病毒收到广播的 ARP  请求包,能够解析出其它节点的(IP, MAC)  地址,  然后病毒伪装为目
的主机,告诉源主机一个假 MAC 地址,这样就使得源主机发送给目的主机的所有数据包都
被病毒软件截取,而源主机和目的主机却浑然不知。ARP 攻击通过伪造 IP 地址和 MAC 地
址实现 ARP 欺骗,能够在网络中产生大量的 ARP 通信量使网络阻塞,攻击者只要持续不断
的发出伪造的 ARP 响应包就能更改目标主机 ARP 缓存中的 IP-MAC 条目。ARP 协议在设
计时᳾考虑网络安全方面的特性,这就注定了其很容易遭受 ARP 攻击。黑客只要在局域网
内阅读送上门来的广播 ARP 请求数据包,就能偷਀到网内所有的(IP, MAC)地址。而源节
点收到 ARP  响应时,它也不会质疑,这样黑客很容易冒充他人。   
        这一节主要针对 ARP 讲解 ARP 表的创建,更新,查询等操作。这里我们先从几个简单
的函数入手讲解 ARP 各个子模块功能,然后再将各个模块与上层协议结合起来,宏观的讲
解 ARP 模块。
         第一个需要迫不及待要说的函数是 find_entry,该函数最重要的输入是一个 IP 地址,返
回值是该 IP 地址对应的 ARP 缓存表项索引。函数声明原型如下,
static s8_t find_entry(struct ip_addr *ipaddr, u8_tflags) 
        这里,很有必要翻译一下源代码中注释的内容:该函数主要功能是寻找一个匹配的 ARP 表
项或者创建一个新的 ARP 表项,并返回该表项的索引号。如果参数 ipaddr 为给定的非空的
内容,则函数需要返回一个处于 pending 或 stable 的索引表项,如果没有匹配的表项,则该
函数需要返回一个 empty 的表项,但该表项的 IP 字段的值要被设置为 ipaddr 的值,这种情
况下, find_entry 函数返回后,调用者需要将表项从状态 empty 改为 pending。还有一种情况,
        如果参数 ipaddr 为空值,同样返回一个状态为 empty 的表项。
返回状态为 empty 的表项,首先从状态标示为 empty 的空闲 ARP 表项中选取,如果这
样的表项都用完了,同时参数 flags 的值被设置为 ETHARP_TRY_HARD,则 find_entry 就回
收最老的 ARP 表项,将该表项设置为 empty 状态返回。
        这个函数比较大,有将近 200 行代码,这里就不贴了,直接讲讲它的工作流程。这部
分的讨论还是参考了网上某位大侠的博客,名字记不得了,对不起啊啊啊啊啊!网络,有时
确实是个好东西,越发的明白。好了,看看 find_entry 的工作流程。
首先, lwip 有一个比较巧妙的地方,它并不是冲上去就是就把 arp 缓存中所有的表项搜
索一遍,而是做了一个假设,假设这次的表项索引还是上一次的 (在很多情况下就是这样的)。
所以,LWIP 中有个全局的变量 etharp_cached_entry,它始终保存着上次用到的索引号,如
果这个索引恰好就是我们要找的内容,且索引的表项已经处于 stable 状态,那就直接返回这
个索引号就完成了,we're really fast!
         如果情况不够理想,就必须去检索整个 ARP 表了,检索的过程是从 ARP 表的第一个表
项开始,依次往后检索直至最后一个表项,过程较复杂。对于每个表项首先判断它是否为
empty 状态, find_entry 只关心第一个状态为 empty 的表项索引值,对该索引值以后的 empty
表项不感兴趣,忽略。如果一个表项不是 empty 状态,则判断它是不是 pending 状态。对于
pending 状态的表项,需要做以下的事情,先看看它里面存的 IP 地址和我们的 ipaddr 是否匹
配,如果匹配,好返回该索引值,记住还要更新 etharp_cached_entry 为该索引值,如果不匹
配,则判断该索引的数据包指针是否为空,find_entry 试图记录生存时间最长的 pending 状
态有数据缓冲或无数据缓冲的表项索引。如果一个表项也不是 pending 状态,则判断它是不
是 stable 状态。对于 stable 状态的表项,与 pending 状态的表项处理过程相似,find_entry
试图记录生存时间最长的 stable 表项的索引。很晕吧,我也很晕,看了下面这段可能你会好
点!
         如果到这里都还没有找到匹配的表项,那就很杯具了,我们需要为 find_entry 调用者返
回一个 empty 的表项索引。经过上面一段后,find_entry 已经知道了第一个 empty 状态表项
的索引、生存时间最老的 pending 状态且有数据缓冲表项的索引、生存时间最老的 pending
状态且无数据缓冲表项的索引、生存时间最老的 stable 状态表项的索引,我们暂且先将这四
个值假设为 a、 b、 c、 d。如果参数 flags 的值被设置为 ETHARP_TRY_HARD,那么 find_entry
会按照 a-->d-->c-->b 的顺序选择一个合适的索引返回,为什么是这样的顺序?很明显,不
解释。find_entry 首先判断 a 是否在 ARP 表项范围内,如果是,则选择 a,如果不是,则判
断 b 是否在 ARP 表项范围内,依此类推。当选中一个索引后,随即就会将该索引对应的表
项设置为 empty 状态,并且将该表项的 IP 地址设置为 ipaddr 的值,ctime 值设置为 0,最后
返回索引。至此,find_entry 大功告成!
        接下来,我很感兴趣的一个函数是 etharp_query,该函数的功能是向给定的 IP 地址发送
一个数据包或者发送一个 ARP 请求,当然情况远不如此简单。还是很有必要翻译一下源代
码中函数功能注释的内容:如果给定的 IP 地址不在 ARP 表中,则一个新的 ARP 表项会被
创建,此时该表项处于 pending 状态,同时一个关于该 IP 地址的 ARP 请求包会被广播出去,
再同时要发送的数据包会被挂接在该表项的数据缓冲指针上;如果 IP 地址在 ARP 表中有相
应的表项存在,但该表项处于 pending 状态,则操作与前者相同,即发送一个 ARP 请求和
挂接数据包;如果 IP 地址在 ARP 表中有相应的表项存在,且表项处于 stable 状态,此时再
来判断给定的数据包是否为空,不为空则直接将该数据包发送出去,为空则向该 IP 地址发
送一个 ARP 请求。
      etharp_query 函数原型如下所示,源代码在 150 行左右,这里主要讲解其流程:
err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) 
   (1)  首先判断给定的 ipaddr 是否合法,对于空 IP 地址、广播 IP 地址、多播 IP 地址不予处理。
   (2)  将 ipaddr 作为参数调用函数 find_entry,函数返回一个 ARP 表项索引,该表项可能是原
来已经有的,此时该表项应该是 pending 或 stable 状态;该表项也可能是新申请得到的,
此时该表项应该是 empty 状态。
   (3)  根据返回的表项索引找到该 ARP 表项,判断该表项是否为 empty 状态,如果是,说明
该表项是新申请的,则将该表项状态设置为 pending 状态。
   (4)  判断要发送的数据包是否为空,或者判断 ARP 表项是否为 pending 状态,这两个条件只
要有一个成立,就发送一个 ARP 请求出去,发送 ARP 请求的函数是 etharp_request。
   (5)  如果待发送的数据包不为空,此刻就根据 ARP 表项的状态作不同的处理:若 ARP 表项
处于 stable 状态,则直接调用函数 etharp_send_ip 发送数据包;若 ARP 表项处于 pending
状态,则需要将该数据包挂接到表项的待发送数据链表上,由于 pending 状态的表项必
然在第(4)步中发出了一个 ARP 请求,当内核接收到 ARP 回应时,会将表项设置为 stable
状态,并将其链表上的数据全部发送出去,当然这项工作具体是怎样完成的那是后话了。
将数据包挂接在表项的发送链表上,这又是一个较复杂的过程:最重要的一点是判断该数据
包 pbuf 的类型,对于 PBUF_REF、PBUF_POOL、PBUF_RAM 型的数据包不能直接挂在发
送链表上,因为这些数据包在被挂接后并不会被立刻发送出去,这可能导致数据包在等待发
送的过程中内部数据被改动。对于以上这些类型的待发送数据包,需要将数据拷贝至新的
pbuf 中,然后将新的 pbuf 挂接至发送链表。至此,etharp_query 函数功德圆满!
时间总是在不经意间走完。。。我却没写完。 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值