LWIPtcpip_input函数解析(协议栈入口)

Posted Evan_ZGYF丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LWIPtcpip_input函数解析(协议栈入口)相关的知识,希望对你有一定的参考价值。

收录于:

【LWIP】LWIP协议|相关知识汇总|LWIP学习笔记


索引:

(*input)().->.tcpip_input().->.ethernet_input()

........................................->.ip_input()


相关链接:

【LWIP】tcpip_input函数解析(协议栈入口)

【LWIP】Ip4_input函数分析

——|【LWIP】pbuf_realloc函数分析

【LWIP】tcp_input()函数分析

——|【LWIP】tcp_timewait_input函数解析

——|【LWIP】tcp_listen_input函数分析

【LWIP】tcp_receive函数分析

【LWIP】udp_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函数解析(协议栈入口)的主要内容,如果未能解决你的问题,请参考以下文章

zigbee组网函数在哪儿

Linux2.6内核协议栈系列--TCP协议2.接收

IoTBLE 协议栈和数据报文解析

浅谈TCP IP协议栈IP协议解析

linux网络协议栈源码分析 - 链路层ARP地址解析协议

linux网络协议栈源码分析 - 链路层ARP地址解析协议