LWIP协议栈实现ARP协议
1) 核心数据结构与状态机(对应图片:etharp_entry 与 etharp_q_entry)
- 代码:struct etharp_entry
- 字段说明:
- q:挂载的等待发送的 pbuf(或队列,取决于 ARP_QUEUEING)
- ipaddr / ethaddr / netif:映射关系与所属接口
- ctime:条目年龄(以秒计;由 etharp_tmr 每秒自增)
- state:ETHARP_STATE_(EMPTY、PENDING、STABLE、REREQUESTING_、STATIC)
- 字段说明:
- 行为:
- PENDING:已发送 ARP 请求,等待应答。可以挂载 1 个或多个待发包(取决 ARP_QUEUEING)。
- STABLE:已解析到 MAC,可直接发送;ctime 达到 ARP_MAXAGE 则过期。
上图是arp_entry结构体和etharp_q_entry结构体。
下图是每个arp结构的state状态机。
2) 条目查找与回收策略(对应代码:etharp_find_entry)
- 功能:一次扫描实现查找/选择回收项/创建新项的逻辑(提高效率,避免多遍扫描)。
- 回收优先级(若无空槽并且允许回收 ETHARP_FLAG_TRY_HARD):
- 最久的 stable(不含 static)
- 最久的 pending(无队列)
- 最久的 pending(有队列)
- 图片提示: ARP_QUEUEING 宏和默认值(通常关闭),说明队列功能默认不开启以节省内存与复杂度。
- 修改建议:若设备需要短时间内缓存多个待发包(例如启动爆发流量),开启 ARP_QUEUEING 并适当设置 ARP_QUEUE_LEN;但要增加 MEMP_ARP_QUEUE 池。
3) 更新缓存(对应代码:etharp_update_arp_entry,etharp_input)
- etharp_input 收到 ARP 包(request/reply)时:
- 先通过 etharp_update_arp_entry 更新/插入缓存(若为请求且目标为本机,会触发 reply)。
- 对于 reply:只是更新条目(并会把挂起队列的数据发出)。
- 若条目由 PENDING -> STABLE:会弹出队列,调用 ethernet_output 发送并 pbuf_free。
- 图片(Wireshark trace)说明:当内核重试 TCP 但 ARP 未解析时会出现大量重传;抓包显示 ARP 请求/应答后 TCP 连接恢复,说明 ARP 解析是许多上层超时/重传的根源。
4) 发包路径与决策(对应代码:etharp_output / etharp_query / etharp_output_to_arp_index)
-
发送单播 IP 包:
- 先检查广播/多播,直接选择目标 MAC。
- 否则在 ARP 表查找稳定条目(快速 path);若找到,直接 etharp_output_to_arp_index -> ethernet_output。
- 若没找到,调用 etharp_query:创建/标记 pending、发送 ARP request,并把 pbuf 挂到表项(或复制/引用,见下)。
-
关于 pbuf 挂载策略(关键点):
- 如果 q 的 pbuf 类型会被上层修改(PBUF_NEEDS_COPY),则必须复制为 PBUF_RAM(pbuf_clone),保证队列中的数据不被上层改写。
- 默认行为(无队列)会只保存一份 pbuf(覆盖旧的)。
-
5) 请求/重发/老化策略(对应代码常量与 etharp_tmr)
- ARP_MAXPENDING(默认 5 秒)控制 pending 条目失效时间。若未收到回复,ethtarp_tmr 会在到期后释放条目。
- ARP_MAXAGE(默认通常 5 分钟),在接近过期时会做 re-request:
- ARP_AGE_REREQUEST_USED_UNICAST = ARP_MAXAGE - 30(15/30s 的差异用于广播/单播策略,见源码)
- etharp_tmr 实现:每秒自增 ctime,达到阈值会 etharp_request(对于 pending)或 etharp_free_entry(过期)。
6) ARP 请求构造与发送(etharp_request / etharp_raw)
- etharp_raw 负责构造 ARP 报文(填 arp header、sender/target 地址)并调用 ethernet_output 发送(PBUF_LINK + PBUF_RAM)。
- 如果使用 AUTOIP(link-local),对特殊源 IP 必须用广播发送(RFC 要求)。
- 图片(ARP 帧格式图)对照:头部字段顺序、各字段长度(hw type、proto type、hwlen、protolen、opcode、sender hw/ip、target hw/ip)。
7) queue 与内存(ARP 队列与 pbuf 类型)
- 如果挂载队列,必须考虑内存池(MEMP_ARP_QUEUE)与 pbuf 池(PBUF_RAM 当需要复制时)。
- PBUF_NEEDS_COPY 宏会检测 pbuf 类型是否安全入队(PBUF_REF/PBUF_ROM 等不可直接入队)。
- 若内存不足(无法为队列分配 memp 或无法复制 pbuf),会丢弃并返回 ERR_MEM,并统计 ETHARP memerr(见代码调用 ETHARP_STATS_INC)。
8) 性能与问题排查建议
- 常见问题:ARP 导致上层 TCP 超时/重传(见 Wireshark),最终原因往往是:
- ARP 请求未发出(驱动问题 / ethernet_output 未调用)
- ARP 请求发送但未到达对端(VLAN/交换机/子网配置错误)
- 驱动或 DMA 未把 RX 帧传递给 lwIP(ethernetif_input 未上送 pbuf)
- ARP 表条目被过快回收(ARP_MAXAGE 设置过小)
- 排查清单:
- 抓包看是否存在 ARP 请求/应答(Wireshark)。若有应答但内核仍在重传,说明内核未把应答更新到表(检查 etharp_input 是否被调用及 netif 关联)。
- 开启 LWIP_DEBUG/ETHARP_DEBUG 与统计(etharp.xmit / eth arp.recv / eth arp.memerr)观察。
- 检查 MEMP_ARP_QUEUE 与 PBUF_POOL 是否不足(pbuf_alloc 失败会导致丢包)。
- 优化建议:
- 在高并发或 burst 场景启用 ARP_QUEUEING 与合理 ARP_QUEUE_LEN,并增大 MEMP_ARP_QUEUE 数量。
- 若系统多接口 / 路由复杂,配置 ETHARP_TABLE_MATCH_NETIF 与 LWIP_HOOK_ETHARP_GET_GW 来控制回路与网关选择。
- 对延迟敏感的应用可以适度增大 ARP_MAXAGE,或对关键目标预填 ARP(etharp_add_static_entry)。
正常arp表如下。