Пример #1
0
/*---------------------------------------------------------------------------*/
struct ip64_addrmap_entry *
ip64_addrmap_lookup(const uip_ip6addr_t *ip6addr,
		    uint16_t ip6port,
		    const uip_ip4addr_t *ip4addr,
		    uint16_t ip4port,
		    uint8_t protocol)
{
  struct ip64_addrmap_entry *m;

  printf("lookup ip4port %d ip6port %d\n", uip_htons(ip4port),
	 uip_htons(ip6port));
  check_age();
  for(m = list_head(entrylist); m != NULL; m = list_item_next(m)) {
    printf("protocol %d %d, ip4port %d %d, ip6port %d %d, ip4 %d ip6 %d\n",
	   m->protocol, protocol,
	   m->ip4port, ip4port,
	   m->ip6port, ip6port,
	   uip_ip4addr_cmp(&m->ip4addr, ip4addr),
	   uip_ip6addr_cmp(&m->ip6addr, ip6addr));
    if(m->protocol == protocol &&
       m->ip4port == ip4port &&
       m->ip6port == ip6port &&
       uip_ip4addr_cmp(&m->ip4addr, ip4addr) &&
       uip_ip6addr_cmp(&m->ip6addr, ip6addr)) {
      return m;
    }
  }
  return NULL;
}
Пример #2
0
void
uip_arp_arpin(nic_interface_t *nic_iface,
	      struct uip_stack *ustack, packet_t *pkt)
{
	struct arp_hdr *arp;
	struct uip_eth_hdr *eth;

	if (pkt->buf_size < sizeof(struct arp_hdr)) {
		pkt->buf_size = 0;
		return;
	}
	pkt->buf_size = 0;

	eth = (struct uip_eth_hdr *)pkt->data_link_layer;
	arp = (struct arp_hdr *)pkt->network_layer;

	switch (arp->opcode) {
	case const_htons(ARP_REQUEST):
		/* ARP request. If it asked for our address, we send out a
		   reply. */
		if (uip_ip4addr_cmp(arp->dipaddr, ustack->hostaddr)) {
			/* First, we register the one who made the request in
			   our ARP table, since it is likely that we will do
			   more communication with this host in the future. */
			uip_arp_update(arp->sipaddr, &arp->shwaddr);

			/* The reply opcode is 2. */
			arp->opcode = htons(2);

			memcpy(arp->dhwaddr.addr, arp->shwaddr.addr, 6);
			memcpy(arp->shwaddr.addr, ustack->uip_ethaddr.addr, 6);
			memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6);
			memcpy(eth->dest.addr, arp->dhwaddr.addr, 6);

			arp->dipaddr[0] = arp->sipaddr[0];
			arp->dipaddr[1] = arp->sipaddr[1];
			arp->sipaddr[0] = ustack->hostaddr[0];
			arp->sipaddr[1] = ustack->hostaddr[1];

			if (nic_iface->vlan_id == 0) {
				eth->type = htons(UIP_ETHTYPE_ARP);
				pkt->buf_size = sizeof(*arp) + sizeof(*eth);
			} else {
				eth->type = htons(UIP_ETHTYPE_8021Q);
				pkt->buf_size = sizeof(*arp) +
						sizeof(struct uip_vlan_eth_hdr);
			}
		}
		break;
	case const_htons(ARP_REPLY):
		uip_arp_update(arp->sipaddr, &arp->shwaddr);
		break;
	default:
		LOG_WARN("Unknown ARP opcode: %d", ntohs(arp->opcode));
		break;
	}

	return;
}
Пример #3
0
/**
 * ARP processing for incoming ARP packets.
 *
 * This function should be called by the device driver when an ARP
 * packet has been received. The function will act differently
 * depending on the ARP packet type: if it is a reply for a request
 * that we previously sent out, the ARP cache will be filled in with
 * the values from the ARP reply. If the incoming ARP packet is an ARP
 * request for our IP address, an ARP reply packet is created and put
 * into the uip_buf[] buffer.
 *
 * When the function returns, the value of the global variable uip_len
 * indicates whether the device driver should send out a packet or
 * not. If uip_len is zero, no packet should be sent. If uip_len is
 * non-zero, it contains the length of the outbound packet that is
 * present in the uip_buf[] buffer.
 *
 * This function expects an ARP packet with a prepended Ethernet
 * header in the uip_buf[] buffer, and the length of the packet in the
 * global variable uip_len.
 */
void uip_arp_ipin(struct uip_stack *ustack, packet_t *pkt)
{
	struct ip_hdr *ip;
	struct uip_eth_hdr *eth;

	eth = (struct uip_eth_hdr *)pkt->data_link_layer;
	ip = (struct ip_hdr *)pkt->network_layer;

	if (uip_ip4addr_cmp(ip->destipaddr, ustack->hostaddr)) {
		/* First, we register the one who made the request in our ARP
		   table, since it is likely that we will do more communication
		   with this host in the future. */
		uip_arp_update(ip->srcipaddr, &eth->src);
	}
}
Пример #4
0
arp_out_t is_in_arp_table(u16_t *ipaddr, struct arp_entry **tabptr)
{
	u8_t i;

	pthread_mutex_lock(&arp_table_mutex);

	for (i = 0; i < UIP_ARPTAB_SIZE; ++i) {
		if (uip_ip4addr_cmp(ipaddr, arp_table[i].ipaddr)) {
			*tabptr = &arp_table[i];
			break;
		}
	}

	pthread_mutex_unlock(&arp_table_mutex);

	if (i == UIP_ARPTAB_SIZE)
		return NOT_IN_ARP_TABLE;
	else
		return IS_IN_ARP_TABLE;
}
Пример #5
0
dest_ipv4_addr_t
uip_determine_dest_ipv4_addr(struct uip_stack *ustack, u16_t *ipaddr)
{
	struct arp_hdr *arp;
	struct uip_eth_hdr *eth;
	struct ip_hdr *ip_buf;

	arp = (struct arp_hdr *)ustack->network_layer;
	eth = (struct uip_eth_hdr *)ustack->data_link_layer;
	ip_buf = (struct ip_hdr *)ustack->network_layer;

	/* Find the destination IP address in the ARP table and construct
	   the Ethernet header. If the destination IP addres isn't on the
	   local network, we use the default router's IP address instead.

	   If not ARP table entry is found, we overwrite the original IP
	   packet with an ARP request for the IP address. */

	/* First check if destination is a local broadcast. */
	if (uip_ip4addr_cmp(ip_buf->destipaddr, broadcast_ipaddr)) {
		memcpy(&eth->dest, broadcast_ethaddr.addr, 6);

		return LOCAL_BROADCAST;
	} else {
		/* Check if the destination address is on the local network. */
		if (!uip_ip4addr_maskcmp(ip_buf->destipaddr,
					 ustack->hostaddr, ustack->netmask)) {
			/* Destination address was not on the local network,
			   so we need to use the default router's IP address
			   instead of the destination address when determining
			   the MAC address. */
			uip_ip4addr_copy(ipaddr, ustack->default_route_addr);
		} else {
			/* Else, we use the destination IP address. */
			uip_ip4addr_copy(ipaddr, ip_buf->destipaddr);
		}

		return NONLOCAL_BROADCAST;
	}
}
Пример #6
0
void
uip_build_eth_header(struct uip_stack *ustack,
		     u16_t *ipaddr,
		     struct arp_entry *tabptr,
		     struct packet *pkt, u16_t vlan_id)
{
	struct uip_ipv4_hdr *ip_buf;
	struct uip_eth_hdr *eth;
	struct uip_vlan_eth_hdr *eth_vlan;

	ip_buf = (struct uip_ipv4_hdr *)ustack->network_layer;
	eth = (struct uip_eth_hdr *)ustack->data_link_layer;
	eth_vlan = (struct uip_vlan_eth_hdr *)ustack->data_link_layer;

	/* First check if destination is a local broadcast. */
	if (uip_ip4addr_cmp(ip_buf->destipaddr, broadcast_ipaddr)) {
		memcpy(eth->dest.addr, broadcast_ethaddr.addr, 6);
	} else {
		/* Build an ethernet header. */
		memcpy(eth->dest.addr, tabptr->ethaddr.addr, 6);
	}
	memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6);

	if (vlan_id == 0) {
		eth->type = htons(UIP_ETHTYPE_IPv4);

		ustack->uip_len += sizeof(struct uip_eth_hdr);
		pkt->buf_size += sizeof(struct uip_eth_hdr);
	} else {
		eth_vlan->tpid = htons(UIP_ETHTYPE_8021Q);
		eth_vlan->vid = htons(vlan_id);
		eth_vlan->type = htons(UIP_ETHTYPE_IPv4);

		ustack->uip_len += sizeof(struct uip_vlan_eth_hdr);
		pkt->buf_size += sizeof(struct uip_vlan_eth_hdr);
	}
}
Пример #7
0
/*---------------------------------------------------------------------------*/
void
ip64_dhcpc_configured(const struct ip64_dhcpc_state *s)
{
  uip_ip6addr_t ip6dnsaddr;
  PRINTF("DHCP Configured with %d.%d.%d.%d\n",
	 s->ipaddr.u8[0], s->ipaddr.u8[1],
	 s->ipaddr.u8[2], s->ipaddr.u8[3]);

  ip64_set_hostaddr((uip_ip4addr_t *)&s->ipaddr);
  ip64_set_netmask((uip_ip4addr_t *)&s->netmask);
  ip64_set_draddr((uip_ip4addr_t *)&s->default_router);
  if(!uip_ip4addr_cmp((uip_ip4addr_t *)&s->dnsaddr, &uip_all_zeroes_addr)) {
    //Note: Currently we assume only one DNS server
    uip_ipaddr_t * dns = uip_nameserver_get(0);
    //Only update DNS entry if it is empty or already IPv4
    if(uip_is_addr_unspecified(dns) || ip64_addr_is_ip64(dns)) {
      ip64_addr_4to6((uip_ip4addr_t *)&s->dnsaddr, &ip6dnsaddr);
      uip_nameserver_update(&ip6dnsaddr, uip_ntohs(s->lease_time[0])*65536ul + uip_ntohs(s->lease_time[1]));
    }
  }
#if CETIC_6LBR
  cetic_6lbr_ip64_dhcpc_configured(s);
#endif
}
/*---------------------------------------------------------------------------*/
int
ip64_4to6(const uint8_t *ipv4packet, const uint16_t ipv4packet_len,
	  uint8_t *resultpacket)
{
  struct ipv4_hdr *v4hdr;
  struct ipv6_hdr *v6hdr;
  struct udp_hdr *udphdr;
  struct tcp_hdr *tcphdr;
  struct icmpv4_hdr *icmpv4hdr;
  struct icmpv6_hdr *icmpv6hdr;
  uint16_t ipv4len, ipv6len, ipv6_packet_len;
  struct ip64_addrmap_entry *m;

  v6hdr = (struct ipv6_hdr *)resultpacket;
  v4hdr = (struct ipv4_hdr *)ipv4packet;

  if((v4hdr->len[0] << 8) + v4hdr->len[1] <= ipv4packet_len) {
    ipv4len = (v4hdr->len[0] << 8) + v4hdr->len[1];
  } else {
    PRINTF("ip64_4to6: packet smaller than reported in IPv4 header, dropping\n");
    return 0;
  }

  if(ipv4len <= IPV4_HDRLEN) {
    return 0;
  }

  /* Make sure that the resulting packet fits in the ip64 packet
     buffer. If not, we drop it. */
  if(ipv4len - IPV4_HDRLEN + IPV6_HDRLEN > BUFSIZE) {
    PRINTF("ip64_4to6: packet too big to fit in buffer, dropping\n");
    return 0;
  }
  /* We copy the data from the IPv4 packet into the IPv6 packet. */
  memcpy(&resultpacket[IPV6_HDRLEN],
	 &ipv4packet[IPV4_HDRLEN],
	 ipv4len - IPV4_HDRLEN);
  
  udphdr = (struct udp_hdr *)&resultpacket[IPV6_HDRLEN];
  tcphdr = (struct tcp_hdr *)&resultpacket[IPV6_HDRLEN];
  icmpv4hdr = (struct icmpv4_hdr *)&ipv4packet[IPV4_HDRLEN];
  icmpv6hdr = (struct icmpv6_hdr *)&resultpacket[IPV6_HDRLEN];

  ipv6len = ipv4len - IPV4_HDRLEN + IPV6_HDRLEN;
  ipv6_packet_len = ipv6len - IPV6_HDRLEN;

  /* Translate the IPv4 header into an IPv6 header. */

  /* We first fill in the simple fields: IP header version, traffic
     class and flow label, and length fields. */
  v6hdr->vtc = 0x60;
  v6hdr->tcflow = 0;
  v6hdr->flow = 0;
  v6hdr->len[0] = ipv6_packet_len >> 8;
  v6hdr->len[1] = ipv6_packet_len & 0xff;

  /* We use the IPv4 TTL field as the IPv6 hop limit field. */
  v6hdr->hoplim = v4hdr->ttl;

  
  /* We now translate the IPv4 source and destination addresses to
     IPv6 source and destination addresses. We translate the IPv4
     source address into an IPv6-encoded IPv4 address. The IPv4
     destination address will be the address with which we have
     previously been configured, through the ip64_set_ipv4_address()
     function. We use the mapping table to look up the new IPv6
     destination address. As we assume that the IPv4 packet is a
     response to a previously sent IPv6 packet, we should have a
     mapping between the (protocol, destport, srcport, srcaddress)
     tuple. If not, we'll return 0 to indicate that we failed to
     translate the packet. */
  if(ip64_addr_4to6(&v4hdr->srcipaddr, &v6hdr->srcipaddr) == 0) {
    PRINTF("ip64_packet_4to6: failed to convert source IP address\n");
    return 0;
  }

    /* For the next header field, we simply use the IPv4 protocol
     field. We only support UDP and TCP packets. */
  switch(v4hdr->proto) {
  case IP_PROTO_UDP:
    v6hdr->nxthdr = IP_PROTO_UDP;
    break;

  case IP_PROTO_TCP:
    v6hdr->nxthdr = IP_PROTO_TCP;
    break;

  case IP_PROTO_ICMPV4:
    /* Allow only ICMPv4 ECHO_REQUESTS (ping packets) through to the
       local IPv6 host. */
    if(icmpv4hdr->type == ICMP_ECHO) {
      PRINTF("ip64_4to6: translating ICMPv4 ECHO packet\n");
      v6hdr->nxthdr = IP_PROTO_ICMPV6;
      icmpv6hdr->type = ICMP6_ECHO;
      ip64_addr_copy6(&v6hdr->destipaddr, &ipv6_local_address);
    } else {
      PRINTF("ip64_packet_4to6: ICMPv4 packet type %d not supported\n",
	     icmpv4hdr->type);
      return 0;
    }
    break;

  default:
    /* For protocol types that we do not support, we return 0 to
       indicate that we failed to translate the packet to an IPv6
       packet. */
    PRINTF("ip64_packet_4to6: protocol type %d not supported\n",
	   v4hdr->proto);
    return 0;
  }

  /* Translate IPv4 broadcasts to IPv6 all-nodes multicasts. */
  if(uip_ip4addr_cmp(&v4hdr->destipaddr, &ipv4_broadcast_addr) ||
     (uip_ipaddr_maskcmp(&v4hdr->destipaddr, &ip64_hostaddr,
			 &ip64_netmask) &&
      ((v4hdr->destipaddr.u16[0] & (~ip64_netmask.u16[0])) ==
       (ipv4_broadcast_addr.u16[0] & (~ip64_netmask.u16[0]))) &&
      ((v4hdr->destipaddr.u16[1] & (~ip64_netmask.u16[1])) ==
       (ipv4_broadcast_addr.u16[1] & (~ip64_netmask.u16[1]))))) {
    uip_create_linklocal_allnodes_mcast(&v6hdr->destipaddr);
  } else {

    if(!ip64_hostaddr_configured) {
      PRINTF("ip64_packet_4to6: no local IPv4 address configured, dropping incoming packet.\n");
      return 0;
    }

    if(!uip_ip4addr_cmp(&v4hdr->destipaddr, &ip64_hostaddr)) {
      PRINTF("ip64_packet_4to6: the IPv4 destination address %d.%d.%d.%d did not match our IPv4 address %d.%d.%d.%d\n",
	     uip_ipaddr_to_quad(&v4hdr->destipaddr),
	     uip_ipaddr_to_quad(&ip64_hostaddr));
      return 0;
    }


  /* Now we translate the transport layer port numbers. We assume that
     the IPv4 packet is a response to a packet that has previously
     been translated from IPv6 to IPv4. If this is the case, the tuple
     (protocol, destport, srcport, srcaddress) corresponds to an address/port
     pair in our mapping table. If we do not find a mapping, we return
     0 to indicate that we could not translate the IPv4 packet to an
     IPv6 packet. */

  /* XXX treat a few ports differently: those ports should be let
     through to the local host. For those ports, we set up an address
     mapping that ensures that the local port number is retained. */

    if((v4hdr->proto == IP_PROTO_TCP || v4hdr->proto == IP_PROTO_UDP)) {
      if(uip_htons(tcphdr->destport) < EPHEMERAL_PORTRANGE) {
	/* This packet should go to the local host. */
	PRINTF("Port is in the non-ephemeral port range %d (%d)\n",
	       tcphdr->destport, uip_htons(tcphdr->destport));
	ip64_addr_copy6(&v6hdr->destipaddr, &ipv6_local_address);
      } else if(ip64_special_ports_incoming_is_special(uip_htons(tcphdr->destport))) {
	uip_ip6addr_t newip6addr;
	uint16_t newport;
	PRINTF("ip64 port %d (%d) is special, treating it differently\n",
	       tcphdr->destport, uip_htons(tcphdr->destport));
	if(ip64_special_ports_translate_incoming(uip_htons(tcphdr->destport),
						 &newip6addr, &newport)) {
	  ip64_addr_copy6(&v6hdr->destipaddr, &newip6addr);
	  tcphdr->destport = uip_htons(newport);
	  PRINTF("New port %d (%d)\n",
		 tcphdr->destport, uip_htons(tcphdr->destport));
	} else {
	  ip64_addr_copy6(&v6hdr->destipaddr, &ipv6_local_address);
	  PRINTF("No new port\n");
	}
      } else {
      /* The TCP or UDP port numbers were not non-ephemeral and not
	 special, so we map the port number according to the address
	 mapping table. */

	m = ip64_addrmap_lookup_port(uip_ntohs(udphdr->destport),
				     v4hdr->proto);
	if(m == NULL) {
	  PRINTF("Inbound lookup failed\n");
	  return 0;
	} else {
	  PRINTF("Inbound lookup did not fail\n");
	}
	ip64_addr_copy6(&v6hdr->destipaddr, &m->ip6addr);
	udphdr->destport = uip_htons(m->ip6port);
      }
    }
  }

  /* The checksum is in different places in the different protocol
     headers, so we need to be sure that we update the correct
     field. */
  switch(v6hdr->nxthdr) {
  case IP_PROTO_TCP:
    tcphdr->tcpchksum = 0;
    tcphdr->tcpchksum = ~(ipv6_transport_checksum(resultpacket,
						  ipv6len,
						  IP_PROTO_TCP));
    break;
  case IP_PROTO_UDP:
    udphdr->udpchksum = 0;
    udphdr->udpchksum = ~(ipv6_transport_checksum(resultpacket,
						  ipv6len,
						  IP_PROTO_UDP));
    if(udphdr->udpchksum == 0) {
      udphdr->udpchksum = 0xffff;
    }
    break;

  case IP_PROTO_ICMPV6:
    icmpv6hdr->icmpchksum = 0;
    icmpv6hdr->icmpchksum = ~(ipv6_transport_checksum(resultpacket,
                                                ipv6len,
                                                IP_PROTO_ICMPV6));
    break;
  default:
    PRINTF("ip64_4to6: transport protocol %d not implemented\n", v4hdr->proto);
    return 0;
  }

  /* Finally, we return the length of the resulting IPv6 packet. */
  PRINTF("ip64_4to6: ipv6len %d\n", ipv6len);
  return ipv6len;
}
/*---------------------------------------------------------------------------*/
int
ip64_6to4(const uint8_t *ipv6packet, const uint16_t ipv6packet_len,
	  uint8_t *resultpacket)
{
  struct ipv4_hdr *v4hdr;
  struct ipv6_hdr *v6hdr;
  struct udp_hdr *udphdr;
  struct tcp_hdr *tcphdr;
  struct icmpv4_hdr *icmpv4hdr;
  struct icmpv6_hdr *icmpv6hdr;
  uint16_t ipv6len, ipv4len;
  struct ip64_addrmap_entry *m;
  
  v6hdr = (struct ipv6_hdr *)ipv6packet;
  v4hdr = (struct ipv4_hdr *)resultpacket;

  if((v6hdr->len[0] << 8) + v6hdr->len[1] <= ipv6packet_len) {
    ipv6len = (v6hdr->len[0] << 8) + v6hdr->len[1] + IPV6_HDRLEN;
  } else {
    PRINTF("ip64_6to4: packet smaller than reported in IPv6 header, dropping\n");
    return 0;
  }

  /* We copy the data from the IPv6 packet into the IPv4 packet. We do
     not modify the data in any way. */
  memcpy(&resultpacket[IPV4_HDRLEN],
	 &ipv6packet[IPV6_HDRLEN],
	 ipv6len - IPV6_HDRLEN);

  udphdr = (struct udp_hdr *)&resultpacket[IPV4_HDRLEN];
  tcphdr = (struct tcp_hdr *)&resultpacket[IPV4_HDRLEN];
  icmpv4hdr = (struct icmpv4_hdr *)&resultpacket[IPV4_HDRLEN];
  icmpv6hdr = (struct icmpv6_hdr *)&ipv6packet[IPV6_HDRLEN];

  /* Translate the IPv6 header into an IPv4 header. */

  /* First the basics: the IPv4 version, header length, type of
     service, and offset fields. Those are the same for all IPv4
     packets we send, regardless of the values found in the IPv6
     packet. */
  v4hdr->vhl = 0x45;
  v4hdr->tos = 0;
  v4hdr->ipoffset[0] = v4hdr->ipoffset[1] = 0;

  /* We assume that the IPv6 packet has a fixed size header with no
     extension headers, and compute the length of the IPv4 packet and
     place the resulting value in the IPv4 packet header. */
  ipv4len = ipv6len - IPV6_HDRLEN + IPV4_HDRLEN;
  v4hdr->len[0] = ipv4len >> 8;
  v4hdr->len[1] = ipv4len & 0xff;

  /* For simplicity, we set a unique IP id for each outgoing IPv4
     packet. */
  ipid++;
  v4hdr->ipid[0] = ipid >> 8;
  v4hdr->ipid[1] = ipid & 0xff;

  /* Set the IPv4 protocol. We only support TCP, UDP, and ICMP at this
     point. While the IPv4 header protocol numbers are the same as the
     IPv6 next header numbers, the ICMPv4 and ICMPv6 numbers are
     different so we cannot simply copy the contents of the IPv6 next
     header field. */
  switch(v6hdr->nxthdr) {
  case IP_PROTO_TCP:
    PRINTF("ip64_6to4: TCP header\n");
    v4hdr->proto = IP_PROTO_TCP;
    break;

  case IP_PROTO_UDP:
    PRINTF("ip64_6to4: UDP header\n");
    v4hdr->proto = IP_PROTO_UDP;
    break;

  case IP_PROTO_ICMPV6:
    PRINTF("ip64_6to4: ICMPv6 header\n");
    v4hdr->proto = IP_PROTO_ICMPV4;
    /* Translate only ECHO_REPLY messages. */
    if(icmpv6hdr->type == ICMP6_ECHO_REPLY) {
      icmpv4hdr->type = ICMP_ECHO_REPLY;
    } else {
      PRINTF("ip64_6to4: ICMPv6 mapping for type %d not implemented.\n",
	     icmpv6hdr->type);
      return 0;
    }
    break;

  default:
    /* We did not recognize the next header, and we do not attempt to
       translate something we do not understand, so we return 0 to
       indicate that no successful translation could be made. */
    PRINTF("ip64_6to4: Could not convert IPv6 next hop %d to an IPv4 protocol number.\n",
	   v6hdr->nxthdr);
    return 0;
  }

  /* We set the IPv4 ttl value to the hoplim number from the IPv6
     header. This means that information about the IPv6 topology is
     transported into to the IPv4 network. */
  v4hdr->ttl = v6hdr->hoplim;

  /* We next convert the destination address. We make this conversion
     with the ip64_addr_6to4() function. If the conversion
     fails, ip64_addr_6to4() returns 0. If so, we also return 0 to
     indicate failure. */
  if(ip64_addr_6to4(&v6hdr->destipaddr,
		    &v4hdr->destipaddr) == 0) {
    PRINTF("ip64_6to4: Could not convert IPv6 destination address.\n");
    uip_debug_ipaddr_print(&v6hdr->destipaddr);
    PRINTF("\n");
    return 0;
  }

  /* We set the source address in the IPv4 packet to be the IPv4
     address that we have been configured with through the
     ip64_set_ipv4_address() function. Only let broadcasts through. */
  if(!ip64_hostaddr_configured &&
     !uip_ip4addr_cmp(&v4hdr->destipaddr, &ipv4_broadcast_addr)) {
    PRINTF("ip64_6to4: no IPv4 address configured.\n");
    return 0;
  }
  ip64_addr_copy4(&v4hdr->srcipaddr, &ip64_hostaddr);


  /* Next we update the transport layer header. This must be updated
     in two ways: the source port number is changed and the transport
     layer checksum must be recomputed. The reason why we change the
     source port number is so that we can remember what IPv6 address
     this packet came from, in case the packet will result in a reply
     from the host on the IPv4 network. If a reply would be sent, it
     would be sent to the port number that we chose, and we will be
     able to map this back to the IPv6 address of the original sender
     of the packet.
  */

  /* We check to see if we already have an existing IP address mapping
     for this connection. If not, we create a new one. */
  if((v4hdr->proto == IP_PROTO_UDP || v4hdr->proto == IP_PROTO_TCP)) {

    if(ip64_special_ports_outgoing_is_special(uip_ntohs(udphdr->srcport))) {
      uint16_t newport;
      if(ip64_special_ports_translate_outgoing(uip_ntohs(udphdr->srcport),
					       &v6hdr->srcipaddr,
					       &newport)) {
	udphdr->srcport = uip_htons(newport);
      }
    } else if(uip_ntohs(udphdr->srcport) >= EPHEMERAL_PORTRANGE) {
      m = ip64_addrmap_lookup(&v6hdr->srcipaddr,
                              uip_ntohs(udphdr->srcport),
			      &v4hdr->destipaddr,
			      uip_ntohs(udphdr->destport),
			      v4hdr->proto);
      if(m == NULL) {
	PRINTF("Lookup failed\n");
	m = ip64_addrmap_create(&v6hdr->srcipaddr,
				uip_ntohs(udphdr->srcport),
				&v4hdr->destipaddr,
				uip_ntohs(udphdr->destport),
				v4hdr->proto);
	if(m == NULL) {
	  PRINTF("Could not create new map\n");
	  return 0;
	} else {
	  PRINTF("Could create new local port %d\n", m->mapped_port);
	}
      } else {
	PRINTF("Lookup: found local port %d (%d)\n", m->mapped_port,
	       uip_htons(m->mapped_port));
      }
      udphdr->srcport = uip_htons(m->mapped_port);
    }
  }

  /* The IPv4 header is now complete, so we can compute the IPv4
     header checksum. */
  v4hdr->ipchksum = 0;
  v4hdr->ipchksum = ~(ipv4_checksum(v4hdr));



  /* The checksum is in different places in the different protocol
     headers, so we need to be sure that we update the correct
     field. */
  switch(v4hdr->proto) {
  case IP_PROTO_TCP:
    tcphdr->tcpchksum = 0;
    tcphdr->tcpchksum = ~(ipv4_transport_checksum(resultpacket, ipv4len,
						  IP_PROTO_TCP));
    break;
  case IP_PROTO_UDP:
    udphdr->udpchksum = 0;
    udphdr->udpchksum = ~(ipv4_transport_checksum(resultpacket, ipv4len,
						  IP_PROTO_UDP));
    if(udphdr->udpchksum == 0) {
      udphdr->udpchksum = 0xffff;
    }
    break;
  case IP_PROTO_ICMPV4:
    icmpv4hdr->icmpchksum = 0;
    icmpv4hdr->icmpchksum = ~(ipv4_transport_checksum(resultpacket, ipv4len,
						      IP_PROTO_ICMPV4));
    break;

  default:
    PRINTF("ip64_6to4: transport protocol %d not implemented\n", v4hdr->proto);
    return 0;
  }

  /* Finally, we return the length of the resulting IPv4 packet. */
  PRINTF("ip64_6to4: ipv4len %d\n", ipv4len);
  return ipv4len;
}