1112 lines
32 KiB
C
Executable File
1112 lines
32 KiB
C
Executable File
/******************************************************************************
|
||
*
|
||
* Copyright (c) 2013 TP-LINK Technologies CO.,LTD.
|
||
* All rights reserved.
|
||
*
|
||
* FILE NAME : tp_domain.c
|
||
* VERSION : 1.0
|
||
* DESCRIPTION: 处理路由器页面的域名登录.
|
||
*
|
||
* AUTHOR : wangbing <wangbing@tp-link.net>
|
||
* CREATE DATE: 27/02/2013
|
||
*
|
||
* HISTORY :
|
||
* 2013-02-27 wangbing revised for adsl router.
|
||
* This file is originally created by huangwenzhong <wangbing@tp-link.net>.
|
||
*
|
||
*
|
||
******************************************************************************/
|
||
#include <linux/module.h>
|
||
#include <linux/vermagic.h>
|
||
#include <linux/compiler.h>
|
||
#include <linux/sockios.h>
|
||
#include <linux/inetdevice.h>
|
||
#include <linux/netdevice.h>
|
||
#include <linux/types.h>
|
||
#include <linux/skbuff.h>
|
||
#include <linux/if_ether.h>
|
||
#include <linux/netfilter.h>
|
||
#include <linux/netfilter_ipv4.h>
|
||
#include <linux/netfilter_ipv6.h>
|
||
#include <linux/string.h>
|
||
|
||
#include <linux/module.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/init.h>
|
||
#include <linux/workqueue.h>
|
||
#include <linux/in.h>
|
||
#include <linux/inet.h>
|
||
#include <linux/socket.h>
|
||
#include <linux/ip.h>
|
||
#include <linux/udp.h>
|
||
#include <net/sock.h>
|
||
#include <net/route.h>
|
||
#include <net/ip6_route.h>
|
||
#include <net/ip.h>
|
||
#include <net/flow.h>
|
||
#include <net/if_inet6.h>
|
||
#include <linux/version.h>
|
||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
|
||
#include <../net/bridge/br_private.h>
|
||
#endif
|
||
#include <linux/netfilter_bridge.h>
|
||
#include "./tp_domain.h"
|
||
#ifdef INCLUDE_DOMAIN_INTERCEPT
|
||
#include "./domain_intercept.h"
|
||
#endif /*INCLUDE_DOMAIN_INTERCEPT*/
|
||
|
||
|
||
#define MAX_LAN_DOMAIN_LEN (64)
|
||
#define DOT_CHAR '.'
|
||
#define IPV6_HDR_LEN 40
|
||
|
||
static __u32 lan_ip = 0xc0a80001; /* default value */
|
||
#if defined(CONFIG_TP_MODEL_MR400V1)
|
||
static char *lan_domain = "tplinkmodem.net";
|
||
#else
|
||
static char *lan_domain = "tplinkmifi.net";
|
||
#if 0
|
||
#define DOMAIN_NUMBER (2)
|
||
#define DOMAIN_LENGTH (32)
|
||
static char lan_domain[DOMAIN_NUMBER][DOMAIN_LENGTH] = {"tplinklogin.net", "tplinkwifi.net"};
|
||
#endif
|
||
#endif
|
||
|
||
static unsigned short lan_ip6[] = {0xfe80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
|
||
|
||
module_param( lan_ip, uint, 0 );
|
||
module_param(lan_domain, charp, 0);
|
||
|
||
#if 1
|
||
static char query[MAX_LAN_DOMAIN_LEN] = {0};
|
||
|
||
|
||
#define DNS_QUERY_LEN (strlen(query))
|
||
#define DNS_DATA_LEN (DNS_QUERY_LEN + 1 + 4 + 16)
|
||
|
||
#else
|
||
static char query[DOMAIN_NUMBER][MAX_LAN_DOMAIN_LEN] = {0};
|
||
|
||
int DNS_QUERY_LEN[DOMAIN_NUMBER];
|
||
int DNS_HIT_NUMBER = 0;
|
||
#define DNS_DATA_LEN (DNS_QUERY_LEN[DNS_HIT_NUMBER] + 1 + 4 + 16);
|
||
#endif
|
||
|
||
static void parse_domain(void)
|
||
{
|
||
#if 1
|
||
char *pstr = NULL;
|
||
char *tmp = query;
|
||
|
||
strncpy(query + 1, lan_domain, MAX_LAN_DOMAIN_LEN - 1);
|
||
|
||
pstr = strchr(tmp + 1, DOT_CHAR);
|
||
while (NULL != pstr)
|
||
{
|
||
*tmp = pstr - tmp - 1;
|
||
tmp = pstr;
|
||
pstr = strchr(tmp + 1, DOT_CHAR);
|
||
}
|
||
|
||
*tmp = strlen(query) - (tmp - query) - 1;
|
||
|
||
return;
|
||
|
||
#else
|
||
char *pstr = NULL;
|
||
char *tmp = NULL;
|
||
int i = 0;
|
||
int len = 0;
|
||
|
||
for (i=0; i<DOMAIN_NUMBER; i++)
|
||
{
|
||
tmp = query[i];
|
||
strncpy(query[i] + 1, lan_domain[i], MAX_LAN_DOMAIN_LEN - 1);
|
||
|
||
pstr = strchr(tmp + 1, DOT_CHAR);
|
||
while (NULL != pstr)
|
||
{
|
||
*tmp = pstr - tmp - 1;
|
||
tmp = pstr;
|
||
pstr = strchr(tmp + 1, DOT_CHAR);
|
||
}
|
||
|
||
*tmp = strlen(query[i]) - (tmp - query[i]) - 1;
|
||
DNS_QUERY_LEN[i] = strlen(query[i]);
|
||
}
|
||
return;
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : get_idev_ip6_addr()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 用于获取传入dev对应接口的IPv6地址, 仅指获取链路本地地址(以fe80::开头的)
|
||
* INPUT : dev - 获取地址所处的接口对应的inet6_dev设备
|
||
* OUTPUT : addr - 存储IPv6地址的内存
|
||
* RETURN : N/A
|
||
* OTHERS : 如果没有桥接口,直接获取当前的地址.否则获取桥下面第一个port的地址
|
||
******************************************************************************/
|
||
static inline void get_idev_ip6_addr(struct inet6_dev *idev, struct in6_addr *addr)
|
||
{
|
||
struct inet6_ifaddr *ifa = NULL;
|
||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) )
|
||
struct list_head *p = NULL;
|
||
if (idev)
|
||
{
|
||
/*
|
||
* Each device address list is sorted in order of scope -
|
||
* global before linklocal.
|
||
*/
|
||
|
||
list_for_each(p, &idev->addr_list)
|
||
{
|
||
ifa = list_entry(p, struct inet6_ifaddr, if_list);
|
||
if (ifa->addr.s6_addr[0] == 0xFE && ifa->addr.s6_addr[1] == 0x80)
|
||
{
|
||
//get the link local address for this port
|
||
memcpy(addr->s6_addr, ifa->addr.s6_addr, 16);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
#else
|
||
if (idev)
|
||
{
|
||
for (ifa=idev->addr_list; ifa; ifa=ifa->if_next)
|
||
{
|
||
if (ifa->addr.s6_addr[0] == 0xFE && ifa->addr.s6_addr[1] == 0x80)
|
||
{
|
||
//get the link local address for this port
|
||
memcpy(addr->s6_addr, ifa->addr.s6_addr, 16);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : get_dev_ip6_addr()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 用于获取传入dev对应接口的IPv6地址, 仅指获取链路本地地址(以fe80::开头的)
|
||
* INPUT : dev - 获取地址所处的接口对应的网络设备
|
||
* OUTPUT : addr - 存储IPv6地址的内存
|
||
* RETURN : N/A
|
||
* OTHERS : 如果没有桥接口,直接获取当前的地址.否则获取桥下面第一个port的地址
|
||
******************************************************************************/
|
||
static inline void get_dev_ip6_addr(struct net_device *dev, struct in6_addr *addr)
|
||
{
|
||
struct inet6_dev * in6_dev = NULL;
|
||
struct net_bridge_port * br_port = NULL;
|
||
if(dev)
|
||
{
|
||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
|
||
if (!br_port_exists(dev) && dev->ip6_ptr)
|
||
{
|
||
in6_dev = (struct inet6_dev*)dev->ip6_ptr;
|
||
get_idev_ip6_addr(in6_dev, addr);
|
||
return;
|
||
}
|
||
br_port = br_port_get_rcu(dev);
|
||
#else
|
||
br_port = dev->br_port;
|
||
#endif
|
||
|
||
if(br_port && br_port->br && br_port->br->dev)
|
||
{
|
||
in6_dev = (struct inet6_dev*)br_port->br->dev->ip6_ptr;
|
||
get_idev_ip6_addr(in6_dev, addr);
|
||
}
|
||
}
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : get_dev_ip_addr()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 用于获取传入dev对应接口的IPv4地址
|
||
* INPUT : dev - 获取地址所处的接口对应的网络设备
|
||
* OUTPUT : addr - 存储IPv4地址的内存
|
||
* RETURN : N/A
|
||
* OTHERS : 如果没有桥接口,直接获取当前的地址.否则获取桥下面第一个port的地址
|
||
******************************************************************************/
|
||
static inline void get_dev_ip_addr(struct net_device *dev, __u32 *addr)
|
||
{
|
||
struct in_device *in_dev;
|
||
struct net_bridge_port *br_port;
|
||
struct in_ifaddr *ifap = NULL;
|
||
if(dev)
|
||
{
|
||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
|
||
if (!br_port_exists(dev) && dev->ip_ptr)
|
||
{
|
||
in_dev = (struct in_device *)dev->ip_ptr;
|
||
if(in_dev)
|
||
{
|
||
ifap = in_dev->ifa_list;
|
||
while(ifap != NULL)
|
||
{
|
||
if (!strcmp(ifap->ifa_label, dev->name))
|
||
break;
|
||
ifap = ifap->ifa_next;
|
||
}
|
||
if (ifap != NULL)
|
||
{
|
||
*addr = ifap->ifa_local;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
br_port = br_port_get_rcu(dev);
|
||
#else
|
||
br_port = dev->br_port;
|
||
#endif
|
||
if(br_port && br_port->br && br_port->br->dev && br_port->br->dev->ip_ptr)
|
||
{
|
||
in_dev = (struct in_device *)br_port->br->dev->ip_ptr;
|
||
ifap = in_dev->ifa_list;
|
||
while(ifap != NULL)
|
||
{
|
||
if (!strcmp(ifap->ifa_label, br_port->br->dev->name))
|
||
break;
|
||
ifap = ifap->ifa_next;
|
||
}
|
||
*addr = ifap->ifa_local;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : udp_csum_calc()
|
||
* AUTHOR : huangwenzhong <huangwenzhong@tp-link.net>
|
||
* DESCRIPTION : 用于计算UDP的检验和
|
||
* INPUT : iph - IP头指针(或者IPv6头指针)
|
||
* udp - UDP头指针
|
||
* payload_len - UDP载荷长度
|
||
* sa_family - 传入的头部指针的类型为IP/IPv6 (取值:AF_INET/AF_INET6)
|
||
* OUTPUT : N/A
|
||
* RETURN : N/A
|
||
* OTHERS :
|
||
******************************************************************************/
|
||
static unsigned short udp_csum_calc( void *iph, struct udphdr *udph, int payload_len, int sa_family)
|
||
{
|
||
unsigned long csum = 0;
|
||
struct iphdr *pIpHdr = (struct iphdr *)iph;
|
||
struct ipv6hdr *pIp6Hdr = (struct ipv6hdr *)iph;
|
||
unsigned long csumed_len = payload_len + sizeof( *udph );
|
||
unsigned short *word_ptr = ( unsigned short * )udph;
|
||
int index = 0;
|
||
|
||
while ( csumed_len >1 )
|
||
{
|
||
csum += htons(*word_ptr++);
|
||
csumed_len -= sizeof( unsigned short );
|
||
}
|
||
|
||
if ( csumed_len )
|
||
{
|
||
csum += ( *( ( unsigned char * )word_ptr ) ) << 8;
|
||
}
|
||
|
||
if (AF_INET == sa_family)
|
||
{ //ip src addr
|
||
csum += ( htonl(pIpHdr->saddr) ) & 0x0000ffff;
|
||
csum += ( (htonl(pIpHdr->saddr)) >> 16 ) & 0x0000ffff;
|
||
//ip dst addr
|
||
csum += ( htonl(pIpHdr->daddr) ) & 0x0000ffff;
|
||
csum += ( (htonl(pIpHdr->daddr)) >> 16 ) & 0x0000ffff;
|
||
//udp hdr len
|
||
csum += htons(udph->len);
|
||
//udp proto
|
||
csum += IPPROTO_UDP & 0x00ff;
|
||
}
|
||
else if (AF_INET6 == sa_family)
|
||
{
|
||
//ipv6 src addr
|
||
for(index = 0; index < 8; index++)
|
||
csum += htons(pIp6Hdr->saddr.s6_addr16[index]);
|
||
//ipv6 dst addr
|
||
for(index = 0; index < 8; index++)
|
||
csum += htons(pIp6Hdr->daddr.s6_addr16[index]);
|
||
//up section length, here dns packet with no extern head is the payload length
|
||
csum += htons(pIp6Hdr->payload_len);
|
||
//next head type
|
||
csum += pIp6Hdr->nexthdr & 0x00ff;
|
||
}
|
||
|
||
while(csum > 0x10000)
|
||
{
|
||
csum = (csum & 0x0000ffff) + (csum >> 16);
|
||
}
|
||
return ( unsigned short )( ~csum );
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : create_ip6_hdr()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 根据原始IPv6数据报头向SKB中填充IPv6数据头
|
||
* INPUT : skb - 被填充数据报文头部的SKB
|
||
* old_ip6hdr - 原始IPv6报文头部指针, 根据此内容生成新的IPv6报文头部
|
||
* data_len - IPv6的载荷长度
|
||
* OUTPUT : N/A
|
||
* RETURN : 指向填充好后的新IPv6报文头部的指针
|
||
* OTHERS : 新IPv6数据报文头部交换原始数据报头中的源目的IPv6地址, 修改载荷长度, 其余内容与原始包头保持不变
|
||
******************************************************************************/
|
||
static inline struct ipv6hdr *create_ip6_hdr(struct sk_buff *skb, struct ipv6hdr *old_ip6hdr, unsigned short data_len)
|
||
{
|
||
struct ipv6hdr *new_ip6hdr;
|
||
|
||
if (IPV6_HDR_LEN > skb_tailroom(skb))
|
||
return NULL;
|
||
|
||
new_ip6hdr = (struct ipv6hdr *)skb_put(skb, IPV6_HDR_LEN);
|
||
|
||
/* we must specify the network header to avoid kernel panic.
|
||
when ip_nat hook was called, it use iphr(skb)to get network header.
|
||
by HouXB, 12May11 */
|
||
skb_set_network_header(skb, ETH_HEADER_LEN);
|
||
|
||
memcpy( ( unsigned char * )new_ip6hdr, ( unsigned char * )old_ip6hdr, IPV6_HDR_LEN);
|
||
new_ip6hdr->hop_limit = IP6_PACKET_HOPLIMIT;
|
||
new_ip6hdr->payload_len = htons(data_len);
|
||
new_ip6hdr->saddr = old_ip6hdr->daddr;
|
||
new_ip6hdr->daddr = old_ip6hdr->saddr;
|
||
return new_ip6hdr;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : create_ip_hdr()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 根据原始IP数据报头向SKB中填充IP数据头
|
||
* INPUT : skb - 被填充数据报文头部的SKB
|
||
* old_iphdr - 原始IP报文头部指针, 根据此内容生成新的IP报文头部
|
||
* data_len - IP的载荷长度
|
||
* OUTPUT : N/A
|
||
* RETURN : 指向填充好后的新IP报文头部的指针
|
||
* OTHERS : 新IP数据报文头部交换原始数据报头中的源目的IP地址, 修改载荷长度, 其余内容与原始包头保持不变
|
||
* 注意由于没有设置头部的id域, 因此调用者需要重新计算checksum.
|
||
******************************************************************************/
|
||
static inline struct iphdr * create_ip_hdr(struct sk_buff *skb, struct iphdr *old_iphdr, unsigned short data_len)
|
||
{
|
||
struct iphdr *new_iphdr;
|
||
|
||
if (sizeof(struct iphdr) > skb_tailroom(skb))
|
||
return NULL;
|
||
new_iphdr = ( struct iphdr * )skb_put( skb, sizeof( struct iphdr ) );
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) )
|
||
|
||
/* we must specify the network header to avoid kernel panic.
|
||
when ip_nat hook was called, it use iphr(skb)to get network header.
|
||
by HouXB, 12May11 */
|
||
//skb_set_network_header(dns_skb, ETH_HEADER_LEN);
|
||
//here sk_buff_data_t is typeof (unsigned char*), not offset
|
||
//but in the kernel definition, dns_skb->network_header is offset
|
||
//so cause the error
|
||
|
||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,20))
|
||
skb->network_header = ETH_HEADER_LEN;
|
||
#else
|
||
skb->network_header = (sk_buff_data_t)new_iphdr;
|
||
#endif
|
||
#else
|
||
skb->nh.iph = new_iphdr;
|
||
#endif
|
||
|
||
memcpy( ( unsigned char * )new_iphdr, ( unsigned char * )old_iphdr, old_iphdr->ihl *4 );
|
||
new_iphdr->ttl = IP_PACKET_TTL;
|
||
new_iphdr->frag_off |= htons( 0x4000 );
|
||
new_iphdr->saddr = ((struct iphdr *)old_iphdr)->daddr;
|
||
new_iphdr->daddr = ((struct iphdr *)old_iphdr)->saddr;
|
||
new_iphdr->tot_len = htons(new_iphdr->ihl * 4 + data_len);
|
||
new_iphdr->check = 0x0000;
|
||
|
||
return new_iphdr;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : create_udp_hdr()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 根据原始UDP数据报头向SKB中填充UDP数据头
|
||
* INPUT : skb - 被填充数据报文头部的SKB
|
||
* old_udphdr - 原始UDP报文头部指针, 根据此内容生成新的UDP报文头部
|
||
* data_len - UDP的载荷长度
|
||
* OUTPUT : N/A
|
||
* RETURN : 指向填充好后的新UDP报文头部的指针
|
||
* OTHERS : 新UDP数据报文头部交换原始数据报头中的源目的端口, 修改载荷长度, 其余内容与原始包头保持不变
|
||
* 注意调用者需要计算checksum.
|
||
******************************************************************************/
|
||
static inline struct udphdr* create_udp_hdr(struct sk_buff *skb, struct udphdr *old_udphdr, unsigned short data_len)
|
||
{
|
||
struct udphdr *new_udphdr;
|
||
/* create udp header */
|
||
if (sizeof(struct udphdr) > skb_tailroom(skb))
|
||
return NULL;
|
||
new_udphdr = ( struct udphdr * )skb_put( skb, sizeof( struct udphdr ) );
|
||
|
||
memcpy( ( unsigned char * )&new_udphdr->dest,
|
||
( unsigned char *)&old_udphdr->source, PORT_LEN );
|
||
memcpy( ( unsigned char * )&new_udphdr->source,
|
||
( unsigned char *)&old_udphdr->dest, PORT_LEN );
|
||
new_udphdr->len = htons( UDP_HEADER_LEN + data_len );
|
||
new_udphdr->check = 0x0000;
|
||
|
||
return new_udphdr;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : create_dns_hdr()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 根据原始DNS数据报头向SKB中填充新的DNS数据头
|
||
* INPUT : skb - 被填充数据报文头部的SKB
|
||
* old_dnshdr - 原始DNS报文头部指针, 根据此内容生成新的DNS报文头部
|
||
* OUTPUT : N/A
|
||
* RETURN : 指向填充好后的新DNS报文头部的指针
|
||
* OTHERS : 新DNS数据报文头部修改FLAG增加了DNS_RESPONSE_FLAG, 其余内容与原始包头保持不变
|
||
******************************************************************************/
|
||
static inline DNS_HEADER * create_dns_hdr(struct sk_buff *skb, DNS_HEADER *old_dnshdr)
|
||
{
|
||
DNS_HEADER *new_dnshdr;
|
||
|
||
/* create dns header */
|
||
if (sizeof(struct udphdr) > skb_tailroom(skb))
|
||
return NULL;
|
||
new_dnshdr = ( DNS_HEADER * )skb_put( skb, sizeof( DNS_HEADER ) );
|
||
|
||
memcpy( ( unsigned char * )&new_dnshdr->transaction_id,
|
||
( unsigned char * )&old_dnshdr->transaction_id, sizeof( __u16 ) );
|
||
memcpy( ( unsigned char * )&new_dnshdr->flag,
|
||
( unsigned char * )&old_dnshdr->flag, sizeof( __u16 ) );
|
||
new_dnshdr->flag |= htons( DNS_RESPONSE_FLAG );
|
||
memcpy( ( unsigned char * )&new_dnshdr->questions,
|
||
( unsigned char * )&old_dnshdr->questions, sizeof( __u16 ) );
|
||
new_dnshdr->answers_rrs = htons( 0x0001 );
|
||
new_dnshdr->authority_rrs = 0x0000;
|
||
new_dnshdr->additional_rrs = 0x0000;
|
||
|
||
return new_dnshdr;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : fill_dns_payload()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 向SKB中填充DNS的载荷
|
||
* INPUT : skb - 被填充数据报文的SKB
|
||
* dev - 用于获取响应地址解析的IP或者IPv6地址的网络设备.
|
||
* query_str - DNS查询请求字符串, 对应DNS请求解析的域名
|
||
* query_type - DNS请求的类型(A/AAAA)
|
||
* data_len - DNS载荷总长度(预先计算好了的)
|
||
* OUTPUT : N/A
|
||
* RETURN : 指向填充好后的新DNS载荷起始地址的指针
|
||
* OTHERS : 调用者需要保证载荷长度data_len的正确性
|
||
******************************************************************************/
|
||
static inline unsigned char * fill_dns_payload(struct sk_buff *skb,
|
||
struct net_device *dev,
|
||
unsigned char *query_str,
|
||
int query_type,
|
||
unsigned short data_len)
|
||
{
|
||
unsigned char *dns_payload = NULL;
|
||
unsigned char *tmp = NULL;
|
||
struct in6_addr addr6;
|
||
unsigned int addr = 0;
|
||
__u32 int_val = 0;
|
||
__u16 short_val = 0;
|
||
|
||
//init the address value
|
||
addr = htonl(lan_ip);
|
||
memcpy(addr6.s6_addr, lan_ip6, 16);
|
||
|
||
/* create dns questions */
|
||
if (data_len > skb_tailroom(skb))
|
||
{
|
||
return NULL;
|
||
}
|
||
dns_payload = ( unsigned char * )skb_put( skb, data_len );
|
||
tmp = dns_payload;
|
||
|
||
memcpy( tmp, query_str, DNS_QUERY_LEN );
|
||
tmp = dns_payload + DNS_QUERY_LEN;
|
||
|
||
*tmp = 0x00;
|
||
tmp++;
|
||
|
||
short_val = htons( query_type );
|
||
memcpy( tmp, &short_val, sizeof( short_val ) );
|
||
tmp += sizeof( short_val );
|
||
|
||
short_val = htons( DNS_QUERY_CLASS );
|
||
memcpy( tmp, &short_val, sizeof( short_val ) );
|
||
tmp += sizeof( short_val );
|
||
|
||
/* create dns answer */
|
||
short_val = htons( DNS_RESPONSE_POINTER );
|
||
memcpy( tmp, &short_val, sizeof( short_val ) );
|
||
tmp += sizeof( short_val );
|
||
|
||
short_val = htons( query_type );
|
||
memcpy( tmp, &short_val, sizeof( short_val ) );
|
||
tmp += sizeof( short_val );
|
||
|
||
short_val = htons( DNS_QUERY_CLASS );
|
||
memcpy( tmp, &short_val, sizeof( short_val ) );
|
||
tmp += sizeof( short_val );
|
||
|
||
int_val = htonl( DNS_ANSWER_TTL );
|
||
memcpy( tmp, &int_val, sizeof( int_val ) );
|
||
tmp += sizeof( int_val );
|
||
|
||
if (query_type == DNS_QUERY_TYPE_AAAA)
|
||
short_val = htons( DNS_RESOURCE_LEN_AAAA );
|
||
else
|
||
short_val = htons( DNS_RESOURCE_LEN_A );
|
||
memcpy( tmp, &short_val, sizeof( short_val ) );
|
||
tmp += sizeof( short_val );
|
||
|
||
/*get lan ip as answer*/
|
||
if (query_type == DNS_QUERY_TYPE_AAAA)
|
||
{
|
||
get_dev_ip6_addr(dev, &addr6);
|
||
//ipv6 address should always be fe80::1
|
||
memcpy(tmp, addr6.s6_addr, DNS_RESOURCE_LEN_AAAA);
|
||
tmp+= DNS_RESOURCE_LEN_AAAA;
|
||
}
|
||
else
|
||
{
|
||
get_dev_ip_addr(dev, &addr);
|
||
int_val = addr;
|
||
memcpy( tmp, &int_val, sizeof( int_val ) );
|
||
tmp+= sizeof(int_val);
|
||
}
|
||
|
||
return dns_payload;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : tp_send_dns6_packet()
|
||
* AUTHOR : chenming <chenming@tp-link.net>
|
||
* DESCRIPTION : 用于构建和发送DNSv6应答包
|
||
* INPUT : skb - DNSv6请求包
|
||
* old_iphdr - DNSv6请求包的IPv6头指针
|
||
* old_udphdr - DNSv6请求包的UDP头指针
|
||
* old_dnshdr - DNSv6请求包的DNS头指针
|
||
* query_type - 请求的域名的类型, A/AAAA
|
||
*
|
||
* OUTPUT : N/A
|
||
* RETURN : N/A
|
||
* OTHERS :
|
||
******************************************************************************/
|
||
static void tp_send_dns6_packet(struct sk_buff *skb,
|
||
struct ipv6hdr *old_iphdr,
|
||
struct udphdr *old_udphdr,
|
||
DNS_HEADER *old_dnshdr,
|
||
unsigned short query_type)
|
||
{
|
||
struct sk_buff *dns_skb = NULL;
|
||
struct ipv6hdr *new_iphdr = NULL; /* 要创建的DNS回答报文的IPV6头 */
|
||
struct udphdr *new_udphdr = NULL;
|
||
DNS_HEADER *new_dnshdr = NULL;
|
||
unsigned char *dns_payload = NULL;
|
||
struct dst_entry *dst = NULL;
|
||
|
||
int skb_len = 0;
|
||
int data_len = 0;
|
||
int ret = 0;
|
||
__u16 udp_sum = 0;/* 用于暂存UDP的检验和 */
|
||
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,8) )
|
||
struct flowi fl = {
|
||
.u = {
|
||
.ip6 = {
|
||
.daddr = old_iphdr->saddr,
|
||
},
|
||
},
|
||
};
|
||
#else
|
||
struct flowi fl = {
|
||
/* .oif = 0, */
|
||
.nl_u = {
|
||
.ip6_u = {
|
||
.daddr = old_iphdr->saddr,
|
||
|
||
}
|
||
},
|
||
/* .proto = IPPROTO_UDP,
|
||
.uli_u = {
|
||
.ports = {
|
||
.dport = old_udphdr->source,
|
||
.sport = old_udphdr->dest,
|
||
|
||
},
|
||
},*/
|
||
};
|
||
#endif
|
||
|
||
/* create route info, if not send success, we need release before return */
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,8) )
|
||
dst = ip6_route_output(dev_net(skb->dev), NULL, &(fl.u.ip6));
|
||
#else
|
||
dst = ip6_route_output(dev_net(skb->dev), NULL, &fl);
|
||
#endif
|
||
|
||
if (NULL == dst)
|
||
{
|
||
return;
|
||
}
|
||
|
||
/* calculate the packet length and alloc sk buffer */
|
||
data_len = DNS_DATA_LEN;
|
||
if (query_type == DNS_QUERY_TYPE_AAAA)
|
||
data_len += DNS_RESOURCE_LEN_AAAA - DNS_RESOURCE_LEN_A; //for ipv6 address len
|
||
|
||
skb_len = ETH_HEADER_LEN + IPV6_HDR_LEN + UDP_HEADER_LEN + DNS_HEADER_LEN + data_len;
|
||
dns_skb = alloc_skb( skb_len, GFP_ATOMIC );
|
||
if ( !dns_skb )
|
||
{
|
||
goto fail_releasert6;
|
||
return;
|
||
}
|
||
|
||
/* init the packet info */
|
||
nf_reset( dns_skb );
|
||
dns_skb->mark = 0;
|
||
dns_skb->pkt_type = PACKET_OTHERHOST;
|
||
dns_skb->protocol = htons(ETH_P_IPV6);
|
||
dns_skb->ip_summed = CHECKSUM_NONE;
|
||
dns_skb->priority = 0;
|
||
|
||
skb_dst_set(dns_skb, dst);
|
||
|
||
skb_reserve( dns_skb, ETH_HEADER_LEN );
|
||
|
||
/* create IP6 header */
|
||
new_iphdr = create_ip6_hdr(dns_skb, (struct ipv6hdr *)old_iphdr, data_len + UDP_HEADER_LEN + DNS_HEADER_LEN);
|
||
if (new_iphdr == NULL)
|
||
{
|
||
goto fail_freeskb6;
|
||
}
|
||
|
||
/* create udp header */
|
||
new_udphdr = create_udp_hdr(dns_skb, old_udphdr, data_len + DNS_HEADER_LEN);
|
||
if (new_udphdr == NULL)
|
||
{
|
||
goto fail_freeskb6;
|
||
}
|
||
|
||
/* create dns header */
|
||
new_dnshdr = create_dns_hdr(dns_skb, old_dnshdr);
|
||
if (new_dnshdr == NULL)
|
||
{
|
||
goto fail_freeskb6;
|
||
}
|
||
|
||
/* create dns payload data */
|
||
dns_payload = fill_dns_payload(dns_skb, (struct net_device *)skb->dev, query, query_type, data_len);
|
||
if (NULL == dns_payload)
|
||
{
|
||
goto fail_freeskb6;
|
||
}
|
||
|
||
/* calculate the udp checksum */
|
||
udp_sum = udp_csum_calc(new_iphdr, new_udphdr, DNS_HEADER_LEN + data_len, AF_INET6 );
|
||
new_udphdr->check = htons( udp_sum );
|
||
|
||
/* send dns packet */
|
||
ret = dst_output( dns_skb );
|
||
return;
|
||
fail_freeskb6:
|
||
/* free skb before return */
|
||
kfree_skb(dns_skb);
|
||
fail_releasert6:
|
||
/* free route info before return */
|
||
dst_release(dst);
|
||
|
||
return;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : tp_send_dns_packet()
|
||
* AUTHOR : huangwenzhong <huangwenzhong@tp-link.net>
|
||
* DESCRIPTION : 用于构建和发送DNS应答包
|
||
* INPUT : skb - DNS请求包
|
||
* old_iphdr - DNS请求包的IP头指针
|
||
* old_udphdr - DNS请求包的UDP头指针
|
||
* old_dnshdr - DNS请求包的DNS头指针
|
||
* query_type - 请求的域名的类型,DNS_QUERY_TYPE_A or DNS_QUERY_TYPE_AAAA
|
||
*
|
||
* OUTPUT : N/A
|
||
* RETURN : N/A
|
||
* OTHERS :
|
||
******************************************************************************/
|
||
static void tp_send_dns_packet( struct sk_buff *skb,
|
||
struct iphdr *old_iphdr,
|
||
struct udphdr *old_udphdr,
|
||
DNS_HEADER *old_dnshdr,
|
||
unsigned short query_type)
|
||
{
|
||
|
||
struct sk_buff *dns_skb;
|
||
|
||
int skb_len;
|
||
int data_len;
|
||
int ret = 0;
|
||
|
||
struct iphdr *new_iphdr; /* 要创建的DNS回答报文的IP头 */
|
||
|
||
struct net_device *br0_dev;
|
||
/*added by wangbing for get LAN ip*/
|
||
//struct net_device *skb_dev;/*获取lan端ip:skb->dev->br_port->br->dev->ip_ptr->ifa_list->ifa_address*/
|
||
//struct net_bridge_port *br_port;
|
||
//struct net_bridge *br;
|
||
//struct net_device *br_dev;
|
||
|
||
//struct in_device * in_dev = NULL;
|
||
|
||
struct udphdr *new_udphdr;
|
||
DNS_HEADER *new_dnshdr;
|
||
unsigned char *dns_payload;
|
||
//unsigned char *tmp;
|
||
|
||
//__u16 short_val;
|
||
__u16 udp_sum;/* 用于暂存UDP的检验和 */
|
||
//__u32 int_val;
|
||
|
||
struct rtable *rt;
|
||
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,8) )
|
||
|
||
struct flowi fl = {
|
||
.u = {
|
||
.ip4 = {
|
||
.daddr = old_iphdr->saddr,
|
||
},
|
||
},
|
||
};
|
||
#else
|
||
struct flowi fl = {
|
||
/* .oif = 0, */
|
||
.nl_u = {
|
||
.ip4_u = {
|
||
.daddr = old_iphdr->saddr,
|
||
/* .saddr = in_aton( LAN_IP ), */
|
||
|
||
}
|
||
},
|
||
/* .proto = IPPROTO_UDP,
|
||
.uli_u = {
|
||
.ports = {
|
||
.dport = old_udphdr->source,
|
||
.sport = old_udphdr->dest,
|
||
|
||
},
|
||
},*/
|
||
};
|
||
#endif
|
||
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,8) )
|
||
br0_dev = dev_get_by_name(&init_net, "bridge0");
|
||
rt = ip_route_output_key(dev_net(br0_dev), &(fl.u.ip4));
|
||
if (IS_ERR(rt))
|
||
ret = (int)rt;
|
||
dev_put(br0_dev);
|
||
#elif ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) )
|
||
br0_dev = dev_get_by_name(&init_net, "bridge0");
|
||
ret = ip_route_output_key(dev_net(br0_dev), &rt, &fl);
|
||
dev_put(br0_dev);
|
||
#else
|
||
ret = ip_route_output_key( &rt, &fl );
|
||
#endif
|
||
if (ret)
|
||
{
|
||
printk("ip_route_output_key() failed in file create_tp_dns_skb.c, ret = %d\r\n", ret);
|
||
return;
|
||
}
|
||
|
||
data_len = DNS_DATA_LEN;
|
||
if (DNS_QUERY_TYPE_AAAA == query_type)
|
||
data_len += DNS_RESOURCE_LEN_AAAA - DNS_RESOURCE_LEN_A; //for ipv6 address len
|
||
|
||
skb_len = ETH_HEADER_LEN + old_iphdr->ihl *4 +
|
||
UDP_HEADER_LEN + DNS_HEADER_LEN + data_len;
|
||
|
||
dns_skb = alloc_skb( skb_len, GFP_ATOMIC );
|
||
if ( !dns_skb )
|
||
{
|
||
goto fail_releasert;
|
||
return;
|
||
}
|
||
nf_reset( dns_skb );
|
||
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) )
|
||
dns_skb->mark = 0;
|
||
#else
|
||
dns_skb->nfmark = 0;
|
||
#endif
|
||
|
||
nf_ct_attach(dns_skb, skb); /* needed by NAT */
|
||
dns_skb->pkt_type = PACKET_OTHERHOST;
|
||
dns_skb->protocol = htons( ETH_P_IP);
|
||
dns_skb->ip_summed = CHECKSUM_NONE;
|
||
dns_skb->priority = 0;
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
|
||
skb_dst_set(dns_skb, &rt->dst);
|
||
#elif( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31))
|
||
skb_dst_set(dns_skb, &rt->u.dst);
|
||
#else
|
||
dns_skb->dst = &rt->u.dst;
|
||
#endif
|
||
|
||
skb_reserve( dns_skb, ETH_HEADER_LEN );
|
||
|
||
/* create IP header */
|
||
new_iphdr = create_ip_hdr(dns_skb, old_iphdr, data_len + UDP_HEADER_LEN + DNS_HEADER_LEN );
|
||
if (new_iphdr == NULL)
|
||
{
|
||
goto fail_freeskb;
|
||
}
|
||
|
||
/* dbs: add for kernel 3.4.103 */
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0) )
|
||
ip_select_ident(dns_skb, NULL);
|
||
#elif ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) )
|
||
ip_select_ident(dns_skb, &rt->dst, NULL);
|
||
#elif ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,103) )
|
||
ip_select_ident( dns_skb, NULL );
|
||
#elif ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) )
|
||
ip_select_ident( new_iphdr, &rt->dst, NULL );
|
||
#else
|
||
ip_select_ident( new_iphdr, &rt->u.dst, NULL );/* always 0 */
|
||
#endif
|
||
|
||
/* need calc the ipv4 head checksum here, as the ip_select_ident would change the ip header */
|
||
new_iphdr->check = 0x0000;
|
||
new_iphdr->check = ip_fast_csum( (unsigned char * )new_iphdr, new_iphdr->ihl );
|
||
|
||
/* create udp header */
|
||
new_udphdr = create_udp_hdr(dns_skb, old_udphdr, data_len + DNS_HEADER_LEN);
|
||
if (new_udphdr == NULL)
|
||
{
|
||
goto fail_freeskb;
|
||
}
|
||
|
||
/* create dns header */
|
||
new_dnshdr = create_dns_hdr(dns_skb, old_dnshdr);
|
||
if (new_dnshdr == NULL)
|
||
{
|
||
goto fail_freeskb;
|
||
}
|
||
|
||
/* create dns answer */
|
||
dns_payload = fill_dns_payload(dns_skb, (struct net_device *)skb->dev, query, query_type, data_len);
|
||
if (NULL == dns_payload)
|
||
{
|
||
goto fail_freeskb;
|
||
}
|
||
|
||
udp_sum = udp_csum_calc( new_iphdr, new_udphdr, DNS_HEADER_LEN + data_len,AF_INET);
|
||
new_udphdr->check = htons( udp_sum );
|
||
|
||
dst_output( dns_skb );
|
||
return;
|
||
|
||
fail_freeskb:
|
||
kfree_skb(dns_skb);
|
||
fail_releasert:
|
||
ip_rt_put(rt);
|
||
return;
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
* FUNCTION : tp_dns_handler()
|
||
* AUTHOR : huangwenzhong <huangwenzhong@tp-link.net>
|
||
* DESCRIPTION : 挂载到FORWARD链的钩子函数,
|
||
用于拦截tplinkmodem.net域名请求的包并发出应答包
|
||
* INPUT : N/A
|
||
*
|
||
* OUTPUT : N/A
|
||
* RETURN : N/A
|
||
* OTHERS :
|
||
******************************************************************************/
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) )
|
||
static unsigned int tp_dns_handler( unsigned int hooknum,
|
||
struct sk_buff *skb,
|
||
const struct net_device *in,
|
||
const struct net_device *out,
|
||
int ( *okfn )(struct sk_buff *) )
|
||
{
|
||
struct sk_buff *pskb = skb;
|
||
#else
|
||
|
||
static unsigned int tp_dns_handler( unsigned int hooknum,
|
||
struct sk_buff **skb,
|
||
const struct net_device *in,
|
||
const struct net_device *out,
|
||
int ( *okfn )(struct sk_buff *) )
|
||
{
|
||
struct sk_buff *pskb = *skb;
|
||
#endif
|
||
struct iphdr *iph = NULL;
|
||
struct ipv6hdr *ip6h = NULL;
|
||
struct udphdr *udp_hdr = NULL;
|
||
DNS_HEADER *dns_hdr= NULL;
|
||
unsigned char * dns_payload = NULL;
|
||
unsigned char * tmp = NULL;
|
||
|
||
__u16 query_type;
|
||
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) )
|
||
iph = ip_hdr(pskb);
|
||
ip6h = ipv6_hdr(pskb);
|
||
#else
|
||
iph = pskb->nh.iph;
|
||
ip6h = ipv6_hdr(pskb);
|
||
#endif
|
||
|
||
switch(ntohs(pskb->protocol))
|
||
{
|
||
case ETH_P_IPV6:
|
||
if (NULL != ip6h && 6 == ip6h->version && IPPROTO_UDP == ip6h->nexthdr)
|
||
udp_hdr = (struct udphdr *)((unsigned char *) ip6h + sizeof(struct ipv6hdr));
|
||
else
|
||
return NF_ACCEPT;
|
||
break;
|
||
case ETH_P_IP:
|
||
if ( NULL != iph && 4 == iph->version && IPPROTO_UDP == iph->protocol)
|
||
udp_hdr = ( struct udphdr * )( ( unsigned char * )iph + iph->ihl * 4 );
|
||
else
|
||
return NF_ACCEPT;
|
||
break;
|
||
default:
|
||
return NF_ACCEPT;
|
||
}
|
||
if ( NULL == udp_hdr || (DOMAIN_PORT != (ntohs(udp_hdr->dest)))) //not dns packet?
|
||
{
|
||
return NF_ACCEPT;
|
||
}
|
||
dns_hdr = ( DNS_HEADER * )( ( unsigned char * )udp_hdr + UDP_HEADER_LEN );
|
||
if ( (NULL == dns_hdr) || ( ntohs( dns_hdr->flag ) & DNS_RESPONSE_PACKET )) //not dns query?
|
||
{
|
||
return NF_ACCEPT;
|
||
}
|
||
|
||
dns_payload = ( unsigned char * )( ( unsigned char * )dns_hdr + DNS_HEADER_LEN );
|
||
if ( NULL == dns_payload || 0 != memcmp( dns_payload, query, DNS_QUERY_LEN )) //not the target query?
|
||
{
|
||
return NF_ACCEPT;
|
||
}
|
||
else
|
||
{
|
||
tmp = dns_payload + DNS_QUERY_LEN + 1;
|
||
}
|
||
|
||
memcpy( &query_type, tmp, sizeof( query_type ) );
|
||
query_type = ntohs( query_type );
|
||
|
||
if( DNS_QUERY_TYPE_A != query_type && DNS_QUERY_TYPE_AAAA != query_type )/* is query type not A (or AAAA)? */
|
||
{
|
||
return NF_ACCEPT;
|
||
}
|
||
if (ETH_P_IPV6 == ntohs( pskb->protocol))
|
||
{
|
||
tp_send_dns6_packet(pskb, ip6h, udp_hdr, dns_hdr, query_type);
|
||
}
|
||
else
|
||
{
|
||
tp_send_dns_packet(pskb, iph, udp_hdr, dns_hdr, query_type);
|
||
}
|
||
|
||
return NF_DROP;
|
||
|
||
}
|
||
static struct nf_hook_ops tp_dns_br_hook_ops = {
|
||
.hook = tp_dns_handler,
|
||
.owner = THIS_MODULE,
|
||
.pf = PF_BRIDGE,
|
||
.hooknum = NF_BR_PRE_ROUTING,
|
||
.priority = NF_BR_PRI_FIRST,
|
||
};
|
||
|
||
static struct nf_hook_ops tp_dns_ip_hook_ops = {
|
||
.hook = tp_dns_handler,
|
||
.owner = THIS_MODULE,
|
||
.pf = PF_INET,
|
||
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) )
|
||
.hooknum = NF_INET_PRE_ROUTING,
|
||
#else
|
||
.hooknum = NF_IP_PRE_ROUTING,
|
||
#endif
|
||
.priority = NF_IP_PRI_NAT_DST - 1, /* before DNAT */
|
||
};
|
||
|
||
static struct nf_hook_ops tp_dns_ip6_hook_ops = {
|
||
.hook = tp_dns_handler,
|
||
.owner = THIS_MODULE,
|
||
.pf = PF_INET6,
|
||
.hooknum = NF_INET_PRE_ROUTING,
|
||
.priority = NF_IP6_PRI_FIRST,
|
||
};
|
||
|
||
static int dns_init(void)
|
||
{
|
||
int ret;
|
||
|
||
ret = nf_register_hook( &tp_dns_br_hook_ops );
|
||
if ( ret < 0 )
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
ret = nf_register_hook(&tp_dns_ip_hook_ops);
|
||
if ( ret < 0 )
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
ret = nf_register_hook(&tp_dns_ip6_hook_ops);
|
||
if ( ret < 0 )
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
printk( "dns_init\n");
|
||
printk( "domain_name:%s\n",lan_domain);
|
||
parse_domain();
|
||
#ifdef INCLUDE_DOMAIN_INTERCEPT
|
||
return domain_intercept_init();
|
||
#else
|
||
return 0;
|
||
#endif /*INCLUDE_DOMAIN_INTERCEPT*/
|
||
}
|
||
|
||
static void dns_exit(void)
|
||
{
|
||
|
||
nf_unregister_hook( &tp_dns_br_hook_ops );
|
||
nf_unregister_hook( &tp_dns_ip_hook_ops );
|
||
nf_unregister_hook( &tp_dns_ip6_hook_ops);
|
||
#ifdef INCLUDE_DOMAIN_INTERCEPT
|
||
domain_intercept_exit();
|
||
#endif /*INCLUDE_DOMAIN_INTERCEPT*/
|
||
return;
|
||
}
|
||
module_init( dns_init );
|
||
module_exit( dns_exit );
|
||
|
||
|
||
/* module license 'BSD' will taints kernel, so change it to GPL. by HouXB, 12May11 */
|
||
//MODULE_LICENSE( "BSD" );
|
||
MODULE_LICENSE( "GPL" );
|
||
|
||
MODULE_AUTHOR( "Huang Wenzhong <huangwenzhong@tp-link.net>");
|
||
MODULE_DESCRIPTION( "The default domain name tplinkmodem.net.");
|
||
|
||
/* by HouXB, 12May11 */
|
||
MODULE_ALIAS("tp_domain");
|
||
|
||
|