dropping packets that contain string linux kernel module - linux

I'm trying to drop network packets that contain a string: i.e. if a webpage says "free download" on it i need my kernel module to drop the packets that contain that string. I'm trying to scan through sk_buff in the netfilter hook, but not sure if this is the right place to look for the string. here is the code:
unsigned int hook_func_outgoing(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state) {
int pos;
struct ts_config *conf;
struct ts_state statetext;
const char *pattern = "free download";
conf = textsearch_prepare("kmp", pattern, strlen(pattern),
GFP_KERNEL, TS_AUTOLOAD);
pos = textsearch_find_continuous(conf, &statetext, skb->data, skb->len);
printk(KERN_INFO "pos: %d", pos);
printk(KERN_INFO "data: %s ", skb->data);
if (pos != UINT_MAX){
return NF_DROP;
printk(KERN_INFO "found spam\n");
}
textsearch_destroy(conf);
return NF_ACCEPT;
}

This is not as easy as it sounds.
I'm not fully aware of writing kernel modules for network filtering, so I cannot comment on your code. But I think the major problem here is that you have only access to the "raw" packet.
The internet nowadays is mostly encrypted using TLS (https) and thus you cannot read the content of the transfer on that level (which is good). This means that your module can only work on unencrypted HTTP-Connections. Another issue might be HTTP compression like GZIP which can scramble your data too.

Related

how to implement tcpdump -i interface arp with libpcap

I want to implement command tcpdump -i eth0 arp to observe arp packets on interface eth0 on my ubuntu. I use libpcap, but the return value of function pcap_next_ex is always 0. With tcpdump -i eth0 arp in the same time , it can observe arp packets.
/*
* compile(root): gcc test.c -lpcap
* run : ./a.out
* output : time out
* time out
* time out
* ...
*/
#include <pcap.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ARP_REQUEST 1
#define ARP_REPLY 2
typedef struct arp_hdr_s arp_hdr_t;
struct arp_hdr_s {
u_int16_t htype;
u_int16_t ptype;
u_char hlen;
u_char plen;
u_int16_t oper;
u_char sha[6];
u_char spa[4];
u_char tha[6];
u_char tpa[4];
};
#define MAXBYTES2CAPTURE 2048
int
main(int argc, char **argv)
{
char err_buf[PCAP_ERRBUF_SIZE];
const unsigned char *packet;
int i;
int ret;
arp_hdr_t *arp_header;
bpf_u_int32 net_addr;
bpf_u_int32 mask;
pcap_t *desrc;
struct pcap_pkthdr *pkthdr;
struct bpf_program filter;
net_addr = 0;
mask = 0;
memset(err_buf, 0, PCAP_ERRBUF_SIZE);
desrc = pcap_open_live("eth0", MAXBYTES2CAPTURE, 0, 512, err_buf);
if (desrc == NULL) {
fprintf(stderr, "error: %s\n", err_buf);
exit(-1);
}
ret = pcap_lookupnet("eth0", &net_addr, &mask, err_buf);
if (ret < 0) {
fprintf(stderr, "error: %s\n", err_buf);
exit(-1);
}
ret = pcap_compile(desrc, &filter, "arp", 1, mask);
if (ret < 0) {
fprintf(stderr, "error: %s\n", pcap_geterr(desrc));
exit(-1);
}
ret = pcap_setfilter(desrc, &filter);
if (ret < 0) {
fprintf(stderr, "errnor: %s\n", pcap_geterr(desrc));
exit(-1);
}
while (1) {
ret = pcap_next_ex(desrc, &pkthdr, &packet);
if (ret == -1) {
printf("%s\n", pcap_geterr(desrc));
exit(1);
} else if (ret == -2) {
printf("no more\n");
} else if (ret == 0) { // here
printf("time out\n");
continue;
}
arp_header = (arp_hdr_t *)(packet + 14);
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) {
printf("src IP: ");
for (i = 0; i < 4; i++) {
printf("%d.", arp_header->spa[i]);
}
printf("dst IP: ");
for (i = 0; i < 4; i++) {
printf("%d.", arp_header->tpa[i]);
}
printf("\n");
}
}
return 0;
}
Without getting too deep in your code, I can see a major problem:
In your use of pcap_open_live(), you do not set promiscuous mode: the third parameter should be non-zero. If the ARP request is not targeted to your interface IP, pcap will not see it without promiscuous mode. tcpdump does, unless specifically told not to do so by using the --no-promiscuous-mode, use promisc (and hence will require CAP_NET_ADMIN privilege, which you'll get by sudo, which your program will require too).
Side note:
1/ Leak: you may want to free your filter using pcap_freecode() after your pcap_setfilter().
2/ I assume you've read the official tuto here:
http://www.tcpdump.org/pcap.html
...if that's not the case you'd be well advised to do that first. I quote:
A note about promiscuous vs. non-promiscuous sniffing: The two
techniques are very different in style. In standard, non-promiscuous
sniffing, a host is sniffing only traffic that is directly related to
it. Only traffic to, from, or routed through the host will be picked
up by the sniffer. Promiscuous mode, on the other hand, sniffs all
traffic on the wire. In a non-switched environment, this could be all
network traffic. [... more stuff on promisc vs non-promisc]
EDIT:
Actually, looking deeper to you code compared to my code running for +1 year at production level (both in-house and at the customer) I can see many more things that could be wrong:
You never call pcap_create()
You never call pcap_set_promisc(), we've talked about this already
You never call pcap_activate(), this may be the core issue here
...pcap is very touchy about the sequence order of operations to first get a pcap_t handle, and then operate on it.
At the moment, the best advice I can give you - otherwise this is going to a live debugging session between you and me, are:
1/ read and play/tweak with the code from the official tutorial:
http://www.tcpdump.org/pcap.html
This is mandatory.
2/ FWIW, my - definitely working - sequence of operations is this:
pcap_lookupnet()
pcap_create()
pcap_set_promisc()
pcap_set_snaplen(), you may or may not need this
pcap_set_buffer_size(), you may or may not need this
pcap_activate() with a note: Very important: first activate, then set non-blocking from PCAP_SETNONBLOCK(3PCAP): When first activated with pcap_activate() or opened with pcap_open_live() , a capture handle is not in non-blocking mode''; a call to pcap_set-nonblock() is required in order to put it intonon-blocking'' mode.
...and then, because I do not use stinking blocking/blocking with timeout, busy looping:
pcap_setnonblock()
pcap_get_selectable_fd()
...then and only then:
- pcap_compile()
- followed by a pcap_setfilter()
- and then as I mentioned a pcap_freecode()
- and then a select() or family on the file'des' I get from pcap_get_selectable_fd(), to pcap_dispatch(), but this is another topic.
pcap is an old API starting back in the 80's, and its really very very touchy. But don't get discouraged! It's great - once you get it right.
It would probably work better if you did
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype) == 0x0800) {
rather than
if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) {
The latter evaluates arp_header->type == 0x0800, which, when running on a little-endian machine (such as a PC), will almost always evaluate to "false", because the value will look like 0x0008, not 0x0800, in an ARP packet - ARP types are big-endian, so they'll look byte-swapped on a little-endian machine). That means it'll evaluate to 0, and byte-swapping 0 gives you zero, so that if condition will evaluate to "false", and the printing code won't be called.
You'll still get lots of timeouts if you fix that, unless there's a flood of ARP packets, but at least you'll get the occasional ARP packet printed out. (I would advise printing nothing on a timeout; pcap-based programs doing live capturing should expect that timeouts should happen, and should not report them as unusual occurrences.)

redirect ethernet packets through sk_buff to localhost

I am writing a Linux kernel module which redirects a packet to the localhost webserver ,which was originally forwarded through this machine using bridge . It also redirects to reply to the client . The client is oblivious of the redirection . So there are 2 parts
1. all forwarded packets through bridge to some webserver outside are redirected to local webserver .
The output of the localhost webserver is channelled to the original client
I am able to do the second part through nf_hook NF_INET_LOCAL_OUT
unsigned int snoop_hook_reply( unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int(*okfn)( struct sk_buff * ) )
{
int offset, len;
struct ethhdr *ethh;
struct iphdr *iph;
struct tcphdr *tcph;
bool flag = false;
struct net_device *eth1_dev , *lo_dev;
if (!skb) return NF_ACCEPT;
iph = ip_hdr(skb);
if (!iph) return NF_ACCEPT;
skb_set_transport_header(skb, iph->ihl * 4);
tcph = tcp_hdr(skb);
/* skip lo packets */
if (iph->saddr == iph->daddr) return NF_ACCEPT;
if (tcph->dest == htons(80))
flag=true;
if(flag != true)
return NF_ACCEPT;
// correct the IP checksum
iph->check = 0;
ip_send_check (iph);
//correct the TCP checksum
offset = skb_transport_offset(skb);
len = skb->len - offset;
tcph->check = 0;
if(skb->len > 60){
tcph->check = csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, csum_partial((unsigned char *)tcph,len,0));
}
else{
tcph->check = ~csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, 0);
}
//send to dev
eth1_dev = dev_get_by_name(&init_net,"eth1");
lo_dev = dev_get_by_name(&init_net,"lo");
skb->dev = eth1_dev;
ethh = (struct ethhdr *) skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
skb->protocol = ethh->h_proto = htons(ETH_P_IP);
memcpy (ethh->h_source,eth1_dev->dev_addr , ETH_ALEN);
memcpy (ethh->h_dest, d_mac, ETH_ALEN); // d_mac is mac of the gateway
dev_queue_xmit(skb);
return NF_STOLEN;
}
the above code works perfectly for me . One issue is that later on I will mangle the packet so need to create a new sk_buff, probably .
I am not able to do the 1st part through NF_INET_PRE_ROUTING, I am not able to push the packet/sk_buff to the webserver process through the TCP/IP stack. I tried using dev_queue_xmit() function with skb->dev as both eth1 and lo . I am seeing the packets hitting on the lo or eth1 through tcpdump . But the packets are not reaching the localhost webserver. Can anyone help me regarding this or point to some similar answered question . I believe instead of dev_queue_xmit() I need to call some receiving function . Also when packets arrive in NF_INET_PREROUTING, I the ethernet headers are already there so I am not forming it .
I have already accomplished the above tasks in variety of ways , first using raw sockets , then using nf_queue , now I want to see the performance through this method.
Thanks
If you want to receive the packet locally, you cannot call dev_queue_xmit() on eth1 as it will be sent out. You probably need to call netif_rx() after pointing the skb->dev to eth1/lo.
One more point is if the dest-ip is not your local host ip, then you need to avoid routing again otherwise, there will be no use of your interception.
To achieve this, either you need to modify packet's dest ip to eth1/lo IP or
fool the IP layer by using skb_dst_set() to set "rth->dst.input= ip_local_deliver" for packet to be accepted as local packet.

Kernel driver, ioremap necessary in MMU-less system?

So, I am a total newbie when it comes to kernel drivers and have a question regarding ioremap function.
I am writing a driver for accessing some registers defined in a custom VHDL-module on a SoC with a ARM Cortex-M3 and FPGA fabric.
Looking at examples I figured I should use ioremap, but since the Cortex-M3 does not have a MMU, I don't really see the point, as per the following example:
/* Physical addresses */
static u32* rcu_trig_recv_physaddr = ((u32 *) 0x50040000);
static int rcu_trig_recv_size = 0x10; // size of 16 for testing
/* Virtual addresses */
static u32* rcu_trig_recv_virtbase = NULL;
/*removed code not relevant for the question*/
static int __init rcumodule_init(void)
{
int iResult = 0; // holding result of operations
u32 buffer;
// Register the driver
iResult = register_chrdev(rcuc_majorID, "rcuc", &rcuc_fops);
if (iResult < 0) {
printk(KERN_INFO "module init: can't register driver\n");
}
else{
printk(KERN_INFO "module init: success!\n");
}
// Map physical address to virtual address
if(rcu_trig_recv_size){
rcu_trig_recv_virtbase = (u32*) ioremap_nocache( (u32 *)rcu_trig_recv_physaddr, rcu_trig_recv_size );
printk("Remapped TRGRECV from 0x%p to 0x%p\n", rcu_trig_recv_physaddr, rcu_trig_recv_virtbase);
}
// try to read some stuff, expecting 0x17240f09
buffer = readl(rcu_trig_recv_virtbase);
printk("read %lx, at 0x%p\n", buffer, rcu_trig_recv_virtbase);
return iResult;
}
This then return, when I insmod the driver:
# insmod trigger.ko
module init: success!
Remapped TRGRECV from 0x50040000 to 0x50040000
read 17240f09, at 0x50040000
According to this, I would just be better off reading the physical address instead. Or is that a bad idea and I should be messing with my registers in a better way?
It's possible that you can get away with this if you know your code will never need to be used on another device, but you're much safer sticking with using ioremap(). Basing your code around obtaining and using the pointers provided by memory-mapped IO will make your code more portable and maintainable than utilizing hard-coded physical addresses.
Even if you don't plan on taking this code to a different device, using physical addresses could potentially break your code when simply upgrading to a newer chip in the same line.

Kernel module get data from user space

What is the proper way of sending some data to a loaded and running kernel module, without using netlink and without using features that may not be in place (e.g. debugfs)?
I'd like to see a clean and safe way of doing this which should work on most kernels (or preferably all modern ones), or at best an approximation of that.
The user who wants to send data to the module is the root user, the amount of data is probably under 64 kiB and consists of a series of strings.
I've already looked into trying to read files from the module, which is not only highly frowned upon for various reasons but also hard to do.
I've looked at netlink, which socket() tells me on my kernel is not supported.
I've looked at debugfs, which is not supported either on my kernel.
Obviously I could use a different kernel but as I mentioned I'd like a proper way of doing this. If someone could show me a simple example of a module that will just do a printk() of a string sent from user space that would be great.
... a simple example of a module that will just do a printk() of a string sent from user space, printkm.c:
#include <linux/module.h>
#include <linux/proc_fs.h>
MODULE_DESCRIPTION("printk example module");
MODULE_AUTHOR("Dietmar.Schindler#manroland-web.com");
MODULE_LICENSE("GPL");
static
ssize_t write(struct file *file, const char *buf, size_t count, loff_t *pos)
{
printk("%.*s", count, buf);
return count;
}
static struct file_operations file_ops;
int init_module(void)
{
printk("init printk example module\n");
struct proc_dir_entry *entry = proc_create("printk", 0, NULL, &file_ops);
if (!entry) return -ENOENT;
file_ops.owner = THIS_MODULE,
file_ops.write = write;
return 0;
}
void cleanup_module(void)
{
remove_proc_entry("printk", NULL);
printk("exit printk example module\n");
}
Example use:
root#kw:~# insmod printkm.ko
root#kw:~# echo a string >/proc/printk
root#kw:~# dmesg|tail -1
[193634.164459] a string
I think you can use a char device. Take a look at Linux Device Driver 3th Chapter 3. With the function *copy_to_user* and *copy_from_user* you can copy data safely to and from userspace.

Linux Kernel MOdule

Is possible to use netfilter to hook all ethernet packets?
I can just get packet from ipv4 or ipv6.
EDit:
The above code is my kernel module. I want to get all packets that arrives to one ethernet interface re-direct to another interface.This module just print the name of the device where the packet arrived (just for testing). WIth this hook i just get packets witch type is ipv4, but i want to get all types.
I searched on web but i didn't find how to configure my hook to get all the packet's that bypass the ethernet interface.
Best Regards
static struct nf_hook_ops nfho;
int hook_func(struct sk_buff * skb)
{
struct ethhdr *eth;
printk("Device: %s\n,skb->dev->name);
}
int init_module() {
printk("Hook Module online!\n");
nfho.hook =(nf_hookfn *)hook_func;
nfho.hooknum = NF_IP_PRE_ROUTING;
nfho.pf = PF_INET;
nfho.priority =NF_IP_PRI_FIRST;
nf_register_hook(&nfho);
return 0; }
/* Cleanup routine */ void cleanup_module() {
printk("Over and Out!\n");
nf_unregister_hook(&nfho); }
sure,
if I am getting it all right, you just willing to accept all Ether validated packets and pass them to another interface.
In the case, just include the kernel file:
#include <linux/if_ether.h>
you get the header by:
struct ethhdr *hdr = eth_hdr(skb);
// *skb is a ptr !!
and make sure that the ether is valid by checking some values
out of the ethhdr struct.
then just sent then by local host or modify the addresses if you want the other interface to accept it like it was his packets.

Resources