首页 > 其他分享 >LWIP协议栈---ARP协议(3)ARP数据包发送过程

LWIP协议栈---ARP协议(3)ARP数据包发送过程

时间:2023-02-26 18:13:09浏览次数:36  
标签:ARP LWIP addr ip etharp netif 数据包 struct

ARP数据包发送过程

先看一些指向流程图,ARP数据包发送的过程:

image


主要看看右边这块内容,ip_output() 调用etharp_output() 函数发送出ip层的内容。而该函数又根据数据包是否是多播,广播,单播,分别调用不同的函数处理,最终是调用netif->linkoutput完成传输。

(1)广播与多播包发送:

​ 当发送数据包为多播或广播数据包时,etharp_output()会构造一个特殊的MAC地址,同时把ip地址和MAC地址传递给etharp_send_ip函数使用。

1.etharp_output()

先看代码。

err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
{
  struct eth_addr *dest;    
  struct eth_addr mcastaddr;
  ip_addr_t *dst_addr = ipaddr;   //初始化目的IP地址。


  /* 调整PBUF 中payload 指针,使其指向以太网帧头部,失败则返回 */
  if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
    return ERR_BUF;
  }


  /* 判断是否是广播地址? */
  if (ip_addr_isbroadcast(ipaddr, netif)) {
    /* 若是,则dest 指向广播地址的MAC */
    dest = (struct eth_addr *)&ethbroadcast;
  /*是多播地址吗? */
  } else if (ip_addr_ismulticast(ipaddr)) {
    /* 若是,则构造多播地址.*/
    mcastaddr.addr[0] = LL_MULTICAST_ADDR_0;
    mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
    mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
    mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
    mcastaddr.addr[4] = ip4_addr3(ipaddr);
    mcastaddr.addr[5] = ip4_addr4(ipaddr);
    /* dest 指向多播地址 */
    dest = &mcastaddr;
  
  } else {  //如果是单播地址
    s8_t i;
    /* 判断目的IP地址是否为本地的子网上,若不在,则修改ipaddr */
    if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
        !ip_addr_islinklocal(ipaddr)) {
      {                                    //将数据包发送到网关上,由网关转发
        if (!ip_addr_isany(&netif->gw)) {  //若网关配置了
          dst_addr = &(netif->gw);         //更改目标ip地址为网关,由网关转发
        } else {                           //若网关未配置
          return ERR_RTE;
        }
      }
    }
    //对于 单播包,调用query函数 查询其mac 并发送数据包。
    return etharp_query(netif, dst_addr, q);
  }

  /*对于多播或广播,由于得到了MAC地址,所以直接发送*/
  return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
}

我们整理一下执行的流程:

先判断数据包是单播,多播,还是广播。

  • 若是广播包,则将MAC地址配置为ff-ff-ff-ff-ff-ff-ff,然后发送

  • 若是多播包,则计算MAC地址,然后发送出去。

  • 若是单播包,则于本地IP进行比较,是否在同一个局域网内部,若不是,要将目标地址改为网关地址,再发送etharp_query(),查找MAC地址,并发送出去。

2. etharp_send_ip() 较为简单,就不再详解了。

static err_t etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
{
  struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; //以太网帧头部

  ETHADDR32_COPY(&ethhdr->dest, dst);
  ETHADDR16_COPY(&ethhdr->src, src);
  ethhdr->type = PP_HTONS(ETHTYPE_IP);
  return netif->linkoutput(netif, p);  //发送出去
}

(2)单播包的发送

接下来就是本篇最后一个函数的讲解了!

etharp_query()

​ 该函数功能是向指定的IP地址处发送一个IP数据包或一个ARP请求。

//q :指向以太网数据帧的pbuf.


err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
{
  struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
  err_t result = ERR_MEM;
  s8_t i; /* ARP entry index */


  /*调用函数 查找或创建一个ARP表项 */
  i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);

  /*若i<0 ,查询失败 */
  if (i < 0) {
    return (err_t)i;
  }

  /* 若状态为empty 说明是刚创建的,改变状态为stable */
  if (arp_table[i].state == ETHARP_STATE_EMPTY) {
    arp_table[i].state = ETHARP_STATE_PENDING;
  }

  /* 如果表项为pending 状态,或者数据包为空 */
  if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
    result = etharp_request(netif, ipaddr);  //发送ARP请求包。
    if (result != ERR_OK) {
    }
    if (q == NULL) {
      return result;
    }
  }
 //如果 数据包不为空,根据表项的状态,则进行数据包的发送,或者将数据包挂接在缓冲队列上。
  if (arp_table[i].state >= ETHARP_STATE_STABLE) {
    result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
      
  } else if (arp_table[i].state == ETHARP_STATE_PENDING) { //若是 pending状态,则 挂接数据包

    struct pbuf *p;
    int copy_needed = 0;  //是否需要重新拷贝数据包?
    p = q;
    while (p) {
   
      if(p->type != PBUF_ROM) {  //是否需要重新拷贝数据包,数据包由 PBUF_ROM类型的PBUF组成,才不需要拷贝
        copy_needed = 1;
        break;
      }
      p = p->next;
    }
    if(copy_needed) {
      p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
      if(p != NULL) {
        if (pbuf_copy(p, q) != ERR_OK) { //执行拷贝动作
          pbuf_free(p);
          p = NULL;
        }
      }
    } else {        //如果不需要拷贝
     
      p = q;
      pbuf_ref(p);  //增加pbuf 的ret的值。
    }
  
      
      //到这里,p指向了我们需要挂接的数据包,下面执行挂接操作
    if (p != NULL) {
      struct etharp_q_entry *new_entry;
      /* allocate a new arp queue entry */
      new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
      if (new_entry != NULL) {  //申请成功,进行挂接
        new_entry->next = 0;
        new_entry->p = p;
        if(arp_table[i].q != NULL) {  //若缓冲队列不为空,
         
          struct etharp_q_entry *r;
          r = arp_table[i].q;
          while (r->next != NULL) { //找到最后一个缓冲包结构
            r = r->next;
          }
          r->next = new_entry;  //将新的数据包挂接在队列尾部
        } else {                 //缓冲队列为空
          /*直接挂接在缓冲队列首部 */
          arp_table[i].q = new_entry;
        }
        result = ERR_OK;
      } else {  //etharp_q_entry 结构申请失败,则释放数据包空间
        pbuf_free(p);
        result = ERR_MEM;
      }
    }
  }
  return result;
}

​ 具体的执行流程如下:

  1. 首先调用etharp_find_entry()函数返回一个arp表项,该表项可能是pending 或stable状态,也有可能是新申请的empty状态,然后更改为pending 状态。

  2. 判断要发送的数据包是否为空,或者 ARP表项 是否为pending 状态,只要符合一个成立,就发送一个arp请求包。

  3. 如果待发送的数据包不为空,要根据ARP表项的状态进行不同的操作:

    • 若是stable状态,则调用 etharp_send_ip()发送出去。

    • 若是pending状态,则将数据包 挂接到 表项的待发送数据链表上,当内核收到arp应答时,会改变为stable状态,并把数据包发送出去。

  4. 接下来将数据包挂接到发送队列上:

    • 要判断pbuf的类型,对于PBUF_REF,PBUF_POOL,PBUF_RAM类型的数据包,不能直接挂接在发送链表上,因为这些数据被挂接在发送队列中不会立刻被发送,在等待期间,数据可能被上层更改。

    • 需要将数据拷贝到新的pbuf中,然后将新的PBUF挂接到缓冲队列。

标签:ARP,LWIP,addr,ip,etharp,netif,数据包,struct
From: https://www.cnblogs.com/maxwell-01/p/17157203.html

相关文章

  • LWIP学习记录---ARP协议(2)ARP数据包发送过程
    (一)ARP之数据包接收过程​​ 先看一下整个数据流的传输过程。首先etherneti_input()函数从底层网卡驱动接收到原始数据,若是ip包或者ARP包则调用ethernet_input()......
  • LWIP学习记录------ARP协议(1)
    关于LWIP网络协议在嵌入式设备使用越来越广泛,还是要好好学习一下,之前也看过一些资料,总是学了又忘(可能实践的太少了吧!!)。所以本文重新整理一下笔记。共同进步!(一)ARP基础知......
  • 解决sharp太慢、失败Could not load js config file/strapi-server.js, pmSomething w
    问题描述项目在本地跑的好好地,使用Windows电脑和MAC电脑,重新下载依赖运行项目均无异常。使用docker部署项目,遇到如下报错[2023-02-2209:55:13.784]debug:⛔️Serverw......
  • OpenCvSharp 学习笔记1 -- 基本对象和常见操作
    一:Mat对象的创建OpenCvSharp版本:v4.0.30319mat对象继承了IDisposable接口,可以直接用using语句。mat对象的构造函数有十几个之多,我这里之列举常用的几个。Mat在C......
  • wiresharp抓包
    PacketDetailsPane(数据包详细信息),在数据包列表中选择指定数据包,在数据包详细信息中会显示数据包的所有详细信息内容。数据包详细信息面板是最重要的,用来查看协议中......
  • 安卓nba2k13数据包该放那个目录下
    今天玩了NBA,感觉很不错上网看很多网友问这个问题说下:安卓nba2k13数据包该放那个目录下sdcard/Android/obb/***游戏运行注意:1、单核手机跑不动,不要费力了,测试OK机型:TF301(t......
  • RestSharp
    RestSharpRestSharp是一个轻量的,不依赖任何第三方的模拟Http的组件或者类库。RestSharp具体以下特性;支持net4.0++,支持HTTP的GET,POST,PUT,HEAD,OPTIONS,DELETE等操作......
  • 支持表格识别,PaddleOCRSharp最新发布
    PaddleOCRSharp2.3.0已经发布nuget包。项目开源地址:https://gitee.com/raoyutian/paddle-ocrsharp2.3.0更新内容: 1.增加表格识别功能2.同步更新飞桨PaddleOCR最新版......
  • CSharp大文件上传解决方案
    ​ 核心原理: 该项目核心就是文件分块上传。前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题。* 如何分片;* 如何合成......
  • CSharp: Select.Pdf Free Html To Pdf Converter for .NET
    ///<summary>///SelectPdfedit:geovindu,GeovinDu///https://github.com/selectpdf////https://www.nuget.org/packages/Select.Pdf////htt......