lwip中的实现代码如下(注:最新版本lwip中关于TCP_SEQ_XXX相关宏实现略有变化):
#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0) //a 小于 b
#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0)//a 小于等于 b
#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) //a 大于 b
#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0)//a 大于等于 b
/* is b<=a<=c? */
#if 0 /* see bug #10548 */
#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b))
#endif
#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) //a 界于b,c之间(包括b,c)
这里关于TCP_SEQ_BETWEEN的实现有点意思,先是使用直接计算的方法,后面由于bug #10548改为TCP_SEQ_XXX版本实现。
但#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b))
这个实现看着觉得没什么问题呀?
为什么呢?当然要看看bug #10548是什么了。我一直想看看这个Bug到底是个啥,但随便搜了下还没找到,今天终于无意间找到了,原文描述如下:
Sun 03 Oct 2004 02:37:02 PM UTC, comment #1: Quote
The call to the macro TCP_SEQ_BETWEEN(a,b,c) causes (b) to
be evaluated twice on LITTLE_ENDIAN machines because of the
call to ntohl(). This produces more code (at least on the ARM). As a current work around, I have left all of the code in tcp_in.c untouched and changed the TCP_SEQ_BETWEEN() macro in tcp.h to
(TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
This both works and produces less code.
-email is unavailable-
Anonymous
Fri 01 Oct 2004 02:11:49 PM UTC, original submission: Quote
I just finished a port from lwIP-0.7.2 to the current code in CVS. The processor is an LPC2292 with a CS8900 ethernet chip. The LPC is an ARM7TDMI which operates in LITTLE_ENDIAN mode. The problem shows up for me when using Adam's minimal http server. I have isolated it to the TCP_SEQ_BETWEEN in tcp_in.c at line 822. When I use the
previous macros, the code serves pages just fine. When I use the new macro, it does not. The code fragment that works is:
while (pcb->unsent != NULL &&
TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) +
TCP_TCPLEN(pcb->unsent), ackno) &&
TCP_SEQ_LEQ(ackno, pcb->snd_max)
/*TCP_SEQ_BETWEEN(ackno,
ntohl(pcb->unsent->tcphdr->seqno) +
TCP_TCPLEN(pcb->unsent), pcb->snd_max)*/
So far I have been unable to determine why in this instance the macro does not work. Maybe something to do with LITTLE_ENDIAN. Maybe another set of eyes can spot it.
原来这里是犯了一个使用宏常见的错误,参数被计算了两次。
修改后的版本是:
#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
有没有问题?显然有!这种改法又可能导致参数"a"被计算两次!只是目前lwip的代码里没有触发到而已,但这个风险还是存在的。
原始问题描述链接:
https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10548#options
再回头看看Linux中的实现:
Linux 内核中的实现:
static inline bool before(__u32 seq1, __u32 seq2)
{
return (__s32)(seq1-seq2) < 0;
}
#define after(seq2, seq1) before(seq1, seq2)
/* is s2<=s1<=s3 ? */ //根据Lwip中的描述,这种实现有Bug? 非也!Lwip中是宏,但这里是函数!
static inline bool between(__u32 seq1, __u32 seq2, __u32 seq3)
{
return seq3 - seq2 >= seq1 - seq2;
}
原来Linux中直接是用inline函数实现的,则不存在lwip中的宏实现引起的参数被重复计算Bug。
关于最新版本lwip中关于TCP_SEQ_XXX相关宏实现变化,请参考:
关于lwip中的tcp序列号防回绕(sequence wraparound)实现分析_青蛙之家-优快云博客