日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 内核抓包功能实现基础(四) 手动查找邻居缓存填充MAC地址

發布時間:2025/4/5 linux 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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 ip_finish_output2(struct sk_buff *skb) {struct dst_entry *dst = skb_dst(skb); /* 協議無關的目的緩存相關的數據結構 */struct rtable *rt = (struct rtable *)dst; /* 路由緩存相關結構體 */struct net_device *dev = dst->dev;unsigned int hh_len = LL_RESERVED_SPACE(dev);struct neighbour *neigh;// if (rt->rt_type == RTN_MULTICAST) {IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);} else if (rt->rt_type == RTN_BROADCAST)IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTBCAST, skb->len);/* Be paranoid, rather than too clever. */// 如果首部空間不夠大的話,則重新分配if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {struct sk_buff *skb2;skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));if (skb2 == NULL) {kfree_skb(skb);return -ENOMEM;}if (skb->sk)skb_set_owner_w(skb2, skb->sk);kfree_skb(skb);skb = skb2;}rcu_read_lock();//獲取鄰居信息,獲取失敗就丟棄報文neigh = dst_get_neighbour_noref(dst);if (neigh) { //調用鄰居層發送接口int res = neigh_output(neigh, skb);rcu_read_unlock();return res;}rcu_read_unlock();if (net_ratelimit())printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");kfree_skb(skb);return -EINVAL; } static inline int neigh_output(struct neighbour *n, struct sk_buff *skb) {struct hh_cache *hh = &n->hh;if ((n->nud_state & NUD_CONNECTED) && hh->hh_len)return neigh_hh_output(hh, skb); //存在緩存則填充并發送elsereturn n->output(n, skb); //不存在則送至緩存隊列 }

? ? ?

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地址的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。