Lwip之ARP模块实现

一:数据结构

这里主要介绍arp表结构

struct etharp_entry {
#if ARP_QUEUEING
      /** 
       * Pointer to queue of pending outgoing packets on this ARP entry.
      */
      struct pbuf *p;
#endif
    struct ip_addr ipaddr;
    struct eth_addr ethaddr;
    enum etharp_state state;
    u8_t ctime;
};

该结构是ARP模块操作的主要数据结构,主要数据项包括:

IP地址:表示要获取该ip地址对应的端口的mac地址

MAC地址:用来保存获取后的mac地址

状态:表示当前的表项的状态

时间计数:每一个表项都有一个有效时间,通过该域可以知道当前的表项是否还有效

另外,还可能包含一个pbuf指针,用来指向等待该入口的数据包。这个数据项选用与否根据外部设置来决定。

通常,arp表是一个数组,其中的每一项表示一个ip-mac地址对,每时每刻,不断有新的地址对被添加进来,同时清除失效的地址对,这可以通过一定的策略来完成。(这里失效的地址对不一定就是时间失效的地址对)

二:函数接口

函数名:etharp_init()

功能:arp模块的初始化

操作:主要操作是初始化arp表,表项的时间清零,状态设置为空状态。

函数名:etharp_tmr()

功能:该函数是arp定时器超时执行函数,每当arp定时到达后执行该函数,它会清除arp表中失效的(超时的?)entry

操作:该函数遍历整个arp表项,每判断一个表项,就根据当前表项的状态和时间决定该表项下一时刻的状态。

函数名:find_entry(struct ip_addr *ipaddr, u8_t flags)

功能:查询arp表,找到一个匹配项或者新的入口

操作:分三步,首先遍历查询整个arp表,记住候选资格,其次选择具备候选资格的入口,最后创建新的入口。在一个简单的遍历查询中,需要做以下一些工作:记住第一个空的入口;记住最老的稳定的入口;记住最老的,不带排队数据包的未决入口;记住最老的带排队数据包的未决入口。除了空状态,对于其他状态,如果其ip地址匹配,则直接返回该表项的索引。另外,参数flags决定是否选择try_hard模式,如果选择了此模式,那么就允许创建新的入口通过回收活动的入口,也就是说这种模式下即使所有的入口都在使用,那么也会根据相应的算法回收一个最近最不重要的入口提供给新的请求。如果没有找到匹配项,并且flag参数设置了try_hard模式,就会按照下述策略进行新入口的选择:空入口;最老的稳定入口;最老的不带排队包的未决入口;最老的带排队包的未决入口。如果按照上述策略能够找到一个arp表项的索引,就将该索引作为新的入口。

函数名:update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags)

功能:在arp表中更新或者插入一个IP/MAC地址对。如果一个未决的入口已经被决定了,那么其上所有排队的包都将被发送。

操作:首先判断参数中的IP地址,如果不是单播地址,则当错误参数处理。之后调用find_entry找到一个索引项,设置该索引项。如果其上有排队的数据包,则发送这些数据包

函数名:etharp_ip_input(struct netif *netif, struct pbuf *p)

功能:使用收到的ip包的源地址更新本地网络的arp表。对该函数的调用处在收到数据包之后,传递给IP层之前。对该函数的调用主要用于更新arp表,基本操作为提取出接收到的数据包中的IP地址信息,如果是属于本地局域网,则调用update_arp_entry进行arp表的更新。

操作:首先判断包的源地址,如果不在本地网络上,则什么也不做,返回;否则,调用update_arp_entry更新arp表。

函数名:eth_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)

功能:响应arp请求;对于arp响应,添加入口项,并发送排队数据包;更新地址对。另外,该函数会进行释放pbuf的操作,也就是说为接收到的arp数据包分配的pbuf会在这里释放。如果是arp请求,则直接复用该pbuf,修改设置,发送arp响应。

操作:如果当前进入的arp包的目的地址是本地地址,则以try_hard模式调用update_arp_entry更新arp表,否则,使用普通模式更新arp表。之后,根据arp的操作码决定何种操作:如果是arp请求,并且目的地址就是本地地址,则就地构造arp响应包,将本机的mac地址填充进去,然后发送;如果是arp响应包,更新本地arp表(前面已做过)。最后释放pbuf。

函数名:etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)

功能:为输出的数据包添加以太网头或者解决解决以太网地址。来自上层的数据包在调用驱动接口发送之前,调用该函数设置mac地址

操作:如果是广播包或者组播包,则直接设置其mac地址,并直接发送。如果是单播包,则查看包是否要出本地网络。如果目的地址在本地局域网,则直接调用etharp_query接口,查询arp表,发送数据包;否则,查看接口是否有默认的网关。若无默认的网关,则返回错误,如果有默认的网关,则将IP地址设置为网关的地址,同样调用etharp_query接口发送。

函数名:etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)

功能:发送arp请求并/或排队数据包。如果要发送包的ip地址不在arp表中,则添加一个pending状态的表项并发送一个arp请求到给定的ip地址,最后将数据包在该入口上排队;如果已经存在一个pending状态的arp表项,一个新的arp请求发送,包排队;如果该ip地址已经是arp表中的一个stable项,并且数据包不为空,则直接发送该数据包,此时不发送arp请求;前述情况,如果数据包为空,此时发送一个arp请求。

操作:调用find_entry找到一个入口索引,如果该入口的状态为empty,则设置为pending。对于pending状态的入口或者空数据包情况,调用etharp_request发送arp请求。如果数据包不为空,并且当前入口为stable状态,则填充包的mac地址,然后发送。如果入口的状态为pending,则将该数据包排队。

函数名:etharp_request(struct netif *netif, struct ip_addr *ipaddr)

功能:根据给定的接口和ip地址,发送请求

操作:首先为arp请求分配一个pbuf,设置该pbuf,发送arp请求包,最后释放pbuf。

网上的关于lwip的arp模块的分析图如下所示

关于arp部分有个问题就是是否需要每收到一个IP包都需要更新arp表(即etharp_ip_input函数是否需要),似乎有点浪费

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙赤子

你的小小鼓励助我翻山越岭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值