summaryrefslogtreecommitdiff
path: root/drivers/net/bih/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/bih/translate.c')
-rw-r--r--drivers/net/bih/translate.c1585
1 files changed, 1585 insertions, 0 deletions
diff --git a/drivers/net/bih/translate.c b/drivers/net/bih/translate.c
new file mode 100644
index 00000000..ddd18c0d
--- /dev/null
+++ b/drivers/net/bih/translate.c
@@ -0,0 +1,1585 @@
+/*
+ * Bump in the Host (BIH)
+ * http://code.google.com/p/bump-in-the-host/source/
+ * ----------------------------------------------------------
+ *
+ * Copyrighted (C) 2010,2011 by the China Mobile Communications
+ * Corporation <bih.cmcc@gmail.com>;
+ * See the COPYRIGHT file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h>
+#include <linux/errno.h> /* error codes */
+#include <linux/types.h> /* size_t */
+#include <linux/interrupt.h> /* mark_bh */
+#include <linux/random.h>
+#include <linux/in.h>
+#include <linux/netdevice.h> /* struct device, and other headers */
+#include <linux/etherdevice.h> /* eth_type_trans */
+#include <linux/inetdevice.h>
+#include <net/ip.h> /* struct iphdr */
+#include <net/icmp.h> /* struct icmphdr */
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <linux/in6.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <net/addrconf.h>
+#include "translate.h"
+#include "map.h"
+#include "pool.h"
+#include "voslist.h"
+#define NIPQUAD(addr) \
+ ((unsigned char *)&addr)[0], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[3]
+#define __BIHDEBUG__
+
+#define BIH_DEBUG printk
+/*
+ * If tos_ignore_flag != 0, we don't copy TOS and Traffic Class
+ * from origin paket and set it to 0
+ */
+int tos_ignore_flag = 0;
+int BIHMODE,NETWORKTYPE=0;
+unsigned int PRIVATEADDR=0;
+extern struct net_device *bihdev;
+
+int ip6option(int type)
+{
+ int i,optarray[8]={IPPROTO_HOPOPTS,IPPROTO_IPV6,IPPROTO_ROUTING,IPPROTO_FRAGMENT,
+ IPPROTO_ESP,IPPROTO_AH,IPPROTO_DSTOPTS,IPPROTO_NONE};
+ for(i=0;i<8;i++)
+ {
+ if(type==optarray[i])
+ return(optarray[i]);
+ }
+ return(0);
+}
+
+unsigned char *getipv6uppkg(unsigned char *ippkg,unsigned char *protocol,int *uppkglen)
+{
+ unsigned char *ippkgpos=ippkg+40;
+ struct ip6_hdr *hdr=(struct ip6_hdr *)ippkg;
+ struct ip6_opthdr *opthdr;
+ int ip6hdrlen;
+
+ if(ip6option(hdr->ip6_nxt))
+ {
+ opthdr=(struct ip6_opthdr *)ippkgpos;
+ while(ip6option(opthdr->nxt))
+ {
+ ippkgpos+=opthdr->len;
+ opthdr=(struct ip6_opthdr *)ippkgpos;
+ }
+ if(protocol)
+ *protocol=opthdr->nxt;
+ ippkgpos+=opthdr->len;
+ }
+ else
+ if(protocol)
+ *protocol=hdr->ip6_nxt;
+ ip6hdrlen=ippkgpos-ippkg;
+ if(uppkglen)
+ *uppkglen=ntohs(hdr->ip6_plen)+40-ip6hdrlen;
+ return(ippkgpos);
+}
+
+unsigned short chksum(unsigned short *data,int len)
+{
+ unsigned short answer=0,*p=data;
+ int nleft=len,sum=0;
+ while(nleft>1)
+ {
+ sum+=*p++;
+ nleft-=2;
+ }
+ if(nleft==1)
+ {
+ *(unsigned char *)(&answer)=*(unsigned char *)p;
+ sum+=answer;
+ }
+ sum=(sum>>16)+(sum&0xffff);
+ sum+=(sum>>16);
+ answer=~sum;
+ return(answer);
+}
+
+unsigned short tcpchksum6(unsigned char *src,unsigned char *dst,unsigned char *tcppkg,int tcppkglen)
+{
+ struct tcp *th;
+ static unsigned char *tmpbuf=NULL;
+ unsigned char *bufpos;
+ int tmpbuflen;
+
+ if(tmpbuf==NULL)
+ {
+ tmpbuf=kmalloc(2048,GFP_KERNEL);
+ if(tmpbuf==NULL)
+ {
+ lprintf(0,"kmalloc error\n");
+ return(0);
+ }
+ }
+ bufpos=tmpbuf;
+ th=(struct tcp *)tcppkg;
+ th->check=0;
+ memcpy(bufpos,src,16);
+ bufpos+=16;
+ memcpy(bufpos,dst,16);
+ bufpos+=16;
+ *bufpos++=0;
+ *bufpos++=IPPROTO_TCP;
+ PKG_PUT_SHORT(tcppkglen,bufpos);
+ memcpy(bufpos,tcppkg,tcppkglen);
+ bufpos+=tcppkglen;
+ tmpbuflen=bufpos-tmpbuf;
+ return(chksum((unsigned short *)tmpbuf,tmpbuflen));
+}
+
+unsigned short tcpchksum(unsigned char *src,unsigned char *dst,unsigned char *tcppkg,int tcppkglen)
+{
+ struct tcp *th;
+ static unsigned char *tmpbuf=NULL;
+ unsigned char *bufpos;
+ int tmpbuflen;
+
+ if(tmpbuf==NULL)
+ {
+ tmpbuf=kmalloc(2048,GFP_KERNEL);
+ if(tmpbuf==NULL)
+ {
+ lprintf(0,"kmalloc error\n");
+ return(0);
+ }
+ }
+ bufpos=tmpbuf;
+ th=(struct tcp *)tcppkg;
+ th->check=0;
+ memcpy(bufpos,src,4);
+ bufpos+=4;
+ memcpy(bufpos,dst,4);
+ bufpos+=4;
+ *bufpos++=0;
+ *bufpos++=IPPROTO_TCP;
+ PKG_PUT_SHORT(tcppkglen,bufpos);
+ memcpy(bufpos,tcppkg,tcppkglen);
+ bufpos+=tcppkglen;
+ tmpbuflen=bufpos-tmpbuf;
+ return(chksum((unsigned short *)tmpbuf,tmpbuflen));
+}
+
+int ipv6_get_addr(struct net_device *dev, struct in6_addr *addr)
+{
+ struct inet6_dev *idev;
+ int err = -EADDRNOTAVAIL;
+
+ BIH_DEBUG("bih: ipv6_get_addr start\n");
+ if ((idev = __in6_dev_get(dev)) != NULL) {
+ struct inet6_ifaddr *ifp;
+
+ read_lock_bh(&idev->lock);
+ list_for_each_entry(ifp, &idev->addr_list, if_list){
+ char dst[64];//test
+ inet_ntop6((unsigned char *)&ifp->addr, dst, sizeof(dst)-1);//test
+ BIH_DEBUG("bih: ipv6_get_addr addr=%s and if->scope=%d ifp->flags=%d\n",dst ,ifp->scope, ifp->flags);
+ if (ifp->scope != IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE))
+ {
+ //ipv6_addr_copy(addr, &ifp->addr);
+ *addr = ifp->addr;
+ err = 0;
+ break;
+ }
+ }
+ read_unlock_bh(&idev->lock);
+ }
+ return err;
+}
+
+int ipv6_get_laddr(struct net_device *dev, struct in6_addr *addr)
+{
+ struct inet6_dev *idev;
+ int err = -EADDRNOTAVAIL;
+
+ BIH_DEBUG("bih: ipv6_get_laddr start\n");
+ if ((idev = __in6_dev_get(dev)) != NULL) {
+ struct inet6_ifaddr *ifp;
+
+ read_lock_bh(&idev->lock);
+ list_for_each_entry(ifp, &idev->addr_list, if_list) {
+ char dst[64];//test
+ inet_ntop6((unsigned char *)&ifp->addr, dst, sizeof(dst)-1);//test
+ BIH_DEBUG("bih: ipv6_get_addr linklocal addr=%s and if->scope=%d ifp->flags=%d\n",dst ,ifp->scope, ifp->flags);
+ if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE))
+ {
+ //ipv6_addr_copy(addr, &ifp->addr); //
+ *addr = ifp->addr;
+ err = 0;
+ break;
+ }
+ }
+ read_unlock_bh(&idev->lock);
+ }
+ return err;
+}
+
+int ipv4_get_addr(struct net_device *dev, unsigned int *addr)
+{
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa = NULL;
+
+ BIH_DEBUG("bih: ipv4_get_addr start\n");
+ if ((in_dev = __in_dev_get_rtnl(dev)) != NULL)
+ {
+ for(ifa=in_dev->ifa_list;ifa!=NULL;ifa=ifa->ifa_next)
+ {
+ if(ifa->ifa_local!=0)
+ {
+ *addr=ifa->ifa_local;
+ return(0);
+ }
+ }
+ }
+ return(-1);
+}
+
+int ip_show(struct ip *ip)
+{
+ if(ip==NULL)
+ return 0;
+ BIH_DEBUG("bih: v(%u) hl(%u) tos(%u) len(%u) id(%u) off(%u) ",
+ ip->ip_v,ip->ip_hl<<2,ip->ip_tos,ntohs(ip->ip_len),ntohs(ip->ip_id),ntohs(ip->ip_off));
+ lprintf(1,"v(%u) hl(%u) tos(%u) len(%u) id(%u) off(%u) ",
+ ip->ip_v,ip->ip_hl<<2,ip->ip_tos,ntohs(ip->ip_len),ntohs(ip->ip_id),ntohs(ip->ip_off));
+ switch(ip->ip_p)
+ {
+ case IPPROTO_TCP:
+ lprintf(1,"TCP ");
+ break;
+ case IPPROTO_UDP:
+ lprintf(1,"UDP ");
+ break;
+ case IPPROTO_ICMP:
+ lprintf(1,"ICMP ");
+ break;
+ default:
+ lprintf(1,"%u ",ip->ip_p);
+ break;
+ }
+ BIH_DEBUG("bih: sum(%04X) %u.%u.%u.%u --> %u.%u.%u.%u\n",ntohs(ip->ip_sum),NIPQUAD(ip->ip_src),NIPQUAD(ip->ip_dst));
+ lprintf(1,"sum(%04X) %u.%u.%u.%u --> %u.%u.%u.%u\n",ntohs(ip->ip_sum),NIPQUAD(ip->ip_src),NIPQUAD(ip->ip_dst));
+ return(0);
+}
+
+int ipv6_show(struct ipv6hdr *ipv6)
+{
+ char dst[64],src[64];
+ if(ipv6==NULL)
+ return 0;
+ dst[0]=src[0]=0;
+ inet_ntop6((unsigned char *)&ipv6->daddr, dst, sizeof(dst)-1);
+ inet_ntop6((unsigned char *)&ipv6->saddr, src, sizeof(src)-1);
+ BIH_DEBUG("bih: v(%u) p(%u) plen(%d) proto(%d) hlimit(%d) %s --> %s\n",
+ ipv6->version,ipv6->priority,ntohs(ipv6->payload_len),ipv6->nexthdr,ipv6->hop_limit,src,dst);
+ lprintf(1,"v(%u) p(%u) plen(%d) proto(%d) hlimit(%d) %s --> %s\n",
+ ipv6->version,ipv6->priority,ntohs(ipv6->payload_len),ipv6->nexthdr,ipv6->hop_limit,src,dst);
+ return(0);
+}
+
+int tcp_show(struct tcp *tcp)
+{
+ if(tcp==NULL)
+ return(0);
+ BIH_DEBUG("bih: SPORT(%d) DPORT(%d) SEQ(%u) ACKSEQ(%u) HEADLEN(%d) FIN(%d) SYN(%d) RST(%d) PSH(%d) WINDOW(%d) SUM(%04x) URGPRT(%d)\n",
+ ntohs(tcp->sport),ntohs(tcp->dport),ntohl(tcp->seq),ntohl(tcp->ack_seq),tcp->doff<<2,
+ tcp->fin,tcp->syn,tcp->rst,tcp->psh,ntohs(tcp->window),ntohs(tcp->check),ntohs(tcp->urg_prt));
+ lprintf(1,"SPORT(%d) DPORT(%d) SEQ(%u) ACKSEQ(%u) HEADLEN(%d) FIN(%d) SYN(%d) RST(%d) PSH(%d) WINDOW(%d) SUM(%04x) URGPRT(%d)\n",
+ ntohs(tcp->sport),ntohs(tcp->dport),ntohl(tcp->seq),ntohl(tcp->ack_seq),tcp->doff<<2,
+ tcp->fin,tcp->syn,tcp->rst,tcp->psh,ntohs(tcp->window),ntohs(tcp->check),ntohs(tcp->urg_prt));
+ return(0);
+}
+
+int udp_show(struct udp *udp)
+{
+ if(udp==NULL)
+ return(0);
+ BIH_DEBUG("bih: SPORT(%d) DPORT(%d) LEN(%d) SUM(%04x)\n",
+ ntohs(udp->sport),ntohs(udp->dport),ntohs(udp->len),ntohs(udp->check));
+ lprintf(1,"SPORT(%d) DPORT(%d) LEN(%d) SUM(%04x)\n",
+ ntohs(udp->sport),ntohs(udp->dport),ntohs(udp->len),ntohs(udp->check));
+ return(0);
+}
+
+int skb_show(struct sk_buff *skb)
+{
+ struct ipv6hdr *ipv6;
+ struct tcp *tcp;
+ struct udp *udp;
+ if(skb==NULL)
+ return(0);
+ ipv6 = (struct ipv6hdr *)ipv6_hdr(skb);
+ tcp=(struct tcp *)(ipv6+1);
+ udp=(struct udp *)(ipv6+1);
+ ipv6_show(ipv6);
+ tcp_show(tcp);
+ return(0);
+}
+
+void array_show(unsigned char *packp,int packlen,char hdflag,char base)
+{
+ int i;
+ if(hdflag)
+ lprintf(7,"==================== ARRAY[%d] ====================\n",packlen);
+ for(i=0;i<packlen;i++)
+ {
+ switch(base)
+ {
+ case 16:
+ if(i&&!(i%16))
+ lprintf(7,"\n");
+ lprintf(7,"%02x ",packp[i]);
+ break;
+ case 10:
+ if(i&&!(i%16))
+ lprintf(7,"\n");
+ lprintf(7,"%03d ",packp[i]);
+ break;
+ case 2:
+ {
+ unsigned char n,mask=0x80;
+ if(i&&!(i%4))
+ lprintf(7,"\n");
+ for(n=0;n<8;n++)
+ {
+ if(n&&!(n%4))
+ lprintf(7,",");
+ if(packp[i]&mask)
+ lprintf(7,"1");
+ else
+ lprintf(7,"0");
+ mask>>=1;
+ }
+ lprintf(7," ");
+ break;
+ }
+ default:
+ lprintf(7,"%c",packp[i]);
+ break;
+ }
+ }
+ if(base==16)
+ lprintf(7,"\n");
+}
+
+
+static int ip4_ip6(struct net_device *dev,char *src, int len, char *dst, int include_flag)/*v4 address be translated*/
+{
+ struct in6_addr addr,dstaddr;
+ struct iphdr *ih4 = (struct iphdr *) src;
+ struct icmphdr *icmp_hdr;
+ struct udphdr *udp_hdr;
+ struct tcphdr *tcp_hdr;
+
+ struct ipv6hdr *ih6 = (struct ipv6hdr *) dst;
+ struct frag_hdr *ih6_frag = (struct frag_hdr *)(dst+sizeof(struct ipv6hdr));
+ struct icmp6hdr *icmp6_hdr;
+
+ int hdr_len = (int)(ih4->ihl * 4);
+ int icmp_len;
+ int plen;
+
+ unsigned int csum;
+ int fl_csum = 0;
+ int icmperr = 1;
+ int fr_flag = 0;
+ __u16 new_tot_len;
+ __u8 new_nexthdr;
+ __u16 icmp_ptr = 0;
+ int ret;
+ struct map_node *mapnode;
+
+ BIH_DEBUG("bih: ip4_ip6 start\n");
+ if(ipv4_is_loopback(ih4->daddr)||ipv4_is_multicast(ih4->daddr)||
+ ipv4_is_lbcast(ih4->daddr)||ipv4_is_zeronet(ih4->daddr)||
+ ih4->daddr==ih4->saddr)
+ {
+ BIH_DEBUG("bih: ip4_ip6 ipv4 is not right addr\n");
+ return(-1);
+ }
+
+ if(ih4->protocol==IPPROTO_UDP)
+ {
+ udp_hdr = (struct udphdr *)(src+hdr_len);
+ if(ntohs(udp_hdr->dest)==53)
+ {
+ BIH_DEBUG("bih: ip4_ip6 pkt type is dns\n");
+ return(-1);
+ }
+ }
+
+ ret=ipv6_get_addr(dev,&addr);
+ if(ret!=0)
+ {
+ BIH_DEBUG("bih: ip4_ip6 get ipv6 addr failed\n");
+ return(-1);
+ }
+ mapnode=map_search(ih4->daddr);
+ if(mapnode)
+ memcpy(&dstaddr,&mapnode->in6addr,sizeof(struct in6_addr));
+
+ else
+ {
+ BIH_DEBUG("bih: ip4_ip6 no correct map searched\n");
+ return(-1);
+ }
+#ifdef __BIHDEBUG__
+{
+ struct ip *ip=(struct ip *)ih4;
+ BIH_DEBUG("bih: ---------- %s ----------\n",mapnode?"466":"464");
+ lprintf(0,"---------- %s ----------\n",mapnode?"466":"464");
+ ip_show(ip);
+}
+#endif
+
+ if (ntohs(ih4->frag_off) == IP_DF || include_flag ) {
+ new_tot_len = ntohs(ih4->tot_len);
+
+ if (ih4->protocol == IPPROTO_ICMP)
+ new_nexthdr = NEXTHDR_ICMP;
+ else
+ new_nexthdr = ih4->protocol;
+ }
+ else {
+ /* need to add Fragment Header */
+ fr_flag = 1;
+ /* total length = total length from IPv4 packet +
+ length of Fragment Header */
+ new_tot_len = ntohs(ih4->tot_len) + sizeof(struct frag_hdr);
+ /* IPv6 Header NextHeader = NEXTHDR_FRAGMENT */
+ new_nexthdr = NEXTHDR_FRAGMENT;
+ /* Fragment Header NextHeader copy from IPv4 packet */
+ if (ih4->protocol == IPPROTO_ICMP)
+ ih6_frag->nexthdr = NEXTHDR_ICMP;
+ else
+ ih6_frag->nexthdr = ih4->protocol;
+
+ /* copy frag offset from IPv4 packet */
+ ih6_frag->frag_off = htons((ntohs(ih4->frag_off) & IP_OFFSET) << 3);
+ /* copy MF flag from IPv4 packet */
+ ih6_frag->frag_off = htons((ntohs(ih6_frag->frag_off) |
+ ((ntohs(ih4->frag_off) & IP_MF) >> 13)));
+ /* copy Identification field from IPv4 packet */
+ ih6_frag->identification = htonl(ntohs(ih4->id));
+ /* reserved field initialized to zero */
+ ih6_frag->reserved = 0;
+ }
+
+ ih6->version = 6;
+
+ if (tos_ignore_flag) {
+ ih6->priority = 0;
+ ih6->flow_lbl[0] = 0;
+ } else {
+ ih6->priority = (ih4->tos & 0xf0) >> 4;
+ ih6->flow_lbl[0] = (ih4->tos & 0x0f) << 4;
+ }
+ ih6->flow_lbl[1] = 0;
+ ih6->flow_lbl[2] = 0;
+
+ /* Hop Limit = IPv4 TTL */
+ ih6->hop_limit = ih4->ttl;
+
+ memcpy(ih6->saddr.s6_addr,addr.s6_addr,16);
+ memcpy(ih6->daddr.s6_addr,dstaddr.s6_addr,16);
+
+ plen = new_tot_len - hdr_len; /* Payload length = IPv4 total len - IPv4 header len */
+ ih6->payload_len = htons(plen);
+ ih6->nexthdr = new_nexthdr; /* Next Header */
+
+ switch (ih4->protocol) {
+ case IPPROTO_ICMP:
+ if ( (ntohs(ih4->frag_off) & IP_OFFSET) != 0 || (ntohs(ih4->frag_off) & IP_MF) != 0 ) {
+ BIH_DEBUG("bih: ip4_ip6(): don't translate ICMPv4 fragments - packet dropped.\n");
+ lprintf(0,"ip4_ip6(): don't translate ICMPv4 fragments - packet dropped.\n");
+ return -1;
+ }
+
+ icmp_hdr = (struct icmphdr *) (src+hdr_len); /* point to ICMPv4 header */
+ csum = 0;
+ icmp_len = ntohs(ih4->tot_len) - hdr_len; /* ICMPv4 packet length */
+ icmp6_hdr = (struct icmp6hdr *)(dst+sizeof(struct ipv6hdr)
+ +fr_flag*sizeof(struct frag_hdr)); /* point to ICMPv6 header */
+
+ if (include_flag) {
+ BIH_DEBUG("bih: ip4_ip6(): It's included ICMPv4 in ICMPv4 Error message - packet dropped.\n");
+ lprintf(0,"ip4_ip6(): It's included ICMPv4 in ICMPv4 Error message - packet dropped.\n");
+ return -1;
+ }
+
+ /* Check ICMPv4 Type field */
+ switch (icmp_hdr->type) {
+ /* ICMP Error messages */
+ /* Destination Unreachable (Type 3) */
+ case ICMP_DEST_UNREACH:
+ icmp6_hdr->icmp6_type = ICMPV6_DEST_UNREACH; /* to Type 1 */
+ icmp6_hdr->icmp6_unused = 0;
+ switch (icmp_hdr->code)
+ {
+ case ICMP_NET_UNREACH: /* Code 0 */
+ case ICMP_HOST_UNREACH: /* Code 1 */
+ case ICMP_SR_FAILED: /* Code 5 */
+ case ICMP_NET_UNKNOWN: /* Code 6 */
+ case ICMP_HOST_UNKNOWN: /* Code 7 */
+ case ICMP_HOST_ISOLATED: /* Code 8 */
+ case ICMP_NET_UNR_TOS: /* Code 11 */
+ case ICMP_HOST_UNR_TOS: /* Code 12 */
+ icmp6_hdr->icmp6_code = ICMPV6_NOROUTE; /* to Code 0 */
+ break;
+ case ICMP_PROT_UNREACH: /* Code 2 */
+ icmp6_hdr->icmp6_type = ICMPV6_PARAMPROB; /* to Type 4 */
+ icmp6_hdr->icmp6_code = ICMPV6_UNK_NEXTHDR; /* to Code 1 */
+ /* Set pointer filed to 6, it's octet offset IPv6 Next Header field */
+ icmp6_hdr->icmp6_pointer = htonl(6);
+ break;
+ case ICMP_PORT_UNREACH: /* Code 3 */
+ icmp6_hdr->icmp6_code = ICMPV6_PORT_UNREACH; /* to Code 4 */
+ break;
+ case ICMP_FRAG_NEEDED: /* Code 4 */
+ icmp6_hdr->icmp6_type = ICMPV6_PKT_TOOBIG; /* to Type 2 */
+ icmp6_hdr->icmp6_code = 0;
+ /* Correct MTU */
+ if (icmp_hdr->un.frag.mtu == 0)
+ icmp6_hdr->icmp6_mtu = htonl(576);
+ else
+ icmp6_hdr->icmp6_mtu = htonl(ntohs(icmp_hdr->un.frag.mtu)+IP4_IP6_HDR_DIFF);
+
+ break;
+ case ICMP_NET_ANO: /* Code 9 */
+ case ICMP_HOST_ANO: /* Code 10 */
+ icmp6_hdr->icmp6_code = ICMPV6_ADM_PROHIBITED; /* to Code 1 */
+ break;
+ default: /* discard any other Code */
+ BIH_DEBUG("bih: ip4_ip6(): Unknown ICMPv4 Type %d Code %d - packet dropped.\n",
+ ICMP_DEST_UNREACH, icmp_hdr->code);
+ lprintf(0,"ip4_ip6(): Unknown ICMPv4 Type %d Code %d - packet dropped.\n",
+ ICMP_DEST_UNREACH, icmp_hdr->code);
+ return -1;
+ }
+ break;
+ /* Time Exceeded (Type 11) */
+ case ICMP_TIME_EXCEEDED:
+ icmp6_hdr->icmp6_type = ICMPV6_TIME_EXCEED;
+ icmp6_hdr->icmp6_code = icmp_hdr->code;
+ break;
+ /* Parameter Problem (Type 12) */
+ case ICMP_PARAMETERPROB:
+ icmp6_hdr->icmp6_type = ICMPV6_PARAMPROB;
+ icmp6_hdr->icmp6_code = icmp_hdr->code;
+
+ icmp_ptr = ntohs(icmp_hdr->un.echo.id) >> 8;
+ switch (icmp_ptr) {
+ case 0:
+ icmp6_hdr->icmp6_pointer = 0;
+ break;
+ case 2:
+ icmp6_hdr->icmp6_pointer = __constant_htonl(4);
+ break;
+ case 8:
+ icmp6_hdr->icmp6_pointer = __constant_htonl(7);
+ break;
+ case 9:
+ icmp6_hdr->icmp6_pointer = __constant_htonl(6);
+ break;
+ case 12:
+ icmp6_hdr->icmp6_pointer = __constant_htonl(8);
+ break;
+ case 16:
+ icmp6_hdr->icmp6_pointer = __constant_htonl(24);
+ break;
+ default:
+ icmp6_hdr->icmp6_pointer = 0xffffffff;
+ break;
+ }
+ break;
+ case ICMP_ECHO:
+ icmperr = 0;
+ icmp6_hdr->icmp6_type = ICMPV6_ECHO_REQUEST;
+ icmp6_hdr->icmp6_code = 0;
+ memcpy(((char *)icmp6_hdr)+4, ((char *)icmp_hdr)+4, len - hdr_len - 4);
+ break;
+
+ case ICMP_ECHOREPLY:
+ icmperr = 0;
+ icmp6_hdr->icmp6_type = ICMPV6_ECHO_REPLY;
+ icmp6_hdr->icmp6_code = 0;
+ memcpy(((char *)icmp6_hdr)+4, ((char *)icmp_hdr)+4, len - hdr_len - 4);
+ break;
+
+ default:
+ BIH_DEBUG("bih: ip4_ip6(): Unknown ICMPv4 packet Type %x - packet dropped.\n", icmp_hdr->type);
+ lprintf(0,"ip4_ip6(): Unknown ICMPv4 packet Type %x - packet dropped.\n", icmp_hdr->type);
+ return -1;
+ }
+
+ if (icmperr) {
+ if (ip4_ip6(dev,src+hdr_len+sizeof(struct icmphdr), len - hdr_len - sizeof(struct icmphdr),
+ dst+sizeof(struct ipv6hdr)+fr_flag*sizeof(struct frag_hdr)
+ +sizeof(struct icmp6hdr), 1) == -1) {
+ BIH_DEBUG("bih: ip4_ip6(): Uncorrect translation of ICMPv4 Error message - packet dropped.\n");
+ lprintf(0,"ip4_ip6(): Uncorrect translation of ICMPv4 Error message - packet dropped.\n");
+ return -1;
+ }
+ icmp_len += 20;
+ plen += 20;
+ ih6->payload_len = htons(plen);
+ }
+
+ icmp6_hdr->icmp6_cksum = 0;
+ csum = 0;
+
+ csum = csum_partial((u_char *)icmp6_hdr, icmp_len, csum);
+ icmp6_hdr->icmp6_cksum = csum_ipv6_magic(&ih6->saddr, &ih6->daddr, icmp_len,
+ IPPROTO_ICMPV6, csum);
+ break;
+
+ case IPPROTO_TCP:
+ memcpy(dst+sizeof(struct ipv6hdr)+fr_flag*sizeof(struct frag_hdr),
+ src+hdr_len, len - hdr_len);
+ tcp_hdr = (struct tcphdr *)(dst+sizeof(struct ipv6hdr)+ fr_flag*sizeof(struct frag_hdr));
+ if(ntohs(ih4->frag_off) == IP_DF||ih4->frag_off==0)
+ {
+ tcp_hdr->check = 0;
+ csum = 0;
+ csum = csum_partial((unsigned char *)tcp_hdr, plen - fr_flag*sizeof(struct frag_hdr), csum);
+ tcp_hdr->check = csum_ipv6_magic(&ih6->saddr, &ih6->daddr, plen - fr_flag*sizeof(struct frag_hdr), IPPROTO_TCP, csum);
+ }
+ else if((ntohs(ih4->frag_off)&IP_OFFSET)==0&&(ntohs(ih4->frag_off)&IP_MF)!= 0)
+ tcp_hdr->check = 0;
+#ifdef __BIHDEBUG__
+{
+ struct tcp *tcp=(struct tcp *)tcp_hdr;
+ ipv6_show(ih6);
+ tcp_show(tcp);
+}
+#endif
+ break;
+
+ case IPPROTO_UDP:
+ udp_hdr = (struct udphdr *)(src+hdr_len);
+ if ((ntohs(ih4->frag_off) & IP_OFFSET) == 0) {
+ if ((ntohs(ih4->frag_off) & IP_MF) != 0) {
+ /* It's a first fragment */
+ if (udp_hdr->check == 0) {
+ BIH_DEBUG("bih: transalte: First fragment of UDP with zero checksum - packet droped\n");
+ BIH_DEBUG("bih: transalte: addr: %x src port: %d dst port: %d\n",
+ htonl(ih4->saddr), htons(udp_hdr->source), htons(udp_hdr->dest));
+ lprintf(0,"transalte: First fragment of UDP with zero checksum - packet droped\n");
+ lprintf(0,"transalte: addr: %x src port: %d dst port: %d\n",
+ htonl(ih4->saddr), htons(udp_hdr->source), htons(udp_hdr->dest));
+ return -1;
+ }
+ }
+ else if (udp_hdr->check == 0)
+ fl_csum = 1;
+ }
+
+ udp_hdr = (struct udphdr *)(dst+sizeof(struct ipv6hdr)
+ + fr_flag*sizeof(struct frag_hdr));
+ memcpy((char *)udp_hdr, src+hdr_len, len - hdr_len);
+
+ fl_csum=1;
+ if (fl_csum && (!include_flag)) {
+ if(ntohs(ih4->frag_off) == IP_DF||ih4->frag_off==0)
+ {
+ udp_hdr->check = 0;
+ csum = 0;
+ csum = csum_partial((unsigned char *)udp_hdr, plen - fr_flag*sizeof(struct frag_hdr), csum);
+ udp_hdr->check = csum_ipv6_magic(&ih6->saddr, &ih6->daddr, plen -
+ fr_flag*sizeof(struct frag_hdr), IPPROTO_UDP, csum);
+ }
+ else if((ntohs(ih4->frag_off)&IP_OFFSET)==0&&(ntohs(ih4->frag_off)&IP_MF)!= 0)
+ udp_hdr->check = 0;
+#ifdef __BIHDEBUG__
+{
+ struct udp *udp=(struct udp *)udp_hdr;
+ ipv6_show(ih6);
+ udp_show(udp);
+ //array_show((unsigned char *)udp_hdr,len-hdr_len,1,16);
+}
+#endif
+ }
+ break;
+
+ /* Discard packets with any other protocol */
+ default:
+ BIH_DEBUG("bih: ip4_ip6(): Unknown upper protocol - packet dropped.\n");
+ lprintf(0,"ip4_ip6(): Unknown upper protocol - packet dropped.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ip6_ip4(struct net_device *dev,char *src, int len, char *dst, int include_flag)/*v6 address be translated*/
+{
+ unsigned int srcaddr,dstaddr;
+ struct in6_addr addr6;
+ struct ipv6hdr *ip6_hdr=(struct ipv6hdr *)src;
+ struct iphdr *ip_hdr=(struct iphdr *)dst;
+ int opts_len = 0;
+ int icmperr = 1;
+ int ntot_len = 0;
+ int real_len;
+ int len_delta;
+ int ip6_payload_len;
+ int inc_opts_len = 0;
+ __u8 next_hdr;
+ int ret;
+ int addr_type;
+ struct map_node *mapnode;
+ struct udphdr *udp_hdr;
+ unsigned char proto=0;
+
+ BIH_DEBUG("bih: ip6_ip4 start\n");
+#if 0
+ //if it is the RA packet, let it go // test
+ //we dont care the linklocal addr translate
+ ret=ipv6_get_laddr(dev,&addr6);
+ if(ret!=0)
+ {
+ BIH_DEBUG("bih: ip4_ip6 get ipv6 laddr failed\n");
+ return(-1);
+ }
+ //if pkt dest ip is link local addr
+ if(memcmp(&ip6_hdr->daddr,&addr6,sizeof(struct in6_addr))!=0)
+ {
+ BIH_DEBUG("bih: ip4_ip6 the dest addr is the linklocal addr\n");
+ if(NEXTHDR_ICMP == ip6_hdr->nexthdr)
+ {
+ BIH_DEBUG("bih: ip4_ip6 the pkt is the icmpv6 pkt\n");
+ return -2;
+ }
+ }
+#endif
+ addr_type=ipv6_addr_type(&ip6_hdr->daddr);
+ ipv6_show(ip6_hdr);
+ if (addr_type&IPV6_ADDR_MULTICAST||
+ addr_type&IPV6_ADDR_LOOPBACK||
+ addr_type&IPV6_ADDR_LINKLOCAL||
+ addr_type&IPV6_ADDR_SITELOCAL)
+ {
+ BIH_DEBUG("bih: ip6_ip4 addr_type=%di return\n",addr_type);
+ return(-1);
+ }
+ addr_type=ipv6_addr_type(&ip6_hdr->saddr);
+ if(addr_type&IPV6_ADDR_LINKLOCAL||
+ addr_type&IPV6_ADDR_SITELOCAL)
+ {
+ BIH_DEBUG("bih: ip6_ip4 addr_type=%d exit\n",addr_type);
+ return(-1);
+ }
+
+ udp_hdr=(struct udphdr *)getipv6uppkg(src,&proto,NULL);
+ if(proto==NEXTHDR_UDP&&ntohs(udp_hdr->source)==53)
+ {
+ BIH_DEBUG("bih: ip6_ip4 dns pkt exit\n");
+ return(-1);
+ }
+ ret=ipv6_get_addr(dev,&addr6);
+ if(ret!=0)
+ {
+ BIH_DEBUG("bih: ip6_ip4 dns pkt exit\n");
+ return(-1);
+ }
+ ret=ipv4_get_addr(dev,&dstaddr);
+ if(ret!=0)
+ {
+ BIH_DEBUG("bih: ip6_ip4 get addr failed\n");
+ return(-1);
+ }
+ if(memcmp(&ip6_hdr->daddr,&addr6,sizeof(struct in6_addr))!=0)
+ {
+
+#ifdef __BIHDEBUG__
+ char dstbuf[64],locbuf[64];
+ inet_ntop6((unsigned char *)&ip6_hdr->daddr.s6_addr, dstbuf, sizeof(dstbuf)-1);
+ inet_ntop6((unsigned char *)&addr6.s6_addr, locbuf, sizeof(locbuf)-1);
+ lprintf(0,"Pkg dst(%s) will be forward\n",dstbuf,locbuf);
+ BIH_DEBUG("bih: Pkg dst(%s) will be forward\n",dstbuf,locbuf);
+#endif
+
+ BIH_DEBUG("bih: ip6_ip4 pkt will be forward\n");
+ return -1;
+ }
+
+ mapnode=map_find(ip6_hdr->saddr);
+ if(mapnode)
+ srcaddr=mapnode->id;
+
+ else
+ {
+ srcaddr=pool_addr_assign(&ip6_hdr->saddr);
+ if(srcaddr==0)
+ {
+ BIH_DEBUG(0,"pool_addr_assign error\n");
+ lprintf(0,"pool_addr_assign error\n");
+ return(-1);
+ }
+ mapnode=(struct map_node *)1;
+ }
+
+#ifdef __BIHDEBUG__
+ BIH_DEBUG("bih: ---------- %s ----------\n",mapnode?"466":"464");
+ lprintf(0,"---------- %s ----------\n",mapnode?"466":"464");
+ ipv6_show(ip6_hdr);
+#endif
+ if ( (len_delta = len - sizeof(struct ipv6hdr)) >= 0)
+ {
+ real_len = sizeof(struct iphdr);
+
+ ip_hdr->frag_off = 0;
+ ip_hdr->id = 0;
+
+ next_hdr = ip6_hdr->nexthdr;
+
+ if (next_hdr == NEXTHDR_HOP) {
+ if ( (len_delta - sizeof(struct ipv6_opt_hdr)) >= 0)
+ {
+ struct ipv6_opt_hdr *ip6h =
+ (struct ipv6_opt_hdr *)(src+sizeof(struct ipv6hdr) + opts_len);
+ if ( (len_delta -= ip6h->hdrlen*8 + 8) >= 0)
+ {
+ opts_len += ip6h->hdrlen*8 + 8;
+ next_hdr = ip6h->nexthdr;
+ }
+ else
+ {
+ BIH_DEBUG("bih: ip6_ip4(): hop_by_hop header error, packet droped\n");
+ lprintf(0,"ip6_ip4(): hop_by_hop header error, packet droped\n");
+ return -1;
+ }
+ }
+ }
+
+ if (len_delta > 0)
+ {
+ while(next_hdr != NEXTHDR_ICMP && next_hdr != NEXTHDR_TCP
+ && next_hdr != NEXTHDR_UDP)
+ {
+ /* Destination options header */
+ if (next_hdr == NEXTHDR_DEST)
+ {
+ if ( (len_delta - sizeof(struct ipv6_opt_hdr)) >= 0)
+ {
+ struct ipv6_opt_hdr *ip6d =
+ (struct ipv6_opt_hdr *)(src + sizeof(struct ipv6hdr) + opts_len);
+ if ( (len_delta -= ip6d->hdrlen*8 + 8) >= 0)
+ {
+ opts_len += ip6d->hdrlen*8 + 8;
+ next_hdr = ip6d->nexthdr;
+ }
+ }
+ else
+ {
+ BIH_DEBUG("bih: ip6_ip4(): destination header error, packet droped");
+ lprintf(0,"ip6_ip4(): destination header error, packet droped");
+ return -1;
+ }
+ }
+ else if (next_hdr == NEXTHDR_ROUTING)
+ {
+ if ( (len_delta - sizeof(struct ipv6_rt_hdr)) >= 0)
+ {
+ struct ipv6_rt_hdr *ip6rt =
+ (struct ipv6_rt_hdr *)(src+sizeof(struct ipv6hdr) + opts_len);
+ if (ip6rt->segments_left != 0) {
+ BIH_DEBUG("bih: ip6_ip4(): routing header type != 0\n");
+ lprintf(0,"ip6_ip4(): routing header type != 0\n");
+ return -1;
+ }
+ if ( (len_delta -= ip6rt->hdrlen*8 + 8) >= 0)
+ {
+ opts_len += ip6rt->hdrlen*8 + 8;
+ next_hdr = ip6rt->nexthdr;
+ }
+ else
+ {
+ BIH_DEBUG("bih: ip6_ip4(): routing header error, packet droped");
+ lprintf(0,"ip6_ip4(): routing header error, packet droped");
+ return -1;
+ }
+ }
+ }
+ else if (next_hdr == NEXTHDR_FRAGMENT)
+ {
+ if ( (len_delta -= sizeof(struct frag_hdr)) >= 0)
+ {
+ struct frag_hdr *ip6f =
+ (struct frag_hdr *)(src+sizeof(struct ipv6hdr)+opts_len);
+
+ opts_len += sizeof(struct frag_hdr); /* Frag Header Length = 8 */
+ ip_hdr->id = htons(ntohl(ip6f->identification)); /* ID field */
+ ip_hdr->frag_off = htons((ntohs(ip6f->frag_off) & IP6F_OFF_MASK) >> 3);
+ /* fragment offset */
+ ip_hdr->frag_off = htons(ntohs(ip_hdr->frag_off) |
+ ((ntohs(ip6f->frag_off) & IP6F_MORE_FRAG) << 13));
+ /* more fragments flag */
+ next_hdr = ip6f->nexthdr;
+ }
+ else
+ {
+ BIH_DEBUG("bih: ip6_ip4(): fragment header error, packet droped");
+ lprintf(0,"ip6_ip4(): fragment header error, packet droped");
+ return -1;
+ }
+ }
+ else if (next_hdr == NEXTHDR_NONE)
+ {
+ break;
+ }
+ else if (next_hdr == NEXTHDR_ESP || next_hdr == NEXTHDR_AUTH)
+ {
+ BIH_DEBUG("bih: ip6_ip4(): cannot translate AUTH or ESP extention header, packet dropped\n");
+ lprintf(0,"ip6_ip4(): cannot translate AUTH or ESP extention header, packet dropped\n");
+ return -1;
+ }
+ else if (next_hdr == NEXTHDR_IPV6)
+ {
+ BIH_DEBUG("bih: ip6_ip4(): cannot translate IPv6-IPv6 packet, packet dropped\n");
+ lprintf(0,"ip6_ip4(): cannot translate IPv6-IPv6 packet, packet dropped\n");
+ return -1;
+ }
+ else if (next_hdr == 0)
+ {
+ BIH_DEBUG("bih: ip6_ip4(): NEXTHDR in extention header = 0, packet dropped\n");
+ lprintf(0,"ip6_ip4(): NEXTHDR in extention header = 0, packet dropped\n");
+ return -1;
+ }
+ else
+ {
+ BIH_DEBUG("bih: ip6_ip4(): cannot translate extention header = %d, packet dropped\n", next_hdr);
+ lprintf(0,"ip6_ip4(): cannot translate extention header = %d, packet dropped\n", next_hdr);
+ return -1;
+ }
+ }
+ }
+ }
+ else
+ {
+ BIH_DEBUG("bih: ip6_ip4(): error packet len, packet dropped.\n");
+ lprintf(0,"ip6_ip4(): error packet len, packet dropped.\n");
+ return -1;
+ }
+
+ ip_hdr->version = IPVERSION;
+ ip_hdr->ihl = 5;
+
+ if (tos_ignore_flag)
+ ip_hdr->tos = 0;
+ else {
+ ip_hdr->tos = ip6_hdr->priority << 4;
+ ip_hdr->tos = ip_hdr->tos | (ip6_hdr->flow_lbl[0] >> 4);
+ }
+
+ ip6_payload_len = ntohs(ip6_hdr->payload_len);
+
+ if (ip6_payload_len == 0)
+ ntot_len = 0;
+ else
+ ntot_len = ip6_payload_len + IP4_IP6_HDR_DIFF - opts_len;
+
+ ip_hdr->tot_len = htons(ntot_len);
+ ip_hdr->ttl = ip6_hdr->hop_limit;
+ ip_hdr->protocol = next_hdr;
+
+ ip_hdr->saddr = srcaddr;
+ ip_hdr->daddr = dstaddr;
+
+ ip_hdr->check = 0;
+ ip_hdr->check = ip_fast_csum((unsigned char *)ip_hdr, ip_hdr->ihl);
+
+ if (len_delta > 0)
+ {
+ if (next_hdr == NEXTHDR_ICMP)
+ {
+ struct icmp6hdr *icmp6_hdr;
+ struct icmphdr *icmp_hdr;
+
+ if ((len_delta -= sizeof(struct icmp6hdr)) >= 0)
+ {
+ icmp6_hdr = (struct icmp6hdr *)(src + sizeof(struct ipv6hdr) + opts_len);
+ icmp_hdr = (struct icmphdr *)(dst + sizeof(struct iphdr));
+
+ real_len += len_delta + sizeof(struct icmphdr);
+
+ ip_hdr->protocol = IPPROTO_ICMP;
+
+ if (include_flag) {
+ if (icmp6_hdr->icmp6_type != ICMPV6_ECHO_REQUEST)
+ {
+ BIH_DEBUG("bih: ip6_ip4(): included ICMPv6 in ICMPv6 Error message, packet dropped\n");
+ lprintf(0,"ip6_ip4(): included ICMPv6 in ICMPv6 Error message, packet dropped\n");
+ return -1;
+ }
+ }
+
+ switch (icmp6_hdr->icmp6_type)
+ {
+ case ICMPV6_DEST_UNREACH: /* Type 1 */
+ icmp_hdr->type = ICMP_DEST_UNREACH; /* to Type 3 */
+ icmp_hdr->un.echo.id = 0;
+ icmp_hdr->un.echo.sequence = 0;
+ switch (icmp6_hdr->icmp6_code)
+ {
+ case ICMPV6_NOROUTE: /* Code 0 */
+ case ICMPV6_NOT_NEIGHBOUR: /* Code 2 */
+ case ICMPV6_ADDR_UNREACH: /* Code 3 */
+ icmp_hdr->code = ICMP_HOST_UNREACH; /* To Code 1 */
+ break;
+ case ICMPV6_ADM_PROHIBITED: /* Code 1 */
+ icmp_hdr->code = ICMP_HOST_ANO; /* To Code 10 */
+ break;
+ case ICMPV6_PORT_UNREACH: /* Code 4 */
+ icmp_hdr->code = ICMP_PORT_UNREACH; /* To Code 3 */
+
+ break;
+ default: /* discard any other codes */
+ BIH_DEBUG("bih: ip6_ip4(): Unknown ICMPv6 Type %d Code %d - packet dropped.\n",
+ ICMPV6_DEST_UNREACH, icmp6_hdr->icmp6_code);
+ lprintf(0,"ip6_ip4(): Unknown ICMPv6 Type %d Code %d - packet dropped.\n",
+ ICMPV6_DEST_UNREACH, icmp6_hdr->icmp6_code);
+ return -1;
+ }
+ break;
+ case ICMPV6_PKT_TOOBIG: /* Type 2 */
+ icmp_hdr->type = ICMP_DEST_UNREACH; /* to Type 3 */
+ icmp_hdr->code = ICMP_FRAG_NEEDED; /* to Code 4 */
+ icmp_hdr->un.frag.mtu = (__u16) icmp6_hdr->icmp6_mtu;
+ break;
+ case ICMPV6_TIME_EXCEED:
+ icmp_hdr->type = ICMP_TIME_EXCEEDED; /* to Type 11 */
+ icmp_hdr->code = icmp6_hdr->icmp6_code; /* Code unchanged */
+ break;
+ case ICMPV6_PARAMPROB:
+ switch (icmp6_hdr->icmp6_code) {
+ case ICMPV6_UNK_NEXTHDR: /* Code 1 */
+ icmp_hdr->type = ICMP_DEST_UNREACH; /* to Type 3 */
+ icmp_hdr->code = ICMP_PROT_UNREACH; /* to Code 2 */
+ break;
+ default: /* if Code != 1 */
+ icmp_hdr->type = ICMP_PARAMETERPROB; /* to Type 12 */
+ icmp_hdr->code = 0; /* to Code 0 */
+ switch (ntohl(icmp6_hdr->icmp6_pointer))
+ {
+ case 0: /* IPv6 Version -> IPv4 Version */
+ icmp_hdr->un.echo.id = 0;
+ break;
+ case 4: /* IPv6 PayloadLength -> IPv4 Total Length */
+ icmp_hdr->un.echo.id = 0x0002; /* 2 */
+ break;
+ case 6: /* IPv6 Next Header-> IPv4 Protocol */
+ icmp_hdr->un.echo.id = 0x0009; /* 9 */
+ break;
+ case 7: /* IPv6 Hop Limit -> IPv4 TTL */
+ icmp_hdr->un.echo.id = 0x0008; /* 8 */
+ break;
+ case 8: /* IPv6 Src addr -> IPv4 Src addr */
+ icmp_hdr->un.echo.id = 0x000c; /* 12 */
+ break;
+ case 24: /* IPv6 Dst addr -> IPv4 Dst addr*/
+ icmp_hdr->un.echo.id = 0x0010; /* 16 */
+ break;
+ default: /* set all ones in other cases */
+ icmp_hdr->un.echo.id = 0xff;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case ICMPV6_ECHO_REQUEST:
+ icmperr = 0; /* not error ICMP message */
+ icmp_hdr->type = ICMP_ECHO; /* to Type 8 */
+ icmp_hdr->code = 0; /* to Code 0 */
+ icmp_hdr->un.echo.id = icmp6_hdr->icmp6_identifier;
+ icmp_hdr->un.echo.sequence = icmp6_hdr->icmp6_sequence;
+ if (len_delta > 0)
+ memcpy(((char *)icmp_hdr) + sizeof(struct icmphdr),
+ ((char *)icmp6_hdr) + sizeof(struct icmp6hdr), len_delta);
+ break;
+ case ICMPV6_ECHO_REPLY:
+ icmperr = 0; /* not error ICMP message */
+ icmp_hdr->type = ICMP_ECHOREPLY; /* to Type 0 */
+ icmp_hdr->code = 0; /* to Code 0 */
+ icmp_hdr->un.echo.id = icmp6_hdr->icmp6_identifier;
+ icmp_hdr->un.echo.sequence = icmp6_hdr->icmp6_sequence;
+ if (len_delta > 0)
+ memcpy(((char *)icmp_hdr) + sizeof(struct icmphdr),
+ ((char *)icmp6_hdr) + sizeof(struct icmp6hdr), len_delta);
+ break;
+ default:
+ BIH_DEBUG("bih: ip6_ip4(): unknown ICMPv6 Type %d, packet dropped.\n", icmp6_hdr->icmp6_type);
+ lprintf(0,"ip6_ip4(): unknown ICMPv6 Type %d, packet dropped.\n", icmp6_hdr->icmp6_type);
+ return -1;
+ }
+
+ if (icmperr)
+ {
+ if (len_delta >= sizeof(struct ipv6hdr))
+ {
+ if((inc_opts_len = ip6_ip4(dev,(char *)icmp6_hdr + sizeof(struct icmp6hdr), len_delta,
+ (char *)icmp_hdr + sizeof(struct icmphdr), 1)) == -1) {
+ BIH_DEBUG("bih: ip6_ip4(): incorrect translation of ICMPv6 Error message, packet dropped\n");
+ lprintf(0,"ip6_ip4(): incorrect translation of ICMPv6 Error message, packet dropped\n");
+ return -1;
+ }
+ if (ntot_len != 0)
+ ip_hdr->tot_len = htons(ntot_len - inc_opts_len - IP4_IP6_HDR_DIFF);
+ real_len = real_len - inc_opts_len - IP4_IP6_HDR_DIFF;
+ }
+ else if (len_delta > 0)
+ {
+ memcpy(((char *)icmp_hdr) + sizeof(struct icmphdr),
+ ((char *)icmp6_hdr) + sizeof(struct icmp6hdr), len_delta);
+ }
+ }
+
+ ip_hdr->check = 0;
+ ip_hdr->check = ip_fast_csum((unsigned char *)ip_hdr, ip_hdr->ihl);
+
+ if (ntot_len != 0)
+ {
+ icmp_hdr->checksum = 0;
+ icmp_hdr->checksum = ip_compute_csum((unsigned char *)icmp_hdr, ntohs(ip_hdr->tot_len)
+ - sizeof(struct iphdr));
+ }
+ }
+ else
+ {
+ BIH_DEBUG("bih: ip6_ip4(): error length ICMP packet, packet dropped.\n");
+ lprintf(0,"ip6_ip4(): error length ICMP packet, packet dropped.\n");
+ return -1;
+ }
+
+ }
+ else {
+ unsigned int csum;
+ struct udphdr *udp_hdr;
+ struct tcphdr *tcp_hdr;
+ real_len += len_delta;
+ memcpy(dst+sizeof(struct iphdr), src + sizeof(struct ipv6hdr) + opts_len, len_delta);
+ if (next_hdr == NEXTHDR_TCP)
+ {
+ tcp_hdr = (struct tcphdr *)(dst+sizeof(struct iphdr));
+ if(ntohs(ip_hdr->frag_off) == IP_DF||ip_hdr->frag_off==0)
+ {
+ tcp_hdr->check = 0;
+ csum = 0;
+ csum = csum_partial((unsigned char *)tcp_hdr, len_delta, csum);
+ tcp_hdr->check = csum_tcpudp_magic(ip_hdr->saddr, ip_hdr->daddr, len_delta, IPPROTO_TCP, csum);
+ }
+ else if((ntohs(ip_hdr->frag_off)&IP_OFFSET)==0&&(ntohs(ip_hdr->frag_off)&IP_MF)!= 0)
+ tcp_hdr->check = 0;
+#ifdef __BIHDEBUG__
+{
+ struct tcp *tcp=(struct tcp *)tcp_hdr;
+ struct ip *ip=(struct ip *)ip_hdr;
+ ip_show(ip);
+ tcp_show(tcp);
+}
+#endif
+ }
+ else if (next_hdr == NEXTHDR_UDP)
+ {
+ udp_hdr = (struct udphdr *)(dst+sizeof(struct iphdr));
+ if(ntohs(ip_hdr->frag_off) == IP_DF||ip_hdr->frag_off==0)
+ {
+ udp_hdr->check = 0;
+ csum = 0;
+ csum = csum_partial((unsigned char *)udp_hdr, len_delta, csum);
+ udp_hdr->check = csum_tcpudp_magic(ip_hdr->saddr, ip_hdr->daddr, len_delta, IPPROTO_UDP, csum);
+ }
+ else if((ntohs(ip_hdr->frag_off)&IP_OFFSET)==0&&(ntohs(ip_hdr->frag_off)&IP_MF)!= 0)
+ udp_hdr->check = 0;
+#ifdef __BIHDEBUG__
+{
+ struct udp *udp=(struct udp *)udp_hdr;
+ struct ip *ip=(struct ip *)ip_hdr;
+ ip_show(ip);
+ udp_show(udp);
+ //array_show(dst+sizeof(struct iphdr), len_delta,1,16);
+}
+#endif
+ }
+ }
+ }
+ if (include_flag)
+ return opts_len;
+ else
+ return real_len;
+}
+
+static int ip4_fragment(struct sk_buff *skb, int len, int hdr_len, struct net_device *dev)
+{
+ struct sk_buff *skb2 = NULL; /* pointer to new struct sk_buff for transleded packet */
+ char buff[FRAG_BUFF_SIZE+hdr_len]; /* buffer to form new fragment packet */
+ char *cur_ptr = skb->data+hdr_len; /* pointter to current packet data with len = frag_len */
+ struct iphdr *ih4 = (struct iphdr *) skb->data;
+ struct iphdr *new_ih4 = (struct iphdr *) buff; /* point to new IPv4 hdr */
+ struct ethhdr *new_eth_h; /* point to ether hdr, need to set hard header data in fragment */
+ int data_len = len - hdr_len; /* origin packet data len */
+ int rest_len = data_len; /* rest data to fragment */
+ int frag_len = 0; /* current fragment len */
+ int last_frag = 0; /* last fragment flag, if = 1, it's last fragment */
+ int flag_last_mf = 0;
+ int ret;
+ __u16 new_id = 0; /* to generate identification field */
+ __u16 frag_offset = 0; /* fragment offset */
+ unsigned int csum;
+ unsigned short udp_len;
+
+ BIH_DEBUG("bih: ip4_fragment start\n");
+ if ((ntohs(ih4->frag_off) & IP_MF) == 0 )
+ /* it's a case we'll clear MF flag in our last packet */
+ flag_last_mf = 1;
+
+ if (ih4->protocol == IPPROTO_UDP) {
+ if ( (ntohs(ih4->frag_off) & IP_OFFSET) == 0) {
+ struct udphdr *udp_hdr = (struct udphdr *)((char *)ih4 + hdr_len);
+ if (!flag_last_mf) {
+ if (udp_hdr->check == 0) {
+ /* it's a first fragment with ZERO checksum and we drop packet */
+ printk("translate: First fragment of UDP with zero checksum - packet droped\n");
+ printk("translate: addr: %x src port: %d dst port: %d\n",
+ htonl(ih4->saddr), htons(udp_hdr->source), htons(udp_hdr->dest));
+ return -1;
+ }
+ }
+ else if (udp_hdr->check == 0) {
+ /* Calculate UDP checksum only if it's not fragment */
+ udp_len = ntohs(udp_hdr->len);
+ csum = 0;
+ csum = csum_partial((unsigned char *)udp_hdr, udp_len, csum);
+ udp_hdr->check = csum_tcpudp_magic(ih4->saddr, ih4->daddr, udp_len, IPPROTO_UDP, csum);
+ }
+ }
+ }
+ frag_offset = ntohs(ih4->frag_off) & IP_OFFSET;
+ new_id = ih4->id;
+ while(1) {
+ if (rest_len <= FRAG_BUFF_SIZE) {
+ /* it's last fragmen */
+ frag_len = rest_len; /* rest data */
+ last_frag = 1;
+ }
+ else
+ frag_len = FRAG_BUFF_SIZE;
+ /* copy IP header to buffer */
+ memcpy(buff, skb->data, hdr_len);
+ /* copy data to buffer with len = frag_len */
+ memcpy(buff + hdr_len, cur_ptr, frag_len);
+ /* set id field in new IPv4 header*/
+ new_ih4->id = new_id;
+ /* is it last fragmet */
+ if(last_frag && flag_last_mf)
+ /* clear MF flag */
+ new_ih4->frag_off = htons(frag_offset & (~IP_MF));
+ else
+ /* set MF flag */
+ new_ih4->frag_off = htons(frag_offset | IP_MF);
+ /* change packet total length */
+ new_ih4->tot_len = htons(frag_len+hdr_len);
+ /* rebuild the header checksum (IP needs it) */
+ new_ih4->check = 0;
+ new_ih4->check = ip_fast_csum((unsigned char *)new_ih4,new_ih4->ihl);
+ /* Allocate new sk_buff to compose translated packet */
+ skb2 = dev_alloc_skb(frag_len+hdr_len+dev->hard_header_len+IP4_IP6_HDR_DIFF+IP6_FRAGMENT_SIZE);
+ if (!skb2) {
+ printk(KERN_DEBUG "%s: alloc_skb failure - packet dropped.\n", dev->name);
+ dev_kfree_skb(skb2);
+ return -1;
+ }
+ skb_put(skb2, frag_len+hdr_len+dev->hard_header_len+IP4_IP6_HDR_DIFF+IP6_FRAGMENT_SIZE);
+ new_eth_h = (struct ethhdr *)skb2->data;
+ new_eth_h->h_proto = htons(ETH_P_IPV6);
+ skb_reset_mac_header(skb2);
+ skb_pull(skb2, dev->hard_header_len);
+ skb_reset_network_header(skb2);
+ skb2->protocol = htons(ETH_P_IPV6);
+ /* call translation function */
+ if ( ip4_ip6(dev,buff, frag_len+hdr_len, skb2->data, 0) == -1) {
+// lprintf(0,"ip4_ip6 error\n");
+ dev_kfree_skb(skb2);
+ return -1;
+ }
+ /*
+ * Set needed fields in new sk_buff
+ */
+ skb2->dev = dev;
+ skb2->ip_summed = CHECKSUM_UNNECESSARY;
+ skb2->pkt_type = PACKET_HOST;
+
+ /* send packet to upper layer */
+// ret=ipv6_rcv(skb2,dev,NULL,NULL);
+ ret=netif_rx(skb2);
+ if(ret!=0)
+ {
+ BIH_DEBUG("bih: ipv6_rcv error\n");
+ lprintf(0,"ipv6_rcv error\n");
+ dev_kfree_skb(skb2);
+ return -1;
+ }
+ /* exit if it was last fragment */
+ if (last_frag)
+ break;
+
+ /* correct current data pointer */
+ cur_ptr += frag_len;
+ /* rest data len */
+ rest_len -= frag_len;
+ /* current fragment offset */
+ frag_offset = (frag_offset*8 + frag_len)/8;
+ }
+ return 0;
+}
+
+int bih_xmit(struct sk_buff *skb,struct net_device *dev)
+{
+ struct sk_buff *skb2 = NULL, *skb3=NULL;
+ int ret,new_packet_len,len,skb_delta = 0;
+ static char *new_packet_buff;
+ struct ethhdr *eth_h;
+ struct in6_addr addr;
+
+ BIH_DEBUG("bih: bih_xmit start\n");
+ if (skb == NULL || dev==NULL)
+ {
+ return -1;
+ }
+/*
+ if(skb->dev==bihdev||BIHMODE!=1)
+ return(-1);
+*/
+ BIH_DEBUG("bih: bih_xmit get ipv6 addr start\n");
+ ret=ipv6_get_addr(dev,&addr);
+ if(ret!=0)
+ {
+#if 0 //test
+ BIH_DEBUG("bih: bih_xmit get ipv6 addr failed\n");
+ if(0 == ipv6_get_laddr(dev, &addr))
+ {
+ BIH_DEBUG("bih: bih_xmit get ipv6 linklocal addr success\n");
+ ret=netif_rx(skb);
+ if(ret!=0)
+ BIH_DEBUG("bih: bih_xmitip_rcv|ip6_rcv error(ret=%d)\n",ret);
+ else
+ kfree_skb(skb);
+ return(ret);
+ }
+#endif
+ BIH_DEBUG("bih: bih_xmit get ipv6 linklocal addr failed\n");
+ return(-1);
+ }
+ BIH_DEBUG("bih: bih_xmit get ipv6 addr end\n");
+
+ if(new_packet_buff==NULL)
+ {
+ new_packet_buff=kmalloc(2048,GFP_KERNEL);
+ if(new_packet_buff==NULL)
+ {
+ BIH_DEBUG("bih: kmalloc error\n");
+ lprintf(0,"kmalloc error\n");
+ return(-1);
+ }
+ }
+
+ dev->trans_start = jiffies;
+ if (ntohs(skb->protocol) == ETH_P_IP)
+ {
+ int hdr_len;
+ int data_len;
+ struct iphdr *ih4;
+ struct icmphdr *icmp_hdr;
+
+ BIH_DEBUG("bih: bih_xmit ip4\n");
+ ih4 = (struct iphdr *)skb->data; /* point to incoming packet's IPv4 header */
+ if (skb->len != ntohs(ih4->tot_len)) {
+ lprintf(0,"skb error\n");
+ BIH_DEBUG("bih: bih_xmit skb error");
+ return -1;
+ }
+ len = skb->len;
+ hdr_len = (int)(ih4->ihl * 4);
+ data_len = len - hdr_len;
+
+// if ( (ntohs(ih4->frag_off) & IP_DF) == 0 )
+ {
+ if ( data_len > FRAG_BUFF_SIZE )
+// if ( data_len > 1440 )
+ {
+ if(skb_is_nonlinear(skb))
+ {
+ skb3=skb_copy(skb, GFP_ATOMIC);
+ if(skb3)
+ {
+ skb3->sk=skb->sk;
+ }
+ }
+ else
+ skb3=skb;
+ len = skb3->len;
+ ret=ip4_fragment(skb3, len, hdr_len, dev);
+ if(skb3!=skb)
+ dev_kfree_skb(skb3);
+ if(ret==0)
+ dev_kfree_skb(skb);
+ BIH_DEBUG("bih: bih_xmit data_len=%d is bigger than FRAG_BUFF_SIZE(%d)", data_len, FRAG_BUFF_SIZE);
+ return ret;
+ }
+ }
+ if ( ntohs(ih4->frag_off) == IP_DF )
+ skb_delta = IP4_IP6_HDR_DIFF;
+ else
+ skb_delta = IP4_IP6_HDR_DIFF + IP6_FRAGMENT_SIZE;
+ if ( ih4->protocol == IPPROTO_ICMP) {
+ icmp_hdr = (struct icmphdr *) (skb->data+hdr_len);
+ if ( icmp_hdr->type != ICMP_ECHO && icmp_hdr->type != ICMP_ECHOREPLY) {
+ skb_delta += IP4_IP6_HDR_DIFF;
+ }
+ }
+ skb2 = dev_alloc_skb(len+dev->hard_header_len+skb_delta);
+ if (!skb2) {
+ lprintf(0,"dev_alloc_skb error\n");
+ BIH_DEBUG("bih: bih_xmit dev_alloc_skb failed\n");
+ return -1;
+ }
+ skb_put(skb2, len+dev->hard_header_len+skb_delta);
+ eth_h = (struct ethhdr *)skb2->data;
+ eth_h->h_proto = htons(ETH_P_IPV6);
+ skb_reset_mac_header(skb2);
+ skb_pull(skb2,dev->hard_header_len);
+// skb2->h.raw = skb2->nh.raw = skb2->data;
+ skb_reset_network_header(skb2);
+ skb2->protocol = htons(ETH_P_IPV6);
+
+ if(skb_is_nonlinear(skb))
+ {
+ skb3=skb_copy(skb, GFP_ATOMIC);
+ if(skb3)
+ {
+ skb3->sk=skb->sk;
+ }
+ }
+ else
+ skb3=skb;
+ if (ip4_ip6(dev,skb3->data, len, skb2->data, 0) == -1 ) {
+ if(skb3!=skb)
+ dev_kfree_skb(skb3);
+ dev_kfree_skb(skb2);
+ BIH_DEBUG("bih: bih_xmit ip4 to ip6 failed\n");
+ return -1;
+ }
+ if(skb3!=skb)
+ dev_kfree_skb(skb3);
+ skb2->dev = bihdev;
+ }
+ else if (ntohs(skb->protocol) == ETH_P_IPV6)
+ {
+ BIH_DEBUG("bih: bih_xmit ip6\n");
+ len = skb->len;
+ if(skb_is_nonlinear(skb))
+ {
+ skb3=skb_copy(skb, GFP_ATOMIC);
+ if(skb3)
+ {
+ skb3->sk=skb->sk;
+ }
+ }
+ else
+ skb3=skb;
+ new_packet_len = ip6_ip4(dev,skb3->data, len, new_packet_buff, 0);
+ if(new_packet_len==-1)
+ {
+ if(skb3!=skb)
+ dev_kfree_skb(skb3);
+ BIH_DEBUG("bih: bih_xmit ip6 to ip4 failed\n");
+ return(-1);
+ }
+#if 0
+ else if(new_packet_len=-2) //add
+ {
+ ret = netif_rx(skb3);
+ if(ret!=0)
+ BIH_DEBUG("bih: ip_rcv|ip6_rcv icmpv6 error(ret=%d)\n",ret);
+ else
+ kfree_skb(skb);
+ return(ret);
+ }
+#endif
+ if(skb3!=skb)
+ dev_kfree_skb(skb3);
+ skb2 = dev_alloc_skb(new_packet_len + dev->hard_header_len);
+ if (!skb2)
+ {
+ BIH_DEBUG("bih: dev_alloc_skb error\n");
+ lprintf(0,"dev_alloc_skb error\n");
+ return(-1);
+ }
+ skb_put(skb2, new_packet_len + dev->hard_header_len);
+ eth_h = (struct ethhdr *)skb2->data;
+ eth_h->h_proto = htons(ETH_P_IP);
+ skb_reset_mac_header(skb2);
+ skb_pull(skb2, dev->hard_header_len);
+ memcpy(skb2->data, new_packet_buff, new_packet_len);
+// skb2->h.raw = skb2->nh.raw = skb2->data;
+ skb_reset_network_header(skb2);
+ skb_reset_transport_header(skb2);
+ skb2->mac_len = skb2->network_header - skb2->mac_header;
+ skb2->protocol = htons(ETH_P_IP);
+ skb2->dev=skb->dev;
+ }
+ else {
+ BIH_DEBUG("bih: bih_xmit skb->protocol=%d exception\n",ntohs(skb->protocol));
+ return(-1);
+ }
+
+ skb2->pkt_type = PACKET_HOST;
+ skb2->dev = dev;
+ skb2->ip_summed = CHECKSUM_UNNECESSARY;
+ if (skb->sk)
+ skb_set_owner_w(skb2, skb->sk);
+
+ BIH_DEBUG("bih: send data to netif_rx\n");
+ ret=netif_rx(skb2);
+/*
+ if (ntohs(skb2->protocol) == ETH_P_IP)
+ ret=ip_rcv(skb2,dev,NULL,NULL);
+ else
+ ret=ipv6_rcv(skb2,dev,NULL,NULL);
+*/
+ if(ret!=0)
+ printk("bih: ip_rcv|ip6_rcv error(ret=%d)\n",ret);
+ else
+ kfree_skb(skb);
+ return(ret);
+}