LWIPtcpip_input函数解析(协议栈入口)
Posted Evan_ZGYF丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LWIPtcpip_input函数解析(协议栈入口)相关的知识,希望对你有一定的参考价值。
收录于:
索引:
(*input)().->.tcpip_input().->.ethernet_input()
........................................->.ip_input()
相关链接:
——|【LWIP】tcp_timewait_input函数解析
调用流程:
当网卡收到数据后,调用数据帧接收函数进行数据帧的接收,最后通过(*input)()函数传至协议栈(实际是netif->input,有一层封装)。
在添加网络接口时将netif->input()指向tcpip_input函数,即:网络协议栈入口为tcpip_input()函数
tcpip_input()函数并不直接进行数据处理,通过tcpip_inpkt()函数将数据传递到tcpip_thread()函数进行处理。
(可能挺绕的,流程就是:(*input)() -> tcpip_input() -> tcpip_inpkt() -> tcpip_thread())
函数简析:
在注册网络接口netif_add()时,将netif->input指向tcpip_input()函数,通过调用netif->input()执行到该处。
将收到的数据包传递到tcpip_thread(),用于进行ethernet_input()或ip_input()的输入处理(不直接调用)
具体分析:
(在源码中有详细注释)
1.收到数据后,先经过一层回调函数(SylixOS 添加的回调函数,用于数据包的过滤)
2.根据inp->flags标志位判断是否包含以太网报头(是否含有NETIF_FLAG_ETHARP或NETIF_FLAG_ETHERNET标志)
3.若包含以太网报头,调用ethernet_input();若不包含以太网报头,调用ip_input()
(注:不是直接调用,通过tcpip_inpkt()将数据传递至tcpip_thread()函数,在lwip主线程函数中调用)
4.tcpip_inpkt()函数填充tcpip_msg结构体,发送一个邮箱消息(在tcpip_thread处进行处理)
5.tcpip_thread()函数收到邮箱消息后,根据msg->type类型,进行不同操作。
(tcpip_thread()函数第一次被调用时,会初始化所有子模块,并进入while循环,等待邮箱消息)
流程图:
源码:
/**
* @ingroup lwip_os
* 将收到的数据包传递到tcpip_thread,用于进行ethernet_input或ip_input的输入处理(不直接调用),
* 在注册网络接口netif_add()时,将netif->input指向tcpip_input()函数,通过调用netif->input()执行到该处。
*
* @参数 p 接收到的数据包,p->payload指向以太网头
* 或IP报头(如果inp没有NETIF_FLAG_ETHARP或NETIF_FLAG_ETHERNET标记)。
* @参数 inp 接收数据包的网络接口
*/
err_t
tcpip_input(struct pbuf *p, struct netif *inp)
#if defined(SYLIXOS) && defined(LWIP_HOOK_LINK_INPUT)
/* SylixOS 添加的回调函数(数据包过滤) */
if (LWIP_HOOK_LINK_INPUT(p, inp))
pbuf_free(p);
return ERR_OK;
#endif /* SYLIXOS && LWIP_HOOK_TCPIP_INPUT */
#if LWIP_ETHERNET
if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET))
return tcpip_inpkt(p, inp, ethernet_input);
else
#endif /* LWIP_ETHERNET */
return tcpip_inpkt(p, inp, ip_input);
/**
* 将收到的数据包传递到tcpip_thread进行输入处理
*
* @参数 p 收到的数据包
* @参数 inp 接收数据包的网络接口
* @参数 input_fn 要调用的函数(ethernet_input或ip_input)
*/
err_t
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
#if LWIP_TCPIP_CORE_LOCKING_INPUT
err_t ret;
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\\n", (void *)p, (void *)inp));
LOCK_TCPIP_CORE();
ret = input_fn(p, inp);
UNLOCK_TCPIP_CORE();
return ret;
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
struct tcpip_msg *msg;
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
/* 申请一个邮箱消息结构体(tcpip_msg共用体) */
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
if (msg == NULL)
return ERR_MEM;
/* 填充tcpip_msg结构体,发送一个邮箱消息(在tcpip_thread处进行处理) */
msg->type = TCPIP_MSG_INPKT;
msg->msg.inp.p = p;
msg->msg.inp.netif = inp;
msg->msg.inp.input_fn = input_fn;
if (sys_mbox_trypost(&mbox, msg) != ERR_OK)
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
return ERR_MEM;
return ERR_OK;
#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
/**
* 主lwIP线程。该线程具有对lwIP核心函数的独占访问权(除非对它们的访问没有被锁定)。
* 其他线程使用邮箱与此线程进行通信。
*
* 它还启动所有计时器,以确保它们在正确的线程上下文中运行。
*
* @参数 arg 未使用
*/
static void
tcpip_thread(void *arg)
struct tcpip_msg *msg;
LWIP_UNUSED_ARG(arg);
if (tcpip_init_done != NULL)
tcpip_init_done(tcpip_init_done_arg);
LOCK_TCPIP_CORE();
/* 主循环 */
while (1)
UNLOCK_TCPIP_CORE();
LWIP_TCPIP_THREAD_ALIVE(); //把它定义为触发一个看门狗的东西(目前为空)。
/* 等待消息,在等待时处理超时 */
TCPIP_MBOX_FETCH(&mbox, (void **)&msg);
LOCK_TCPIP_CORE();
if (msg == NULL)
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\\n"));
LWIP_ASSERT("tcpip_thread: invalid message", 0);
continue;
switch (msg->type)
#if !LWIP_TCPIP_CORE_LOCKING //SylixOS中此处未执行(宏配置)
case TCPIP_MSG_API:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\\n", (void *)msg));
msg->msg.api_msg.function(msg->msg.api_msg.msg);
break;
case TCPIP_MSG_API_CALL:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\\n", (void *)msg));
msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
sys_sem_signal(msg->msg.api_call.sem);
break;
#endif /* !LWIP_TCPIP_CORE_LOCKING */
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
/* msg->type == TCPIP_MSG_INPKT,执行input_fn函数 */
case TCPIP_MSG_INPKT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\\n", (void *)msg));
msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
break;
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS SylixOS中此处未执行(宏配置)
case TCPIP_MSG_TIMEOUT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\\n", (void *)msg));
sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
memp_free(MEMP_TCPIP_MSG_API, msg);
break;
case TCPIP_MSG_UNTIMEOUT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\\n", (void *)msg));
sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
memp_free(MEMP_TCPIP_MSG_API, msg);
break;
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
/* msg->type == TCPIP_MSG_CALLBACK,执行function函数 */
case TCPIP_MSG_CALLBACK:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\\n", (void *)msg));
msg->msg.cb.function(msg->msg.cb.ctx);
memp_free(MEMP_TCPIP_MSG_API, msg);
break;
/* msg->type == TCPIP_MSG_CALLBACK_STATIC,执行function函数 */
case TCPIP_MSG_CALLBACK_STATIC:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\\n", (void *)msg));
msg->msg.cb.function(msg->msg.cb.ctx);
break;
default:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\\n", msg->type));
LWIP_ASSERT("tcpip_thread: invalid message", 0);
break;
以上是关于LWIPtcpip_input函数解析(协议栈入口)的主要内容,如果未能解决你的问题,请参考以下文章