UIP協議棧二

1. 網卡如何與uIP協議交互(包括arp, icmp等)
   在我看來,CP2200提供了讀取網絡數據的能力,而UIP提供的是一種如何封裝網路數據的策略。對用戶數
據不斷封裝,最後交給CP2200發送,在UIP協議中有一個uip_buf緩衝用來接收和發送數據。

(轉自:維庫電子開發網>子通列表 > 協議棧)

   ARP請求和應答
在UIP協議中定義了一個ARP的struct。維護了一張
緩存表。
  1. struct arp_entry {
  2. u16_t ipaddr[2];  // 保存的是IP地址
  3. struct uip_eth_addr ethaddr; // 保存的是mac地址
  4. u8_t time;       // 緩存更新時間
  5. };
    ARP請求發送函數:void uip_arp_out(void)
* 爲傳出的IP包添加以太網頭並看是否需要發送ARP請求. 
* 此函數應該在發送IP包時調用,它會檢查IP包的目的IP地址,看看以太網應該使用什麼目的MAC地址.
* 如果目的IP地址是在局域網中(由IP地址與子網掩碼的與邏輯決定),函數就會從ARP緩存表中查找有
* 無對應項.若有,就取對應的MAC地址,加上以太網頭,並返回,否則uip_buf[]中的數據包會被替換成一個
* 目的IP在址的ARP請求.原來的IP包會被簡單的仍掉,此函數假設高層協議(如TCP)會最終重傳扔掉的包.
* 如果目標IP地址並非一個局域網IP,則會使用默認路由的IP地址.
* uip_len.函數返回時,uip_buf[]中已經有了一個包,其長度由uip_len指定.
  1. void uip_arp_out(void)
  2. {
  3.   struct arp_entry *tabptr=0;
  4.   // 在ARP表中找到目的IP地址,構成以太網頭.如果目的IP地址不在局域網中,則使用默認路由的IP.
  5.   // 如果ARP表中找不到,則將原來的IP包替換成一個ARP請求.  
  6.   // 首先檢查目標是不是廣播
  7.   if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr))
  8.   {
  9.     memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6);
  10.   } 
  11.   else 
  12.   {
  13.         /* 檢查目標地址是否在局域網內 */
  14.         if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask))
  15.         {
  16.               /* 目的地址不在局域網內,所以保用默認路由器的地址來確在MAC地址 */
  17.               uip_ipaddr_copy(ipaddr, uip_draddr);
  18.         } 
  19.         else 
  20.         {
  21.             /* 否則,使用目標IP地址 */
  22.           uip_ipaddr_copy(ipaddr, IPBUF->destipaddr);
  23.         }
  24.         //這裏遍歷表,對比目的IP與ARP緩存表中的IP.
  25.         for(= 0; i < UIP_ARPTAB_SIZE; ++i)
  26.         {
  27.              tabptr = &arp_table[i];
  28.              if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr))
  29.              {
  30.                  break;
  31.              }
  32.          }
  33.          
  34.          if(== UIP_ARPTAB_SIZE)
  35.          {
  36.             /* 如果遍歷到頭沒找到,將原IP包替換爲ARP請求並返回 */
  37.               memset(BUF->ethhdr.dest.addr, 0xff, 6);  // 以太網目的地址
  38.               memset(BUF->dhwaddr.addr, 0x00, 6);      // 目的以太網地址
  39.               memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);  //
  40.               memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);  // 源以太網地址
  41.     
  42.               uip_ipaddr_copy(BUF->dipaddr, ipaddr);  // 目的IP地址
  43.               uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr);  // 源IP地址
  44.               BUF->opcode = HTONS(ARP_REQUEST);  // ARP 請求
  45.               BUF->hwtype = HTONS(ARP_HWTYPE_ETH);  // 硬件類型 值爲1
  46.               BUF->protocol = HTONS(UIP_ETHTYPE_IP); // 協議類型 值爲0x8000表示IP地址
  47.               BUF->hwlen = 6;  
  48.               BUF->protolen = 4
  49.               BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);  

  50.               uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];
  51.               uip_len = sizeof(struct arp_hdr);
  52.               return;
  53.           }

  54.         // 如果是在局域網中,且在ARP緩存中找到了(如果沒找到進行不到這一步,在上面就返回了),則構建以太網頭
  55.          memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6);
  56.   }
  57.   memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
  58.   IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP);
  59.   uip_len += sizeof(struct uip_eth_hdr);
  60. }
  61. 接下來看看UIP如何處理ARP應答的情況,在主循環中一段代碼:
    1. else if(BUF->type == htons(UIP_ETHTYPE_ARP)) 
    2. {   
    3.     uip_arp_arpin();  // 處理ARP應答
    4.     /* If the above function invocation resulted in data that
    5.        should be sent out on the network, the global variable
    6.        uip_len is set to a value > 0. */
    7.    // 如果上面的函數返回的結果需要發送到網絡上,那麼uip_len就必須設置 > 0
    8.     if(uip_len > 0) 
    9.     {
    10.         network_device_send(); // 迴應ARP包
    11.     }
    12. }

    在uip_arp_arpin()函數中主要是處理ARP應答。
        這個函數是在設備接收到ARP包時,由驅動程序調用的.如果收到是ARP包是一個對本地主機上次發送的ARP請求的應答,那麼就從包中取得自己想要的主機的MAC地址,加入自己的ARP緩存表中.如果收到是一個ARP請求,那就把自己的MAC地址打包成一個ARP應答,發送給請求的主機.看代碼uip_arp.c的254行:
    1. void uip_arp_arpin(void)
    2.  {
    3.   if(uip_len < sizeof(struct arp_hdr))
    4.   {
    5.     uip_len = 0;
    6.     return;
    7.   }
    8.   uip_len = 0;
    9.   
    10.   switch(BUF->opcode)  // 操作碼
    11.   {
    12.       case HTONS(ARP_REQUEST):
    13.       // 如果是一個ARP請求,則發送應答
    14.       if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr))
    15.       {
    16.           // 首先,我們將發送請求的主機註冊到ARP緩存表中,因爲我們很
    17.           // 可能要跟它要有更多的交流
    18.           uip_arp_update(BUF->sipaddr, &BUF->shwaddr);  
    19.       
    20.           // 迴應的操作碼是 2.
    21.           BUF->opcode = HTONS(2);
    22.           // 將收到的ARP包的發送端以太網地址,變爲目的以太網地址
    23.           memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6);
    24.           // 將自己的以太網地址,賦值給ARP包的發送端以太網地址
    25.           memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);
    26.           // 對應以太網源地址
    27.           memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
    28.           // 對應以太網目的地址
    29.           memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6);
    30.       
    31.           BUF->dipaddr[0] = BUF->sipaddr[0];
    32.           BUF->dipaddr[1] = BUF->sipaddr[1];
    33.           BUF->sipaddr[0] = uip_hostaddr[0];
    34.           BUF->sipaddr[1] = uip_hostaddr[1];

    35.           BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
    36.           uip_len = sizeof(struct arp_hdr);
    37.      }
    38.      break;
    39.     // 如果收到的是一個ARP應答,而且也是我們所要的應答的話,就插件
    40.     // 並更新ARP緩存表
    41.     case HTONS(ARP_REPLY):  
    42.     if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr))
    43.     {
    44.       uip_arp_update(BUF->sipaddr, &BUF->shwaddr);
    45.     }
    46.     break;
    47.   }

    48.   return;
    49. }
    還有一個是ARP週期處理函數,在主循環中代碼如下:
    // 每10秒運行一次
    if(timer_expired(&arp_timer)) 
    {
        timer_reset(&arp_timer); 

        uip_arp_timer();
    }
    具體的代碼:
    1. void uip_arp_timer(void)
    2. {
    3.   struct arp_entry *tabptr;
    4.   ++arptime;    // 這個是個全局變量,結合uip_arp_update來更新緩存表
    5.   for(= 0; i < UIP_ARPTAB_SIZE; ++i)
    6.   {
    7.     tabptr = &arp_table[i];
    8.     // 把超過20分鐘都沒有更新的項扔掉
    9.     if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 &&
    10.        arptime - tabptr->time >= UIP_ARP_MAXAGE)
    11.     {
    12.       memset(tabptr->ipaddr, 0, 4);
    13.     }
    14.   }
    15. }

發佈了28 篇原創文章 · 獲贊 15 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章