First version for general use. No servers at fixed IPv6 addresses yet.
authorRick van Rein <rick@openfortress.nl>
Sun, 23 Dec 2012 14:36:22 +0000 (15:36 +0100)
committerRick van Rein <rick@openfortress.nl>
Sun, 23 Dec 2012 14:36:22 +0000 (15:36 +0100)
COPYRIGHT [new file with mode: 0644]
README [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/java/nl/openfortress/socket6bed4/ConnectionPool.java [new file with mode: 0644]
src/main/java/nl/openfortress/socket6bed4/DatagramSocket6bed4.java [new file with mode: 0644]
src/main/java/nl/openfortress/socket6bed4/Inet6bed4Address.java [new file with mode: 0644]
src/main/java/nl/openfortress/socket6bed4/NeighborCache.java [new file with mode: 0644]
src/main/java/nl/openfortress/socket6bed4/ServerNode.java [new file with mode: 0644]
src/main/java/nl/openfortress/socket6bed4/Socket6bed4.java [new file with mode: 0644]
src/main/java/nl/openfortress/socket6bed4/Utils.java [new file with mode: 0644]

diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..5dc6ddd
--- /dev/null
+++ b/README
@@ -0,0 +1,83 @@
+Socket6bed4 README
+==================
+
+Socket6bed4 is a Java package that introduces an IPv6 tunnel as a socket subclass.
+There is no need to configure anything, just adapt your datagram factory to the
+new tunnel and you will be able to use IPv6.
+
+
+Purpose
+-------
+
+Java is used in many places, including some that will remain IPv4-only for years
+to come.  The 6bed4 tunnel is a zero-config tunnel that eliminates dependency on
+your ISP so you can assume that IPv6 is always available.  Or better even, you
+can distribute Java software that is IPv6-only by using 6bed4 as a fallback for
+users that don't have native IPv6 yet.
+
+
+About 6bed4
+-----------
+
+The design of 6bed4 is intended to support zero-config operation.  You are welcome
+to setup your private 6bed4 server though; it will still be able to communicate
+with the rest of the IPv6 world, native as well as 6bed4.
+
+The design of 6bed4 also explicitly supports peer-to-peer communication.  It will
+attempt to setup direct links between clients, which are therefore referred to as
+peers instead of clients.  The tunnel server is a fallback service, and will only
+be used in practice if a symmetric NAT is used anywhere between the peers.  This
+means that 6bed4 is suitable for realtime media streaming between directly
+connected peers.  The disadvantages of IPv4 are not transferred to IPv6 through
+this tunnel mechanism!
+
+Until 6bed4 is a formal IETF standard, something that we are indeed working
+towards, there is a serious chance that the addresses used will change in the
+future.  This means that you should keep an eye on such changes and keep the
+software using 6bed4 up to date.  To help you with that, announcements of
+such an infrastructural nature are sent to an extremely low-traffic list,
+
+https://lists.sourceforge.net/lists/listinfo/tun6bed4-infra
+
+More information about the 6bed4 tunnel is concentrated at
+
+http://devel.0cpm.org/6bed4
+
+Specifically for this module, you will find information on
+
+http://devel.0cpm.org/6bed4/java-socket6bed4
+
+
+Using Socket6bed4 on a peer or client
+-------------------------------------
+
+We prefer to speak of 6bed4 endpoints as peers, rather than clients.  This is to
+emphasise that they are normally connected to each other, instead of to an
+intermediate server.
+
+Peer use of 6bed4 is straightforward.  You should adapt the code that generates
+your DatagramSocket to create an instance of nl.openfortress.DatagramSocket6bed4
+instead.  That's all.  If you are already using a factory, you have to do this in
+one place only.
+
+If you have access to a native IPv6 address, you probably want to prefer using
+that.  We will not enforce that however -- as we can imagine that you would want
+to run native IPv6 and 6bed4 in parallel, to approach other 6bed4 peers in
+parallel with yours.
+
+
+Using Socket6bed4 on a server
+-----------------------------
+
+Every run of Java will normally create a different IPv6 address.  This address
+contains the external IPv4 address and UDP port for use with the server.  These
+will change every restart of the Socket6bed4 stack, so a server-side address
+would not be fixed.  This may present a problem; let us know if this is important
+for you.
+
+
+Feedback
+--------
+
+We would like to hear what you use Socket6bed4 for.
+
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..12a9e22
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,79 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>socket6bed4</groupId>
+  <artifactId>nl.openfortress.socket6bed4</artifactId>
+  <version>0.0</version>
+  <packaging>jar</packaging>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>2.2.1</version>
+        <executions>
+
+<!--
+          <execution>
+            <id>jar-with-dependencies</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifest>
+                  <mainClass>net.AbstractSIPlet</mainClass>
+                </manifest>
+              </archive>
+              <descriptorRefs>
+                <descriptorRef>jar-with-dependencies</descriptorRef>
+              </descriptorRefs>
+            </configuration>
+          </execution>
+-->
+
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.3.2</version>
+        <configuration>
+<!--
+          <source>1.6</source>
+          <target>1.6</target>
+-->
+        </configuration>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+       <groupId>org.apache.maven.plugins</groupId>
+         <artifactId>maven-javadoc-plugin</artifactId>
+         <version>2.9</version>
+<!--
+         <configuration>
+          ...
+        </configuration>
+-->
+      </plugin>
+    </plugins>
+  </reporting>
+
+
+</project>
diff --git a/src/main/java/nl/openfortress/socket6bed4/ConnectionPool.java b/src/main/java/nl/openfortress/socket6bed4/ConnectionPool.java
new file mode 100644 (file)
index 0000000..6a76ef0
--- /dev/null
@@ -0,0 +1,175 @@
+package nl.openfortress.socket6bed4;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import java.util.Hashtable;
+
+
+/** The ConnectionPool links IPv4 sockets for 6bed4 with the implementations
+ * of IPv6 sockets.  Each IPv4 socket may have multiple IPv6 sockets, as
+ * a result of these variation factors:
+ *  - the router supplies a range of IPv6 addresses
+ *  - the IPv6 DatagramSockets can listen to multiple ports
+ *  - the IPv6 DatagramSockets could receive from multiple ports
+ *
+ * Incoming traffic is mapped based on an InetSocketAddress; if an exact
+ * match is not available, then the zero address is tried instead.
+ *
+ * The sender information is stored in a Datagrampacket's SocketAddress.
+ */
+public class ConnectionPool {
+
+       protected final static byte serveripbytes[] = { (byte) 145, (byte) 136, 0, 1 };
+       protected final static int serverport = 25788;
+       protected InetSocketAddress default_server;
+       protected Hashtable<InetSocketAddress,DatagramSocket6bed4> clients;
+       protected Hashtable<InetSocketAddress,ServerNode         > servers;
+       protected Hashtable<Inet6Address,InetSocketAddress       > addressmap;
+
+
+       /** The connection pool is normally a shared resource,
+        * although private instances are certainly possible.
+        */
+       private static ConnectionPool pool;
+
+       /** Create the shared connection pool when loading this class.
+        */
+       static {
+               try {
+                       pool = new ConnectionPool ();
+               } catch (SocketException se) {
+                       throw new RuntimeException ("Failed to create initial default ConnectionPool");
+               }
+       }
+
+       /** Return the connection pool intended to be shared.
+        */
+       public static ConnectionPool getSharedConnectionPool () {
+               return pool;
+       }
+
+
+       /** Start using a server, possibly introducing it.
+        * Return the ServerNode that is herewith opened.
+        */
+       public ServerNode openServer (InetSocketAddress isa)
+       throws UnknownHostException, SocketException {
+               if (!(isa.getAddress () instanceof Inet4Address)) {
+                       throw new UnknownHostException ("openServer() must use an IPv4 address");
+               }
+               synchronized (servers) {
+                       ServerNode srv = servers.get (isa);
+                       if (srv == null) {
+                               srv = new ServerNode (isa);
+                               servers.put (isa, srv);
+                               srv.useMore ();
+                               addressmap.put (srv.getShared6bed4Address (), isa);  //TODO:BOOTSTRAP LOCKUP -- NO SHARED ADDRESS UNTIL MAINTAINER STARTS
+                       } else {
+                               srv.useMore ();
+                       }
+                       return srv;
+               }
+       }
+
+       /** Stop using a server, possibly removing it.
+        */
+       public void closeServer (InetSocketAddress isa)
+       throws UnknownHostException {
+               if (!(isa.getAddress () instanceof Inet4Address)) {
+                       throw new UnknownHostException ("closeServer() must use an IPv4 address");
+               }
+               synchronized (servers) {
+                       ServerNode srv = servers.get (isa);
+                       if (srv == null) {
+                               throw new UnknownHostException ("Cannot close a new server");
+                       }
+                       if (srv.useLess ()) {
+                               servers.remove (isa);
+                               addressmap.remove (srv.getShared6bed4Address ());
+                       }
+               }
+       }
+
+       /** Return the default 6bed4 tunnel server.
+        */
+       public InetSocketAddress getDefaultServer () {
+               return default_server;
+       }
+       
+       /** Return the ServerNode for the default 6bed4 tunnel server.
+        */
+       public ServerNode getDefaultServerNode () {
+               return servers.get (default_server);
+       }
+
+       /** Return the ServerNode for a given InetSocketAddress,
+        * holding the server's IPv4 address and UDP port.
+        * Returns null if not found.
+        * TODO: WHAT IS THE PURPOSE HERE?!?
+        */
+       public ServerNode getServerNode (InetSocketAddress isa) {
+               synchronized (servers) {
+                       return servers.get (isa);
+               }
+       }
+
+       /** Returns the ServerNode for a given InetAddress, which
+        * is then assumed to match an Inet6Address in the
+        * hash tables.
+        * Returns null if not found.
+        */
+       public ServerNode getServerNode (InetAddress ia) {
+               InetSocketAddress isa = addressmap.get (ia);
+               if (isa == null) {
+                       return null;
+               }
+               return servers.get (isa);
+       }
+
+       /** Change the server that counts as the default tunnel server
+        * for future connection attempts.  Existing connections are not
+        * influenced.
+        */
+       public void setDefaultServer (InetSocketAddress isa)
+       throws UnknownHostException, SocketException {
+               if (!(isa.getAddress () instanceof Inet4Address)) {
+                       throw new UnknownHostException ("setDefaultServer() must use an IPv4 address");
+               }
+               try {
+                       synchronized (servers) {
+                               closeServer (default_server);
+                               default_server = isa;
+                               openServer (default_server);
+                       }
+               } catch (UnknownHostException uhe) {
+                       throw new RuntimeException ("UseCount error for default 6bed4 tunnel server");
+               }
+       }
+
+       /** Construct a ServerPool with nothing but the default server.
+        * There are no clients yet, of course.
+        */
+       public ConnectionPool ()
+       throws SocketException {
+               clients = new Hashtable<InetSocketAddress,DatagramSocket6bed4> ();
+               servers = new Hashtable<InetSocketAddress,ServerNode         > ();
+               addressmap = new Hashtable<Inet6Address,InetSocketAddress    > ();
+               try {
+                       default_server = new InetSocketAddress (InetAddress.getByAddress (serveripbytes), serverport);
+               } catch (UnknownHostException uhe) {
+                       throw new RuntimeException ("Failed to parse IPv4 address of 6bed4 default server");
+               }
+               try {
+                       openServer (default_server);
+               } catch (UnknownHostException uhe) {
+                       throw new RuntimeException ("Failed to initialize default 6bed4 tunnel server");
+               }
+       }
+
+}
+
diff --git a/src/main/java/nl/openfortress/socket6bed4/DatagramSocket6bed4.java b/src/main/java/nl/openfortress/socket6bed4/DatagramSocket6bed4.java
new file mode 100644 (file)
index 0000000..92dd30c
--- /dev/null
@@ -0,0 +1,333 @@
+package nl.openfortress.socket6bed4;
+
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.DatagramSocketImpl;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.io.IOException;
+
+import java.util.Arrays;
+import java.nio.ByteBuffer;
+
+
+/** Socket6bed4 describes sockets that are run over IPv6.  This means
+ * that a remote IPv6 address can be contacted when there are only
+ * IPv4 addresses available locally.
+ *
+ * A Socket6bed4 can switch between IPv4 transports; it can either send
+ * to a 6bed4 server, or directly to the targeted peer.  Either path is
+ * acceptable as.  The Socket can arrange this automatically, using
+ * peering attempts through Neighbor Discovery.
+ *
+ * Perhaps it's a bit silly to make a 6bed4 DatagramSocket a subclass
+ * of a plain DatagramSocket.  It is very practical however; it means
+ * that these objects can be substituted anywhere, without question.
+ */
+public class DatagramSocket6bed4 extends DatagramSocket {
+
+       protected static DatagramSocket server_ipv4socket;
+       protected DatagramSocket ipv4socket;
+       protected InetSocketAddress cnx6sa = null;
+       protected InetSocketAddress my6sa = null;
+       protected ServerNode my6sn = null;
+       protected int ephemeral = 3210;
+       static final byte prephdr_payload [] = { 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, Utils.IPPROTO_UDP, 64 /*HopLimit*/ };
+       protected byte prephdr_udp [] = new byte [8];
+
+       /* Tell this connection to switch back to the default server,
+        * skipping any direct route.
+        */
+       public void useDefaultServer () {
+               ipv4socket = server_ipv4socket;
+       }
+
+       /* Set the IPv4 address and UDP port of the default server,
+        * as shared by all sockets.  Note that sockets will not
+        * move over immediately.  Invoke useDefaultServer() if this
+        * is needed.
+        */
+       public void setDefaultServer (Inet4Address server4, int port4)
+       throws SocketException {
+               DatagramSocket sox = new DatagramSocket ();
+               sox.connect (server4, port4);
+               server_ipv4socket = sox;
+       }
+
+
+       /** Return currect remote address */
+       public InetAddress getInetAddress () {
+               return (cnx6sa != null)? cnx6sa.getAddress (): null;
+       }
+
+       /** Return currect local port */
+       public int getPort () {
+               return (cnx6sa != null)? cnx6sa.getPort (): 0;
+       }
+
+       /** Return remote sock address */
+       public InetSocketAddress getRemoteSocketAddress () {
+               return cnx6sa;
+       }
+
+       /** Return local address */
+       public InetAddress getLocalAddress () {
+               return my6sa.getAddress ();
+       }
+
+       /** Return local port */
+       public int getLocalPort () {
+               return my6sa.getPort ();
+       }
+
+       /** Return bound local socket address */
+       public InetSocketAddress getLocalSocketAddress () {
+               return my6sa;
+       }
+
+       public void bind (int port)
+       throws SocketException {
+               //
+               // Verify if binding request is properly formed
+               if ((port <= 0) || (port > 65535)) {
+                       throw new SocketException ("Binding is only possible to ports from 1 to 65535");
+               }
+               //TODO// Checks against Router Advertisement
+               //
+               // Perform the actual binding, and unbind old
+               ConnectionPool pool = ConnectionPool.getSharedConnectionPool ();
+               my6sn = pool.getDefaultServerNode ();
+               synchronized (this) {
+                       my6sn.registerDatagramClient (port);
+                       if (my6sa != null) {
+                               my6sn.unregisterDatagramClient (my6sa.getPort ());
+                       }
+                       prephdr_udp [0] = (byte) (port >> 8);
+                       prephdr_udp [1] = (byte) (port & 0x00ff);
+                       my6sa = new InetSocketAddress (my6sn.getShared6bed4Address (), port);
+               }
+       }
+
+       /** Attempt to bind to an address and/or port, exercising
+        * 6bed4 constraints as they arise from the router advertisement.
+        */
+       public void bind (InetSocketAddress sa)
+       throws SocketException {
+               if (sa == null) {
+                       //
+                       // Iterate over ports until one is found to be free
+                       for (int i=0; i < 4095; i++) {
+                               ephemeral = (ephemeral + 1) & 0x0ffff;
+                               try {
+                                       bind (ephemeral + 49152);
+                                       return; /* No complaints? Than break out of the loop */
+                               } catch (SocketException se) {
+                                       /* Taken?  Then continue trying */
+                               }
+                       }
+                       throw new SocketException ("No ephemeral ports are free on the 6bed4 address");
+
+               } else {
+                       bind (sa.getPort ());
+               }
+       }
+
+       /** Are we bound? */
+       public boolean isBound () {
+               return my6sa != null;
+       }
+
+       /** Connect to a remote IPv6 address and UDP port. */
+       public void connect (InetAddress address, int port) {
+               connect (new InetSocketAddress (address, port));
+       }
+       public void connect (InetSocketAddress addr) {
+               cnx6sa = (InetSocketAddress) addr;
+               prephdr_udp [2] = (byte) (cnx6sa.getPort () >> 8);
+       prephdr_udp [3] = (byte) (cnx6sa.getPort () & 0x00ff);
+
+       }
+
+       /** Disconnect from a remote IPv6 address and UDP port. */
+       public void disconnect () {
+               cnx6sa = null;
+       }
+
+       /** Are we connected? */
+       public boolean isConnected () {
+               return cnx6sa != null;
+       }
+
+
+       /** The interface for DatagramPackets conceals the UDP layer
+        * underlaying the actual data exchanged.  For 6bed4 however,
+        * a few headers will have to be prefixed.  These are the
+        * IPv6 header and UDP-over-IPv6 header.  The underlying
+        * DatagramSocket for IPv4 will conceal the UDP-over-IPv4.
+        *
+        * Details about playful hints are described in the class
+        * NeighborCache.  In general, the hint should only be given
+        * for packets that will be re-sent upon failure, and whose
+        * success of delivery can be reported back.  This can then
+        * be used to setup direct connections to peers without
+        * explicit negotiation through Neighbor Discovery.  You can
+        * safely set the playful flag on all similar traffic, as it
+        * will only influence on initial attempts at traffic.  A good
+        * efficiency trade-off is to use playful hints only on
+        * initiating UDP messages, such as a SIP INVITE.
+        */
+       public void send_playful (DatagramPacket pkt6, boolean playful)
+       throws IOException {
+               int pkt6len = pkt6.getLength ();
+               ByteBuffer buf = ByteBuffer.allocate (Utils.OFS_UDP6_PLOAD + pkt6len);
+               buf.put (prephdr_payload);
+               buf.put ( my6sa.getAddress ().getAddress ());
+               Inet6Address remote;
+               if (cnx6sa != null) {
+                       remote = (Inet6Address) cnx6sa.getAddress ();
+               } else {
+                       remote = (Inet6Address) pkt6.getAddress ();
+               }
+               buf.put (remote.getAddress ());
+               buf.put (prephdr_udp);
+               buf.put (pkt6.getData (), pkt6.getOffset (), pkt6len);
+               byte[] tundata = buf.array ();
+               tundata [Utils.OFS_IP6_PLEN + 0] =
+               tundata [Utils.OFS_UDP6_PLEN + 0] = (byte) ((pkt6len + 8) >> 8);
+               tundata [Utils.OFS_IP6_PLEN + 1] = 
+               tundata [Utils.OFS_UDP6_PLEN + 1] = (byte) ((pkt6len + 8) & 0x00ff);
+               if (cnx6sa == null) {
+                       int rport = pkt6.getPort ();
+                       tundata [Utils.OFS_UDP6_DSTPORT + 0] = (byte) (rport >> 8);
+                       tundata [Utils.OFS_UDP6_DSTPORT + 1] = (byte) (rport & 0x00ff);
+               }
+               int csum = Utils.checksum_udpv6 (tundata);
+               tundata [Utils.OFS_UDP6_CSUM + 0] = (byte) (csum >> 8);
+               tundata [Utils.OFS_UDP6_CSUM + 1] = (byte) (csum & 0x00ff);
+               //TODO// Shared and locked send4 instead of local pkt4
+               DatagramPacket pkt4 = new DatagramPacket (tundata, 0, tundata.length);
+               pkt4.setSocketAddress (my6sn.lookup_neighbor ((Inet6Address) pkt6.getAddress (), playful));
+               my6sn.send (pkt4);
+       }
+
+
+       /** Receive a packet from the underlying IPv4 layer.  As with
+        * sending, the UDP underneath the packet is hidden, but for
+        * 6bed4 the IPv6 and UDP-over-IPv6 headers must be stripped
+        * and interpreted.  Only traffic destined for our own
+        * combination of IPv6 address and UDP-over-IPv6 port will be
+        * passed on to us.
+        *
+        * The hint returned indicates if the datagram was received
+        * directly from the peer.  This information may be of varying
+        * use to implementations, but it would be specifically useful
+        * for confirmation of playfully sent packets, as described
+        * for the send_playful () method.  To this end, the method
+        * acknowledge_playful () is used to complete the cycle of
+        * optimistic direct connections to peers.
+        */
+       public boolean receive_playful (DatagramPacket pkt6)
+       throws IOException {
+               byte msg[] = my6sn.receive_datagram (my6sa.getPort (), getSoTimeout ());
+               if (msg == null) {
+                       throw new SocketTimeoutException ("No data available within timeout");
+               }
+               int len = Utils.fetch_net16 (msg, Utils.OFS_IP6_PLEN) + Utils.OFS_IP6_PLOAD;
+               if (len < Utils.OFS_UDP6_PLOAD) {
+                       throw new IOException ("Datagram packet with silly short size received over 6bed4 tunnel");
+               }
+               if (Utils.checksum_udpv6 (msg, 0) != Utils.fetch_net16 (msg, Utils.OFS_UDP6_CSUM)) {
+                       throw new IOException ("Datagram packet with faulty checksum received over 6bed4 tunnel");
+               }
+               if (Utils.fetch_net16 (msg, Utils.OFS_UDP6_PLEN) + Utils.OFS_IP6_PLOAD > len) {
+                       throw new IOException ("Incomplete datagram received over 6bed4 tunnel");
+               }
+               pkt6.setAddress ((Inet6Address) InetAddress.getByAddress (Arrays.copyOfRange (msg, Utils.OFS_IP6_SRC, Utils.OFS_IP6_SRC + 16)));
+               pkt6.setPort (Utils.fetch_net16 (msg, Utils.OFS_UDP6_SRCPORT));
+               // PROBABLY FORMALLY CORRECT, BUT NOT HOW PEOPLE USE IT: pkt6.setData (msg, Utils.OFS_UDP6_PLOAD, len);
+               pkt6.setData (Arrays.copyOfRange (msg, Utils.OFS_UDP6_PLOAD, Utils.OFS_UDP6_PLOAD + len - 48), 0, len - 48);
+               //TODO// return ! pkt4.getAddress ().equals (ipv4socket.getInetAddress ());
+               return false;
+       }
+
+       /** In a playfully hinting exchange with send_playful() and
+        * receive_playful(), this is the final acknowledgement that
+        * should be called upon completion.  This acknowledges a
+        * reliable direct connection to a peer without a need to
+        * go through explicit Neighbor Discovery.  The pkt6
+        * parameter holds the remote peer's address.
+        *
+        * Note that invoking this function when receive_playful()
+        * returned false for the exchane is a damn lie, and may
+        * end up configuring the NeighborCache entry for this
+        * neighbor with a direct route that does not actually
+        * function.  You will then end up sending messages into
+        * oblivia.
+        *
+        * Also note that plaful sends are an optimistic variation
+        * that is not strictly necessary; the NeighborCache has
+        * its own method builtin, based on Neighbor Discovery.
+        * If a direct link to a peer is possible at all, then
+        * this will find it.  Only if you wish to use the
+        * optimistic variation to avoid these extra exchanges
+        * should you consider playful mode.  After all this
+        * discouraging information it should also be noted that
+        * your network traffic will look extremely cool if it
+        * manages to get through directly to a 6bed4 peer without
+        * any explicit negotiation!
+        */
+       public void acknowledge_playful (Inet6Address ia6bed4) {
+               my6sn.acknowledge_playful (ia6bed4.getAddress(), 0);
+       }
+
+       /** The "standard" interface for sending bytes, overriding the
+        * parent function and not sending playful hints.
+        */
+       public void send (DatagramPacket pkt6)
+       throws IOException {
+               send_playful (pkt6, false);
+       }
+
+       /** The "standard" interface for receiving bytes, overriding the
+        * parent function and not supporting playful operation.
+        */
+       public void receive (DatagramPacket pkt6)
+       throws IOException {
+               /*(void)*/ receive_playful (pkt6);
+       }
+
+
+       /** Construct a new DatagramSocket6bed4 based on an underlying
+        * DatagramSocket for IPv4.
+        */
+       public DatagramSocket6bed4 (InetSocketAddress bindaddr)
+       throws SocketException {
+               super ();
+               bind (bindaddr);
+       }
+       public DatagramSocket6bed4 (int port, Inet6Address bindaddr)
+       throws SocketException  {
+               this (new InetSocketAddress (bindaddr, port));
+       }
+       public DatagramSocket6bed4 (int port)
+       throws SocketException  {
+               this (port, null /*TODO:INADDR_ANY*/);
+       }
+       public DatagramSocket6bed4 ()
+       throws SocketException  {
+               super ();
+               bind ((InetSocketAddress) null);
+       }
+       public DatagramSocket6bed4 (DatagramSocketImpl impl)
+       throws SocketException  {
+               throw new RuntimeException ("Cannot choose DatagramSocketImpl");
+       }
+
+
+}
+
diff --git a/src/main/java/nl/openfortress/socket6bed4/Inet6bed4Address.java b/src/main/java/nl/openfortress/socket6bed4/Inet6bed4Address.java
new file mode 100644 (file)
index 0000000..f7e3151
--- /dev/null
@@ -0,0 +1,92 @@
+package nl.openfortress.socket6bed4;
+
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
+
+
+/** The Inet6bed4Address class acts a bit like InetAddress and
+ * Inet6Address.  It is not, however related to these classes;
+ * they are locked down by the Java language [1].  There are
+ * a few calls similar to those in Inet*Address that are also
+ * available here; however, an address created here will be an
+ * Inet6Address.
+ *  
+ *  [1] Inet6Address is a final class, and InetAddress has no
+ *      constructors to call.  Nasty.
+ */
+public final class Inet6bed4Address {
+       
+       /** Returns the shared 6bed4 address for this host.
+        * Note that a 6bed4 address is available on any host that
+        * has an IPv4 address and supports IPv6.  This is also the
+        * when a native IPv6 address exists.  You should however
+        * prefer native IPv6 over 6bed4, except perhaps for the
+        * communication with 6bed4 peers.
+        */
+       public static Inet6Address getLocalHost ()
+       throws UnknownHostException {
+               return ConnectionPool.
+                       getSharedConnectionPool ().
+                       getDefaultServerNode ().
+                       getShared6bed4Address ();
+       }
+
+       /** Test if a given InetAddress is a 6bed4 address.
+        * Note that this does not guarantee that it can be cast
+        * to an Inet6bed4Address.
+        * TODO: See notes for is6bed4Address(byte addr[])
+        */
+       public static boolean is6bed4Address (Inet6Address ia) {
+               if (! (ia instanceof Inet6Address)) {
+                       return false;
+               }
+               //TODO// Quickfix to incorporate active servers, ideally also to be done for the byte[] version, and would ideally not be limited to the shared pool:
+               if (ConnectionPool.getSharedConnectionPool ().getServerNode (ia) != null) {
+                       return true;
+               }
+               return is6bed4Address (ia.getAddress ());
+       }
+
+       /** Test if the given byte array represents a 6bed4 address.
+        * TODO: These addresses are subject to change until 6bed4
+        * is formalised as an RFC!  Please subscribe to
+        * tun6bed4-infra for (only those) updates:
+        * https://lists.sourceforge.net/lists/listinfo/tun6bed4-infra
+        * The intention is to obtain a /16 for ten years to come,
+        * and recognise a 6bed4 address based on that.
+        */
+       public static boolean is6bed4Address (byte addr[]) {
+               if (addr.length != 16) {
+                       return false;
+               }
+               //TODO// Take local 6bed4 serverpool into account
+               return (addr [0] == 0x20) && (addr [1] == 0x01) && (addr [2] == 0x06) && (addr [3] == 0x7c) && (addr [4] == 0x12) && (addr [5] == 0x7c);
+       }
+
+       /** Return an Inet6bed4Address based on a byte string.
+        * TODO: Perhaps try sharing with existing instances?
+        */
+       public static Inet6Address getByAddress (byte addr[]) {
+               //TODO// Instead of just checking length, eventually use is6bed4Address
+               if (addr.length != 16) {
+                       throw new ClassCastException ("Byte array has wrong length");
+               }
+               if (!is6bed4Address (addr)) {
+                       throw new ClassCastException ("Address is not a 6bed4 address");
+               }
+               try {
+                       return (Inet6Address) InetAddress.getByAddress (addr);
+               } catch (UnknownHostException uhe) {
+                       throw new RuntimeException ("Failed to parse IPv6 address bytes");
+               }
+       }
+
+       /** A private constructor avoids instantiation.
+        */
+       private Inet6bed4Address () {
+               ;
+       }
+       
+}
+
diff --git a/src/main/java/nl/openfortress/socket6bed4/NeighborCache.java b/src/main/java/nl/openfortress/socket6bed4/NeighborCache.java
new file mode 100644 (file)
index 0000000..6e545a7
--- /dev/null
@@ -0,0 +1,755 @@
+package nl.openfortress.socket6bed4;
+
+
+import java.util.Hashtable;
+import java.util.Queue;
+import java.util.ArrayDeque;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+
+import java.io.IOException;
+
+
+
+/* The Neighbor Cache is modelled in Java, because Android/Java won't
+ * give access to the network stack.
+ * 
+ * The primary task of the cache is to map IPv6-addresses in byte [16]
+ * format to a destination, which is stored as a Inet4SocketAddress
+ * for direct use with DatagramPacket facilities in Java.  A hash is
+ * used to gain fast access to the target object.
+ * 
+ * To find mappings, the cache will send up to 3 Neighor Solicitation
+ * messages to the direct peer.  During this period it will pass through
+ * states ATTEMPT1 to ATTEMPT3, and finally become FAILED.  This is a sign
+ * that no further attempts need to be made until the expiration of the
+ * mapping, about 30 seconds after starting it.  This process starts
+ * when an unknown peer is approached, or one that is STALE.
+ * 
+ * If a Neighbor Advertisement comes in, this information is sent here
+ * for processing.  The cache entry will usually go to REACHABLE in such
+ * situations.
+ * 
+ * When traffic comes in with the direct address of this peer as its
+ * destination, then the cache is informed; if no mapping in REACHABLE
+ * state exists, then new attempts are started, so as to enable direct
+ * connectivity after the remote peer has opened a hole to us.
+ * 
+ * The cache maintains a seperate timer queue for each state, and will
+ * act upon the objects if they expire.  This makes the queue rather
+ * self-controlled.
+ * 
+ * Before changing any piece of this code, please assure yourself that
+ * you understand each single synchronized section, and convince
+ * yourself that you understand the trick in NeighborQueue.dequeue().
+ * This is not just multitasking code; it may well be the most complex
+ * example of multitasking code that you ever came accross.  Do not
+ * change the code unless you are absolutely certain that you have
+ * understood all the invariants that make this code work!
+ */
+
+class NeighborCache {
+       
+       /* The states of neighbors in the cache.
+        */
+       public final static int ATTEMPT1 = 0;           /* Discovery sequence */
+       public final static int ATTEMPT2 = 1;
+       public final static int ATTEMPT3 = 2;
+       public final static int FAILED = 3;
+       public final static int REACHABLE = 4;          /* Confirmed in the last ~30s */
+       public final static int STALE = 5;                      /* Not used for up to ~30s */
+       public final static int NUM_NGB_STATES = 6;     /* Count of normal states */
+       public final static int INTRANSIT = 7;          /* Traveling between queues */
+       
+       
+       /* The timing spent in the various states of a cache entry.  Stable states
+        * take up several seconds, while waiting for a response is faster.
+        * Note the remarks at NeighborQueue.dequeue() for an important constraint;
+        * specifically, STALE is spent in an attempt to recycle an entry as
+        * REACHABLE, so the time spent in STALE must not exceed the time spent
+        * in REACHABLE.  This avoids having two entries for one neighbor in a
+        * queue, which would jeapourdise the timeout calculations.  Note that
+        * this problem does not arise when an object occurs in multiple queues
+        * at once, because a mismatch with the queue's state means that those
+        * lingering neighbors are ignored as timeout sources.
+        * 
+        * TODO: I found that Android can be pretty slow in responding over eth0,
+        * possibly worse over UMTS or (shiver) GPRS.  So, it may be that the
+        * following timing must be slowed down, *or* that NgbAdv ought to be
+        * accepted in the STALE state.
+        */
+       final static int state_timing_millis [] = {
+                       50, 50, 50, 30000,                              /* Discovery -- a few ms between steps */
+                       27500,                                                  /* STALE -- 30 additional seconds */
+                       30000                                                   /* REACHABLE -- 30 seconds of bliss */
+       };
+       
+       /* The timer queues for neighbor discovery, each in its own thread */
+       NeighborQueue queues [];
+       
+       /* The neighbor cache contains all elements, regardless of their state.
+        * This is needed to avoid doing double work.  If a neighbor is considered
+        * to be available, then it should be in here.  Once it stops being of
+        * interest (STALE or FAILED expires) it will be removed.
+        * 
+        * Note that the key and value are both Neighbor objects.  The only part
+        * of the Neighbor dictating equality (and the hashCode) is the 6-byte
+        * key holding the remote peer's IPv4 address and UDP port, so it is
+        * possible to put one object into the Hashtable as both key and value;
+        * and then to lookup an entry with a minimally setup Neighbor as key
+        * to find a more evolved value; the latter will be incorporated into
+        * the procedures of the Neighbor Cache, including the various queues.
+        */
+       private Hashtable <Integer, Neighbor> neighbor_cache;
+       
+       
+       /* The public server, used as default return value for queires after
+        * unsettled neighbor cache entries.
+        */
+       private InetSocketAddress public_server;
+       
+       
+       /* The socket used as uplink to the rest of the 6bed4-using World.
+        */
+       private DatagramSocket uplink;
+       
+       
+       /* The 6bed4 address as a 16-byte array; may also be used as an 8-byte prefix.
+        */
+       private byte address_6bed4 [];
+       
+       /* The 6bed4 address as a 6-byte key.
+        */
+       private byte address_6bed4_key [];
+       
+       
+       /* Construct a neighbor cache with no entries but a lot of structure */
+       public NeighborCache (DatagramSocket ipv4_uplink, InetSocketAddress pubserver, byte link_address_6bed4 []) {
+               uplink = ipv4_uplink;
+               public_server = pubserver;
+               address_6bed4 = new byte [16];
+               Utils.memcp_address (address_6bed4, 0, link_address_6bed4, 0);
+               address_6bed4_key = new byte [6];
+               address2key (address_6bed4, 0, address_6bed4_key);
+               queues = new NeighborQueue [NUM_NGB_STATES];
+               for (int i=0; i < NUM_NGB_STATES; i++) {
+                       queues [i] = new NeighborQueue (this, i);
+               }
+               neighbor_cache = new Hashtable <Integer,Neighbor> ();
+       }
+       
+       /* Destroy a neighbor cache */
+       public void cleanup () {
+               for (int i=0; i < NUM_NGB_STATES; i++) {
+                       queues [i].interrupt ();
+               }
+       }
+
+       /* Remove an expired entry from the neighbor cache.
+        */
+       public void remove_from_cache (Neighbor ngb) {
+               byte key [] = new byte [6];
+               socketaddress2key (ngb.target, key);
+               synchronized (neighbor_cache) {
+                       neighbor_cache.remove (key);
+               }
+       }
+       
+       /* Attempt to retrieve an entry from the neighbor cache.  The resulting
+        * actions as well as the return value may differ, depending on the
+        * state of the cache entry.  This function always returns immediately,
+        * with the tunnel server as a default response for instant connections,
+        * but the cache may asynchronously attempt to find a direct path to the
+        * neighbor.
+        * 
+        * The "playful" argument is rather special.  When set to true, it
+        * indicates that an initial experiment attempting to contact the
+        * neighbor directly is acceptable.  This is usually the cae if the
+        * following conditions apply:
+        *  1. The message is resent upon failure
+        *  2. Success can be recognised and reported back here
+        *  Setting this flag causes the first (and only the first) result
+        *  from lookup_neighbor to point directly to the host; resends will
+        *  be sent through the tunnel, while a few neighbor discoveries are
+        *  still tried towards the remote peer, until something comes back
+        *  directly to acknowledge contact.  When something comes back over
+        *  a direct route, this should be reported to the neighbor cache
+        *  through received_peer_direct_acknowledgement() so it can
+        *  learn that future traffic can be directly sent to the peer.
+        *  
+        * A common case for which this holds is TCP connection setup:
+        *  1. Packets with SYN are sent repeatedly if need be
+        *  2. Packets with ACK stand out in a crowd
+        * So, when TCP packets with SYN are sent, their lookup_neighbor()
+        * invocation can set the playful flag; the first attempt will go
+        * directly to the peer and hopefully initiate direct contact; if
+        * not, future attempts are sent through the tunnel.  When the
+        * remote peer accepts, it will return a packet with ACK set over
+        * the direct route, which is enough reason to signal success to
+        * the neighbor cache.  Interestingly, the first reply message
+        * from a TCP server will generally hold the SYN flag as well, so
+        * if the remote 6bed4 stack is also playful about TCP, it is
+        * likely to send it back directly even if it received the opening
+        * SYN through the tunnel.  This means that even a blocking NAT on
+        * the remote end will cause a new attempt on return.  As usual,
+        * an ACK from client to server follows, which asserts to the
+        * server that it can talk directly (because, at that time, the
+        * client has picked up on the directly sent SYN+ACK).
+        * 
+        * It is possible for TCP to initiate a connection from both ends
+        * at the same time.  Effectively this splits the SYN+ACK into
+        * two messages, one with SYN and one with ACK.  It may happen
+        * that direct contact is not correctly established if that is
+        * done, but the follow-up neighbor discovery that follow on a
+        * playful SYN that does not return an ACK over the direct route
+        * will establish NAT-passthrough if it is technically possible.
+        * A similar thing occurs when the initial SYN is lost for some
+        * reason. 
+        */
+       public InetSocketAddress lookup_neighbor (byte addr [], int addrofs, boolean playful) {
+               if (!is6bed4 (addr, addrofs)) {
+                       return public_server;
+               }
+               boolean puppy = false;
+               Neighbor ngb;
+               byte seeker_key [] = new byte [6];
+               address2key (addr, addrofs, seeker_key);        //TODO// Only used for seeker creation?
+               int seeker_hash = key2hash (seeker_key);
+               synchronized (neighbor_cache) {
+                       ngb = neighbor_cache.get (seeker_hash);
+                       if (ngb == null) {
+                               ngb = new Neighbor (seeker_key, playful);       //TODO// Possibly seeker.clone ()
+                               neighbor_cache.put (seeker_hash, ngb);
+                               puppy = true;
+                       }
+               }
+               switch (ngb.state) {
+               case ATTEMPT1:
+               case ATTEMPT2:
+               case ATTEMPT3:
+               case FAILED:
+               case INTRANSIT:
+                       //
+                       // No usable entry exists, but no new attempts are needed.
+                       // Simply return the default router's address.
+                       //
+                       // If the new entry was just inserted into the neighbor
+                       // cache for this thread, then enqueue into ATTEMPT1, part
+                       // of which is sending a neighbor solicitation.  However,
+                       // if the  request is made by a playful puppy, then skip
+                       // sending the Neighbor Discovery upon insertion into the
+                       // ATTEMPT1 queue; the 6bed4 stack will instead send the
+                       // initial message directly to the peer.  To that end, return
+                       // the direct neighbor instead of the safe default of the
+                       // public server.  Note that a repeat of the packet will
+                       // no longer count as a puppy, so this is done only once.
+                       if (puppy) {
+                               queues [ATTEMPT1].enqueue (ngb, playful);
+                               if (playful) {
+                                       return ngb.target;
+                               }
+                       }
+                       return public_server;
+               case STALE:
+                       //
+                       // The neighbor has been reached directly before; assume this
+                       // is still possible (continue into REACHABLE) but also send a
+                       // neighbor solicitation to keep the lines open.
+                       solicit_neighbor (ngb);
+               case REACHABLE:
+                       //
+                       // The neighbor is known to be available for direct contact.
+                       // There is no work to be done to that end.
+                       return ngb.target;
+               default:
+                       return public_server;
+               }
+       }
+       
+       /* Send a Neighbor Solicitation to the peer.  When this succeeds at
+        * penetrating to the remote peer's 6bed4 stack, then a Neighbor
+        * Advertisement will return, and be reported through the method
+        * NeighborCache.received_peer_direct_acknowledgement() so the
+        * cache can update its state, and support direct sending to the
+        * remote peer.  
+        * 
+        * Even though this kind of message may be sent after failed
+        * playful attempts, it still does not rule out the possibility
+        * of any such attempts returning, so the "playful" flag for
+        * the Neighbor solicited is not changed.
+        */
+       public void solicit_neighbor (Neighbor ngb) {
+               byte key [] = new byte [6];
+               socketaddress2key (ngb.target, key);
+               byte ngbsol [] = new byte [72];
+               //
+               // IPv6 header:
+               ngbsol [0] = 0x60;
+               ngbsol [1] = ngbsol [2] = ngbsol [3] = 0;
+               ngbsol [4] = 0; ngbsol [5] = 4 + 28;
+               ngbsol [6] = Utils.IPPROTO_ICMPV6;
+               ngbsol [7] = (byte) 255;
+               Utils.memcp_address (ngbsol, 8, address_6bed4, 0);
+               key2ipv6address (key, ngbsol, 24);
+               int ofs = 40;
+               // ICMPv6 header:
+               ngbsol [ofs++] = (byte) Utils.ND_NEIGHBOR_SOLICIT;
+               ngbsol [ofs++] = 0;
+               ofs += 2;       // Checksum
+               ngbsol [ofs++] =
+               ngbsol [ofs++] =
+               ngbsol [ofs++] =
+               ngbsol [ofs++] = 0x00;
+               key2ipv6address (ngb.key, ngbsol, ofs);
+               ofs += 16;
+               // ICMPv6 Option: Source Link-Layer Address
+               ngbsol [ofs++] = 1;     // SrcLLAddr
+               ngbsol [ofs++] = 1;     // length is 1x 8 bytes
+               for (int i=0; i<6; i++) {
+                       ngbsol [ofs++] = address_6bed4_key [i];
+               }
+               int csum = Utils.checksum_icmpv6 (ngbsol, 0);
+               ngbsol [42] = (byte) ((csum >> 8) & 0xff);
+               ngbsol [43] = (byte) ( csum       & 0xff);
+               try {
+                       DatagramPacket pkt = new DatagramPacket (ngbsol, ngbsol.length, ngb.target);
+                       uplink.send (pkt);
+               } catch (IOException ioe) {
+                       ;
+               }
+       }
+       
+       /* The TunnelServer reports having received a neighbor advertisement
+        * by calling this function.  This may signal the cache that the
+        * corresponding cache entry needs updating.
+        * 
+        * The "playful" flag is used to indicate that the acknowledgement
+        * arrived as part of a playful attempt in lookup_neighbor().  This
+        * information is used to determine if resends have taken place
+        * through the tunnel server.  If such resends were sent, then it
+        * is not safe anymore to assume that a one-to-one exchange has
+        * taken place, and it becomes necessary to rely on the generic
+        * mechanism of Neighbor Discovery to detect the ability for direct
+        * contact between peers.
+        */
+       public void received_peer_direct_acknowledgement (byte addr [], int addrofs, boolean playful) {
+               Neighbor ngb;
+               byte key [] = new byte [6];
+               address2key (addr, addrofs, key);
+               int seeker_hash = key2hash (key);
+               synchronized (neighbor_cache) {
+                       ngb = neighbor_cache.get (seeker_hash);
+               }
+               if (ngb == null) {
+                       return;
+               }
+               if (playful && !ngb.playful) {
+                       return;
+               }
+               int nst = ngb.state;
+               switch (nst) {
+               case ATTEMPT1:
+               case ATTEMPT2:
+               case ATTEMPT3:
+               case STALE:
+                       //
+                       // There is an entry waiting to be resolved.
+                       // Apply the lessons learnt to that entry.
+                       // Be careful -- another thread may be trying
+                       // the same, so we use the dequeue function
+                       // that checks again before proceeding.
+                       if (queues [nst].dequeue (ngb)) {
+                               //
+                               // Only one thread will continue here, having dequeued the neighbor
+                               queues [REACHABLE].enqueue (ngb);
+                       }
+                       break;
+               case REACHABLE:
+               case FAILED:
+                       //
+                       // A persistent state has been reached, and any
+                       // advertisements coming in have missed their
+                       // window of opportunity.  Note that this may be
+                       // a sign of attempted abuse.  So ignore it.
+                       break;
+               case INTRANSIT:
+                       //
+                       // Some thread is already working on this neighbor, so drop
+                       // this extra notification.
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       
+       /* The Neighbor class represents individual entries in the NeighborCache.
+        * Each contains an IPv6 address as 16 bytes, its state, and a timeout for
+        * its current state queue.
+        * 
+        * Most of this class is storage, and is publicly accessible by the
+        * other components in this class/file.
+        * 
+        * Note that the Neighbor class serves in the hashtable as a key and value;
+        * but as a key, the only input used are the 6 bytes with the remote peer's
+        * IPv4 address and UDP port.  This means that another object can easily be
+        * constructed as a search entry, to resolve into the full entry.
+        * 
+        * TODO: Inner class -- does that mean that the context is copied into this object?  That'd be wasteful!
+        */
+       static class Neighbor {
+               protected int state;
+               protected InetSocketAddress target;
+               protected long timeout;
+               byte key [];
+               boolean playful;
+               
+               /* The hashCode for this object depends solely on the key value.
+                * Integrate all the bits of the key for optimal spreading.  The
+                * hashCode may only differ if the equals() output below also
+                * differs.
+                */
+               public int hashCode () {
+                       return key2hash (key);
+               }
+               
+               /* The equals output determines that two Neighbor objects are
+                * the same if their key matches; that is, if their remote
+                * IPv4 address and UDP port is the same.  It is guaranteed
+                * that the hashCode is the same for two equal objects; if two
+                * objects are unequal then their hashCode is *likely* to differ,
+                * but not guaranteed. 
+                */
+               public boolean equals (Object oth) {
+                       try {
+                               Neighbor other = (Neighbor) oth;
+                               for (int i=0; i<5; i++) {
+                                       if (this.key [i] != ((Neighbor) other).key [i]) {
+                                               return false;
+                                       }
+                               }
+                               return true;
+                       } catch (Throwable thr) {
+                               return false;
+                       }
+               }
+               
+               /* Construct a new Neighbor based on its 6-byte key */
+               public Neighbor (byte fromkey [], boolean create_playfully) {
+                       key = new byte [6];
+                       for (int i=0; i<6; i++) {
+                               key [i] = fromkey [i];
+                       }
+                       state = NeighborCache.ATTEMPT1;
+                       target = key2socketaddress (key);
+                       playful = create_playfully;
+               }
+       }
+       
+       /* The NeighborQueue inner class represents a timer queue.  The objects in this
+        * queue each have their own timeouts, as specified by state_timing_millis.
+        * New entries are attached to the back, timeouts are picked up at the front.
+        * 
+        * The queues have understanding of the various states, and how to handle
+        * expiration.
+        */
+       class NeighborQueue extends Thread {
+               
+               private NeighborCache cache;
+               private int whoami;
+               private Queue <Neighbor> queue;
+               
+               public NeighborQueue (NeighborCache owner, int mystate) {
+                       cache = owner;
+                       whoami = mystate;
+                       queue = new ArrayDeque <Neighbor> ();
+                       this.start ();
+               }
+               
+               public void run () {
+                       while (!isInterrupted ()) {
+                               //
+                               // First, fetch the head element (or wait for one to appear).
+                               // Sample its timeout.  Even if the head disappears later on,
+                               // any further entries are certain to have a later timeout.
+                               long timeout;
+                               synchronized (queue) {
+                                       Neighbor head = null;
+                                       while (head == null) {
+                                               head = queue.peek ();
+                                               if (head == null) {
+                                                       try {
+                                                               queue.wait ();
+                                                       } catch (InterruptedException ie) {
+                                                               return;
+                                                       }
+                                               }
+                                       }
+                                       if (head.state == whoami) {
+                                               timeout = 0;
+                                       } else {
+                                               timeout = head.timeout;
+                                       }
+                               }
+                               //
+                               // Now wait until the timeout expires.  This is not done in
+                               // a synchronous fashion, so the queue can be changed as
+                               // desired by other threads.  Note that misplaced items
+                               // in the queue will be removed by the code further down
+                               // before this thread can find rest.
+                               if (timeout != 0) {
+                                       try {
+                                               sleep (timeout - System.currentTimeMillis ());
+                                       } catch (InterruptedException ie) {
+                                               return;
+                                       }
+                               }
+                               //
+                               // Now the time has come to remove the head elements that
+                               // have expired.  If nothing happened to the head of the
+                               // queue, then this one should at least be removed, but
+                               // following entries may just as well need removal.  Do
+                               // this in a queue-synchronous manner to stop other threads
+                               // from altering the queue.
+                               long now = System.currentTimeMillis ();
+                               Queue <Neighbor> removed = new ArrayDeque <Neighbor> ();
+                               synchronized (queue) {
+                                       Neighbor head = queue.peek ();
+                                       while ((head != null) && ((head.state != whoami) || (head.timeout <= now))) {
+                                               head = queue.remove ();
+                                               if (head.state == whoami) {
+                                                       removed.offer (head);
+                                               } // else, misplaced item that can be silently dropped
+                                               head = queue.peek ();
+                                       }
+                               }
+                               //
+                               // We now have a few elements in a temporary removed list;
+                               // handle those by inserting them into their next lifecycle
+                               // state (possibly meaning, destroy them).
+                               switch (whoami) {
+                               case ATTEMPT3:
+/* TODO: Why would we want to remove the remote socket address?
+                                       //
+                                       // Remove the remote socket address, and continue with
+                                       // promotion to the next lifecycle state
+                                       for (Neighbor ngb : removed) {
+                                               ngb.target = null;
+                                       }
+ */
+                               case ATTEMPT1:
+                               case ATTEMPT2:
+                               case REACHABLE:
+                                       //
+                                       // Promote the neighbor to its next lifecycle state
+                                       for (Neighbor ngb : removed) {
+                                               queues [whoami + 1].enqueue (ngb);
+                                       }
+                                       break;
+                               case FAILED:
+                               case STALE:
+                                       //
+                                       // Remove the neighbor from the cache
+                                       for (Neighbor ngb : removed) {
+                                               cache.remove_from_cache (ngb);
+                                       }
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+               }
+
+               /* Insert a new element into the queue.  If this is the first
+                * entry, be sure to notify the thread that may be waiting for
+                * something to chew on.
+                * Note that an entry may be enqueued only once, use dequeue
+                * to remove it from a possible former queue.
+                */
+               public void enqueue (Neighbor ngb) {
+                       enqueue (ngb, false);
+               }
+               
+               /* Insert a new element into the queue.  If this is the first
+                * entry, be sure to notify the thread that may be waiting for
+                * something to chew on.
+                * Note that an entry may be enqueued only once, use dequeue
+                * to remove it from a possible former queue.
+                * The flag "no_action" requests that the side-effects that
+                * may take place after inserting the element in the queue
+                * should be skipped.
+                */
+               public void enqueue (Neighbor ngb, boolean no_action) {
+                       //
+                       // Initialise the neighbor for this queue
+                       ngb.state = whoami;
+                       ngb.timeout = System.currentTimeMillis () + NeighborCache.state_timing_millis [whoami];
+                       //
+                       // Insert the Neighbor into the queue
+                       synchronized (queue) {
+                               boolean wasEmpty = queue.isEmpty ();
+                               queue.offer (ngb);
+                               if (wasEmpty) {
+                                       queue.notifyAll ();
+                               }
+                       }
+                       //
+                       // Skip the side-effect of insertion is so desired.
+                       if (no_action) {
+                               return;
+                       }
+                       //
+                       // If this queue represents waiting for a reply to Neighbor
+                       // Solicitation, then solicit the neighbor.  This is not
+                       // synchronous, because networking does not directly modify
+                       // the queue, is slow and is asynchronous in nature anyway.
+                       switch (whoami) {
+                       case ATTEMPT1:
+                       case ATTEMPT2:
+                       case ATTEMPT3:
+                               cache.solicit_neighbor (ngb);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               /* Remove an element from the queue without awaiting timer
+                * expiration.  This is only of interest when incoming traffic
+                * follows a direct route, while our previous attempts ran into
+                * FAILED because the remote didn't have its ports open for us yet.
+                * In addition, this may be used when a timer has gone STALE.
+                * Note that expiration timers on the queue can continue to run,
+                * they may simply find that the first element has not timed out
+                * and another cycle is needed for what is then the head.
+                * The state and timeout of the neighbor entry will not be modified
+                * by this call, but future enqueueing may do that nonetheless.
+                * The function returns success; that is, it tells the caller if
+                * the object was indeed found in the queue. 
+                * 
+                * *** Implementation note ***
+                * 
+                * Dequeueing is expensive, as it involves going through the list
+                * of stored items.  Keeping the structures up to date to speedup
+                * this operation is hardly more pleasant.  So, we'll use a trick.
+                * We simply won't dequeue.  This means that queues can hold items
+                * that should have been removed.  As long as objects never cycle
+                * back to this queue before their timers in this queue have
+                * expired, the misplacement of a queue item could be directly
+                * inferred from the state, which does not match the queue's state.
+                * Such misplaced items can be silently removed.  Doing this, the
+                * only remaining task for dequeue is to change the state in an
+                * atomic fashion -- that is, without risk of overlapping similar
+                * operations by other threads.  As originally intended, there
+                * must be only one thread that receives true when removing an
+                * item from a queue that holds it.  The loop that removes the
+                * queue elements from the head also changes to accommodate this
+                * behaviour.    
+                */
+               public boolean dequeue (Neighbor ngb) {
+                       boolean wasthere;
+                       synchronized (queue) {
+                               if (ngb.state != whoami) {
+                                       wasthere = false;
+                               } else {
+                                       ngb.state = NeighborCache.INTRANSIT;
+                                       wasthere = true;
+                               }
+                       }
+                       return wasthere;
+               }
+       }
+
+       
+       /***
+        *** UTILITIES
+        ***/
+       
+       
+       /* Check if an address is a 6bed4 address.
+        */
+       public boolean is6bed4 (byte addr [], int addrofs) {
+               if (Utils.memdiff_halfaddr (addr, addrofs, address_6bed4, 0)) {
+                       return false;
+               }
+               if ((addr [addrofs + 11] != (byte) 0xff) || (addr [addrofs + 12] != (byte) 0xfe)) {
+                       return false;
+               }
+               //TODO// Possibly require that the port number is even
+               return true;
+       }
+       
+       /* Map an address to a 6-byte key in the provided space.  Assume that the
+        * address is known to be a 6bed4 address.
+        */
+       private static void address2key (byte addr [], int addrofs, byte key []) {
+               key [0] = (byte) (addr [addrofs +  8] ^ 0x02);
+               key [1] = addr [addrofs +  9];
+               key [2] = addr [addrofs + 10];
+               key [3] = addr [addrofs + 13];
+               key [4] = addr [addrofs + 14];
+               key [5] = addr [addrofs + 15];
+       }
+
+       /* Map an InetSocketAddress to a 6-byte key in the provided space.
+        */
+       private static void socketaddress2key (InetSocketAddress sa, byte key []) {
+               int port = sa.getPort ();
+               byte addr [] = sa.getAddress ().getAddress ();
+               key [0] = (byte) (port & 0xff);
+               key [1] = (byte) (port >> 8  );
+               key [2] = addr [0];
+               key [3] = addr [1];
+               key [4] = addr [2];
+               key [5] = addr [3];
+       }
+
+       /* Map a key to an IPv6 address, following the 6bed4 structures.
+        */
+       private void key2ipv6address (byte key [], byte addr [], int addrofs) {
+               for (int i=0; i<8; i++) {
+                       addr [addrofs + i] = address_6bed4 [i];
+               }
+               addr [addrofs +  8] = (byte) (key [0] ^ 0x02);
+               addr [addrofs +  9] = key [1];
+               addr [addrofs + 10] = key [2];
+               addr [addrofs + 11] = (byte) 0xff;
+               addr [addrofs + 12] = (byte) 0xfe;
+               addr [addrofs + 13] = key [3];
+               addr [addrofs + 14] = key [4];
+               addr [addrofs + 15] = key [5];
+       }
+
+       /* Map a key to an InetSocketAddress.
+        */
+       private static InetSocketAddress key2socketaddress (byte key []) {
+               int port = (int) (key [0] & 0xff);
+               port = port | (((int) (key [1] << 8)) & 0xff00);
+               byte v4addr [] = new byte [4];
+               v4addr [0] = key [2];
+               v4addr [1] = key [3];
+               v4addr [2] = key [4];
+               v4addr [3] = key [5];
+               try {
+                       Inet4Address remote = (Inet4Address) Inet4Address.getByAddress (v4addr);
+                       return new InetSocketAddress (remote, port);                                            
+               } catch (UnknownHostException uhe) {
+                       throw new RuntimeException ("Internet Error", uhe);
+               }
+       }
+       
+       private static int key2hash (byte key []) {
+               int retval;
+               retval = key [0] ^ key [3];
+               retval <<= 8;
+               retval += key [1] ^ key [4];
+               retval <<= 8;
+               retval += key [2] ^ key [5];
+               return retval;
+       }
+
+}
diff --git a/src/main/java/nl/openfortress/socket6bed4/ServerNode.java b/src/main/java/nl/openfortress/socket6bed4/ServerNode.java
new file mode 100644 (file)
index 0000000..ecbfe86
--- /dev/null
@@ -0,0 +1,769 @@
+package nl.openfortress.socket6bed4;
+
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.SocketException;
+import java.io.IOException;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+
+/** Each ServerNode instance serves a connection to a particular 6bed4
+ * tunnel server.  This is reflected by its superclass, which is the
+ * principal address serviced -- the IPv4 address and UDP port for the
+ * server.
+ *
+ * As part of the service for a node, there is a neighbor cache.  This
+ * may be compared to having a per-network neighbor cache as part of
+ * an operating system.  Lookups may lead to more direct routes than
+ * would otherwise be possible.
+ * 
+ * TODO: Hook up handle_4to6/handle_6to4 methods with receive/send.
+ * TODO: Handle IPv6 addr chg by throwing an IOException subclass.
+ * TODO: Establish NeighborCache with lladdr_6bed4, somehow.
+ */
+public class ServerNode
+extends DatagramSocket {
+
+       private int usecount = 0;
+       protected InetSocketAddress tunserver;
+       protected NeighborCache ngbcache;
+       protected Inet6Address sharedAddress;
+       private Maintainer maintainer;
+       private Worker worker;
+       private BlockingQueue<byte[]> udp_clients [];
+       static byte local_address [] = new byte [16];
+
+       /** Create a connection to a 6bed4 tunnel server, and keep it active.
+        */
+       public ServerNode (InetSocketAddress isa)
+       throws SocketException {
+               super ();
+               maintainer = new Maintainer (this);
+               worker = new Worker (this);
+               tunserver = isa;
+               udp_clients = new BlockingQueue [65536];
+       }
+
+       /** Create a connection to a 6bed4 tunnel server, and keep it active.
+        * This constructor binds locally to the given UDP port, if possible.
+        * This may provide a more predictable IPv6 address, but that depends
+        * on your networking setup.  If you are behind NAT, and/or someone
+        * else is already using the port you will still have a different IPv6
+        * address provided by the 6bed4 server, because it "sees" you on
+        * another UDP port at the outside of NAT.  NAT is NAsTy, we know.
+        * Things may improve when you setup port forwarding, but that would
+        * involve fixating the LAN host that can run this ServerNode at the
+        * given IPv6 address.  And again, it would depend on your NAT.
+        */
+       public ServerNode (InetSocketAddress isa, int port)
+       throws SocketException {
+               super (port);
+               maintainer = new Maintainer (this);
+               worker = new Worker (this);
+               tunserver = isa;
+               udp_clients = new BlockingQueue [65536];
+       }
+
+       
+       /** Teardown the connection to a 6bed4 tunnel server.
+        */
+       public void stop () {
+               worker.interrupt ();
+               maintainer.interrupt ();
+               if (ngbcache != null) {
+                       ngbcache.cleanup ();
+                       ngbcache = null;  /* Intended exceptions on continued use */
+               }
+       }
+
+       /** Increment the use counter */
+       public synchronized void useMore () {
+               if (usecount++ == 0) {
+                       maintainer.start ();
+                       worker.start ();
+               }
+       }
+
+       /** Decrement the use counter, return if it is now useless */
+       public synchronized boolean useLess () {
+               if (--usecount == 0) {
+                       maintainer.interrupt ();
+                       worker.interrupt ();
+                       return true;
+               }
+               return false;
+       }
+
+
+       /* Register a UDP client for a given port on the default
+        * IPv6 address as handed out by getDefault6bed4Address ().
+        * This is a claim to that port, and a SocketException is
+        * raised if it is not available.
+        */
+       public void registerDatagramClient (int port)
+       throws SocketException {
+               synchronized (udp_clients) {
+                       if (udp_clients [port] != null) {
+                               throw new SocketException ("port taken");
+                       }
+                       udp_clients [port] = new ArrayBlockingQueue<byte[]> (10);
+               }
+       }
+
+       /* Unregister a client from a port that it has claimed.
+        */
+       public void unregisterDatagramClient (int port)
+       throws SocketException {
+               synchronized (udp_clients) {
+                       if (udp_clients [port] == null) {
+                               throw new SocketException ("port not in use");
+                       }
+                       //TODO// possibly cleanup the Queue before letting go
+                       udp_clients [port] = null;
+               }
+       }
+
+       /** Return the shared 6bed4 address, usable for anyone who
+        * wants to allocate ports for UDP/TCP through either a
+        * Socket6bed4 or a DatagramSocket6bed4.  This routine will
+        * block until an address has been obtained.
+        * 
+        * To allocate recipients on the shared address, use
+        * registerDatagramClient(port) and, hopefully one day,
+        * registerStreamClient(port).  Raw access is not possible
+        * with this address; for that, use getUnique6bed4Address()
+        * as long as supply lasts.
+        */
+       public Inet6Address getShared6bed4Address () {
+               synchronized (maintainer) {
+                       while (sharedAddress == null) {
+                               try {
+                                       maintainer.wait ();
+                               } catch (InterruptedException ie) {
+                                       return null;
+                               }
+                       }
+               }
+               return sharedAddress;
+       }
+
+       /** Return a unique 6bed4 address, which is available for
+        * raw communication over IPv6.  Return null if no addresses
+        * are available anymore.
+        * Addresses returned from this function can be used as
+        * raw sockets.  They share the communication channel setup
+        * for getShared6bed4Address() but the contents of the IPv6
+        * packet will not be implemented, but rather relayed
+        * directly to the provided BlockingQueue instance.
+        * TODO: Sending, NxtHdr, and how to deal with security.
+        * TODO: Implement.  Consider security implications.
+        */
+       public synchronized Inet6Address getUnique6bed4Address (BlockingQueue<byte[]> recv_TODO_OR_KEEP_INTERNAL) {
+               //TODO// Register the address in the ConnectionPool so getServerNode() will work!
+               return null;
+       }
+       
+       /** Pull an element from the BlockingQueue for the given
+        * Datagram port.  Block if no input is available.
+        * The timeout value is in milli-seconds, zero
+        * representing infinity (just as with setSoTimeout). 
+        */
+       public byte[] receive_datagram (int port, int timeout)
+       throws SocketException {
+               if (udp_clients [port] == null) {
+                       throw new SocketException ("port is not allocated");
+               }
+               try {
+                       if (timeout > 0) {
+                               return udp_clients [port].poll (timeout, TimeUnit.MILLISECONDS);
+                       } else {
+                               return udp_clients [port].take ();
+                       }
+               } catch (InterruptedException ie) {
+                       return null;
+               }
+       }
+
+       /** Acknowledge a playful exchange to the Neighbor Cache.
+        * This is a complex protocol, involving an interaction
+        * with the application layer, to confirm initial messages
+        * that support resends.  Please see the documentation in
+        * NeighborCache and DatagramSocket6bed4 for details.
+        */
+       public void acknowledge_playful (byte addr[], int ofs) {
+               if ((ngbcache != null) && (ofs + 16 <= addr.length)) {
+                       ngbcache.received_peer_direct_acknowledgement (addr, ofs, true);
+               }
+       }
+
+       /** Lookup a neighbor.  Forwards to NeighborCache.
+        */
+       public InetSocketAddress lookup_neighbor (Inet6Address ia, boolean playful) {
+               while (ngbcache == null) {
+                       synchronized (maintainer) {
+                               try {
+                                       maintainer.wait ();
+                               } catch (InterruptedException ie) {
+                                       return null;
+                               }
+                       }
+               }
+               return ngbcache.lookup_neighbor (ia.getAddress (), 0, playful);
+       }
+
+
+       public void handle_4to6_nd (byte pkt [], int pktlen, SocketAddress src)
+       throws IOException, SocketException {
+               if (Utils.checksum_icmpv6 (pkt, 0) != Utils.fetch_net16 (pkt, Utils.OFS_ICMP6_CSUM)) {
+                       // Checksum is off
+                       return;
+               }
+               switch (pkt [Utils.OFS_ICMP6_TYPE]) {
+               //
+               // Handle Router Solicitation by dropping it -- this is a peer, not a router
+               case Utils.ND_ROUTER_SOLICIT:
+                       return;
+               //
+               // Handle Router Advertisement as an addressing offering -- but validate the sender
+               case Utils.ND_ROUTER_ADVERT:
+                       if (pktlen < 40 + 16 + 16) {
+                               // Too short to contain IPv6, ICMPv6 RtrAdv and Prefix Option
+                               return;
+                       }
+                       if ((pkt [Utils.OFS_ICMP6_DATA+1] & 0x80) != 0x00) {
+                               // Indecent proposal to use DHCPv6 over 6bed4
+                               return;
+                       }
+                       if (Utils.memdiff_addr (pkt, Utils.OFS_IP6_SRC, Utils.router_linklocal_address, 0)) {
+                               // Sender is not 0xfe80::/128
+                               return;
+                       }
+                       if (Utils.memdiff_halfaddr (pkt, Utils.OFS_IP6_DST, Utils.router_linklocal_address, 0)) {
+                               // Receiver address is not 0xfe80::/64
+                               return;
+                       }
+                       if ((pkt [Utils.OFS_IP6_DST + 11] != (byte) 0xff) || (pkt [Utils.OFS_IP6_DST + 12] != (byte) 0xfe)) {
+                               // No MAC-based destination address ending in ....:..ff:fe..:....
+                               return;
+                       }
+                       //TODO// Check if offered address looks like a multicast-address (MAC byte 0 is odd)
+                       //TODO// Check Secure ND on incoming Router Advertisement?
+                       //
+                       // Having validated the Router Advertisement, process its contents
+                       int destprefix_ofs = 0;
+                       int rdofs = Utils.OFS_ICMP6_DATA + 12;
+                       //TODO:+4_WRONG?// while (rdofs <= ntohs (v4v6plen) + 4) { ... }
+                       while (rdofs + 4 < pktlen) {
+                               if (pkt [rdofs + 1] == 0) {
+                                       return;   /* zero length option */
+                               }
+                               if (pkt [rdofs + 0] != Utils.ND_OPT_PREFIX_INFORMATION) {
+                                       /* skip to next option */
+                               } else if (pkt [rdofs + 1] != 4) {
+                                       return;   /* bad length field */
+                               } else if (rdofs + (pkt [rdofs + 1] << 3) > pktlen + 4) {
+                                       return;   /* out of packet length */
+                               } else if ((pkt [rdofs + 3] & (byte) 0xc0) != (byte) 0xc0) {
+                                       /* no on-link autoconfig prefix */
+                               } else if (pkt [rdofs + 2] != 64) {
+                                       return;
+                               } else {
+                                       destprefix_ofs = rdofs + 16;
+                               }
+                               rdofs += (pkt [rdofs + 1] << 3);
+                       }
+                       if (destprefix_ofs > 0) {
+                               for (int i=0; i<8; i++) {
+                                       local_address [0 + i] = pkt [destprefix_ofs + i];
+                                       local_address [8 + i] = pkt [Utils.OFS_IP6_DST + 8 + i]; 
+                               }
+                               sharedAddress = (Inet6Address) InetAddress.getByAddress (local_address);
+                               //TODO// syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
+                               // update_local_netconfig ();
+                               if (ngbcache != null) {
+                                       ngbcache.cleanup ();
+                               }
+                               ngbcache = new NeighborCache (this, tunserver, local_address);
+                               maintainer.have_local_address (true);
+                               synchronized (maintainer) {
+                                       maintainer.notifyAll ();
+                               }
+                               // Log.i (TAG, "Assigned address to tunnel");
+                       }
+                       return;
+               //
+               // Neighbor Solicitation is an attempt to reach us peer-to-peer, and should be responded to
+               case Utils.ND_NEIGHBOR_SOLICIT:
+                       if (pktlen < 24) {
+                               // Too short to make sense
+                               return;
+                       }
+                       if (Utils.memdiff_addr (pkt, Utils.OFS_ICMP6_NGBSOL_TARGET, local_address, 0)) {
+                               // Neighbor Solicitation not aimed at me
+                               return;
+                       }
+                       if ((ngbcache == null) || !ngbcache.is6bed4 (pkt, Utils.OFS_IP6_SRC)) {
+                               // Source is not a 6bed4 address
+                               return;
+                       }
+                       //
+                       // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
+                       // Not checked here: LLaddr in NgbSol -- simply send back to IPv6 src address
+                       //
+                       Utils.memcp_address (pkt, Utils.OFS_IP6_DST, pkt, Utils.OFS_IP6_SRC);
+                       Utils.memcp_address (pkt, Utils.OFS_IP6_SRC, local_address, 0);
+                       pkt [Utils.OFS_ICMP6_TYPE] = Utils.ND_NEIGHBOR_ADVERT;
+                       pkt [Utils.OFS_IP6_PLEN + 0] = 0;
+                       pkt [Utils.OFS_IP6_PLEN + 1] = 8 + 16;
+                       pkt [Utils.OFS_ICMP6_NGBADV_FLAGS] = 0x60;      // Solicited, Override
+                       // Assume that Utils.OFS_ICMP6_NGBADV_TARGET == Utils.OFS_ICMP6_NGBSOL_TARGET
+                       int csum = Utils.checksum_icmpv6 (pkt, 0);
+                       pkt [Utils.OFS_ICMP6_CSUM + 0] = (byte) (csum >> 8  );
+                       pkt [Utils.OFS_ICMP6_CSUM + 1] = (byte) (csum & 0xff);
+                       DatagramPacket replypkt = new DatagramPacket (pkt, 0, 40 + 8 + 16, src);
+                       super.send (replypkt);
+
+                       //
+                       // TODO:OLD Replicate the message over the tunnel link
+                       //
+                       // We should attach a Source Link-Layer Address, but
+                       // we cannot automatically trust the one provided remotely.
+                       // Also, we want to detect if routes differ, and handle it.
+                       //
+                       // 0. if no entry in the ngb.cache
+                       //    then use 6bed4 server in ND, initiate ngb.sol to src.ll
+                       //         impl: use 6bed4-server lladdr, set highest metric
+                       // 1. if metric (ngb.cache) < metric (src.ll)
+                       //    then retain ngb.cache, send Redirect to source
+                       // 2. if metric (ngb.cache) > metric (src.ll)
+                       //    then retain ngb.cache, initiate ngb.sol to src.ll
+                       // 3. if metric (ngb.cache) == metric (src.ll)
+                       //    then retain ngb.cache
+                       //
+                       //TODO// Handle Utils.ND_NEIGHBOR_SOLICIT (handle_4to6_nd)
+                       return;
+               //
+               // Neighbor Advertisement may be in response to our peer-to-peer search
+               case Utils.ND_NEIGHBOR_ADVERT:
+       //
+       // Process Neighbor Advertisement coming in over 6bed4
+       // First, make sure it is against an item in the ndqueue
+                       //
+                       // Validate the Neighbor Advertisement
+                       if (pktlen < 64) {
+                               // Packet too small to hold ICMPv6 Neighbor Advertisement
+                               return;
+                       }
+                       if ((pkt [Utils.OFS_ICMP6_TYPE] != Utils.ND_NEIGHBOR_ADVERT) || (pkt [Utils.OFS_ICMP6_CODE] != 0)) {
+                               // ICMPv6 Type or Code is wrong
+                               return;
+                       }
+                       if ((ngbcache == null) || (!ngbcache.is6bed4 (pkt, Utils.OFS_IP6_SRC)) || (!ngbcache.is6bed4 (pkt, Utils.OFS_IP6_DST))) {
+                               // Source or Destination IPv6 address is not a 6bed4 address
+                               return;
+                       }
+                       if (Utils.memdiff_addr (pkt, Utils.OFS_IP6_SRC, pkt, Utils.OFS_ICMP6_NGBADV_TARGET)) {
+                               // NgbAdv's Target Address does not match IPv6 source
+                               return;
+                       }
+                       //
+                       // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
+                       //
+                       ngbcache.received_peer_direct_acknowledgement (pkt, Utils.OFS_ICMP6_NGBADV_TARGET, false);
+                       return;
+               //
+               // Route Redirect messages are not supported in 6bed4 draft v01
+               case Utils.ND_REDIRECT:
+                       return;
+               }
+       }
+
+       /* Forward an IPv6 packet, wrapped into UDP and IPv4 in the 6bed4
+        * way, as a pure IPv6 packet over the tunnel interface.  This is
+        * normally a simple copying operation.  One exception exists for
+        * TCP ACK packets; these may be in response to a "playful" TCP SYN
+        * packet that was sent directly to the IPv4 recipient.  This is a
+        * piggyback ride of the opportunistic connection efforts on the
+        * 3-way handshake for TCP, without a need to modify the packets!
+        * The only thing needed to make that work is to report success
+        * back to the Neighbor Cache, in cases when TCP ACK comes back in
+        * directly from the remote peer.
+        * 
+        * Note that nothing is stopping an ACK packet that is meaningful
+        * to us from also being a SYN packet that is meaningful to the
+        * remote peer.  We will simply do our thing and forward any ACK
+        * to the most direct route we can imagine -- which may well be
+        * the sender, _especially_ since we opened our 6bed4 port to the
+        * remote peer when sending our playful initial TCP packet.
+        * 
+        * Observing the traffic on the network, this may well look like
+        * magic!  All you see is plain TCP traffic crossing over directly
+        * if it is possible --and bouncing one or two packets through the
+        * tunnel otherwise-- and especially in the case where it can work
+        * directly it will be a surprise.  Servers are therefore strongly
+        * encouraged to setup port forwarding for their 6bed4 addresses,
+        * or just open a hole in full cone NAT/firewall setups.  This will
+        * mean zero delay and zero bypasses for 6bed4 on the majority of
+        * TCP connection initiations between 6bed4 peers!
+        */
+       public void handle_4to6_plain (byte pkt [], int pktlen)
+       throws IOException {
+               //
+               // Check if the designated queue for this packet exists
+               int port = Utils.fetch_net16 (pkt, Utils.OFS_UDP6_DSTPORT);
+               //TODO// Handling for udp_clients: only for UDP-over-IPv6, only to the default address
+               BlockingQueue queue = udp_clients [port];
+               if (queue == null) {
+                       return;
+               }
+               // The buffer is already full -- drop the oldest element
+               if (queue.remainingCapacity () == 0) {
+                       /* (void) */ queue.poll ();
+               }
+               //
+               // If this is a successful peering attempt, that is, a tcpack packet, report that back
+               // Note that the UDP/IPv4 source has already been validated against the IPv6 source
+               boolean tcpack = (pktlen >= 40 + 20) && (pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_TCP) && ((pkt [Utils.OFS_TCP6_FLAGS] & Utils.TCP_FLAG_ACK) != 0x00);
+               if (tcpack) {
+                       if ((ngbcache != null) && ngbcache.is6bed4 (pkt, Utils.OFS_IP6_SRC)) {
+                               ngbcache.received_peer_direct_acknowledgement (pkt, Utils.OFS_IP6_SRC, true);
+                       }
+               }
+               //
+               // Enqueue the packet in the designated client queue
+               //TODO// Select whether this is UDP/TCP/??? for the shared address, or raw for unique addresses!
+               queue.offer (pkt);
+       }
+       
+       /** Handle a 6bed4 packet that is being stripped and passed on as
+        * an IPv6 packet.  Returns true if the packet is suitable to be
+        * relayed as IPv6.
+        */
+       public void handle_4to6 (DatagramPacket datagram)
+       throws IOException {
+               byte pkt [] = datagram.getData ();
+               int pktlen = datagram.getLength ();
+
+               if (pktlen < 40) {
+                       return;
+               }
+               if ((pkt [0] & (byte) 0xf0) != 0x60) {
+                       return;
+               }
+               validate_originator (pkt, (InetSocketAddress) datagram.getSocketAddress ());
+               if ((pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_ICMPV6) && (pkt [Utils.OFS_ICMP6_TYPE] >= Utils.ND_LOWEST) && (pkt [Utils.OFS_ICMP6_TYPE] <= Utils.ND_HIGHEST)) {
+                       //
+                       // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
+                       handle_4to6_nd (pkt, pktlen, datagram.getSocketAddress ());
+                       return;
+               } else {
+                       //
+                       // Plain Unicast or Plain Multicast (both may enter)
+                       handle_4to6_plain (pkt, pktlen);
+                       return;
+               }
+       }
+
+       public void validate_originator (byte pkt [], InetSocketAddress originator)
+       throws IOException {
+/* TODO: validate originator address
+               if (tunserver.equals (originator)) {
+                       return;
+               }
+               if (memdiff_halfaddr (pkt, Utils.OFS_IP6_SRC, router_linklocal, 0) && ((local_address == null) || memdiff_halfaddr (pkt, Utils.OFS_IP6_SRC, local_address, 8))) {
+                       throw new IOException ("Incoming 6bed4 uses bad /64 prefix");
+               }
+               int port = (pkt [Utils.OFS_IP6_SRC + 8] ^ 0x02) & 0xff;
+               port = (port | (pkt [Utils.OFS_IP6_SRC + 9] << 8) & 0xffff;
+               if (originator.getPort () != port) {
+                       throw new IOException ("Incoming 6bed4 uses ")
+               }
+*/
+       }
+
+       /* This routine passes IPv6 traffic from the tunnel interface on
+        * to the 6bed4 interface where it is wrapped into UDP and IPv4.
+        * The only concern for this is where to send it to -- should it
+        * be sent to the tunnel server, or directly to the peer?  The
+        * Neighbor Cache is consulted for advise.
+        * 
+        * A special flag exists to modify the behaviour of the response
+        * to this inquiry.  This flag is used to signal that a first
+        * packet might be tried directly, which should be harmless if
+        * it fails and otherwise lead to optimistic connections if:
+        *  1. the packet will repeat upon failure, and
+        *  2. explicit acknowledgement can be reported to the cache
+        * This is the case with TCP connection setup; during a SYN,
+        * it is possible to be playful and try to send the first
+        * packet directly.  A TCP ACK that returns directly from the
+        * sender indicates that return traffic is possible, which is
+        * then used to update the Neighbor Cache with positivism on
+        * the return route.
+        */
+       public void handle_6to4_plain_unicast (byte pkt [], int pktlen)
+       throws IOException {
+               InetSocketAddress target;
+               if ((ngbcache != null) && ngbcache.is6bed4 (pkt, 24)) {
+                       boolean tcpsyn = (pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_TCP) && ((pkt [Utils.OFS_TCP6_FLAGS] & Utils.TCP_FLAG_SYN) != 0x00);
+                       target = ngbcache.lookup_neighbor (pkt, 24, tcpsyn);
+               } else {
+                       target = tunserver;
+               }
+       }
+       
+       public void handle_6to4_nd (byte pkt [], int pktlen)
+       throws IOException {
+               throw new RuntimeException ("You should not be trying to send ND over the 6bed4 interface");
+/* TODO: Entire routine can probably be removed
+               switch (pkt [Utils.OFS_ICMP6_TYPE]) {
+               //
+               // Handle Router Solicitation by answering it with the local configuration
+               case Utils.ND_ROUTER_SOLICIT:
+                       int ofs = Utils.OFS_ICMP6_TYPE;
+                       pkt [ofs++] = Utils.ND_ROUTER_ADVERT;           // type
+                       pkt [ofs++] = 0;                                        // code
+                       ofs += 2;                                                       // checksum
+                       pkt [ofs++] = 0;                                        // hop limit -- unspecified
+                       pkt [ofs++] = 0x18;                                     // M=0, O=0, H=0, Prf=11=Low, Reserved=0
+                       pkt [ofs++] = 0x00;                                     // Lifetime
+                       pkt [ofs++] = 0x00;                                     // (cont)
+                       for (int i=0; i<8; i++) {
+                               pkt [ofs++] = 0;                                // Reachable time, Retrans timer
+                       }
+                       pkt [ofs-6] = (byte) 0x80;                      // Reachable time := 32s
+                       pkt [ofs-2] = 0x01;                                     // Retrans timer := 0.25s
+                       // Start of Prefix Option
+                       pkt [ofs++] = Utils.ND_OPT_PREFIX_INFORMATION;
+                       pkt [ofs++] = 4;                // Option length = 4 * 8 bytes
+                       pkt [ofs++] = (byte) 128;               // Announce a /64 prefix (TODO: Temporarily /128)
+                       pkt [ofs++] = (byte) 0x80;      // Link-local, No autoconfig, tunnel does the work
+                       for (int i=0; i<8; i++) {
+                               pkt [ofs++] = (byte) 0xff;              // Valid / Preferred Lifetime: Infinite
+                       }
+                       for (int i=0; i<4; i++) {
+                               pkt [ofs++] = 0;                                // Reserved
+                       }
+                       Utils.memcp_address (pkt, ofs, local_address, 0);
+                       ofs += 16;
+                       // End of Prefix Option
+                       Utils.memcp_address (pkt, Utils.OFS_IP6_DST, pkt, Utils.OFS_IP6_SRC);   // dst:=src
+                       Utils.memcp_address (pkt, Utils.OFS_IP6_SRC, local_address, 0);
+                       //TODO// Send packet back to IPv6 downlink
+                       return;
+               //
+               // Handle Router Advertisement by dropping it -- Android is not setup a router
+               case Utils.ND_ROUTER_ADVERT:
+                       return;
+               //
+               // Neighbor Solicitation is not normally sent by the phone due to its /128 on 6bed4
+               case Utils.ND_NEIGHBOR_SOLICIT:
+                       return;
+               //
+               // Neighbor Advertisement is a response to a peer, and should be relayed
+               case Utils.ND_NEIGHBOR_ADVERT:
+                       //TODO// Possibly arrange the peer's receiving address 
+                       handle_6to4_plain_unicast (pkt, pktlen);
+                       return;
+               // Route Redirect messages are not supported in 6bed4 draft v01
+               case Utils.ND_REDIRECT:
+                       return;
+               }
+*/
+       }
+
+       public void handle_6to4 (byte pkt [], int pktlen)
+       throws IOException {
+               if (pktlen < 41) {
+                       return;
+               }
+               if ((pkt [0] & 0xf0) != 0x60) {
+                       return;
+               }
+               if ((pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_ICMPV6) && (pkt [Utils.OFS_ICMP6_TYPE] >= Utils.ND_LOWEST) && (pkt [Utils.OFS_ICMP6_TYPE] <= Utils.ND_HIGHEST)) {
+                       //
+                       // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
+                       handle_6to4_nd (pkt, pktlen);
+               } else if ((pkt [Utils.OFS_IP6_DST+0] != 0xff) && ((pkt [Utils.OFS_IP6_DST+8] & 0x01) == 0x00)) {
+                       //
+                       // Plain Unicast
+                       pkt [Utils.OFS_IP6_HOPS]--;
+                       if (pkt [Utils.OFS_IP6_HOPS] == 0) {
+                               return;
+                       }
+                       handle_6to4_plain_unicast (pkt, pktlen);
+               } else {
+                       //
+                       // Plain Multicast
+                       //TODO: Ignore Multicast for now...
+               }
+       }
+
+       
+       /** The Worker inner class ensures that incoming traffic, and that
+        * includes 6bed4 tunnel management traffic, is processed as soon
+        * as it arrives.  This means that applications need not actively
+        * read input to get it processed.  Furthermore, this approach
+        * simplifies receiving all 6bed4 traffic on one IPv4/UDP socket
+        * and subsequently dispatching it to the various 6bed4 sockets
+        * connected to the same ServerNode, and possibly each applying
+        * their own idea of a socket timeout or other queueing disciplines.
+        * The expense (there always seems to be one) is having to queue
+        * received data between the ServerNode and the 6bed4 socket.  
+        */
+       private class Worker extends Thread {
+               
+               protected ServerNode uplink;
+               
+               /** The thread logic for the Worker inner class comes down
+                * to reading input from the IPv4/UDP socket, seeing if it
+                * needs local handling as a 6bed4 management packet, and
+                * otherwise shipping it off to the queue of a 6bed4 socket
+                * that is read when the user of the DatagramSocket6bed4
+                * wants to grab its input.  Note how this reflects the way
+                * that native UDP ports operate -- packets wait until they
+                * are picked up. 
+                */
+               public void run () {
+                       DatagramPacket dgram4 = null;
+                       while (!isInterrupted ()) {
+                               try {
+                                       dgram4 = new DatagramPacket (new byte [1280 + 28], 1280 + 28);
+                                       uplink.receive (dgram4);
+                                       handle_4to6 (dgram4);
+                               } catch (IOException ioe) {
+                                       ; //TODO// What to do here?  Not sure...
+                               }
+                       }
+               }
+               
+               public Worker (ServerNode owner) {
+                       this.uplink = owner;
+               }
+               
+       }
+
+       /** The Maintainer inner class performs regular maintainence.
+        * One task is to acquire the router address by sending regular
+        * Router Solicitation messages to the 6bed4 tunnel server,
+        * and once it has an address it will regularly send keep-alives.
+        */
+       private class Maintainer extends Thread {
+               
+               /* The time for the next scheduled maintenance: routersol or keepalive.
+                * The milliseconds are always 0 for maintenance tasks.
+                */
+               private long maintenance_time_millis;
+               private int maintenance_time_cycle = 0;
+               private int maintenance_time_cycle_max = 30;
+               private boolean have_lladdr = false;
+               private DatagramPacket keepalive_packet = null;
+               private DatagramSocket uplink;
+       
+               /* Perform the initial Router Solicitation exchange with the public server.
+                */
+               public void solicit_router () {
+                       if (uplink != null) {
+                               try {
+                                       DatagramPacket rtrsol = new DatagramPacket (Utils.router_solicitation, Utils.router_solicitation.length, tunserver);
+                                       uplink.send (rtrsol);
+                               } catch (IOException ioe) {
+                                       /* Network is probably down, so don't
+                                        * throw new RuntimeException ("Network failure", ioe);
+                                        */
+                               }
+                       }
+               }
+               
+               /* Send a KeepAlive packet to the public server.
+                * Note, ideally, we would set a low-enough TTL to never reach it;
+                * after all, the only goal is to open /local/ firewalls and NAT.
+                * Java however, is not capable of setting TTL on unicast sockets.
+                */
+               public void keepalive () {
+                       if ((keepalive_packet != null) && (uplink != null)) {
+                               try {
+                                       uplink.send (keepalive_packet);
+                                       // Log.i (TAG, "Sent KeepAlive (empty UDP) to Tunnel Server");
+                               } catch (IOException ioe) {
+                                       ;       /* Network is probably down; order reconnect to tunnel server */
+                                       have_lladdr = false;
+                               }
+                       }
+               }
+               
+               /* Perform regular maintenance tasks: KeepAlive, and requesting a local address.
+                */
+               public void regular_maintenance () {
+                       if (!have_lladdr) {
+                               solicit_router ();
+                               maintenance_time_cycle <<= 1;
+                               maintenance_time_cycle += 1;
+                               if (maintenance_time_cycle > maintenance_time_cycle_max) {
+                                       maintenance_time_cycle = maintenance_time_cycle_max;
+                               }
+                               //TODO// syslog (LOG_INFO, "Sent Router Advertisement to Public 6bed4 Service, next attempt in %d seconds\n", maintenance_time_cycle);
+                               // Log.i (TAG, "Sent Router Advertisement to Tunnel Server");
+                       } else {
+                               //TODO// syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
+                               keepalive ();
+                               if (have_lladdr) {
+                                       maintenance_time_cycle = maintenance_time_cycle_max;
+                               } else {
+                                       maintenance_time_cycle = 1;
+                               }
+                       }
+                       maintenance_time_millis = System.currentTimeMillis () + 1000 * (long) maintenance_time_cycle;
+               }
+               
+               /* Run the regular maintenance thread.  This involves sending KeepAlives
+                * and possibly requesting a local address through Router Solicitation.
+                */
+               public void run () {
+                       try {
+                               while (!isInterrupted ()) {
+                                       regular_maintenance ();
+                                       sleep (maintenance_time_millis - System.currentTimeMillis());
+                               }
+                       } catch (InterruptedException ie) {
+                               return;
+                       }
+               }
+
+               /* Tell the maintenance routine whether a local address has been setup.
+                * Until this is called, the maintenance will focus on getting one through
+                * regular Router Solicitation messages.  It is possible to revert to this
+                * behaviour by setting the flag to false; this can be useful in case of
+                * changes, for instance resulting from an IPv4 address change.
+                */
+               public void have_local_address (boolean new_setting) {
+                       have_lladdr = new_setting;
+                       if (have_lladdr) {
+                               maintenance_time_cycle = maintenance_time_cycle_max;
+                               maintenance_time_millis = System.currentTimeMillis () + 1000 * maintenance_time_cycle;
+                       }
+               }
+               
+               /* See if a local address has been setup.
+                */
+               public boolean have_local_address () {
+                       return have_lladdr;
+               }
+
+               /* Construct the Maintainer thread.
+                */
+               public Maintainer (DatagramSocket uplink) {
+                       this.uplink = uplink;
+               }
+       }
+       
+
+}
+
diff --git a/src/main/java/nl/openfortress/socket6bed4/Socket6bed4.java b/src/main/java/nl/openfortress/socket6bed4/Socket6bed4.java
new file mode 100644 (file)
index 0000000..71b6b1f
--- /dev/null
@@ -0,0 +1,14 @@
+package nl.openfortress.socket6bed4;
+
+import java.net.Socket;
+
+
+public class Socket6bed4
+extends Socket {
+
+       public Socket6bed4 () {
+               throw new UnsupportedOperationException ("Socket6bed4 cannot be implemented until a TCP implementation is written in Java");
+       }
+
+}
+
diff --git a/src/main/java/nl/openfortress/socket6bed4/Utils.java b/src/main/java/nl/openfortress/socket6bed4/Utils.java
new file mode 100644 (file)
index 0000000..e2d2007
--- /dev/null
@@ -0,0 +1,146 @@
+package nl.openfortress.socket6bed4;
+
+
+class Utils {
+
+       final static byte IPPROTO_ICMPV6 = 58;
+       final static byte IPPROTO_UDP = 17;
+       final static byte IPPROTO_TCP = 6;
+       
+       final static byte ND_ROUTER_SOLICIT   = (byte) 133;
+       final static byte ND_ROUTER_ADVERT    = (byte) 134;
+       final static byte ND_NEIGHBOR_SOLICIT = (byte) 135;
+       final static byte ND_NEIGHBOR_ADVERT  = (byte) 136;
+       final static byte ND_REDIRECT         = (byte) 137;
+       final static byte ND_LOWEST           = (byte) 133;
+       final static byte ND_HIGHEST          = (byte) 137;
+       
+       final static byte ND_OPT_PREFIX_INFORMATION = 3;
+       
+       final static int OFS_IP6_SRC        = 8;
+       final static int OFS_IP6_DST        = 24;
+       final static int OFS_IP6_PLEN       = 4;
+       final static int OFS_IP6_NXTHDR         = 6;
+       final static int OFS_IP6_HOPS           = 7;
+       final static int OFS_IP6_PLOAD          = 40;
+       
+       final static int OFS_UDP6_SRCPORT       = 40 + 0;
+       final static int OFS_UDP6_DSTPORT       = 40 + 2;
+       final static int OFS_UDP6_CSUM      = 40 + 6;
+       final static int OFS_UDP6_PLEN          = 40 + 4;
+       final static int OFS_UDP6_PLOAD         = 40 + 8;
+       
+       final static int OFS_ICMP6_TYPE         = 40 + 0;
+       final static int OFS_ICMP6_CODE         = 40 + 1;
+       final static int OFS_ICMP6_CSUM         = 40 + 2;
+       final static int OFS_ICMP6_DATA         = 40 + 4;
+       
+       final static int OFS_ICMP6_NGBSOL_TARGET = 40 + 8;
+       final static int OFS_ICMP6_NGBADV_TARGET = 40 + 8;
+       final static int OFS_ICMP6_NGBADV_FLAGS  = 40 + 4;
+               
+       final static int OFS_TCP6_FLAGS     = 13;
+       final static int TCP_FLAG_SYN           = 0x02;
+       final static int TCP_FLAG_ACK           = 0x01;
+       
+       final static byte router_solicitation [] = {
+               // IPv6 header
+               0x60, 0x00, 0x00, 0x00,
+               16 / 256, 16 % 256, IPPROTO_ICMPV6, (byte) 255,
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,          // unspecd src
+               (byte) 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // all-rtr tgt
+               // ICMPv6 header: router solicitation
+               ND_ROUTER_SOLICIT, 0, 0x7a, (byte) 0xae,        // Checksum courtesy of WireShark :)
+               // ICMPv6 body: reserved
+               0, 0, 0, 0,
+               // ICMPv6 option: source link layer address 0x0001 (end-aligned)
+               0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
+       };
+
+       final static byte router_linklocal_address [] = { (byte)0xfe,(byte)0x80,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+
+       /* Retrieve an unsigned 16-bit value from a given index in a byte array and
+        * return it as an integer.
+        */
+       public static int fetch_net16 (byte pkt [], int ofs16) {
+               int retval = ((int) pkt [ofs16]) << 8 & 0xff00;
+               retval = retval + (((int) pkt [ofs16+1]) & 0xff);
+               return retval;
+       }
+       
+       /* Calculate the ICMPv6 checksum field in a given IPv6 packet.
+        */
+       public static int checksum_icmpv6 (byte pkt []) {
+               return checksum_icmpv6 (pkt, 0);
+       }
+       public static int checksum_icmpv6 (byte pkt [], int pktofs) {
+               int plen = fetch_net16 (pkt, pktofs + OFS_IP6_PLEN);
+               int nxth = ((int) pkt [pktofs + 6]) & 0xff;
+               // Pseudo header is IPv6 src/dst (included with packet) and plen/nxth and zeroes:
+               int csum = plen + nxth;
+               for (int i=8; i < OFS_IP6_PLOAD + plen; i += 2) {
+                       if (i != OFS_ICMP6_CSUM) {
+                               // Skip current checksum value
+                               csum += fetch_net16 (pkt, pktofs + i);
+                       }
+               }
+               // No need to treat a trailing single byte: ICMPv6 has no odd packet lengths
+               csum = (csum & 0xffff) + (csum >> 16);
+               csum = (csum & 0xffff) + (csum >> 16);
+               csum = csum ^ 0xffff;   // 1's complement limited to 16 bits
+               return csum;
+       }
+
+       /* Calculate the UDP checksum field in a given IPv6 packet.
+        */
+       public static int checksum_udpv6 (byte pkt []) {
+               return checksum_udpv6 (pkt, 0);
+       }
+       public static int checksum_udpv6 (byte pkt [], int pktofs) {
+               int udplen = fetch_net16 (pkt, pktofs + OFS_UDP6_PLEN);
+               int nxth = ((int) pkt [pktofs + 6]) & 0xff;
+               // Pseudo header is IPv6 src/dst (included with packet) and plen/nxth and zeroes:
+               int csum = udplen + nxth;
+               for (int i=8; i < OFS_IP6_PLOAD + udplen; i++) {
+                       if ((i & 0x0001) == 0x0000) {
+                               if (i != OFS_UDP6_CSUM + 0) {
+                                       csum += (((int) pkt [pktofs + i]) << 8) & 0xff00;
+                               }
+                       } else {
+                               if (i != OFS_UDP6_CSUM + 1) {
+                                       csum += ((int) pkt [pktofs + i]) & 0x00ff;
+                               }
+                       }
+               }
+               csum = (csum & 0xffff) + (csum >> 16);
+               csum = (csum & 0xffff) + (csum >> 16);
+               csum = csum ^ 0xffff;   // 1's complement limited to 16 bits
+               return csum;
+       }
+       
+       public static void memcp_address (byte tgt [], int tgtofs, byte src [], int srcofs) {
+               for (int i=0; i<16; i++) {
+                       tgt [tgtofs+i] = src [srcofs+i];
+               }
+       }
+       
+       public static boolean memdiff_addr (byte one[], int oneofs, byte oth[], int othofs) {
+               for (int i=0; i<16; i++) {
+                       if (one [oneofs + i] != oth [othofs + i]) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+       
+       public static boolean memdiff_halfaddr (byte one[], int oneofs, byte oth[], int othofs) {
+               for (int i=0; i<8; i++) {
+                       if (one [oneofs + i] != oth [othofs + i]) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+}
+