1 package nl.openfortress.android6bed4;
3 import android.net.VpnService;
4 import android.os.ParcelFileDescriptor;
5 import android.util.Log;
9 import java.util.Collection;
10 import java.util.Iterator;
13 public final class TunnelService extends VpnService {
15 private static final String TAG = "android6bed4.TunnelService";
17 static private TunnelService singular_instance = null;
19 static private ParcelFileDescriptor fio = null;
20 static private FileInputStream downlink_rd = null;
21 static private FileOutputStream downlink_wr = null;
23 static private InetSocketAddress tunserver = null;
24 static public DatagramSocket uplink = null;
26 static private NeighborCache ngbcache = null;
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];
34 static Maintainer maintainer;
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 };
40 final static byte IPPROTO_ICMPV6 = 58;
41 final static byte IPPROTO_TCP = 6;
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;
51 final static byte ND_OPT_PREFIX_INFORMATION = 3;
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;
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;
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;
68 final static int OFS_TCP6_FLAGS = 13;
69 final static int TCP_FLAG_SYN = 0x02;
70 final static int TCP_FLAG_ACK = 0x01;
72 final static byte router_solicitation [] = {
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
82 // ICMPv6 option: source link layer address 0x0001 (end-aligned)
83 0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
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 };
88 private class Worker extends Thread {
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.
95 private int polling_millis = 10;
96 private int polling_millis_min = 1;
97 private int polling_millis_max = 50;
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)) {
105 switch (pkt [OFS_ICMP6_TYPE]) {
107 // Handle Router Solicitation by dropping it -- this is a peer, not a router
108 case ND_ROUTER_SOLICIT:
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
117 if ((pkt [OFS_ICMP6_DATA+1] & 0x80) != 0x00) {
118 // Indecent proposal to use DHCPv6 over 6bed4
121 if (memdiff_addr (pkt, OFS_IP6_SRC, router_linklocal_address, 0)) {
122 // Sender is not 0xfe80::/128
125 if (memdiff_halfaddr (pkt, OFS_IP6_DST, router_linklocal_address, 0)) {
126 // Receiver address is not 0xfe80::/64
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..:....
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?
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 */
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) {
155 destprefix_ofs = rdofs + 16;
157 rdofs += (pkt [rdofs + 1] << 3);
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];
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");
170 // Neighbor Solicitation is an attempt to reach us peer-to-peer, and should be responded to
171 case ND_NEIGHBOR_SOLICIT:
173 // Too short to make sense
176 if (memdiff_addr (pkt, OFS_ICMP6_NGBSOL_TARGET, local_address, 0)) {
177 // Neighbor Solicitation not aimed at me
180 if (!ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) {
181 // Source is not a 6bed4 address
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
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);
202 // TODO:OLD Replicate the message over the tunnel link
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.
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
218 //TODO// Handle ND_NEIGHBOR_SOLICIT (handle_4to6_nd)
221 // Neighbor Advertisement may be in response to our peer-to-peer search
222 case ND_NEIGHBOR_ADVERT:
224 // Process Neighbor Advertisement coming in over 6bed4
225 // First, make sure it is against an item in the ndqueue
227 // Validate the Neighbor Advertisement
229 // Packet too small to hold ICMPv6 Neighbor Advertisement
232 if ((pkt [OFS_ICMP6_TYPE] != ND_NEIGHBOR_ADVERT) || (pkt [OFS_ICMP6_CODE] != 0)) {
233 // ICMPv6 Type or Code is wrong
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
240 if (memdiff_addr (pkt, OFS_IP6_SRC, pkt, OFS_ICMP6_NGBADV_TARGET)) {
241 // NgbAdv's Target Address does not match IPv6 source
245 // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
247 ngbcache.received_peer_direct_acknowledgement (pkt, OFS_ICMP6_NGBADV_TARGET, false);
250 // Route Redirect messages are not supported in 6bed4 draft v01
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.
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.
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!
284 public void handle_4to6_plain (byte pkt [], int pktlen)
286 if (downlink_wr != null) {
287 downlink_wr.write (pkt, 0, pktlen);
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);
294 if (ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) {
295 ngbcache.received_peer_direct_acknowledgement (pkt, OFS_IP6_SRC, true);
300 public void handle_4to6 (DatagramPacket datagram)
302 byte pkt [] = datagram.getData ();
303 int pktlen = datagram.getLength ();
308 if ((pkt [0] & (byte) 0xf0) != 0x60) {
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)) {
314 // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
315 handle_4to6_nd (pkt, pktlen, datagram.getSocketAddress ());
318 // Plain Unicast or Plain Multicast (both may enter)
319 handle_4to6_plain (pkt, pktlen);
323 public void validate_originator (byte pkt [], InetSocketAddress originator)
325 /* TODO: validate originator address
326 if (tunserver.equals (originator)) {
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");
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 ")
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.
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
359 public void handle_6to4_plain_unicast (byte pkt [], int pktlen)
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);
368 uplink.send (new DatagramPacket (pkt, pktlen, target));
371 public void handle_6to4_nd (byte pkt [], int pktlen)
373 switch (pkt [OFS_ICMP6_TYPE]) {
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
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
398 for (int i=0; i<4; i++) {
399 pkt [ofs++] = 0; // Reserved
401 memcp_address (pkt, ofs, local_address, 0);
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
409 // Handle Router Advertisement by dropping it -- Android is not setup a router
410 case ND_ROUTER_ADVERT:
413 // Neighbor Solicitation is not normally sent by the phone due to its /128 on 6bed4
414 case ND_NEIGHBOR_SOLICIT:
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);
422 // Route Redirect messages are not supported in 6bed4 draft v01
428 public void handle_6to4 (byte pkt [], int pktlen)
433 if ((pkt [0] & 0xf0) != 0x60) {
436 if ((pkt [OFS_IP6_NXTHDR] == IPPROTO_ICMPV6) && (pkt [OFS_ICMP6_TYPE] >= ND_LOWEST) && (pkt [OFS_ICMP6_TYPE] <= ND_HIGHEST)) {
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)) {
443 pkt [OFS_IP6_HOPS]--;
444 if (pkt [OFS_IP6_HOPS] == 0) {
447 handle_6to4_plain_unicast (pkt, pktlen);
451 //TODO: Ignore Multicast for now...
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.
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);
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.
478 * Rick van Rein, October 2012.
483 synchronized (this) {
485 if (downlink_rd != null) {
486 uplen = downlink_rd.read (packet_up);
489 handle_6to4 (packet_up, uplen);
491 } catch (SocketTimeoutException ex) {
493 } catch (IOException ex) {
495 } catch (ArrayIndexOutOfBoundsException ex) {
499 synchronized (this) {
500 if (uplink == null) {
504 uplink.receive (packet_dn);
505 handle_4to6 (packet_dn);
507 } catch (SocketTimeoutException ex) {
509 } catch (IOException ex) {
511 } catch (ArrayIndexOutOfBoundsException ex) {
516 if ((uplen == 0) || nothingdown) {
518 polling_millis <<= 1;
519 if (polling_millis > polling_millis_max) {
520 polling_millis = polling_millis_max;
522 sleep (polling_millis);
523 } catch (InterruptedException ie) {
524 ; // Great, let's move on!
527 polling_millis = polling_millis_min;
533 private class Maintainer extends Thread {
535 /* The time for the next scheduled maintenance: routersol or keepalive.
536 * The milliseconds are always 0 for maintenance tasks.
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;
546 /* Perform the initial Router Solicitation exchange with the public server.
548 public void solicit_router () {
549 if (uplink != null) {
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);
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.
566 public void keepalive () {
567 if ((keepalive_packet != null) && (uplink != null)) {
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 */
578 /* Perform regular maintenance tasks: KeepAlive, and requesting a local address.
580 public void regular_maintenance () {
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;
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");
591 //TODO// syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
594 maintenance_time_cycle = maintenance_time_cycle_max;
596 maintenance_time_cycle = 1;
599 maintenance_time_millis = System.currentTimeMillis () + 1000 * (long) maintenance_time_cycle;
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.
608 public void have_local_address (boolean new_setting) {
609 have_lladdr = new_setting;
611 maintenance_time_cycle = maintenance_time_cycle_max;
612 maintenance_time_millis = System.currentTimeMillis () + 1000 * maintenance_time_cycle;
616 /* Run the regular maintenance thread. This involves sending KeepAlives
617 * and possibly requesting a local address through Router Solicitation.
622 regular_maintenance ();
623 sleep (maintenance_time_millis - System.currentTimeMillis());
625 } catch (InterruptedException ie) {
630 /* Construct the Maintainer thread.
632 public Maintainer (SocketAddress server) {
633 byte payload [] = { };
634 if (server != null) {
636 keepalive_packet = new DatagramPacket (payload, 0, server);
637 } catch (SocketException se) {
638 keepalive_packet = null;
644 public TunnelService () {
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
653 public void change_local_netconfig () {
655 builder = new Builder ();
656 builder.setSession ("6bed4 uplink to IPv6");
657 builder.setMtu (1280);
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);
664 if (new_setup_defaultroute) {
665 Log.i (TAG, "Creating default route through 6bed4 tunnel");
666 builder.addRoute ("::", 0);
668 Log.i (TAG, "Skipping default route through 6bed4 tunnel");
673 } catch (IOException ioe) {
674 ; /* Uncommon: Fallback to garbage collection */
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);
682 // Setup a new neighboring cache, possibly replacing an old one
683 ngbcache = new NeighborCache (uplink, tunserver, local_address);
685 // Now actually construct the tunnel as prepared
686 fio = builder.establish ();
688 uplink.setSoTimeout (1);
689 } catch (SocketException se) {
690 throw new RuntimeException ("UDP socket refuses turbo mode", se);
692 synchronized (this) {
693 downlink_rd = new FileInputStream (fio.getFileDescriptor ());
694 downlink_wr = new FileOutputStream (fio.getFileDescriptor ());
696 synchronized (worker) {
699 maintainer.have_local_address (true);
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;
712 // If the default route should change, change the local network configuration
713 if (new_setup_defaultroute != setup_defaultroute) {
714 change_local_netconfig ();
718 public TunnelService (DatagramSocket uplink_socket, InetSocketAddress publicserver) {
719 synchronized (this) {
720 uplink = uplink_socket;
721 tunserver = publicserver;
725 // Setup a link to the instance of this singular class, for use in the EventMonitor
726 singular_instance = this;
728 // Create the worker thread that will pass information back and forth
729 if (worker == null) {
730 worker = new Worker ();
733 synchronized (worker) {
737 if (maintainer == null) {
738 maintainer = new Maintainer (tunserver);
741 maintainer.have_local_address (false);
745 public static TunnelService theTunnelService () {
746 return singular_instance;
749 synchronized public void teardown () {
751 synchronized (this) {
752 singular_instance = null;
753 if (worker != null) {
757 if (maintainer != null) {
758 maintainer.interrupt ();
761 if (downlink_rd != null) {
762 downlink_rd.close ();
765 if (downlink_wr != null) {
766 downlink_wr.close ();
774 } catch (IOException ioe) {
775 ; /* Uncommon: Fallback to garbage collection */
779 synchronized public void onRevoke () {
785 *** UTILITY FUNCTIONS
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];
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]) {
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]) {
812 /* Retrieve an unsigned 16-bit value from a given index in a byte array and
813 * return it as an integer.
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);
821 /* Fill in the ICMPv6 checksum field in a given IPv6 packet.
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;
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);
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