1 package nl.openfortress.android6bed4;
3 import android.net.VpnService;
4 import android.os.ParcelFileDescriptor;
8 import java.util.Collection;
9 import java.util.Iterator;
12 public final class TunnelService extends VpnService {
14 static private ParcelFileDescriptor fio = null;
15 static private FileInputStream downlink_rd = null;
16 static private FileOutputStream downlink_wr = null;
18 static private InetSocketAddress tunserver = null;
19 static private DatagramSocket uplink = null;
21 static private EventListener netmon = null;
22 static private NeighborCache ngbcache = null;
24 static private boolean new_setup_defaultroute = true;
25 static private boolean setup_defaultroute = false;
26 static byte new_local_address [] = new byte [16];
27 static byte local_address [] = new byte [16];
30 static Maintainer maintainer;
32 //static final byte sender_unknown [] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
33 //static final byte linklocal_all_noders [] = { (byte)0xff,(byte)0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 };
34 //static final byte linklocal_all_routers [] = { (byte)0xff,(byte)0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2 };
36 final static byte IPPROTO_ICMPV6 = 58;
37 final static byte IPPROTO_TCP = 6;
39 final static byte ND_ROUTER_SOLICIT = (byte) 133;
40 final static byte ND_ROUTER_ADVERT = (byte) 134;
41 final static byte ND_NEIGHBOR_SOLICIT = (byte) 135;
42 final static byte ND_NEIGHBOR_ADVERT = (byte) 136;
43 final static byte ND_REDIRECT = (byte) 137;
44 final static byte ND_LOWEST = (byte) 133;
45 final static byte ND_HIGHEST = (byte) 137;
47 final static byte ND_OPT_PREFIX_INFORMATION = 3;
49 final static int OFS_IP6_SRC = 8;
50 final static int OFS_IP6_DST = 24;
51 final static int OFS_IP6_PLEN = 4;
52 final static int OFS_IP6_NXTHDR = 6;
53 final static int OFS_IP6_HOPS = 7;
55 final static int OFS_ICMP6_TYPE = 40 + 0;
56 final static int OFS_ICMP6_CODE = 40 + 1;
57 final static int OFS_ICMP6_CSUM = 40 + 2;
58 final static int OFS_ICMP6_DATA = 40 + 4;
60 final static int OFS_ICMP6_NGBSOL_TARGET = 40 + 8;
61 final static int OFS_ICMP6_NGBADV_TARGET = 40 + 8;
62 final static int OFS_ICMP6_NGBADV_FLAGS = 40 + 4;
64 final static int OFS_TCP6_FLAGS = 13;
65 final static int TCP_FLAG_SYN = 0x02;
66 final static int TCP_FLAG_ACK = 0x01;
68 final static byte router_solicitation [] = {
70 0x60, 0x00, 0x00, 0x00,
71 16 / 256, 16 % 256, IPPROTO_ICMPV6, (byte) 255,
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // unspecd src
73 (byte) 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // all-rtr tgt
74 // ICMPv6 header: router solicitation
75 ND_ROUTER_SOLICIT, 0, 0x7a, (byte) 0xae, // Checksum courtesy of WireShark :)
76 // ICMPv6 body: reserved
78 // ICMPv6 option: source link layer address 0x0001 (end-aligned)
79 0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
82 final static byte router_linklocal_address [] = { (byte)0xfe,(byte)0x80,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
84 private class Worker extends Thread {
86 /* The timing for polling of the two tunnel endpoints.
87 * There is an exponential fallback scheme, where the
88 * tunnel hopes to find the best frequency to watch
89 * for regularly occurring traffic.
91 private int polling_millis = 10;
92 private int polling_millis_min = 1;
93 private int polling_millis_max = 50;
95 public void handle_4to6_nd (byte pkt [], int pktlen, SocketAddress src)
96 throws IOException, SocketException {
97 if (checksum_icmpv6 (pkt, 0) != fetch_net16 (pkt, OFS_ICMP6_CSUM)) {
101 switch (pkt [OFS_ICMP6_TYPE]) {
103 // Handle Router Solicitation by dropping it -- this is a peer, not a router
104 case ND_ROUTER_SOLICIT:
107 // Handle Router Advertisement as an addressing offering -- but validate the sender
108 case ND_ROUTER_ADVERT:
109 if (pktlen < 40 + 16 + 16) {
110 // Too short to contain IPv6, ICMPv6 RtrAdv and Prefix Option
113 if ((pkt [OFS_ICMP6_DATA+1] & 0x80) != 0x00) {
114 // Indecent proposal to use DHCPv6 over 6bed4
117 if (memdiff_addr (pkt, OFS_IP6_SRC, router_linklocal_address, 0)) {
118 // Sender is not 0xfe80::/128
121 if (memdiff_halfaddr (pkt, OFS_IP6_DST, router_linklocal_address, 0)) {
122 // Receiver address is not 0xfe80::/64
125 if ((pkt [OFS_IP6_DST + 11] != (byte) 0xff) || (pkt [OFS_IP6_DST + 12] != (byte) 0xfe)) {
126 // No MAC-based destination address ending in ....:..ff:fe..:....
129 //TODO// Check if offered address looks like a multicast-address (MAC byte 0 is odd)
130 //TODO// Check Secure ND on incoming Router Advertisement?
132 // Having validated the Router Advertisement, process its contents
133 int destprefix_ofs = 0;
134 int rdofs = OFS_ICMP6_DATA + 12;
135 //TODO:+4_WRONG?// while (rdofs <= ntohs (v4v6plen) + 4) { ... }
136 while (rdofs + 4 < pktlen) {
137 if (pkt [rdofs + 1] == 0) {
138 return; /* zero length option */
140 if (pkt [rdofs + 0] != ND_OPT_PREFIX_INFORMATION) {
141 /* skip to next option */
142 } else if (pkt [rdofs + 1] != 4) {
143 return; /* bad length field */
144 } else if (rdofs + (pkt [rdofs + 1] << 3) > pktlen + 4) {
145 return; /* out of packet length */
146 } else if ((pkt [rdofs + 3] & (byte) 0xc0) != (byte) 0xc0) {
147 /* no on-link autoconfig prefix */
148 } else if (pkt [rdofs + 2] != 64) {
151 destprefix_ofs = rdofs + 16;
153 rdofs += (pkt [rdofs + 1] << 3);
155 if (destprefix_ofs > 0) {
156 for (int i=0; i<8; i++) {
157 new_local_address [0 + i] = pkt [destprefix_ofs + i];
158 new_local_address [8 + i] = pkt [OFS_IP6_DST + 8 + i];
160 //TODO// syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
161 change_local_netconfig (); //TODO// parameters?
165 // Neighbor Solicitation is an attempt to reach us peer-to-peer, and should be responded to
166 case ND_NEIGHBOR_SOLICIT:
168 // Too short to make sense
171 if (memdiff_addr (pkt, OFS_ICMP6_NGBSOL_TARGET, local_address, 0)) {
172 // Neighbor Solicitation not aimed at me
175 if (!ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) {
176 // Source is not a 6bed4 address
180 // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
181 // Not checked here: LLaddr in NgbSol -- simply send back to IPv6 src address
183 memcp_address (pkt, OFS_IP6_DST, pkt, OFS_IP6_SRC);
184 memcp_address (pkt, OFS_IP6_SRC, local_address, 0);
185 pkt [OFS_ICMP6_TYPE] = ND_NEIGHBOR_ADVERT;
186 pkt [OFS_IP6_PLEN + 0] = 0;
187 pkt [OFS_IP6_PLEN + 1] = 8 + 16;
188 pkt [OFS_ICMP6_NGBADV_FLAGS] = 0x60; // Solicited, Override
189 // Assume that OFS_ICMP6_NGBADV_TARGET == OFS_ICMP6_NGBSOL_TARGET
190 int csum = TunnelService.checksum_icmpv6 (pkt, 0);
191 pkt [OFS_ICMP6_CSUM + 0] = (byte) (csum >> 8 );
192 pkt [OFS_ICMP6_CSUM + 1] = (byte) (csum & 0xff);
193 DatagramPacket replypkt = new DatagramPacket (pkt, 0, 40 + 8 + 16, src);
194 uplink.send (replypkt);
197 // TODO:OLD Replicate the message over the tunnel link
199 // We should attach a Source Link-Layer Address, but
200 // we cannot automatically trust the one provided remotely.
201 // Also, we want to detect if routes differ, and handle it.
203 // 0. if no entry in the ngb.cache
204 // then use 6bed4 server in ND, initiate ngb.sol to src.ll
205 // impl: use 6bed4-server lladdr, set highest metric
206 // 1. if metric (ngb.cache) < metric (src.ll)
207 // then retain ngb.cache, send Redirect to source
208 // 2. if metric (ngb.cache) > metric (src.ll)
209 // then retain ngb.cache, initiate ngb.sol to src.ll
210 // 3. if metric (ngb.cache) == metric (src.ll)
211 // then retain ngb.cache
213 //TODO// Handle ND_NEIGHBOR_SOLICIT (handle_4to6_nd)
216 // Neighbor Advertisement may be in response to our peer-to-peer search
217 case ND_NEIGHBOR_ADVERT:
219 // Process Neighbor Advertisement coming in over 6bed4
220 // First, make sure it is against an item in the ndqueue
222 // Validate the Neighbor Advertisement
224 // Packet too small to hold ICMPv6 Neighbor Advertisement
227 if ((pkt [OFS_ICMP6_TYPE] != ND_NEIGHBOR_ADVERT) || (pkt [OFS_ICMP6_CODE] != 0)) {
228 // ICMPv6 Type or Code is wrong
231 if ((!ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) || (!ngbcache.is6bed4 (pkt, OFS_IP6_DST))) {
232 // Source or Destination IPv6 address is not a 6bed4 address
235 if (memdiff_addr (pkt, OFS_IP6_SRC, pkt, OFS_ICMP6_NGBADV_TARGET)) {
236 // NgbAdv's Target Address does not match IPv6 source
240 // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
242 ngbcache.received_neighbor_direct_acknowledgement (pkt, OFS_ICMP6_NGBADV_TARGET);
245 // Route Redirect messages are not supported in 6bed4 draft v01
251 /* Forward an IPv6 packet, wrapped into UDP and IPv4 in the 6bed4
252 * way, as a pure IPv6 packet over the tunnel interface. This is
253 * normally a simple copying operation. One exception exists for
254 * TCP ACK packets; these may be in response to a "playful" TCP SYN
255 * packet that was sent directly to the IPv4 recipient. This is a
256 * piggyback ride of the opportunistic connection efforts on the
257 * 3-way handshake for TCP, without a need to modify the packets!
258 * The only thing needed to make that work is to report success
259 * back to the Neighbor Cache, in cases when TCP ACK comes back in
260 * directly from the remote peer.
262 * Note that nothing is stopping an ACK packet that is meaningful
263 * to us from also being a SYN packet that is meaningful to the
264 * remote peer. We will simply do our thing and forward any ACK
265 * to the most direct route we can imagine -- which may well be
266 * the sender, _especially_ since we opened our 6bed4 port to the
267 * remote peer when sending our playful initial TCP packet.
269 * Observing the traffic on the network, this may well look like
270 * magic! All you see is plain TCP traffic crossing over directly
271 * if it is possible --and bouncing one or two packets through the
272 * tunnel otherwise-- and especially in the case where it can work
273 * directly it will be a surprise. Servers are therefore strongly
274 * encouraged to setup port forwarding for their 6bed4 addresses,
275 * or just open a hole in full cone NAT/firewall setups. This will
276 * mean zero delay and zero bypasses for 6bed4 on the majority of
277 * TCP connection initiations between 6bed4 peers!
279 public void handle_4to6_plain (byte pkt [], int pktlen)
281 if (downlink_wr != null) {
282 downlink_wr.write (pkt, 0, pktlen);
284 boolean tcpack = (pkt [OFS_IP6_NXTHDR] == IPPROTO_TCP) && ((pkt [OFS_TCP6_FLAGS] & TCP_FLAG_ACK) != 0x00);
285 //TODO// report back if this is a success, that is, a tcpack packet
288 public void handle_4to6 (DatagramPacket datagram)
290 byte pkt [] = datagram.getData ();
291 int pktlen = datagram.getLength ();
296 if ((pkt [0] & (byte) 0xf0) != 0x60) {
299 validate_originator (pkt, (InetSocketAddress) datagram.getSocketAddress ());
300 if ((pkt [OFS_IP6_NXTHDR] == IPPROTO_ICMPV6) && (pkt [OFS_ICMP6_TYPE] >= ND_LOWEST) && (pkt [OFS_ICMP6_TYPE] <= ND_HIGHEST)) {
302 // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
303 handle_4to6_nd (pkt, pktlen, datagram.getSocketAddress ());
306 // Plain Unicast or Plain Multicast (both may enter)
307 handle_4to6_plain (pkt, pktlen);
311 public void validate_originator (byte pkt [], InetSocketAddress originator)
313 /* TODO: validate originator address
314 if (tunserver.equals (originator)) {
317 if (memdiff_halfaddr (pkt, OFS_IP6_SRC, router_linklocal, 0) && ((local_address == null) || memdiff_halfaddr (pkt, OFS_IP6_SRC, local_address, 8))) {
318 throw new IOException ("Incoming 6bed4 uses bad /64 prefix");
320 int port = (pkt [OFS_IP6_SRC + 8] ^ 0x02) & 0xff;
321 port = (port | (pkt [OFS_IP6_SRC + 9] << 8) & 0xffff;
322 if (originator.getPort () != port) {
323 throw new IOException ("Incoming 6bed4 uses ")
328 /* This routine passes IPv6 traffic from the tunnel interface on
329 * to the 6bed4 interface where it is wrapped into UDP and IPv4.
330 * The only concern for this is where to send it to -- should it
331 * be sent to the tunnel server, or directly to the peer? The
332 * Neighbor Cache is consulted for advise.
334 * A special flag exists to modify the behaviour of the response
335 * to this inquiry. This flag is used to signal that a first
336 * packet might be tried directly, which should be harmless if
337 * it fails and otherwise lead to optimistic connections if:
338 * 1. the packet will repeat upon failure, and
339 * 2. explicit acknowledgement can be reported to the cache
340 * This is the case with TCP connection setup; during a SYN,
341 * it is possible to be playful and try to send the first
342 * packet directly. A TCP ACK that returns directly from the
343 * sender indicates that return traffic is possible, which is
344 * then used to update the Neighbor Cache with positivism on
347 public void handle_6to4_plain_unicast (byte pkt [], int pktlen)
349 InetSocketAddress target;
350 if ((ngbcache != null) && ngbcache.is6bed4 (pkt, 24)) {
351 boolean tcpsyn = (pkt [OFS_IP6_NXTHDR] == IPPROTO_TCP) && ((pkt [OFS_TCP6_FLAGS] & TCP_FLAG_SYN) != 0x00);
352 target = ngbcache.lookup_neighbor (pkt, 24, false); //TODO// use tcpsyn as "playful" parameter
356 uplink.send (new DatagramPacket (pkt, pktlen, target));
359 public void handle_6to4_nd (byte pkt [], int pktlen)
361 switch (pkt [OFS_ICMP6_TYPE]) {
363 // Handle Router Solicitation by answering it with the local configuration
364 case ND_ROUTER_SOLICIT:
365 int ofs = OFS_ICMP6_TYPE;
366 pkt [ofs++] = ND_ROUTER_ADVERT; // type
367 pkt [ofs++] = 0; // code
368 ofs += 2; // checksum
369 pkt [ofs++] = 0; // hop limit -- unspecified
370 pkt [ofs++] = 0x18; // M=0, O=0, H=0, Prf=11=Low, Reserved=0
371 pkt [ofs++] = setup_defaultroute? (byte) 0xff: 0x00; // Lifetime
372 pkt [ofs++] = setup_defaultroute? (byte) 0xff: 0x00; // (cont)
373 for (int i=0; i<8; i++) {
374 pkt [ofs++] = 0; // Reachable time, Retrans timer
376 pkt [ofs-6] = (byte) 0x80; // Reachable time := 32s
377 pkt [ofs-2] = 0x01; // Retrans timer := 0.25s
378 // Start of Prefix Option
379 pkt [ofs++] = ND_OPT_PREFIX_INFORMATION;
380 pkt [ofs++] = 4; // Option length = 4 * 8 bytes
381 pkt [ofs++] = (byte) 128; // Announce a /64 prefix (TODO: Temporarily /128)
382 pkt [ofs++] = (byte) 0x80; // Link-local, No autoconfig, tunnel does the work
383 for (int i=0; i<8; i++) {
384 pkt [ofs++] = (byte) 0xff; // Valid / Preferred Lifetime: Infinite
386 for (int i=0; i<4; i++) {
387 pkt [ofs++] = 0; // Reserved
389 memcp_address (pkt, ofs, local_address, 0);
391 // End of Prefix Option
392 memcp_address (pkt, OFS_IP6_DST, pkt, OFS_IP6_SRC); // dst:=src
393 memcp_address (pkt, OFS_IP6_SRC, local_address, 0);
394 //TODO// Send packet back to IPv6 downlink
397 // Handle Router Advertisement by dropping it -- Android is not setup a router
398 case ND_ROUTER_ADVERT:
401 // Neighbor Solicitation is not normally sent by the phone due to its /128 on 6bed4
402 case ND_NEIGHBOR_SOLICIT:
405 // Neighbor Advertisement is a response to a peer, and should be relayed
406 case ND_NEIGHBOR_ADVERT:
407 //TODO// Possibly arrange the peer's receiving address
408 handle_6to4_plain_unicast (pkt, pktlen);
410 // Route Redirect messages are not supported in 6bed4 draft v01
416 public void handle_6to4 (byte pkt [], int pktlen)
421 if ((pkt [0] & 0xf0) != 0x60) {
424 if ((pkt [OFS_IP6_NXTHDR] == IPPROTO_ICMPV6) && (pkt [OFS_ICMP6_TYPE] >= ND_LOWEST) && (pkt [OFS_ICMP6_TYPE] <= ND_HIGHEST)) {
426 // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
427 handle_6to4_nd (pkt, pktlen);
428 } else if ((pkt [OFS_IP6_DST+0] != 0xff) && ((pkt [OFS_IP6_DST+8] & 0x01) == 0x00)) {
431 pkt [OFS_IP6_HOPS]--;
432 if (pkt [OFS_IP6_HOPS] == 0) {
435 handle_6to4_plain_unicast (pkt, pktlen);
439 //TODO: Ignore Multicast for now...
443 /* The worker thread pulls data from both tunnel ends, and passes it on to the other.
444 * Packets dealing with Neighbor Discovery are treated specially by this thread.
448 // Pump data back and forth, interpreting Neighbor Discovery locally
449 byte packet_up [] = new byte [1280];
450 DatagramPacket packet_dn = new DatagramPacket (new byte [1280+28], 1280+28);
452 * I am aware that the following code is less than optimal!
453 * For some reason, the VpnService only returns a non-blocking
454 * file descriptor that cannot subsequently be altered to be
455 * blocking (for two parallel Threads) or used in a select()
456 * style. So polling is required.
457 * The uplink is blocking, just to continue the confusion.
458 * A slight upgrade of performance might be achieved by setting
459 * up a separate Thread to handle that, but given that polling
460 * is needed anyway, I decided not to bother to get the best out
461 * of what seems to me like a bad design choice in VpnService.
462 * If I have missed a way to turn the VpnService interface into
463 * one that works in select() or even just a blocking version
464 * than *please* let me know! IMHO, polling is a rude technique.
466 * Rick van Rein, October 2012.
471 synchronized (this) {
473 if (downlink_rd != null) {
474 uplen = downlink_rd.read (packet_up);
477 handle_6to4 (packet_up, uplen);
479 } catch (SocketTimeoutException ex) {
481 } catch (IOException ex) {
483 } catch (ArrayIndexOutOfBoundsException ex) {
487 synchronized (this) {
488 if (uplink == null) {
492 uplink.receive (packet_dn);
493 handle_4to6 (packet_dn);
495 } catch (SocketTimeoutException ex) {
497 } catch (IOException ex) {
499 } catch (ArrayIndexOutOfBoundsException ex) {
504 if ((uplen == 0) || nothingdown) {
506 polling_millis <<= 1;
507 if (polling_millis > polling_millis_max) {
508 polling_millis = polling_millis_max;
510 sleep (polling_millis);
511 } catch (InterruptedException ie) {
512 ; // Great, let's move on!
515 polling_millis = polling_millis_min;
521 private class Maintainer extends Thread {
523 /* The time for the next scheduled maintenance: routersol or keepalive.
524 * The milliseconds are always 0 for maintenance tasks.
526 private long maintenance_time_millis;
527 private int maintenance_time_cycle = 0;
528 private int maintenance_time_cycle_max = 30;
529 private boolean have_lladdr = false;
530 private int keepalive_period = 30;
531 private int keepalive_ttl = -1;
532 private DatagramPacket keepalive_packet = null;
534 /* Perform the initial Router Solicitation exchange with the public server.
536 public void solicit_router () {
537 if (uplink != null) {
539 DatagramPacket rtrsol = new DatagramPacket (router_solicitation, router_solicitation.length, tunserver);
540 uplink.send (rtrsol);
541 } catch (IOException ioe) {
542 throw new RuntimeException ("Network failure", ioe);
547 /* Send a KeepAlive packet to the public server.
548 * Note, ideally, we would set a low-enough TTL to never reach it;
549 * after all, the only goal is to open /local/ firewalls and NAT.
550 * Java however, is not capable of setting TTL on unicast sockets.
552 public void keepalive () {
553 if ((keepalive_packet != null) && (uplink != null)) {
555 uplink.send (keepalive_packet);
556 } catch (IOException ioe) {
557 ; /* Better luck next time? */
562 /* Perform regular maintenance tasks: KeepAlive, and requesting a local address.
564 public void regular_maintenance () {
567 maintenance_time_cycle <<= 1;
568 maintenance_time_cycle += 1;
569 if (maintenance_time_cycle > maintenance_time_cycle_max) {
570 maintenance_time_cycle = maintenance_time_cycle_max;
572 //TODO// syslog (LOG_INFO, "Sent Router Advertisement to Public 6bed4 Service, next attempt in %d seconds\n", maintenance_time_cycle);
574 //TODO// syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
576 maintenance_time_cycle = maintenance_time_cycle_max;
578 maintenance_time_millis = System.currentTimeMillis () + 1000 * (long) maintenance_time_cycle;
581 /* Tell the maintenance routine whether a local address has been setup.
582 * Until this is called, the maintenance will focus on getting one through
583 * regular Router Solicitation messages. It is possible to revert to this
584 * behaviour by setting the flag to false; this can be useful in case of
585 * changes, for instance resulting from an IPv4 address change.
587 public void have_local_address (boolean new_setting) {
588 have_lladdr = new_setting;
590 maintenance_time_cycle = maintenance_time_cycle_max;
591 maintenance_time_millis = System.currentTimeMillis () + 1000 * maintenance_time_cycle;
595 /* Run the regular maintenance thread. This involves sending KeepAlives
596 * and possibly requesting a local address through Router Solicitation.
601 regular_maintenance ();
602 sleep (maintenance_time_millis - System.currentTimeMillis());
604 } catch (InterruptedException ie) {
609 /* Construct the Maintainer thread.
611 public Maintainer (SocketAddress server) {
612 byte payload [] = { };
613 if (server != null) {
615 keepalive_packet = new DatagramPacket (payload, 0, server);
616 } catch (SocketException se) {
617 keepalive_packet = null;
623 public TunnelService () {
626 /* Notify the tunnel of a new local address. This is called after
627 * updating the values of the following variables which are
628 * private, but accessible to the maintenance cycle:
629 * - byte new_local_address [16]
630 * - boolean new_setup_defaultroute
632 public void change_local_netconfig () {
634 builder = new Builder ();
635 builder.setSession ("6bed4 uplink to IPv6");
636 builder.setMtu (1280);
638 //TODO// For now, setup a /128 address to avoid v01-style Neighbor Discovery
639 builder.addAddress (Inet6Address.getByAddress (new_local_address), 128);
640 } catch (UnknownHostException uhe) {
641 throw new RuntimeException ("6bed4 address rejected", uhe);
643 if (new_setup_defaultroute) {
644 builder.addRoute ("::", 0);
649 } catch (IOException ioe) {
650 ; /* Uncommon: Fallback to garbage collection */
654 // Setup the address information for the current tunnel
655 setup_defaultroute = new_setup_defaultroute;
656 memcp_address (local_address, 0, new_local_address, 0);
658 // Setup a new neighboring cache, possibly replacing an old one
659 ngbcache = new NeighborCache (uplink, tunserver, local_address);
661 // Now actually construct the tunnel as prepared
662 fio = builder.establish ();
664 uplink.setSoTimeout (1);
665 } catch (SocketException se) {
666 throw new RuntimeException ("UDP socket refuses turbo mode", se);
668 synchronized (this) {
669 downlink_rd = new FileInputStream (fio.getFileDescriptor ());
670 downlink_wr = new FileOutputStream (fio.getFileDescriptor ());
672 synchronized (worker) {
675 maintainer.have_local_address (true);
678 public void notify_ipv6_addresses (Collection <byte []> addresslist) {
679 // See if the IPv6 address list causes a change to the wish for a default route through 6bed4
680 Iterator <byte []> adr_iter = addresslist.iterator();
681 new_setup_defaultroute = true;
682 while (adr_iter.hasNext ()) {
683 byte addr [] = adr_iter.next ();
684 if ((addr.length == 16) && memdiff_addr (addr, 0, local_address, 0)) {
685 new_setup_defaultroute = false;
688 // If the default route should change, change the local network configuration
689 if (new_setup_defaultroute != setup_defaultroute) {
690 change_local_netconfig ();
694 public TunnelService (DatagramSocket uplink_socket, InetSocketAddress publicserver) {
695 synchronized (this) {
696 uplink = uplink_socket;
697 tunserver = publicserver;
701 // Setup a network monitor that will watch for broadcast events with network changes
702 if (netmon == null) {
703 netmon = new EventListener ();
705 netmon.register_network_monitor (this);
707 // Create the worker thread that will pass information back and forth
708 if (worker == null) {
709 worker = new Worker ();
712 synchronized (worker) {
716 if (maintainer == null) {
717 maintainer = new Maintainer (tunserver);
720 maintainer.have_local_address (false);
724 synchronized public void teardown () {
726 synchronized (this) {
727 if (netmon != null) {
728 netmon.unregister_network_monitor ();
731 if (worker != null) {
735 if (maintainer != null) {
736 maintainer.interrupt ();
739 if (downlink_rd != null) {
740 downlink_rd.close ();
743 if (downlink_wr != null) {
744 downlink_wr.close ();
752 } catch (IOException ioe) {
753 ; /* Uncommon: Fallback to garbage collection */
757 synchronized public void onRevoke () {
763 *** UTILITY FUNCTIONS
766 public static void memcp_address (byte tgt [], int tgtofs, byte src [], int srcofs) {
767 for (int i=0; i<16; i++) {
768 tgt [tgtofs+i] = src [srcofs+i];
772 public static boolean memdiff_addr (byte one[], int oneofs, byte oth[], int othofs) {
773 for (int i=0; i<16; i++) {
774 if (one [oneofs + i] != oth [othofs + i]) {
781 public static boolean memdiff_halfaddr (byte one[], int oneofs, byte oth[], int othofs) {
782 for (int i=0; i<8; i++) {
783 if (one [oneofs + i] != oth [othofs + i]) {
790 /* Retrieve an unsigned 16-bit value from a given index in a byte array and
791 * return it as an integer.
793 public static int fetch_net16 (byte pkt [], int ofs16) {
794 int retval = ((int) pkt [ofs16]) << 8 & 0xff00;
795 retval = retval + (((int) pkt [ofs16+1]) & 0xff);
799 /* Fill in the ICMPv6 checksum field in a given IPv6 packet.
801 public static int checksum_icmpv6 (byte pkt [], int pktofs) {
802 int plen = fetch_net16 (pkt, pktofs + OFS_IP6_PLEN);
803 int nxth = ((int) pkt [pktofs + 6]) & 0xff;
804 // Pseudo header is IPv6 src/dst (included with packet) and plen/nxth and zeroes:
805 int csum = plen + nxth;
807 for (i=8; i < 40+plen; i += 2) {
808 if (i != OFS_ICMP6_CSUM) {
809 // Skip current checksum value
810 csum += fetch_net16 (pkt, pktofs + i);
813 // No need to treat a trailing single byte: ICMPv6 has no odd packet lengths
814 csum = (csum & 0xffff) + (csum >> 16);
815 csum = (csum & 0xffff) + (csum >> 16);
816 csum = csum ^ 0xffff; // 1's complement limited to 16 bits