在netif_receive_skb()函數(shù)中,可以看出處理的是像ARP、IP這些鏈路層以上的協(xié)議,那么,鏈路層報(bào)頭是在哪里去掉的呢?答案是網(wǎng)卡驅(qū)動(dòng)中,在調(diào)用netif_receive_skb()前,
skb->protocol = eth_type_trans(skb, bp->dev);
該函數(shù)對(duì)處理后skb>data跳過以太網(wǎng)報(bào)頭,由mac_header指示以太網(wǎng)報(bào)頭:
進(jìn)入netif_receive_skb()函數(shù)
list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list)
按照協(xié)議類型依次由相應(yīng)的協(xié)議模塊進(jìn)行處理,而所以的協(xié)議模塊處理都會(huì)注冊(cè)在ptype_base中,實(shí)際是鏈表結(jié)構(gòu)。
net/core/dev.c
static struct list_head ptype_base __read_mostly; /* Taps */
而相應(yīng)的協(xié)議模塊是通過dev_add_pack()函數(shù)加入的:
void dev_add_pack(struct packet_type *pt)
{
int hash;
spin_lock_bh(&ptype_lock);
if (pt->type == htons(ETH_P_ALL))
list_add_rcu(&pt->list, &ptype_all);
else {
hash = ntohs(pt->type) & PTYPE_HASH_MASK;
list_add_rcu(&pt->list, &ptype_base[hash]);
}
spin_unlock_bh(&ptype_lock);
}
以ARP處理為例
該模塊的定義,它會(huì)在arp_init()中注冊(cè)進(jìn)ptype_base鏈表中:
static struct packet_type arp_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_ARP),
.func = arp_rcv,
};
然后在根據(jù)報(bào)文的TYPE來在ptype_base中查找相應(yīng)協(xié)議模塊進(jìn)行處理時(shí),實(shí)際調(diào)用arp_rcv()進(jìn)行接收
arp_rcv() --> arp_process()
arp = arp_hdr(skb);
……
arp_ptr= (unsigned char *)(arp+1);
sha= arp_ptr;
arp_ptr += dev->addr_len;
memcpy(&sip, arp_ptr, 4);
arp_ptr += 4;
arp_ptr += dev->addr_len;
memcpy(&tip, arp_ptr, 4);
操作后這指針位置:
然后判斷是ARP請(qǐng)求報(bào)文,這時(shí)先查詢路由表ip_route_input()
if (arp->ar_op == htons(ARPOP_REQUEST) &&
ip_route_input(skb, tip, sip, 0, dev) == 0)
在ip_route_input()函數(shù)中,先在cache中查詢是否存在相應(yīng)的路由表項(xiàng):
hash = rt_hash(daddr, saddr, iif, rt_genid(net));
緩存的路由項(xiàng)在內(nèi)核中組織成hash表的形式,因此在查詢時(shí),先算出的hash值,再用該項(xiàng)- rt_hash_table[hash].chain即可。這里可以看到,緩存路由項(xiàng)包括了源IP地址、目的IP地址、網(wǎng)卡號(hào)。
如果在緩存中沒有查到匹配項(xiàng),或指定不查詢cache,則查詢路由表ip_route_input_slow();
進(jìn)入ip_route_input_slow()函數(shù),最終調(diào)用fib_lookup()得到查詢結(jié)果fib_result
if ((err = fib_lookup(net, &fl, &res)) != 0)
如果結(jié)果fib_result合法,則需要更新路由緩存,將此次查詢結(jié)果寫入緩存
hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));
err = rt_intern_hash(hash, rth, NULL, skb, fl.iif);
在查找完路由表后,回到arp_process()函數(shù),如果路由項(xiàng)指向本地,則應(yīng)由本機(jī)接收該報(bào)文:
if (addr_type == RTN_LOCAL) {
……
if (!dont_send) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n) {
arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
neigh_release(n);
}
}
goto out;
}
首先更新鄰居表neigh_event_ns(),然后發(fā)送ARP響應(yīng) – arp_send。
至此,大致的ARP流程完成。由于ARP部分涉及到路由表以及鄰居表,這都是很大的概念,在下一篇中介紹,這里直接略過了。
聯(lián)系客服