Thursday, May 5, 2022

[網路科普] Linux cooked-mode capture (SLL)

之前用GoPacket去抓device name 為 "any" device interface (Pseudo-device that captures on all interfaces),"any"可以透過 tcpdump -D 查看到:

"any"這個device interface可以抓到經過所有interfaces的封包,部分程式碼如下:

    // Open device
    handle, err = pcap.OpenLive("andy", snapshot_len, promiscuous, timeout)
    if err != nil {
        log.Printf("[%v] Error : %s", device, err)
    }


但,突然發現抓到的封包,link-type 都是 LINUX_SLL,這就讓我起了滿頭黑人問號...。同樣使用tcpdump去抓"any" device interface,也會有相同的結果:

$ sudo tcpdump -i any -w test.pcap tcp
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes


經過survey,有兩種可能的情形:

1. 數據包從“any”設備進行捕獲(即tcpdump -i any,Pseudo-device),因為不是所有接口都具有相同的鏈路層類型。

2. 數據包從鏈路層頭部不可用或不能使用的設備上進行捕獲(譬如Linux PPP),因為Linux PPP代碼不能可靠地向libpcap提供PPP報頭。

Wiki上對於SLL的解釋也非常清楚:

This is the pseudo-protocol used by libpcap on Linux to capture from the "any" device and to capture on some devices where the native link layer header isn't available or can't be used. 
...
...
When capturing from the "any" device, or from one of those other devices, in Linux, the libpcap doesn't supply the link-layer header for the real "hardware protocol" like Ethernet, but instead supplies a fake link-layer header for this pseudo-protocol.


在這篇文章中有提到如何做轉換: 
Linux cooked-mode capture 格式转换

若需要將linux cooked capture格式的包轉換為Ethernet格式,有那麼幾種方法:

1. 寫代碼讀出每一個包後再改寫到新文件(使用libpcap或者基於pcap頭部結構體偏移);

2. tcpdump 3.0+ 版本下,可以用tcprewrite直接改寫,這應該是最快捷的方法;

$ tcpdump -r linux_sll.pcap從文件 linux_sll.pcap //讀取,鏈接類型 LINUX_SLL(Linux 熟)
$ tcprewrite --dlt=enet --infile=linux_sll.pcap --outfile=enet.pcap
$ tcpdump - r enet.pcap //從文件 enet.pcap 中讀取,鏈接類型 EN10MB(以太網)

也可以用 named pipe 來避免寫入到Disk

$ sudo rm /tmp/linux_sll.pcap
$ sudo rm /tmp/enet.pcap
$ mkfifo /tmp/linux_sll.pcap
$ mkfifo /tmp/enet.pcap
$ sudo tcpdump -s 0 -n -w - -U -i any > /tmp/linux_sll.pcap
$ tcprewrite --dlt=enet --infile=/tmp/linux_sll.pcap --outfile=/tmp/enet.pcap
$ tcpdump -r /tmp/enet.pcap









No comments: