LWIP學習記錄------ARP協議(1)基礎知識

来源:https://www.cnblogs.com/maxwell-01/archive/2023/02/26/17155351.html
-Advertisement-
Play Games

關於LWIP網路協議在嵌入式設備使用越來越廣泛,還是要好好學習一下,之前也看過一些資料,總是學了又忘(可能實踐的太少了吧!!)。所以本文重新整理一下筆記。共同進步! (一)ARP基礎知識 (1)ARP協議的本質: ​ ARP協議的基本功能是使用目標主機的IP地址,查詢其對應的MAC地址,來進行底層鏈 ...


關於LWIP網路協議在嵌入式設備使用越來越廣泛,還是要好好學習一下,之前也看過一些資料,總是學了又忘(可能實踐的太少了吧!!)。所以本文重新整理一下筆記。共同進步!

(一)ARP基礎知識

(1)ARP協議的本質:

ARP協議的基本功能是使用目標主機的IP地址,查詢其對應的MAC地址,來進行底層鏈路上數據包的通信工作。其中,ARP表的功能就是 記錄IP地址MAC地址 的對應關係的表格。

​ 在乙太網中,ARP數據包與IP數據包是兩個獨立的部分,他們都是封裝在乙太網中進行傳送的。ARP數據包分為兩類一個是ARP請求包,另一個是arp應答包

  • 所謂ARP請求包:就是它是通過 廣播 的方式在乙太網中進行傳輸,然後希望能得到目標主機的相應。已知IP地址,請求MAC地址。

  • 顯然,ARP應答包的功能,就是收到ARP請求包的主機,會解析請求包的IP地址與本機IP地址做比較,若符號,則返回一個APR應答包,包含了請求的IP地址與對應的MAC地址。這樣,源主機就知道目標主機的MAC地址了,並把它加入到自己的ARP表中。

(2)ARP表的建立過程:

  1. 階段一:當系統初始化時,ARP表為空。主機會廣播自己的 <IP,MAC>,這個數據包叫 無彙報 ARP請求包。其他主機收到後,會把這個數據加入到自己的ARP表中。

  2. 階段二:當主機要發送一個IP數據包時,要先檢查自己的ARP表有沒有目標主機的MAC地址?要有,好,直接發送。要沒有,就要廣播一個ARP請求包,然後其他主機接收後,若和自己匹配,則返回一個ARP應答包。源主機就得到了這個IP對應的MAC地址。 如果該表項的緩衝隊列上有未發送的數據,相應的數據會被髮送出去(後面結合代碼詳解)

  3. 階段三:由於網路硬體狀態可能隨時改變,所以ARP還需要採用一定的定時機制來保證 緩存表中地址的 有效性。要有定時機制。

(二)lwip中關於ARP的數據結構

​ etharp.c/h 文件中 實現了ARP協議的全部數據結構和函數定義。主要是ARP緩存表ARP報文

(1)ARP表

  1. ARP表是由緩存表項(entry)組成。LWIP只描述緩存表項的數據結構叫做 etharp_entry 。單個緩存表項的結構如下:

    struct etharp_entry {
      struct etharp_q_entry *q; 	  //**數據包緩衝隊列指針**;
      struct pbuf *q;                 //
      ip_addr_t ipaddr;               //目標IP地址
      struct netif *netif;            //對應的網路介面信息
      struct eth_addr ethaddr;        //目標MAC地址
      u8_t state;                     //該entry 表項的狀態
      u8_t ctime;                     //該entry的時間信息
    };
    
    • 第一個成員:"*q" 指向緩存表項的數據包緩存隊列。因為當主機發送一個IP數據包時候,發現緩存表中並沒有對應的MAC地址,那該怎麼辦呢?於是在得到對應的MAC地址之前,主機會新建立一個緩存表項,然後把要發送的數據 掛在這個緩存隊列指針上。當接收到ARP應答包後,再發送出去。

    struct etharp_q_entry 結構是一個鏈表,包含一個*next 指針和一個 指向pbuf 數據包的指針。系統為 etharp_q_entry 結構開闢了一些 MEMP_APR_QUEUE類型的記憶體池。


    • state有四種狀態,分別為empty 狀態、Pending狀態、stable狀態、stable狀態且發送了一個ARP請求。

      初始時,是以數組形式定義了10條ARP表項。這都是空的,沒有記錄任何信息。

      enum etharp_state {
        ETHARP_STATE_EMPTY = 0,        //empty狀態
        ETHARP_STATE_PENDING,
        ETHARP_STATE_STABLE,
        ETHARP_STATE_STABLE_REREQUESTING
      };
      
      • pending狀態:不穩定狀態,此時只是找到了IP地址,正在尋找MAC地址;

      • stable狀態:當Pending狀態的表項接收到ARP應答後,就會變成stable穩定狀態

      • stabl_rerequesting狀態:系統定時更新ARP表項,當時間到了之後,會向目標主機發送一個ARP請求,來驗證表項的有效性,在驗證期間就會變成 stable_rerequesting狀態。


    • ctime 成員:用來計時,系統會刪除到時的 ARP表項。內核每5秒一次調用eth_tmr()函數,他會為每個 ARP表項 的ctime 值加1,當改值大於系統規定的值時,就會產生相應的動作。

      void etharp_tmr(void)
      {
        u8_t i;
      
        for (i = 0; i < ARP_TABLE_SIZE; ++i) {
          u8_t state = arp_table[i].state;
          if (state != ETHARP_STATE_EMPTY)  //表項不為空,說明被使用。
          {
            arp_table[i].ctime++;
            if ((arp_table[i].ctime >= ARP_MAXAGE) ||             //表項大於生存時間20分鐘
                ((arp_table[i].state == ETHARP_STATE_PENDING)  &&  //達到pending 最大時間10s
                 (arp_table[i].ctime >= ARP_MAXPENDING))) 
            {
              	etharp_free_entry(i);                          //刪除表項
            }
            else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING)
            {
              /* Reset state to stable, so that the next transmitted packet will
                 re-send an ARP request. */
              arp_table[i].state = ETHARP_STATE_STABLE;
            }
      
          }
        }
      }
      

      (2)ARP報文

    ​ 1. 前面提到的ARP請求和應答是組裝在一個ARP數據包中發送的。如下圖所示是一個APR包的組成;

    乙太網目的地址(MAC) 乙太網源地址(MAC) 幀類型 硬體協議 協議類型 硬體地址長度 協議地址長度 OP 發送方乙太網地址 發送方IP 接收方乙太網地址 接收方IP
    6位元組 6 2 2 2 1 1 2 6 4 6 4

    前面 14個位元組是乙太網首部,後面28個位元組是ARP數據包。

    • 幀類型:對於ARP包是0X806,對於IP包是0X0800。

    • 硬體協議:發送方想要知道的硬體介面類型,對於乙太網是 1

    • 協議類型:表示要映射的協議地址類型,為0X0800,代表映射為IP地址

    • 操作欄位op:表示數據包類型。ARP請求包為 1,ARP應答包為2.

    • 後面欄位含義較為明顯,不再贅述。

    乙太網首部用結構eth_hdr表示

    struct eth_hdr {
      PACK_STRUCT_FIELD(struct eth_addr dest);  //乙太網目的地址,6位元組
      PACK_STRUCT_FIELD(struct eth_addr src);   //乙太網源地址,6位元組。
      PACK_STRUCT_FIELD(u16_t type);            //幀類型
    } PACK_STRUCT_STRUCT;
    

    (PACK_STRUCT_FIELD巨集定義來禁止編譯器自動對齊)

    ARP數據包部分用結構etharp_hdr 表示:

    struct etharp_hdr {
      PACK_STRUCT_FIELD(u16_t hwtype);
      PACK_STRUCT_FIELD(u16_t proto);
      PACK_STRUCT_FIELD(u8_t  hwlen);
      PACK_STRUCT_FIELD(u8_t  protolen);
      PACK_STRUCT_FIELD(u16_t opcode);
      PACK_STRUCT_FIELD(struct eth_addr shwaddr);
      PACK_STRUCT_FIELD(struct ip_addr2 sipaddr);
      PACK_STRUCT_FIELD(struct eth_addr dhwaddr);
      PACK_STRUCT_FIELD(struct ip_addr2 dipaddr);
    } PACK_STRUCT_STRUCT;
    

    1. 結合源碼,來看看ARP請求包是怎麼發送出去的。ARP請求包是 調用etharp_requeset()實現。

      etharp_request(struct netif *netif, ip_addr_t *ipaddr)
      {
        return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
                          (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero,
                          ipaddr, ARP_REQUEST);
      }
      

      好家伙,裡面原來是調用是了etharp_raw()。那我們看看etharp_raw()具體實現。

      重頭戲來了!

      /**
      
       * @param netif       the lwip network interface on which to send the ARP packet
       * @param ethsrc_addr the source MAC address for the ethernet header
       * @param ethdst_addr the destination MAC address for the ethernet header
       * @param hwsrc_addr the source MAC address for the ARP protocol header
       * @param ipsrc_addr the source IP address for the ARP protocol header
       * @param hwdst_addr the destination MAC address for the ARP protocol header
       * @param ipdst_addr the destination IP address for the ARP protocol header
       * @param opcode the type of the ARP packet
       * @return ERR_OK if the ARP packet has been sent
       *         ERR_MEM if the ARP packet couldn't be allocated
       *         any other err_t on failure
       */
      
      err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
                 const struct eth_addr *ethdst_addr,
                 const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
                 const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
                 const u16_t opcode)
      {
        struct pbuf *p;
        err_t result = ERR_OK;
        struct eth_hdr *ethhdr;  //乙太網幀首部結構體指針
        struct etharp_hdr *hdr;  //ARP數據包結構體指針
      
        /* 先在記憶體堆中,為ARP包分配空間 */
        p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM); //14+28個位元組
        
          /* 若分配失敗,返回err */
        if (p == NULL) {
          return ERR_MEM;
        }
      
      
        ethhdr = (struct eth_hdr *)p->payload;        //ethhdr指向乙太網幀首部區域
        hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); //hdr 指向arp數據包首部區域
        hdr->opcode = htons(opcode);                //填寫op欄位。
          
         下麵是繼續填寫數據包中欄位:
             
        /* Write the ARP MAC-Addresses */
        ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
        ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
          
        /* Write the Ethernet MAC-Addresses */  
        ETHADDR16_COPY(&ethhdr->src, ethsrc_addr);
        IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
        IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
      
        hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
        hdr->proto = PP_HTONS(ETHTYPE_IP);
        /* set hwlen and protolen */
        hdr->hwlen = ETHARP_HWADDR_LEN;
        hdr->protolen = sizeof(ip_addr_t);
      
        ethhdr->type = PP_HTONS(ETHTYPE_ARP);  //乙太網幀類型ARP包
        
          /* 發送!!send ARP query */
        result = netif->linkoutput(netif, p);
        /* 釋放!free ARP query packet */
        pbuf_free(p);
        p = NULL;
        return result;
      }
      

    到此為止,總結就是 先分配記憶體,然後填充這個記憶體中各個欄位的數據信息(保存在pbuf 中),然後再調用netif->linkoutput()底層數據包發送函數,最後再釋放掉pbuf。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 【深進1.例1】求區間和 題目描述 給定 $n$ 個正整數組成的數列 $a_1, a_2, \cdots, a_n$ 和 $m$ 個區間 $[l_i,r_i]$,分別求這 $m$ 個區間的區間和。 輸入格式 共 $n+m+2$ 行。 第一行,為一個正整數 $n$ 。 第二行,為 $n$ 個正整數 $ ...
  • 原創:扣釘日記(微信公眾號ID:codelogs),歡迎分享,非公眾號轉載保留此聲明。 簡介 日常編程工作中,Java集合會經常被使用到,且經常需要對集合做一些類似過濾、排序、對象轉換之類的操作。 為了簡化這類操作,Java8添加了一套新的Stream API,使用方式就像寫SQL一樣,大大簡化了這 ...
  • 這篇文章主要描述分散式數據存儲系統中的數據分片方法,包括哈希方法、一致性哈希方法、帶有限負載的一致性哈希方法以及帶虛擬節點的一致性哈希方法。 ...
  • Lambda 表達式以及方法引用 Java 8 的新特性筆記,重點講的是: Lambda 函數式介面 方法引用 Steam 流 Lambda 表達式 Lambda 的基礎使用不記錄,記錄 JDK 8 實戰 書上的一些底層和核心筆記。 行為參數化 一個貫徹 Lambda 表達式的一個模式、編程規範。 ...
  • 服務端 工作需要又需要用到socketTCP通訊,這麼多年了,終於稍微能寫點了。讓我說其實也說不出個啥來,看了很多的非同步後稍微對非同步socket的導流 endreceive後 再beginreceive 形成一個內迴圈有了個認識,加上我自己的封包拆包機制,然後再仿那些其它的大多數代碼結構弄點onRe ...
  • 輸入系統 常見的輸入設備有鍵盤、滑鼠、遙控桿、書寫板、觸摸屏等等,用戶通過這些輸入設備與Linux系統進行數據交換。 內核中怎樣表示一個輸入設備 // include/linux/input.h struct input_dev { const char *name; //設備名稱 const ch ...
  • Mac支持 NTFS 系統版本 CPU型號 Ventura 13.2 Apple M1 安裝brew 前往官網查看官網安裝教程,安裝過程中可能存在安裝失敗的問題,基本安裝失敗都是網路的問題。可以嘗試使用知乎大佬金牛肖馬的國內加速安裝腳本。 brew官網 國內加速安裝 安裝macfuse 是安裝mac ...
  • (一)ARP之 數據包接收過程 ​ ​ 先看一下整個數據流的傳輸過程。 首先etherneti_input()函數 從底層網卡驅動接收到原始數據,若是ip包或者ARP包則調用ethernet_input()。 s32_t ethernetif_input(struct netif *netif) { ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...