伦伦影院久久影视,天天操天天干天天射,ririsao久久精品一区 ,一本大道香蕉大久在红桃,999久久久免费精品国产色夜,色悠悠久久综合88,亚洲国产精品久久无套麻豆,亚洲香蕉毛片久久网站,一本一道久久综合狠狠老

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

tcpdump如何實現抓內核態的包

Linux愛好者 ? 來源:開發內功修煉 ? 作者:張彥飛allen ? 2021-10-08 10:34 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

今天聊聊大家工作中經常用到的 tcpdump。

在網絡包的發送和接收過程中,絕大部分的工作都是在內核態完成的。那么問題來了,我們常用的運行在用戶態的程序 tcpdump 是那如何實現抓到內核態的包的呢?有的同學知道 tcpdump 是基于 libpcap 的,那么 libpcap 的工作原理又是啥樣的呢。如果讓你裸寫一個抓包程序,你有沒有思路?

按照飛哥的風格,不搞到最底層的原理咱是不會罷休的。所以我對相關的源碼進行了深入分析。通過本文,你將徹底搞清楚了以下這幾個問題。

tcpdump 是如何工作的?

netfilter 過濾的包 tcpdump 是否可以抓的到?

讓你自己寫一個抓包程序的話該如何下手?

借助這幾個問題,我們來展開今天的探索之旅!

一、網絡包接收過程

在圖解Linux網絡包接收過程一文中我們詳細介紹了網絡包是如何從網卡到達用戶進程中的。這個過程我們可以簡單用如下這個圖來表示。

772732fa-239b-11ec-82a8-dac502259ad0.png

找到 tcpdump 抓包點

我們在網絡設備層的代碼里找到了 tcpdump 的抓包入口。在 __netif_receive_skb_core 這個函數里會遍歷 ptype_all 上的協議。還記得上文中我們提到 tcpdump 在 ptype_all 上注冊了虛擬協議。這時就能執行的到了。來看函數:

//file: net/core/dev.cstatic int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)

{

。..。..

//遍歷 ptype_all (tcpdump 在這里掛了虛擬協議)

list_for_each_entry_rcu(ptype, &ptype_all, list) {

if (!ptype-》dev || ptype-》dev == skb-》dev) {

if (pt_prev)

ret = deliver_skb(skb, pt_prev, orig_dev);

pt_prev = ptype;

}

}

}

在上面函數中遍歷 ptype_all,并使用 deliver_skb 來調用協議中的回調函數。

//file: net/core/dev.c static inline int deliver_skb(。..)

{

return pt_prev-》func(skb, skb-》dev, pt_prev, orig_dev);

}

對于 tcpdump 來說,就會進入 packet_rcv 了(后面我們再說為啥是進入這個函數)。這個函數在 net/packet/af_packet.c 文件中。

//file: net/packet/af_packet.cstatic int packet_rcv(struct sk_buff *skb, 。..)

{

__skb_queue_tail(&sk-》sk_receive_queue, skb);

。..。..

}

可見 packet_rcv 把收到的 skb 放到了當前 packet socket 的接收隊列里了。這樣后面調用 recvfrom 的時候就可以獲取到所抓到的包!!

再找 netfilter 過濾點

為了解釋我們開篇中提到的問題,這里我們再稍微到協議層中多看一些。在 ip_rcv 中我們找到了一個 netfilter 相關的執行邏輯。

//file: net/ipv4/ip_input.cint ip_rcv(。..)

{

。..。..

return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,

ip_rcv_finish);

}

如果你用 NF_HOOK 作為關鍵詞來搜索,還能搜到不少 netfilter 的過濾點。不過所有的過濾點都是位于 IP 協議層的。

在接收包的過程中,數據包是先經過網絡設備層然后才到協議層的。

77693a4c-239b-11ec-82a8-dac502259ad0.png

那么我們開篇中的一個問題就有了答案了。假如我們設置了 netfilter 規則,在接收包的過程中,工作在網絡設備層的 tcpdump 先開始工作。還沒等 netfilter 過濾,tcpdump 就抓到包了!

所以,在接收包的過程中,netfilter 過濾并不會影響 tcpdump 的抓包!

二、網絡包發送過程

我們接著再來看網絡包發送過程。在25 張圖,一萬字,拆解 Linux 網絡包發送過程一文中,我們詳細描述過網絡包的發送過程。發送過程可以匯總成簡單的一張圖。

77a4a190-239b-11ec-82a8-dac502259ad0.png

找到 netfilter 過濾點

在發送的過程中,同樣是在 IP 層進入各種 netfilter 規則的過濾。

//file: net/ipv4/ip_output.c int ip_local_out(struct sk_buff *skb)

{

//執行 netfilter 過濾

err = __ip_local_out(skb);

}

int __ip_local_out(struct sk_buff *skb)

{

。..。..

return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,

skb_dst(skb)-》dev, dst_output);

}

在這個文件中,還能看到若干處 netfilter 過濾邏輯。

找到 tcpdump 抓包點

發送過程在協議層處理完畢到達網絡設備層的時候,也有 tcpdump 的抓包點。

//file: net/core/dev.cint dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,

struct netdev_queue *txq)

{

。..

if (!list_empty(&ptype_all))

dev_queue_xmit_nit(skb, dev);

}

static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)

{

list_for_each_entry_rcu(ptype, &ptype_all, list) {

if ((ptype-》dev == dev || !ptype-》dev) &&

(!skb_loop_sk(ptype, skb))) {

if (pt_prev) {

deliver_skb(skb2, pt_prev, skb-》dev);

pt_prev = ptype;

continue;

}

。..。..

}

}

}

在上述代碼中我們看到,在 dev_queue_xmit_nit 中遍歷 ptype_all 中的協議,并依次調用 deliver_skb。這就會執行到 tcpdump 掛在上面的虛擬協議。

在網絡包的發送過程中,和接收過程恰好相反,是協議層先處理、網絡設備層后處理。

77da513c-239b-11ec-82a8-dac502259ad0.png

如果 netfilter 設置了過濾規則,那么在協議層就直接過濾掉了。在下層網絡設備層工作的 tcpdump 將無法再捕獲到該網絡包。

三、TCPDUMP 啟動

前面兩小節我們說到了內核收發包都通過遍歷 ptype_all 來執行抓包的。那么我們現在來看看用戶態的 tcpdump 是如何掛載協議到內 ptype_all 上的。

我們通過 strace 命令我們抓一下 tcpdump 命令的系統調用,顯示結果中有一行 socket 系統調用。Tcpdump 秘密的源頭就藏在這行對 socket 函數的調用里。

# strace tcpdump -i eth0

socket(AF_PACKET, SOCK_RAW, 768)

。..。..

socket 系統調用的第一個參數表示創建的 socket 所屬的地址簇或者協議簇,取值以 AF 或者 PF 開頭。在 Linux 里,支持很多種協議族,在 include/linux/socket.h 中可以找到所有的定義。這里創建的是 packet 類型的 socket。

協議族和地址族:每一種協議族都有其對應的地址族。比如 IPV4 的協議族定義叫 PF_INET,其地址族的定義是 AF_INET。它們是一一對應的,而且值也完全一樣,所以經常混用。

//file: include/linux/socket.h#define AF_UNSPEC 0#define AF_UNIX 1 /* Unix domain sockets */#define AF_LOCAL 1 /* POSIX name for AF_UNIX */#define AF_INET 2 /* Internet IP Protocol */#define AF_INET6 10 /* IP version 6 */#define AF_PACKET 17 /* Packet family */

。..。..

另外上面第三個參數 768 代表的是 ETH_P_ALL,socket.htons(ETH_P_ALL) = 768。

我們來展開看這個 packet 類型的 socket 創建的過程中都干了啥,找到 socket 創建源碼。

//file: net/socket.c

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)

{

。..。..

retval = sock_create(family, type, protocol, &sock);

}

int __sock_create(struct net *net, int family, int type, 。..)

{

。..。..

pf = rcu_dereference(net_families[family]);

err = pf-》create(net, sock, protocol, kern);

}

在 __sock_create 中,從 net_families 中獲取了指定協議。并調用了它的 create 方法來完成創建。

net_families 是一個數組,除了我們常用的 PF_INET( ipv4 ) 外,還支持很多種協議族。比如 PF_UNIX、PF_INET6(ipv6)、PF_PACKET等等。每一種協議族在 net_families 數組的特定位置都可以找到其 family 類型。在這個 family 類型里,成員函數 create 指向該協議族的對應創建函數。

780b9012-239b-11ec-82a8-dac502259ad0.png

根據上圖,我們看到對于 packet 類型的 socket,pf-》create 實際調用到的是 packet_create 函數。我們進入到這個函數中來一探究竟,這是理解 tcpdump 工作原理的關鍵!

//file: packet/af_packet.cstatic int packet_create(struct net *net, struct socket *sock, int protocol,

int kern)

{

。..

po = pkt_sk(sk);

po-》prot_hook.func = packet_rcv;

//注冊鉤子

if (proto) {

po-》prot_hook.type = proto;

register_prot_hook(sk);

}

}

static void register_prot_hook(struct sock *sk)

{

struct packet_sock *po = pkt_sk(sk);

dev_add_pack(&po-》prot_hook);

}

在 packet_create 中設置回調函數為 packet_rcv,再通過 register_prot_hook =》 dev_add_pack 完成注冊。注冊完后,是在全局協議 ptype_all 鏈表中添加了一個虛擬的協議進來。

78583390-239b-11ec-82a8-dac502259ad0.png

我們再來看下 dev_add_pack 是如何注冊協議到 ptype_all 中的。回顧我們開頭看到的 socket 函數調用,第三個參數 proto 傳入的是 ETH_P_ALL。那 dev_add_pack 其實最后是把 hook 函數添加到了 ptype_all 里了,代碼如下。

//file: net/core/dev.cvoid dev_add_pack(struct packet_type *pt)

{

struct list_head *head = ptype_head(pt);

list_add_rcu(&pt-》list, head);

}

static inline struct list_head *ptype_head(const struct packet_type *pt)

{

if (pt-》type == htons(ETH_P_ALL))

return &ptype_all;

else

return &ptype_base[ntohs(pt-》type) & PTYPE_HASH_MASK];

}

我們整篇文章都以 ETH_P_ALL 為例,但其實有的時候也會有其它情況。在別的情況下可能會注冊協議到 ptype_base 里了,而不是 ptype_all。同樣, ptype_base 中的協議也會在發送和接收的過程中被執行到。

總結:tcpdump 啟動的時候內部邏輯其實很簡單,就是在 ptype_all 中注冊了一個虛擬協議而已。

四、總結

現在我們再回頭看開篇提到的幾個問題。

1. tcpdump是如何工作的

用戶態 tcpdump 命令是通過 socket 系統調用,在內核源碼中用到的 ptype_all 中掛載了函數鉤子上去。無論是在網絡包接收過程中,還是在發送過程中,都會在網絡設備層遍歷 ptype_all 中的協議,并執行其中的回調。tcpdump 命令就是基于這個底層原理來工作的。

2. netfilter 過濾的包 tcpdump是否可以抓的到

關于這個問題,得分接收和發送過程分別來看。在網絡包接收的過程中,由于 tcpdump 近水樓臺先得月,所以完全可以捕獲到命中 netfilter 過濾規則的包。

77693a4c-239b-11ec-82a8-dac502259ad0.png

但是在發送的過程中,恰恰相反。網絡包先經過協議層,這時候被 netfilter 過濾掉的話,底層工作的 tcpdump 還沒等看見就啥也沒了。

77da513c-239b-11ec-82a8-dac502259ad0.png

3. 讓你自己寫一個抓包程序的話該如何下手

如果你想自己寫一段類似 tcpdump 的抓包程序的話,使用 packet socket 就可以了。我用 c 寫了一段抓包,并且解析源 IP 和目的 IP 的簡單 demo。

源碼地址:https://github.com/yanfeizhang/coder-kung-fu/blob/main/tests/network/test04/main.c

編譯一下,注意運行需要 root 權限。

# gcc -o main main.c# 。/main

運行結果預覽如下。

78f00468-239b-11ec-82a8-dac502259ad0.png

責任編輯:haq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    4

    文章

    1472

    瀏覽量

    43066
  • 網絡
    +關注

    關注

    14

    文章

    8313

    瀏覽量

    95450

原文標題:用戶態 tcpdump 如何實現抓到內核網絡包的?

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    睿擎派 3562 整固件制作指南

    在嵌入式開發中,經常遇到需要定制固件的場景:更新RT-Thread應用程序、替換Linux內核或設備樹、修改根文件系統配置等。這些需求都離不開對Rockchipupdate.img整固件的解包
    的頭像 發表于 04-01 18:08 ?324次閱讀
    睿擎派 3562 整<b class='flag-5'>包</b>固件制作指南

    Linux內核驅動開發的技術核心精要

    開發中必須理解的技術要點,供從業者參考。 一、并發與同步:多核系統的根基 現代內核默認支持SMP(對稱多處理),驅動代碼可能同時運行在多個CPU核、中斷、軟中斷、搶占路徑上,競風險無處不在。內核提供
    發表于 03-10 13:56

    內核配置項引發網絡性能下降的深度剖析

    在嵌入式系統開發中,內核配置對系統性能起著關鍵作用。近期在對基于 Rockchip 平臺的 Linux 內核配置調試時,發現三個內核跟蹤器配置項(CONFIG_IRQSOFF_TRACER
    的頭像 發表于 02-01 16:48 ?1782次閱讀
    <b class='flag-5'>內核</b>配置項引發網絡性能下降的深度剖析

    如何使用wireshark進行遠程

    一、概述 通常我們使用wireshark進行分析的時候,在遠端設備抓取一部分數據后,再回傳到本地,然后使用wireshark進行分析。這種操作主要是抓取的數據不是實時的,不能做
    的頭像 發表于 01-16 16:05 ?1129次閱讀
    如何使用wireshark進行遠程<b class='flag-5'>抓</b><b class='flag-5'>包</b>

    深入Linux內核:進程調度的核心邏輯與實現細節

    ,背后都離不開內核調度算法的精準操控。今天,我們就從優先級、調度算法、時間片分配到底層實現,全方位拆解Linux內核進程調度的核心邏輯。 一、進程調度的“身份標識”:優先級與分類 要理解調度邏輯,首先得搞懂:進程憑什么“插隊”?
    的頭像 發表于 12-24 07:05 ?4482次閱讀
    深入Linux<b class='flag-5'>內核</b>:進程調度的核心邏輯與<b class='flag-5'>實現</b>細節

    Linux內核日志玩明白了嗎?printk調試神器全解析

    的日志等級機制,從參數配置到實戰用法一次講透~一、printk與printf的差異用戶的printf大家都熟,直接打印內容,簡單粗暴。但內核場景更復雜,系統崩潰或是
    的頭像 發表于 12-19 08:32 ?996次閱讀
    Linux<b class='flag-5'>內核</b>日志玩明白了嗎?printk調試神器全解析

    fpga嵌入e203內核搭建soc如何實現通信功能?

    在fpga嵌入e203內核實現以太網,開發板有PHY芯片LAN8720A,怎么搭建soc,如何使用總線,實現通信功能?
    發表于 11-10 06:54

    探索操作系統底層的關鍵接口

      在linux中,將程序的運行空間分為內核空間與用戶空間(內核和用戶),在邏輯上它們之間是相互隔離的,因此用戶程序不能訪問內核數據,也
    的頭像 發表于 11-08 12:42 ?841次閱讀

    求助,關于rt-smart用戶線程實時性差的問題求解

    我在樹莓派4B上使用v5.2.0 開啟smart的rt-thread 并啟用SMP多核(4核)內核時,在用戶內核運行同樣的代碼測試: #include #include
    發表于 09-26 08:25

    TCPDump分析實戰

    作為一名資深運維工程師,我在生產環境中遇到過各種奇葩的網絡問題。今天分享10個真實案例,帶你掌握TCPDump這把利器,讓網絡問題無處遁形!
    的頭像 發表于 08-06 17:58 ?1171次閱讀

    詳解Linux網絡管理中的關鍵命令

    本文檔概述了網絡管理中的關鍵命令,如ifconfig配置網絡接口,ip管理路由,ping測試連通性,以及nmap進行安全掃描。還介紹了nslookup和dig用于域名解析,tcpdump分析,以及arp操作和nmap的深入應
    的頭像 發表于 07-04 11:37 ?1372次閱讀
    詳解Linux網絡管理中的關鍵命令

    鴻蒙5開發寶藏案例分享---折疊屏懸停開發實踐

    ?【鴻蒙折疊屏開發寶藏指南】原來官方藏了這么多好東西!手把手教你玩轉懸停開發**?** Hey小伙伴們!我是你們的老朋友XX,最近在肝鴻蒙折疊屏項目時,意外挖到了官方文檔里的隱藏寶藏!原來
    發表于 06-03 12:04

    單片機有沒有串口包工具推薦的,純小白,想像網絡協議那樣直接curl協議轉化為代碼

    能否做到像網絡協議那樣重放呢?剛剛涉及 esp32 單片機開發,不太懂這方面的知識點
    發表于 06-01 11:04

    TwinCAT3 EtherCAT | 技術集結

    在使用TwinCAT測試EtherCATEOE功能時,我們會發現正常是無法使用Wireshark去進行網絡抓取EtherCAT報文的,今天這篇文章就帶大家來上手EtherCAT
    的頭像 發表于 05-15 18:04 ?6165次閱讀
    TwinCAT3 EtherCAT<b class='flag-5'>抓</b><b class='flag-5'>包</b> | 技術集結

    關于晶振的三

    晶振的高阻在電路起什么作用,為什么有的晶振需要三腳有的不需要,晶振的三是靠什么去控制的?
    發表于 05-15 11:08