├── Install_guide ├── Makefile ├── Readme ├── changelog.txt ├── napt66_conntrack.c ├── napt66_conntrack.h ├── napt66_ftp_alg.c ├── napt66_ftp_alg.h ├── napt66_global.h ├── napt66_hash_table.c ├── napt66_hash_table.h ├── napt66_main.c ├── napt66_main.h ├── napt66_nat.c └── napt66_nat.h /Install_guide: -------------------------------------------------------------------------------- 1 | Build a NAPT66 Router with GNU/Linux 2 | 3 | This guide will help you build a NAPT66 router quickly if you have complete knowledge on IPv4 NAT and IPv6. 4 | 5 | 1. First, we have to modify the Linux kernel codes. 6 | 7 | Since kernel team don't think NAPT66 is a good idea, they have made some limitations on IPv6. That is, an IPv6 device couldn't be a router and a host meanwhile. 8 | 9 | The simplest way to cancel this limitation is deleting two lines in 'net/ipv6/ip6_output.c' and rebuilding the kernel. 10 | 11 | /* if (net->ipv6.devconf_all->forwarding == 0)*/ 12 | /* goto error;*/ 13 | 14 | 15 | 16 | 2. Build the NAPT66 module and install it. 17 | 18 | Download the codes from SourceForge.net or our site napt66.buptcs.cn. 19 | # make 20 | # insmod napt66.ko wan_if=eth0 21 | 22 | The 'eth0' is an interface connecting the external IPv6 network (e.g. provided by an ISP). Next, set an IPv6 address 'fc00:0101:0101::1' (just like '192.168.1.1' in IPv4) on 'eth1', the interface connecting the internal IPv6 network. 23 | 24 | The device can do NAPT66 now but we have to manually edit the IPv6 connection on each host. Actually Radvd and Dnsmasq can help us do that. 25 | 26 | 27 | 28 | 3. Install Radvd. 29 | 30 | IPv6 has involved two schemes to automatically configure hosts: the stateful one DHCPv6, and the stateless one Router Advertisement. Since the stateless one is more compatible, we had better turn to Radvd. 31 | 32 | To cooperate the modification of Linux kernel, we have to modify the codes of Radvd too. Delete four lines in 'radvd-1.6/radvd.c'. 33 | 34 | int 35 | check_ip6_forwarding(void) 36 | { 37 | ...... 38 | // if (value != 1) { 39 | // flog(LOG_DEBUG, "IPv6 forwarding setting is: %u, should be 1", value); 40 | // return(-1); 41 | // } 42 | ...... 43 | } 44 | 45 | Then build, install and configure Radvd. You can take this as a reference. 46 | 47 | #/etc/radvd.conf 48 | interface eth1 49 | { 50 | AdvSendAdvert on; 51 | MinRtrAdvInterval 5; 52 | MaxRtrAdvInterval 10; 53 | #以下两个参数就是以前提到的M和O标记,作用是通知内网计算机网络的地址配置方式 54 | AdvManagedFlag off; 55 | AdvOtherConfigFlag off; 56 | AdvDefaultPreference high; 57 | #前缀信息 58 | prefix fc00:0101:0101::/64 59 | { 60 | AdvOnLink on; 61 | AdvAutonomous on; 62 | AdvRouterAddr on; 63 | }; 64 | #DNS信息 65 | RDNSS fc00:0101:0101::1 66 | { 67 | AdvRDNSSPreference 15; 68 | AdvRDNSSOpen on; 69 | }; 70 | }; 71 | 72 | 73 | 4. Install Dnsmasq. 74 | Dnsmasq is a DNS forwarder for NAT firewalls. You have to Install Dnsmasq since we have define DNS server as 'fc00:0101:0101::1' in 'radvd.conf'. The newest version (maybe dndnsmasq-2.56test18 and later) supports IPv6 well. Build and install Dnsmasq now. 75 | 76 | In addition, an advanced usage of Dnsmasq can help us redefine the records of domains. 77 | 78 | For example, Dnsmasq will return a specific AAAA record but forward the A record query to the external DNS server due to the configuration below. 79 | 80 | #/etc/dnsmasq.conf 81 | address=/www.youtube.com/2404:6800:8005::65 82 | server=/www.youtube.com/# 83 | 84 | If your NAPT6 router still doesn't work, contact me please. 85 | 86 | If you have no time to configure NAPT66, you can use a firmware provided by us. The firmware is NAPT66-ready for Broadcom BCM63xx device (e.g. Shanghai Bell RG100A). Download it from SourceForge.net. 87 | 88 | 89 | Weilin Xu 90 | Beijing Univ. of Posts & Telecom. 91 | Feb 23, 2011 92 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile under 2.6.25 2 | ifneq ($(KERNELRELEASE),) 3 | #kbuild syntax. dependency relationshsip of files and target modules are listed here. 4 | obj-m := napt66.o 5 | napt66-objs := napt66_main.o napt66_conntrack.o napt66_nat.o napt66_hash_table.o napt66_ftp_alg.o 6 | else 7 | PWD := $(shell pwd) 8 | KVER ?= $(shell uname -r) 9 | KDIR := /lib/modules/$(KVER)/build 10 | all: 11 | $(MAKE) -C $(KDIR) M=$(PWD) modules 12 | clean: 13 | rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.symvers *.order 14 | endif 15 | -------------------------------------------------------------------------------- /Readme: -------------------------------------------------------------------------------- 1 | Introduction 2 | IPv6-IPv6 Network Address Port Translation (NAPT66) is a stateful IPv6 NAT mechanism. Like IPv4 NAT, NAPT66 technique makes several hosts share a public IPv6 address. As a result, NAPT66 helps to hide the private network topology and promote network security. 3 | 4 | NAPT66 based on GNU/Linux is implemented in kernel space, which provides satisfied performance and portability. It has been ported to several open-source router firmware (e.g. OpenWrt) so that it can be run on low-end, commodity hardware (e.g. BCM63xx platform). 5 | 6 | NAPT66 should be installed on a boundary router situated between two IPv6 networks. It performs stateful packet translation between internal IPv6 hosts and external IPv6 hosts. 7 | NAPT66 uses Application Level Gateways (ALGs) and DNS Proxy to deal with complex applications (like active FTP and DNS). More applications can travels NAPT66 in the future. 8 | 9 | Tested Protocols 10 | ·HTTP 11 | ·MMS 12 | ·FTP - active and passive 13 | ·Telnet 14 | ·ICMPv6 - echo request, echo reply,error messages(Destination Unreachable、Packet Too Big、Time Exceeded、Parameter Problem) 15 | 16 | Limitations 17 | In some certain scenarios, NAPT66 is unacceptable for these limitations. 18 | ·Breaking the end-to-end model 19 | Several traditional NAT traversal techniques (e.g. ALGs, UPnP) may helps to solve the problem. 20 | ·No fragmentation 21 | Avoiding fragmentation is one of the principles of IPv6. NAPT66 don’t support IPv6 fragmentation for the moment. 22 | 23 | Members 24 | Weilin Xu, Yigang Yang, Huiting Liu 25 | They are from Grade 2008, School of Computer, Beijing Univ. of Posts & Telecom. 26 | 27 | Acknowledgements 28 | This work is supported by the Research Innovation Fund for College Students of Beijing University of Posts and Telecommunications (Grant No. 101104537) and guided by Lecturer Hua Zhang from Institute of Network Technology, BUPT. 29 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | 2011.4.10 2 | fix a bug about TCP checksum 3 | 4 | NAPT66_v1.1 5 | fix some bugs about FTP-ALG 6 | -------------------------------------------------------------------------------- /napt66_conntrack.c: -------------------------------------------------------------------------------- 1 | #include "napt66_conntrack.h" 2 | 3 | #define MAX_ID 60000 4 | #define MIN_ID 2048 5 | 6 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) 7 | #define LINUX2635 8 | #endif 9 | 10 | extern char *wan_if; 11 | extern struct in6_addr wan_ipv6; 12 | extern int hash_add_entry(struct conn_entry* p_entry); 13 | extern struct conn_entry* hash_search_ct(int direc,struct conn_entry* p_entry); 14 | extern long time(void* ptr); 15 | extern bool inet6_addr_flag; 16 | 17 | int in_cksum(u_int16_t *addr, int len) 18 | { 19 | int sum; 20 | sum = 0; 21 | 22 | while (len > 1){ 23 | sum += *addr++; 24 | len -= 2; 25 | } 26 | if (len == 1){ 27 | sum += htons(*(unsigned char *)addr << 8); 28 | } 29 | 30 | return (sum); 31 | } 32 | 33 | 34 | int find_conflict(struct conn_entry* pkt_entry) 35 | { 36 | struct conn_entry* p_entry; 37 | //查一下wan端口是否已被占用。虽然新建连接是在snat中完成的,但查冲突的参数是rsnat 38 | p_entry = hash_search_ct(RSNAT,pkt_entry); 39 | 40 | if(p_entry == NULL) 41 | return 0; 42 | else 43 | return 1; 44 | } 45 | 46 | int get_random_id(void){ 47 | u16 rnd; 48 | do{ 49 | get_random_bytes(&rnd,sizeof(rnd)); 50 | }while(rnd < MIN_ID || rnd > MAX_ID); 51 | 52 | return rnd; 53 | } 54 | 55 | //获得一个可用表项,包含了可用的WAN信息 56 | struct conn_entry* get_free_ct(struct conn_entry* pkt_entry) 57 | { 58 | int status; 59 | u_int32_t sub_sum = 0; 60 | struct conn_entry* p_new_entry; 61 | 62 | pkt_entry->wan_ipv6 = wan_ipv6; 63 | 64 | //在连接数比较少时,内网id已被占用的机率很小。 65 | pkt_entry->wan_id = pkt_entry->lan_id; 66 | 67 | while(ntohs(pkt_entry->wan_id) < MIN_ID){ 68 | pkt_entry->wan_id = get_random_id(); 69 | } 70 | while(find_conflict(pkt_entry)){ 71 | pkt_entry->wan_id = get_random_id(); 72 | } 73 | //获得了不冲突的L4PROTO ID 74 | 75 | //获取校验和差值,填充表项 76 | switch(pkt_entry->proto) 77 | { 78 | case IPPROTO_TCP: 79 | case IPPROTO_UDP: 80 | sub_sum = in_cksum((u_int16_t *)&pkt_entry->wan_ipv6,16) + in_cksum((u_int16_t *)&pkt_entry->wan_port,2) 81 | -in_cksum((u_int16_t *)&pkt_entry->lan_ipv6,16) - in_cksum((u_int16_t *)&pkt_entry->lan_port,2); 82 | break; 83 | case IPPROTO_ICMPV6: 84 | sub_sum = in_cksum((u_int16_t *)&pkt_entry->wan_ipv6,16) + in_cksum((u_int16_t *)&pkt_entry->wan_id,2) 85 | -in_cksum((u_int16_t *)&pkt_entry->lan_ipv6,16) - in_cksum((u_int16_t *)&pkt_entry->lan_id,2); 86 | break; 87 | default: 88 | break; 89 | } 90 | 91 | if(sub_sum >> 31 != 0){//负数 92 | sub_sum--; 93 | } 94 | 95 | while(sub_sum >> 16){ 96 | sub_sum = (sub_sum & 0xffff) + (sub_sum >> 16); 97 | } 98 | pkt_entry->sub_sum = sub_sum; 99 | pkt_entry->eprt_len_change = 0; 100 | pkt_entry->sum_change = 0; 101 | 102 | //kcalloc(1,sizeof(struct in6_addr),GFP_KERNEL);//元素个数,元素大小,内存类型 103 | p_new_entry = (struct conn_entry*)kcalloc(1,sizeof(struct conn_entry),GFP_KERNEL); 104 | memcpy(p_new_entry,pkt_entry,sizeof(struct conn_entry)); 105 | 106 | status = hash_add_entry(p_new_entry); 107 | if(status == 0){ 108 | printk(KERN_INFO "hash_add_entry(p_new_entry) failed\n"); 109 | return NULL; 110 | } 111 | 112 | return p_new_entry; 113 | } 114 | 115 | //根据报文方向填充连接信息。成功返回1,不成功返回0 116 | int conn_init(struct sk_buff *skb,struct conn_entry* entry,int direc) 117 | { 118 | struct ipv6hdr* ip6_h = ipv6_hdr(skb); 119 | struct ipv6hdr* pl_ipv6_header; 120 | struct ipv6_opt_hdr* ip6e_h; 121 | struct udphdr* udp_h; 122 | struct tcphdr* pl_tcp_header; 123 | struct icmp6hdr* icmpv6_h; 124 | struct icmp6hdr* pl_icmpv6_header; 125 | u_int8_t proto; 126 | u_int16_t hlen; 127 | 128 | entry->time = 0; 129 | 130 | hlen = sizeof(struct ipv6hdr); 131 | proto = ip6_h->nexthdr;//uint8_t ip6_un1_nxt; 132 | 133 | /*关键一步:对分片数据包进行重组*/ 134 | if (0 != skb_linearize(skb)) { 135 | return NF_ACCEPT; 136 | } 137 | 138 | 139 | while(1){ 140 | switch (proto) { 141 | case IPPROTO_HOPOPTS: 142 | case IPPROTO_ROUTING: 143 | case IPPROTO_DSTOPTS: 144 | ip6e_h = (struct ipv6_opt_hdr*)((char *)ip6_h + hlen); 145 | 146 | proto = ip6e_h->nexthdr; 147 | hlen += ip6e_h->hdrlen * 8;//u_int8_t ip6e_len; 148 | printk(KERN_INFO "发现扩展报头\n"); 149 | break; 150 | 151 | case IPPROTO_FRAGMENT: 152 | //printf("Find fragment.\n"); 153 | return 0; 154 | break; 155 | 156 | case IPPROTO_UDP://报头端口位置相同 157 | case IPPROTO_TCP: 158 | entry->proto = proto; 159 | udp_h = (struct udphdr*)((char *)ip6_h + hlen); 160 | entry->proto_offset = hlen;//记下四层协议报头的偏移值(基于IPv6报头) 161 | 162 | if(direc == SNAT){//填充LAN信息 163 | entry->lan_ipv6 = ip6_h->saddr; 164 | #ifdef __FAVOR_BSD 165 | entry->lan_port = udp_h->uh_sport; 166 | entry->dport = udp_h->uh_dport; 167 | #else 168 | entry->lan_port = udp_h->source; 169 | entry->dport = udp_h->dest; 170 | #endif 171 | 172 | } 173 | else{//填充WAN信息 174 | entry->wan_ipv6 = ip6_h->daddr; 175 | #ifdef __FAVOR_BSD 176 | entry->wan_port = udp_h->uh_dport; 177 | #else 178 | entry->wan_port = udp_h->dest; 179 | #endif 180 | } 181 | 182 | return 1; 183 | break; 184 | case IPPROTO_ICMPV6: 185 | entry->proto = proto; 186 | 187 | icmpv6_h = (struct icmp6hdr*)((char *)ip6_h + hlen); 188 | entry->proto_offset = hlen;//记下四层协议报头的偏移值(基于IPv6报头) 189 | if((icmpv6_h->icmp6_type) != ICMPV6_ECHO_REQUEST 190 | && (icmpv6_h->icmp6_type) != ICMPV6_ECHO_REPLY 191 | && (icmpv6_h->icmp6_type) != ICMPV6_DEST_UNREACH 192 | && (icmpv6_h->icmp6_type) != ICMPV6_PKT_TOOBIG 193 | && (icmpv6_h->icmp6_type) != ICMPV6_TIME_EXCEED 194 | && (icmpv6_h->icmp6_type) != ICMPV6_PARAMPROB){ 195 | //分别处理四种错误报文 196 | //printf("ICMPv6 type can't be deal with.\n"); 197 | return 0; 198 | } 199 | 200 | if(direc == SNAT){ 201 | entry->lan_ipv6 = ip6_h->saddr; 202 | entry->lan_id = icmpv6_h->icmp6_identifier; 203 | } 204 | else{ 205 | 206 | /*针对上面的类型,决定怎么填入id字段*/ 207 | /*echo正常填入,否则填入负载中的id字段*/ 208 | /*问题:如果是tcp等其他协议,对表项设置为tcp*/ 209 | 210 | /*正确报文,正常传输*/ 211 | if(icmpv6_h->icmp6_type == ICMPV6_ECHO_REQUEST 212 | || icmpv6_h->icmp6_type == ICMPV6_ECHO_REPLY){ 213 | entry->wan_ipv6 = ip6_h->daddr; 214 | entry->wan_id = icmpv6_h->icmp6_identifier;//id 215 | } 216 | /*负载中如果是ICMPv6,则将负载中的源地址和id填入wan表项*/ 217 | /*负载如果是tcp/udp,则将负载中的源地址和端口填入wan表项*/ 218 | else { 219 | pl_ipv6_header = (struct ipv6hdr *)((char *)icmpv6_h + 8); 220 | if(pl_ipv6_header->nexthdr == IPPROTO_ICMPV6){ 221 | pl_icmpv6_header = (struct icmp6hdr*)((char *)pl_ipv6_header + 40); 222 | entry->wan_ipv6 = pl_ipv6_header->saddr; 223 | entry->wan_id = pl_icmpv6_header->icmp6_identifier; 224 | } 225 | else if(pl_ipv6_header->nexthdr == IPPROTO_TCP){ 226 | pl_tcp_header = (struct tcphdr*)((char *)pl_ipv6_header + 40); 227 | entry->wan_ipv6 = pl_ipv6_header->saddr; 228 | entry->wan_port = pl_tcp_header->source; 229 | entry->proto = IPPROTO_TCP; 230 | } 231 | else if(pl_ipv6_header->nexthdr == IPPROTO_UDP){ 232 | pl_tcp_header = (struct tcphdr*)((char *)pl_ipv6_header + 40); 233 | entry->wan_ipv6 = pl_ipv6_header->saddr; 234 | entry->wan_port = pl_tcp_header->source; 235 | entry->proto = IPPROTO_UDP; 236 | } 237 | else { 238 | return 0; 239 | } 240 | } 241 | } 242 | 243 | return 1; 244 | break; 245 | default: 246 | //printf("Unknown protocol.\n"); 247 | return 0; 248 | break; 249 | }//switch(proto) 250 | 251 | }//while(1) 252 | } 253 | 254 | /* 255 | 256 | */ 257 | 258 | #ifdef LINUX2635 259 | /*kernel>=2.6.35*/ 260 | /*Modified by woshiyuhao0819@gmail.com to support the kernel 2.6.35 or upper*/ 261 | int get_ip6_by_name(char* if_name,struct in6_addr* p_ipv6) 262 | { 263 | struct net_device *dev; 264 | struct net *net = NULL; 265 | struct socket *sock; 266 | struct inet6_dev *in_dev6; 267 | struct inet6_ifaddr *ifa6 = NULL; 268 | struct list_head* list_head_ipv6 = NULL; 269 | struct list_head* temp; 270 | sock_create_kern(PF_INET6, SOCK_DGRAM, 0,&sock); 271 | net = sock_net((const struct sock *)sock->sk); 272 | dev = dev_get_by_name(net,if_name); 273 | 274 | in_dev6 = (struct inet6_dev *)dev->ip6_ptr; 275 | 276 | while (in_dev6){ 277 | temp=&(in_dev6->addr_list); 278 | list_for_each(list_head_ipv6,temp) 279 | { 280 | ifa6=list_entry(list_head_ipv6,struct inet6_ifaddr,if_list); 281 | if(ifa6) 282 | if(ifa6->scope == IPV6_ADDR_ANY){ 283 | (*p_ipv6) = ifa6->addr;//addr即为eth0接口的v6地址 284 | dev_put(dev); 285 | return 1; 286 | } 287 | } 288 | in_dev6 = in_dev6->next; 289 | } 290 | 291 | dev_put(dev); 292 | return 0; 293 | } 294 | #else 295 | /*other kernel*/ 296 | int get_ip6_by_name(char* if_name,struct in6_addr* p_ipv6) 297 | { 298 | struct net_device *dev; 299 | struct net *net = NULL; 300 | struct socket *sock; 301 | struct inet6_dev *in_dev6; 302 | struct inet6_ifaddr *ifa6 = NULL; 303 | 304 | sock_create_kern(PF_INET6, SOCK_DGRAM, 0,&sock); 305 | net = sock_net((const struct sock *)sock->sk); 306 | dev = dev_get_by_name(net,if_name); 307 | 308 | in_dev6 = (struct inet6_dev *)dev->ip6_ptr; 309 | 310 | while (in_dev6){ 311 | ifa6 = in_dev6->addr_list; 312 | while (ifa6){ 313 | if(ifa6->scope == IPV6_ADDR_ANY){ 314 | (*p_ipv6) = ifa6->addr;//addr即为eth0接口的v6地址 315 | dev_put(dev); 316 | return 1; 317 | } 318 | ifa6 = ifa6->if_next; 319 | } 320 | in_dev6 = in_dev6->next; 321 | } 322 | dev_put(dev); 323 | return 0; 324 | } 325 | #endif 326 | 327 | //获取连接记录返回1 328 | int get_entry(struct sk_buff *skb,struct conn_entry** pp_entry,int direc) 329 | { 330 | struct conn_entry pkt_entry;//临时变量 331 | struct conn_entry* p_entry; 332 | int status; 333 | *pp_entry = NULL; 334 | 335 | if(inet6_addr_flag == false){ 336 | status = get_ip6_by_name(wan_if,&wan_ipv6); 337 | if(status <= 0){ 338 | printk(KERN_INFO "get_ip6_by_name() failed.\n"); 339 | return 0; 340 | } 341 | printk(KERN_INFO "get_ip6_by_name() succeed.\n"); 342 | inet6_addr_flag = true; 343 | } 344 | 345 | status = conn_init(skb,&pkt_entry,direc); 346 | if(status == 0) 347 | return 0;//不能处理的ICMPV6包类型或其他类型,Accept,让协议栈自己处理。 348 | 349 | p_entry = hash_search_ct(direc,&pkt_entry); 350 | 351 | if(direc == SNAT){ 352 | if(p_entry == NULL){//SNAT找不到连接,新建一个 353 | p_entry = get_free_ct(&pkt_entry); 354 | if(p_entry == NULL){//SNAT获取新连接失败 355 | //printf("SNAT获取新连接失败\n"); 356 | return -1;//连接找不到可用端口外发数据,这种情况很难发生。 357 | } 358 | } 359 | } 360 | else{//RSNAT 361 | if(p_entry == NULL){//RSNAT找不到连接,丢弃包 362 | //printf("RSNAT找不到连接,丢弃包\n"); 363 | return -1;//数据包连接无记录,不被接受。 364 | } 365 | } 366 | //time()的内核API 367 | p_entry->time = time(NULL); 368 | *pp_entry = p_entry; 369 | return 1; 370 | } 371 | -------------------------------------------------------------------------------- /napt66_conntrack.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #include "napt66_global.h" 16 | 17 | int get_entry(struct sk_buff *skb,struct conn_entry** pp_entry,int direc); 18 | int in_cksum(u_int16_t *addr, int len); 19 | -------------------------------------------------------------------------------- /napt66_ftp_alg.c: -------------------------------------------------------------------------------- 1 | #include "napt66_ftp_alg.h" 2 | 3 | 4 | #ifdef SPRINTF_CHAR 5 | # define SPRINTF(x) strlen(sprintf x) 6 | #else 7 | # define SPRINTF(x) ((size_t)sprintf x) 8 | #endif 9 | 10 | #define NS_INT16SZ 2 /* #/bytes of data in a u_int16_t */ 11 | 12 | extern long time(void* ptr); 13 | extern struct conn_entry* hash_search_ct(int direc,struct conn_entry* p_entry); 14 | extern struct conn_entry* get_free_ct(struct conn_entry* pkt_entry); 15 | 16 | 17 | static int getbits(const char *src, int *bitsp) 18 | { 19 | static const char digits[] = "0123456789"; 20 | int n; 21 | int val; 22 | char ch; 23 | 24 | val = 0; 25 | n = 0; 26 | while ((ch = *src++) != '\0') { 27 | const char *pch; 28 | 29 | pch = strchr(digits, ch); 30 | if (pch != NULL) { 31 | if (n++ != 0 && val == 0) /* no leading zeros */ 32 | return 0; 33 | val *= 10; 34 | val += (pch - digits); 35 | if (val > 128) /* range */ 36 | return 0; 37 | continue; 38 | } 39 | return 0; 40 | } 41 | if (n == 0) 42 | return 0; 43 | *bitsp = val; 44 | return 1; 45 | } 46 | 47 | 48 | /*将字符串类型IPv6地址更改为in6_addr结构体类型*/ 49 | static int inet_net_pton_ipv6(const char *src,struct in6_addr *dst, size_t size) 50 | { 51 | static const char xdigits_l[] = "0123456789abcdef", 52 | xdigits_u[] = "0123456789ABCDEF"; 53 | u_char tmp[INET6_ADDRSTRLEN], *tp, *endp, *colonp; 54 | const char *xdigits, *curtok; 55 | int ch, saw_xdigit; 56 | u_int val; 57 | int digits; 58 | int bits; 59 | size_t bytes; 60 | int words; 61 | int ipv4; 62 | 63 | memset((tp = tmp), '\0', INET6_ADDRSTRLEN); 64 | endp = tp + INET6_ADDRSTRLEN; 65 | colonp = NULL; 66 | /* Leading :: requires some special handling. */ 67 | if (*src == ':') 68 | if (*++src != ':') 69 | goto enoent; 70 | curtok = src; 71 | saw_xdigit = 0; 72 | val = 0; 73 | digits = 0; 74 | bits = -1; 75 | ipv4 = 0; 76 | while ((ch = *src++) != '\0') { 77 | const char *pch; 78 | 79 | if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) 80 | pch = strchr((xdigits = xdigits_u), ch); 81 | if (pch != NULL) { 82 | val <<= 4; 83 | val |= (pch - xdigits); 84 | if (++digits > 4) 85 | goto enoent; 86 | saw_xdigit = 1; 87 | continue; 88 | } 89 | if (ch == ':') { 90 | curtok = src; 91 | if (!saw_xdigit) { 92 | if (colonp) 93 | goto enoent; 94 | colonp = tp; 95 | continue; 96 | } else if (*src == '\0') 97 | goto enoent; 98 | if (tp + NS_INT16SZ > endp) 99 | return (0); 100 | *tp++ = (u_char) (val >> 8) & 0xff; 101 | *tp++ = (u_char) val & 0xff; 102 | saw_xdigit = 0; 103 | digits = 0; 104 | val = 0; 105 | continue; 106 | } 107 | if (ch == '.' && ((tp + INET6_ADDRSTRLEN) <= endp)) { 108 | tp += INET6_ADDRSTRLEN; 109 | saw_xdigit = 0; 110 | ipv4 = 1; 111 | break; /* '\0' was seen by inet_pton4(). */ 112 | } 113 | if (ch == '/' && getbits(src, &bits) > 0) 114 | break; 115 | goto enoent; 116 | } 117 | if (saw_xdigit) { 118 | if (tp + NS_INT16SZ > endp) 119 | goto enoent; 120 | *tp++ = (u_char) (val >> 8) & 0xff; 121 | *tp++ = (u_char) val & 0xff; 122 | } 123 | if (bits == -1) 124 | bits = 128; 125 | 126 | words = (bits + 15) / 16; 127 | if (words < 2) 128 | words = 2; 129 | if (ipv4) 130 | words = 8; 131 | endp = tmp + 2 * words; 132 | 133 | if (colonp != NULL) { 134 | const int n = tp - colonp; 135 | int i; 136 | 137 | if (tp == endp) 138 | goto enoent; 139 | for (i = 1; i <= n; i++) { 140 | endp[- i] = colonp[n - i]; 141 | colonp[n - i] = 0; 142 | } 143 | tp = endp; 144 | } 145 | if (tp != endp) 146 | goto enoent; 147 | 148 | bytes = (bits + 7) / 8; 149 | if (bytes > size) 150 | goto emsgsize; 151 | memcpy(dst, tmp, bytes); 152 | return (bits); 153 | 154 | enoent: 155 | return (-1); 156 | 157 | emsgsize: 158 | return (-1); 159 | } 160 | 161 | /*将in6_addr结构体类型IPv6地址更改为字符串类型*/ 162 | static char *inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) 163 | { 164 | u_int m; 165 | int b; 166 | int p; 167 | int zero_s, zero_l, tmp_zero_s, tmp_zero_l; 168 | int i; 169 | int is_ipv4 = 0; 170 | unsigned char inbuf[16]; 171 | char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; 172 | char *cp; 173 | int words; 174 | u_char *s; 175 | 176 | cp = outbuf; 177 | 178 | if(bits == 0){ 179 | *cp++ = ':'; 180 | *cp++ = ':'; 181 | *cp = '\0'; 182 | } 183 | else{ 184 | /* Copy src to private buffer. Zero host part. */ 185 | p = (bits + 7) / 8; 186 | memcpy(inbuf, src, p); 187 | memset(inbuf + p, 0, 16 - p); 188 | b = bits % 8; 189 | if (b != 0) { 190 | m = ~0 << (8 - b); 191 | inbuf[p-1] &= m; 192 | } 193 | 194 | s = inbuf; 195 | 196 | /* how many words need to be displayed in output */ 197 | words = (bits + 15) / 16; 198 | if(words == 1) 199 | words = 2; 200 | 201 | /* Find the longest substring of zero's */ 202 | zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0; 203 | for (i = 0; i < (words * 2); i += 2) { 204 | if ((s[i] | s[i+1]) == 0) { 205 | if (tmp_zero_l == 0) 206 | tmp_zero_s = i / 2; 207 | tmp_zero_l++; 208 | } 209 | else{ 210 | if (tmp_zero_l && zero_l < tmp_zero_l) { 211 | zero_s = tmp_zero_s; 212 | zero_l = tmp_zero_l; 213 | tmp_zero_l = 0; 214 | } 215 | } 216 | } 217 | 218 | if (tmp_zero_l && zero_l < tmp_zero_l) { 219 | zero_s = tmp_zero_s; 220 | zero_l = tmp_zero_l; 221 | } 222 | 223 | if (zero_l != words && zero_s == 0 && ((zero_l == 6) || 224 | ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) || 225 | ((zero_l == 7 && s[14] != 0 && s[15] != 1))))) 226 | is_ipv4 = 1; 227 | 228 | /* Format whole words. */ 229 | for (p = 0; p < words; p++) { 230 | if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) { 231 | /* Time to skip some zeros */ 232 | if (p == zero_s) 233 | *cp++ = ':'; 234 | if (p == words - 1) 235 | *cp++ = ':'; 236 | s++; 237 | s++; 238 | continue; 239 | } 240 | 241 | if (is_ipv4 && p > 5 ) { 242 | *cp++ = (p == 6) ? ':' : '.'; 243 | cp += SPRINTF((cp, "%u", *s++)); 244 | /* we can potentially drop the last octet */ 245 | if (p != 7 || bits > 120) { 246 | *cp++ = '.'; 247 | cp += SPRINTF((cp, "%u", *s++)); 248 | } 249 | } 250 | else{ 251 | if(cp != outbuf) 252 | *cp++ = ':'; 253 | cp += SPRINTF((cp, "%x", *s * 256 + s[1])); 254 | s += 2; 255 | } 256 | } 257 | } 258 | 259 | if (strlen(outbuf) + 1 > size) 260 | goto emsgsize; 261 | strcpy(dst, outbuf); 262 | 263 | return (dst); 264 | 265 | emsgsize: 266 | return (NULL); 267 | } 268 | 269 | /*对EPRT命令的提取分析以及修改*/ 270 | int analysis_eprt(struct sk_buff *skb,struct conn_entry *entry) 271 | { 272 | struct ipv6hdr* ipv6_header; 273 | struct tcphdr* tcp_header; 274 | u_int16_t eprt_old_port = 0; 275 | struct in6_addr eprt_old_addr; 276 | char ftp_ptr[200]; 277 | 278 | 279 | struct conn_entry new_entry; 280 | struct conn_entry* n_entry; 281 | 282 | char eprt_new_command[100]; 283 | char eprt_new_addr[INET6_ADDRSTRLEN]; 284 | char eprt_new_port[10]; 285 | 286 | u_int16_t eprt_old_len; 287 | u_int16_t eprt_new_len; 288 | u_int16_t temp_port; 289 | 290 | int i = 0,j = 0,k = 0; 291 | char port[10]; 292 | char ip6_address[INET6_ADDRSTRLEN] = "0"; 293 | 294 | ipv6_header = ipv6_hdr(skb); 295 | tcp_header = (struct tcphdr*)((char*)ipv6_header + entry->proto_offset); 296 | 297 | eprt_old_len = ntohs(ipv6_header->payload_len) - (int)(tcp_header->doff * 4); 298 | 299 | /*是否存在FTP命令*/ 300 | if(eprt_old_len > 0){ 301 | memcpy(ftp_ptr,(unsigned char *)tcp_header + (tcp_header->doff * 4),eprt_old_len); 302 | 303 | /*如果不是EPRT命令,则返回0*/ 304 | if(strncmp(ftp_ptr,"EPRT",4) != 0) 305 | return 0; 306 | /*提取EPRT命令*/ 307 | else { 308 | //printk("eprt cmd before nat is:%s\n",ftp_ptr); 309 | i = 8; 310 | while(ftp_ptr[i] != '|'){ 311 | ip6_address[j] = ftp_ptr[i]; 312 | j++; 313 | i++; 314 | } 315 | i++; 316 | while(ftp_ptr[i] != '|'){ 317 | port[k] = ftp_ptr[i]; 318 | k++; 319 | i++; 320 | } 321 | 322 | /*将字符串地址改为主机字节序,并且保存端口号 即pton*/ 323 | inet_net_pton_ipv6(ip6_address,&eprt_old_addr,sizeof(struct in6_addr)); 324 | /*将字符型端口号转换为长整数*/ 325 | eprt_old_port = simple_strtol(port,NULL,eprt_old_port); 326 | } 327 | } 328 | else { 329 | /*不存在FTP命令*/ 330 | return 2; 331 | } 332 | 333 | /*通过分析的EPRT命令建立新的连接表项,处理之后由服务器发起的数据连接*/ 334 | new_entry.proto = IPPROTO_TCP; 335 | new_entry.lan_ipv6 = eprt_old_addr; 336 | new_entry.lan_port = htons(eprt_old_port); 337 | 338 | n_entry = hash_search_ct(SNAT,&new_entry); 339 | if(n_entry == NULL){ 340 | n_entry = get_free_ct(&new_entry); 341 | } 342 | 343 | n_entry->proto_offset = 40; 344 | n_entry->time = time(NULL); 345 | 346 | /* 347 | 将端口号由整数类型转换为字符串形式 348 | 在转换为字符串之前需要将n_entry表项中的网络字节序转换为主机字节序 349 | */ 350 | temp_port = ntohs(n_entry->wan_port); 351 | //printk("entry wan port is %d\n",n_entry->wan_port); 352 | 353 | /*ntop函数结果为相应的主机序字符串,所以之前并不需要额外的字节序转换*/ 354 | inet_net_ntop_ipv6((char *)(&(n_entry->wan_ipv6)),128,eprt_new_addr,128); 355 | snprintf(eprt_new_port,sizeof(eprt_new_port),"%d",temp_port); 356 | 357 | eprt_new_len = sprintf(eprt_new_command,"EPRT |2|%s|%s|\r\n",eprt_new_addr,eprt_new_port); 358 | 359 | /*保存每次EPRT长度变化的累计值*/ 360 | entry->sum_change += entry->eprt_len_change; 361 | entry->eprt_len_change = eprt_new_len - eprt_old_len; 362 | 363 | //printk("eprt len change is %d\n",entry->eprt_len_change); 364 | 365 | if(entry->eprt_len_change > 0){ 366 | /*skb_put和skb_trim函数对skb长度进行调整*/ 367 | skb_put(skb,entry->eprt_len_change); 368 | } 369 | else if(entry->eprt_len_change < 0){ 370 | skb_trim(skb,skb->len + entry->eprt_len_change); 371 | } 372 | 373 | /*构造新的EPRT命令报文*/ 374 | memcpy((unsigned char *)tcp_header + (tcp_header->doff * 4),eprt_new_command,eprt_new_len); 375 | 376 | // printk("new eprt cmd is:%s\n",eprt_new_command); 377 | return 1; 378 | 379 | } 380 | 381 | -------------------------------------------------------------------------------- /napt66_ftp_alg.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "napt66_global.h" 8 | 9 | int analysis_eprt(struct sk_buff *skb,struct conn_entry *entry); 10 | -------------------------------------------------------------------------------- /napt66_global.h: -------------------------------------------------------------------------------- 1 | #define SNAT 1 2 | #define RSNAT 0 3 | 4 | #define HASHRANGE 3540 5 | #define HASHNUMBER 3533 6 | #define CLEANUP_TIME 20 7 | 8 | struct hash_entry; 9 | 10 | struct conn_entry{ 11 | u_int8_t proto; 12 | int proto_offset; 13 | struct hash_entry* source;//引用该连接项在source_table的hash链节 14 | struct hash_entry* ipproto;//引用该连接项在ipproto_table的hash链节 15 | struct in6_addr lan_ipv6; 16 | union{ 17 | u_int16_t port; 18 | u_int16_t id; 19 | }lan_l4_info; 20 | struct in6_addr wan_ipv6; 21 | union{ 22 | u_int16_t port; 23 | u_int16_t id; 24 | }wan_l4_info; 25 | int time; 26 | u_int16_t sub_sum; 27 | 28 | u_int16_t dport; 29 | //eprt_len_change如果不为0,表示为eprt命令之后的控制连接 30 | int eprt_len_change; 31 | //sum_change用于累计每次修改EPRT之后的变化值 32 | int sum_change; 33 | 34 | }; 35 | #define lan_port lan_l4_info.port 36 | #define lan_id lan_l4_info.id 37 | #define wan_port wan_l4_info.port 38 | #define wan_id wan_l4_info.id 39 | 40 | struct hash_entry{ 41 | struct hash_entry* next; 42 | struct hash_entry* prev; 43 | struct conn_entry* conntrack_entry; 44 | }; 45 | -------------------------------------------------------------------------------- /napt66_hash_table.c: -------------------------------------------------------------------------------- 1 | #include "napt66_hash_table.h" 2 | 3 | extern struct hash_entry source_table[HASHRANGE]; 4 | extern struct hash_entry ipproto_table[HASHRANGE]; 5 | extern long time(void* ptr); 6 | 7 | void hash_table_init(struct hash_entry* table) 8 | { 9 | int i; 10 | printk(KERN_INFO "hash_table_init()\n"); 11 | for(i = 0; i < HASHRANGE; i++){ 12 | table[i].next = NULL; 13 | table[i].prev = NULL; 14 | table[i].conntrack_entry = NULL; 15 | } 16 | } 17 | 18 | //使用elf算法参数哈希值,elf算法用于根据字符串产生哈希值,把key按照协议、地址、端口组成一个字符串,经过运算后得到哈希值。 19 | int hash(int proto,struct in6_addr ipv6,u_int16_t id) 20 | { 21 | unsigned int hash = 0; 22 | int i; 23 | for(i = 0; i < 16; i++){ 24 | hash = hash + ipv6.s6_addr[i]; 25 | } 26 | hash = hash + proto + id; 27 | return hash%HASHNUMBER; 28 | } 29 | 30 | //将连接项在两张表的位置都删除 31 | int hash_del_entry(struct conn_entry* p_entry) 32 | { 33 | struct hash_entry *pre_entry = p_entry->source->prev; 34 | struct hash_entry *next_entry = p_entry->source->next; 35 | //删除source_table中的链节 36 | 37 | if(next_entry==NULL){//头结点后的第一个链节特殊处理 38 | pre_entry->next=NULL; 39 | } 40 | else{ 41 | pre_entry->next = next_entry; 42 | next_entry->prev = pre_entry; 43 | } 44 | kfree(p_entry->source);//释放内存 45 | //删除ipproto_table中的链节 46 | pre_entry = p_entry->ipproto->prev; 47 | next_entry = p_entry->ipproto->next; 48 | if(next_entry==NULL){//头结点后的第一个链节特殊处理 49 | pre_entry->next=NULL; 50 | } 51 | else{ 52 | pre_entry->next = next_entry; 53 | next_entry->prev = pre_entry; 54 | } 55 | kfree(p_entry->ipproto);//释放内存 56 | return 0; 57 | } 58 | 59 | //清理链表中的过期连接,参数是空的哈希头节点,不包含连接信息 60 | int hash_clean_entry(struct hash_entry* p_hash_entry) 61 | { 62 | struct conn_entry* ct_entry; 63 | int cur_time = time(NULL); 64 | 65 | p_hash_entry = p_hash_entry->next;//跳过空的头节点 66 | 67 | while(p_hash_entry != NULL){ 68 | ct_entry = p_hash_entry->conntrack_entry; 69 | p_hash_entry = p_hash_entry->next; 70 | 71 | if(cur_time - ct_entry->time > CLEANUP_TIME){//超时 72 | hash_del_entry(ct_entry); 73 | kfree(ct_entry); 74 | } 75 | } 76 | return 0; 77 | } 78 | 79 | //搜索到则返回指针,否则返回NULL 80 | struct conn_entry* hash_search_ct(int direc,struct conn_entry* p_entry) 81 | { 82 | int place; 83 | struct hash_entry *tmp; 84 | if(direc == SNAT){//source SNAT 85 | place = hash(p_entry->proto , p_entry->lan_ipv6 , p_entry->lan_id);//得到哈希值 86 | tmp = source_table[place].next;//搜索哈希值对应位置的链表 87 | while(tmp != NULL){ 88 | if(tmp->conntrack_entry->proto == p_entry->proto && 89 | tmp->conntrack_entry->lan_id == p_entry->lan_id && 90 | 0 == memcmp(&(tmp->conntrack_entry->lan_ipv6),&(p_entry->lan_ipv6),sizeof(struct in6_addr))) 91 | { 92 | return tmp->conntrack_entry;//返回搜索结果 93 | } 94 | tmp = tmp->next; 95 | } 96 | } 97 | else if(direc == RSNAT){//ipproto RSNAT 98 | place = hash(p_entry->proto , p_entry->wan_ipv6 , p_entry->wan_id);//得到哈希值 99 | tmp = ipproto_table[place].next;//搜索哈希值对应位置的链表 100 | while(tmp != NULL){ 101 | if(tmp->conntrack_entry->proto == p_entry->proto && 102 | tmp->conntrack_entry->wan_id == p_entry->wan_id && 103 | 0 == memcmp(&(tmp->conntrack_entry->wan_ipv6),&(p_entry->wan_ipv6),sizeof(struct in6_addr))) 104 | return (*tmp).conntrack_entry;//返回搜索结果 105 | tmp = tmp->next; 106 | } 107 | } 108 | return NULL; 109 | } 110 | 111 | //在两张hash表中插入ct项 112 | int hash_add_entry(struct conn_entry* p_entry) 113 | { 114 | //把实体地址分别保存到两个新的链节中,然后把链节添加到对应的表中。 115 | int source_place,ipproto_place; 116 | struct hash_entry *source_entry = (struct hash_entry *)kcalloc(1,sizeof (struct hash_entry),GFP_KERNEL); 117 | struct hash_entry *ipproto_entry = (struct hash_entry *)kcalloc(1,sizeof (struct hash_entry),GFP_KERNEL); 118 | source_entry->conntrack_entry = p_entry;//保存实体地址到链节 119 | ipproto_entry->conntrack_entry = p_entry;//保存实体地址到链节 120 | p_entry->source = source_entry;//保存链节地址到实体 121 | p_entry->ipproto = ipproto_entry;//保存链节地址到实体 122 | //添加到source_table 123 | source_place = hash(p_entry->proto , p_entry->lan_ipv6 , p_entry->lan_id);//宏直接简化为lan_id 124 | 125 | hash_clean_entry(&source_table[source_place]);//clean应是对称的,执行一次就够。 126 | if (source_table[source_place].next==NULL){//如果是第一个新增节点 127 | source_table[source_place].next=source_entry; 128 | source_entry->next=NULL; 129 | source_entry->prev=&source_table[source_place]; 130 | } 131 | else{ 132 | source_entry->next = source_table[source_place].next; 133 | source_entry->next->prev=source_entry; 134 | source_table[source_place].next = source_entry; 135 | source_entry->prev = &source_table[source_place]; 136 | } 137 | //添加到ipproto_table 138 | ipproto_place = hash(p_entry->proto , p_entry->wan_ipv6 , p_entry->wan_id); 139 | hash_clean_entry(&ipproto_table[ipproto_place]); 140 | if (ipproto_table[ipproto_place].next==NULL){//如果是第一个新增节点 141 | ipproto_table[ipproto_place].next=ipproto_entry; 142 | ipproto_entry->next=NULL; 143 | ipproto_entry->prev=&ipproto_table[ipproto_place]; 144 | } 145 | else{ 146 | ipproto_entry->next = ipproto_table[ipproto_place].next; 147 | ipproto_entry->next->prev=ipproto_entry; 148 | ipproto_table[ipproto_place].next = ipproto_entry; 149 | ipproto_entry->prev = &ipproto_table[ipproto_place]; 150 | } 151 | return 1; 152 | } 153 | -------------------------------------------------------------------------------- /napt66_hash_table.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "napt66_global.h" 12 | 13 | #define SOURCE 1 14 | #define IPPROTO 2 15 | //外部接口 16 | 17 | void hash_table_init(struct hash_entry* table); 18 | 19 | //搜索到则返回指针,否则返回NULL 20 | struct conn_entry* hash_search_ct(int hook,struct conn_entry* p_entry); 21 | //在两张hash表中插入ct项 22 | int hash_add_entry(struct conn_entry* p_entry); 23 | -------------------------------------------------------------------------------- /napt66_main.c: -------------------------------------------------------------------------------- 1 | #define __NO_VERSION__ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "napt66_global.h" 13 | #include "napt66_main.h" 14 | 15 | MODULE_LICENSE("GPL"); 16 | MODULE_AUTHOR("NSRC NAPT66 Team"); 17 | MODULE_DESCRIPTION("IPv6-to-IPv6 Network Address PORT Translation (NAPT66)"); 18 | MODULE_VERSION("V0.1"); 19 | 20 | /* hooks for out filter*/ 21 | //静态全局变量,作用域是本源文件。 22 | static struct nf_hook_ops nfho_in; /* Netfilter钩子,RSNAT */ 23 | static struct nf_hook_ops nfho_out; /* Netfilter钩子, SNAT */ 24 | 25 | struct hash_entry source_table[HASHRANGE]; 26 | struct hash_entry ipproto_table[HASHRANGE]; 27 | 28 | /* module parameters */ 29 | char *wan_if="eth0"; //参数,WAN接口名称 30 | //module_param(wan_if,charp,S_IRUGO|S_IWUSR); //字符指针,允许所有人读取,允许root写入 31 | //默认为eth0,如要更改,在挂载模块时输入 32 | //sudo insmod napt66.ko wan_if="eth1" 33 | module_param(wan_if,charp,0000); 34 | MODULE_PARM_DESC(wan_if, "A character string" ); 35 | 36 | //WAN接口ipv6地址,新建连接记录时要引用。 37 | //不知道接口,先手动填入本机eth0的IPv6地址。 38 | struct in6_addr wan_ipv6; 39 | bool inet6_addr_flag; 40 | 41 | extern int get_entry(struct sk_buff *skb,struct conn_entry** pp_entry,int direc); 42 | extern int nat(struct sk_buff *skb,struct conn_entry* entry,int direc); 43 | extern void hash_table_init(struct hash_entry* table); 44 | 45 | long time(void* ptr) 46 | { 47 | struct timeval tv; 48 | do_gettimeofday(&tv); 49 | return tv.tv_sec; 50 | } 51 | 52 | 53 | /* the implementation of the hook function at PRE_ROUTING hook point to deal with input data packets */ 54 | unsigned int hook_func_in(unsigned int hooknum,struct sk_buff *skb,const struct net_device *in, 55 | const struct net_device *out,int (*okfn)(struct sk_buff *)) 56 | { 57 | int status; 58 | struct conn_entry* conntrack_entry = NULL; 59 | 60 | //printk(KERN_INFO "device: %s\n",skb->dev->name); //in设备名称,只处理eth0 61 | if(strncmp(in->name,wan_if,IFNAMSIZ)){ 62 | return NF_ACCEPT; 63 | } 64 | if(skb->pkt_type != PACKET_HOST){//通过mac地址过滤不是发往本机的包, 65 | //printk(KERN_INFO "host packet.\n"); 66 | return NF_ACCEPT; 67 | } 68 | 69 | status = get_entry(skb,&conntrack_entry,RSNAT); 70 | 71 | if(status > 0){ 72 | nat(skb,conntrack_entry,RSNAT); 73 | } 74 | 75 | return NF_ACCEPT;/* the follow data process as V6 before */ 76 | } 77 | 78 | /* the implementation of the hook function at POST_ROUTING hook point to deal with output data packets */ 79 | unsigned int hook_func_out(unsigned int hooknum,struct sk_buff *skb,const struct net_device *in, 80 | const struct net_device *out,int (*okfn)(struct sk_buff *)) 81 | { 82 | int status; 83 | struct conn_entry* conntrack_entry = NULL; 84 | 85 | 86 | //根据设备和IP地址过滤数据包 87 | //printk(KERN_INFO "out device: %s\n",((struct dst_entry *)skb->_skb_dst)->dev->name);//out设备名称,只处理eth0 88 | if(strncmp(out->name,wan_if,IFNAMSIZ)){ 89 | return NF_ACCEPT; 90 | } 91 | 92 | //判断TCP,UDP,ICMPv6协议。struct sk_buff提供的transport_header只是指明了IPv6报头之后的地址。可能是其他扩展报头。 93 | 94 | status = get_entry(skb,&conntrack_entry,SNAT); 95 | 96 | if(status > 0){ 97 | nat(skb,conntrack_entry,SNAT); 98 | } 99 | 100 | return NF_ACCEPT; /* the follow data process as V6 before*/ 101 | } 102 | 103 | /* module initialization function */ 104 | int init_module() 105 | { 106 | printk(KERN_INFO "Netfilter module for IPv6 NAPT66. \n"); 107 | printk(KERN_INFO "TIME 2011.4.10 final\n"); 108 | 109 | inet6_addr_flag = false; 110 | 111 | hash_table_init(source_table); 112 | hash_table_init(ipproto_table); 113 | 114 | /* Register the hook data */ 115 | /* RSNAT */ 116 | nfho_in.hook = hook_func_in; /* hook function deal with input data to modify the dstif necessary*/ 117 | nfho_in.hooknum = NF_INET_PRE_ROUTING;/* register the hook at PRE_ROUTING 118 | Note:in linux 2.6.25 is NF_INET_PRE_ROUTING,the earlier kernel is NF_IP6_PRE_ROUTING */ 119 | nfho_in.pf = PF_INET6; /* just deal with IPv6 packets */ 120 | nfho_in.priority = NF_IP6_PRI_NAT_DST; /* dst */ 121 | nf_register_hook(&nfho_in); 122 | 123 | /* SNAT */ 124 | nfho_out.hook = hook_func_out; /* hook function deal with output data to modify the src if necessary*/ 125 | nfho_out.hooknum = NF_INET_POST_ROUTING;/* register the hook at PRE_ROUTING */ 126 | nfho_out.pf = PF_INET6; 127 | nfho_out.priority = NF_IP6_PRI_NAT_SRC; /* src */ 128 | nf_register_hook(&nfho_out); 129 | 130 | return 0; 131 | } 132 | 133 | void cleanup_module() 134 | { 135 | //kfree(g_in_addr_1); 136 | 137 | printk(KERN_INFO "Remove netfilter module for IPv6 NAPT66 . \n"); 138 | nf_unregister_hook(&nfho_in); 139 | nf_unregister_hook(&nfho_out); 140 | } 141 | -------------------------------------------------------------------------------- /napt66_main.h: -------------------------------------------------------------------------------- 1 | #ifndef __IPV6_NAT_H__ 2 | #define __IPV6_NAT_H__ 3 | 4 | #define NAT66_DEBUG 5 | 6 | #define PREFIX_LEN 48 /* Global Prefix Length, more see GSE draft */ 7 | 8 | #ifdef NAT66_DEBUG 9 | #define DBUG(fmt,args...) printk(KERN_DEBUG fmt,##args) 10 | #else 11 | #define DBUG(fmt,args...) 12 | #endif 13 | 14 | #define NIP6_48(addr) \ 15 | ntohs((addr).s6_addr16[0]), \ 16 | ntohs((addr).s6_addr16[1]), \ 17 | ntohs((addr).s6_addr16[2]) 18 | 19 | #define NIP6(addr) \ 20 | ntohs((addr).s6_addr16[0]), \ 21 | ntohs((addr).s6_addr16[1]), \ 22 | ntohs((addr).s6_addr16[2]) 23 | 24 | #define NIP6_FMT_48 "%04x:%04x:%04x" 25 | 26 | #define NIP6_FMT "%04x:%04x:%04x" 27 | 28 | #endif //end of __IPV6_NAT_H__ 29 | -------------------------------------------------------------------------------- /napt66_nat.c: -------------------------------------------------------------------------------- 1 | #include "napt66_nat.h" 2 | 3 | 4 | #ifdef SPRINTF_CHAR 5 | # define SPRINTF(x) strlen(sprintf x) 6 | #else 7 | # define SPRINTF(x) ((size_t)sprintf x) 8 | #endif 9 | 10 | #define FTP_PORT 0x0015 11 | 12 | extern long time(void* ptr); 13 | extern struct conn_entry* hash_search_ct(int direc,struct conn_entry* p_entry); 14 | extern int analysis_eprt(struct sk_buff *skb,struct conn_entry *entry); 15 | extern int in_cksum(u_int16_t *addr, int len); 16 | extern struct conn_entry* get_free_ct(struct conn_entry* pkt_entry); 17 | 18 | /* 19 | 第二个参数特意设置成32bit整数,以便与函数体内的32bit数协同运算时,正确处理符号位与进位。 20 | 因为16bit整数的符号位与32bit整数的符号位不是对齐的。 21 | */ 22 | u_int16_t adjust_checksum(u_int16_t old_checksum,u_int32_t delta) 23 | { 24 | u_int32_t new_checksum; 25 | 26 | new_checksum = old_checksum - delta; 27 | 28 | if(new_checksum >> 31 != 0){//负数 29 | new_checksum--; 30 | } 31 | 32 | while(new_checksum >> 16){ 33 | new_checksum = (new_checksum & 0xffff) + (new_checksum >> 16); 34 | } 35 | 36 | return new_checksum; 37 | } 38 | 39 | u_int16_t calc_checksum(u_int16_t *addr,int length,u_int32_t sum) 40 | { 41 | /* u_int32_t sum = 0;*/ 42 | 43 | while(length > 0){ 44 | sum += *addr++; 45 | length -= 2; 46 | } 47 | 48 | if(length > 0) 49 | sum += *(unsigned char *)addr; 50 | 51 | while(sum >> 16) 52 | sum = (sum & 0xffff) + (sum >> 16); 53 | return(~sum); 54 | } 55 | 56 | struct in6_addr inet6_addr_ntohs(struct in6_addr *net) 57 | { 58 | struct in6_addr addr6; 59 | int i; 60 | memcpy(&addr6,net,sizeof(struct in6_addr)); 61 | 62 | 63 | for(i=0;i<8;i++){ 64 | addr6.s6_addr16[i] = ntohs(addr6.s6_addr16[i]); 65 | } 66 | 67 | return addr6; 68 | } 69 | 70 | 71 | struct in6_addr inet6_addr_htons(struct in6_addr *net) 72 | { 73 | struct in6_addr addr6; 74 | int i; 75 | 76 | memcpy(&addr6,net,sizeof(struct in6_addr)); 77 | 78 | 79 | for(i=0;i<8;i++){ 80 | addr6.s6_addr16[i] = htons(addr6.s6_addr16[i]); 81 | } 82 | 83 | return addr6; 84 | } 85 | 86 | int nat(struct sk_buff *skb,struct conn_entry* entry,int direc) 87 | { 88 | /* 89 | POSTROUTING点为SNAT,direc为1 90 | PREROUTING点为RSNAT,direc为0 91 | */ 92 | struct ipv6hdr* ipv6_header; 93 | u_int8_t proto; 94 | u_int8_t pl_proto; 95 | struct tcphdr* tcp_header; 96 | struct udphdr* udp_header; 97 | struct icmp6hdr* icmpv6_header; 98 | struct ipv6hdr* pl_ipv6_header; 99 | struct tcphdr* pl_tcp_header; 100 | struct icmp6hdr* pl_icmpv6_header; 101 | 102 | u_int32_t sum; 103 | int len; 104 | 105 | /*定位IPv6头部*/ 106 | ipv6_header = ipv6_hdr(skb); 107 | proto = ipv6_header->nexthdr; 108 | 109 | /*SNAT*/ 110 | if(direc == 1){ 111 | memcpy(&(ipv6_header->saddr),&(entry->wan_ipv6),sizeof(struct in6_addr)); 112 | switch (proto){ 113 | case IPPROTO_TCP: 114 | tcp_header = (struct tcphdr*)((char*)ipv6_header + entry->proto_offset); 115 | tcp_header->source = entry->wan_port; 116 | 117 | /*非FTP报文,正常处理*/ 118 | if(entry->dport != htons(FTP_PORT)){ 119 | tcp_header->check = adjust_checksum(tcp_header->check,entry->sub_sum); 120 | } 121 | 122 | /*EPRT报文之前的控制报文*/ 123 | else if(entry->eprt_len_change == 0){ 124 | if(analysis_eprt(skb,entry) == 1){ 125 | 126 | //printk("old ipv6 len is %x\n",ipv6_header->payload_len); 127 | ipv6_header->payload_len = htons(ntohs(ipv6_header->payload_len) + entry->eprt_len_change); 128 | 129 | //printk("First EPRT\n"); 130 | len = htons(ipv6_header->payload_len); 131 | tcp_header->check = 0; 132 | sum = in_cksum((u_int16_t *)&ipv6_header->saddr,32); 133 | sum += ntohs(IPPROTO_TCP + len); 134 | sum += in_cksum((u_int16_t *)tcp_header, len); 135 | tcp_header->check = CKSUM_CARRY(sum); 136 | 137 | //printk("checksum done\n"); 138 | //printk("eprt out seq is %x\n",tcp_header->seq); 139 | } 140 | 141 | else { 142 | //tcp_header->check = adjust_checksum(tcp_header->check,entry->sub_sum); 143 | /*EPRT之前的控制报文*/ 144 | len = htons(ipv6_header->payload_len); 145 | //printk("eprt ck len is %x",len); 146 | 147 | tcp_header->check = 0; 148 | sum = in_cksum((u_int16_t *)&ipv6_header->saddr,32); 149 | sum += ntohs(IPPROTO_TCP + len); 150 | sum += in_cksum((u_int16_t *)tcp_header, len); 151 | tcp_header->check = CKSUM_CARRY(sum); 152 | } 153 | } 154 | 155 | /*EPRT之后的控制报文*/ 156 | else { 157 | /*修改EPRT报文之后,需要对后续数据包的SEQ字段进行调整*/ 158 | tcp_header->seq = htonl(ntohl(tcp_header->seq) + entry->eprt_len_change + entry->sum_change); 159 | 160 | if(analysis_eprt(skb,entry) == 1){ 161 | ipv6_header->payload_len = htons(ntohs(ipv6_header->payload_len) + entry->eprt_len_change); 162 | //printk("Houxu EPRT\n"); 163 | //printk("new ipv6 len is %x\n",ipv6_header->payload_len); 164 | //printk("old checksum is %x\n",tcp_header->check); 165 | } 166 | 167 | len = htons(ipv6_header->payload_len); 168 | tcp_header->check = 0; 169 | sum = in_cksum((u_int16_t *)&ipv6_header->saddr,32); 170 | sum += ntohs(IPPROTO_TCP + len); 171 | sum += in_cksum((u_int16_t *)tcp_header, len); 172 | tcp_header->check = CKSUM_CARRY(sum); 173 | 174 | } 175 | break; 176 | case IPPROTO_UDP: 177 | udp_header = (struct udphdr*)((char*)ipv6_header + entry->proto_offset); 178 | udp_header->source = entry->wan_port; 179 | udp_header->check = adjust_checksum(udp_header->check,entry->sub_sum); 180 | break; 181 | case IPPROTO_ICMPV6: 182 | icmpv6_header = (struct icmp6hdr*)((char*)ipv6_header + entry->proto_offset); 183 | icmpv6_header->icmp6_identifier = entry->wan_id; 184 | icmpv6_header->icmp6_cksum = adjust_checksum(icmpv6_header->icmp6_cksum,entry->sub_sum); 185 | break; 186 | default: 187 | break; 188 | } 189 | } 190 | 191 | /*RSNAT*/ 192 | else { 193 | memcpy(&(ipv6_header->daddr),&(entry->lan_ipv6),sizeof(struct in6_addr)); 194 | switch (proto){ 195 | case IPPROTO_TCP: 196 | tcp_header = (struct tcphdr*)((char*)ipv6_header + entry->proto_offset); 197 | tcp_header->dest = entry->lan_port; 198 | 199 | if(entry->eprt_len_change == 0){ 200 | tcp_header->check = adjust_checksum(tcp_header->check,-entry->sub_sum); 201 | } 202 | 203 | else { 204 | /*对服务器返回数据包的ack进行调整*/ 205 | //printk("old in ack is %x\n",tcp_header->ack_seq); 206 | tcp_header->ack_seq = htonl(ntohl(tcp_header->ack_seq) - entry->eprt_len_change - entry->sum_change); 207 | //printk("new in ack is %x\n",tcp_header->ack_seq); 208 | 209 | //printk("maybe bug start!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); 210 | //printk("eprt_len_change is %d,sum_change is %d\n",entry->eprt_len_change,entry->sum_change); 211 | //printk("new in ipv6 len is %x\n",ipv6_header->payload_len); 212 | len = htons(ipv6_header->payload_len); 213 | 214 | tcp_header->check = 0; 215 | sum = in_cksum((u_int16_t *)&ipv6_header->saddr,32); 216 | sum += ntohs(IPPROTO_TCP + len); 217 | sum += in_cksum((u_int16_t *)tcp_header, len); 218 | tcp_header->check = CKSUM_CARRY(sum); 219 | } 220 | break; 221 | 222 | case IPPROTO_UDP: 223 | udp_header = (struct udphdr*)((char*)ipv6_header + entry->proto_offset); 224 | udp_header->dest = entry->lan_port; 225 | udp_header->check = adjust_checksum(udp_header->check,-entry->sub_sum); 226 | break; 227 | 228 | case IPPROTO_ICMPV6: 229 | 230 | icmpv6_header = (struct icmp6hdr*)((char*)ipv6_header + entry->proto_offset); 231 | 232 | /*ICMPv6消息报文,正常传输*/ 233 | if(icmpv6_header->icmp6_type == ICMPV6_ECHO_REQUEST || icmpv6_header->icmp6_type == ICMPV6_ECHO_REPLY){ 234 | icmpv6_header->icmp6_identifier = entry->lan_id; 235 | icmpv6_header->icmp6_cksum = adjust_checksum(icmpv6_header->icmp6_cksum,-entry->sub_sum); 236 | } 237 | 238 | /* 239 | ICMPv6错误报文 240 | 负载如果是ICMPv6协议,则将lan表项内容填入负载中的源地址和identifier字段 241 | 负载如果是TCP/UDP协议,则将lan表项内容填入负载中的源地址和端口字段 242 | 之后重新计算负载的校验和,填入负载的相应字段 243 | */ 244 | else { 245 | pl_ipv6_header = (struct ipv6hdr *)((char *)icmpv6_header + 8); 246 | 247 | memcpy(&(pl_ipv6_header->saddr),&(entry->lan_ipv6),sizeof(struct in6_addr)); 248 | pl_proto = pl_ipv6_header->nexthdr; 249 | 250 | if(pl_proto == IPPROTO_ICMPV6){ 251 | pl_icmpv6_header = (struct icmp6hdr*)((char *)pl_ipv6_header + 40); 252 | pl_icmpv6_header->icmp6_identifier = entry->lan_id; 253 | 254 | len = htons(pl_ipv6_header->payload_len); 255 | pl_icmpv6_header->icmp6_cksum = 0; 256 | sum = in_cksum((u_int16_t *)&pl_ipv6_header->saddr, 32); 257 | sum += ntohs(IPPROTO_ICMPV6 + len); 258 | sum += in_cksum((u_int16_t *)pl_icmpv6_header, len); 259 | pl_icmpv6_header->icmp6_cksum = CKSUM_CARRY(sum); 260 | } 261 | else if(pl_proto == IPPROTO_TCP || pl_proto == IPPROTO_UDP){ 262 | pl_tcp_header = (struct tcphdr*)((char *)pl_ipv6_header + 40); 263 | pl_tcp_header->source = entry->lan_port; 264 | 265 | len = htons(pl_ipv6_header->payload_len); 266 | pl_tcp_header->check = 0; 267 | sum = in_cksum((u_int16_t *)&pl_ipv6_header->saddr, 32); 268 | sum += ntohs(pl_proto + len); 269 | sum += in_cksum((u_int16_t *)pl_tcp_header, len); 270 | pl_tcp_header->check = CKSUM_CARRY(sum); 271 | } 272 | 273 | /*重新计算ICMPv6报文本身校验和*/ 274 | 275 | len = htons(ipv6_header->payload_len); 276 | 277 | icmpv6_header->icmp6_cksum = 0; 278 | sum = in_cksum((u_int16_t *)&ipv6_header->saddr, 32); 279 | sum += ntohs(IPPROTO_ICMPV6 + len); 280 | sum += in_cksum((u_int16_t *)icmpv6_header, len); 281 | icmpv6_header->icmp6_cksum = CKSUM_CARRY(sum); 282 | } 283 | 284 | break; 285 | default: 286 | break; 287 | } 288 | } 289 | 290 | return 1; 291 | } 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /napt66_nat.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "napt66_global.h" 14 | 15 | #define CKSUM_CARRY(x) \ 16 | (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff)) 17 | 18 | --------------------------------------------------------------------------------