M7350/tp-proprietary/tp-domain/tp_domain.c
2024-09-09 08:59:52 +00:00

1112 lines
32 KiB
C
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************************
*
* 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");