[文/告别年代   Email:byeyear@hotmail.com]

重大修订记录

-----------------------------------------

2016.11.03

感谢@ wayne88 指出(见文后讨论帖),对本文所述的NETIF_FLAG_UP标记和有效IP地址之间的关系,lwip代码作者其实是存有疑虑的。见以下链接:

http://savannah.nongnu.org/bugs/?func=detailitem&item_id=37068

所幸该链接不会影响本文内容。对1.4.1版代码,代码作者仍然采用了如本文所述的、他认为可能是“unclear”的做法。

注意

-----------------------------------------

本文基于lwip-1.4.1,若文章与代码对不上,请确认代码版本。

基本流程

-----------------------------------------

A. link up -> link down:

关闭MAC和DMA;

调用netif_set_link_down

B. link down -> link up:

打开MAC和DMA;

调用netif_set_link_up

C. 注意:当使用RAW API时,所有使用RAW API的代码(包括处理链路状态的代码)必须运行于同一线程环境。

-----------------------------------------

代码分析

-----------------------------------------

有四个函数和两个标志位和链路状态改变有关:

A. netif_set_up

该函数设置NETIF_FLAG_UP标记,并在链路已up(NETIF_FLAG_LINK_UP有效)的情况下发送arp探测。

/* 删除了部分预编译条件和无关代码 */
void netif_set_up(struct netif *netif)
{
if (!(netif->flags & NETIF_FLAG_UP)) {
netif->flags |= NETIF_FLAG_UP;
/* 链路有效情况下发送ARP探测 */
if (netif->flags & NETIF_FLAG_LINK_UP) {
if (netif->flags & (NETIF_FLAG_ETHARP)) {
etharp_gratuitous(netif);
}
}
}
}

如果你的网络使用静态IP,那么在lwip初始化时调用该函数;

如果你的网络使用DHCP,那么DHCP成功后lwip会帮你调用netif_set_up。

B. netif_set_down

除非你需要关闭网络,否则一般不需要主动调用该函数。

C. netif_set_link_up

该函数设置NETIF_FLAG_LINK_UP标记,启动DHCP和AutoIP,并在NETIF_FLAG_UP标记有效的情况下发送arp探测。

void netif_set_link_up(struct netif *netif )
{
if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
netif->flags |= NETIF_FLAG_LINK_UP;
if (netif->dhcp) {
dhcp_network_changed(netif);
}
/* ...其它代码(autoip相关)... */
if (netif->flags & NETIF_FLAG_UP) {
if (netif->flags & NETIF_FLAG_ETHARP) {
etharp_gratuitous(netif);
}
}
NETIF_LINK_CALLBACK(netif);
}
}

当驱动程序检测到链路从down变化为up时,重新初始化MAC和DMA,然后调用该函数。

注意:初始化时如果链路有效,low_level_init直接设置NETIF_FLAG_LINK_UP,而不调用netif_set_link_up函数,避免在lwip没有完全初始化好时启动DHCP。

D. netif_set_link_down

当驱动程序检测到链路从up变为down时调用该函数,并关闭MAC和DMA。

E. NETIF_FLAG_UP和NETIF_FLAG_LINK_UP

前者表示lwip协议栈已经就绪(已得到合法IP地址,且协议栈已准备好收发数据包);后者表示链路层有效。

或者说,一个是软件(协议栈)就绪标志,一个是硬件(链路层)就绪标志。

F. 参考lwip代码中的netif.h文件对这两个标记的详细说明。

网上很多port代码不管是否使用DHCP都在lwip初始化时设置NETIF_FLAG_UP,这是*不*正确的。

NETIF_FLAG_UP必须在获得有效IP地址后才能置位,以保证顺利发送ARP探测。

所以,如果使用了DHCP,那么初始化时就不能设置NETIF_FLAG_UP。

LwIP会在链路有效情况下发送DHCP请求,并在拿到有效IP地址后为你设置NETIF_FLAG_UP。

情景分析

--------------------------------------------------------------

A. 静态IP,初始化时链路有效

这是最简情况。驱动层会看到Link有效,并直接设置NETIF_FLAG_LINK_UP。

随后lwip初始化过程中调用netif_set_up,该函数设置NETIF_FLAG_UP,并发送arp探测。

B. 静态IP,初始化时链路无效

驱动层不会设置NETIF_FLAG_LINK_UP。

lwip初始化过程会调用netif_set_up,该函数看到没有link,除了设置NETIF_FLAG_UP标记外不会做任何事。

当链路变为有效后,驱动层调用netif_set_link_up,发送arp探测。

C. 动态IP,初始化时链路有效

驱动层直接设置NETIF_FLAG_LINK_UP。

lwip初始化过程中调用dhcp_start,启动DHCP过程。

但lwip初始化过程不会调用netif_set_up,因为还没有获得有效IP地址。

获得有效IP后,lwip会帮我们调用netif_set_up,发送arp探测。

D. 动态IP,初始化时链路无效

驱动层不会设置NETIF_FLAG_LINK_UP。

lwip初始化过程中仍然会调用dhcp_start。对dhcp_start的调用是必须的,因为dhcp过程需要的资源将在该函数中分配。

等到链路有效后驱动层调用netif_set_link_up,该函数会启动DHCP过程。

获得有效IP后,lwip会帮我们调用netif_set_up,发送arp探测。

E. 静态IP,链路断开后重建

链路断开时netif_set_link_down,重建后netif_set_link_up发送arp探测。

NETIF_FLAG_UP在设置后就一直有效。

F. 动态IP,链路断开后重建

链路重建后netif_set_link_up重新启动DHCP过程,该过程会清除NETIF_FLAG_UP标记。

DHCP完成后lwip自动调用netif_set_up重新置位该标记并发起arp探测。

总结

-------------------------------------------------------------------------------------

1. 使用静态IP

low_level_init根据当前链路状态设置或不设置NETIF_FLAG_LINK_UP;

lwip初始化时调用netif_set_up;

链路状态改变时调用netif_set_link_up/netif_set_link_down。

2. 使用动态IP

low_level_init根据当前链路状态设置或不设置NETIF_FLAG_LINK_UP;

lwip初始化时调用dhcp_start;

链路状态改变时调用netif_set_link_up/netif_set_link_down;

不要在使用动态IP时直接调用netif_set_up,而是由lwip协议栈代码在成功获得IP后为你调用这个函数。

[文/告别年代   Email:byeyear@hotmail.com]

最新文章

  1. ps批量处理图片
  2. Android进程间的通信之AIDL
  3. MVP 个人理解2
  4. RestEasy 3.x 系列之一:Hello world
  5. centOS 6.4下安装中文输入法
  6. Linux内核系列—C语言中内嵌汇编 asm __volatile__,asm__volatile_【转】
  7. POJ1700:Crossing River(过河问题)
  8. Oracle除去换行符的方法
  9. 专题:DUILIB Win32 透明效果
  10. vijos1090题解
  11. MASM中3中文本宏的使用与区别
  12. Flask自带的常用组件介绍
  13. 输入框中的空"",0,null的判断
  14. 动态SQL之、条件判断(转)
  15. SuperMap iClient for JavaScript image出图
  16. RocketMQ事务消息-demo
  17. LeetCode(283. 移动零)
  18. WKWebView实现网页静态资源优先从本地加载
  19. OpenStack 安装:nova服务
  20. 转:C#判断ContextMenuStrip右键菜单的来源(从哪个控件弹出来的)

热门文章

  1. learning uboot source command
  2. 2.strcpy使用注意(2)
  3. 找到多个与名为“Home”的控制器匹配的类型,如果为此请求(“{controller}/{action}/{id}”)提供服务的路由没有指定命名空间来搜索匹配此请求的
  4. jquery条形码生成器
  5. Python 字符串转换为字典(String to Dict)
  6. 深度分析:Android4.3下MMS发送到附件为音频文件(音频为系统内置音频)的彩信给自己,添加音频-发送彩信-接收彩信-下载音频附件-预览-播放(三,接收彩信<2,下载彩信>)
  7. java程序员常用的八个工具
  8. postman--安装及Interceptor插件
  9. Redis 缓存服务器
  10. Ubuntu16.04怎样安装Python3.6