一個險惡bug的深入分析(3)

發表于:2012-06-06來源:伯樂在線作者:不詳點擊數: 標簽:bug
這段代碼對存儲了所有pcap條目信息的列表ptype_all進行遍歷, 判斷pcap條目的設備結構能否匹配skb所屬設備。 循環中對設備的檢查非常有意思: 1 if (!ptype-d

  這段代碼對存儲了所有pcap條目信息的列表ptype_all進行遍歷, 判斷pcap條目的設備結構能否匹配skb所屬設備。

  循環中對設備的檢查非常有意思:

1
if (!ptype->dev || ptype->dev == skb->dev)

  如果你試圖嗅探來自eth0的數據包,但是由于eth0已經是bond的一部分,因而這個檢查一定會失敗。原因是skb->dev已經被重寫為bond設備的dev結構了。

  這就是為什么tcpdump和其他的測量工具嗅探bond相關的物理設備時看不到發送的數據包了!

  只要簡單地把if語句修改成:

1
if (!ptype->dev || ptype->dev == skb->dev || ptype->dev == orig_dev) {

  因為加入了orig_dev檢查,這下pcap就能夠處理被dev指針被修改過的skb了。

  讓我們來測試一下這個修改。

  有意思的現象,第二輪觀察

  接下來,重新構建并安裝修改后的內核(順便說一下,這里有一份非常有用的文檔),重新ping目標機器并開始嗅探發向物理設備的數據包:

1
2
3
4
5
% sudo tcpdump -i eth0 dst 172.16.209.136 and proto 1
^C
0 packets captured
2 packets received by filter
0 packets dropped by kernel

  什么情況?

  為什么修改以后還是沒有看到發送的數據包?

  libpcap

  讓我們快速檢查一下內核中負責處理AF_PACKET地址家族的libpcap接口。

  AF_PACKET 在內核中是單獨實現的一個地址家族,相關的代碼位于net/packet/af_packet.c。libpcap通過調用socket系統調用建立一個socket,調用的第一個參數被設置為PACKET。libpcap接下來會使用bind系統調用把這個socket綁定想要嗅探的設備上。

  現在有兩種方法可以從內核中拿到數據包:

  • “以前的方法”: 對每個數據包的文件描述符調用recvfrom函數。在老版本的內核上,只有這一個函數可用。

  • “新方法”:調用poll函數會通知libpcap有一組數據包到達,在內核與libpcap的共享內存中等待讀取。比起“老辦法”這種方法效率更高(使用的系統調用更少),在最近大多數的內核包括Debian Lenny都支持這種辦法。

  結果是,盡管Debian Lenny的內核支持“新方法”實現的AF_PACKET,但是相應的libpcap卻不支持。這就意味著tcpdump(依賴于libpcap)只能逐次逐個地從內核中取得數據包。

  更新版本的libpcap默認使用“新方法”從內核讀取數據包。因為Lenny支持這種用法,我試著構建了一個更新版本的libpcap并修改了tcpdump。在修改過的Lenny內核上測試這個修改,我看到當我在bond上的物理設備進行嗅探時,數據包從RX路徑流出。如果我把新的libpcap修改成使用“以前的方法”搜集數據包,沒有數據包從RX路徑流出。

  這意味著在使用“以前的方法”時,要么AF_PACKET有bug,要么多版本的libpcap有bug。

  if語句

  經過數小時痛苦地閱讀代碼,我找了一條if語句可以控制libpcap使用“以前的方法”讀取數據包。

1
2
3
4
5
// From pcap_read_packet in pcap-linux.c:
 
if (handle->md.ifindex != -1 &&
    from.sll_ifindex != handle->md.ifindex)
  return 0;

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97