地址轉換協議ARP


地址轉換協議ARP

  在以太網協議中規定,同一局域網中的一台主機要和另一台主機進行直接通信,必須要知道目標主機的MAC地址。而在TCP/IP協議中,網絡層和傳輸層只關心目標主機的IP地址。這就導致在以太網中使用IP協議時,數據鏈路層的以太網協議接到上層IP協議提供的數據中,只包含目的主機的IP地址。於是需要一種方法,根據目的主機的IP地址,獲得其MAC地址。這就是ARP協議要做的事情。

  所謂地址解析(address resolution)就是主機在發送幀前將目標IP地址轉換成目標MAC地址的過程。

理論結構

  ARP軟件可分為三部分:

  • 輸出模塊
    • 將高層協議地址與相應的物理地址進行綁定,返回給網絡接口程序
  • 輸入模塊
    • 處理來自網絡的ARP分組,並通過增加新的綁定來修改ARP高速緩存的內容
  • 高速緩存管理程序
    • 實現高速緩存替換策略;檢測高速緩存中的所有表項,刪除已達到規定時限的表項

輸出模塊

  該模塊主要是要接收IP數組請求,然后查找物理地址,返回。

  主要步驟為:

  1. 睡眠,直到IP軟件收到IP分組。

  2. 檢查高速緩存表,尋找對應於這個IP分組的項目。

  3. if ( 找到 ){

      if ( 狀態為 RESOLVED ){

        提取硬件物理地址;

        將分組連同硬件物理地址一起發送到數據鏈路層;

                   return;

      }

      else if ( 狀態為 PENDING ){

       將分組放入相應的隊列;

                  return;

     }

    }

    else{

      創建一個隊列;

      將分組加入到隊列中;

      創建一個高速緩存項目,狀態設置為 PENDING ,ATTEMPTS 為 1;

      發送ARP請求;

    }

ARP高速緩存隊列

  它是用數組來存儲的。

extern struct arpentry arptable[ARP_TSIZE]

搜索ARP高速緩存

/* arpfind.c - arpfind */   
   
#include <conf.h>   
#include <kernel.h>   
#include <network.h>   
   
/*------------------------------------------------------------------------  
 * arpfind - find an ARP entry given a protocol address and interface  
 *------------------------------------------------------------------------  
 */   
struct arpentry *   
arpfind(u_char *pra, u_short prtype, struct netif *pni)   
{   
    struct arpentry *pae;   /* 定義ARP緩存結構體指針 */
    int     i;   
   
    for (i=0; i<ARP_TSIZE; ++i) {   /* 遍歷ARP高速緩存 */
        pae = &arptable[i];   /* 高速緩存數組 */
        if (pae->ae_state == AS_FREE)   /* 緩存為空閑接找下一個 */
            continue;   
        if (pae->ae_prtype == prtype && /* 協議類型相同 */
            pae->ae_pni == pni && /* 接口和協議地址相同 */
            BLKEQU(pae->ae_pra, pra, pae->ae_prlen))   /* BLKEQU的定義 #define    BLKEQU(b1, b2, len)    (!memcmp((b1), (b2), len))*/
            return pae;   
    }   
    return 0;   
} 

ARP請求分組的廣播

/* arpsend.c - arpsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>

/*------------------------------------------------------------------------
 * arpsend - broadcast an ARP request
 *    N.B. Assumes interrupts disabled
 *------------------------------------------------------------------------
 */
int arpsend(pae)
struct    arpentry    *pae; //指向高速緩存的表項
{
    struct    netif    *pni = pae->ae_pni;
    struct    ep    *pep;
    struct    arp    *parp;
    int        arplen;

    pep = (struct ep *) getbuf(Net.netpool); //生成ARP請求分組
    if ((int)pep == SYSERR)
        return SYSERR;
    blkcopy(pep->ep_dst, pni->ni_hwb.ha_addr, pae->ae_hwlen);
    pep->ep_type = EPT_ARP;
    pep->ep_order = EPO_NET;
    parp = (struct arp *) pep->ep_data;
    parp->ar_hwtype = hs2net(pae->ae_hwtype);
    parp->ar_prtype = hs2net(pae->ae_prtype);
    parp->ar_hwlen = pae->ae_hwlen;
    parp->ar_prlen = pae->ae_prlen;
    parp->ar_op = hs2net(AR_REQUEST);
    blkcopy(SHA(parp), pni->ni_hwa.ha_addr, pae->ae_hwlen);
    blkcopy(SPA(parp), &pni->ni_ip, pae->ae_prlen);
    bzero(THA(parp), pae->ae_hwlen);
    blkcopy(TPA(parp), pae->ae_pra, pae->ae_prlen);
    arplen = ARP_HLEN + 2*(parp->ar_hwlen + parp->ar_prlen);
    write(pni->ni_dev, pep, EP_HLEN+arplen); //發送請求分組
    return OK;
}

輸入模塊

    從一個隊列中拿走一個分組,並連同解析出的物理地址一起發送給數據報鏈路層傳輸。

  主要步驟為

  1. 睡眠,直到ARP分組到達(請求或回答)。

  2. 檢查高速緩存表,尋找對應這個ARP分組的項目。

  3. if ( 找到 ){

     if ( 狀態是 RESOLVED ){

      更新項目;

      return;

     }

     else if ( 狀態是 PENDING ){

      更新項目;

                 如果隊列非空的話,將一個分組從隊列中取出,將它與硬件地址一起發送給數據鏈路層;

      return;

     {

    }

    else{

    創建一個項目;

    將此項目添加到表中;

    return;

    }

  4.  如果分組是一個請求, 發送ARP回答。

向表中增加已轉換的表項

/* arpadd.c - arpadd */

#include <conf.h>
#include <kernel.h>
#include <network.h>

struct arpentry *arpalloc(void);

/*------------------------------------------------------------------------
 * arpadd - Add a RESOLVED entry to the ARP cache
 *     N.B. Assumes interrupts disabled
 *------------------------------------------------------------------------
 */
struct    arpentry *
arpadd(struct netif *pni, struct arp *parp)
{
    struct    arpentry    *pae;

    pae = arpalloc(); //在高速緩存中分配一個表項

        /* 利用ARP分組信息填寫表項 */
    pae->ae_hwtype = parp->ar_hwtype;
    pae->ae_prtype = parp->ar_prtype;
    pae->ae_hwlen = parp->ar_hwlen;
    pae->ae_prlen = parp->ar_prlen;
    pae->ae_pni = pni;
    pae->ae_queue = EMPTY;
    memcpy(pae->ae_hwa, SHA(parp), parp->ar_hwlen);
    memcpy(pae->ae_pra, SPA(parp), parp->ar_prlen);
        /* 初始化 */
    pae->ae_ttl = ARP_TIMEOUT;
    pae->ae_state = AS_RESOLVED;
    return pae;
}

發送等待發送的分組

/* arpqsend.c - arpqsend */

#include <conf.h>
#include <kernel.h>
#include <network.h>

int netwrite(struct netif *, struct ep *, unsigned);

/*------------------------------------------------------------------------
 * arpqsend - write packets queued waiting for an ARP resolution
 *------------------------------------------------------------------------
 */
void
arpqsend(struct arpentry *pae)
{
    struct    ep    *pep;
    struct    netif    *pni;

    if (pae->ae_queue == EMPTY)
        return;

    pni = pae->ae_pni;
        /* 遍歷等待發送的分組隊列,調用netwrite逐個放入網絡輸出隊列中 */
    while (pep = (struct ep *)deq(pae->ae_queue)) 
        netwrite(pni, pep, pep->ep_len);
    freeq(pae->ae_queue); //隊列為空后,釋放自身
    pae->ae_queue = EMPTY;
}

高速緩存管理

   高速緩存是用來存儲IP地址與物理地址的。如果一個IP進行需要發送一個數據報,但其目的地址不在ARP高速緩存中,就會創建一個新的表項,然后廣播相應的請求分組,並等待分組置入隊列中。

  主要步驟:

1. 睡眠,周期性的喚醒。

2. 遍歷高速緩存的每一個項目:

  if ( 狀態為FREE )

           continue;

      if ( 狀態為PENDING ){

    嘗試次數+1;

    if ( 嘗試次數達到最大次數 ){

      該項目狀態->FREE;

          撤銷相應的隊列;

    }

    else {

      發送ARP請求;

    }

    continue;

  }

   else if( 狀態為RESOLVED ){

    將超時字段的值減去已經過去的時間;

    若結果小於0,狀態->FREE,撤銷相應隊列。

  }

 替換策略

/* arpalloc.c - arpalloc */   
   
#include <conf.h>   
#include <kernel.h>   
#include <proc.h>   
#include <network.h>   
   
void arpdq(struct arpentry *);   
   
/*------------------------------------------------------------------------  
 * arpalloc - allocate an entry in the ARP table  
 *  N.B. Assumes interrupts DISABLED  
 *------------------------------------------------------------------------  
 */   
struct arpentry *arpalloc()   
{   
    static  int aenext = 0; //靜態變量,保證循環  
    struct  arpentry *pae;   
    int i;   
   
    for (i=0; i<ARP_TSIZE; ++i) {   //遍歷表 if (arptable[aenext].ae_state == AS_FREE)   
            break;   
        aenext = (aenext + 1) % ARP_TSIZE;   //循環替換
    }   
    pae = & arptable[aenext];   
    aenext = (aenext + 1) % ARP_TSIZE;   
   
    if (pae->ae_state == AS_PENDING && pae->ae_queue >= 0)   
        arpdq(pae);   
    pae->ae_state = AS_PENDING;   
    return pae;   
} 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM