lwIP ARP协议分析
2012-11-08 21:23
399 查看
/article/4791085.html
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 协议处理流程图
1. ARP:
从功能上来说,arp可以简单的分成两个部分:
a. 当我要向目的ip发送一个数据包的时候,需要通过arp实现ip到物理地址(一般为mac地址)的映射------------》ethernet_output函数
b. 处理输入包,更新arp缓存,如果是ip包后递交给ip层,如果是arp包,对于不同的arp操作做相应的相应------------》etharp_input函数。
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
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 协议处理流程图
1. ARP:
从功能上来说,arp可以简单的分成两个部分:
a. 当我要向目的ip发送一个数据包的时候,需要通过arp实现ip到物理地址(一般为mac地址)的映射------------》ethernet_output函数
b. 处理输入包,更新arp缓存,如果是ip包后递交给ip层,如果是arp包,对于不同的arp操作做相应的相应------------》etharp_input函数。
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
相关文章推荐
- lwIP ARP协议分析0
- lwIP ARP协议分析
- lwIP ARP协议分析1
- lwIP ARP协议分析1
- lwIP ARP协议分析1 (转)
- lwIP ARP协议分析0
- ARP协议分析
- ARP协议分析
- ARP协议-攻击与欺骗分析-交换机工作原理--(精华全篇版)
- lwip源码分析5——udp层
- lwip驱动的分析(基于LPC17XX)
- ARP协议分析
- Lwip ARP分析(2)
- LWIP ethernetif.c分析
- DE2开发板:NiosII+LWIP环境下DM9000A的驱动程序分析(web_server.c)
- ARP协议分析
- lwip源码分析6——tcp层
- lwIP配置文件opt.h和lwipopts.h初步分析
- lwip内存分配详解对比 mem_init memp_init 源码分析
- lwIP配置文件opt.h和lwipopts.h初步分析