注册 登录
自由的生活_软路由 返回首页

心想事成的个人空间 https://bbs.routerclub.com/?681 [收藏] [复制] [分享] [RSS]

日志

在内核中实现URL重定向

热度 2已有 2331 次阅读2011-4-7 23:44 |个人分类:内核|

URL redirection,或称网址重定向或URL重定向,是指当使用者浏览某个网址时,将他导向到另一个网址的技术。常用在把一串很长的网站网址,转成较短的网址。因为当要传播某网站的网址时,常常因为网址太长,不好记忆;又有可能因为换了网络的免费网页空间,网址又必须要变更,不知情的使用者还以为网站关闭了。这时就可以用网络上的转址服务了。这种方法还可以用在广告推送及拦截上, 最常见的就是电信使用的了。

 
 
在技术上, URL重定向可以很多种方法实现, 下面介绍其中常用的几种.
 
方法一:
 
       通过返回一个简单的HTML页面, 采用自刷新方式,将页面重新引至新URL页面. 示例:
 
<html><head>
 
  <meta http-equiv="Refresh" content="0; url=http://www.example.com/">
 
</head><body>
 
  <p>Please follow <a href="http://www.example.com/">link</a>!</p>
 
</body></html>
 
方法二:
 
       通过返回一个HTTP refresh header, 进行重定向. 示例:
 
       HTTP/1.1 200 ok
 
Refresh: 0; url=http://www.example.com/
 
Content-type: text/html
 
Content-length: 78
 
 
 
Please follow <a href="http://www.example.com/">link</a>!
 
方法三:
 
       通过HTTP 状态码301 Moved Permanently 永久重定向, 示例:
 
HTTP/1.1 301 Moved Permanently
 
Location: http://www.example.org/
 
Content-Type: text/html
 
Content-Length: 174
 
 
 
<html>
 
<head>
 
<title>Moved</title>
 
</head>
 
<body>
 
<h1>Moved</h1>
 
<p>This page has moved to <a href="http://www.example.org/">http://www.example.org/</a>.</p>
 
</body>
 
</html>
 
 
 
经过个人测试, 发现采用HTTP 状态码301 Moved Permanently方式速度比较快.
 
 
 
以下是内核实现源码, 测试的时候有个小bug 把mac地址填错了, 结果调试了N久,还发帖了, 最终自己才发现, 真是惭愧.

 

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

#define OPTION_SACK_ADVERTISE (1 << 0)
#define OPTION_TS  (1 << 1)
#define OPTION_MD5  (1 << 2)

struct tcp_out_options {
 u8 options;  /* bit field of OPTION_* */
 u8 ws;   /* window scale, 0 to disable */
 u8 num_sack_blocks; /* number of SACK blocks to include */
 u16 mss;  /* 0 to disable */
 __u32 tsval, tsecr; /* need to include OPTION_TS */
};


void _tcp_parse_options(struct tcphdr *th,
  struct tcp_options_received *opt_rx,
  int estab);

struct sk_buff* tcp_newpack( u32 saddr, u32 daddr,
  u16 sport, u16 dport,
  u32 seq, u32 ack_seq,
  const struct tcp_out_options *opts,
  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 "127.0.0.1/"

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 ) {
  DBG_ERROR( verbose,
   "__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;
  DBG_ERROR( verbose,
   "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 ) {
  DBG_ERROR( verbose,
   "__gbuffer_alloc failed.\n" );
  return -1;
 }
 if ( http_build_redirect_url( fqdn_redirect_url,
   p ) ) {
  DBG_ERROR( verbose,
   "http_build_redirect_url %s failed.\n",
   fqdn_redirect_url );
  _gbuffer_free( p );
  return -1;
 }

 DBG_INFO( verbose,
  "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的几种格式
 */

#if 0
const char *http_redirect_header =
 "HTTP/1.1 200 OK\r\n"
 //"Date: Fri, 23 Apr 2010 12:54:40 GMT\r\n"
 //"Server: Apache/2.2.15 (Win32)\r\n"
 "Content-Type: text/html; charset=iso-8859-1\r\n"
 "Content-length: %d\r\n"
 "\r\n";
const char *http_redirect_body =   
 "<html><head>\n"
   "<meta http-equiv=\"Refresh\" content=\"0; url=http://%s\">\n"
 "</head><body>\n"
   "<p>Please follow <a href="\" mce_href="\""http://%s\">link</a>!</p>\n"
 "</body></html>\n";
#elif 0
const char *http_redirect_header =
 "HTTP/1.1 200 OK\r\n"
 "Refresh: 0; url=http://%s\r\n"
 "Content-Type: text/html; charset=iso-8859-1\r\n"
 "Content-length: %d\r\n"
 "\r\n";
 
const char *http_redirect_body = 
 "Please follow <a href="\" mce_href="\""http://%s\">link</a>!";

#else
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: %d\r\n"
 "\r\n";
 
const char *http_redirect_body = 
 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
 "<html><head>\n"
 "<title>301 Moved Permanently</title>\n"
 "</head><body>\n"
 "<h1>Moved Permanently</h1>\n"
 "<p>The document has moved <a href="\" mce_href="\""http://%s\">here</a>.</p>\n"
 "</body></html>\n";
#endif

/*
 * 构建一个重定向HTML缓冲
 */
int http_build_redirect_url( const char *url, gbuffer_t *p )
{
 char *header = NULL;
 char *body  = NULL;
 char *buf   = NULL;
 int header_len, body_len;
 int rc = -1; 
 
 if ( NULL == p )
  goto _out;
  
 header = kzalloc( PATH_MAX, GFP_KERNEL );
 if ( NULL == header ) {
  goto _out;
 }
 body = kzalloc( PATH_MAX, GFP_KERNEL );
 if ( NULL == body ){
  goto _out;
 }
  
 body_len = snprintf( body, PATH_MAX,
     http_redirect_body,
     url
     //,url
     );
   
 body_len = strlen( body ); 
 //DBG_INFO( verbose, "Length=%d\nBody:%s\n", body_len, body );
      
 header_len = snprintf( header, PATH_MAX,
     http_redirect_header, 
     url,    
     body_len
     );
     
 //DBG_INFO( verbose, "Header:%s\n", header ); 
      
 buf = kzalloc( header_len + body_len, GFP_KERNEL );
 if ( NULL == buf ){
  goto _out;
 }
 
 p->buf = buf;
 p->len = header_len + body_len;
 
 memcpy( buf, header, header_len );
 memcpy( buf + header_len, body, body_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;
}

/*
 * 构建一个tcp数据包
 */
struct sk_buff* tcp_newpack( u32 saddr, u32 daddr,
  u16 sport, u16 dport,
  u32 seq, u32 ack_seq,
  const struct tcp_out_options *opts,
  u8 *msg, int len )
{
 struct sk_buff *skb = NULL;
 int total_len, eth_len, ip_len, header_len;
 int tcp_opt_len, tcp_len; 
    struct tcphdr *th;
    struct iphdr *iph;    
 __wsum tcp_hdr_csum;
 __be32 *ptr = NULL;
 
 tcp_opt_len = 0;
 if ( likely(OPTION_TS & opts->options) ){
  tcp_opt_len += TCPOLEN_TSTAMP_ALIGNED;
 }
    // 设置各个协议数据长度
    tcp_len = len + sizeof( *th ) + tcp_opt_len;
    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 ) {
        dbg_err( "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;
 
 if ( likely(OPTION_TS & opts->options) ) {
  ptr = (__be32 *)skb_push( skb, TCPOLEN_TSTAMP_ALIGNED );
  #if 0
  if (unlikely(OPTION_SACK_ADVERTISE & opts->options)) {
   *ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
           (TCPOLEN_SACK_PERM << 16) |
           (TCPOPT_TIMESTAMP << 8) |
           TCPOLEN_TIMESTAMP);
  } else {
   *ptr++ = htonl((TCPOPT_NOP << 24) |
           (TCPOPT_NOP << 16) |
           (TCPOPT_TIMESTAMP << 8) |
           TCPOLEN_TIMESTAMP);
  }
  #else
  *ptr++ = htonl((TCPOPT_NOP << 24) |
           (TCPOPT_NOP << 16) |
           (TCPOPT_TIMESTAMP << 8) |
           TCPOLEN_TIMESTAMP);      
          
  #endif
  *ptr++ = htonl(opts->tsval);
  *ptr++ = htonl(opts->tsecr);
 }
 
    // skb->data 移动到udp首部
    skb_push( skb, sizeof( *th ) );
    skb_reset_transport_header( skb );
    th = tcp_hdr( skb );

    memset( th, 0x0, sizeof( *th ) );
    if ( tcp_opt_len ) {
     th->doff = (tcp_opt_len + sizeof(*th)) >> 2;
    }
    else {
     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;
    #if 1
    tcp_hdr_csum = csum_partial( th, tcp_len, 0 );
    th->check = csum_tcpudp_magic( saddr,
            daddr,
            tcp_len, IPPROTO_TCP,
            tcp_hdr_csum );
    #else
    th->check = tcp_v4_check(tcp_len,
         saddr, daddr,
         csum_partial(th,
        tcp_len, 0));
 #endif          
    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;
 struct tcp_options_received opt_rx;
 struct tcp_out_options opts;
 int tcp_len = 0;
 u32 seq = 0, ack_seq = 0;
 u32 tcp_rcv_tsecr = tcp_time_stamp;
 int rc = -1;

 //
 opt_rx.tstamp_ok = 1;
 _tcp_parse_options( th, &opt_rx, 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);
 //
 get_random_bytes( &seq, sizeof(seq) );
 //seq = common_seq;
 
 memset( &opts, 0x0, sizeof(opts) );
 if ( opt_rx.saw_tstamp ) {
  opts.options |= OPTION_TS;  
  opts.tsecr = opt_rx.rcv_tsval;
  opts.tsval = tcp_time_stamp - tcp_rcv_tsecr + opt_rx.rcv_tsval;
 }
 
 pskb = tcp_newpack( iph->daddr, iph->saddr,
    th->dest, th->source,
    th->ack_seq, ack_seq,
    &opts,
    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 );
  dbg_err( "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;
  DBG_INFO( verbose,
   "Send default redirect URL http://%s\n", DEFAULT_REDIRECT_URL );
 }
 if ( NULL != p && NULL != p->buf ) {
  rc = _tcp_send_pack(skb, iph, th, p );
 }
 rcu_read_unlock();

 return rc;
}
 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/force_eagle/archive/2010/05/09/5572264.aspx


路过

雷人

握手

鲜花

鸡蛋

发表评论 评论 (2 个评论)

回复 fjingxu 2011-4-8 09:17
学习了。
回复 11 2012-5-13 23:00
http://vivincent.yo2.cn/

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|Archiver|手机版|小黑屋|软路由 ( 渝ICP备15001194号-1|渝公网安备 50011602500124号 )

GMT+8, 2024-5-7 04:01 , Processed in 0.095043 second(s), 7 queries , Gzip On, Redis On.

Powered by Discuz! X3.5 Licensed

© 2001-2023 Discuz! Team.

返回顶部