1 package nl.openfortress.android6bed4;
4 import java.util.Hashtable;
5 import java.util.Queue;
6 import java.util.ArrayDeque;
8 import java.net.DatagramPacket;
9 import java.net.DatagramSocket;
10 import java.net.InetSocketAddress;
11 import java.net.Inet4Address;
12 import java.net.UnknownHostException;
14 import java.io.IOException;
18 /* The Neighbor Cache is modelled in Java, because Android/Java won't
19 * give access to the network stack.
21 * The primary task of the cache is to map IPv6-addresses in byte [16]
22 * format to a destination, which is stored as a Inet4SocketAddress
23 * for direct use with DatagramPacket facilities in Java. A hash is
24 * used to gain fast access to the target object.
26 * To find mappings, the cache will send up to 3 Neighor Solicitation
27 * messages to the direct peer. During this period it will pass through
28 * states ATTEMPT1 to ATTEMPT3, and finally become FAILED. This is a sign
29 * that no further attempts need to be made until the expiration of the
30 * mapping, about 30 seconds after starting it. This process starts
31 * when an unknown peer is approached, or one that is STALE.
33 * If a Neighbor Advertisement comes in, this information is sent here
34 * for processing. The cache entry will usually go to REACHABLE in such
37 * When traffic comes in with the direct address of this peer as its
38 * destination, then the cache is informed; if no mapping in REACHABLE
39 * state exists, then new attempts are started, so as to enable direct
40 * connectivity after the remote peer has opened a hole to us.
42 * The cache maintains a seperate timer queue for each state, and will
43 * act upon the objects if they expire. This makes the queue rather
46 * Before changing any piece of this code, please assure yourself that
47 * you understand each single synchronized section, and convince
48 * yourself that you understand the trick in NeighborQueue.dequeue().
49 * This is not just multitasking code; it may well be the most complex
50 * example of multitasking code that you ever came accross. Do not
51 * change the code unless you are absolutely certain that you have
52 * understood all the invariants that make this code work!
57 /* The states of neighbors in the cache.
59 public final static int ATTEMPT1 = 0; /* Discovery sequence */
60 public final static int ATTEMPT2 = 1;
61 public final static int ATTEMPT3 = 2;
62 public final static int FAILED = 3;
63 public final static int REACHABLE = 4; /* Confirmed in the last ~30s */
64 public final static int STALE = 5; /* Not used for up to ~30s */
65 public final static int NUM_NGB_STATES = 6; /* Count of normal states */
66 public final static int INTRANSIT = 7; /* Traveling between queues */
69 /* The timing spent in the various states of a cache entry. Stable states
70 * take up several seconds, while waiting for a response is faster.
71 * Note the remarks at NeighborQueue.dequeue() for an important constraint;
72 * specifically, STALE is spent in an attempt to recycle an entry as
73 * REACHABLE, so the time spent in STALE must not exceed the time spent
74 * in REACHABLE. This avoids having two entries for one neighbor in a
75 * queue, which would jeapourdise the timeout calculations. Note that
76 * this problem does not arise when an object occurs in multiple queues
77 * at once, because a mismatch with the queue's state means that those
78 * lingering neighbors are ignored as timeout sources.
80 * TODO: I found that Android can be pretty slow in responding over eth0,
81 * possibly worse over UMTS or (shiver) GPRS. So, it may be that the
82 * following timing must be slowed down, *or* that NgbAdv ought to be
83 * accepted in the STALE state.
85 final static int state_timing_millis [] = {
86 50, 50, 50, 30000, /* Discovery -- a few ms between steps */
87 27500, /* STALE -- 30 additional seconds */
88 30000 /* REACHABLE -- 30 seconds of bliss */
91 /* The timer queues for neighbor discovery, each in its own thread */
92 NeighborQueue queues [];
94 /* The neighbor cache contains all elements, regardless of their state.
95 * This is needed to avoid doing double work. If a neighbor is considered
96 * to be available, then it should be in here. Once it stops being of
97 * interest (STALE or FAILED expires) it will be removed.
99 * Note that the key and value are both Neighbor objects. The only part
100 * of the Neighbor dictating equality (and the hashCode) is the 6-byte
101 * key holding the remote peer's IPv4 address and UDP port, so it is
102 * possible to put one object into the Hashtable as both key and value;
103 * and then to lookup an entry with a minimally setup Neighbor as key
104 * to find a more evolved value; the latter will be incorporated into
105 * the procedures of the Neighbor Cache, including the various queues.
107 private Hashtable <Integer, Neighbor> neighbor_cache;
110 /* The public server, used as default return value for queires after
111 * unsettled neighbor cache entries.
113 private InetSocketAddress public_server;
116 /* The socket used as uplink to the rest of the 6bed4-using World.
118 private DatagramSocket uplink;
121 /* The 6bed4 address as a 16-byte array; may also be used as an 8-byte prefix.
123 private byte address_6bed4 [];
125 /* The 6bed4 address as a 6-byte key.
127 private byte address_6bed4_key [];
130 /* Construct a neighbor cache with no entries but a lot of structure */
131 public NeighborCache (DatagramSocket ipv4_uplink, InetSocketAddress pubserver, byte link_address_6bed4 []) {
132 uplink = ipv4_uplink;
133 public_server = pubserver;
134 address_6bed4 = new byte [16];
135 TunnelService.memcp_address (address_6bed4, 0, link_address_6bed4, 0);
136 address_6bed4_key = new byte [6];
137 address2key (address_6bed4, 0, address_6bed4_key);
138 queues = new NeighborQueue [NUM_NGB_STATES];
139 for (int i=0; i < NUM_NGB_STATES; i++) {
140 queues [i] = new NeighborQueue (this, i);
142 neighbor_cache = new Hashtable <Integer,Neighbor> ();
145 /* Remove an expired entry from the neighbor cache.
147 public void remove_from_cache (Neighbor ngb) {
148 byte key [] = new byte [6];
149 socketaddress2key (ngb.target, key);
150 synchronized (neighbor_cache) {
151 neighbor_cache.remove (key);
155 /* Attempt to retrieve an entry from the neighbor cache. The resulting
156 * actions as well as the return value may differ, depending on the
157 * state of the cache entry. This function always returns immediately,
158 * with the tunnel server as a default response for instant connections,
159 * but the cache may asynchronously attempt to find a direct path to the
162 * The "playful" argument is rather special. When set to true, it
163 * indicates that an initial experiment attempting to contact the
164 * neighbor directly is acceptable. This is usually the cae if the
165 * following conditions apply:
166 * 1. The message is resent upon failure
167 * 2. Success can be recognised and reported back here
168 * Setting this flag causes the first (and only the first) result
169 * from lookup_neighbor to point directly to the host; resends will
170 * be sent through the tunnel, while a few neighbor discoveries are
171 * still tried towards the remote peer, until something comes back
172 * directly to acknowledge contact. When something comes back over
173 * a direct route, this should be reported to the neighbor cache
174 * through received_peer_direct_acknowledgement() so it can
175 * learn that future traffic can be directly sent to the peer.
177 * A common case for which this holds is TCP connection setup:
178 * 1. Packets with SYN are sent repeatedly if need be
179 * 2. Packets with ACK stand out in a crowd
180 * So, when TCP packets with SYN are sent, their lookup_neighbor()
181 * invocation can set the playful flag; the first attempt will go
182 * directly to the peer and hopefully initiate direct contact; if
183 * not, future attempts are sent through the tunnel. When the
184 * remote peer accepts, it will return a packet with ACK set over
185 * the direct route, which is enough reason to signal success to
186 * the neighbor cache. Interestingly, the first reply message
187 * from a TCP server will generally hold the SYN flag as well, so
188 * if the remote 6bed4 stack is also playful about TCP, it is
189 * likely to send it back directly even if it received the opening
190 * SYN through the tunnel. This means that even a blocking NAT on
191 * the remote end will cause a new attempt on return. As usual,
192 * an ACK from client to server follows, which asserts to the
193 * server that it can talk directly (because, at that time, the
194 * client has picked up on the directly sent SYN+ACK).
196 * It is possible for TCP to initiate a connection from both ends
197 * at the same time. Effectively this splits the SYN+ACK into
198 * two messages, one with SYN and one with ACK. It may happen
199 * that direct contact is not correctly established if that is
200 * done, but the follow-up neighbor discovery that follow on a
201 * playful SYN that does not return an ACK over the direct route
202 * will establish NAT-passthrough if it is technically possible.
203 * A similar thing occurs when the initial SYN is lost for some
206 public InetSocketAddress lookup_neighbor (byte addr [], int addrofs, boolean playful) {
207 if (!is6bed4 (addr, addrofs)) {
208 return public_server;
210 boolean puppy = false;
212 byte seeker_key [] = new byte [6];
213 address2key (addr, addrofs, seeker_key); //TODO// Only used for seeker creation?
214 int seeker_hash = key2hash (seeker_key);
215 synchronized (neighbor_cache) {
216 ngb = neighbor_cache.get (seeker_hash);
218 ngb = new Neighbor (seeker_key, playful); //TODO// Possibly seeker.clone ()
219 neighbor_cache.put (seeker_hash, ngb);
230 // No usable entry exists, but no new attempts are needed.
231 // Simply return the default router's address.
233 // If the new entry was just inserted into the neighbor
234 // cache for this thread, then enqueue into ATTEMPT1, part
235 // of which is sending a neighbor solicitation. However,
236 // if the request is made by a playful puppy, then skip
237 // sending the Neighbor Discovery upon insertion into the
238 // ATTEMPT1 queue; the 6bed4 stack will instead send the
239 // initial message directly to the peer. To that end, return
240 // the direct neighbor instead of the safe default of the
241 // public server. Note that a repeat of the packet will
242 // no longer count as a puppy, so this is done only once.
244 queues [ATTEMPT1].enqueue (ngb, playful);
249 return public_server;
252 // The neighbor has been reached directly before; assume this
253 // is still possible (continue into REACHABLE) but also send a
254 // neighbor solicitation to keep the lines open.
255 solicit_neighbor (ngb);
258 // The neighbor is known to be available for direct contact.
259 // There is no work to be done to that end.
262 return public_server;
266 /* Send a Neighbor Solicitation to the peer. When this succeeds at
267 * penetrating to the remote peer's 6bed4 stack, then a Neighbor
268 * Advertisement will return, and be reported through the method
269 * NeighborCache.received_peer_direct_acknowledgement() so the
270 * cache can update its state, and support direct sending to the
273 * Even though this kind of message may be sent after failed
274 * playful attempts, it still does not rule out the possibility
275 * of any such attempts returning, so the "playful" flag for
276 * the Neighbor solicited is not changed.
278 public void solicit_neighbor (Neighbor ngb) {
279 byte key [] = new byte [6];
280 socketaddress2key (ngb.target, key);
281 byte ngbsol [] = new byte [72];
285 ngbsol [1] = ngbsol [2] = ngbsol [3] = 0;
286 ngbsol [4] = 0; ngbsol [5] = 4 + 28;
287 ngbsol [6] = TunnelService.IPPROTO_ICMPV6;
288 ngbsol [7] = (byte) 255;
289 TunnelService.memcp_address (ngbsol, 8, address_6bed4, 0);
290 key2ipv6address (key, ngbsol, 24);
293 ngbsol [ofs++] = (byte) TunnelService.ND_NEIGHBOR_SOLICIT;
295 ofs += 2; // Checksum
299 ngbsol [ofs++] = 0x00;
300 key2ipv6address (ngb.key, ngbsol, ofs);
302 // ICMPv6 Option: Source Link-Layer Address
303 ngbsol [ofs++] = 1; // SrcLLAddr
304 ngbsol [ofs++] = 1; // length is 1x 8 bytes
305 for (int i=0; i<6; i++) {
306 ngbsol [ofs++] = address_6bed4_key [i];
308 int csum = TunnelService.checksum_icmpv6 (ngbsol, 0);
309 ngbsol [42] = (byte) ((csum >> 8) & 0xff);
310 ngbsol [43] = (byte) ( csum & 0xff);
312 DatagramPacket pkt = new DatagramPacket (ngbsol, ngbsol.length, ngb.target);
314 } catch (IOException ioe) {
319 /* The TunnelServer reports having received a neighbor advertisement
320 * by calling this function. This may signal the cache that the
321 * corresponding cache entry needs updating.
323 * The "playful" flag is used to indicate that the acknowledgement
324 * arrived as part of a playful attempt in lookup_neighbor(). This
325 * information is used to determine if resends have taken place
326 * through the tunnel server. If such resends were sent, then it
327 * is not safe anymore to assume that a one-to-one exchange has
328 * taken place, and it becomes necessary to rely on the generic
329 * mechanism of Neighbor Discovery to detect the ability for direct
330 * contact between peers.
332 public void received_peer_direct_acknowledgement (byte addr [], int addrofs, boolean playful) {
334 byte key [] = new byte [6];
335 address2key (addr, addrofs, key);
336 int seeker_hash = key2hash (key);
337 synchronized (neighbor_cache) {
338 ngb = neighbor_cache.get (seeker_hash);
343 if (playful && !ngb.playful) {
353 // There is an entry waiting to be resolved.
354 // Apply the lessons learnt to that entry.
355 // Be careful -- another thread may be trying
356 // the same, so we use the dequeue function
357 // that checks again before proceeding.
358 if (queues [nst].dequeue (ngb)) {
360 // Only one thread will continue here, having dequeued the neighbor
361 queues [REACHABLE].enqueue (ngb);
367 // A persistent state has been reached, and any
368 // advertisements coming in have missed their
369 // window of opportunity. Note that this may be
370 // a sign of attempted abuse. So ignore it.
374 // Some thread is already working on this neighbor, so drop
375 // this extra notification.
383 /* The Neighbor class represents individual entries in the NeighborCache.
384 * Each contains an IPv6 address as 16 bytes, its state, and a timeout for
385 * its current state queue.
387 * Most of this class is storage, and is publicly accessible by the
388 * other components in this class/file.
390 * Note that the Neighbor class serves in the hashtable as a key and value;
391 * but as a key, the only input used are the 6 bytes with the remote peer's
392 * IPv4 address and UDP port. This means that another object can easily be
393 * constructed as a search entry, to resolve into the full entry.
395 * TODO: Inner class -- does that mean that the context is copied into this object? That'd be wasteful!
397 static class Neighbor {
399 protected InetSocketAddress target;
400 protected long timeout;
404 /* The hashCode for this object depends solely on the key value.
405 * Integrate all the bits of the key for optimal spreading. The
406 * hashCode may only differ if the equals() output below also
409 public int hashCode () {
410 return key2hash (key);
413 /* The equals output determines that two Neighbor objects are
414 * the same if their key matches; that is, if their remote
415 * IPv4 address and UDP port is the same. It is guaranteed
416 * that the hashCode is the same for two equal objects; if two
417 * objects are unequal then their hashCode is *likely* to differ,
418 * but not guaranteed.
420 public boolean equals (Object oth) {
422 Neighbor other = (Neighbor) oth;
423 for (int i=0; i<5; i++) {
424 if (this.key [i] != ((Neighbor) other).key [i]) {
429 } catch (Throwable thr) {
434 /* Construct a new Neighbor based on its 6-byte key */
435 public Neighbor (byte fromkey [], boolean create_playfully) {
437 for (int i=0; i<6; i++) {
438 key [i] = fromkey [i];
440 state = NeighborCache.ATTEMPT1;
441 target = key2socketaddress (key);
442 playful = create_playfully;
446 /* The NeighborQueue inner class represents a timer queue. The objects in this
447 * queue each have their own timeouts, as specified by state_timing_millis.
448 * New entries are attached to the back, timeouts are picked up at the front.
450 * The queues have understanding of the various states, and how to handle
453 class NeighborQueue extends Thread {
455 private NeighborCache cache;
457 private Queue <Neighbor> queue;
459 public NeighborQueue (NeighborCache owner, int mystate) {
462 queue = new ArrayDeque <Neighbor> ();
469 // First, fetch the head element (or wait for one to appear).
470 // Sample its timeout. Even if the head disappears later on,
471 // any further entries are certain to have a later timeout.
473 synchronized (queue) {
474 Neighbor head = null;
475 while (head == null) {
476 head = queue.peek ();
480 } catch (InterruptedException io) {
485 if (head.state == whoami) {
488 timeout = head.timeout;
492 // Now wait until the timeout expires. This is not done in
493 // a synchronous fashion, so the queue can be changed as
494 // desired by other threads. Note that misplaced items
495 // in the queue will be removed by the code further down
496 // before this thread can find rest.
499 sleep (timeout - System.currentTimeMillis ());
500 } catch (InterruptedException ie) {
505 // Now the time has come to remove the head elements that
506 // have expired. If nothing happened to the head of the
507 // queue, then this one should at least be removed, but
508 // following entries may just as well need removal. Do
509 // this in a queue-synchronous manner to stop other threads
510 // from altering the queue.
511 long now = System.currentTimeMillis ();
512 Queue <Neighbor> removed = new ArrayDeque <Neighbor> ();
513 synchronized (queue) {
514 Neighbor head = queue.peek ();
515 while ((head != null) && ((head.state != whoami) || (head.timeout <= now))) {
516 head = queue.remove ();
517 if (head.state == whoami) {
518 removed.offer (head);
519 } // else, misplaced item that can be silently dropped
520 head = queue.peek ();
524 // We now have a few elements in a temporary removed list;
525 // handle those by inserting them into their next lifecycle
526 // state (possibly meaning, destroy them).
529 /* TODO: Why would we want to remove the remote socket address?
531 // Remove the remote socket address, and continue with
532 // promotion to the next lifecycle state
533 for (Neighbor ngb : removed) {
541 // Promote the neighbor to its next lifecycle state
542 for (Neighbor ngb : removed) {
543 queues [whoami + 1].enqueue (ngb);
549 // Remove the neighbor from the cache
550 for (Neighbor ngb : removed) {
551 cache.remove_from_cache (ngb);
560 /* Insert a new element into the queue. If this is the first
561 * entry, be sure to notify the thread that may be waiting for
562 * something to chew on.
563 * Note that an entry may be enqueued only once, use dequeue
564 * to remove it from a possible former queue.
566 public void enqueue (Neighbor ngb) {
567 enqueue (ngb, false);
570 /* Insert a new element into the queue. If this is the first
571 * entry, be sure to notify the thread that may be waiting for
572 * something to chew on.
573 * Note that an entry may be enqueued only once, use dequeue
574 * to remove it from a possible former queue.
575 * The flag "no_action" requests that the side-effects that
576 * may take place after inserting the element in the queue
579 public void enqueue (Neighbor ngb, boolean no_action) {
581 // Initialise the neighbor for this queue
583 ngb.timeout = System.currentTimeMillis () + NeighborCache.state_timing_millis [whoami];
585 // Insert the Neighbor into the queue
586 synchronized (queue) {
587 boolean wasEmpty = queue.isEmpty ();
594 // Skip the side-effect of insertion is so desired.
599 // If this queue represents waiting for a reply to Neighbor
600 // Solicitation, then solicit the neighbor. This is not
601 // synchronous, because networking does not directly modify
602 // the queue, is slow and is asynchronous in nature anyway.
607 cache.solicit_neighbor (ngb);
614 /* Remove an element from the queue without awaiting timer
615 * expiration. This is only of interest when incoming traffic
616 * follows a direct route, while our previous attempts ran into
617 * FAILED because the remote didn't have its ports open for us yet.
618 * In addition, this may be used when a timer has gone STALE.
619 * Note that expiration timers on the queue can continue to run,
620 * they may simply find that the first element has not timed out
621 * and another cycle is needed for what is then the head.
622 * The state and timeout of the neighbor entry will not be modified
623 * by this call, but future enqueueing may do that nonetheless.
624 * The function returns success; that is, it tells the caller if
625 * the object was indeed found in the queue.
627 * *** Implementation note ***
629 * Dequeueing is expensive, as it involves going through the list
630 * of stored items. Keeping the structures up to date to speedup
631 * this operation is hardly more pleasant. So, we'll use a trick.
632 * We simply won't dequeue. This means that queues can hold items
633 * that should have been removed. As long as objects never cycle
634 * back to this queue before their timers in this queue have
635 * expired, the misplacement of a queue item could be directly
636 * inferred from the state, which does not match the queue's state.
637 * Such misplaced items can be silently removed. Doing this, the
638 * only remaining task for dequeue is to change the state in an
639 * atomic fashion -- that is, without risk of overlapping similar
640 * operations by other threads. As originally intended, there
641 * must be only one thread that receives true when removing an
642 * item from a queue that holds it. The loop that removes the
643 * queue elements from the head also changes to accommodate this
646 public boolean dequeue (Neighbor ngb) {
648 synchronized (queue) {
649 if (ngb.state != whoami) {
652 ngb.state = NeighborCache.INTRANSIT;
666 /* Check if an address is a 6bed4 address.
668 public boolean is6bed4 (byte addr [], int addrofs) {
669 if (TunnelService.memdiff_halfaddr (addr, addrofs, address_6bed4, 0)) {
672 if ((addr [addrofs + 11] != (byte) 0xff) || (addr [addrofs + 12] != (byte) 0xfe)) {
675 //TODO// Possibly require that the port number is even
679 /* Map an address to a 6-byte key in the provided space. Assume that the
680 * address is known to be a 6bed4 address.
682 private static void address2key (byte addr [], int addrofs, byte key []) {
683 key [0] = (byte) (addr [addrofs + 8] ^ 0x02);
684 key [1] = addr [addrofs + 9];
685 key [2] = addr [addrofs + 10];
686 key [3] = addr [addrofs + 13];
687 key [4] = addr [addrofs + 14];
688 key [5] = addr [addrofs + 15];
691 /* Map an InetSocketAddress to a 6-byte key in the provided space.
693 private static void socketaddress2key (InetSocketAddress sa, byte key []) {
694 int port = sa.getPort ();
695 byte addr [] = sa.getAddress ().getAddress ();
696 key [0] = (byte) (port & 0xff);
697 key [1] = (byte) (port >> 8 );
704 /* Map a key to an IPv6 address, following the 6bed4 structures.
706 private void key2ipv6address (byte key [], byte addr [], int addrofs) {
707 for (int i=0; i<8; i++) {
708 addr [addrofs + i] = address_6bed4 [i];
710 addr [addrofs + 8] = (byte) (key [0] ^ 0x02);
711 addr [addrofs + 9] = key [1];
712 addr [addrofs + 10] = key [2];
713 addr [addrofs + 11] = (byte) 0xff;
714 addr [addrofs + 12] = (byte) 0xfe;
715 addr [addrofs + 13] = key [3];
716 addr [addrofs + 14] = key [4];
717 addr [addrofs + 15] = key [5];
720 /* Map a key to an InetSocketAddress.
722 private static InetSocketAddress key2socketaddress (byte key []) {
723 int port = (int) (key [0] & 0xff);
724 port = port | (((int) (key [1] << 8)) & 0xff00);
725 byte v4addr [] = new byte [4];
726 v4addr [0] = key [2];
727 v4addr [1] = key [3];
728 v4addr [2] = key [4];
729 v4addr [3] = key [5];
731 Inet4Address remote = (Inet4Address) Inet4Address.getByAddress (v4addr);
732 return new InetSocketAddress (remote, port);
733 } catch (UnknownHostException uhe) {
734 throw new RuntimeException ("Internet Error", uhe);
738 private static int key2hash (byte key []) {
740 retval = key [0] ^ key [3];
742 retval += key [1] ^ key [4];
744 retval += key [2] ^ key [5];