6b539e1524af522d65b3bdf77a6d6a6ba9b8aed6
[android6bed4] / src / nl / openfortress / android6bed4 / TunnelService.java
1 package nl.openfortress.android6bed4;
2
3 import android.net.VpnService;
4 import android.os.ParcelFileDescriptor;
5
6 import java.net.*;
7 import java.io.*;
8 import java.util.Collection;
9 import java.util.Iterator;
10
11
12 public final class TunnelService extends VpnService {
13
14         static private ParcelFileDescriptor fio = null;
15         static private FileInputStream  downlink_rd = null;
16         static private FileOutputStream downlink_wr = null;
17
18         static private InetSocketAddress tunserver = null;
19         static private DatagramSocket uplink = null;
20
21         static private EventListener netmon = null;
22         static private NeighborCache ngbcache = null;
23         
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];
28         
29         static Worker worker;
30         static Maintainer maintainer;
31         
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 };
35         
36         final static byte IPPROTO_ICMPV6 = 58;
37         final static byte IPPROTO_TCP = 6;
38         
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;
46         
47         final static byte ND_OPT_PREFIX_INFORMATION = 3;
48         
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;
54         
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;
59         
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;
63                 
64         final static int OFS_TCP6_FLAGS     = 13;
65         final static int TCP_FLAG_SYN           = 0x02;
66         final static int TCP_FLAG_ACK           = 0x01;
67         
68         final static byte router_solicitation [] = {
69                         // IPv6 header
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
77                         0, 0, 0, 0,
78                         // ICMPv6 option: source link layer address 0x0001 (end-aligned)
79                         0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
80                 };
81
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 };
83         
84         private class Worker extends Thread {
85
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.
90                  */
91                 private int polling_millis     = 10;
92                 private int polling_millis_min =  1;
93                 private int polling_millis_max = 50;
94                 
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)) {
98                                 // Checksum is off
99                                 return;
100                         }
101                         switch (pkt [OFS_ICMP6_TYPE]) {
102                         //
103                         // Handle Router Solicitation by dropping it -- this is a peer, not a router
104                         case ND_ROUTER_SOLICIT:
105                                 return;
106                         //
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
111                                         return;
112                                 }
113                                 if ((pkt [OFS_ICMP6_DATA+1] & 0x80) != 0x00) {
114                                         // Indecent proposal to use DHCPv6 over 6bed4
115                                         return;
116                                 }
117                                 if (memdiff_addr (pkt, OFS_IP6_SRC, router_linklocal_address, 0)) {
118                                         // Sender is not 0xfe80::/128
119                                         return;
120                                 }
121                                 if (memdiff_halfaddr (pkt, OFS_IP6_DST, router_linklocal_address, 0)) {
122                                         // Receiver address is not 0xfe80::/64
123                                         return;
124                                 }
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..:....
127                                         return;
128                                 }
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?
131                                 //
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 */
139                                         }
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) {
149                                                 return;
150                                         } else {
151                                                 destprefix_ofs = rdofs + 16;
152                                         }
153                                         rdofs += (pkt [rdofs + 1] << 3);
154                                 }
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]; 
159                                         }
160                                         //TODO// syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
161                                         change_local_netconfig ();  //TODO// parameters?
162                                 }
163                                 return;
164                         //
165                         // Neighbor Solicitation is an attempt to reach us peer-to-peer, and should be responded to
166                         case ND_NEIGHBOR_SOLICIT:
167                                 if (pktlen < 24) {
168                                         // Too short to make sense
169                                         return;
170                                 }
171                                 if (memdiff_addr (pkt, OFS_ICMP6_NGBSOL_TARGET, local_address, 0)) {
172                                         // Neighbor Solicitation not aimed at me
173                                         return;
174                                 }
175                                 if (!ngbcache.is6bed4 (pkt, OFS_IP6_SRC)) {
176                                         // Source is not a 6bed4 address
177                                         return;
178                                 }
179                                 //
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
182                                 //
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);
195
196                                 //
197                                 // TODO:OLD Replicate the message over the tunnel link
198                                 //
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.
202                                 //
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
212                                 //
213                                 //TODO// Handle ND_NEIGHBOR_SOLICIT (handle_4to6_nd)
214                                 return;
215                         //
216                         // Neighbor Advertisement may be in response to our peer-to-peer search
217                         case ND_NEIGHBOR_ADVERT:
218                 //
219                 // Process Neighbor Advertisement coming in over 6bed4
220                 // First, make sure it is against an item in the ndqueue
221                                 //
222                                 // Validate the Neighbor Advertisement
223                                 if (pktlen < 64) {
224                                         // Packet too small to hold ICMPv6 Neighbor Advertisement
225                                         return;
226                                 }
227                                 if ((pkt [OFS_ICMP6_TYPE] != ND_NEIGHBOR_ADVERT) || (pkt [OFS_ICMP6_CODE] != 0)) {
228                                         // ICMPv6 Type or Code is wrong
229                                         return;
230                                 }
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
233                                         return;
234                                 }
235                                 if (memdiff_addr (pkt, OFS_IP6_SRC, pkt, OFS_ICMP6_NGBADV_TARGET)) {
236                                         // NgbAdv's Target Address does not match IPv6 source
237                                         return;
238                                 }
239                                 //
240                                 // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
241                                 //
242                                 ngbcache.received_neighbor_direct_acknowledgement (pkt, OFS_ICMP6_NGBADV_TARGET);
243                                 return;
244                         //
245                         // Route Redirect messages are not supported in 6bed4 draft v01
246                         case ND_REDIRECT:
247                                 return;
248                         }
249                 }
250
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.
261                  * 
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.
268                  * 
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!
278                  */
279                 public void handle_4to6_plain (byte pkt [], int pktlen)
280                 throws IOException {
281                         if (downlink_wr != null) {
282                                 downlink_wr.write (pkt, 0, pktlen);
283                         }
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
286                 }
287                 
288                 public void handle_4to6 (DatagramPacket datagram)
289                 throws IOException {
290                         byte pkt [] = datagram.getData ();
291                         int pktlen = datagram.getLength ();
292
293                         if (pktlen < 40) {
294                                 return;
295                         }
296                         if ((pkt [0] & (byte) 0xf0) != 0x60) {
297                                 return;
298                         }
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)) {
301                                 //
302                                 // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
303                                 handle_4to6_nd (pkt, pktlen, datagram.getSocketAddress ());
304                         } else {
305                                 //
306                                 // Plain Unicast or Plain Multicast (both may enter)
307                                 handle_4to6_plain (pkt, pktlen);
308                         }
309                 }
310                 
311                 public void validate_originator (byte pkt [], InetSocketAddress originator)
312                 throws IOException {
313 /* TODO: validate originator address
314                         if (tunserver.equals (originator)) {
315                                 return;
316                         }
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");
319                         }
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 ")
324                         }
325 */
326                 }
327
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.
333                  * 
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
345                  * the return route.
346                  */
347                 public void handle_6to4_plain_unicast (byte pkt [], int pktlen)
348                 throws IOException {
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
353                         } else {
354                                 target = tunserver;
355                         }
356                         uplink.send (new DatagramPacket (pkt, pktlen, target));
357                 }
358                 
359                 public void handle_6to4_nd (byte pkt [], int pktlen)
360                 throws IOException {
361                         switch (pkt [OFS_ICMP6_TYPE]) {
362                         //
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
375                                 }
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
385                                 }
386                                 for (int i=0; i<4; i++) {
387                                         pkt [ofs++] = 0;                                // Reserved
388                                 }
389                                 memcp_address (pkt, ofs, local_address, 0);
390                                 ofs += 16;
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
395                                 return;
396                         //
397                         // Handle Router Advertisement by dropping it -- Android is not setup a router
398                         case ND_ROUTER_ADVERT:
399                                 return;
400                         //
401                         // Neighbor Solicitation is not normally sent by the phone due to its /128 on 6bed4
402                         case ND_NEIGHBOR_SOLICIT:
403                                 return;
404                         //
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);
409                                 return;
410                         // Route Redirect messages are not supported in 6bed4 draft v01
411                         case ND_REDIRECT:
412                                 return;
413                         }
414                 }
415
416                 public void handle_6to4 (byte pkt [], int pktlen)
417                 throws IOException {
418                         if (pktlen < 41) {
419                                 return;
420                         }
421                         if ((pkt [0] & 0xf0) != 0x60) {
422                                 return;
423                         }
424                         if ((pkt [OFS_IP6_NXTHDR] == IPPROTO_ICMPV6) && (pkt [OFS_ICMP6_TYPE] >= ND_LOWEST) && (pkt [OFS_ICMP6_TYPE] <= ND_HIGHEST)) {
425                                 //
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)) {
429                                 //
430                                 // Plain Unicast
431                                 pkt [OFS_IP6_HOPS]--;
432                                 if (pkt [OFS_IP6_HOPS] == 0) {
433                                         return;
434                                 }
435                                 handle_6to4_plain_unicast (pkt, pktlen);
436                         } else {
437                                 //
438                                 // Plain Multicast
439                                 //TODO: Ignore Multicast for now...
440                         }
441                 }
442
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. 
445                  */
446                 public void run () {
447                         //
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);
451                         /* 
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.
465                          * 
466                          * Rick van Rein, October 2012.
467                          */
468                         while (true) {
469                                 int uplen = 0;
470                                 boolean nothingdown;
471                                 synchronized (this) {
472                                         try {
473                                                 if (downlink_rd != null) {
474                                                         uplen = downlink_rd.read (packet_up);
475                                                 }
476                                                 if (uplen > 0) {
477                                                         handle_6to4 (packet_up, uplen);
478                                                 }
479                                         } catch (SocketTimeoutException ex) {
480                                                 ;
481                                         } catch (IOException ex) {
482                                                 ;
483                                         } catch (ArrayIndexOutOfBoundsException ex) {
484                                                 ;
485                                         }
486                                 }
487                                 synchronized (this) {
488                                         if (uplink == null) {
489                                                 nothingdown = true;
490                                         } else {
491                                                 try {
492                                                         uplink.receive (packet_dn);
493                                                         handle_4to6 (packet_dn);
494                                                         nothingdown = false;
495                                                 } catch (SocketTimeoutException ex) {
496                                                         nothingdown = true;
497                                                 } catch (IOException ex) {
498                                                         nothingdown = true;
499                                                 } catch (ArrayIndexOutOfBoundsException ex) {
500                                                         nothingdown = true;
501                                                 }
502                                         }
503                                 }
504                                 if ((uplen == 0) || nothingdown) {
505                                         try {
506                                                 polling_millis <<= 1;
507                                                 if (polling_millis > polling_millis_max) {
508                                                         polling_millis = polling_millis_max;
509                                                 }
510                                                 sleep (polling_millis);
511                                         } catch (InterruptedException ie) {
512                                                 ;       // Great, let's move on!
513                                         }
514                                 } else {
515                                         polling_millis = polling_millis_min;
516                                 }
517                         }
518                 }
519         }
520         
521         private class Maintainer extends Thread {
522                 
523                 /* The time for the next scheduled maintenance: routersol or keepalive.
524                  * The milliseconds are always 0 for maintenance tasks.
525                  */
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;
533         
534                 /* Perform the initial Router Solicitation exchange with the public server.
535                  */
536                 public void solicit_router () {
537                         if (uplink != null) {
538                                 try {
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);
543                                 }
544                         }
545                 }
546                 
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.
551                  */
552                 public void keepalive () {
553                         if ((keepalive_packet != null) && (uplink != null)) {
554                                 try {
555                                         uplink.send (keepalive_packet);
556                                 } catch (IOException ioe) {
557                                         ;       /* Better luck next time? */
558                                 }
559                         }
560                 }
561                 
562                 /* Perform regular maintenance tasks: KeepAlive, and requesting a local address.
563                  */
564                 public void regular_maintenance () {
565                         if (!have_lladdr) {
566                                 solicit_router ();
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;
571                                 }
572                                 //TODO// syslog (LOG_INFO, "Sent Router Advertisement to Public 6bed4 Service, next attempt in %d seconds\n", maintenance_time_cycle);
573                         } else {
574                                 //TODO// syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
575                                 keepalive ();
576                                 maintenance_time_cycle = maintenance_time_cycle_max;
577                         }
578                         maintenance_time_millis = System.currentTimeMillis () + 1000 * (long) maintenance_time_cycle;
579                 }
580                 
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.
586                  */
587                 public void have_local_address (boolean new_setting) {
588                         have_lladdr = new_setting;
589                         if (have_lladdr) {
590                                 maintenance_time_cycle = maintenance_time_cycle_max;
591                                 maintenance_time_millis = System.currentTimeMillis () + 1000 * maintenance_time_cycle;
592                         }
593                 }
594                 
595                 /* Run the regular maintenance thread.  This involves sending KeepAlives
596                  * and possibly requesting a local address through Router Solicitation.
597                  */
598                 public void run () {
599                         try {
600                                 while (true) {
601                                         regular_maintenance ();
602                                         sleep (maintenance_time_millis - System.currentTimeMillis());
603                                 }
604                         } catch (InterruptedException ie) {
605                                 ;
606                         }
607                 }
608                 
609                 /* Construct the Maintainer thread.
610                  */
611                 public Maintainer (SocketAddress server) {
612                         byte payload [] = { };
613                         if (server != null) {
614                                 try {
615                                         keepalive_packet = new DatagramPacket (payload, 0, server);
616                                 } catch (SocketException se) {
617                                         keepalive_packet = null;
618                                 }
619                         }
620                 }
621         }
622         
623         public TunnelService () {
624         }
625         
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
631          */
632         public void change_local_netconfig () {
633                 Builder builder;
634                 builder = new Builder ();
635                 builder.setSession ("6bed4 uplink to IPv6");
636                 builder.setMtu (1280);
637                 try {
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);
642                 }
643                 if (new_setup_defaultroute) {
644                         builder.addRoute ("::", 0);
645                 }
646                 if (fio != null) {
647                         try {
648                                 fio.close ();
649                         } catch (IOException ioe) {
650                                 ; /* Uncommon: Fallback to garbage collection */
651                         }
652                 }
653                 //
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);
657                 //
658                 // Setup a new neighboring cache, possibly replacing an old one
659                 ngbcache = new NeighborCache (uplink, tunserver, local_address);
660                 //
661                 // Now actually construct the tunnel as prepared
662                 fio = builder.establish ();
663                 try {
664                         uplink.setSoTimeout (1);
665                 } catch (SocketException se) {
666                         throw new RuntimeException ("UDP socket refuses turbo mode", se);
667                 }
668                 synchronized (this) {
669                         downlink_rd = new FileInputStream  (fio.getFileDescriptor ());
670                         downlink_wr = new FileOutputStream (fio.getFileDescriptor ());
671                 }
672                 synchronized (worker) {
673                         worker.notifyAll ();
674                 }
675                 maintainer.have_local_address (true);
676         }
677         
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;
686                         }
687                 }
688                 // If the default route should change, change the local network configuration
689                 if (new_setup_defaultroute != setup_defaultroute) {
690                         change_local_netconfig ();
691                 }
692         }
693         
694         public TunnelService (DatagramSocket uplink_socket, InetSocketAddress publicserver) {
695                 synchronized (this) {
696                         uplink = uplink_socket;
697                         tunserver = publicserver;
698                         // notifyAll ();
699                 }
700                 //
701                 // Setup a network monitor that will watch for broadcast events with network changes
702                 if (netmon == null) {
703                         netmon = new EventListener ();
704                 }
705                 netmon.register_network_monitor (this);
706                 //
707                 // Create the worker thread that will pass information back and forth
708                 if (worker == null) {
709                         worker = new Worker ();
710                         worker.start ();
711                 } else {
712                         synchronized (worker) {
713                                 worker.notifyAll ();
714                         }
715                 }
716                 if (maintainer == null) {
717                         maintainer = new Maintainer (tunserver);
718                         maintainer.start ();
719                 } else {
720                         maintainer.have_local_address (false);
721                 }
722         }
723         
724         synchronized public void teardown () {
725                 try {
726                         synchronized (this) {
727                                 if (netmon != null) {
728                                         netmon.unregister_network_monitor ();
729                                         netmon = null;
730                                 }
731                                 if (worker != null) {
732                                         worker.interrupt ();
733                                         worker = null;
734                                 }
735                                 if (maintainer != null) {
736                                         maintainer.interrupt ();
737                                         maintainer = null;
738                                 }
739                                 if (downlink_rd != null) {
740                                         downlink_rd.close ();
741                                         downlink_rd = null;
742                                 }
743                                 if (downlink_wr != null) {
744                                         downlink_wr.close ();
745                                         downlink_wr = null;
746                                 }
747                                 if (fio != null) {
748                                         fio.close ();
749                                         fio = null;
750                                 }
751                         }
752                 } catch (IOException ioe) {
753                         ;       /* Uncommon: Fallback to garbage collection */
754                 }
755         }
756         
757         synchronized public void onRevoke () {
758                 this.teardown ();
759         }
760
761
762         /***
763          *** UTILITY FUNCTIONS
764          ***/
765
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];
769                 }
770         }
771         
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]) {
775                                 return true;
776                         }
777                 }
778                 return false;
779         }
780         
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]) {
784                                 return true;
785                         }
786                 }
787                 return false;
788         }
789
790         /* Retrieve an unsigned 16-bit value from a given index in a byte array and
791          * return it as an integer.
792          */
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);
796                 return retval;
797         }
798         
799         /* Fill in the ICMPv6 checksum field in a given IPv6 packet.
800          */
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;
806                 int i;
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);
811                         }
812                 }
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
817                 return csum;
818         }
819         
820 }