设为首页收藏本站

腾控科技论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 750|回复: 1

lwIP ARP协议分析

[复制链接]
steelen 发表于 2017-3-20 11:20:30 | 显示全部楼层 |阅读模式
ARP 协议分析总的来说,lwip将链路层ethernet的协议分组格式分为ether和etherarp
分开处理。ip分组先进入etharp_ip_input更新一下arp表项,然后直接进入
netif的input传递给上层ip层。arp分组直接进入etharp_arp_input。
不送如ip层。
奇怪的是,lwip把ether header的结构定义在etharp中。

-ARP 数据结构-- arp表状态
enum etharp_state {
  ETHARP_STATE_EMPTY,  /* 表项空 */
  ETHARP_STATE_PENDING,
  ETHARP_STATE_STABLE, /* 稳定状态表项,该表项中MAC值可直接取出 */
  ETHARP_STATE_EXPIRED  /* 超时表项 */
};

-- arp表项结构
struct etharp_entry {
  struct pbuf *p;  /* arp 请求队列 */
  struct ip_addr ipaddr;
  struct eth_addr ethaddr;
  enum etharp_state state;
  u8_t ctime; /* 超时值 */
};

-- ARP 链路层协议分组
struct etharp_hdr {
  struct eth_hdr ethhdr; /* ether header */
  u16_t hwtype;
  u16_t proto;
  u16_t _hwlen_protolen;
  u16_t opcode;
  struct eth_addr shwaddr;
  struct ip_addr2 sipaddr;
  struct eth_addr dhwaddr;
  struct ip_addr2 dipaddr;
}

- ARP 函数void etharp_init();
初始化所有静态ARP表项,状态为EMPTY。


static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags);
首先排除广播、多播及any地址。然后,
将ipaddr及ethaddr对加入arp表项中,该表项索引由find_entry获得。
加入后,将该arp表项中还未发送的IP包(这些IP包是由etharp_ouput函数,在
处理IP包发送时,由于未找到对应ip的mac地址,由etharp_query加入到
pending arp的未发送IP包链表中。现在我们得到mac和ip对应值后,就
可以将这些IP包发送出去),发送到netif驱动。
该函数被etharp_ip_input及etharp_arp_input调用。


static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags);
查询包含ipaddr的表项。
查询优先级:1. pending 2. stable 3. empty。
find_entry总是根据这些优先级查找是否由匹配项,如果有,立刻返回该项
索引。否则根据优先级产看是否有过期表项,并覆盖它。


void etharp_ip_input(struct netif *netif, struct pbuf *p);
该函数是再ip分组传上ip层之前,将ip分组的mac和ip映射到
arp表项中做更新。这样的设计好像比较浪费。完全可以加大arp
表项,去除该过程


void etharp_arp_input(struct netif *netif,
struct eth_addr *ethaddr, struct pbuf *p);
arp分组到来,直接交给该函数处理。
无论netif的ip地址是否被配置过,都将该ARP分组的源ip和mac地址
加入到ARP表项中。如果一个ARP请求的目的ip地址是本地ip地址。
则发送response ARP分组。


err_t etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q);
该函数分开处理两种地址类型的网络层包,
1)多播及广播。直接调用netif->linkoutput发送出去,该函数为网卡驱动。
2)单播。单播的IP包又分三种处理方式。a. 目的ip在arp表项中,并且stable。
则直接构造以太头,调用网卡驱动,发送以太帧。b. 目的ip不在arp表项中,
修改arp表项状态为pending,并调用etharp_request,发送ARP REQUEST。
c. 目的ip在ARP表项中。但状态为pending(这个状态是由b条件引起的,可能
ARP RESPONSE在处理该条件时还为返回)。将待发送的IP包的缓冲区PBUF_REF
替换成PBUF_POOL或PBUF_RAM(暂时不知道为啥)。这些包将会在update_arp_input
中被发送。

- ARP 协议处理流程图
腾控T919宽温以太网PLC上市,集成4路热电阻(PT100),同时集成8路DI,4路DO(晶体管输出),8路AI,3个串口
 楼主| steelen 发表于 2017-3-20 11:22:45 | 显示全部楼层

2011-6-13-20-55

1. ARP:

从功能上来说,arp可以简单的分成两个部分:

a. 当我要向目的ip发送一个数据包的时候,需要通过arp实现ip到物理地址(一般为mac地址)的映射------------》ethernet_output函数

b. 处理输入包,更新arp缓存,如果是ip包后递交给ip层,如果是arp包,对于不同的arp操作做相应的相应------------》etharp_input函数。

2011-6-13-20-55





ethernet_input函数:

以太网的帧类型可以是:IP,ARP 甚至可以是pppoe, wlan等。这里主要分为IP, ARP(注意:ip arp在以太网的帧类型中是并列的,所以在input这个函数中分为ip,arp两大部分)

对于ip类型的:主要工作就是看是否开启了ETHARP_TRUST_IP_MAC这个选项,如果开启了就是要用这个帧中的信息来更新arp缓冲(利用帧首部的源mac地址和帧数据中ip报文中的源ip地址),然后丢弃以太网帧首部传递给ip层(即ip_input)。

对于arp类型的:同样先更新arp缓存,然后判断arp报文的操作类型,在lwip中对于arp数据包实现了两种操作:(rarp请求,rarp响应已经几乎淘汰了)

a.arp请求:首先判断这个包是不是给本机的,如果是给本机的,在原有包的基础上重组一个回应包并发出(注意此处并没有重新分配一个pbuf,而是借用了原来的缓冲结构)。如果不是本机的忽略。、

b.arp回应:主要的工作是更新arp缓存,但是这一步已经在arp包刚进来的时候就处理了,所以这里不需要再重复做,这里有一些dhcp的东东,暂时还未涉及到。

这里无论是ip类型,还是arp类型,都会更新arp缓存,也就是我们的

update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags),其中netif是对应的网络接口,ipaddr和ethaddr分别是对应的ip地址和mac地址。这个函数中会去更新arp缓存,并且把这个arp表项的等待的队列通过发送函数发送出去()。

update_arp_entry:先通过调用find_entry找到对应ipaddr对应的表项——>设置相应的arp选项的成员(主要是state,netif, ethaddr, cttime)——>如果定义了arp_queue的话,并且这个arp表项上有未发送队列的话,把这些队列发送出去。其中find_entry这个函数蛮重要的,下面解释下这个函数的流程。

find_entry:

1. lwip有一个比较巧妙的地方,它并不是冲上去就是就把arp缓存中所有的表项搜索一遍,而是做了一个假设,假设这次的表项索引还是上一次的,if so ,we are really fast!(因为在很多情况下就是这样的)

2. 首先搜索分成三类,empty,suspending,stable。第一个是对状态为empty的检查,arp表项中第一个状态为empty的索引号,第二个对状态为suspend的检查,分成三部分,首先判断是否恰好为此次想要的ip对应的索引,如果是直接返回;不是的话又分这个索引是有queue还是没有queue,分别记录这两种类型中cttime最大的一个索引,第三个是对状态为stable的检查,分成两部分,首先判断是否恰好为此次想要的ip对应的索引,如果是直接返回;不是的话,记录状态为stable中cttime最大的(时间戳,最老的)。

因此这部分,主要做了两件事:

●如果arp缓存中有现成的索引,则直接返回(状态时suspend和stable);

●通过索引记录几个重要的参数:a. arp表项中第一个状态为empty的索引号 b. arp表项中最老的状态为suspend的有queue的索引号 c. arp表项中最老的状态为suspend的没有queue的索引号 d. arp表项中最老的状态为stable的索引号。这些参数是在arp缓存没有现成索引号时,会根据优先级来对这四个参数来选择或删除表项。优先级等级一次为:empty——》oldest stable——》oldest pending without queue——》oldest pending with queue

3. arp没有现存的缓存,而状态又不是empty的选项,意味着需要在里面删除现有的arp选项,这里则需要调用snmp_delete****,由于snmp的东东暂时还未看到,这里就不详细讲了。

4. 最后更新表项的一些成员,有状态,时间戳,索引缓存

ethernet_output函数:

由于是发送ip数据包,所以一开始需要增加缓冲区大小,大小为以太网的数据首部的大小。然后检查ip地址,可以分为广播包,多播包,单播包(单播包又分为是局域网内部还是局域网外面)

广播包:判断目的ip地址是不是为全1,或者是全0(老版本中使用的),如果是广播包则目的ip的mac地址不需要查询arp缓存或者发送arprequest,mac地址为全一,即0xff,0xff,0xff,0xff,0xff,0xff。

多播包:判断目的ip地址是不是d类地址,即eXXXX,如果是多播的话,mac地址也是确定的,即将ip地址的低23位映射到mac地址为01-00-5e-00-00-00的低23位上。

单播包:要比较目的ip和本地ip地址,看是否是局域网内的,不是局域网内的,则调用默认网关的地址,然后再统一调用etharp_query(netif, ipaddr, q);函数

而广播包和多播包则无需调用etharp_query(netif, ipaddr, q);函数,因为已经得到了明确的mac地址,基本只需要添加以太网帧首部然后发送即可。

etharp_query:大概流程如下:

a. 先通过ipaddr利用函数find_entry找到arp缓存中的索引号

b. 根据索引号就能得到arp的对应项,此时根据项的state分成三大类,

empty:说明原来表项中是没有这个arp缓存的,所以把表项状态切换为pending并发送arp_requst包,把待发送的数据放在这个entry(表项)的队列上,系统在input的时候解析了这个ip后会发送(具体可以看前面input中的讲解);

pending:说明这个ip原来就有了,我们再重新发一次arp_request,同样把待发送数据放在队列上;

stable:说明在arp缓存中ip地址已经有了解析的mac地址,此时又分成两类,一类是数据包不为空,则直接调用etharp_send_ip函数发送;第二类是如果数据包为空,则说明是一个request包,还是调用arp_request

腾控T919宽温以太网PLC上市,集成4路热电阻(PT100),同时集成8路DI,4路DO(晶体管输出),8路AI,3个串口
您需要登录后才可以回帖 登录 | 注册

本版积分规则

产品样机试用申请

QQ|小黑屋|手机版|Archiver|腾控科技|腾控科技 ( 京ICP备09109731号  

GMT+8, 2018-1-19 21:09 , Processed in 0.117767 second(s), 19 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表