找回密码
 立即注册
首页 业界区 业界 lwip-autoip

lwip-autoip

撷监芝 2025-9-17 00:16:03
lwip-2.1.3/src/core/ipv4/autoip.c
AutoIP 通常指的是在动态网络环境中,系统自动为其网络接口分配、管理和使用一个IP地址的技术,而无需依赖中心化的服务器(如DHCP服务器)或手动静态配置
需要使用autoip,需要在opt.h头文件当中使能LWIP_AUTOIP这个宏,通常也会使能LWIP_DHCP_AUTOIP_COOP这个宏
  1. /*
  2.    ------------------------------------
  3.    ---------- AUTOIP options ----------
  4.    ------------------------------------
  5. */
  6. /**
  7. * @defgroup lwip_opts_autoip AUTOIP
  8. * @ingroup lwip_opts_ipv4
  9. * @{
  10. */
  11. /**
  12. * LWIP_AUTOIP==1: Enable AUTOIP module.
  13. */
  14. #if !defined LWIP_AUTOIP || defined __DOXYGEN__
  15. #define LWIP_AUTOIP                     0
  16. #endif
  17. #if !LWIP_IPV4
  18. /* disable AUTOIP when IPv4 is disabled */
  19. #undef LWIP_AUTOIP
  20. #define LWIP_AUTOIP                     0
  21. #endif /* !LWIP_IPV4 */
  22. /**
  23. * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on
  24. * the same interface at the same time.
  25. */
  26. #if !defined LWIP_DHCP_AUTOIP_COOP || defined __DOXYGEN__
  27. #define LWIP_DHCP_AUTOIP_COOP           0
  28. #endif
复制代码
在使用netif_init初始化网络接口的的时候,在调用netif_add之后,如果有启用DHCP和auto IP功能,lwIP 内核就会同时启动DHCP和AutoIP客户端(如果DHCP失败),lwip遵循先DHCP再autoIP的原则,首先使用DHCP去获取IP,如果有超时之类的情况导致获取失败,则会启动autoIP功能。
一旦AutoIP启动,lwIP会随机地从链路本地地址块 169.254.1.0 到 169.254.254.255 中选择一个地址作为候选地址。可以通过查看IP是169.254.x.x还是192.168.x.x来判断使用了DHCP还是autoIP。
为了避免候选地址冲突,autoIP会使用发送arp协议来完成地址冲突检测,在autoip_start调用autoip_start_probing之后,会调用autoip_arp_probe来发送一个arp_request来判断随机到的IP是否冲突(request的源IP为0.0.0.0),在发送完成之后会等待看是否有arp_reply,如果有说明该IP已被使用,如果没有表示未发生冲突,收到reply后通过autoip_arp_reply去处理
​PROBING 状态下的冲突判断​: 如果收到的 ARP 包中,源IP地址 (sipaddr) 等于我们正在探测的地址 (llipaddr),或者目标IP地址 (dipaddr) 等于 llipaddr 且源MAC地址不是自己的,都视为冲突。
ANNOUNCING/BOUND 状态下的冲突判断*: 如果收到的 ARP 包中,源IP地址等于我们已绑定的 llipaddr,但源MAC地址不是自己的,说明有另一个设备也使用了这个地址,需要处理冲突。
autoip_arp_reply的调用是由etharp_input触发的,那么数据是如何从底层到上层的?etharp_input是由ethernet_input中调用
网卡驱动(属于底层硬件抽象层)在收到数据后,​必须主动地将收到的数据包注入到 lwIP 协议栈中​。这个注入的入口点,通常就是 ethernet_input() 函数。
而lwip提供了一个标准的接口给用于给上层传输数据netif->input(),这个回调函数在网络接口初始化netif_init的时候通过netif_add注册
lwip_init  - --  netif_init  ---  netif_add  --- netif_input
当底层网卡驱动收到数据之后,通过DMA的方式将数据放到一个缓冲区当中,当数据存入之后,网卡硬件会向MCU发出一个硬件中断,之后MCU响应中断,找到驱动对应的中断服务程序(ISR),在中断服务程序当中将数据传入到队列当中,通过任务或者信号量机制通知上层的任务来处理数据,这个任务会调用netif_input()即ethernet_input()去将数据处理。
  1. /*
  2. * Handles every incoming ARP Packet, called by etharp_input().
  3. *
  4. * @param netif network interface to use for autoip processing
  5. * @param hdr Incoming ARP packet
  6. */
  7. void
  8. autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
  9. {
  10.   struct autoip *autoip = netif_autoip_data(netif);
  11.   LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
  12.   if ((autoip != NULL) && (autoip->state != AUTOIP_STATE_OFF)) {
  13.         /* when ip.src == llipaddr && hw.src != netif->hwaddr
  14.          *
  15.          * when probing  ip.dst == llipaddr && hw.src != netif->hwaddr
  16.          * we have a conflict and must solve it
  17.          */
  18.         ip4_addr_t sipaddr, dipaddr;
  19.         struct eth_addr netifaddr;
  20.         SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
  21. /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
  22. * structure packing (not using structure copy which breaks strict-aliasing rules).
  23. */
  24. IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
  25. IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
  26. if (autoip->state == AUTOIP_STATE_PROBING) {
  27.   /* RFC 3927 Section 2.2.1:
  28.    * from beginning to after ANNOUNCE_WAIT
  29.    * seconds we have a conflict if
  30.    * ip.src == llipaddr OR
  31.    * ip.dst == llipaddr && hw.src != own hwaddr
  32.    */
  33.   if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
  34.       (ip4_addr_isany_val(sipaddr) &&
  35.        ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
  36.        !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
  37.     LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
  38.                 ("autoip_arp_reply(): Probe Conflict detected\n"));
  39.     autoip_restart(netif);
  40.   }
  41. } else {
  42.   /* RFC 3927 Section 2.5:
  43.    * in any state we have a conflict if
  44.    * ip.src == llipaddr && hw.src != own hwaddr
  45.    */
  46.   if (ip4_addr_cmp(&sipaddr, &autoip->llipaddr) &&
  47.       !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
  48.     LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
  49.                 ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
  50.     autoip_handle_arp_conflict(netif);
  51.   }
  52. }
复制代码
}
}

发生冲突后需要处理冲突,使用autoip_handle_arp_conflict函数去处理冲突,lwip实现的plan B
从下面代码可以看出在首次冲突的时候不撤退,采用defend防御策略,将lastconflict置为DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND,在倒计时期间(lastconflict>0)如果再发生冲突则防御失败,程序会选择​撤退​(autoip_restart)并换一个地址重新开始。倒计时结束都未发送冲突则防御成功,lastconflict为0,lastconflict在 autoip_tmr() 中每100毫秒递减,防御成功或失败后重置
  1. /**
  2. * Handle a IP address conflict after an ARP conflict detection
  3. */
  4. static void
  5. autoip_handle_arp_conflict(struct netif *netif)
  6. {
  7.   struct autoip *autoip = netif_autoip_data(netif);
  8.   /* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where
  9.          a) means retreat on the first conflict and
  10.          b) allows to keep an already configured address when having only one
  11.                 conflict in 10 seconds
  12.          We use option b) since it helps to improve the chance that one of the two
  13.          conflicting hosts may be able to retain its address. */
  14.   if (autoip->lastconflict > 0) {
  15.         /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
  16.         LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
  17.                                 ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
  18.         /* Active TCP sessions are aborted when removing the ip addresss */
  19.         autoip_restart(netif);
  20.   } else {
  21.         LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
  22.                                 ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
  23.         autoip_arp_announce(netif);
  24.         autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
  25.   }
  26. }
复制代码

如未发生冲突,之后会进行 autoip_arp_announce发送无偿arp。目的是为了正式向网络中宣告“这个 IP 地址现在属于这个 MAC 地址”,刷新其他设备的 ARP 缓存,并作为最后一道冲突检测的防线。
先介绍autoip的核心结构体
  1. /** AutoIP state information per netif */
  2. struct autoip
  3. {
  4.   /** the currently selected, probed, announced or used LL IP-Address */
  5. // 当前autoIP模块选择、探测、宣告的逻辑链路层的IP地址
  6.   ip4_addr_t llipaddr;
  7.   /** current AutoIP state machine state */
  8. // 当前autoIP状态机的状态
  9.   u8_t state;
  10.   /** sent number of probes or announces, dependent on state */
  11. // 记录当前状态下发送的报文次数
  12.   u8_t sent_num;
  13.   /** ticks to wait, tick is AUTOIP_TMR_INTERVAL long */
  14.   // Time To Wait 的缩写,是一个倒计时器,表示距离下一次操作还需要等待多少个定时器周期
  15.   u16_t ttw;
  16.   /** ticks until a conflict can be solved by defending */
  17. // 冲突防御倒计时器。用于记录在一次冲突发生后,进入防御状态的剩余时间
  18.   u8_t lastconflict;
  19.   /** total number of probed/used Link Local IP-Addresses */
  20. // 记录已经尝试过的链路本地地址的总数
  21.   u8_t tried_llipaddr;
  22. };
复制代码

autoIP的核心是autoip_tmr状态机,状态机函数
34行当TTW到0的时候需要进行下一步操作,此时判断发送probe的次数是否到达PROBE_NUM次(通常为3),如果到达三次,表示已发送足够数量的Probe,未检测到冲突。切换状态为AUTOIP_STATE_
ANNOUNCING并将候选地址 (autoip->llipaddr) 正式设置到网络接口上 (netif->ip_addr)。此时,设备已经可以使用这个 IP 地址了。下面的else则表示发送次数还不够。
65行进入AUTOIP_STATE_ANNOUNCING状态,在这个状态,地址已被绑定,设备通过发送 ARP Announcement 来向网络正式宣告它的存在,当发送次数达到ANNOUNCE_NUM后切换状态为AUTOIP_STATE_BOUND
  1. void
  2. autoip_tmr(void)
  3. {
  4.   struct netif *netif;
  5.   /* loop through netif's */
  6.   NETIF_FOREACH(netif) {
  7.         struct autoip *autoip = netif_autoip_data(netif);
  8.         /* only act on AutoIP configured interfaces */
  9.         if (autoip != NULL) {
  10.           if (autoip->lastconflict > 0) {
  11.                 autoip->lastconflict--;
  12.           }
  13.           LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
  14.                                   ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
  15.                                    (u16_t)(autoip->state), autoip->ttw));
  16.           if (autoip->ttw > 0) {
  17.                 autoip->ttw--;
  18.           }
  19.           switch (autoip->state) {
  20.                 case AUTOIP_STATE_PROBING:
  21.                   if (autoip->ttw == 0) {
  22.                         if (autoip->sent_num >= PROBE_NUM) {
  23.                           /* Switch to ANNOUNCING: now we can bind to an IP address and use it */
  24.                           autoip->state = AUTOIP_STATE_ANNOUNCING;
  25.                           autoip_bind(netif);
  26.                           /* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP
  27.                                  which counts as an announcement */
  28.                           autoip->sent_num = 1;
  29.                           autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
  30.                           LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
  31.                                                   ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
  32.                                                    ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
  33.                                                    ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
  34.                         } else {
  35.                           autoip_arp_probe(netif);
  36.                           LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n"));
  37.                           autoip->sent_num++;
  38.                           if (autoip->sent_num == PROBE_NUM) {
  39.                                 /* calculate time to wait to for announce */
  40.                                 autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
  41.                           } else {
  42.                                 /* calculate time to wait to next probe */
  43.                                 autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
  44.                                                                            ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
  45.                                                                           PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
  46.                           }
  47.                         }
  48.                   }
  49.                   break;
  50.                 case AUTOIP_STATE_ANNOUNCING:
  51.                   if (autoip->ttw == 0) {
  52.                         autoip_arp_announce(netif);
  53.                         LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n"));
  54.                         autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
  55.                         autoip->sent_num++;
  56.                         if (autoip->sent_num >= ANNOUNCE_NUM) {
  57.                           autoip->state = AUTOIP_STATE_BOUND;
  58.                           autoip->sent_num = 0;
  59.                           autoip->ttw = 0;
  60.                           LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
  61.                                                   ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
  62.                                                    ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
  63.                                                    ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
  64.                         }
  65.                   }
  66.                   break;
  67.                 default:
  68.                   /* nothing to do in other states */
  69.                   break;
  70.           }
  71.         }
  72.   }
  73. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册