I have a kernel module that utilizes netfilter hooks. The goal is to forward packets to another destination. As I can see by design packets coming from outside with daddr
set to my servers IP pass through NF_INET_PRE_ROUTING and then suppose to be queued for local application. On NF_INET_PRE_ROUTING I alter specific packets (detect my own protocol) and replace daddr
with remote servers IP and saddr
with my servers IP. I would like to do it from within kernel module itself but cannot find a way to either move existing packet to another routing point (either NF_INET_FORWARD
or NF_INET_LOCAL_OUT
or even NF_INET_POST_ROUTING
) or to create new packet and insert it into TCP/IP stack as if it is sent from server itself. Currently the packet simply goes to the blackhole after first hook. I do not see it going to any other hooks somehow. How could I do that?
My current code (testing code where remote server is same as client):
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/skbuff.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/ip.h> #include <linux/tcp.h> #include <net/ip.h> #include <net/tcp.h> #include <net/route.h> #define DEBUG 1 static struct nf_hook_ops nfho; static __be32 srv_addr = 0x620aa8c0; static __be32 cli_addr = 0x630aa8c0; static __be32 rem_addr = 0x630aa8c0; static unsigned int hook_func(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)){ struct iphdr *ip_header; struct tcphdr *tcp_header; ip_header = (struct iphdr *)skb_network_header(skb); skb_set_transport_header(skb, ip_header->ihl * 4); tcp_header = (struct tcphdr *)skb_transport_header(skb); #if DEBUG > 0 if(tcp_header->dest == ntohs(80) || tcp_header->source == ntohs(80))//(ip_header->saddr == cli_addr || ip_header->saddr == srv_addr || ip_header->saddr == rem_addr) && printk(KERN_INFO "[HTTP] Got a packet to %d.%d.%d.%d:%d from %d.%d.%d.%d:%d in hooknum=%d\n", ip_header->daddr & 0x000000FF, (ip_header->daddr & 0x0000FF00) >> 8, (ip_header->daddr & 0x00FF0000) >> 16, (ip_header->daddr & 0xFF000000) >> 24, ntohs(tcp_header->dest), ip_header->saddr & 0x000000FF, (ip_header->saddr & 0x0000FF00) >> 8, (ip_header->saddr & 0x00FF0000) >> 16, (ip_header->saddr & 0xFF000000) >> 24, ntohs(tcp_header->source), hooknum); #endif if(ip_header->saddr == cli_addr && tcp_header->dest == ntohs(80)){ ip_header->daddr = rem_addr; ip_header->saddr = srv_addr; ip_header->check = 0; ip_send_check(ip_header); tcp_header->check = 0; tcp_header->check = tcp_v4_check(skb->len - 4*ip_header->ihl, ip_header->saddr, ip_header->daddr, csum_partial((char *)tcp_header, skb->len - 4*ip_header->ihl,0)); okfn(skb); return NF_STOP; } if(ip_header->saddr == rem_addr && tcp_header->source == ntohs(80)){ ip_header->daddr = cli_addr; ip_header->saddr = srv_addr; ip_header->check = 0; ip_send_check(ip_header); tcp_header->check = 0; tcp_header->check = tcp_v4_check(skb->len - 4*ip_header->ihl, ip_header->saddr, ip_header->daddr, csum_partial((char *)tcp_header, skb->len - 4*ip_header->ihl,0)); okfn(skb); return NF_STOP; } return NF_ACCEPT; } static int __init init_main(void) { nfho.hook = hook_func; nfho.hooknum = 0; nfho.pf = PF_INET; nfho.priority = NF_IP_PRI_FIRST; nf_register_hook(&nfho); #if DEBUG > 0 printk(KERN_INFO "[HTTP] Successfully inserted protocol module into kernel.\n"); #endif return 0; } static void __exit cleanup_main(void) { nf_unregister_hook(&nfho); #if DEBUG > 0 printk(KERN_INFO "[HTTP] Successfully unloaded protocol module.\n"); #endif } module_init(init_main); module_exit(cleanup_main); MODULE_LICENSE("GPL v3"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC);
iptables
only routes packets, it doesn't alter them. Maybe you meant netfilter_queue userspace library? I could use that, but I first am looking into solution within kernel module. – Alexey Kamenskiy