/****************************************************************************** * * Copyright (c) 2013 TP-LINK Technologies CO.,LTD. * All rights reserved. * * FILE NAME : tp_domain.c * VERSION : 1.0 * DESCRIPTION: 处理路由器页面的域名登录. * * AUTHOR : wangbing * CREATE DATE: 27/02/2013 * * HISTORY : * 2013-02-27 wangbing revised for adsl router. * This file is originally created by huangwenzhong . * * ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) #include <../net/bridge/br_private.h> #endif #include #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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 "); MODULE_DESCRIPTION( "The default domain name tplinkmodem.net."); /* by HouXB, 12May11 */ MODULE_ALIAS("tp_domain");