如何将在内核模块中捕获的 hex_dump 个数据包转换为 pcap 文件?

How to convert hex_dump of packets, which were captured in kernel module, to pcap file?

我正在 Linux (Xubuntu x64) 上编写内核模块。内核版本为 5.4.0-52-generic。我的内核模块正在从接口捕获流量并以十六进制打印:

Nov 10 14:04:34 ubuntu kernel: [404009.566887] Packet hex dump:
Nov 10 14:04:34 ubuntu kernel: [404009.566889] 000000   00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 
Nov 10 14:04:34 ubuntu kernel: [404009.566899] 000010   00 54 49 4C 40 00 40 01 A7 EF C0 A8 64 0E C0 A8 
Nov 10 14:04:34 ubuntu kernel: [404009.566907] 000020   64 0E 08 00 9E FE 00 03 00 08 72 0E AB 5F 00 00 
Nov 10 14:04:34 ubuntu kernel: [404009.566914] 000030   00 00 7B B5 01 00 00 00 00 00 10 11 12 13 14 15 
Nov 10 14:04:34 ubuntu kernel: [404009.566922] 000040   16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 
Nov 10 14:04:34 ubuntu kernel: [404009.566929] 000050   26 27 28 29

这个输出是我在 root 下使用这个命令得到的:tail -f /var/log/kern.log

整个问题是我需要将此输出保存为 pcap 文件。我知道有 text2pcap 但它的库 (libpcap) 只是用户模式所以我不能在内核模块中使用它(或者也许不能?如果我错了请纠正我)。

是否可以在内核模块中使用text2pcap?否则,如何在内核模块中将输出保存为 pcap 文件?

源代码:

#include <linux/module.h> // included for all kernel modules
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
#include <linux/skbuff.h> // included for struct sk_buff
#include <linux/if_packet.h> // include for packet info
#include <linux/ip.h> // include for ip_hdr 
#include <linux/netdevice.h> // include for dev_add/remove_pack
#include <linux/if_ether.h> // include for ETH_P_ALL
#include <linux/unistd.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tester");
MODULE_DESCRIPTION("Sample linux kernel module program to capture all network packets");

struct packet_type ji_proto;
void pkt_hex_dump(struct sk_buff *skb)
{
    size_t len;
    int rowsize = 16;
    int i, l, linelen, remaining;
    int li = 0;
    uint8_t *data, ch; 

    printk("Packet hex dump:\n");
    data = (uint8_t *) skb_mac_header(skb);

    if (skb_is_nonlinear(skb)) {
        len = skb->data_len;
    } else {
        len = skb->len;
    }

    remaining = len;
    for (i = 0; i < len; i += rowsize) {
        printk("%06d\t", li);

        linelen = min(remaining, rowsize);
        remaining -= rowsize;

        for (l = 0; l < linelen; l++) {
            ch = data[l];
            printk(KERN_CONT "%02X ", (uint32_t) ch);
        }

        data += linelen;
        li += 10; 

        printk(KERN_CONT "\n");
    }
}

int ji_packet_rcv (struct sk_buff *skb, struct net_device *dev,struct packet_type *pt, struct net_device *orig_dev)
{
 printk(KERN_INFO "New packet captured.\n");

/* linux/if_packet.h : Packet types */
 // #define PACKET_HOST 0 /* To us */
 // #define PACKET_BROADCAST 1 /* To all */
 // #define PACKET_MULTICAST 2 /* To group */
 // #define PACKET_OTHERHOST 3 /* To someone else */
 // #define PACKET_OUTGOING 4 /* Outgoing of any type */
 // #define PACKET_LOOPBACK 5 /* MC/BRD frame looped back */
 // #define PACKET_USER 6 /* To user space */
 // #define PACKET_KERNEL 7 /* To kernel space */
 /* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */
 // #define PACKET_FASTROUTE 6 /* Fastrouted frame */

switch (skb->pkt_type)
 {
 case PACKET_HOST:
 printk(KERN_INFO "PACKET to us − ");
 break;
 case PACKET_BROADCAST:
 printk(KERN_INFO "PACKET to all − ");
 break;
 case PACKET_MULTICAST:
 printk(KERN_INFO "PACKET to group − ");
 break;
 case PACKET_OTHERHOST:
 printk(KERN_INFO "PACKET to someone else − ");
 break;
 case PACKET_OUTGOING:
 printk(KERN_INFO "PACKET outgoing − ");
 break;
 case PACKET_LOOPBACK:
 printk(KERN_INFO "PACKET LOOPBACK − ");
 break;
 case PACKET_FASTROUTE:
 printk(KERN_INFO "PACKET FASTROUTE − ");
 break;
 }
//printk(KERN_CONT "Dev: %s ; 0x%.4X ; 0x%.4X \n", skb->dev->name, ntohs(skb->protocol), ip_hdr(skb)->protocol);
struct ethhdr *ether = eth_hdr(skb);

//printk("Source: %x:%x:%x:%x:%x:%x\n", ether->h_source[0], ether->h_source[1], ether->h_source[2], ether->h_source[3], ether->h_source[4], ether->h_source[5]);

//printk("Destination: %x:%x:%x:%x:%x:%x\n", ether->h_dest[0], ether->h_dest[1], ether->h_dest[2], ether->h_dest[3], ether->h_dest[4], ether->h_dest[5]);

//printk("Protocol: %d\n", ether->h_proto);
pkt_hex_dump(skb);
kfree_skb (skb);
return 0;
}

static int __init ji_init(void)
{
 /* See the <linux/if_ether.h>
 When protocol is set to htons(ETH_P_ALL), then all protocols are received.
 All incoming packets of that protocol type will be passed to the packet
 socket before they are passed to the protocols implemented in the kernel. */
 /* Few examples */
 //ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
 //ETH_P_IP 0x0800 /* Internet Protocol packet */
 //ETH_P_ARP 0x0806 /* Address Resolution packet */
 //ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */
 //ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */
 //ETH_P_802_2 0x0004 /* 802.2 frames */
 //ETH_P_SNAP 0x0005 /* Internal only */

ji_proto.type = htons(ETH_P_IP);

/* NULL is a wildcard */
 //ji_proto.dev = NULL;
 ji_proto.dev = dev_get_by_name (&init_net, "enp0s3");

ji_proto.func = ji_packet_rcv;

/* Packet sockets are used to receive or send raw packets at the device
 driver (OSI Layer 2) level. They allow the user to implement
 protocol modules in user space on top of the physical layer. */

/* Add a protocol handler to the networking stack.
 The passed packet_type is linked into kernel lists and may not be freed until 
  it has been removed from the kernel lists. */
 dev_add_pack (&ji_proto);

printk(KERN_INFO "Module insertion completed successfully!\n");
 return 0; // Non-zero return means that the module couldn't be loaded.
}

static void __exit ji_cleanup(void)
{
 dev_remove_pack(&ji_proto);
 printk(KERN_INFO "Cleaning up module....\n");
}

module_init(ji_init);
module_exit(ji_cleanup);

Is it possible to use text2pcap in kernel module?

不是没有把它 它用来编写 pcap 文件的代码(它不是来自 libpcap,它来自 Wireshark 的一部分的小型库,也被dumpcap 将 pcap 和 pcapng 文件写入内核。

How can I save an output as pcap file while being in kernel module?

您可以在内核模块中编写自己的代码来打开文件并写入; "Writing to a file from the Kernel" 谈到那个。

它还说

A "preferred" technique would be to pass the parameters in via IOCTLs and implement a read() function in your module. Then reading the dump from the module and writing into the file from userspace.

所以你可能要考虑一下;用户空间代码可以只使用 libpcap 来写入文件。

问题已解决,使用 call_usermodehelper() 调用带参数的用户模式 ​​text2pcap,就像使用终端调用 text2pcap 一样。