<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ARP協(xié)議與鄰居子系統(tǒng)剖析

          共 13852字,需瀏覽 28分鐘

           ·

          2021-02-02 19:59

          學(xué)習(xí)過 TCP/IP 協(xié)議的同學(xué)都應(yīng)該了解過 TCP/IP 五層網(wǎng)絡(luò)模型,如下圖:


          從上圖可以看出,ARP協(xié)議?位于 TCP/IP 五層網(wǎng)絡(luò)模型的?網(wǎng)絡(luò)層。那么,ARP協(xié)議?的用途是什么呢?

          ARP協(xié)議介紹

          在局域網(wǎng)中(同一個路由器內(nèi)),主機(jī)與主機(jī)之間需要通過 MAC 地址進(jìn)行通訊。但由于 MAC 地址過于復(fù)雜,不容易被人類記憶。所以,人們更傾向于使用更容易記憶的 IP 地址。

          但局域網(wǎng)只能使用 MAC 地址通訊,那有什么辦法可以通過主機(jī)的 IP 地址來獲取到主機(jī)的 MAC 地址呢?ARP協(xié)議?就應(yīng)運而生。

          ARP(Address Resolution Protocol)?即地址解析協(xié)議, 用于實現(xiàn)從 IP 地址到 MAC 地址的映射,即詢問目標(biāo)IP對應(yīng)的MAC地址。

          ARP協(xié)議?通過廣播消息,向局域網(wǎng)的所有主機(jī)廣播?ARP請求消息,從而詢問主機(jī)的 IP 地址對應(yīng)的 MAC 地址,如下圖:

          如上圖所示,A主機(jī)要與B主機(jī)通信,但是只知道B主機(jī)的 IP 地址,所以這時可以向局域網(wǎng)廣播一條?ARP請求消息,用于詢問 IP 地址為?192.168.1.2?的主機(jī)所對應(yīng)的 MAC 地址。

          由于?ARP請求消息?是廣播消息,所以局域網(wǎng)的所有主機(jī)都會收到這條消息,但只有對應(yīng) IP 地址的主機(jī)才會回答這條消息。如上圖的B主機(jī)會回復(fù)一條?ARP應(yīng)答消息,用于告訴A主機(jī)自己的 MAC 地址。

          當(dāng)A主機(jī)知道了B主機(jī)的 MAC 地址后,就能通過 MAC 地址與B主機(jī)進(jìn)行通信了。

          ARP協(xié)議頭部

          每種網(wǎng)絡(luò)協(xié)議都有其協(xié)議頭部,用于本協(xié)議的通信,ARP協(xié)議?的頭部格式如下:

          上圖是?ARP協(xié)議?頭部各個字段的信息,其代碼結(jié)構(gòu)定義如下(路徑:?/src/include/linux/if_arp.h):

          struct arphdr{    unsigned short  ar_hrd;     /* format of hardware address   */    unsigned short  ar_pro;     /* format of protocol address   */    unsigned char   ar_hln;     /* length of hardware address   */    unsigned char   ar_pln;     /* length of protocol address   */    unsigned short  ar_op;      /* ARP opcode (command)         */
          #if 0 /* 下面部分沒有定義, 因為不同的鏈路層協(xié)議使用的地址格式不一定相同, * 所以下面只是以太網(wǎng)和IP協(xié)議的示例而已. * Ethernet looks like this : This bit is variable sized however... */ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ unsigned char ar_sip[4]; /* sender IP address */ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ unsigned char ar_tip[4]; /* target IP address */#endif}

          從代碼可以看出,arphdr?結(jié)構(gòu)的各個字段與上圖一一對應(yīng)。下面說說各個字段的作用:

          • ar_hrd:硬件類型,如硬件類型是以太網(wǎng),那么設(shè)置為?1

          • ar_pro:協(xié)議類型,由于 ARP 協(xié)議支持將多種不同協(xié)議地址轉(zhuǎn)換成 MAC 地址(如 IP 協(xié)議、AX.25 協(xié)議等),所以需要通過這個字段指明要轉(zhuǎn)換的協(xié)議類型。如 IP 協(xié)議設(shè)置為?0x0800

          • ar_hln:硬件地址長度,如以太網(wǎng)地址長度是 6。

          • ar_hln:協(xié)議地址長度,如 IP 地址長度是 4。

          • ar_op:操作碼,如?ARP請求?設(shè)置為 1,而?ARP應(yīng)答?設(shè)置為 2。

          下面的字段是不定長的,根據(jù)硬件類型和協(xié)議類型的改變而改變。

          譬如:如果是硬件類型是以太網(wǎng),并且協(xié)議類型是 IP 協(xié)議,那么?ar_sha?字段和?ar_tha?字段分別為 6 個字節(jié),而?ar_sip?字段和?ar_tip?字段分別為 4 個字節(jié)。

          鄰居子系統(tǒng)

          首先說明一下什么是?鄰居:在同一局域網(wǎng)中,每一臺主機(jī)都可以稱為其他主機(jī)的?鄰居。例如?Windows?系統(tǒng)可以在網(wǎng)絡(luò)中查看到同一局域網(wǎng)的鄰居主機(jī),如下圖:

          如上圖所示,每一臺主機(jī)都可以稱為?鄰居節(jié)點

          在 Linux 內(nèi)核中,也把局域網(wǎng)中的每臺主機(jī)稱為?鄰居節(jié)點,使用結(jié)構(gòu)?neighbour?來描述,neighbour?結(jié)構(gòu)定義如下:

          struct neighbour{  struct neighbour    *next;      // 用于連接哈希表中相同哈希值的鄰居節(jié)點  struct neigh_table  *tbl;       // 管理鄰居節(jié)點的鄰居表結(jié)構(gòu)  struct neigh_parms  *parms;     // 參數(shù)列表  struct net_device   *dev;       // 可以與這個鄰居節(jié)點通信的設(shè)備  unsigned long       used;       // 最后使用時間  unsigned long       confirmed;  // 最后確認(rèn)時間  unsigned long       updated;    // 最后更新時間  __u8                flags;      // 標(biāo)志位  __u8                nud_state;  // 鄰居節(jié)點所處于的狀態(tài)  __u8                type;       // 類型  __u8                dead;       // 是否已經(jīng)失效  atomic_t            probes;  rwlock_t            lock;       // 鎖                                  // 鄰居節(jié)點的硬件地址  unsigned char       ha[(MAX_ADDR_LEN+sizeof(unsigned long)-1)&~(sizeof(unsigned long)-1)];  struct hh_cache     *hh;  atomic_t            refcnt;         // 引用計數(shù)器  int                 (*output)(struct sk_buff *skb); // 發(fā)送數(shù)據(jù)給此鄰居節(jié)點的接口  struct sk_buff_head arp_queue;      // 等待ARP回復(fù)的數(shù)據(jù)包列表(需要發(fā)送的數(shù)據(jù)包列表)  struct timer_list   timer;          // 定時器  struct neigh_ops    *ops;           // 操作方法列表  u8                  primary_key[0]; // 要轉(zhuǎn)換成MAC地址的上層協(xié)議地址(如IP地址)};

          在?neighbour?結(jié)構(gòu)中,比較重要的字段有:

          • ha:鄰居節(jié)點的硬件地址,因為與鄰居節(jié)點通信需要知道其硬件地址(MAC地址)。

          • output:向鄰居節(jié)點發(fā)送數(shù)據(jù)的接口,當(dāng)要向鄰居節(jié)點發(fā)送數(shù)據(jù)時,使用這個接口把數(shù)據(jù)發(fā)送出去。

          • dev:輸出設(shè)備,如果向當(dāng)前鄰居節(jié)點發(fā)送數(shù)據(jù),需要通過這個設(shè)備來發(fā)送。

          • primary_key:要轉(zhuǎn)換成 MAC 地址的上層協(xié)議地址(如 IP 地址),由于上層協(xié)議不確定,所以這里設(shè)置?primary_key?為?柔性數(shù)組(可變大小數(shù)組)(不了解柔性數(shù)組可以查閱相關(guān)的資料)。

          如下圖所示:

          所以,當(dāng)本機(jī)需要向某一臺?鄰居節(jié)點?主機(jī)發(fā)送數(shù)據(jù)時,首先需要通過上層協(xié)議地址與輸出設(shè)備查找對應(yīng)的?neighbour?對象是否已經(jīng)存在。如果存在,那么就使用這個?neighbour?對象。否則,就新創(chuàng)建一個?neighbour?對象,并初始化其各個字段。

          查找鄰居節(jié)點信息

          要查找一個?鄰居節(jié)點?的信息,可以通過調(diào)用?neigh_lookup()?函數(shù)來完成,其實現(xiàn)如下:

          struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev){  struct neighbour *n;  u32 hash_val;  int key_len = tbl->key_len;
          hash_val = tbl->hash(pkey, dev); // 通過IP地址與設(shè)備計算鄰居節(jié)點信息的哈希值
          read_lock_bh(&tbl->lock);
          // 通過設(shè)備和IP地址從鄰居節(jié)點哈希表中查找鄰居節(jié)點信息 for (n = tbl->hash_buckets[hash_val]; n; n = n->next) { if (dev == n->dev && memcmp(n->primary_key, pkey, key_len) == 0) { neigh_hold(n); break; } }
          read_unlock_bh(&tbl->lock);
          return n; // 返回鄰居節(jié)點信息對象}

          neigh_lookup()?函數(shù)的參數(shù)含義如下:

          • tbl:鄰居節(jié)點管理表。

          • pkey:上層協(xié)議地址(如 IP 地址)。

          • dev:輸出設(shè)備對象。

          neigh_lookup()?函數(shù)工作原理如下:

          • 首先通過上層協(xié)議地址與設(shè)備計算鄰居節(jié)點信息的哈希值。

          • 然后在鄰居節(jié)點哈希表中查找對應(yīng)的鄰居節(jié)點信息,如果找到即返回鄰居節(jié)點信息,否則返回NULL。

          創(chuàng)建鄰居節(jié)點信息

          當(dāng)?鄰居節(jié)點?信息不存在時,可以通過調(diào)用?neigh_create()?函數(shù)來創(chuàng)建,其實現(xiàn)如下:

          struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, struct net_device *dev){    struct neighbour *n, *n1;    u32 hash_val;    int key_len = tbl->key_len;    int error;
          n = neigh_alloc(tbl); // 創(chuàng)建一個新的鄰居節(jié)點信息對象 if (n == NULL) return ERR_PTR(-ENOBUFS);
          memcpy(n->primary_key, pkey, key_len); // 復(fù)制上層協(xié)議地址到 primary_key 字段
          n->dev = dev; // 綁定輸出設(shè)備 dev_hold(dev);
          // 對于ARP協(xié)議會調(diào)用 arp_constructor() 函數(shù) if (tbl->constructor && (error = tbl->constructor(n)) < 0) { neigh_release(n); return ERR_PTR(error); } ... hash_val = tbl->hash(pkey, dev);
          write_lock_bh(&tbl->lock); ... // 把鄰居節(jié)點信息添加到鄰居節(jié)點信息管理哈希表中 n->next = tbl->hash_buckets[hash_val]; tbl->hash_buckets[hash_val] = n; n->dead = 0; neigh_hold(n);
          write_unlock_bh(&tbl->lock);
          return n;}

          neigh_create()?函數(shù)的工作原理如下:

          • 調(diào)用?neigh_alloc()?函數(shù)向內(nèi)存申請一個新的鄰居節(jié)點信息對象。

          • 把上層協(xié)議地址(如 IP 地址)復(fù)制到?primary_key?字段中。

          • 綁定輸出設(shè)備到?dev?字段。

          • 調(diào)用鄰居節(jié)點信息管理哈希表的?constructor()?方法來初始化鄰居節(jié)點信息對象,對于?ARP協(xié)議?這里調(diào)用的是?arp_constructor()?函數(shù)。

          • 把新創(chuàng)建的鄰居節(jié)點信息對象添加到鄰居節(jié)點信息管理哈希表中。

          對于?arp_constructor()?函數(shù),主要是用于初始化鄰居節(jié)點信息對象的操作方法列表和?output?字段,如下:

          static struct neigh_ops arp_generic_ops = {    AF_INET,                  // family    NULL,                     // destructor    arp_solicit,              // solicit    arp_error_report,         // error_report    neigh_resolve_output,     // output    neigh_connected_output,   // connected_output    dev_queue_xmit,           // hh_output    dev_queue_xmit            // queue_xmit};
          static int arp_constructor(struct neighbour *neigh){ u32 addr = *(u32*)neigh->primary_key; struct net_device *dev = neigh->dev; ... neigh->type = inet_addr_type(addr); ... if (dev->hard_header_cache) neigh->ops = &arp_hh_ops; else neigh->ops = &arp_generic_ops;
          if (neigh->nud_state & NUD_VALID) neigh->output = neigh->ops->connected_output; else neigh->output = neigh->ops->output;
          return 0;}

          從?arp_constructor()?函數(shù)的實現(xiàn)可以知道,鄰居節(jié)點信息對象的ops?字段被設(shè)置為?arp_generic_ops,而?output?字段會被設(shè)置為?neigh_resolve_output()?函數(shù)(當(dāng)鄰居節(jié)點信息對象不可用時)或者?neigh_connected_output()?函數(shù)(當(dāng)鄰居節(jié)點信息對象可用時)。

          所以,一個新創(chuàng)建的?鄰居節(jié)點信息對象?各個字段的值大概如下圖所示:

          由于此時還不知道鄰居節(jié)點的 MAC 地址,所以其?ha?字段的值為 0。

          向鄰居節(jié)點發(fā)送數(shù)據(jù)

          當(dāng)向鄰居節(jié)點發(fā)送數(shù)據(jù)時,需要調(diào)用鄰居節(jié)點信息對象的?output?接口。根據(jù)前面的分析,output?接口被設(shè)置為?neigh_resolve_output()?函數(shù)。我們來分析一下?neigh_resolve_output()?函數(shù)的實現(xiàn):

          int neigh_resolve_output(struct sk_buff *skb){    struct dst_entry *dst = skb->dst;    struct neighbour *neigh;    ...    if (neigh_event_send(neigh, skb) == 0) {        int err;        struct net_device *dev = neigh->dev;
          if (dev->hard_header_cache && dst->hh == NULL) { ... } else { read_lock_bh(&neigh->lock); err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); read_unlock_bh(&neigh->lock); } if (err >= 0) return neigh->ops->queue_xmit(skb); kfree_skb(skb); return -EINVAL; } return 0; ...}

          neigh_resolve_output()?函數(shù)主要完成三件事:

          • 調(diào)用?neigh_event_send()?函數(shù)發(fā)送一個查詢鄰居節(jié)點 MAC 地址的 ARP 請求。

          • 調(diào)用設(shè)備的?hard_header()?方法設(shè)置數(shù)據(jù)包的目標(biāo) MAC 地址(如果鄰居節(jié)點的 MAC 地址已經(jīng)獲取到)。

          • 如果數(shù)據(jù)包的目標(biāo) MAC 地址設(shè)置成功,調(diào)用鄰居節(jié)點信息對象的?queue_xmit()?方法把數(shù)據(jù)發(fā)送出去(對于 ARP 協(xié)議,queue_xmit()?方法對應(yīng)的是?dev_queue_xmit()?函數(shù))。

          我們再來看看?neigh_event_send()?函數(shù)怎么發(fā)送 ARP 請求:

          int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb){    write_lock_bh(&neigh->lock);    if (!(neigh->nud_state & (NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) {        if (!(neigh->nud_state & (NUD_STALE|NUD_INCOMPLETE))) {            if (neigh->parms->mcast_probes + neigh->parms->app_probes) {                ...                neigh->nud_state = NUD_INCOMPLETE;                ...                neigh->ops->solicit(neigh, skb); // 發(fā)送查詢鄰居節(jié)點MAC地址的ARP請求                ...            } else {                ...            }        }
          if (neigh->nud_state == NUD_INCOMPLETE) { if (skb) { ... __skb_queue_head(&neigh->arp_queue, skb); // 把數(shù)據(jù)包添加到arp_queue隊列中 } write_unlock_bh(&neigh->lock); return 1; } ... } write_unlock_bh(&neigh->lock); return 0;}

          __neigh_event_send()?函數(shù)主要完成兩個工作:

          • 首先調(diào)用鄰居節(jié)點信息對象的?solicit()?方法發(fā)送一個 ARP 請求,從前面的分析可知,solicit()?方法會被設(shè)置為?arp_solicit()?函數(shù)。

          • 然后把要發(fā)送的數(shù)據(jù)包添加到鄰居節(jié)點信息對象的?arp_queue?隊列中,等待獲取到鄰居節(jié)點 MAC 地址后重新發(fā)送這個數(shù)據(jù)包。

          鄰居節(jié)點信息對象的?arp_queue?隊列用于緩存等待發(fā)送的數(shù)據(jù)包,如下圖:

          發(fā)送 ARP 請求

          通過前面的分析可知,當(dāng)向鄰居節(jié)點發(fā)送數(shù)據(jù)時,如果還不知道鄰居節(jié)點的 MAC 地址,那么首先會調(diào)用?arp_solicit()?函數(shù)發(fā)送一個?ARP請求?來獲取鄰居節(jié)點的 MAC 地址,其實現(xiàn)如下:

          static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb){    u32 saddr;                               // 源IP地址(本地IP地址)    u8 *dst_ha = NULL;                       // 接收ARP請求目標(biāo)MAC地址(發(fā)廣播信息設(shè)置為NULL)    struct net_device *dev = neigh->dev;     // 輸出設(shè)備    u32 target = *(u32*)neigh->primary_key;  // 要查詢的鄰居節(jié)點IP地址    ...
          if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) saddr = skb->nh.iph->saddr; else saddr = inet_select_addr(dev, target, RT_SCOPE_LINK); ...
          arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr, dst_ha, dev->dev_addr, NULL); ...}

          arp_solicit()?函數(shù)最終會調(diào)用?arp_send()?函數(shù)發(fā)送一個 ARP 請求,我們來分析一下?arp_send()?函數(shù)的實現(xiàn):

          void arp_send(int type, int ptype, u32 dest_ip,              struct net_device *dev, u32 src_ip,              unsigned char *dest_hw, unsigned char *src_hw,              unsigned char *target_hw){    struct sk_buff *skb;    struct arphdr *arp;    unsigned char *arp_ptr;    ...    // 申請一個數(shù)據(jù)包對象    skb = alloc_skb(sizeof(struct arphdr) + 2*(dev->addr_len+4)                        + dev->hard_header_len + 15, GFP_ATOMIC);    ...
          // ARP請求頭部 arp = (struct arphdr *)skb_put(skb, sizeof(struct arphdr) + 2*(dev->addr_len+4));
          skb->dev = dev; skb->protocol = __constant_htons(ETH_P_ARP);
          if (src_hw == NULL) src_hw = dev->dev_addr; if (dest_hw == NULL) dest_hw = dev->broadcast;
          // 下面設(shè)置ARP頭部的各個字段信息 ... switch (dev->type) { default: arp->ar_hrd = htons(dev->type); // 設(shè)置硬件類型 arp->ar_pro = __constant_htons(ETH_P_IP); // 設(shè)置上層協(xié)議類型為IP協(xié)議 break; ... }
          arp->ar_hln = dev->addr_len; // 設(shè)置硬件地址長度 arp->ar_pln = 4; // 設(shè)置上層協(xié)議地址長度 arp->ar_op = htons(type); // ARP請求操作碼類型
          arp_ptr = (unsigned char *)(arp + 1);
          memcpy(arp_ptr, src_hw, dev->addr_len); // 復(fù)制源MAC地址(本機(jī)MAC地址)
          arp_ptr += dev->addr_len; memcpy(arp_ptr, &src_ip,4); // 復(fù)制源IP地址(本機(jī)IP地址)
          // 復(fù)制目標(biāo)MAC地址(對于查詢請求設(shè)置為0) arp_ptr += 4; if (target_hw != NULL) memcpy(arp_ptr, target_hw, dev->addr_len); else memset(arp_ptr, 0, dev->addr_len);
          // 復(fù)制目標(biāo)IP地址 arp_ptr += dev->addr_len; memcpy(arp_ptr, &dest_ip, 4); ... dev_queue_xmit(skb); // 把數(shù)據(jù)發(fā)送出去 return;}

          arp_send()?函數(shù)的工作也比較清晰:

          • 首先調(diào)用?alloc_skb()?函數(shù)申請一個數(shù)據(jù)包對象。

          • 然后設(shè)置 ARP 頭部各個字段的信息。

          • 調(diào)用?dev_queue_xmit()?函數(shù)把數(shù)據(jù)發(fā)送出去。

          處理 ARP 回復(fù)

          當(dāng)鄰居節(jié)點回復(fù) MAC 地址查詢 ARP 請求時,本機(jī)需要處理此 ARP 回復(fù)。本機(jī)通過?arp_rcv()?函數(shù)來處理 ARP 回復(fù),代碼如下:

          int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt){    struct arphdr *arp = skb->nh.arph;    unsigned char *arp_ptr = (unsigned char *)(arp+1);    struct rtable *rt;    unsigned char *sha, *tha;    u32 sip, tip;    u16 dev_type = dev->type;    int addr_type;    struct in_device *in_dev = in_dev_get(dev);    struct neighbour *n;    ...    // 從ARP回復(fù)中獲取數(shù)據(jù)    sha = arp_ptr;             // 源MAC地址(鄰居節(jié)點的MAC地址)    arp_ptr += dev->addr_len;    memcpy(&sip, arp_ptr, 4);  // 源IP地址(鄰居節(jié)點的IP地址)
          arp_ptr += 4; tha = arp_ptr; // 目標(biāo)MAC地址(本機(jī)的MAC地址)
          arp_ptr += dev->addr_len; memcpy(&tip, arp_ptr, 4); // 目標(biāo)IP地址(本機(jī)的IP地址) ...
          n = __neigh_lookup(&arp_tbl, &sip, dev, 0); // 查找鄰居節(jié)點信息對象 if (n) { int state = NUD_REACHABLE; int override = 0;
          if (jiffies - n->updated >= n->parms->locktime) override = 1;
          if (arp->ar_op != __constant_htons(ARPOP_REPLY) || skb->pkt_type != PACKET_HOST) state = NUD_STALE;
          neigh_update(n, sha, state, override, 1); // 更新鄰居節(jié)點信息對象 neigh_release(n); } ... return 0;}

          arp_rcv()?函數(shù)主要完成以下工作:

          • 通過從 ARP 回復(fù)中獲取到鄰居節(jié)點的 MAC 地址和 IP 地址。

          • 通過鄰居節(jié)點的 IP 地址和輸入設(shè)備來查找對應(yīng)的鄰居節(jié)點信息對象。

          • 如果鄰居節(jié)點信息對象已經(jīng)存在,那么調(diào)用?neigh_update()?函數(shù)更新鄰居節(jié)點信息對象的數(shù)據(jù)。

          我們來看看?neigh_update()?函數(shù)怎么更新鄰居節(jié)點信息對象的數(shù)據(jù):

          int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override, int arp){    u8 old;    int err;    int notify = 0;    struct net_device *dev = neigh->dev;    ...    old = neigh->nud_state;    // 更新前鄰居節(jié)點信息對象的狀態(tài)    ...    neigh->nud_state = new;    // 更新鄰居節(jié)點信息對象的狀態(tài)    if (lladdr != neigh->ha) { // 更新鄰居節(jié)點信息對象的MAC地址        memcpy(&neigh->ha, lladdr, dev->addr_len);        ...    }    ...    if (!(old&NUD_VALID)) {        struct sk_buff *skb;
          // 如果 arp_queue 隊列中有等待發(fā)送的數(shù)據(jù)包, 現(xiàn)在可以把這些數(shù)據(jù)包發(fā)送出去 while (neigh->nud_state & NUD_VALID && (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { struct neighbour *n1 = neigh; write_unlock_bh(&neigh->lock); ... n1->output(skb); write_lock_bh(&neigh->lock); } skb_queue_purge(&neigh->arp_queue); } ... return err;}

          neigh_update()?函數(shù)主要完成以下工作:

          • 更新鄰居節(jié)點信息對象的?ha?字段(也就是 MAC 地址)為 ARP 回復(fù)中獲得的鄰居節(jié)點 MAC 地址。

          • 如果鄰居節(jié)點對象的?arp_queue?隊列不為空,說明有等待發(fā)送的數(shù)據(jù)包,此時調(diào)用鄰居節(jié)點信息的?output()?接口把這些數(shù)據(jù)發(fā)送出去。從上下文可知,此時的?output()?還是為?neigh_resolve_output()?函數(shù)。但由于鄰居節(jié)點的 MAC 地址已經(jīng)獲得,所以不會再發(fā)送 ARP 請求。而是調(diào)用設(shè)備的?hard_header()?接口設(shè)置數(shù)據(jù)包的目標(biāo) MAC 地址,然后 調(diào)用?dev_queue_xmit()?函數(shù)把數(shù)據(jù)包發(fā)送出去。


          瀏覽 76
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  麻豆精品无码国产 | 九九久久精品视频 | 成人视频导航 | 动漫操逼视频 | A片在线视频免费观看 |