关于lwip中的TCP_SEQ_BETWEEN的实现

博客讨论了LWIP库中TCP序列号比较宏TCP_SEQ_BETWEEN的实现问题,该实现可能导致参数被计算两次,从而引发问题。文章引用了bug#10548的描述,指出问题在于小端机上宏展开导致的代码膨胀。为解决此问题,宏被修改为使用TCP_SEQ_GEQ和TCP_SEQ_LEQ。同时,对比了Linux内核中使用inline函数的实现,该实现避免了宏的问题。文章强调了宏和inline函数在处理此类计算时的差异,并提醒注意潜在的代码优化风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)实现分析_青蛙之家-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值