[Feature][ZXW-237]merge P54U03 version

Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No

Change-Id: Id39ef8b992af691eab09c01d4ea26da89e5f4049
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c
index 5df877d..09a1b2b 100755
--- a/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/psnet/psnet_dev.c
@@ -6,6 +6,14 @@
 #include "psnet.h"
 #include "psnet_io.h"
 #include <mach/iomap.h>
+
+#include <linux/if_addr.h>
+#include <linux/ip.h>
+#include <net/ip.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include <net/protocol.h>
+
 #define WATCHDOG_TIMEO (5*HZ)
 
 /* NET NUMBER;  channel id;  lan/wan*/
@@ -248,6 +256,49 @@
     netif_wake_queue(net);
 }
 
+typedef int (*set_xlat_CB)(unsigned char *ip_info, unsigned char *dev_name);
+extern set_xlat_CB g_set_xlat_cb;
+extern struct in6_addr g_plat_subnet;
+extern struct in6_addr g_ipv6_local_subnet;
+extern struct in_addr g_ipv4_local_subnet;
+extern struct net_device *g_xlat_dev;
+int psnet_set_xlat(unsigned char *ip_info, unsigned char *dev_name)
+{
+	unsigned char *buff = ip_info;
+	memcpy(&g_plat_subnet, buff, sizeof(struct in6_addr));
+	buff = buff + sizeof(struct in6_addr);
+	memcpy(&g_ipv6_local_subnet, buff, sizeof(struct in6_addr));
+	buff = buff + sizeof(struct in6_addr);
+	memcpy(&g_ipv4_local_subnet, buff, sizeof(struct in_addr));
+	g_xlat_dev = dev_get_by_name(&init_net, dev_name);
+}
+
+int fill_ip_header(struct iphdr *ip, const struct ipv6hdr *old_header) {
+	if(memcmp(&old_header->saddr, &g_plat_subnet, 12)){
+		return 0;
+	}
+	if(memcmp(&old_header->daddr, &g_ipv6_local_subnet, 16)){
+		return 0;
+	}
+	ip->saddr = old_header->saddr.s6_addr32[3];
+	ip->daddr = g_ipv4_local_subnet.s_addr;
+	ip->ihl = 5;
+	ip->version = 4;
+	ip->tos = 0;
+	ip->tot_len = htons(sizeof(struct iphdr) + ntohs(old_header->payload_len));
+	ip->id = 0;
+	ip->frag_off = htons(IP_DF);
+	ip->ttl = old_header->hop_limit;
+	ip->protocol = old_header->nexthdr;
+	ip->check = 0;
+	return 1;
+}
+extern int fast_nat_check(unsigned char *data);
+extern uint32_t ipv6_pseudo_header_checksum(const struct ipv6hdr *ip6, uint16_t len, uint8_t protocol);
+extern uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len);
+extern uint16_t ip_checksum(const void *data, int len);
+extern uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+
 #define PSBUFF_DL_HEADER_OFFSET 64 //80
 //extern void * zGetpsbufferHead(void * data);
 void psnet_recv_notify(unsigned int index, const void *buffer, unsigned int length)//(T_ZDrvRpMsg_Msg rpmsg)
@@ -267,6 +318,41 @@
     psData.pbuf_head = buffer - PSBUFF_DL_HEADER_OFFSET;
 #endif
 	psData.datalen = length;
+	
+	unsigned char header[40] = {0};
+	if(unlikely(g_xlat_dev) && g_xlat_dev == dev->net && ((*(uint8_t *)psData.pbuf_data)&0xf0) == 0x60 && (g_xlat_dev->flags & IFF_UP)){
+		struct iphdr *ip_h = header;
+		struct ipv6hdr *ip6_h = (struct ipv6hdr *)psData.pbuf_data;
+		memcpy(header + sizeof(struct iphdr), psData.pbuf_data + sizeof(struct ipv6hdr), 4);
+		if(fill_ip_header(ip_h, ip6_h) && fast_nat_check(ip_h)){//
+			uint32_t old_sum, new_sum;
+			uint16_t checksum, len_left;
+			uint8_t protocol = ip6_h->nexthdr;
+			
+			len_left = ntohs(ip6_h->payload_len);
+			old_sum = ipv6_pseudo_header_checksum(psData.pbuf_data, len_left, protocol);
+			new_sum = ipv4_pseudo_header_checksum(ip_h, len_left);
+			ip_h->check = ip_checksum(ip_h, sizeof(struct iphdr));
+			if (protocol == IPPROTO_TCP) {
+				struct tcphdr *tcp_targ = (struct tcphdr *)(psData.pbuf_data + sizeof(struct ipv6hdr));
+				checksum = ip_checksum_adjust(tcp_targ->check, old_sum, new_sum);
+				tcp_targ->check = checksum;
+				psData.pbuf_data = psData.pbuf_data + sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+				memcpy(psData.pbuf_data, ip_h, sizeof(struct iphdr));
+				psData.datalen = psData.datalen - sizeof(struct ipv6hdr) + sizeof(struct iphdr);
+			}else if (protocol == IPPROTO_UDP) {
+				struct udphdr *udp_targ = (struct udphdr *)(psData.pbuf_data + sizeof(struct ipv6hdr));
+				if(udp_targ->check){
+					checksum = ip_checksum_adjust(udp_targ->check, old_sum, new_sum);
+					udp_targ->check = checksum;
+				}
+				psData.pbuf_data = psData.pbuf_data + sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+				memcpy(psData.pbuf_data, ip_h, sizeof(struct iphdr));
+				psData.datalen = psData.datalen - sizeof(struct ipv6hdr) + sizeof(struct iphdr);
+			}
+		}
+	}
+
       skb = skb_build_psbuf(&psData);
 
 	if (!skb) 
@@ -369,7 +455,7 @@
     int err;
     struct psnet *dev;
     struct net_device *net;
-	
+	g_set_xlat_cb = psnet_set_xlat;
     dbg("ddr_ap_net_lan_init.\n");
     
     err = -ENOMEM;
diff --git a/ap/os/linux/linux-3.4.x/drivers/net/tun.c b/ap/os/linux/linux-3.4.x/drivers/net/tun.c
old mode 100644
new mode 100755
index 8e8b74e..3309fc9
--- a/ap/os/linux/linux-3.4.x/drivers/net/tun.c
+++ b/ap/os/linux/linux-3.4.x/drivers/net/tun.c
@@ -603,7 +603,7 @@
 
 	return skb;
 }
-
+extern int (*fast_from_softirq) (struct sk_buff *skb);
 /* Get packet from user space buffer */
 static ssize_t tun_get_user(struct tun_struct *tun,
 			    const struct iovec *iv, size_t count,
@@ -733,7 +733,12 @@
 		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
 		skb_shinfo(skb)->gso_segs = 0;
 	}
-
+	if (fast_from_softirq && fast_from_softirq(skb))
+	{
+		tun->dev->stats.rx_packets++;
+		tun->dev->stats.rx_bytes += len;
+		return count;  
+	}
 	netif_rx_ni(skb);
 
 	tun->dev->stats.rx_packets++;