Linux 内核抓包功能实现基础(四) 手动查找邻居缓存填充MAC地址
????????? 之前寫了三篇關于內核抓包功能的實現,包括抓包原理、實現以及抓包服務器的實現。基本的功能都已經有了,但是還有些小問題有待解決。今天有空就解決一下。
??????????? 開發版本基于內核3.4.39。
?????????? 先介紹一下問題背景,抓包框架是基于netfilter的,通過在Pre-Routing和Post-Routing鏈上分別掛一個鉤子函數來對報文處理。抓包基本上需要報文所有的原始數據,包括mac地址、ip地址以及數據層。對于后兩者數據本來都在,但是對于mac地址,就不一定啦。所有進來的報文都是帶有mac地址的,但是對于本機出去的報文來說,mac地址也是有的,只不過在IP層還看不到,因為我們掛載post-routing鏈上的鉤子仍屬于IP層,系統報文在IP層只負責IP層頭域的修改,至于mac層,還需要繼續往下走。直到查找鄰居緩存,找到后就填寫mac地址發送出去,找不到的話就發送arp廣播同時緩存該報文,一定時間內沒收到arp響應或者緩存的報文數量過多,報文就會被丟棄。如果我們要在IP層去填充mac地址的話,就必須手動去查找系統鄰居緩存了。至于怎么查,可以直接參考系統TCP/IP協議棧的處理方式。
????????? 我們首先看一下系統是怎么處理mac地址信息的:
?????? 首先,ip_output接收skb后,會先讓掛在netfilter post-routing鏈上的鉤子函數處理,我們的抓包鉤子也在這里,處理完成后,如果報文還在的話,就傳給ip_finish_output處理,這個函數主要就是根據 MTU 判斷報文長度是否太長,太長的話就調用ip_fragment函數處理,不管報文是否分片,最終都會調用ip_finish_output2函數。
??????? 而這個ip_finish_output2函數就去獲取鄰居信息,通過調用dst_get_neighbour_noref得到鄰居信息,之后調用neigh_output函數。neigh_output函數就去判斷是否存在緩存項,存在的話就調用neigh_hh_output填充mac地址,填完之后就調用發送函數,至此報文發送完成,但是如果不存在緩存項的話,就會先緩存報文并發送arp請求,直到有響應或者超時或者緩存報文過多就把它丟棄。我們來看一下這幾個函數:
? ? ?
static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) {unsigned seq;int hh_len;//填充mac地址do { int hh_alen;seq = read_seqbegin(&hh->hh_lock);hh_len = hh->hh_len;hh_alen = HH_DATA_ALIGN(hh_len);memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);} while (read_seqretry(&hh->hh_lock, seq));//將mac頭域壓入數據棧中skb_push(skb, hh_len);//發送函數,報文處理完成return dev_queue_xmit(skb); }? 以上就是內核對網絡報文mac地址的處理,可以看到在Netfilter 的Post-Routing鏈上報文是還沒有mac地址的,因此我們這里可以手動去設置,方式就是參考內核的實現,只不過提前處理了。
總結
以上是生活随笔為你收集整理的Linux 内核抓包功能实现基础(四) 手动查找邻居缓存填充MAC地址的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BFD (双向转发检测) 协议简介与开发
- 下一篇: 推荐优秀团员个人简历(优秀团员个人简历)