Chapter 4 Performance Evaluation of Teredo Relay Implementations
4.2 Experimental Result
Figure 10 shows the 1280-byte packet encapsulation latency histograms of the three Teredo
relays. The latency of the NICI-Teredo relay is clustered around 7-9µs and 12-13µs. The
latency of the 6WIND-Teredo relay is around 12-16µs. The latency of the Miredo-Teredo relay is clustered around 23-26µs and 33-39µs. The 1280-byte packet decapsulation latency histograms are similar to those in Figure 10, and the average packet processing latency of the three Teredo relays are listed in Table 1.
0 5 10 15 20 25 30 35 40 45 50 55 60
0 5 10 15 20 25 30 35 40 45 50
Delay(µs)
Percentage(%)
NICI-Teredo 6WIND-Teredo Miredo-Teredo
Figure 10. Encapsulation latency histograms of the Teredo relays (1280 bytes)
Table 1. Average processing latency
Avg. latency of encapsulation (µs) Avg. latency of decapsulation (µs) Teredo relay
64 bytes 512 bytes 1280 bytes 64 bytes 512 bytes 1280 bytes NICI 7.69 7.82 8.06 8.77 9.10 9.24 6WIND 13.30 13.53 14.00 14.34 14.62 14.80
Miredo 23.05 25.46 33.08 24.90 33.14 34.85
0 5 10 15 20 25 30 35 40 45 50 55 60
0 5 10 15 20 25 30 35 40 45 50
Delay(µs)
Percentage(%)
NICI-Teredo 6WIND-Teredo Miredo-Teredo
Figure 11. Encapsulation latency histograms of the Teredo relays (512 bytes)
0 5 10 15 20 25 30 35 40 45 50 55 60
0 5 10 15 20 25 30 35 40 45 50
Delay(µs)
Percentage(%)
NICI-Teredo 6WIND-Teredo Miredo-Teredo
Figure 12. Encapsulation latency histograms of the Teredo relays (64 bytes)
With different packet sizes (1280, 512, 64 bytes), the encapsulation latency histograms of
NICI-Teredo relay and 6WIND-Teredo relay (see Figures 10, 11 and 12) have similar shapes,
where the Miredo-Teredo relay has longer latency than that of the NICI-Teredo relay. The
decapsulation latency histograms of the three Teredo relays can be found in Appendix A. The
histograms in Appendix A shows again that NICI-Teredo relay has the best packet processing
performance among the three implementations of Teredo relays. For the 1280-byte packet
encapsulation latency listed in Table 1, the latency improvement of the NICI-Teredo relay
over the 6WIND-Teredo relay is around 42%, and the improvement of the NICI-Teredo relay
over the Miredo-Teredo relay is around 75%.
Chapter 5 Conclusions
This thesis addressed the NAT traversal issue between the private IPv4 and the IPv6 networks.
As an NAT traversable automatic tunneling mechanism, Teredo provides convenient IPv6 access from the private IPv4 networks. We developed an efficient implementation for Teredo tunneling called NICI-Teredo. To our knowledge, NICI-Teredo is the first non-commercial Teredo implementation on Linux. Our approach has shorter packet processing latency than that of the 6WIND-Teredo relay (FreeBSD-based) and the Miredo-Teredo relay (Linux-based).
The advantage of packet processing performance of the NICI-Teredo relay makes it an appropriate IPv6 tunneling solution.
As a final remark, we point out that Teredo does not work for all NAT servers. According to the rules for port mapping and access control in NAT [9], the four major types are full cone NAT, restricted cone NAT, port restricted cone NAT, and symmetric NAT. Although Teredo can successfully traverse the first three types of NATs, it fails in traversing symmetric NAT.
To enhance Teredo for symmetric NAT is still an open issue for further study.
Bibliography
[1] R. Gilligan and E. Nordmark, “Transition Mechanisms for IPv6 Hosts and Routers”, IETF RFC 2893, Aug. 2000.
[2] B. Carpenter and K. Moore, “Connection of IPv6 Domains via IPv4 Clouds”, IETF RFC 3056, Feb. 2001.
[3] A. Durand, P. Fasano, I. Guardini and D. Lento, “IPv6 Tunnel Broker”, IETF RFC 3053, Jan. 2001.
[4] Y.-B. Lin and I. Chlamtac, Wireless and Mobile Network Architectures. John Wiley &
Sons, 2001.
[5] P. Srisuresh and M. Holdrege, “IP Network Address Translator (NAT) Terminology and Considerations”, IETF RFC 2663, Aug. 1999.
[6] H. Levkowetz and S. Vaarala, “Mobile IP Traversal of Network Address Translation (NAT) Devices”, IETF RFC 3519, Apr. 2003.
[7] C. Huitema, “Teredo: Tunneling IPv6 over UDP through NATs”, Internet draft, draft-huitema-v6ops-teredo-05.txt (Work in Progress), Apr. 2005.
[8] M. Liu, X. Wu, Y. Cai, M. Jin and D. Li, “Tunneling IPv6 with private IPv4 addresses through NAT devices”, Internet Draft, draft-liumin-v6ops-silkroad-02.txt (Work in Progress), Nov. 2004.
[9] J. Rosenberg, J. Weinberger, C. Huitema and R. Mahy, “STUN - Simple Traversal of User
Datagram Protocol (UDP) Through Network Address Translators (NATs)”, IETF RFC
3489, Mar. 2003.
[10] S.-M. Huang and Q. Wu, “Implementation of Teredo - Tunneling IPv6 through NATs”, Technical Report for National Information and Communication Initiative (NICI) IPv6 R&D Division, Taiwan, ROC, 2003.
[11] Teredo for FreeBSD, http://www-rp.lip6.fr/teredo/
[12] Miredo: Teredo for Linux, http://www.simphalempin.com/dev/miredo/
[13] B. Henderson, Linux Loadable Kernel Module HOWTO, http://www.linux.org/docs/ldp/howto/Module-HOWTO/
[14] S. Bradner and J. McQuaid, “Benchmarking Methodology for Network Interconnect Devices”, IETF RFC 2544, Mar. 1999.
[15] The netfilter/iptables project, http://www.netfilter.org/
[16] Tcpdump and libpcap, http://www.tcpdump.org/
Appendix A
Decapsulation Latency Histograms
Appedix A is a supplement to the experimantal result in Chapter 4.2. This appendix shows the packet decapsulation latency histograms of the three Teredo relays (NICI-Teredo,
6WIND-Teredo and Miredo-Teredo). The 1280-byte result is showed in Figure 13, and the 512-byte and 64-byte results are showed in Figure 14 and 15,respectively.
0 5 10 15 20 25 30 35 40 45 50 55 60
0 5 10 15 20 25 30 35 40 45 50
Delay(µs)
Percentage(%)
NICI-Teredo 6WIND-Teredo Miredo-Teredo
Figure 13. Decapsulation latency histograms of the Teredo relays (1280 bytes)
0 5 10 15 20 25 30 35 40 45 50 55 60
0 5 10 15 20 25 30 35 40 45 50
Delay(µs)
Percentage(%)
NICI-Teredo 6WIND-Teredo Miredo-Teredo
Figure 14. Decapsulation latency histograms of the Teredo relays (512 bytes)
0 5 10 15 20 25 30 35 40 45 50 55 60
0 5 10 15 20 25 30 35 40 45 50
Delay(µs)
Percentage(%)
NICI-Teredo 6WIND-Teredo
Miredo-Teredo
Figure 15. Decapsulation latency histograms of the Teredo relays (64 bytes)
Appendix B
The NICI-Teredo Server Program
Appendix B lists the source code of the NICI-Teredo server. The NICI-Teredo server is implemented as a C program, which consists of a header file teredo_server.h (Appendix B.1) and a program file teredo_server.c (Appendix B.2).
B.1 teredo_server.h
1 /*
2 * Teredo Server - Tunneling IPv6 over UDP through NATs 3 * Linux INET6 implementation
4 *
5 * $Id: teredo_server.h,v 1.2 2005/05/17 12:14:12 smhuang Exp $ 6 *
7 * File:
8 * teredo.h - header for Teredo Server 9 *
10 * Version 0.0 - 12/03/03 - Initial Version.
11 * 0.1 - 03/14/05 - Remove self-defined ipv6 structure (use ip6.h).
12 * 0.2 - 03/17/05 - Add DEBUG_TEREDO definition.
13 */
14 #ifndef _TEREDO_SERVER_H_
15 #define _TEREDO_SERVER_H_
16
17 const unsigned short TEREDO_PORT = 3544;
18 const unsigned long TEREDO_PREFIX = 0x3FFE831F;
19 const unsigned short MAX_BUF = 2048;
20
21 struct teredo_auth 22 {
23 unsigned char raw_data[13];
24 };
25 26
27 struct teredo_orig { 28 unsigned short type;
29 unsigned short port;
30 unsigned int addr;
31 };
36 unsigned char prefix_len;
53 #define ERROR_TRACE(x) printk(KERN_ERR x) 54
18 #include "teredo_server.h"
19 #include "in6.h"
20 #include "ipv6.h"
21 #include <linux/icmpv6.h>
22
23 /* server address */
24 unsigned long TEREDO_PRI_SERVER;
25 unsigned long TEREDO_SEC_SERVER;
26
addresses 192.88.99.0/24 */
43 checksum(unsigned short v6_payload_len, unsigned short v6_nexthdr,
44 struct in6_addr *v6_saddr, struct in6_addr *v6_daddr, unsigned short *data, int len)
92 printf("Usage: %s PRI_SERVER_IPv4_ADDR SEC_SERVER_IPv4_ADDR\n", argv[0]);
93 exit(1);
94 } 95
100 perror("fork");
125 my_addr.sin_addr.s_addr = TEREDO_PRI_SERVER;
126 my_addr.sin_port = htons(TEREDO_PORT);
127 status = bind(sockd, (struct sockaddr*)&my_addr, sizeof(my_addr));
128 if (status == -1)
142 my2_addr.sin_addr.s_addr = TEREDO_SEC_SERVER;
143 my2_addr.sin_port = htons(TEREDO_PORT);
144 status = bind(secfd, (struct sockaddr*)&my2_addr, sizeof(my2_addr));
145 if (status == -1)
166
230 {
250 if (rv6hdr->daddr.in6_u.u6_addr16[0]&htons(0xffc0) == htons(0xff80) ||
251 rv6hdr->daddr.in6_u.u6_addr16[0]&htons(0xffc0) == htons(0xfe80) ||
278 fprintf(stderr, "source addr %x:%x\n", peer_addr.sin_addr, ntohs(peer_addr.sin_port));
279
296 }
321 sv6hdr->saddr.in6_u.u6_addr32[0] = htonl(0xfe800000); sv6hdr->saddr.in6_u.u6_addr32[1]
= htonl(0x00000000);
356 /* ICMPv6 options */
370 mp6hdr->icmp6_cksum = checksum(sv6hdr->payload_len, htons(sv6hdr->nexthdr), 371 &(sv6hdr->saddr), &(sv6hdr->daddr),
417 }
Appendix C
The NICI-Teredo Relay Program
Appendix C lists the source code of the NICI-Teredo relay. The NICI-Teredo relay is
implemented as a C program, which consists of two header files teredo.h (Appendix C.1), bubble.h (Appendix C.2) and a program file teredo.c (Appendix C.3). In teredo.h,
a data structure for maintenance the tunnel reachability between the Teredo client and the Teredo relay is defined (i.e. struct teredo_peer_list). Several data structures for kernel level configuration are also defined in this file. Bubble.h definces several inline functions for handling packet tunneling. The packet encapsulation and decapsulation functions are defined in teredo.c.
C.1 teredo.h
1 /*
2 * Teredo tunnel device - Tunneling IPv6 over UDP through NATs 3 * Linux INET6 implementation
4 * 5 *
6 * $Id: teredo.h,v 1.13 2005/03/10 12:50:03 smhuang Exp $ 7 *
8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 11 * 2 of the License, or (at your option) any later version.
12 *
13 * Version 0.0 - 10/24/03 - Initial Version.
14 * 0.1 - 12/24/03 - Add Teredo Bubble support.
15 * 0.2 - 01/12/04 - Bug Fix (tunnel packet format in udpip6_tunnel_xmit).
16 * 0.3 - 03/16/04 - Add kernel thread for Bubble retransmission.
17 * 0.4 - 01/15/05 - Remove kernel thread feature.
18 * 0.5 - 03/11/05 - Add packet check feature.
23 #define _TEREDO_H_
66 } teredo_peer[TEREDO_MAX_PEER+1];
67
68 static int udpip6_tunnel_init(struct net_device* dev);
69 int udpip6_rcv(struct sk_buff* skb);
89 0, 90 NULL, 91 "IPv6"
92 };
93
94 static struct ip_tunnel *teredo_tunnel;
95
96 static rwlock_t udpip6_lock = RW_LOCK_UNLOCKED;
97
107 #define ERROR_TRACE(x) printk(KERN_ERR x) 108 31 * comes from TEREDO(draft-huitema-v6ops-teredo-00) addr. space*
32 * +---+---+---+---+---+ * 33 * | Prefix | Server IPv4 | Flags | Port | Client IPv4 | *
38 * - Port: the obfuscated "mapped UDP port" of the Teredo *
45 try_teredo(const struct in6_addr *v6dst) 46 {
59 teredo_port(const struct in6_addr *v6dst) 60 {
72 teredo_server_addr (const struct in6_addr *v6dst) 73 {
80 teredo_iscone (const struct in6_addr *v6dst) 81 {
82 return (v6dst->s6_addr16[4] == htons(0x8000))?1:0;
83 } 84
85 static inline int
86 teredo_isaddr (const struct in6_addr *v6dst) 87 {
88 return ((v6dst->s6_addr16[0] != htons(0x3ffe) || v6dst->s6_addr16[1] != htons(0x831f)))?0:1;
89 /*if (v6dst->s6_addr16[0] != htons(0x3ffe) ||
((v6addr->in6_u.u6_addr16[2])^(v6addr->in6_u.u6_addr16[3])^(v6addr->in6_u.u6_addr16[5])^
104 (v6addr->in6_u.u6_addr16[6])^(v6addr->in6_u.u6_addr16[7]))&TEREDO_MAX_PEER;
105 } 106
107 static inline void
108 teredo_new_cone_peer (const struct in6_addr *v6addr) 109 {
110 u16 key = teredo_phash (v6addr);
111 teredo_peer[key].v6addr.in6_u.u6_addr32[0] = v6addr->in6_u.u6_addr32[0];
112 teredo_peer[key].v6addr.in6_u.u6_addr32[1] = v6addr->in6_u.u6_addr32[1];
113 teredo_peer[key].v6addr.in6_u.u6_addr32[2] = v6addr->in6_u.u6_addr32[2];
114 teredo_peer[key].v6addr.in6_u.u6_addr32[3] = v6addr->in6_u.u6_addr32[3];
115
116 teredo_peer[key].status = 1;
117 skb_queue_head_init(&(teredo_peer[key].pktq));
118 return;
119 } 120
121 static inline void
122 teredo_new_peer (const struct in6_addr *v6addr) 123 {
124 u16 key = teredo_phash (v6addr);
125 teredo_peer[key].v6addr.in6_u.u6_addr32[0] = v6addr->in6_u.u6_addr32[0];
126 teredo_peer[key].v6addr.in6_u.u6_addr32[1] = v6addr->in6_u.u6_addr32[1];
127 teredo_peer[key].v6addr.in6_u.u6_addr32[2] = v6addr->in6_u.u6_addr32[2];
128 teredo_peer[key].v6addr.in6_u.u6_addr32[3] = v6addr->in6_u.u6_addr32[3];
129
130 teredo_peer[key].status = 0;
131 teredo_peer[key].tx_bubble = 0;
132 teredo_peer[key].mapped_addr = 0;
133 teredo_peer[key].mapped_port = 0;
134 skb_queue_head_init(&(teredo_peer[key].pktq));
135 return;
136 } 137
138 static inline int
139 teredo_inc_bubble (const struct in6_addr *v6addr) 140 {
155 teredo_ispeer (const struct in6_addr *v6addr) 156 {
157 u16 key = teredo_phash (v6addr);
158 if ((teredo_peer[key].v6addr.in6_u.u6_addr32[0] == v6addr->in6_u.u6_addr32[0]) &&
159 (teredo_peer[key].v6addr.in6_u.u6_addr32[1] == v6addr->in6_u.u6_addr32[1]) &&
160 (teredo_peer[key].v6addr.in6_u.u6_addr32[2] == v6addr->in6_u.u6_addr32[2]) &&
161 (teredo_peer[key].v6addr.in6_u.u6_addr32[3] == v6addr->in6_u.u6_addr32[3]) ) 162 {
163 return 1;
164 }
169 } 170
171 static inline int
172 teredo_istrust (const struct in6_addr *v6addr) 173 {
185 //return ((tv.tv_sec- teredo_peer[key].last_tx.tv_sec) >= TEREDO_TOUT)?0:1;
186 return 1;
187 } 188 } 189
190 static inline void
191 teredo_trust (const struct in6_addr *v6addr) 192 {
199 teredo_add_mapped(const struct in6_addr *v6addr, const u32 addr, const u16 port) 200 {
208 teredo_peer_port(const struct in6_addr *v6addr) 209 {
215 teredo_peer_addr(const struct in6_addr *v6addr) 216 {
222 teredo_settime_tx (const struct in6_addr *v6addr) 223 {
224 u16 key = teredo_phash (v6addr);
225 do_gettimeofday(&(teredo_peer[key].last_tx));
226 return;
227 } 228
229 static inline void
230 teredo_settime_rx (const struct in6_addr *v6addr)
235 } 236
237 static inline int
238 teredo_cmptime (const struct timeval *tva, const struct timeval *tvb) 239 {
240 return (tva->tv_sec - tva->tv_sec);
241 } 242
243 static inline void
244 teredo_build_bubble (struct sk_buff *skb) 245 {
264 teredo_enqueue (const struct in6_addr *v6addr, const struct sk_buff *skb) 265 {
266 u16 key = teredo_phash (v6addr);
267 skb_queue_tail(&(teredo_peer[key].pktq), (struct sk_buff *) skb);
268 return;
269 } 270
271 static inline struct sk_buff*
272 teredo_dequeue (const struct in6_addr *v6addr) 273 {
274 u16 key = teredo_phash (v6addr);
275 struct sk_buff* skb = skb_dequeue(&(teredo_peer[key].pktq));
276 return skb;
277 } 278
279 static inline int
280 teredo_isqempty (const struct in6_addr *v6addr) 281 {
282 u16 key = teredo_phash (v6addr);
283 return skb_queue_empty(&(teredo_peer[key].pktq));
284 }
7 *
27 #include <linux/netfilter_ipv4.h>
28
61 DEBUG_TRACE("TEREDO:udpip6_rcv\n");
62 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
73 /* check source and dest, dest must be 3544 */
139 tunnel->stat.rx_packets++;
140 tunnel->stat.rx_bytes += skb->len;
141 skb->dev = tunnel->dev;
142 dst_release(skb->dst);
143 skb->dst = NULL;
144 #ifdef CONFIG_NETFILTER
145 nf_conntrack_put(skb->nfct);
146 skb->nfct = NULL;
147 #ifdef CONFIG_NETFILTER_DEBUG 148 skb->nf_debug = 0;
149 #endif 150 #endif
151 if (INET_ECN_is_ce(iph->tos) && INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h))) 152 {
160 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
161 fout:
175 do_ip_send(struct sk_buff *skb) 176 {
177 DEBUG_TRACE("TEREDO:do_ip_send\n");
178 return ip_send(skb);
192 udpip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) 193 {
194 struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
195 struct net_device_stats *stats = &tunnel->stat;
196 struct iphdr *tiph = &tunnel->parms.iph;
197 struct ipv6hdr *iph6 = skb->nh.ipv6h;
198 u8 tos = tunnel->parms.iph.tos;
199 struct rtable *rt; /* Route to the other host */
200 struct net_device *tdev; /* Device to other host */
205
206 struct udphdr *uh;
207 int teredo_send_bubble = 0; //flag for bubble 208 struct sk_buff *bubble = NULL;
209
210 DEBUG_TRACE("TEREDO:udpip6_tunnel_xmit\n");
211 if (tunnel->recursion++)
271 }
289 if (ip_route_output(&rt, dst, tiph->saddr, RT_TOS(tos), tunnel->parms.link)) 290 {
337 }
361 max_headroom = (((tdev->hard_header_len+15)&~15)+sizeof(struct iphdr)+sizeof(struct udphdr));
362
363 if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) { 364 struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
385 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
386 dst_release(skb->dst);
403 uh->dest = teredo_peer_port(&iph6->daddr);//use mapped port in peer list
430 #ifdef CONFIG_NETFILTER 431 nf_conntrack_put(skb->nfct);
432 skb->nfct = NULL;
433 #ifdef CONFIG_NETFILTER_DEBUG 434 skb->nf_debug = 0;
454 net_device_stats *udpip6_tunnel_get_stats(struct net_device *dev) 455 {
456 DEBUG_TRACE("TEREDO:udpip6_tunnel_get_stats\n");
457 return &(((struct ip_tunnel*)dev->priv)->stat);
458 } 459 460
461 static int
462 udpip6_tunnel_change_mtu(struct net_device *dev, int new_mtu) 463 {
464 DEBUG_TRACE("TEREDO:udpip6_tunnel_change_mtu\n");
469 dev->mtu = new_mtu;
475 udpip6_unuse_module(struct net_device *dev) 476 {
477 DEBUG_TRACE("TEREDO:udpip6_tunnel_close\n");
478 MOD_DEC_USE_COUNT;
484 udpip6_use_module(struct net_device *dev) 485 {
486 DEBUG_TRACE("TEREDO:udpip6_tunnel_open\n");
487 MOD_INC_USE_COUNT;
493 udpip6_tunnel_uninit(struct net_device *dev) 494 {
495 DEBUG_TRACE("TEREDO:udpip6_tunnel_uninit\n");
496 write_lock_bh(&udpip6_lock);
510 udpip6_tunnel_init(struct net_device *dev) 511 {
512 struct iphdr *iph;
513 DEBUG_TRACE("TEREDO:udpip6_tunnel_init\n");
514 struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
515
516 dev->destructor = 0;
517 dev->uninit = udpip6_tunnel_uninit;
518 dev->hard_start_xmit = udpip6_tunnel_xmit;
519 dev->get_stats = udpip6_tunnel_get_stats;
520 dev->do_ioctl = 0;
521 dev->change_mtu = udpip6_tunnel_change_mtu;
522
523 dev->type = ARPHRD_SIT;
524 dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr) + sizeof(struct udphdr);
525 dev->mtu = TEREDO_MTU;
526 dev->flags = IFF_NOARP;
527 dev->iflink = 0;
528 dev->addr_len = 4;
529 memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
530 memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
535 iph->version = 4;
552 cleanup_module(void) 553 {
554 DEBUG_TRACE("TEREDO:cleanup_module\n");
555 inet_del_protocol(&teredo_protocol);
556 unregister_netdev(&udpip6_tunnel_dev);
557 }
568 DEBUG_TRACE("TEREDO:init_module\n");
569 printk(KERN_INFO "IPv6 over UDP tunneling driver\n");
570
571 udpip6_tunnel_dev.priv = (void*)&udpip6_tunnel;
572 strcpy(udpip6_tunnel_dev.name, udpip6_tunnel.parms.name);
573 register_netdev(&udpip6_tunnel_dev);
574 inet_add_protocol(&teredo_protocol);
575
576 return 0;
577 } 578 579
580 MODULE_DESCRIPTION("Teredo IPv6 Tunneling module");
581 MODULE_LICENSE("GPL");