从 lwIP-2.0.0 开始,lwIP 终于有可用的 IPv6 协议栈了!IPv6 支持 双栈
(IPv4 和 IPv6 同时使用) 或 IPv4/IPv6 二选一
模式。
lwIP-1.4.1 版本也有 IPv6,但那是实验性质的(见…\lwip-1.4.1\src\core\ipv6目录下的README
文件),并不能在实际项目中使用的。
ipv6目录下的
README
文件内容:IPv6 support in lwIP is very experimental.
lwIP 对 IPv6 的支持是非常实验性质的.
在 2011 年 5 月,开发者Ivan Delamer
编写了可以同时运行 IPv4 和 IPv6 的双栈版本,这也是 lwIP 的第一个可以工作的 IPv6 版本。现在我们在…\lwip-2.1.3\src\core\ipv6 目录下看到的文件,也是在这个时候成型的。但这个版本仍很初级,并需要大量测试。
所以 2012 年 12 月发布的 lwIP-1.4.1 正式版中并不包括 Ivan Delamer
编写的 IPv6 模块。
时间来到了 2016 年 8 月 3 日。这天,开发者goldsimon
删除了 ipv6 目录下的 README
文件,并在提交日志中写下了 IPv6 再也不是实验性质的了 😃:
IPv6 is NOT experimental any more 😃
此时距离第一个可以工作的 IPv6 版本,已经过去了 5 年多,有关 IPv6 的修改提交多达 350+ 次。
改善 IPv4/v6 的地址处理
2011 年 5 月,lwIP-1.4.0 版本,IP 地址使用类型ip_addr_t
表示。
#define IP_PCB \
/* ip addresses in network byte order */ \
ip_addr_t local_ip; \
ip_addr_t remote_ip; \
/* Socket options */ \
u8_t so_options; \
/* Type Of Service */ \
u8_t tos; \
/* Time To Live */ \
u8_t ttl \
/* link layer address resolution hint */ \
IP_PCB_ADDRHINT
2011 年 5 月 18,lwIP 终于有了第一个可以同时运行 IPv4 和 IPv6 的双栈版本。随之而来的是,ip.h 文件中宏 IP_PCB
的定义也发生了变化,变量 local_ip
和 remote_ip
需要兼容 ipv6,所以 IP 地址使用了联合体表示:
#define IP_PCB \
IP_PCB_ISIPV6 \
/* ip addresses in network byte order */ \
union { \ /*<--这里*/
ip_addr_t ip4; \
IP_PCB_IP6 \
} local_ip; \
union { \ /*<--这里*/
ip_addr_t ip4; \
IP_PCB_IP6 \
} remote_ip; \
/* Socket options */ \
u8_t so_options; \
/* Type Of Service */ \
u8_t tos; \
/* Time To Live */ \
u8_t ttl \
/* link layer address resolution hint */ \
IP_PCB_ADDRHINT
为了使代码更具可读性,也为了尽可能的合并 IPv4 和 IPv6 代码,2011 年 5 月 26 日,宏 IP_PCB
有了新的变化:
#define IP_PCB \
IP_PCB_ISIPV6_MEMBER \
/* ip addresses in network byte order */ \
ipX_addr_t local_ip; \ /*<--这里*/
ipX_addr_t remote_ip; \ /*<--这里*/
/* Socket options */ \
u8_t so_options; \
/* Type Of Service */ \
u8_t tos; \
/* Time To Live */ \
u8_t ttl \
/* link layer address resolution hint */ \
IP_PCB_ADDRHINT
这次变化很小,只是定义了一个新的IP地址类型 ipX_addr_t
,用来代替上个版本的联合体:
#if LWIP_IPV6
/* A union struct for both IP version's addresses. */
typedef union {
ip_addr_t ip4;
ip6_addr_t ip6;
} ipX_addr_t;
#else /* LWIP_IPV6 */
typedef ip_addr_t ipX_addr_t;
#endif /* LWIP_IPV6 */
不知道你们是如何看待新的类型 ipX_addr_t
,我第一次看到它,虽然我还不怎么明白这个类型的含义,就觉得它不应该出现在源码中。
新的类型 ipX_addr_t
与 lwIP 代码风格很不搭,也容易让人疑惑,大写的 X
到底代表什么意思。
两年后,2013 年 6 月。Simon Goldschmidt
表示对目前的 IPv6,有一件事情仍然不满意,那就是 ipX_addr_t
的实现。有几点原因:
-
不够不清晰。虽然类型
ipX_addr_t
同时涵盖 IPv4 和 IPv6 地址,但是一旦独立于 PCB(ip_pcb
)使用,仍无法分辨类型ipX_addr_t
定义的变量是 IPv4 还是 IPv6 地址。 -
不易使用。引入
ipX_addr_t
(或者是说联合体)是为了节省空间,但是Simon Goldschmidt
深思之后,认为这不值得。能用 IPv6 的应用,不会计较以字节为单位的内存得失。应该把关注点放到如何更容易使用 IPv6 模块上。 -
编译出错。使能 IPv6 之后,下面的 smtp 示例代码无法编译:
char* ipa = ip_ntoa(&pcb->local_ip);
最终,在 2015 年 3 月,为了代码整洁清晰,也为了应用程序的兼容性,Simon Goldschmidt
决定删除所有 ipX_addr_t
:
ip_addr_t
应为通用地址类型,对 API 可见。将ipX_addr_t
重命名为ip_addr_t
;- IPv4 地址类型从
ip_addr_t
重命名为ip4_addr_t
; - IPv6 地址类型保持不变,仍为
ip6_addr_t
; ip4_addr_t
和ip6_addr_t
仅用于协议栈内部,或者调用与版本相关的函数时使用。
这是一项大工程,涉及的文件达到了 74 个,其中新类型 ip_addr_t
定义为:
#if LWIP_IPV4 && LWIP_IPV6
/**
* @ingroup ipaddr
* A union struct for both IP version's addresses.
* ATTENTION: watch out for its size when adding IPv6 address scope!
*/
typedef struct ip_addr {
union {
ip6_addr_t ip6;
ip4_addr_t ip4;
} u_addr;
/** @ref lwip_ip_addr_type */
u8_t type;
} ip_addr_t;
#else /* LWIP_IPV4 && LWIP_IPV6 */
#if LWIP_IPV4
typedef ip4_addr_t ip_addr_t;
#else /* LWIP_IPV4 */
typedef ip6_addr_t ip_addr_t;
#endif /* LWIP_IPV4 */
#endif /* LWIP_IPV4 && LWIP_IPV6 */
在升级到 lwIP-2.0.0 以及后续版本后,IP 地址的定义需要修改以适应新的版本。
对于 lwIP-1.4.1 以及更老版本,定义 IP 地址方式如下:
struct ip_addr app_ip;
而对于 lwIP-2.0.0 以及后续版本,定义 IP 地址要改成:
ip_addr_t app_ip;
此外,如果你从 lwIP-1.4.1 以及更老版本升级到 lwIP-2.0.0 以及后续版本,在编译时通常会遇到以下错误:
../main/source/upgrade_udp.c(37): error: incomplete definition of type 'struct ip_addr'
这是因为应用程序还在使用旧的 IP 地址定义方式,将 struct ip_addr
改为 ip_addr_t
即可解决。
读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)