百货商城网站建设/百度导航2023年最新版
从 lwIP-2.0.0 开始,网络接口 netif 的 up
标志修改为管理标志,up
标志不再具有以前的 IP4 地址有效
含义。
什么是网络接口
netif
?
网络接口
属于链路层范畴,它旨在对具体网络硬件、软件进行统一封装,并为协议栈上层(IP 层)提供统一的接口服务。lwIP 协议栈使用一个名为netif
的网络接口结构来描述物理网卡,如接口 IP 地址、接口状态等,同时在该结构中注册对于的操作函数,如数据包输入函数、输出函数等。一个嵌入式系统可能有多个网络接口,比如多个网口,lwIP 会为每个网络接口分配一个netif
数据结构。
现在,如果一个网络接口 netif 没有设置 up
标志,则用户不能在这个网络接口上收发数据!即便是使用 DHCP,在启动 DHCP 客户端之前,也要将 netif 设置为up
状态。
这与lwIP-1.4.1 以及更早的版本不同,两者的区别为:
lwIP 1.4.1版本 | lwIP 2.1.2版本 |
---|---|
设置网络接口 netif 为 up 状态,可用于处理流量。此外,当网络接口使用 DHCP 时,DHCP 完成配置后会自动将接口设置为 up 状态,因此这个标志还经常被用于 “IP4 地址有效”标志。 | 设置网络接口 netif 为 up 状态,只用于处理流量。 |
两种版本都是使用函数 netif_set_up()
将 netif 设置为 up
状态,通常在初始化网络的最后调用这个函数:
/*初始化网络*/
void init_ethernet(void)
{ip_addr_t ulIPAddr, ulNetMask, ulGWAddr;struct netif * err;init_ethernet_gpio(); lwip_init();IP4_ADDR(&ulIPAddr, 192, 168, 1, 100); // 这里使用最简单的方式说明IP4_ADDR(&ulNetMask, 255.255.255.0); IP4_ADDR(&ulGWAddr, 192, 168, 1, 1);/* 注册网络接口 */err=netif_add(&lpc_netif, &ulIPAddr, &ulNetMask, &ulGWAddr, NULL, lpc_enetif_init, ethernet_input);if(err==NULL) // 需自行增加错误处理return ;netif_set_default(&lpc_netif);netif_set_up(&lpc_netif); // <--- 这里
}
函数 netif_set_up(netif)
代码如下(有所简化):
void netif_set_up(struct netif *netif)
{if (!(netif->flags & NETIF_FLAG_UP)) {netif->flags |= NETIF_FLAG_UP;NETIF_STATUS_CALLBACK(netif);
}
可以看到将网络接口设置为 up
状态只是简单的将 flags
字段增加 NETIF_FLAG_UP
标志。
这个标志是怎么控制数据流量的呢?
数据流量有发送和接收两部分组成,先来看发送部分。
1.控制数据发送
在IP层,函数 ip_route(ip_addr_t *dest)
根据指定的目的 IP 地址找到正确的网络接口 netif。函数会先判断网络接口是否为up
状态,如果网络接口没有up
,则不会查看是否匹配目的 IP 地址,而是返回NULL
,即没有找到合适的网络接口。没有网络接口,自然不能发送数据。
在实现上,lwIP-1.4.1 和 lwIP-2.1.3 略有不同:
对于 lwIP-1.4.1:
// 节选自 lwip1.4.1
/* iterate through netifs */
for (netif = netif_list; netif != NULL; netif = netif->next) {/* network mask matches? */if (netif_is_up(netif)) { //<--这里if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {/* return netif on which to forward IP packet */return netif;}}
}if ((netif_default == NULL) || (!netif_is_up(netif_default))) {//<--这里LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));return NULL;
}
对于 lwIP-2.1.3,不但检查了netif 是否处于 up
状态, 还检查了物理层连接是否处于 up
状态:
// 节选自lwip 2.1.3for (netif = netif_list; netif != NULL; netif = netif->next) {/* is the netif up, does it have a link and a valid address? */if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {/* network mask matches? */if (ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {/* return netif on which to forward IP packet */return netif;}}}if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) ||ip4_addr_isany_val(*netif_ip4_addr(netif_default)) || ip4_addr_isloopback(dest)) {LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));return NULL;}
应用层在发送数据时,无论是 UDP 还是 TCP ,都会调用 ip_route
函数,因此如果没有设置网络接口 netif 为 up
状态,则应用不能发送数据,此时应用层发送的数据将被忽略。。
- UDP 的发送 API 函数
udp_send
和udp_sendto
都会调用ip_route
; - TCP 的发送 API 函数
tcp_output
也会调用ip_route
。
2.控制数据接收
看完了数据发送,再来看看数据的接收。还是在IP层,函数 ip_input
用于处理 IP 数据包。函数会根据收的的 IP 数据包的 目的 IP
找到正确的网络接口 netif 。在查找的过程中,会判断网络接口是否是 up
状态,如果网络接口没有 up
,则表示找不到网络接口。而如果网络接口没有找到,并且数据包不是来自 DHCP服务器 ,则接收的数据被丢掉。
/* 寻找网络接口 */
if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { //<--这里/* unicast to this interface address? */if (ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) ||/* or broadcast on this interface network address? */ip_addr_isbroadcast(¤t_iphdr_dest, netif)) {LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",netif->name[0], netif->name[1]));/* break out of for loop */break;}
}
在应用层,无论是 TCP 还是 UDP 接收 API 函数,都是从 ip_input
获取的数据,所以如果一个 netif 没有设置成 up
状态,应用层将接收不到数据。
因此协议栈通过一个软件标志,就可以把应用层发送和接收拿捏的死死的,这样可以方便的处理数量流量。但这只是针对应用层而言的,因为 lwip-1.4.1 协议栈给 DHCP 开了后门。
DHCP 发送数据时,直接调用udp_sendto_if
,绕过了 ip_route
函数,即使 netif 没有设置 up
也可以发送数据。
接收方面,接收处理函数专门对 DHCP 开了绿灯,即便找不到合适的网络接口,也能将数据递交给 HDCP 处理函数,而且师出有名,有RFC 1542
背书。
这就产生了不一致:如果使用静态IP,用户必须调用函数netif_set_up
,将网络接口设置为 up
状态,但是如果使用 DHCP 动态获取 IP,在分配到 IP 后,由DHCP客户端将网络接口设置为 up
状态。
DHCP 客户端的这种操作,会让很多人误用 up
标志。网络接口设置为 up
状态的时候,就意味着设备已经分配到IP地址,所以很多人会检查网络接口是否为 up
状态来判断IP地址是否有效。这让这个标志产生了二义性:本来是一个方便处理数据流量的软件标志,又担当了IP地址有效的含义。
所以早在 2012 年 8 月,lwIP 的开发人员 Simon Goldschmidt
就提出了这个问题:
网络接口 netif 为
down
状态时,DHCP 和 AutoIP 仍可以在这个网络接口上正确的发送和接收数据。我认为这样不够清晰,应该只有一种方式,即在发送或接收时,netif 必须始终处于
up
状态。DHCP 和AutoIP 的初始化阶段应该只是将 netif 的 IP 地址设置为 ‘0.0.0.0’,并且路由器应该忽略使用 ‘0.0.0.0’ IP 的 netif。
然后在2015年3月份,Simon Goldschmidt
修复了 netif up/down
处理不清晰的问题,也就是本节一开提到的更新内容:
修改网络接口 netif 的
up
标志为管理标志。up
标志不再具有以前的“IP4 地址有效”含义。现在,如果一个网络接口 netif 没有设置up
标志,则用户不能在这个网络接口上收发数据!即便是使用 DHCP,在启动 DHCP 客户端之前,也要将 netif 设置为up
状态。
那 lwip2.1.x 是如何堵住 DHCP 的呢?
-
在 dhcp.c 中,去除所有
netif_set_up
和netif_set_down
函数。即 DHCP 不能更新 netif 标志。 -
在 dhcp.c 的
dhcp_start
函数,增加代码:
LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
如果 netif 不是 up
状态,则函数返回参数错误
(ERR_ARG)代码。即如果 netif 不是 up
状态,DHCP 不能启动。
所以在调用 dhcp_start
函数时,判断返回值有助于快速发现问题。
小插曲:
2015 年 3 月 Simon Goldschmidt
修复了 netif up/down
处理不清晰的问题后,函数 netif_set_up
的注释并没有修改,注释仍显示在为 down
状态的网络接口上启用 DHCP,DHCP 完成配置后会将接口设置为 up
状态。直到 2015 年的 10 月份,才由 Erik Ekman
修改了这个函数的注释,变成了 2.1.3 版本现在的样子。
读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)