Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1912502
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: C/C++

2014-12-05 14:46:18

url_redirect.c:

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include "url_redirect.h"

 

struct sk_buff* tcp_newpack( u32 saddr, u32 daddr, 

        u16 sport, u16 dport,

        u32 seq, u32 ack_seq,

        u8 *msg, int len );

        

int _tcp_send_pack( struct sk_buff *skb, struct iphdr *iph,

        struct tcphdr *th, gbuffer_t *p );

        

 

#ifndef MAX_URL_LEN

#define MAX_URL_LEN  253

#endif

 

#define DEFAULT_REDIRECT_URL ""

 

int http_build_redirect_url( const char *url, gbuffer_t *p );

 

int http_send_redirect(struct sk_buff *skb, struct iphdr *iph,

        struct tcphdr *th, const char *url);

 

int _http_send_redirect(struct sk_buff *skb, struct iphdr *iph,

        struct tcphdr *th );

        

int setup_redirect_url( const char *url );

void clear_redirect_url(void);

 

int redirect_url_init(void);

void redirect_url_fini(void);

 

char *get_redirect_url(void);

 

/*****************************************************************************/

static char fqdn_redirect_url[MAX_URL_LEN + 1] = {0};

static gbuffer_t *url_redirect_data = NULL;

static gbuffer_t *url_redirect_default = NULL;

static spinlock_t url_redirect_lock;

/*

 * 初始化默认重定向DEFAULT_REDIRECT_URL HTML数据

 */ 

int redirect_url_init(void)

{

    spin_lock_init( &url_redirect_lock );

    

    url_redirect_default = __gbuffer_alloc();

    if ( NULL == url_redirect_default ) {

        printk("__gbuffer_alloc for default redirect URL failed./n" );

        return -1;

    }

 

    if ( http_build_redirect_url( DEFAULT_REDIRECT_URL,

            url_redirect_default ) ){

        _gbuffer_free( url_redirect_default );

        url_redirect_default = NULL;

        printk("http_build_redirect_url %s failed.\n",

            DEFAULT_REDIRECT_URL );

        return -1;

    }

 

    return 0;

}

/*

 * 释放重定向数据

 */

void redirect_url_fini(void)

{

    gbuffer_t *p = NULL;

    _gbuffer_free( url_redirect_default );

    url_redirect_default = NULL; 

 

    p = url_redirect_data;

    rcu_assign_pointer( url_redirect_data, NULL );

    _gbuffer_free( p );

}

 

/*

 * 设置重定向URL, 构建重定向数据

 */

int setup_redirect_url( const char *url )

{

    int len;

    gbuffer_t *p = NULL, *ptr;

    

    if ( NULL == url )

        return -1;

 

    len = strlen(url);

    if ( len > MAX_URL_LEN )

        return -1;

 

    memset( fqdn_redirect_url, 0x0, MAX_URL_LEN );

    memcpy( fqdn_redirect_url, url, len );

 

    p = __gbuffer_alloc();

    if ( NULL == p ) {

        printk("__gbuffer_alloc failed.\n" );

        return -1;

    }

    if ( http_build_redirect_url( fqdn_redirect_url,

            p ) ) {

        printk("http_build_redirect_url %s failed.\n",

            fqdn_redirect_url );

        _gbuffer_free( p );

        return -1;

    }

 

    printk("Setup Redirect URL http://%s\n", fqdn_redirect_url );

        

    spin_lock_bh( &url_redirect_lock );

    ptr = url_redirect_data;

    rcu_assign_pointer( url_redirect_data, p );

    spin_unlock_bh( &url_redirect_lock );

    

    synchronize_rcu();

    _gbuffer_free( ptr );

    

    return 0;

}

/*

 * 清除重定向数据

 */

void clear_redirect_url(void)

{

    gbuffer_t *ptr;

    

    memset( fqdn_redirect_url, 0x0, MAX_URL_LEN );

 

    spin_lock_bh( &url_redirect_lock );

    ptr = url_redirect_data;

    rcu_assign_pointer( url_redirect_data, NULL );

    spin_unlock_bh( &url_redirect_lock );

    

    synchronize_rcu();

    _gbuffer_free( ptr );

}

 

/*

 * 获取重定向数据缓冲

 */ 

char *get_redirect_url(void)

{

    if ( 0 == *fqdn_redirect_url )

        return DEFAULT_REDIRECT_URL;

        

    return fqdn_redirect_url;

}

/*

 * 重定向HTML的几种格式

 */

const char *http_redirect_header = 

    "HTTP/1.1 301 Moved Permanently\r\n"

    "Location: http://%s\r\n"

    "Content-Type: text/html; charset=iso-8859-1\r\n"

    "Content-length: 0\r\n"

    "Cache-control: no-cache\r\n"

    "\r\n";

 

/*

 * 构建一个重定向HTML缓冲

 */

int http_build_redirect_url( const char *url, gbuffer_t *p )

{

    char *header = NULL;

    char *body  = NULL;

    char *buf   = NULL;

    int header_len;

    int rc = -1;    

    

    if ( NULL == p )

        goto _out;

        

    header = kzalloc( PATH_MAX, GFP_KERNEL );

    if ( NULL == header ) {

        goto _out;

    }

                        

    header_len = snprintf( header, PATH_MAX,

                    http_redirect_header,   

                    url 

                    );  

                        

    buf = kzalloc( header_len , GFP_KERNEL );

    if ( NULL == buf ){

        goto _out;

    }

    

    p->buf = buf;

    p->len = header_len ;

    

    memcpy( buf, header, header_len );

    

#if 0

    {

        int i = 0;

        for( ; i < p->len; i ++ ){

            printk( "%c", buf[i] );

        }

        printk( "\n" );

    }

#endif  

    rc = 0;

_out:

    if ( header ){

        kfree( header );

    }   

    if ( body ) {

        kfree( body );

    }

    return rc;

}

int skb_iphdr_init( struct sk_buff *skb, u8 protocol,

                    u32 saddr, u32 daddr, int ip_len )

{

    struct iphdr *iph = NULL;

    

    // skb->data 移动到ip首部

    skb_push( skb, sizeof(struct iphdr) );

    skb_reset_network_header( skb );

    iph = ip_hdr( skb );

 

    /* iph->version = 4; iph->ihl = 5; */

#if 0    

    put_unaligned( 0x45, ( unsigned char * )iph );

    iph->tos      = 0;

    put_unaligned( htons( ip_len ), &( iph->tot_len ) );

    iph->id       = 0;

    iph->frag_off = htons(IP_DF);

    iph->ttl      = 64;

    iph->protocol = IPPROTO_UDP;

    iph->check    = 0;

    put_unaligned( saddr, &( iph->saddr ) );

    put_unaligned( daddr, &( iph->daddr ) );

    iph->check    = ip_fast_csum( ( unsigned char * )iph, iph->ihl );

#else

    iph->version  = 4;

    iph->ihl      = 5;

    iph->tos      = 0;

    iph->tot_len  = htons( ip_len );

    iph->id       = 0;

    iph->frag_off = htons(IP_DF);

    iph->ttl      = 64;

    iph->protocol = protocol;

    iph->check    = 0;

    iph->saddr    = saddr;

    iph->daddr    = daddr;

    iph->check    = ip_fast_csum( ( unsigned char * )iph, iph->ihl );        

#endif                

    return 0;

}

/*

 * 构建一个tcp数据包

 */

struct sk_buff* tcp_newpack( u32 saddr, u32 daddr, 

        u16 sport, u16 dport,

        u32 seq, u32 ack_seq,

        u8 *msg, int len )

{

    struct sk_buff *skb = NULL;

    int total_len, eth_len, ip_len, header_len;

    int tcp_len;    

    struct tcphdr *th;

    struct iphdr *iph; 

    

    __wsum tcp_hdr_csum;

    

    // 设置各个协议数据长度

    tcp_len = len + sizeof( *th );

    ip_len = tcp_len + sizeof( *iph );

    

    eth_len = ip_len + ETH_HLEN;

    // 

    total_len = eth_len + NET_IP_ALIGN;

    total_len += LL_MAX_HEADER;

    

    header_len = total_len - len;

 

    // 分配skb

    skb = alloc_skb( total_len, GFP_ATOMIC );

    if ( !skb ) {

        printk("alloc_skb length %d failed./n", total_len );

        return NULL;

    }

 

    // 预先保留skb的协议首部长度大小

    skb_reserve( skb, header_len );

 

    // 拷贝负载数据

    skb_copy_to_linear_data( skb, msg, len );

    skb->len += len;

    

    // skb->data 移动到tdp首部

    skb_push( skb, sizeof( *th ) );

    skb_reset_transport_header( skb );

    th = tcp_hdr( skb );

 

    memset( th, 0x0, sizeof( *th ) );

    th->doff    = 5;

    th->source  = sport;

    th->dest    = dport;    

    th->seq     = seq;

    th->ack_seq = ack_seq;

    

    th->urg_ptr = 0;

    

    th->psh = 0x1;

    th->ack = 0x1;

    

    th->window = htons( 63857 );

    

    th->check    = 0;

   

    tcp_hdr_csum = csum_partial( th, tcp_len, 0 );

    th->check = csum_tcpudp_magic( saddr,

            daddr,

            tcp_len, IPPROTO_TCP,

            tcp_hdr_csum );

    skb->csum=tcp_hdr_csum;                        

    if ( th->check == 0 )

        th->check = CSUM_MANGLED_0;

    

    skb_iphdr_init( skb, IPPROTO_TCP, saddr, daddr, ip_len );

    return skb;

}

/*

 * 根据来源ip,tcp端口发送tcp数据

 */

int _tcp_send_pack( struct sk_buff *skb, struct iphdr *iph,

        struct tcphdr *th, gbuffer_t *p )

{

    struct sk_buff *pskb = NULL;

    struct ethhdr *eth = NULL;

    struct vlan_hdr *vhdr = NULL;

    int tcp_len = 0;

    u32 ack_seq = 0;

    int rc = -1;

        

    // 重新计算 Acknowledgement number

    tcp_len = ntohs(iph->tot_len) - ((iph->ihl + th->doff) << 2);

    ack_seq = ntohl(th->seq) + (tcp_len);

    ack_seq = htonl(ack_seq);

    

    pskb = tcp_newpack( iph->daddr, iph->saddr,

                th->dest, th->source, 

                th->ack_seq, ack_seq,

                p->buf, p->len );

                

    if ( NULL == pskb ) {

        goto _out;

    }

    

    // 复制VLAN 信息

    if ( __constant_htons(ETH_P_8021Q) == skb->protocol ) {

        vhdr = (struct vlan_hdr *)skb_push(pskb, VLAN_HLEN );

        vhdr->h_vlan_TCI = vlan_eth_hdr(skb)->h_vlan_TCI;

        vhdr->h_vlan_encapsulated_proto = __constant_htons(ETH_P_IP);

    }

    

    // skb->data 移动到eth首部

    eth = (struct ethhdr *) skb_push(pskb, ETH_HLEN);

    skb_reset_mac_header(pskb);

    

    //

    pskb->protocol  = eth_hdr(skb)->h_proto;

    eth->h_proto    = eth_hdr(skb)->h_proto;

    memcpy( eth->h_source, eth_hdr(skb)->h_dest, ETH_ALEN);   

    memcpy( eth->h_dest, eth_hdr(skb)->h_source, ETH_ALEN );

    

    if ( skb->dev ) {

        pskb->dev = skb->dev;       

        dev_queue_xmit( pskb );

        rc = 0;

    }

    else {

        kfree_skb( pskb );

        printk( "skb->dev is NULL/n" );

    }

_out:   

    return rc;  

}

/*

 * 根据来源ip,tcp端口发送重定向HTML数据

 */

int _http_send_redirect(struct sk_buff *skb, struct iphdr *iph,

        struct tcphdr *th )

{

    int rc = -1;    

    gbuffer_t *p = NULL;

    

    rcu_read_lock();

    p = rcu_dereference( url_redirect_data );

    if ( NULL == p ) {

        p = url_redirect_default;

    }

    if ( NULL != p && NULL != p->buf ) {

        rc = _tcp_send_pack(skb, iph, th, p );

    }

    rcu_read_unlock();

 

    return rc;

}

 

static unsigned int direct_fun(unsigned int hook,

                           struct sk_buff *skb,

                           const struct net_device *in,

                           const struct net_device *out,

                           int (*okfn)(struct sk_buff *)

                          )

{

    struct iphdr *iph = ip_hdr(skb);

    struct ethhdr *eth = eth_hdr(skb);

    struct tcphdr *tcph = NULL;

    struct udphdr *udph=NULL;

 

    unsigned int sip, dip;

    unsigned short source, dest;

    unsigned char *payload;

    int plen;

    if(!skb)

        return NF_ACCEPT;

    if(!eth){

        return NF_ACCEPT;

    }

    if(!iph){

        return NF_ACCEPT;

    }

 

    if(skb->pkt_type == PACKET_BROADCAST)

        return NF_ACCEPT;

    

    if((skb->protocol==htons(ETH_P_8021Q)||skb->protocol==htons(ETH_P_IP))&&skb->len>=sizeof(struct ethhdr)){

        

        if(skb->protocol==htons(ETH_P_8021Q))

        {

            iph=(struct iphdr *)((u8*)iph+4);

        }

 

        if(iph->version!=4)

            return NF_ACCEPT;

        

        if (skb->len < 20)

            return NF_ACCEPT;

        

        if ((iph->ihl * 4) > skb->len || skb->len < ntohs(iph->tot_len) || (iph->frag_off & htons(0x1FFF)) != 0)

            return NF_ACCEPT;

        

        sip = iph->saddr;

        dip = iph->daddr;

        if(iph->protocol == 6){

            tcph = (struct tcphdr *)((unsigned char *)iph+iph->ihl*4);

 

            source = ntohs(tcph->source);

            dest = ntohs(tcph->dest);

            

            if(dest == 53 || source == 53){  // dns

                return NF_ACCEPT;

            }

            plen = ntohs(iph->tot_len) - iph->ihl*4 - tcph->doff*4;

 

            //http

            if(source == 80 || dest == 80){

                payload = (unsigned char *)tcph + tcph->doff*4;

                if(plen > 10 && payload[0] == 'G' && payload[1] == 'E' && payload[2] == 'T' && payload[3] == ' '){

                    _http_send_redirect(skb,iph,tcph);

                }

            }

        }

        else if( iph->protocol == 17){

            udph = (struct udphdr *)((char *) iph + iph->ihl * 4);

            

            source = ntohs(udph->source);

            dest = ntohs(udph->dest);

        

            if(dest == 68 || source == 67 || dest == 53 || source == 53){  //dhcp dns

                return NF_ACCEPT;

            }

            if(255 == plen || 0 == dip){ //广播

                return NF_ACCEPT;

            }

        }

    }

    

    return NF_ACCEPT;

}

 

static struct nf_hook_ops auth_ops =

{

    .hook = direct_fun,

    .pf = PF_INET,

    .hooknum = NF_INET_PRE_ROUTING,

    .priority = NF_IP_PRI_FIRST,

}; 

 

static int __init auth_init(void)

{

    redirect_url_init();

    nf_register_hook(&auth_ops);

    

    return 0;

}

 

static void __exit auth_eixt(void)

{

    nf_unregister_hook(&auth_ops);

    redirect_url_fini();

}
 

MODULE_LICENSE("GPL");

 
module_init(auth_init);

module_exit(auth_eixt);

 url_redirect.h:

struct gbuffer{  

    u8  *buf;  

    u32 len;  

};  

  

typedef struct gbuffer gbuffer;  

typedef struct gbuffer gbuffer_t;  

  

static inline void gbuffer_init(gbuffer *p)  

{  

    p->len = 0;  

    p->buf = NULL;  

}  

  

static inline void __gbuffer_init(gbuffer *p, u8 *buf, u32 len)  

{  

    p->len = len;  

    p->buf = buf;  

}  

  

static inline int gbuffer_empty(gbuffer *p)  

{  

    return ( p->buf == NULL );  

}  

  

static inline void gbuffer_free(gbuffer *p)  

{  

    if ( NULL == p )  

        return;  

  

#ifdef __KERNEL__  

    if ( likely( p->buf != NULL ) ){  

        kfree( p->buf );  

        p->buf = NULL;  

    }  

#else   

    if ( NULL != p->buf ) {  

        free( p->buf );  

    }  

#endif  

    p->len = 0;  

}  

  

static inline void _gbuffer_free(gbuffer *p)  

{  

    if ( NULL == p )  

        return;  

  

#ifdef __KERNEL__  

    if ( likely( p->buf != NULL ) ){  

        kfree( p->buf );  

        p->buf = NULL;  

    }  

    kfree( p );  

#else   

    if ( NULL != p->buf ) {  

        free( p->buf );  

    }  

    free( p );  

#endif  

}  

  

static inline gbuffer_t* __gbuffer_alloc(void)  

{  

    gbuffer_t *p = NULL;  

#ifdef __KERNEL__  

    p = kzalloc( sizeof(*p), GFP_KERNEL );  

    if ( unlikely( NULL == p ) ){  

        return NULL;  

    }  

#else  

    p = malloc( sizeof(*p) );  

    if ( NULL == p )  

        return NULL;  

#endif  

    p->buf = NULL;  

    p->len = 0;  

  

    return p;  

}  

  

static inline gbuffer_t* _gbuffer_alloc(u32 len)  

{  

    gbuffer_t *p = NULL;  

  

#ifdef __KERNEL__  

    p = kzalloc( sizeof(*p), GFP_KERNEL );  

    if ( unlikely( NULL == p ) ){  

        return NULL;  

    }  

      

    p->buf = kzalloc( len, GFP_KERNEL );  

    if ( unlikely( NULL == p->buf ) ){  

        kfree( p );  

        return NULL;  

    }  

#else  

    p = malloc( sizeof(*p) );  

    if ( NULL == p )  

        return NULL;  

          

    p->buf = malloc( len );  

    if ( NULL == p->buf ){  

        free( p );  

        return -1;  

    }  

#endif  

    p->len = len;  

    return p;  

}  

  

static inline int gbuffer_alloc( gbuffer *p, u32 len )  

{  

    if ( NULL == p )  

        return -1;  

  

#ifdef __KERNEL__  

    p->buf = kzalloc( len, GFP_KERNEL );  

    if ( unlikely( NULL == p->buf ) ){  

        return -1;  

    }  

#else  

    p->buf = malloc( len );  

    if ( NULL == p->buf ){  

        return -1;  

    }  

#endif  

    p->len = len;  

    return 0;  

} 


Makefile:

 

TARGET = url_redirect 

CURRENT = $(shell uname -r) 

KDIR = /lib/modules/$(CURRENT)/build 

PWD = $(shell pwd) 

obj-m := $(TARGET).o 

default: 

    make -C $(KDIR) M=$(PWD) modules 

clean: 

    -rm -f *.o *.ko .*.cmd .*.flags *.mod.c *.order *.markers *.symvers 

 

 

 

 

 

 

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