Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1090899
  • 博文数量: 252
  • 博客积分: 4561
  • 博客等级: 上校
  • 技术积分: 2833
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-15 08:23
文章分类

全部博文(252)

文章存档

2015年(2)

2014年(1)

2013年(1)

2012年(16)

2011年(42)

2010年(67)

2009年(87)

2008年(36)

分类: LINUX

2010-06-23 14:52:09

#include <asm/atomic.h>
#include <asm/byteorder.h>
#include <asm/checksum.h>
#include <asm/unaligned.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/types.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/sysctl.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/route.h>
#include <linux/if_arp.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/mutex.h>
#include <linux/compiler.h>
#include <linux/icmp.h>
#include <linux/jhash.h>
#include <linux/list.h>
#include <linux/spinlock_types.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/route.h>
#include <net/inet_connection_sock.h>
#include <net/request_sock.h>
#include <net/icmp.h>
#include <net/ip.h>

#include "main.h"

static DEFINE_SPINLOCK(hashtable_lock);
static struct list_head hashtable[HASHSIZE] __read_mostly;
static struct kmem_cache *cachep __read_mostly;

static u32 rand;

static u32 conn_hash(__be16 sport, __be16 dport, u8 which)
{
    if (which == CONNIN)
        return jhash_1word((__force u32)sport, rand) & HASHMASK;

    return jhash_1word((__force u32)dport, rand) & HASHMASK;
}

static struct conn_struct * __conn_get(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, u8 which)
{
    struct conn_struct *cp = NULL;
    u32 hash;

    hash = conn_hash(sport, dport, which);

    spin_lock_bh(&hashtable_lock);
    list_for_each_entry(cp, &hashtable[hash], c_list) {
        if (which == CONNIN) {
            if (cp->caddr == saddr && cp->vaddr == daddr && cp->cport == sport && cp->vport == dport) {
                cp->which = CONNIN;
                goto out;
            }
        } else {
            if (cp->daddr == saddr && cp->vaddr == daddr && cp->dport == sport && cp->cport == dport) {
                cp->which = CONNOUT;
                goto out;
            }
        }
    }
    cp = NULL;    /* note this, current cp pointer to the last member */
out:
    spin_unlock_bh(&hashtable_lock);
    return cp;
}

static struct conn_struct * conn_schedule(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport)
{
    struct conn_struct *cp;
    u32 hash;

    cp = kmem_cache_zalloc(cachep, GFP_ATOMIC);
    if (cp == NULL)
        goto out;

    INIT_LIST_HEAD(&cp->c_list);
    cp->caddr = saddr;
    cp->vaddr = daddr;
    cp->daddr = 0x130AA8C0;        
    cp->cport = sport;
    cp->vport = dport;
    cp->dport = htons(7777);
    init_timer(&cp->timer);
    cp->which = CONNIN;
    
    hash = conn_hash(sport, dport, CONNIN);    
    spin_lock_bh(&hashtable_lock);
    list_add(&cp->c_list, &hashtable[hash]);
    spin_unlock_bh(&hashtable_lock);
out:
    return cp;
}

static struct conn_struct * conn_get(struct sk_buff *skb)
{
    struct conn_struct *cp = NULL;
    struct iphdr *iph;
    struct tcphdr *tcph;

    iph = ip_hdr(skb);
    if (iph == NULL) {
        err("ip_hdr");
        goto out;
    }
    
    tcph = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));
    if (tcph == NULL) {
        err("tcph is NULL.\n");
        goto out;
    }

    cp = __conn_get(iph->saddr, iph->daddr, tcph->source, tcph->dest, CONNIN);
    if (cp == NULL)
        cp = __conn_get(iph->saddr, iph->daddr, tcph->source, tcph->dest, CONNOUT);

    if (cp == NULL && tcph->syn == 1)     /* new syn packet */
        cp = conn_schedule(iph->saddr, iph->daddr, tcph->source, tcph->dest);

    return cp;
out:
    return NULL;
}

static struct rtable * get_output_route(__be32 rdaddr, u8 rtos)
{
    struct rtable *rt = NULL;
    
    struct flowi fl = { .oif = 0, .nl_u = { .ip4_u = { .daddr = rdaddr, .saddr = 0, .tos = rtos, } }, };

    if (ip_route_output_key(&init_net, &rt, &fl)) {
        err("ip_route_output_key");
        goto out;
    }
out:    
    return rt;
}

static void packet_dnat(struct conn_struct *cp, struct sk_buff *skb)
{
    struct rtable *rt;
    struct iphdr *iph = ip_hdr(skb);
    struct tcphdr *tcph = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));

    rt = get_output_route(cp->daddr, RT_TOS(iph->tos));
    if (rt == NULL) {
        printk(KERN_INFO "rt is NULL.\n");
        goto out;
    }

    if (!skb_make_writable(skb, iph->ihl * 4 + tcph->doff * 4)) {
        err("skb_make_writable");
        goto out;
    }
    if (skb_cow(skb, rt->u.dst.dev->hard_header_len)) {
        err("skb_cow");
        goto out;
    }

    skb_dst_drop(skb);
    skb_dst_set(skb, &rt->u.dst);

    iph->saddr = cp->vaddr;
    iph->daddr = cp->daddr;
    tcph->dest = cp->dport;

    tcph->check = 0;
    skb->csum = skb_checksum(skb, ip_hdrlen(skb), skb->len - ip_hdrlen(skb), 0);
    tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - ip_hdrlen(skb), iph->protocol, skb->csum);
    ip_send_check(ip_hdr(skb));

    dst_output(skb);
out:
    return;
}

static void packet_snat(struct conn_struct *cp, struct sk_buff *skb)
{
    struct rtable *rt;
    struct iphdr *iph = ip_hdr(skb);
    struct tcphdr *tcph = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));

    rt = get_output_route(cp->caddr, RT_TOS(iph->tos));
    if (rt == NULL) {
        printk(KERN_INFO "rt is NULL.\n");
        goto out;
    }

    if (!skb_make_writable(skb, iph->ihl * 4 + tcph->doff * 4)) {
        err("skb_make_writable");
        goto out;
    }
    if (skb_cow(skb, rt->u.dst.dev->hard_header_len)) {
        err("skb_cow");
        goto out;
    }

    skb_dst_drop(skb);
    skb_dst_set(skb, &rt->u.dst);

    iph->saddr = cp->vaddr;
    iph->daddr = cp->caddr;
    tcph->source = cp->vport;
    tcph->dest = cp->cport;
    
    tcph->check = 0;
    skb->csum = skb_checksum(skb, ip_hdrlen(skb), skb->len - ip_hdrlen(skb), 0);
    tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - ip_hdrlen(skb), iph->protocol, skb->csum);
    ip_send_check(ip_hdr(skb));

    dst_output(skb);
out:
    return;
}

/* 192.168.10.250 network byteorder 0xFA0AA8C0 */
/* 192.168.10.188 network byteorder 0xBC0AA8C0 */
/* 192.168.10.19 network byteorder 0x130AA8C0 */

static unsigned int sg_vs_in(unsigned int hooknum,
            struct sk_buff *skb,
            const struct net_device *in,
            const struct net_device *out,
            int (*okfn)(struct sk_buff *))
{
    struct conn_struct *cp;
    struct tcphdr *tcph = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));

    if (tcph->dest != htons(8888) && tcph->source != htons(7777))
        goto accept;

    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
        err("skb_share_check");
        goto out;
    }

    if (!pskb_may_pull(skb, sizeof(struct iphdr) + sizeof(struct tcphdr))) {
        err("pskb_may_pull");
        goto out;
    }

    cp = conn_get(skb);    
    if (cp == NULL)
        goto accept;

    if (cp->which == CONNIN)
        packet_dnat(cp, skb);
    else
        packet_snat(cp, skb);
    
    return NF_STOLEN;
accept:
    return NF_ACCEPT;
out:
    return NF_DROP;
}

static struct nf_hook_ops sg_nf_ops = {
    .hook        = sg_vs_in,    
    .owner        = THIS_MODULE,
    .pf        = PF_INET,
    .hooknum = NF_INET_LOCAL_IN,        
};

static void conn_hashtable_init(void)
{
    int i;

    for (i = 0; i < HASHSIZE; i++)
        INIT_LIST_HEAD(&hashtable[i]);
}

static int main_init(void)
{
    int rc;

    get_random_bytes(&rand, sizeof(rand));
    conn_hashtable_init();

    cachep = kmem_cache_create("ipnat", sizeof(struct conn_struct), 0, SLAB_HWCACHE_ALIGN, NULL);
    if (cachep == NULL) {
        err("kmem_cache_create");
        goto out;
    }

    rc = nf_register_hook(&sg_nf_ops);
    if (rc < 0) {
        err("nf_register_hook");
        goto out;
    }
    
    printk(KERN_INFO "NAT module register.\n");
    return 0;
out:
    return -ENOMEM;
}

static void conn_cleanup(void)
{
    struct conn_struct *cp, *nxt;
    int i;

    spin_lock_bh(&hashtable_lock);
        
    for (i = 0; i < HASHSIZE; i++) {
        list_for_each_entry_safe(cp, nxt, &hashtable[i], c_list) {
            list_del(&cp->c_list);
            del_timer_sync(&cp->timer);
            kmem_cache_free(cachep, cp);
        }
    }

    spin_unlock_bh(&hashtable_lock);
}

static void main_exit(void)
{
    nf_unregister_hook(&sg_nf_ops);
    conn_cleanup();        
    kmem_cache_destroy(cachep);

    printk(KERN_INFO "NAT module unregister.\n");
}

module_init(main_init);
module_exit(main_exit);
MODULE_LICENSE("GPL");


阅读(905) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~