Netfilter Kernel module doesn't get the FTP packets' data
Question
I've been trying to write a kernel module to read outgoing FTP packets' username, password and cmd by netfilter.When I test my code ,I find the ftp packets' length is right,but all the data I get is 0x00 when check kernel module's output.
Code
Here is my code. And I wirte a pkt_hex_dump functioin to dump all my packets's bytes above the Internet layer(Include Internet layer) accroding to this Printing sk_buff data:
/* Sample code to install a Netfilter hook function */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
MODULE_LICENSE("MIT");
MODULE_AUTHOR("1933");
MODULE_DESCRIPTION("An net sniff module for demonstration");
MODULE_VERSION("1.0");
/* Used to describe our Netfilter hooks */
static struct nf_hook_ops post_hook;
/* THESE values are used to keep the USERname and PASSword until
* they are queried. Only one USER/PASS pair will be held at one
* time
*/
static char *username = NULL;
static char *password = NULL;
static unsigned short src_port = 0;
static int have_pair = 0; /* Marks if we already have a pair */
/* dump packet's data */
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;
struct iphdr *ip = (struct iphdr *)skb_network_header(skb);
printk("Packet hex dump:\n");
data = (uint8_t *) skb_network_header(skb);
len=ntohs(ip->tot_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");
}
}
/* This is the hook function itself */
unsigned int watch_out(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct iphdr *ip = NULL;
struct tcphdr *tcp = NULL;
unsigned char *data=NULL;
ip = (struct iphdr *)skb_network_header(skb);
if (ip->protocol != IPPROTO_TCP){
return NF_ACCEPT;
}
tcp = (struct tcphdr *)skb_transport_header(skb);
/* Now check to see if it's an FTP packet */
if (tcp->dest!= htons(21)){
return NF_ACCEPT;
}
pkt_hex_dump(skb);
/* Parse the FTP packet for relevant information if we don't already
* have a username and password pair. */
data = (unsigned char *)((unsigned char *)tcp + (tcp->doff * 4));
printk("hex : data[0-3] = 0x%02x%02x%02x%02x\n", data[0], data[1], data[2], data[3]);
printk("char: data[0-3] = %c%c%c%c\n", data[0], data[1], data[2], data[3]);
printk("--------------- findpkt_iwant ------------------\n");
return NF_ACCEPT;
}
/* Initialisation routine */
int init_module(){
/* Fill in our hook structure */
post_hook.hook = watch_out; /* Handler function */
post_hook.hooknum = NF_INET_POST_ROUTING;
post_hook.pf = AF_INET;
post_hook.priority = NF_IP_PRI_FIRST; /* Make our function first */
nf_register_net_hook(&init_net,&post_hook);
// Debug
printk("HELLO: this is hello module speaking\n");
return 0;
}
/* Cleanup routine */
void cleanup_module(){
printk("HELLO : Goodbye!\n");
nf_unregister_net_hook(&init_net,&post_hook);
}
Enviroment
- Linux 5.13.0-37-generic x86_64 GNU/Linux
- Ubuntu 20.04.4 LTS
- Makefile:
obj-m+=NetKernal.o all: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
The Kernel module's Output and the Wireshark' capture:
This is the whole demsg output when I connected to ftp server and inputed my username and password on my Local area network.
And this is the whole wireshark packet after adding filter rule:
tcp.dstport == 21.The Internet layer bytes start with the offset0xein the pcapng.
What weird is these ftp packets.Take the fourth packet for example.
- This is Kernel module's log.(The
/**/which does not exist in the original data is comment I added for better understanding)
[ 4964.195893] Packet hex dump:
[ 4964.195904] 000000 45 10 00 42 D9 86 40 00 40 06 DD BC C0 A8 01 07 /* dump start from Internet layer */
[ 4964.195953] 000010 C0 A8 01 0B 93 E4 00 15 A5 63 17 A8 92 28 25 E3
[ 4964.195982] 000020 80 18 01 F6 83 97 00 00 01 01 08 0A C0 EC BD AB
[ 4964.196011] 000030 00 08 A7 2F 00 00 00 00 00 00 00 00 00 00 00 00 /* ftp content:0x00 */
[ 4964.196038] 000040 00 00
[ 4964.196045] hex : data[0-3] = 0x00000000
[ 4964.196049] char: data[0-3] =
[ 4964.196052] --------------- findpkt_iwant ------------------
- And this is the wireshark captured packet accroding to:
No. Time Source Source Port Destination Destination Port Protocol Length Info
132 14.635575358 192.168.1.7 37860 192.168.1.11 21 FTP 80 Request: USER ftpuser
Frame 132: 80 bytes on wire (640 bits), 80 bytes captured (640 bits) on interface wlp3s0, id 0
Ethernet II, Src: 58:a0:23:05:3b:2e, Dst: 1c:c1:de:65:e5:d4
Internet Protocol Version 4, Src: 192.168.1.7, Dst: 192.168.1.11
Transmission Control Protocol, Src Port: 37860, Dst Port: 21, Seq: 1, Ack: 28, Len: 14
File Transfer Protocol (FTP)
[Current working directory: ]
0000 1c c1 de 65 e5 d4 58 a0 23 05 3b 2e 08 00 45 10 ...e..X.#.;...E. /* # Internet layer starts from the offset 0x0e */
0010 00 42 d9 86 40 00 40 06 dd bc c0 a8 01 07 c0 a8 .B..@.@.........
0020 01 0b 93 e4 00 15 a5 63 17 a8 92 28 25 e3 80 18 .......c...(%...
0030 01 f6 ab 01 00 00 01 01 08 0a c0 ec bd ab 00 08 ................
0040 a7 2f 55 53 45 52 20 66 74 70 75 73 65 72 0d 0a ./USER ftpuser..
As can be seen,the ftp packet's content is 0x00,but whole packet's length is right(above the Internet layer).And all other ftp packets have the same problem. It seems sk_buff don't get the ftp data.
As wireshark can get the correct packets, I don't think it's firewall's queseton.
My quesetons are :
- Why all ftp packets' content is all
0x00while packets' length is correct ? - Where is my ftp data in the struct sk_buff or it's stored in other place?
- Is there anyone who has had the same problem?
Any help will be appreciated.