Lots of fresh code! You have been warned!!! Do pls report trouble!
[android6bed4] / src / nl / openfortress / android6bed4 / TunnelService.java
1 package nl.openfortress.android6bed4;
2
3 import android.net.VpnService;
4 import android.os.ParcelFileDescriptor;
5 import android.util.Log;
6
7 import java.net.*;
8 import java.io.*;
9 import java.util.Collection;
10 import java.util.Iterator;
11
12
13 public final class TunnelService extends VpnService {
14
15         private static final String TAG = "android6bed4.TunnelService";
16         
17         static private TunnelService singular_instance = null;
18         
19         static private ParcelFileDescriptor fio = null;
20         static private FileInputStream  downlink_rd = null;
21         static private FileOutputStream downlink_wr = null;
22
23         static private InetSocketAddress tunserver = null;
24         static public DatagramSocket uplink = null;
25
26         static private NeighborCache ngbcache = null;
27         
28         static private boolean new_setup_defaultroute = true;
29         static private boolean     setup_defaultroute = false;
30         static byte new_local_address [] = new byte [16];
31         static byte     local_address [] = new byte [16];
32         
33         static Worker worker;
34         static Maintainer maintainer;
35         
36         //static final byte sender_unknown []        = {                   0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
37         //static final byte linklocal_all_noders  [] = { (byte)0xff,(byte)0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 };
38         //static final byte linklocal_all_routers [] = { (byte)0xff,(byte)0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2 };
39         
40         final static byte IPPROTO_ICMPV6 = 58;
41         final static byte IPPROTO_TCP = 6;
42         
43         final static byte ND_ROUTER_SOLICIT   = (byte) 133;
44         final static byte ND_ROUTER_ADVERT    = (byte) 134;
45         final static byte ND_NEIGHBOR_SOLICIT = (byte) 135;
46         final static byte ND_NEIGHBOR_ADVERT  = (byte) 136;
47         final static byte ND_REDIRECT         = (byte) 137;
48         final static byte ND_LOWEST           = (byte) 133;
49         final static byte ND_HIGHEST          = (byte) 137;
50         
51         final static byte ND_OPT_PREFIX_INFORMATION = 3;
52         
53         final static int OFS_IP6_SRC        = 8;
54         final static int OFS_IP6_DST        = 24;
55         final static int OFS_IP6_PLEN       = 4;
56         final static int OFS_IP6_NXTHDR         = 6;
57         final static int OFS_IP6_HOPS           = 7;
58         
59         final static int OFS_ICMP6_TYPE         = 40 + 0;
60         final static int OFS_ICMP6_CODE         = 40 + 1;
61         final static int OFS_ICMP6_CSUM         = 40 + 2;
62         final static int OFS_ICMP6_DATA         = 40 + 4;
63         
64         final static int OFS_ICMP6_NGBSOL_TARGET = 40 + 8;
65         final static int OFS_ICMP6_NGBADV_TARGET = 40 + 8;
66         final static int OFS_ICMP6_NGBADV_FLAGS  = 40 + 4;
67                 
68         final static int OFS_TCP6_FLAGS     = 13;
69         final static int TCP_FLAG_SYN           = 0x02;
70         final static int TCP_FLAG_ACK           = 0x01;
71         
72         final static byte router_solicitation [] = {
73                         // IPv6 header
74                         0x60, 0x00, 0x00, 0x00,
75                         16 / 256, 16 % 256, IPPROTO_ICMPV6, (byte) 255,
76                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,          // unspecd src
77                         (byte) 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // all-rtr tgt
78                         // ICMPv6 header: router solicitation
79                         ND_ROUTER_SOLICIT, 0, 0x7a, (byte) 0xae,        // Checksum courtesy of WireShark :)
80                         // ICMPv6 body: reserved
81                         0, 0, 0, 0,
82                         // ICMPv6 option: source link layer address 0x0001 (end-aligned)
83                         0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
84                 };
85
86         final static byte router_linklocal_address [] = { (byte)0xfe,(byte)0x80,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
87         
88         private class Worker extends Thread {
89
90                 /* The timing for polling of the two tunnel endpoints.
91                  * There is an exponential fallback scheme, where the
92                  * tunnel hopes to find the best frequency to watch
93                  * for regularly occurring traffic.
94                  */
95                 private int polling_millis     = 10;
96                 private int polling_millis_min =  1;
97                 private int polling_millis_max = 50;
98                 
99                 public void handle_4to6_nd (byte pkt [], int pktlen, SocketAddress src)
100                 throws IOException, SocketException {
101                         if (checksum_icmpv6 (pkt, 0) != fetch_net16 (pkt, OFS_ICMP6_CSUM)) {
102                                 // Checksum is off
103                                 return;
104                         }
105                         switch (pkt [OFS_ICMP6_TYPE]) {
106                         //
107                         // Handle Router Solicitation by dropping it -- this is a peer, not a router
108                         case ND_ROUTER_SOLICIT:
109                                 return;
110                         //
111                         // Handle Router Advertisement as an addressing offering -- but validate the sender
112                         case ND_ROUTER_ADVERT:
113                                 if (pktlen < 40 + 16 + 16) {
114                                         // Too short to contain IPv6, ICMPv6 RtrAdv and Prefix Option
115                                         return;
116                                 }
117                                 if ((pkt [OFS_ICMP6_DATA+1] & 0x80) != 0x00) {
118                                         // Indecent proposal to use DHCPv6 over 6bed4
119                                         return;
120                                 }
121                                 if (memdiff_addr (pkt, OFS_IP6_SRC, router_linklocal_address, 0)) {
122                                         // Sender is not 0xfe80::/128
123                                         return;
124                                 }
125                                 if (memdiff_halfaddr (pkt, OFS_IP6_DST, router_linklocal_address, 0)) {
126                                         // Receiver address is not 0xfe80::/64
127                                         return;
128                                 }
129                                 if ((pkt [OFS_IP6_DST + 11] != (byte) 0xff) || (pkt [OFS_IP6_DST + 12] != (byte) 0xfe)) {
130                                         // No MAC-based destination address ending in ....:..ff:fe..:....
131                                         return;
132                                 }
133                                 //TODO// Check if offered address looks like a multicast-address (MAC byte 0 is odd)
134                                 //TODO// Check Secure ND on incoming Router Advertisement?
135                                 //
136                                 // Having validated the Router Advertisement, process its contents
137                                 int destprefix_ofs = 0;
138                                 int rdofs = OFS_ICMP6_DATA + 12;
139                                 //TODO:+4_WRONG?// while (rdofs <= ntohs (v4v6plen) + 4) { ... }
140                                 while (rdofs + 4 < pktlen) {
141                                         if (pkt [rdofs + 1] == 0) {
142                                                 return;   /* zero length option */
143                                         }
144                                         if (pkt [rdofs + 0] != ND_OPT_PREFIX_INFORMATION) {
145                                                 /* skip to next option */
146                                         } else if (pkt [rdofs + 1] != 4) {
147                                                 return;   /* bad length field */
148                                         } else if (rdofs + (pkt [rdofs + 1] << 3) > pktlen + 4) {
149                                                 return;   /* out of packet length */
150                                         } else if ((pkt [rdofs + 3] & (byte) 0xc0) != (byte) 0xc0) {
151                                                 /* no on-link autoconfig prefix */
152                                         } else if (pkt [rdofs + 2] != 64) {
153                                                 return;
154                                         } else {
155                                                 destprefix_ofs = rdofs + 16;
156                                         }
157                                         rdofs += (pkt [rdofs + 1] << 3);
158                                 }
159                                 if (destprefix_ofs > 0) {
160                                         for (int i=0; i<8; i++) {
161                                                 new_local_address [0 + i] = pkt [destprefix_ofs + i];
162                                                 new_local_address [8 + i] = pkt [OFS_IP6_DST + 8 + i]; 
163                                         }
164                                         //TODO// syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
165                                         change_local_netconfig ();  //TODO// parameters?
166                                         Log.i (TAG, "Assigned address to tunnel");
167                                 }
168                                 return;
169                         //
170                         // Neighbor Solicitation is an attempt to reach us peer-to-peer, and should be responded to
171                         case ND_NEIGHBOR_SOLICIT:
172                                 if (pktlen < 24) {
173                                         // Too short to make sense
174                                         return;
175                                 }
176                                 if (memdiff_addr (pkt, OFS_ICMP6_NGBSOL_TARGET, local_address, 0)) {
177                                         // Neighbor Solicitation not aimed at me
178                                         return;
179                                 }
180                                 if (!ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) {
181                                         // Source is not a 6bed4 address
182                                         return;
183                                 }
184                                 //
185                                 // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
186                                 // Not checked here: LLaddr in NgbSol -- simply send back to IPv6 src address
187                                 //
188                                 memcp_address (pkt, OFS_IP6_DST, pkt, OFS_IP6_SRC);
189                                 memcp_address (pkt, OFS_IP6_SRC, local_address, 0);
190                                 pkt [OFS_ICMP6_TYPE] = ND_NEIGHBOR_ADVERT;
191                                 pkt [OFS_IP6_PLEN + 0] = 0;
192                                 pkt [OFS_IP6_PLEN + 1] = 8 + 16;
193                                 pkt [OFS_ICMP6_NGBADV_FLAGS] = 0x60;    // Solicited, Override
194                                 // Assume that OFS_ICMP6_NGBADV_TARGET == OFS_ICMP6_NGBSOL_TARGET
195                                 int csum = TunnelService.checksum_icmpv6 (pkt, 0);
196                                 pkt [OFS_ICMP6_CSUM + 0] = (byte) (csum >> 8  );
197                                 pkt [OFS_ICMP6_CSUM + 1] = (byte) (csum & 0xff);
198                                 DatagramPacket replypkt = new DatagramPacket (pkt, 0, 40 + 8 + 16, src);
199                                 uplink.send (replypkt);
200
201                                 //
202                                 // TODO:OLD Replicate the message over the tunnel link
203                                 //
204                                 // We should attach a Source Link-Layer Address, but
205                                 // we cannot automatically trust the one provided remotely.
206                                 // Also, we want to detect if routes differ, and handle it.
207                                 //
208                                 // 0. if no entry in the ngb.cache
209                                 //    then use 6bed4 server in ND, initiate ngb.sol to src.ll
210                                 //         impl: use 6bed4-server lladdr, set highest metric
211                                 // 1. if metric (ngb.cache) < metric (src.ll)
212                                 //    then retain ngb.cache, send Redirect to source
213                                 // 2. if metric (ngb.cache) > metric (src.ll)
214                                 //    then retain ngb.cache, initiate ngb.sol to src.ll
215                                 // 3. if metric (ngb.cache) == metric (src.ll)
216                                 //    then retain ngb.cache
217                                 //
218                                 //TODO// Handle ND_NEIGHBOR_SOLICIT (handle_4to6_nd)
219                                 return;
220                         //
221                         // Neighbor Advertisement may be in response to our peer-to-peer search
222                         case ND_NEIGHBOR_ADVERT:
223                 //
224                 // Process Neighbor Advertisement coming in over 6bed4
225                 // First, make sure it is against an item in the ndqueue
226                                 //
227                                 // Validate the Neighbor Advertisement
228                                 if (pktlen < 64) {
229                                         // Packet too small to hold ICMPv6 Neighbor Advertisement
230                                         return;
231                                 }
232                                 if ((pkt [OFS_ICMP6_TYPE] != ND_NEIGHBOR_ADVERT) || (pkt [OFS_ICMP6_CODE] != 0)) {
233                                         // ICMPv6 Type or Code is wrong
234                                         return;
235                                 }
236                                 if ((!ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) || (!ngbcache.is6bed4 (pkt, OFS_IP6_DST))) {
237                                         // Source or Destination IPv6 address is not a 6bed4 address
238                                         return;
239                                 }
240                                 if (memdiff_addr (pkt, OFS_IP6_SRC, pkt, OFS_ICMP6_NGBADV_TARGET)) {
241                                         // NgbAdv's Target Address does not match IPv6 source
242                                         return;
243                                 }
244                                 //
245                                 // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
246                                 //
247                                 ngbcache.received_peer_direct_acknowledgement (pkt, OFS_ICMP6_NGBADV_TARGET, false);
248                                 return;
249                         //
250                         // Route Redirect messages are not supported in 6bed4 draft v01
251                         case ND_REDIRECT:
252                                 return;
253                         }
254                 }
255
256                 /* Forward an IPv6 packet, wrapped into UDP and IPv4 in the 6bed4
257                  * way, as a pure IPv6 packet over the tunnel interface.  This is
258                  * normally a simple copying operation.  One exception exists for
259                  * TCP ACK packets; these may be in response to a "playful" TCP SYN
260                  * packet that was sent directly to the IPv4 recipient.  This is a
261                  * piggyback ride of the opportunistic connection efforts on the
262                  * 3-way handshake for TCP, without a need to modify the packets!
263                  * The only thing needed to make that work is to report success
264                  * back to the Neighbor Cache, in cases when TCP ACK comes back in
265                  * directly from the remote peer.
266                  * 
267                  * Note that nothing is stopping an ACK packet that is meaningful
268                  * to us from also being a SYN packet that is meaningful to the
269                  * remote peer.  We will simply do our thing and forward any ACK
270                  * to the most direct route we can imagine -- which may well be
271                  * the sender, _especially_ since we opened our 6bed4 port to the
272                  * remote peer when sending our playful initial TCP packet.
273                  * 
274                  * Observing the traffic on the network, this may well look like
275                  * magic!  All you see is plain TCP traffic crossing over directly
276                  * if it is possible --and bouncing one or two packets through the
277                  * tunnel otherwise-- and especially in the case where it can work
278                  * directly it will be a surprise.  Servers are therefore strongly
279                  * encouraged to setup port forwarding for their 6bed4 addresses,
280                  * or just open a hole in full cone NAT/firewall setups.  This will
281                  * mean zero delay and zero bypasses for 6bed4 on the majority of
282                  * TCP connection initiations between 6bed4 peers!
283                  */
284                 public void handle_4to6_plain (byte pkt [], int pktlen)
285                 throws IOException {
286                         if (downlink_wr != null) {
287                                 downlink_wr.write (pkt, 0, pktlen);
288                         }
289                         //
290                         // If this is a successful peering attempt, that is, a tcpack packet, report that back
291                         // Note that the UDP/IPv4 source has already been validated against the IPv6 source
292                         boolean tcpack = (pktlen >= 40 + 20) && (pkt [OFS_IP6_NXTHDR] == IPPROTO_TCP) && ((pkt [OFS_TCP6_FLAGS] & TCP_FLAG_ACK) != 0x00);
293                         if (tcpack) {
294                                 if (ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) {
295                                         ngbcache.received_peer_direct_acknowledgement (pkt, OFS_IP6_SRC, true);
296                                 }
297                         }
298                 }
299                 
300                 public void handle_4to6 (DatagramPacket datagram)
301                 throws IOException {
302                         byte pkt [] = datagram.getData ();
303                         int pktlen = datagram.getLength ();
304
305                         if (pktlen < 40) {
306                                 return;
307                         }
308                         if ((pkt [0] & (byte) 0xf0) != 0x60) {
309                                 return;
310                         }
311                         validate_originator (pkt, (InetSocketAddress) datagram.getSocketAddress ());
312                         if ((pkt [OFS_IP6_NXTHDR] == IPPROTO_ICMPV6) && (pkt [OFS_ICMP6_TYPE] >= ND_LOWEST) && (pkt [OFS_ICMP6_TYPE] <= ND_HIGHEST)) {
313                                 //
314                                 // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
315                                 handle_4to6_nd (pkt, pktlen, datagram.getSocketAddress ());
316                         } else {
317                                 //
318                                 // Plain Unicast or Plain Multicast (both may enter)
319                                 handle_4to6_plain (pkt, pktlen);
320                         }
321                 }
322                 
323                 public void validate_originator (byte pkt [], InetSocketAddress originator)
324                 throws IOException {
325 /* TODO: validate originator address
326                         if (tunserver.equals (originator)) {
327                                 return;
328                         }
329                         if (memdiff_halfaddr (pkt, OFS_IP6_SRC, router_linklocal, 0) && ((local_address == null) || memdiff_halfaddr (pkt, OFS_IP6_SRC, local_address, 8))) {
330                                 throw new IOException ("Incoming 6bed4 uses bad /64 prefix");
331                         }
332                         int port = (pkt [OFS_IP6_SRC + 8] ^ 0x02) & 0xff;
333                         port = (port | (pkt [OFS_IP6_SRC + 9] << 8) & 0xffff;
334                         if (originator.getPort () != port) {
335                                 throw new IOException ("Incoming 6bed4 uses ")
336                         }
337 */
338                 }
339
340                 /* This routine passes IPv6 traffic from the tunnel interface on
341                  * to the 6bed4 interface where it is wrapped into UDP and IPv4.
342                  * The only concern for this is where to send it to -- should it
343                  * be sent to the tunnel server, or directly to the peer?  The
344                  * Neighbor Cache is consulted for advise.
345                  * 
346                  * A special flag exists to modify the behaviour of the response
347                  * to this inquiry.  This flag is used to signal that a first
348                  * packet might be tried directly, which should be harmless if
349                  * it fails and otherwise lead to optimistic connections if:
350                  *  1. the packet will repeat upon failure, and
351                  *  2. explicit acknowledgement can be reported to the cache
352                  * This is the case with TCP connection setup; during a SYN,
353                  * it is possible to be playful and try to send the first
354                  * packet directly.  A TCP ACK that returns directly from the
355                  * sender indicates that return traffic is possible, which is
356                  * then used to update the Neighbor Cache with positivism on
357                  * the return route.
358                  */
359                 public void handle_6to4_plain_unicast (byte pkt [], int pktlen)
360                 throws IOException {
361                         InetSocketAddress target;
362                         if ((ngbcache != null) && ngbcache.is6bed4 (pkt, 24)) {
363                                 boolean tcpsyn = (pkt [OFS_IP6_NXTHDR] == IPPROTO_TCP) && ((pkt [OFS_TCP6_FLAGS] & TCP_FLAG_SYN) != 0x00);
364                                 target = ngbcache.lookup_neighbor (pkt, 24, tcpsyn);
365                         } else {
366                                 target = tunserver;
367                         }
368                         uplink.send (new DatagramPacket (pkt, pktlen, target));
369                 }
370                 
371                 public void handle_6to4_nd (byte pkt [], int pktlen)
372                 throws IOException {
373                         switch (pkt [OFS_ICMP6_TYPE]) {
374                         //
375                         // Handle Router Solicitation by answering it with the local configuration
376                         case ND_ROUTER_SOLICIT:
377                                 int ofs = OFS_ICMP6_TYPE;
378                                 pkt [ofs++] = ND_ROUTER_ADVERT;         // type
379                                 pkt [ofs++] = 0;                                        // code
380                                 ofs += 2;                                                       // checksum
381                                 pkt [ofs++] = 0;                                        // hop limit -- unspecified
382                                 pkt [ofs++] = 0x18;                                     // M=0, O=0, H=0, Prf=11=Low, Reserved=0
383                                 pkt [ofs++] = setup_defaultroute? (byte) 0xff: 0x00;    // Lifetime
384                                 pkt [ofs++] = setup_defaultroute? (byte) 0xff: 0x00;    // (cont)
385                                 for (int i=0; i<8; i++) {
386                                         pkt [ofs++] = 0;                                // Reachable time, Retrans timer
387                                 }
388                                 pkt [ofs-6] = (byte) 0x80;                      // Reachable time := 32s
389                                 pkt [ofs-2] = 0x01;                                     // Retrans timer := 0.25s
390                                 // Start of Prefix Option
391                                 pkt [ofs++] = ND_OPT_PREFIX_INFORMATION;
392                                 pkt [ofs++] = 4;                // Option length = 4 * 8 bytes
393                                 pkt [ofs++] = (byte) 128;               // Announce a /64 prefix (TODO: Temporarily /128)
394                                 pkt [ofs++] = (byte) 0x80;      // Link-local, No autoconfig, tunnel does the work
395                                 for (int i=0; i<8; i++) {
396                                         pkt [ofs++] = (byte) 0xff;              // Valid / Preferred Lifetime: Infinite
397                                 }
398                                 for (int i=0; i<4; i++) {
399                                         pkt [ofs++] = 0;                                // Reserved
400                                 }
401                                 memcp_address (pkt, ofs, local_address, 0);
402                                 ofs += 16;
403                                 // End of Prefix Option
404                                 memcp_address (pkt, OFS_IP6_DST, pkt, OFS_IP6_SRC);     // dst:=src
405                                 memcp_address (pkt, OFS_IP6_SRC, local_address, 0);
406                                 //TODO// Send packet back to IPv6 downlink
407                                 return;
408                         //
409                         // Handle Router Advertisement by dropping it -- Android is not setup a router
410                         case ND_ROUTER_ADVERT:
411                                 return;
412                         //
413                         // Neighbor Solicitation is not normally sent by the phone due to its /128 on 6bed4
414                         case ND_NEIGHBOR_SOLICIT:
415                                 return;
416                         //
417                         // Neighbor Advertisement is a response to a peer, and should be relayed
418                         case ND_NEIGHBOR_ADVERT:
419                                 //TODO// Possibly arrange the peer's receiving address 
420                                 handle_6to4_plain_unicast (pkt, pktlen);
421                                 return;
422                         // Route Redirect messages are not supported in 6bed4 draft v01
423                         case ND_REDIRECT:
424                                 return;
425                         }
426                 }
427
428                 public void handle_6to4 (byte pkt [], int pktlen)
429                 throws IOException {
430                         if (pktlen < 41) {
431                                 return;
432                         }
433                         if ((pkt [0] & 0xf0) != 0x60) {
434                                 return;
435                         }
436                         if ((pkt [OFS_IP6_NXTHDR] == IPPROTO_ICMPV6) && (pkt [OFS_ICMP6_TYPE] >= ND_LOWEST) && (pkt [OFS_ICMP6_TYPE] <= ND_HIGHEST)) {
437                                 //
438                                 // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
439                                 handle_6to4_nd (pkt, pktlen);
440                         } else if ((pkt [OFS_IP6_DST+0] != 0xff) && ((pkt [OFS_IP6_DST+8] & 0x01) == 0x00)) {
441                                 //
442                                 // Plain Unicast
443                                 pkt [OFS_IP6_HOPS]--;
444                                 if (pkt [OFS_IP6_HOPS] == 0) {
445                                         return;
446                                 }
447                                 handle_6to4_plain_unicast (pkt, pktlen);
448                         } else {
449                                 //
450                                 // Plain Multicast
451                                 //TODO: Ignore Multicast for now...
452                         }
453                 }
454
455                 /* The worker thread pulls data from both tunnel ends, and passes it on to the other.
456                  * Packets dealing with Neighbor Discovery are treated specially by this thread. 
457                  */
458                 public void run () {
459                         //
460                         // Pump data back and forth, interpreting Neighbor Discovery locally
461                         byte packet_up [] = new byte [1280];
462                         DatagramPacket packet_dn = new DatagramPacket (new byte [1280+28], 1280+28);
463                         /* 
464                          * I am aware that the following code is less than optimal!
465                          * For some reason, the VpnService only returns a non-blocking
466                          * file descriptor that cannot subsequently be altered to be
467                          * blocking (for two parallel Threads) or used in a select()
468                          * style.  So polling is required.
469                          * The uplink is blocking, just to continue the confusion.
470                          * A slight upgrade of performance might be achieved by setting
471                          * up a separate Thread to handle that, but given that polling
472                          * is needed anyway, I decided not to bother to get the best out
473                          * of what seems to me like a bad design choice in VpnService.
474                          * If I have missed a way to turn the VpnService interface into
475                          * one that works in select() or even just a blocking version
476                          * than *please* let me know!  IMHO, polling is a rude technique.
477                          * 
478                          * Rick van Rein, October 2012.
479                          */
480                         while (true) {
481                                 int uplen = 0;
482                                 boolean nothingdown;
483                                 synchronized (this) {
484                                         try {
485                                                 if (downlink_rd != null) {
486                                                         uplen = downlink_rd.read (packet_up);
487                                                 }
488                                                 if (uplen > 0) {
489                                                         handle_6to4 (packet_up, uplen);
490                                                 }
491                                         } catch (SocketTimeoutException ex) {
492                                                 ;
493                                         } catch (IOException ex) {
494                                                 ;
495                                         } catch (ArrayIndexOutOfBoundsException ex) {
496                                                 ;
497                                         }
498                                 }
499                                 synchronized (this) {
500                                         if (uplink == null) {
501                                                 nothingdown = true;
502                                         } else {
503                                                 try {
504                                                         uplink.receive (packet_dn);
505                                                         handle_4to6 (packet_dn);
506                                                         nothingdown = false;
507                                                 } catch (SocketTimeoutException ex) {
508                                                         nothingdown = true;
509                                                 } catch (IOException ex) {
510                                                         nothingdown = true;
511                                                 } catch (ArrayIndexOutOfBoundsException ex) {
512                                                         nothingdown = true;
513                                                 }
514                                         }
515                                 }
516                                 if ((uplen == 0) || nothingdown) {
517                                         try {
518                                                 polling_millis <<= 1;
519                                                 if (polling_millis > polling_millis_max) {
520                                                         polling_millis = polling_millis_max;
521                                                 }
522                                                 sleep (polling_millis);
523                                         } catch (InterruptedException ie) {
524                                                 ;       // Great, let's move on!
525                                         }
526                                 } else {
527                                         polling_millis = polling_millis_min;
528                                 }
529                         }
530                 }
531         }
532         
533         private class Maintainer extends Thread {
534                 
535                 /* The time for the next scheduled maintenance: routersol or keepalive.
536                  * The milliseconds are always 0 for maintenance tasks.
537                  */
538                 private long maintenance_time_millis;
539                 private int maintenance_time_cycle = 0;
540                 private int maintenance_time_cycle_max = 30;
541                 private boolean have_lladdr = false;
542                 private int keepalive_period = 30;
543                 private int keepalive_ttl = -1;
544                 private DatagramPacket keepalive_packet = null;
545         
546                 /* Perform the initial Router Solicitation exchange with the public server.
547                  */
548                 public void solicit_router () {
549                         if (uplink != null) {
550                                 try {
551                                         DatagramPacket rtrsol = new DatagramPacket (router_solicitation, router_solicitation.length, tunserver);
552                                         uplink.send (rtrsol);
553                                 } catch (IOException ioe) {
554                                         /* Network is probably down, so don't
555                                          * throw new RuntimeException ("Network failure", ioe);
556                                          */
557                                 }
558                         }
559                 }
560                 
561                 /* Send a KeepAlive packet to the public server.
562                  * Note, ideally, we would set a low-enough TTL to never reach it;
563                  * after all, the only goal is to open /local/ firewalls and NAT.
564                  * Java however, is not capable of setting TTL on unicast sockets.
565                  */
566                 public void keepalive () {
567                         if ((keepalive_packet != null) && (uplink != null)) {
568                                 try {
569                                         uplink.send (keepalive_packet);
570                                         Log.i (TAG, "Sent KeepAlive (empty UDP) to Tunnel Server");
571                                 } catch (IOException ioe) {
572                                         ;       /* Network is probably down; order reconnect to tunnel server */
573                                         have_lladdr = false;
574                                 }
575                         }
576                 }
577                 
578                 /* Perform regular maintenance tasks: KeepAlive, and requesting a local address.
579                  */
580                 public void regular_maintenance () {
581                         if (!have_lladdr) {
582                                 solicit_router ();
583                                 maintenance_time_cycle <<= 1;
584                                 maintenance_time_cycle += 1;
585                                 if (maintenance_time_cycle > maintenance_time_cycle_max) {
586                                         maintenance_time_cycle = maintenance_time_cycle_max;
587                                 }
588                                 //TODO// syslog (LOG_INFO, "Sent Router Advertisement to Public 6bed4 Service, next attempt in %d seconds\n", maintenance_time_cycle);
589                                 Log.i (TAG, "Sent Router Advertisement to Tunnel Server");
590                         } else {
591                                 //TODO// syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
592                                 keepalive ();
593                                 if (have_lladdr) {
594                                         maintenance_time_cycle = maintenance_time_cycle_max;
595                                 } else {
596                                         maintenance_time_cycle = 1;
597                                 }
598                         }
599                         maintenance_time_millis = System.currentTimeMillis () + 1000 * (long) maintenance_time_cycle;
600                 }
601                 
602                 /* Tell the maintenance routine whether a local address has been setup.
603                  * Until this is called, the maintenance will focus on getting one through
604                  * regular Router Solicitation messages.  It is possible to revert to this
605                  * behaviour by setting the flag to false; this can be useful in case of
606                  * changes, for instance resulting from an IPv4 address change.
607                  */
608                 public void have_local_address (boolean new_setting) {
609                         have_lladdr = new_setting;
610                         if (have_lladdr) {
611                                 maintenance_time_cycle = maintenance_time_cycle_max;
612                                 maintenance_time_millis = System.currentTimeMillis () + 1000 * maintenance_time_cycle;
613                         }
614                 }
615                 
616                 /* Run the regular maintenance thread.  This involves sending KeepAlives
617                  * and possibly requesting a local address through Router Solicitation.
618                  */
619                 public void run () {
620                         try {
621                                 while (true) {
622                                         regular_maintenance ();
623                                         sleep (maintenance_time_millis - System.currentTimeMillis());
624                                 }
625                         } catch (InterruptedException ie) {
626                                 ;
627                         }
628                 }
629                 
630                 /* Construct the Maintainer thread.
631                  */
632                 public Maintainer (SocketAddress server) {
633                         byte payload [] = { };
634                         if (server != null) {
635                                 try {
636                                         keepalive_packet = new DatagramPacket (payload, 0, server);
637                                 } catch (SocketException se) {
638                                         keepalive_packet = null;
639                                 }
640                         }
641                 }
642         }
643         
644         public TunnelService () {
645         }
646         
647         /* Notify the tunnel of a new local address.  This is called after
648          * updating the values of the following variables which are
649          * private, but accessible to the maintenance cycle:
650          *  - byte new_local_address [16]
651          *  - boolean new_setup_defaultroute
652          */
653         public void change_local_netconfig () {
654                 Builder builder;
655                 builder = new Builder ();
656                 builder.setSession ("6bed4 uplink to IPv6");
657                 builder.setMtu (1280);
658                 try {
659                         //TODO// For now, setup a /128 address to avoid v01-style Neighbor Discovery
660                         builder.addAddress (Inet6Address.getByAddress (new_local_address), 128);
661                 } catch (UnknownHostException uhe) {
662                         throw new RuntimeException ("6bed4 address rejected", uhe);
663                 }
664                 if (new_setup_defaultroute) {
665                         Log.i (TAG, "Creating default route through 6bed4 tunnel");
666                         builder.addRoute ("::", 0);
667                 } else {
668                         Log.i (TAG, "Skipping default route through 6bed4 tunnel");
669                 }
670                 if (fio != null) {
671                         try {
672                                 fio.close ();
673                         } catch (IOException ioe) {
674                                 ; /* Uncommon: Fallback to garbage collection */
675                         }
676                 }
677                 //
678                 // Setup the address information for the current tunnel
679                 setup_defaultroute = new_setup_defaultroute;
680                 memcp_address (local_address, 0, new_local_address, 0);
681                 //
682                 // Setup a new neighboring cache, possibly replacing an old one
683                 ngbcache = new NeighborCache (uplink, tunserver, local_address);
684                 //
685                 // Now actually construct the tunnel as prepared
686                 fio = builder.establish ();
687                 try {
688                         uplink.setSoTimeout (1);
689                 } catch (SocketException se) {
690                         throw new RuntimeException ("UDP socket refuses turbo mode", se);
691                 }
692                 synchronized (this) {
693                         downlink_rd = new FileInputStream  (fio.getFileDescriptor ());
694                         downlink_wr = new FileOutputStream (fio.getFileDescriptor ());
695                 }
696                 synchronized (worker) {
697                         worker.notifyAll ();
698                 }
699                 maintainer.have_local_address (true);
700         }
701         
702         public void notify_ipv6_addresses (Collection <byte []> addresslist) {
703                 // See if the IPv6 address list causes a change to the wish for a default route through 6bed4
704                 Iterator <byte []> adr_iter = addresslist.iterator();
705                 new_setup_defaultroute = true;
706                 while (adr_iter.hasNext ()) {
707                         byte addr [] = adr_iter.next ();
708                         if ((addr.length == 16) && memdiff_addr (addr, 0, local_address, 0)) {
709                                 new_setup_defaultroute = false;
710                         }
711                 }
712                 // If the default route should change, change the local network configuration
713                 if (new_setup_defaultroute != setup_defaultroute) {
714                         change_local_netconfig ();
715                 }
716         }
717         
718         public TunnelService (DatagramSocket uplink_socket, InetSocketAddress publicserver) {
719                 synchronized (this) {
720                         uplink = uplink_socket;
721                         tunserver = publicserver;
722                         // notifyAll ();
723                 }
724                 //
725                 // Setup a link to the instance of this singular class, for use in the EventMonitor
726                 singular_instance = this;
727                 //
728                 // Create the worker thread that will pass information back and forth
729                 if (worker == null) {
730                         worker = new Worker ();
731                         worker.start ();
732                 } else {
733                         synchronized (worker) {
734                                 worker.notifyAll ();
735                         }
736                 }
737                 if (maintainer == null) {
738                         maintainer = new Maintainer (tunserver);
739                         maintainer.start ();
740                 } else {
741                         maintainer.have_local_address (false);
742                 }
743         }
744         
745         public static TunnelService theTunnelService () {
746                 return singular_instance;
747         }
748         
749         synchronized public void teardown () {
750                 try {
751                         synchronized (this) {
752                                 singular_instance = null;
753                                 if (worker != null) {
754                                         worker.interrupt ();
755                                         worker = null;
756                                 }
757                                 if (maintainer != null) {
758                                         maintainer.interrupt ();
759                                         maintainer = null;
760                                 }
761                                 if (downlink_rd != null) {
762                                         downlink_rd.close ();
763                                         downlink_rd = null;
764                                 }
765                                 if (downlink_wr != null) {
766                                         downlink_wr.close ();
767                                         downlink_wr = null;
768                                 }
769                                 if (fio != null) {
770                                         fio.close ();
771                                         fio = null;
772                                 }
773                         }
774                 } catch (IOException ioe) {
775                         ;       /* Uncommon: Fallback to garbage collection */
776                 }
777         }
778         
779         synchronized public void onRevoke () {
780                 this.teardown ();
781         }
782
783
784         /***
785          *** UTILITY FUNCTIONS
786          ***/
787
788         public static void memcp_address (byte tgt [], int tgtofs, byte src [], int srcofs) {
789                 for (int i=0; i<16; i++) {
790                         tgt [tgtofs+i] = src [srcofs+i];
791                 }
792         }
793         
794         public static boolean memdiff_addr (byte one[], int oneofs, byte oth[], int othofs) {
795                 for (int i=0; i<16; i++) {
796                         if (one [oneofs + i] != oth [othofs + i]) {
797                                 return true;
798                         }
799                 }
800                 return false;
801         }
802         
803         public static boolean memdiff_halfaddr (byte one[], int oneofs, byte oth[], int othofs) {
804                 for (int i=0; i<8; i++) {
805                         if (one [oneofs + i] != oth [othofs + i]) {
806                                 return true;
807                         }
808                 }
809                 return false;
810         }
811
812         /* Retrieve an unsigned 16-bit value from a given index in a byte array and
813          * return it as an integer.
814          */
815         public static int fetch_net16 (byte pkt [], int ofs16) {
816                 int retval = ((int) pkt [ofs16]) << 8 & 0xff00;
817                 retval = retval + (((int) pkt [ofs16+1]) & 0xff);
818                 return retval;
819         }
820         
821         /* Fill in the ICMPv6 checksum field in a given IPv6 packet.
822          */
823         public static int checksum_icmpv6 (byte pkt [], int pktofs) {
824                 int plen = fetch_net16 (pkt, pktofs + OFS_IP6_PLEN);
825                 int nxth = ((int) pkt [pktofs + 6]) & 0xff;
826                 // Pseudo header is IPv6 src/dst (included with packet) and plen/nxth and zeroes:
827                 int csum = plen + nxth;
828                 int i;
829                 for (i=8; i < 40+plen; i += 2) {
830                         if (i != OFS_ICMP6_CSUM) {
831                                 // Skip current checksum value
832                                 csum += fetch_net16 (pkt, pktofs + i);
833                         }
834                 }
835                 // No need to treat a trailing single byte: ICMPv6 has no odd packet lengths
836                 csum = (csum & 0xffff) + (csum >> 16);
837                 csum = (csum & 0xffff) + (csum >> 16);
838                 csum = csum ^ 0xffff;   // 1's complement limited to 16 bits
839                 return csum;
840         }
841         
842 }