Update mirror to chrony version 4.2.

Change-Id: Ibfab5e57c05ca1573cb78d6ee04b9cf9059ff0dd
diff --git a/BUILD b/BUILD
index 9bd8eaa..b5672c4 100644
--- a/BUILD
+++ b/BUILD
@@ -11,13 +11,11 @@
 
 licenses(["restricted"])
 
-chrony_version_dir = "chrony_3_3"
-
 package_group(
     name = "internal",
     packages = [
         "//third_party/chrony",
-        "//third_party/chrony/" + chrony_version_dir,
+        "//third_party/chrony/chrony",
     ],
 )
 
@@ -27,7 +25,7 @@
 
 genrule(
     name = "chrony_daemon",
-    srcs = ["//third_party/chrony/" + chrony_version_dir + ":chronyd"],
+    srcs = ["//third_party/chrony/chrony:chronyd"],
     outs = ["chronyd"],
     cmd = "cp $< $@",
     visibility = ["//visibility:public"],
@@ -35,7 +33,7 @@
 
 genrule(
     name = "chrony_client",
-    srcs = ["//third_party/chrony/" + chrony_version_dir + ":chronyc"],
+    srcs = ["//third_party/chrony/chrony:chronyc"],
     outs = ["chronyc"],
     cmd = "cp $< $@",
     visibility = ["//visibility:public"],
diff --git a/LICENSE b/LICENSE
index d511905..a200da7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -337,3 +337,643 @@
 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.
+
+------------------
+
+Files: chrony_4_2/getdate.c
+
+                    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.
+
+Bison Exception
+
+As a special exception, you may create a larger work that contains part or all
+of the Bison parser skeleton and distribute that work under terms of your
+choice, so long as that work isn't itself a parser generator using the skeleton
+or a modified version thereof as a parser skeleton.  Alternatively, if you
+modify or redistribute the parser skeleton itself, you may (at your option)
+remove this special exception, which will cause the skeleton and the resulting
+Bison output files to be licensed under the GNU General Public License without
+this special exception.
+
+This special exception was added by the Free Software Foundation in version
+2.2 of Bison.
+
+                     END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/chrony_3_3/BUILD b/chrony/BUILD
similarity index 89%
rename from chrony_3_3/BUILD
rename to chrony/BUILD
index c309720..1582956 100644
--- a/chrony_3_3/BUILD
+++ b/chrony/BUILD
@@ -12,7 +12,7 @@
 # config.h created with ./configure --without-readline \
 # --without-editline --without-libcap --without-nss
 
-licenses(["restricted"])  # GPLv2
+licenses(["restricted"])
 
 package(default_visibility = ["//third_party/chrony:internal"])
 
@@ -40,7 +40,9 @@
         "memory.c",
         "nameserv.c",
         "nameserv_async.c",
+        "ntp_auth.c",
         "ntp_core.c",
+        "ntp_ext.c",
         "ntp_io.c",
         "ntp_io_linux.c",
         "ntp_sources.c",
@@ -54,8 +56,10 @@
         "regress.c",
         "rtc.c",
         "rtc_linux.c",
+        "samplefilt.c",
         "sched.c",
         "smooth.c",
+        "socket.c",
         "sources.c",
         "sourcestats.c",
         "stubs.c",
@@ -63,6 +67,7 @@
         "sys_generic.c",
         "sys_linux.c",
         "sys_null.c",
+        "sys_posix.c",
         "sys_timex.c",
         "tempcomp.c",
         "util.c",
@@ -73,7 +78,7 @@
         "-Wno-error",
     ],
     defines = select({
-        "//tools/cc_target_os:loonix": [],
+        "//tools/cc_target_os:chromiumos": [],
         "//conditions:default": ["HAVE_LONG_TIME_T"],
     }),
     deps = [
@@ -92,10 +97,11 @@
         "memory.c",
         "nameserv.c",
         "pktlength.c",
+        "socket.c",
         "util.c",
     ],
     defines = select({
-        "//tools/cc_target_os:loonix": [],
+        "//tools/cc_target_os:chromiumos": [],
         "//conditions:default": ["HAVE_LONG_TIME_T"],
     }),
     deps = [
diff --git a/chrony_3_3/COPYING b/chrony/COPYING
similarity index 100%
rename from chrony_3_3/COPYING
rename to chrony/COPYING
diff --git a/chrony/FAQ b/chrony/FAQ
new file mode 100644
index 0000000..d68f885
--- /dev/null
+++ b/chrony/FAQ
@@ -0,0 +1,911 @@
+Frequently Asked Questions
+
+Table of Contents
+
+  o 1. chrony compared to other programs
+      ? 1.1. How does chrony compare to ntpd?
+  o 2. Configuration issues
+      ? 2.1. What is the minimum recommended configuration for an NTP client?
+      ? 2.2. How do I make an NTP server?
+      ? 2.3. Should all computers on a LAN be clients of an external server?
+      ? 2.4. Must I specify servers by IP address if DNS is not available on
+        chronyd start?
+      ? 2.5. How can I make chronyd more secure?
+      ? 2.6. How can I make the system clock more secure?
+      ? 2.7. How can I improve the accuracy of the system clock with NTP
+        sources?
+      ? 2.8. Does chronyd have an ntpdate mode?
+      ? 2.9. Can chronyd be configured to control the clock like ntpd?
+      ? 2.10. Can NTP server be separated from NTP client?
+      ? 2.11. Should be a leap smear enabled on NTP server?
+      ? 2.12. Does chrony support PTP?
+      ? 2.13. What happened to the commandkey and generatecommandkey
+        directives?
+  o 3. Computer is not synchronising
+      ? 3.1. Behind a firewall?
+      ? 3.2. Are NTP servers specified with the offline option?
+      ? 3.3. Is name resolution working correctly?
+      ? 3.4. Is chronyd allowed to step the system clock?
+      ? 3.5. Using NTS?
+      ? 3.6. Using a Windows NTP server?
+      ? 3.7. An unreachable source is selected?
+      ? 3.8. Does selected source drop new measurements?
+      ? 3.9. Using a PPS reference clock?
+  o 4. Issues with chronyc
+      ? 4.1. I keep getting the error 506 Cannot talk to daemon
+      ? 4.2. I keep getting the error 501 Not authorised
+      ? 4.3. What is the reference ID reported by the tracking command?
+      ? 4.4. Is the chronyc / chronyd protocol documented anywhere?
+  o 5. Real-time clock issues
+      ? 5.1. What is the real-time clock (RTC)?
+      ? 5.2. Does hwclock have to be disabled?
+      ? 5.3. I just keep getting the 513 RTC driver not running message
+      ? 5.4. I get Could not open /dev/rtc, Device or resource busy in my
+        syslog file
+      ? 5.5. When I start chronyd, the log says Could not enable RTC interrupt
+        : Invalid argument (or it may say disable)
+      ? 5.6. What if my computer does not have an RTC or backup battery?
+  o 6. NTP-specific issues
+      ? 6.1. Can chronyd be driven from broadcast/multicast NTP servers?
+      ? 6.2. Can chronyd transmit broadcast NTP packets?
+      ? 6.3. Can chronyd keep the system clock a fixed offset away from real
+        time?
+      ? 6.4. What happens if the network connection is dropped without using
+        chronyc's offline command first?
+      ? 6.5. Why is an offset measured between two computers synchronised to
+        each another?
+  o 7. Operating systems
+      ? 7.1. Does chrony support Windows?
+      ? 7.2. Are there any plans to support Windows?
+
+1. chrony compared to other programs
+
+1.1. How does chrony compare to ntpd?
+
+chrony and ntpd are two different implementations of the Network Time Protocol
+(NTP).
+
+chrony is a newer implementation, which was designed to work well in a wider
+range of conditions. It can usually synchronise the system clock faster and
+with better time accuracy. It has many features, but it does not implement some
+of the less useful NTP modes like broadcast client or multicast server/client.
+
+If your computer is connected to the Internet only for few minutes at a time,
+the network connection is often congested, you turn your computer off or
+suspend it frequently, the clock is not very stable (e.g. there are rapid
+changes in the temperature or it is a virtual machine), or you want to use NTP
+on an isolated network with no hardware reference clocks in sight, chrony will
+probably work better for you.
+
+For a more detailed comparison of features and performance, see the comparison
+page on the chrony website.
+
+2. Configuration issues
+
+2.1. What is the minimum recommended configuration for an NTP client?
+
+First, the client needs to know which NTP servers it should ask for the current
+time. They are specified by the server or pool directive. The pool directive is
+used with names that resolve to multiple addresses of different servers. For
+reliable operation, the client should have at least three servers.
+
+The iburst option enables a burst of requests to speed up the initial
+synchronisation.
+
+To stabilise the initial synchronisation on the next start, the estimated drift
+of the system clock is saved to a file specified by the driftfile directive.
+
+If the system clock can be far from the true time after boot for any reason,
+chronyd should be allowed to correct it quickly by stepping instead of slewing,
+which would take a very long time. The makestep directive does that.
+
+In order to keep the real-time clock (RTC) close to the true time, so the
+system time is reasonably close to the true time when it is initialised on the
+next boot from the RTC, the rtcsync directive enables a mode in which the
+system time is periodically copied to the RTC. It is supported on Linux and
+macOS.
+
+If you wanted to use public NTP servers from the pool.ntp.org project, the
+minimal chrony.conf file could be:
+
+pool pool.ntp.org iburst
+driftfile /var/lib/chrony/drift
+makestep 1 3
+rtcsync
+
+2.2. How do I make an NTP server?
+
+By default, chronyd does not operate as an NTP server. You need to add an allow
+directive to the chrony.conf file in order for chronyd to open the server NTP
+port and respond to client requests.
+
+allow 192.168.1.0/24
+
+An allow directive with no specified subnet allows access from all IPv4 and
+IPv6 addresses.
+
+2.3. Should all computers on a LAN be clients of an external server?
+
+It depends on the requirements. Usually, the best configuration is to make one
+computer the server, with the others as clients of it. Add a local directive to
+the server's chrony.conf file. This configuration will be better because
+
+  o the load on the external connection is less
+
+  o the load on the external NTP server(s) is less
+
+  o if your external connection goes down, the computers on the LAN will
+    maintain a common time with each other.
+
+2.4. Must I specify servers by IP address if DNS is not available on chronyd
+start?
+
+No, chronyd will keep trying to resolve the names specified by the server,
+pool, and peer directives in an increasing interval until it succeeds. The
+online command can be issued from chronyc to force chronyd to try to resolve
+the names immediately.
+
+2.5. How can I make chronyd more secure?
+
+If you do not need to use chronyc, or you want to run chronyc only under the
+root or chrony user (which can access chronyd through a Unix domain socket),
+you can disable the IPv4 and IPv6 command sockets (by default listening on
+localhost) by adding cmdport 0 to the configuration file.
+
+You can specify an unprivileged user with the -u option, or the user directive
+in the chrony.conf file, to which chronyd will switch after start in order to
+drop root privileges. The configure script has a --with-user option, which sets
+the default user. On Linux, chronyd needs to be compiled with support for the
+libcap library. On other systems, chronyd forks into two processes. The child
+process retains root privileges, but can only perform a very limited range of
+privileged system calls on behalf of the parent.
+
+Also, if chronyd is compiled with support for the Linux secure computing
+(seccomp) facility, you can enable a system call filter with the -F option. It
+will significantly reduce the kernel attack surface and possibly prevent kernel
+exploits from the chronyd process if it is compromised. It is recommended to
+enable the filter only when it is known to work on the version of the system
+where chrony is installed as the filter needs to allow also system calls made
+from libraries that chronyd is using (e.g. libc) and different versions or
+implementations of the libraries might make different system calls. If the
+filter is missing some system call, chronyd could be killed even in normal
+operation.
+
+2.6. How can I make the system clock more secure?
+
+An NTP client synchronising the system clock to an NTP server is susceptible to
+various attacks, which can break applications and network protocols relying on
+accuracy of the clock (e.g. DNSSEC, Kerberos, TLS, WireGuard).
+
+Generally, a man-in-the-middle (MITM) attacker between the client and server
+can
+
+  o make fake responses, or modify real responses from the server, to create an
+    arbitrarily large time and frequency offset, make the server appear more
+    accurate, insert a leap second, etc.
+
+  o delay the requests and/or responses to create a limited time offset and
+    temporarily also a limited frequency offset
+
+  o drop the requests or responses to prevent updates of the clock with new
+    measurements
+
+  o redirect the requests to a different server
+
+The attacks can be combined for a greater effect. The attacker can delay
+packets to create a significant frequency offset first and then drop all
+subsequent packets to let the clock quickly drift away from the true time. The
+attacker might also be able to control the server's clock.
+
+Some attacks cannot be prevented. Monitoring is needed for detection, e.g. the
+reachability register in the sources report shows missing packets. The extent
+to which the attacker can control the client's clock depends on its
+configuration.
+
+Enable authentication to prevent chronyd from accepting modified, fake, or
+redirected packets. It can be enabled with a symmetric key specified by the key
+option, or Network Time Security (NTS) by the nts option (supported since
+chrony version 4.0). The server needs to support the selected authentication
+mechanism. Symmetric keys have to be configured on both client and server, and
+each client must have its own key (one per server).
+
+The maximum offset that the attacker can insert in an NTP measurement by
+delaying packets can be limited by the maxdelay option. The default value is 3
+seconds. The measured delay is reported as the peer delay in the ntpdata report
+and measurements log. Set the maxdelay option to a value larger than the
+maximum value that is normally observed. Note that the delay can increase
+significantly even when not under an attack, e.g. when the network is congested
+or the routing has changed.
+
+The maximum accepted change in time offset between clock updates can be limited
+by the maxchange directive. Larger changes in the offset will be ignored or
+cause chronyd to exit. Note that the attacker can get around this limit by
+splitting the offset into multiple smaller offsets and/or creating a large
+frequency offset. When this directive is used, chronyd will have to be
+restarted after a successful attack. It will not be able to recover on its own.
+It must not be restarted automatically (e.g. by the service manager).
+
+The impact of a large accepted time offset can be reduced by disabling clock
+steps, i.e. by not using the makestep and initstepslew directives. The offset
+will be slowly corrected by speeding up or slowing down the clock at a rate
+which can be limited by the maxslewrate directive. Disabling clock steps
+completely is practical only if the clock cannot gain a larger error on its
+own, e.g. when the computer is shut down or suspended, and the maxslewrate
+limit is large enough to correct an expected error in an acceptable time. The
+rtcfile directive with the -s option can be used to compensate for the RTC
+drift.
+
+A more practical approach is to enable makestep for a limited number of clock
+updates (the 2nd argument of the directive) and limit the offset change in all
+updates by the maxchange directive. The attacker will be able to make only a
+limited step and only if the attack starts in a short window after booting the
+computer, or when chronyd is restarted without the -R option.
+
+The frequency offset can be limited by the maxdrift directive. The measured
+frequency offset is reported in the drift file, tracking report, and tracking
+log. Set maxdrift to a value larger than the maximum absolute value that is
+normally observed. Note that the frequency of the clock can change due to aging
+of the crystal, differences in calibration of the clock source between reboots,
+migrated virtual machine, etc. A typical computer clock has a drift smaller
+than 100 parts per million (ppm), but much larger drifts are possible (e.g. in
+some virtual machines).
+
+Use only trusted servers, which you expect to be well configured and managed,
+using authentication for their own servers, etc. Use multiple servers, ideally
+in different locations. The attacker will have to deal with a majority of the
+servers in order to pass the source selection and update the clock with a large
+offset. Use the minsources directive to increase the required number of
+selectable sources to make the selection more robust.
+
+Do not specify servers as peers. The symmetric mode is less secure than the
+client/server mode. If not authenticated, it is vulnerable to off-path
+denial-of-service attacks, and even when it is authenticated, it is still
+susceptible to replay attacks.
+
+Mixing of authenticated and unauthenticated servers should generally be
+avoided. If mixing is necessary (e.g. for a more accurate and stable
+synchronisation to a closer server which does not support authentication), the
+authenticated servers should be configured as trusted and required to not allow
+the unauthenticated servers to override the authenticated servers in the source
+selection. Since chrony version 4.0, the selection options are enabled in such
+a case automatically. This behaviour can be disabled or modified by the
+authselmode directive.
+
+An example of a client configuration limiting the impact of the attacks could
+be
+
+server foo.example.net iburst nts maxdelay 0.1
+server bar.example.net iburst nts maxdelay 0.2
+server baz.example.net iburst nts maxdelay 0.05
+server qux.example.net iburst nts maxdelay 0.1
+server quux.example.net iburst nts maxdelay 0.1
+minsources 3
+maxchange 100 0 0
+makestep 0.001 1
+maxdrift 100
+maxslewrate 100
+driftfile /var/lib/chrony/drift
+ntsdumpdir /var/lib/chrony
+rtcsync
+
+2.7. How can I improve the accuracy of the system clock with NTP sources?
+
+Select NTP servers that are well synchronised, stable and close to your
+network. It is better to use more than one server. Three or four is usually
+recommended as the minimum, so chronyd can detect servers that serve false time
+and combine measurements from multiple sources.
+
+If you have a network card with hardware timestamping supported on Linux, it
+can be enabled by the hwtimestamp directive. It should make local receive and
+transmit timestamps of NTP packets much more stable and accurate.
+
+The server directive has some useful options: minpoll, maxpoll, polltarget,
+maxdelay, maxdelayratio, maxdelaydevratio, xleave, filter.
+
+The first three options set the minimum and maximum allowed polling interval,
+and how should be the actual interval adjusted in the specified range. Their
+default values are 6 (64 seconds) for minpoll, 10 (1024 seconds) for maxpoll
+and 8 (samples) for polltarget. The default values should be used for general
+servers on the Internet. With your own NTP servers, or if you have permission
+to poll some servers more frequently, setting these options for shorter polling
+intervals might significantly improve the accuracy of the system clock.
+
+The optimal polling interval depends mainly on two factors, stability of the
+network latency and stability of the system clock (which mainly depends on the
+temperature sensitivity of the crystal oscillator and the maximum rate of the
+temperature change).
+
+Generally, if the sourcestats command usually reports a small number of samples
+retained for a source (e.g. fewer than 16), a shorter polling interval should
+be considered. If the number of samples is usually at the maximum of 64, a
+longer polling interval might work better.
+
+An example of the directive for an NTP server on the Internet that you are
+allowed to poll frequently could be
+
+server foo.example.net minpoll 4 maxpoll 6 polltarget 16
+
+An example using shorter polling intervals with a server located in the same
+LAN could be
+
+server ntp.local minpoll 2 maxpoll 4 polltarget 30
+
+The maxdelay options are useful to ignore measurements with an unusually large
+delay (e.g. due to congestion in the network) and improve the stability of the
+synchronisation. The maxdelaydevratio option could be added to the example with
+local NTP server
+
+server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
+
+If your server supports the interleaved mode (e.g. it is running chronyd), the
+xleave option should be added to the server directive to enable the server to
+provide the client with more accurate transmit timestamps (kernel or preferably
+hardware). For example:
+
+server ntp.local minpoll 2 maxpoll 4 xleave
+
+When combined with local hardware timestamping, good network switches, and even
+shorter polling intervals, a sub-microsecond accuracy and stability of a few
+tens of nanoseconds might be possible. For example:
+
+server ntp.local minpoll 0 maxpoll 0 xleave
+hwtimestamp eth0
+
+For best stability, the CPU should be running at a constant frequency (i.e.
+disabled power saving and performance boosting). Energy-Efficient Ethernet
+(EEE) should be disabled in the network. The switches should be configured to
+prioritize NTP packets, especially if the network is expected to be heavily
+loaded. The dscp directive can be used to set the Differentiated Services Code
+Point in transmitted NTP packets if needed.
+
+If it is acceptable for NTP clients in the network to send requests at a high
+rate, a sub-second polling interval can be specified. A median filter can be
+enabled in order to update the clock at a reduced rate with more stable
+measurements. For example:
+
+server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
+hwtimestamp eth0 minpoll -6
+
+As an experimental feature added in version 4.2, chronyd supports an NTPv4
+extension field containing an additional timestamp to enable frequency transfer
+and significantly improve stability of synchronisation. It can be enabled by
+the extfield F323 option. For example:
+
+server ntp.local minpoll 0 maxpoll 0 xleave extfield F323
+
+2.8. Does chronyd have an ntpdate mode?
+
+Yes. With the -q option chronyd will set the system clock once and exit. With
+the -Q option it will print the measured offset without setting the clock. If
+you do not want to use a configuration file, NTP servers can be specified on
+the command line. For example:
+
+# chronyd -q 'pool pool.ntp.org iburst'
+
+The command above would normally take about 5 seconds if the servers were well
+synchronised and responding to all requests. If not synchronised or responding,
+it would take about 10 seconds for chronyd to give up and exit with a non-zero
+status. A faster configuration is possible. A single server can be used instead
+of four servers, the number of measurements can be reduced with the maxsamples
+option to one (supported since chrony version 4.0), and a timeout can be
+specified with the -t option. The following command would take only up to about
+one second.
+
+# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1'
+
+It is not recommended to run chronyd with the -q option periodically (e.g. from
+a cron job) as a replacement for the daemon mode, because it performs
+significantly worse (e.g. the clock is stepped and its frequency is not
+corrected). If you must run it this way and you are using a public NTP server,
+make sure chronyd does not always start around the first second of a minute,
+e.g. by adding a random sleep before the chronyd command. Public servers
+typically receive large bursts of requests around the first second as there is
+a large number of NTP clients started from cron with no delay.
+
+2.9. Can chronyd be configured to control the clock like ntpd?
+
+It is not possible to perfectly emulate ntpd, but there are some options that
+can configure chronyd to behave more like ntpd if there is a reason to prefer
+that.
+
+In the following example the minsamples directive slows down the response to
+changes in the frequency and offset of the clock. The maxslewrate and
+corrtimeratio directives reduce the maximum frequency error due to an offset
+correction and the maxdrift directive reduces the maximum assumed frequency
+error of the clock. The makestep directive enables a step threshold and the
+maxchange directive enables a panic threshold. The maxclockerror directive
+increases the minimum dispersion rate.
+
+minsamples 32
+maxslewrate 500
+corrtimeratio 100
+maxdrift 500
+makestep 0.128 -1
+maxchange 1000 1 1
+maxclockerror 15
+
+Note that increasing minsamples might cause the offsets in the tracking and
+sourcestats reports/logs to be significantly smaller than the actual offsets
+and be unsuitable for monitoring.
+
+2.10. Can NTP server be separated from NTP client?
+
+Yes, it is possible to run multiple instances of chronyd on a computer at the
+same time. One can operate primarily as an NTP client to synchronise the system
+clock and another as a server for other computers. If they use the same
+filesystem, they need to be configured with different pidfiles, Unix domain
+command sockets, and any other file or directory specified in the configuration
+file. If they run in the same network namespace, they need to use different NTP
+and command ports, or bind the ports to different addresses or interfaces.
+
+The server instance should be started with the -x option to prevent it from
+adjusting the system clock and interfering with the client instance. It can be
+configured as a client to synchronise its NTP clock to other servers, or the
+client instance running on the same computer. In the latter case, the copy
+option (added in chrony version 4.1) can be used to assume the reference ID and
+stratum of the client instance, which enables detection of synchronisation
+loops with its own clients.
+
+On Linux, starting with chrony version 4.0, it is possible to run multiple
+server instances sharing a port to better utilise multiple cores of the CPU.
+Note that for rate limiting and client/server interleaved mode to work well it
+is necessary that all packets received from the same address are handled by the
+same server instance.
+
+An example configuration of the client instance could be
+
+pool pool.ntp.org iburst
+allow 127.0.0.1
+port 11123
+driftfile /var/lib/chrony/drift
+makestep 1 3
+rtcsync
+
+and configuration of the first server instance could be
+
+server 127.0.0.1 port 11123 minpoll 0 maxpoll 0 copy
+allow
+cmdport 11323
+bindcmdaddress /var/run/chrony/chronyd-server1.sock
+pidfile /var/run/chronyd-server1.pid
+driftfile /var/lib/chrony/drift-server1
+
+2.11. Should be a leap smear enabled on NTP server?
+
+With the smoothtime and leapsecmode directives it is possible to enable a
+server leap smear in order to hide leap seconds from clients and force them to
+follow a slow server's adjustment instead.
+
+This feature should be used only in local networks and only when necessary,
+e.g. when the clients cannot be configured to handle the leap seconds as
+needed, or their number is so large that configuring them all would be
+impractical. The clients should use only one leap-smearing server, or multiple
+identically configured leap-smearing servers. Note that some clients can get
+leap seconds from other sources (e.g. with the leapsectz directive in chrony)
+and they will not work correctly with a leap smearing server.
+
+2.12. Does chrony support PTP?
+
+No, the Precision Time Protocol (PTP) is not supported as a protocol for
+synchronisation of clocks and there are no plans to support it. It is a complex
+protocol, which shares some issues with the NTP broadcast mode. One of the main
+differences between NTP and PTP is that PTP was designed to be easily supported
+in hardware (e.g. network switches and routers) in order to make more stable
+and accurate measurements. PTP relies on the hardware support. NTP does not
+rely on any support in the hardware, but if it had the same support as PTP, it
+could perform equally well.
+
+On Linux, chrony supports hardware clocks that some NICs have for PTP. They are
+called PTP hardware clocks (PHC). They can be used as reference clocks
+(specified by the refclock directive) and for hardware timestamping of NTP
+packets (enabled by the hwtimestamp directive) if the NIC can timestamp other
+packets than PTP, which is usually the case at least for transmitted packets.
+The ethtool -T command can be used to verify the timestamping support.
+
+As an experimental feature added in version 4.2, chrony can use PTP as a
+transport for NTP messages (NTP over PTP) to enable hardware timestamping on
+hardware which can timestamp PTP packets only. It can be enabled by the ptpport
+directive.
+
+2.13. What happened to the commandkey and generatecommandkey directives?
+
+They were removed in version 2.2. Authentication is no longer supported in the
+command protocol. Commands that required authentication are now allowed only
+through a Unix domain socket, which is accessible only by the root and chrony
+users. If you need to configure chronyd remotely or locally without the root
+password, please consider using ssh and/or sudo to run chronyc under the root
+or chrony user on the host where chronyd is running.
+
+3. Computer is not synchronising
+
+This is the most common problem. There are a number of reasons, see the
+following questions.
+
+3.1. Behind a firewall?
+
+Check the Reach value printed by the chronyc's sources command. If it is zero,
+it means chronyd did not get any valid responses from the NTP server you are
+trying to use. If there is a firewall between you and the server, the packets
+might be blocked. Try using a tool like wireshark or tcpdump to see if you are
+getting any responses from the server.
+
+When chronyd is receiving responses from the servers, the output of the sources
+command issued few minutes after chronyd start might look like this:
+
+MS Name/IP address         Stratum Poll Reach LastRx Last sample
+===============================================================================
+^* foo.example.net               2   6   377    34   +484us[ -157us] +/-   30ms
+^- bar.example.net               2   6   377    34    +33ms[  +32ms] +/-   47ms
+^+ baz.example.net               3   6   377    35  -1397us[-2033us] +/-   60ms
+
+3.2. Are NTP servers specified with the offline option?
+
+Check that the chronyc's online and offline commands are used appropriately
+(e.g. in the system networking scripts). The activity command prints the number
+of sources that are currently online and offline. For example:
+
+200 OK
+3 sources online
+0 sources offline
+0 sources doing burst (return to online)
+0 sources doing burst (return to offline)
+0 sources with unknown address
+
+3.3. Is name resolution working correctly?
+
+NTP servers specified by their hostname (instead of an IP address) have to have
+their names resolved before chronyd can send any requests to them. If the
+activity command prints a non-zero number of sources with unknown address,
+there is an issue with the resolution. Typically, a DNS server is specified in
+/etc/resolv.conf. Make sure it is working correctly.
+
+Since chrony version 4.0, you can run chronyc -N sources -a command to print
+all sources, even those that do not have a known address yet, with their names
+as they were specified in the configuration. This can be useful to verify that
+the names specified in the configuration are used as expected.
+
+3.4. Is chronyd allowed to step the system clock?
+
+By default, chronyd adjusts the clock gradually by slowing it down or speeding
+it up. If the clock is too far from the true time, it will take a long time to
+correct the error. The System time value printed by the chronyc's tracking
+command is the remaining correction that needs to be applied to the system
+clock.
+
+The makestep directive can be used to allow chronyd to step the clock. For
+example, if chrony.conf had
+
+makestep 1 3
+
+the clock would be stepped in the first three updates if its offset was larger
+than one second. Normally, it is recommended to allow the step only in the
+first few updates, but in some cases (e.g. a computer without an RTC or virtual
+machine which can be suspended and resumed with an incorrect time) it might be
+necessary to allow the step on any clock update. The example above would change
+to
+
+makestep 1 -1
+
+3.5. Using NTS?
+
+The Network Time Security (NTS) mechanism uses Transport Layer Security (TLS)
+to establish the keys needed for authentication of NTP packets.
+
+Run the authdata command to check whether the key establishment was successful:
+
+# chronyc -N authdata
+Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+foo.example.net              NTS     1   15  256  33m    0    0    8  100
+bar.example.net              NTS     1   15  256  33m    0    0    8  100
+baz.example.net              NTS     1   15  256  33m    0    0    8  100
+
+The KeyID, Type, and KLen columns should have non-zero values. If they are
+zero, check the system log for error messages from chronyd. One possible cause
+of failure is a firewall blocking the client's connection to the server's TCP
+port 4460.
+
+Another possible cause of failure is a certificate that is failing to verify
+because the client's clock is wrong. This is a chicken-and-egg problem with
+NTS. You might need to manually correct the date, or temporarily disable NTS,
+in order to get NTS working. If your computer has an RTC and it is backed up by
+a good battery, this operation should be needed only once, assuming the RTC
+will be set periodically with the rtcsync directive, or compensated with the
+rtcfile directive and the -s option.
+
+If the computer does not have an RTC or battery, you can use the -s option
+without rtcfile directive to restore time of the last shutdown or reboot from
+the drift file. The clock will start behind the true time, but if the computer
+was not shut down for too long and the server's certificate was not renewed too
+close to its expiration, it should be sufficient for the time checks to
+succeed.
+
+As a last resort, you can disable the time checks by the nocerttimecheck
+directive. This has some important security implications. To reduce the
+security risk, you can use the nosystemcert and ntstrustedcerts directives to
+disable the system's default trusted certificate authorities and trust only a
+minimal set of selected authorities needed to validate the certificates of used
+NTP servers.
+
+3.6. Using a Windows NTP server?
+
+A common issue with Windows NTP servers is that they report a very large root
+dispersion (e.g. three seconds or more), which causes chronyd to ignore the
+server for being too inaccurate. The sources command might show a valid
+measurement, but the server is not selected for synchronisation. You can check
+the root dispersion of the server with the chronyc's ntpdata command.
+
+The maxdistance value needs to be increased in chrony.conf to enable
+synchronisation to such a server. For example:
+
+maxdistance 16.0
+
+3.7. An unreachable source is selected?
+
+When chronyd is configured with multiple time sources, it tries to select the
+most accurate and stable sources for synchronisation of the system clock. They
+are marked with the * or + symbol in the report printed by the sources command.
+
+When the best source (marked with the * symbol) becomes unreachable (e.g. NTP
+server stops responding), chronyd will not immediately switch to the second
+best source in an attempt to minimise the error of the clock. It will let the
+clock run free for as long as its estimated error (in terms of root distance)
+based on previous measurements is smaller than the estimated error of the
+second source, and there is still an interval which contains some measurements
+from both sources.
+
+If the first source was significantly better than the second source, it can
+take many hours before the second source is selected, depending on its polling
+interval. You can force a faster reselection by increasing the clock error rate
+(maxclockerror directive), shortening the polling interval (maxpoll option), or
+reducing the number of samples (maxsamples option).
+
+3.8. Does selected source drop new measurements?
+
+chronyd can drop a large number of successive NTP measurements if they are not
+passing some of the NTP tests. The sources command can report for a selected
+source the fully-reachable value of 377 in the Reach column and at the same
+time a LastRx value that is much larger than the current polling interval. If
+the source is online, this indicates that a number of measurements was dropped.
+You can use the ntpdata command to check the NTP tests for the last
+measurement. Usually, it is the test C which fails.
+
+This can be an issue when there is a long-lasting increase in the measured
+delay, e.g. due to a routing change in the network. Unfortunately, chronyd does
+not know for how long it should wait for the delay to come back to the original
+values, or whether it is a permanent increase and it should start from scratch.
+
+The test C is an adaptive filter. It can take many hours before it accepts a
+measurement with the larger delay, and even much longer before it drops all
+measurements with smaller delay, which determine an expected delay used by the
+test. You can use the reset sources command to drop all measurements
+immediately (available in chrony 4.0 and later). If this issue happens
+frequently, you can effectively disable the test by setting the
+maxdelaydevratio option to a very large value (e.g. 1000000), or speed up the
+recovery by increasing the clock error rate with the maxclockerror directive.
+
+3.9. Using a PPS reference clock?
+
+A pulse-per-second (PPS) reference clock requires a non-PPS time source to
+determine which second of UTC corresponds to each pulse. If it is another
+reference clock specified with the lock option in the refclock directive, the
+offset between the two reference clocks must be smaller than 0.2 seconds in
+order for the PPS reference clock to work. With NMEA reference clocks it is
+common to have a larger offset. It needs to be corrected with the offset
+option.
+
+One approach to find out a good value of the offset option is to configure the
+reference clocks with the noselect option and compare them to an NTP server.
+For example, if the sourcestats command showed
+
+Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
+==============================================================================
+PPS0                        0   0     0     +0.000   2000.000     +0ns  4000ms
+NMEA                       58  30   231    -96.494     38.406   +504ms  6080us
+foo.example.net             7   3   200     -2.991     16.141   -107us   492us
+
+the offset of the NMEA source would need to be increased by about 0.504
+seconds. It does not have to be very accurate. As long as the offset of the
+NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
+able to determine the seconds corresponding to the pulses and allow the samples
+to be used for synchronisation.
+
+4. Issues with chronyc
+
+4.1. I keep getting the error 506 Cannot talk to daemon
+
+When accessing chronyd remotely, make sure that the chrony.conf file (on the
+computer where chronyd is running) has a cmdallow entry for the computer you
+are running chronyc on and an appropriate bindcmdaddress directive. This is not
+necessary for localhost.
+
+Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, ps
+-auxw) to see if it is running. Or try netstat -a and see if the UDP port 323
+is listening. If chronyd is not running, you might have a problem with the way
+you are trying to start it (e.g. at boot time).
+
+Perhaps you have a firewall set up in a way that blocks packets on the UDP port
+323. You need to amend the firewall configuration in this case.
+
+4.2. I keep getting the error 501 Not authorised
+
+This error indicates that chronyc sent the command to chronyd using a UDP
+socket instead of the Unix domain socket (e.g. /var/run/chrony/chronyd.sock),
+which is required for some commands. For security reasons, only the root and
+chrony users are allowed to access the socket.
+
+It is also possible that the socket does not exist. chronyd will not create the
+socket if the directory has a wrong owner or permissions. In this case there
+should be an error message from chronyd in the system log.
+
+4.3. What is the reference ID reported by the tracking command?
+
+The reference ID is a 32-bit value used in NTP to prevent synchronisation
+loops.
+
+In chrony versions before 3.0 it was printed in the quad-dotted notation, even
+if the reference source did not actually have an IPv4 address. For IPv4
+addresses, the reference ID is equal to the address, but for IPv6 addresses it
+is the first 32 bits of the MD5 sum of the address. For reference clocks, the
+reference ID is the value specified with the refid option in the refclock
+directive.
+
+Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
+confusion with IPv4 addresses.
+
+If you need to get the IP address of the current reference source, use the -n
+option to disable resolving of IP addresses and read the second field (printed
+in parentheses) on the Reference ID line.
+
+4.4. Is the chronyc / chronyd protocol documented anywhere?
+
+Only by the source code. See cmdmon.c (chronyd side) and client.c (chronyc
+side).
+
+5. Real-time clock issues
+
+5.1. What is the real-time clock (RTC)?
+
+This is the clock which keeps the time even when your computer is turned off.
+It is used to initialise the system clock on boot. It normally does not drift
+more than few seconds per day.
+
+There are two approaches how chronyd can work with it. One is to use the
+rtcsync directive, which tells chronyd to enable a kernel mode which sets the
+RTC from the system clock every 11 minutes. chronyd itself will not touch the
+RTC. If the computer is not turned off for a long time, the RTC should still be
+close to the true time when the system clock will be initialised from it on the
+next boot.
+
+The other option is to use the rtcfile directive, which tells chronyd to
+monitor the rate at which the RTC gains or loses time. When chronyd is started
+with the -s option on the next boot, it will set the system time from the RTC
+and also compensate for the drift it has measured previously. The rtcautotrim
+directive can be used to keep the RTC close to the true time, but it is not
+strictly necessary if its only purpose is to set the system clock when chronyd
+is started on boot. See the documentation for details.
+
+5.2. Does hwclock have to be disabled?
+
+The hwclock program is run by default in the boot and/or shutdown scripts in
+some Linux installations. With the kernel RTC synchronisation (rtcsync
+directive), the RTC will be set also every 11 minutes as long as the system
+clock is synchronised. If you want to use chronyd's RTC monitoring (rtcfile
+directive), it is important to disable hwclock in the shutdown procedure. If
+you do not do that, it will overwrite the RTC with a new value, unknown to
+chronyd. At the next reboot, chronyd started with the -s option will compensate
+this (wrong) time with its estimate of how far the RTC has drifted whilst the
+power was off, giving a meaningless initial system time.
+
+There is no need to remove hwclock from the boot process, as long as chronyd is
+started after it has run.
+
+5.3. I just keep getting the 513 RTC driver not running message
+
+For the real-time clock support to work, you need the following three things
+
+  o an RTC in your computer
+
+  o a Linux kernel with enabled RTC support
+
+  o an rtcfile directive in your chrony.conf file
+
+5.4. I get Could not open /dev/rtc, Device or resource busy in my syslog file
+
+Some other program running on the system might be using the device.
+
+5.5. When I start chronyd, the log says Could not enable RTC interrupt :
+Invalid argument (or it may say disable)
+
+Your real-time clock hardware might not support the required ioctl requests:
+
+  o RTC_UIE_ON
+
+  o RTC_UIE_OFF
+
+A possible solution could be to build the Linux kernel with support for
+software emulation instead; try enabling the following configuration option
+when building the Linux kernel:
+
+  o CONFIG_RTC_INTF_DEV_UIE_EMUL
+
+5.6. What if my computer does not have an RTC or backup battery?
+
+In this case you can still use the -s option to set the system clock to the
+last modification time of the drift file, which should correspond to the system
+time when chronyd was previously stopped. The initial system time will be
+increasing across reboots and applications started after chronyd will not
+observe backward steps.
+
+6. NTP-specific issues
+
+6.1. Can chronyd be driven from broadcast/multicast NTP servers?
+
+No, the broadcast/multicast client mode is not supported and there is currently
+no plan to implement it. While this mode can simplify configuration of clients
+in large networks, it is inherently less accurate and less secure (even with
+authentication) than the ordinary client/server mode.
+
+When configuring a large number of clients in a network, it is recommended to
+use the pool directive with a DNS name which resolves to addresses of multiple
+NTP servers. The clients will automatically replace the servers when they
+become unreachable, or otherwise unsuitable for synchronisation, with new
+servers from the pool.
+
+Even with very modest hardware, an NTP server can serve time to hundreds of
+thousands of clients using the ordinary client/server mode.
+
+6.2. Can chronyd transmit broadcast NTP packets?
+
+Yes, the broadcast directive can be used to enable the broadcast server mode to
+serve time to clients in the network which support the broadcast client mode
+(it is not supported in chronyd). Note that this mode should generally be
+avoided. See the previous question.
+
+6.3. Can chronyd keep the system clock a fixed offset away from real time?
+
+Yes. Starting from version 3.0, an offset can be specified by the offset option
+for all time sources in the chrony.conf file.
+
+6.4. What happens if the network connection is dropped without using chronyc's
+offline command first?
+
+chronyd will keep trying to access the sources that it thinks are online, and
+it will take longer before new measurements are actually made and the clock is
+corrected when the network is connected again. If the sources were set to
+offline, chronyd would make new measurements immediately after issuing the
+online command.
+
+Unless the network connection lasts only few minutes (less than the maximum
+polling interval), the delay is usually not a problem, and it might be
+acceptable to keep all sources online all the time.
+
+6.5. Why is an offset measured between two computers synchronised to each
+another?
+
+When two computers are synchronised to each other using the client/server or
+symmetric NTP mode, there is an expectation that NTP measurements between the
+two computers made on both ends show an average offset close to zero.
+
+With chronyd that can be expected only when the interleaved mode is enabled by
+the xleave option. Otherwise, chronyd will use different transmit timestamps
+(e.g. daemon timestamp vs kernel timestamp) for serving time and
+synchronisation of its own clock, which will cause the other computer to
+measure a significant offset.
+
+7. Operating systems
+
+7.1. Does chrony support Windows?
+
+No. The chronyc program (the command-line client used for configuring chronyd
+while it is running) has been successfully built and run under Cygwin in the
+past. chronyd is not portable, because part of it is very system-dependent. It
+needs adapting to work with Windows' equivalent of the adjtimex() call, and it
+needs to be made to work as a service.
+
+7.2. Are there any plans to support Windows?
+
+We have no plans to do this. Anyone is welcome to pick this work up and
+contribute it back to the project.
+
+Last updated 2021-12-16 13:17:42 +0100
diff --git a/chrony_3_3/INSTALL b/chrony/INSTALL
similarity index 74%
rename from chrony_3_3/INSTALL
rename to chrony/INSTALL
index ebaf9d3..656537a 100644
--- a/chrony_3_3/INSTALL
+++ b/chrony/INSTALL
@@ -4,6 +4,35 @@
 code is supplied in the form of a gzipped tar file, which unpacks to a
 subdirectory identifying the name and version of the program.
 
+A C compiler (e.g. gcc or clang) and GNU Make are needed to build chrony. The
+following libraries with their development files, and programs, are needed to
+enable optional features:
+
+  o pkg-config: detection of development libraries
+
+  o Nettle, NSS, or LibTomCrypt: secure hash functions (SECHASH)
+
+  o libcap: dropping root privileges on Linux (DROPROOT)
+
+  o libseccomp: system call filter on Linux (SCFILTER)
+
+  o GnuTLS and Nettle: Network Time Security (NTS)
+
+  o Editline: line editing in chronyc (READLINE)
+
+  o timepps.h header: PPS reference clock
+
+  o Asciidoctor: documentation in HTML format
+
+  o Bash: test suite
+
+The following programs are needed when building chrony from the git repository
+instead of a released tar file:
+
+  o Asciidoctor: manual pages
+
+  o Bison: parser for chronyc settime command
+
 After unpacking the source code, change directory into it, and type
 
 ./configure
@@ -50,9 +79,9 @@
 don't want to enable the support, specify the --disable-sechash flag to
 configure.
 
-If development files for the editline or readline library are available,
-chronyc will be built with line editing support. If you don't want this,
-specify the --disable-readline flag to configure.
+If development files for the editline library are available, chronyc will be
+built with line editing support. If you don't want this, specify the
+--disable-readline flag to configure.
 
 If a timepps.h header is available (e.g. from the LinuxPPS project), chronyd
 will be built with PPS API reference clock driver. If the header is installed
@@ -108,44 +137,6 @@
 attack surface and possibly prevent kernel exploits from chronyd if it is
 compromised.
 
-Support for line editing libraries
-
-chronyc can be built with support for line editing, this allows you to use the
-cursor keys to replay and edit old commands. Two libraries are supported which
-provide such functionality, editline and GNU readline.
-
-Please note that readline since version 6.0 is licensed under GPLv3+ which is
-incompatible with chrony's license GPLv2. You should use editline instead if
-you don't want to use older readline versions.
-
-The configure script will automatically enable the line editing support if one
-of the supported libraries is available. If they are both available, the
-editline library will be used.
-
-If you don't want to use it (in which case chronyc will use a minimal command
-line interface), invoke configure like this:
-
-./configure --disable-readline other-options...
-
-If you have editline, readline or ncurses installed in locations that aren't
-normally searched by the compiler and linker, you need to use extra options:
-
---with-readline-includes=directory_name
-
-    This defines the name of the directory above the one where readline.h is.
-    readline.h is assumed to be in editline or readline subdirectory of the
-    named directory.
-
---with-readline-library=directory_name
-
-    This defines the directory containing the libedit.a or libedit.so file, or
-    libreadline.a or libreadline.so file.
-
---with-ncurses-library=directory_name
-
-    This defines the directory containing the libncurses.a or libncurses.so
-    file.
-
 Extra options for package builders
 
 The configure and make procedures have some extra options that may be useful if
@@ -171,4 +162,4 @@
 to build a package. When untarred within the root directory, this will install
 the files to the intended final locations.
 
-Last updated 2018-04-04 09:18:44 CEST
+Last updated 2021-12-16 13:17:42 +0100
diff --git a/chrony_3_3/Makefile b/chrony/Makefile
similarity index 74%
rename from chrony_3_3/Makefile
rename to chrony/Makefile
index c820b77..93c68ce 100644
--- a/chrony_3_3/Makefile
+++ b/chrony/Makefile
@@ -21,44 +21,42 @@
 #
 # Makefile template
 
-SYSCONFDIR=/etc
-BINDIR=/usr/local/bin
-SBINDIR=/usr/local/sbin
-LOCALSTATEDIR=/var
-CHRONYVARDIR=/var/lib/chrony
+SYSCONFDIR = /etc
+BINDIR = /usr/local/bin
+SBINDIR = /usr/local/sbin
+LOCALSTATEDIR = /var
+CHRONYVARDIR = /var/lib/chrony
+DESTDIR =
 
 CC = gcc
 CFLAGS = -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread
 CPPFLAGS = 
+LDFLAGS =  -pie -Wl,-z,relro,-z,now
 
-DESTDIR=
-
-HASH_OBJ = hash_intmd5.o
+EXTRA_OBJS = sys_generic.o sys_linux.o sys_timex.o sys_posix.o cmdmon.o manual.o pktlength.o ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o addrfilt.o clientlog.o keys.o nameserv.o refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o nameserv_async.o hwclock.o ntp_io_linux.o rtc_linux.o hash_intmd5.o
 
 OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
-       reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
-       smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
+       reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
+       stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
 
-EXTRA_OBJS=sys_generic.o sys_linux.o sys_timex.o cmdmon.o manual.o pktlength.o ntp_core.o ntp_io.o ntp_sources.o addrfilt.o clientlog.o keys.o nameserv.o refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o nameserv_async.o hwclock.o ntp_io_linux.o rtc_linux.o
+EXTRA_CLI_OBJS =  hash_intmd5.o
 
 CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \
-           pktlength.o util.o $(HASH_OBJ)
+           pktlength.o socket.o util.o $(EXTRA_CLI_OBJS)
 
-ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS)
+ALL_OBJS = $(OBJS) $(CLI_OBJS)
 
-LDFLAGS =  -pie -Wl,-z,relro,-z,now
-LIBS = -lm
-
-EXTRA_LIBS=
-EXTRA_CLI_LIBS= 
+LIBS =  -lm 
+EXTRA_LIBS = 
+EXTRA_CLI_LIBS = 
 
 # Until we have a main procedure we can link, just build object files
 # to test compilation
 
 all : chronyd chronyc
 
-chronyd : $(OBJS) $(EXTRA_OBJS)
-	$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
+chronyd : $(OBJS)
+	$(CC) $(CFLAGS) -o chronyd $(OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
 
 chronyc : $(CLI_OBJS)
 	$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
@@ -70,7 +68,9 @@
 	-rm -f Makefile config.h config.log
 
 clean :
-	-rm -f *.o *.s chronyc chronyd core *~
+	$(MAKE) -C test/unit clean
+	-rm -f *.o *.s chronyc chronyd core.* *~
+	-rm -f *.gcda *.gcno
 	-rm -rf .deps
 	-rm -rf *.dSYM
 
@@ -112,10 +112,15 @@
 quickcheck : chronyd chronyc
 	$(MAKE) -C test/unit check
 	cd test/simulation && ./run
+	cd test/system && ./run
 
 check : chronyd chronyc
 	$(MAKE) -C test/unit check
 	cd test/simulation && ./run -i 20 -m 2
+	cd test/system && ./run
+
+print-chronyd-objects :
+	@echo $(OBJS)
 
 Makefile : Makefile.in configure
 	@echo
@@ -129,4 +134,6 @@
 .deps/%.d: %.c | .deps
 	@$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@
 
+ifndef NODEPS
 -include $(ALL_OBJS:%.o=.deps/%.d)
+endif
diff --git a/chrony_3_3/Makefile.in b/chrony/Makefile.in
similarity index 79%
rename from chrony_3_3/Makefile.in
rename to chrony/Makefile.in
index 5a4aeee..9a51df2 100644
--- a/chrony_3_3/Makefile.in
+++ b/chrony/Makefile.in
@@ -21,44 +21,42 @@
 #
 # Makefile template
 
-SYSCONFDIR=@SYSCONFDIR@
-BINDIR=@BINDIR@
-SBINDIR=@SBINDIR@
-LOCALSTATEDIR=@LOCALSTATEDIR@
-CHRONYVARDIR=@CHRONYVARDIR@
+SYSCONFDIR = @SYSCONFDIR@
+BINDIR = @BINDIR@
+SBINDIR = @SBINDIR@
+LOCALSTATEDIR = @LOCALSTATEDIR@
+CHRONYVARDIR = @CHRONYVARDIR@
+DESTDIR =
 
 CC = @CC@
 CFLAGS = @CFLAGS@
 CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
 
-DESTDIR=
-
-HASH_OBJ = @HASH_OBJ@
+EXTRA_OBJS = @EXTRA_OBJS@
 
 OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
-       reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
-       smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
+       reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
+       stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
 
-EXTRA_OBJS=@EXTRA_OBJECTS@
+EXTRA_CLI_OBJS = @EXTRA_CLI_OBJS@
 
 CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \
-           pktlength.o util.o $(HASH_OBJ)
+           pktlength.o socket.o util.o $(EXTRA_CLI_OBJS)
 
-ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS)
+ALL_OBJS = $(OBJS) $(CLI_OBJS)
 
-LDFLAGS = @LDFLAGS@
 LIBS = @LIBS@
-
-EXTRA_LIBS=@EXTRA_LIBS@
-EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@
+EXTRA_LIBS = @EXTRA_LIBS@
+EXTRA_CLI_LIBS = @EXTRA_CLI_LIBS@
 
 # Until we have a main procedure we can link, just build object files
 # to test compilation
 
 all : chronyd chronyc
 
-chronyd : $(OBJS) $(EXTRA_OBJS)
-	$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
+chronyd : $(OBJS)
+	$(CC) $(CFLAGS) -o chronyd $(OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
 
 chronyc : $(CLI_OBJS)
 	$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
@@ -70,7 +68,9 @@
 	-rm -f Makefile config.h config.log
 
 clean :
-	-rm -f *.o *.s chronyc chronyd core *~
+	$(MAKE) -C test/unit clean
+	-rm -f *.o *.s chronyc chronyd core.* *~
+	-rm -f *.gcda *.gcno
 	-rm -rf .deps
 	-rm -rf *.dSYM
 
@@ -112,10 +112,15 @@
 quickcheck : chronyd chronyc
 	$(MAKE) -C test/unit check
 	cd test/simulation && ./run
+	cd test/system && ./run
 
 check : chronyd chronyc
 	$(MAKE) -C test/unit check
 	cd test/simulation && ./run -i 20 -m 2
+	cd test/system && ./run
+
+print-chronyd-objects :
+	@echo $(OBJS)
 
 Makefile : Makefile.in configure
 	@echo
@@ -129,4 +134,6 @@
 .deps/%.d: %.c | .deps
 	@$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@
 
+ifndef NODEPS
 -include $(ALL_OBJS:%.o=.deps/%.d)
+endif
diff --git a/chrony_3_3/NEWS b/chrony/NEWS
similarity index 84%
rename from chrony_3_3/NEWS
rename to chrony/NEWS
index bfbb4ce..6965684 100644
--- a/chrony_3_3/NEWS
+++ b/chrony/NEWS
@@ -1,3 +1,148 @@
+New in version 4.2
+==================
+
+Enhancements
+------------
+* Add support for NTPv4 extension field improving synchronisation
+  stability and resolution of root delay and dispersion (experimental)
+* Add support for NTP over PTP (experimental)
+* Add support for AES-CMAC and hash functions in GnuTLS
+* Improve server interleaved mode to be more reliable and support
+  multiple clients behind NAT
+* Update seccomp filter
+* Add statistics about interleaved mode to serverstats report
+
+Bug fixes
+---------
+* Fix RTC support with 64-bit time_t on 32-bit Linux
+* Fix seccomp filter to work correctly with bind*device directives
+* Suppress kernel adjustments of system clock (dosynctodr) on illumos
+
+Other changes
+-------------
+* Switch Solaris support to illumos
+
+New in version 4.1
+==================
+
+Enhancements
+------------
+* Add support for NTS servers specified by IP address (matching
+  Subject Alternative Name in server certificate)
+* Add source-specific configuration of trusted certificates
+* Allow multiple files and directories with trusted certificates
+* Allow multiple pairs of server keys and certificates
+* Add copy option to server/pool directive
+* Increase PPS lock limit to 40% of pulse interval
+* Perform source selection immediately after loading dump files
+* Reload dump files for addresses negotiated by NTS-KE server
+* Update seccomp filter and add less restrictive level
+* Restart ongoing name resolution on online command
+
+Bug fixes
+---------
+* Fix responding to IPv4 command requests on FreeBSD
+* Fix dump files to not include uncorrected offset
+* Fix initstepslew to accept time from own NTP clients
+* Reset NTP address and port when no longer negotiated by NTS-KE server
+
+New in version 4.0
+==================
+
+Enhancements
+------------
+* Add support for Network Time Security (NTS) authentication
+* Add support for AES-CMAC keys (AES128, AES256) with Nettle
+* Add authselectmode directive to control selection of unauthenticated sources
+* Add binddevice, bindacqdevice, bindcmddevice directives
+* Add confdir directive to better support fragmented configuration
+* Add sourcedir directive and "reload sources" command to support dynamic
+  NTP sources specified in files
+* Add clockprecision directive
+* Add dscp directive to set Differentiated Services Code Point (DSCP)
+* Add -L option to limit log messages by severity
+* Add -p option to print whole configuration with included files
+* Add -U option to allow start under non-root user
+* Allow maxsamples to be set to 1 for faster update with -q/-Q option
+* Avoid replacing NTP sources with sources that have unreachable address
+* Improve pools to repeat name resolution to get "maxsources" sources
+* Improve source selection with trusted sources
+* Improve NTP loop test to prevent synchronisation to itself
+* Repeat iburst when NTP source is switched from offline state to online
+* Update clock synchronisation status and leap status more frequently
+* Update seccomp filter
+* Add "add pool" command
+* Add "reset sources" command to drop all measurements
+* Add authdata command to print details about NTP authentication
+* Add selectdata command to print details about source selection
+* Add -N option and sourcename command to print original names of sources
+* Add -a option to some commands to print also unresolved sources
+* Add -k, -p, -r options to clients command to select, limit, reset data
+
+Bug fixes
+---------
+* Don't set interface for NTP responses to allow asymmetric routing
+* Handle RTCs that don't support interrupts
+* Respond to command requests with correct address on multihomed hosts
+
+Removed features
+----------------
+* Drop support for RIPEMD keys (RMD128, RMD160, RMD256, RMD320)
+* Drop support for long (non-standard) MACs in NTPv4 packets (chrony 2.x
+  clients using non-MD5/SHA1 keys need to use option "version 3")
+* Drop support for line editing with GNU Readline
+
+New in version 3.5.1
+====================
+
+Security fixes
+--------------
+* Create new file when writing pidfile (CVE-2020-14367)
+
+New in version 3.5
+==================
+
+Enhancements
+------------
+* Add support for more accurate reading of PHC on Linux 5.0
+* Add support for hardware timestamping on interfaces with read-only
+  timestamping configuration
+* Add support for memory locking and real-time priority on FreeBSD,
+  NetBSD, Solaris
+* Update seccomp filter to work on more architectures
+* Validate refclock driver options
+
+Bug fixes
+---------
+* Fix bindaddress directive on FreeBSD
+* Fix transposition of hardware RX timestamp on Linux 4.13 and later
+* Fix building on non-glibc systems
+
+New in version 3.4
+==================
+
+Enhancements
+------------
+* Add filter option to server/pool/peer directive
+* Add minsamples and maxsamples options to hwtimestamp directive
+* Add support for faster frequency adjustments in Linux 4.19
+* Change default pidfile to /var/run/chrony/chronyd.pid to allow
+  chronyd without root privileges to remove it on exit
+* Disable sub-second polling intervals for distant NTP sources
+* Extend range of supported sub-second polling intervals
+* Get/set IPv4 destination/source address of NTP packets on FreeBSD
+* Make burst options and command useful with short polling intervals
+* Modify auto_offline option to activate when sending request failed
+* Respond from interface that received NTP request if possible
+* Add onoffline command to switch between online and offline state
+  according to current system network configuration
+* Improve example NetworkManager dispatcher script
+
+Bug fixes
+---------
+* Avoid waiting in Linux getrandom system call
+* Fix PPS support on FreeBSD and NetBSD
+
 New in version 3.3
 ==================
 
diff --git a/chrony/README b/chrony/README
new file mode 100644
index 0000000..00214a5
--- /dev/null
+++ b/chrony/README
@@ -0,0 +1,160 @@
+This is the README for chrony.
+
+What is chrony?
+===============
+
+chrony is a versatile implementation of the Network Time Protocol (NTP).
+It can synchronise the system clock with NTP servers, reference clocks
+(e.g. GPS receiver), and manual input using wristwatch and keyboard.
+It can also operate as an NTPv4 (RFC 5905) server and peer to provide
+a time service to other computers in the network.
+
+It is designed to perform well in a wide range of conditions, including
+intermittent network connections, heavily congested networks, changing
+temperatures (ordinary computer clocks are sensitive to temperature),
+and systems that do not run continuosly, or run on a virtual machine.
+
+Typical accuracy between two machines synchronised over the Internet is
+within a few milliseconds; on a LAN, accuracy is typically in tens of
+microseconds.  With hardware timestamping, or a hardware reference clock,
+sub-microsecond accuracy may be possible.
+
+Two programs are included in chrony, chronyd is a daemon that can be
+started at boot time and chronyc is a command-line interface program
+which can be used to monitor chronyd's performance and to change various
+operating parameters whilst it is running.
+
+What will chrony run on?
+========================
+
+The software is known to work on Linux, FreeBSD, NetBSD, macOS and
+illumos.  Closely related systems may work too.  Any other system will
+likely require a porting exercise.
+
+How do I set it up?
+===================
+
+The file INSTALL gives instructions.  On supported systems the
+compilation process should be automatic.  You will need a C compiler,
+e.g. gcc or clang.
+
+What documentation is there?
+============================
+
+The distribution includes manual pages and a document containing
+Frequently Asked Questions (FAQ).
+
+The documentation is also available on the chrony web pages, accessible
+through the URL 
+
+    https://chrony.tuxfamily.org/
+
+Where are new versions announced?
+=================================
+
+There is a low volume mailing list where new versions and other
+important news relating to chrony are announced.  You can join this list
+by sending mail with the subject "subscribe" to
+
+chrony-announce-request@chrony.tuxfamily.org
+
+How can I get support for chrony?
+=================================
+
+There are two other mailing lists relating to chrony.  chrony-users is a
+discussion list for users, e.g. for questions about chrony configuration
+and bug reports.  chrony-dev is a more technical list for developers,
+e.g. for submitting patches and discussing how new features should be
+implemented.  To subscribe to either of these lists, send a message with
+the subject "subscribe" to
+
+chrony-users-request@chrony.tuxfamily.org
+or
+chrony-dev-request@chrony.tuxfamily.org
+
+as applicable.
+
+License
+=======
+
+chrony is distributed under the GNU General Public License version 2.
+
+Authors
+=======
+
+Richard P. Curnow <rc@rc0.org.uk>
+Miroslav Lichvar <mlichvar@redhat.com>
+
+Acknowledgements
+================
+
+In writing the chronyd program, extensive use has been made of the NTPv3 (RFC
+1305) and NTPv4 (RFC 5905) specification.  The source code of the xntpd/ntpd
+implementation written by Dennis Fergusson, Lars Mathiesen, David Mills, and
+others has been used to check the details of the protocol.
+
+The following people have provided patches and other major contributions
+to chrony:
+
+Lonnie Abelbeck <lonnie@abelbeck.com>
+Benny Lyne Amorsen <benny@amorsen.dk>
+Andrew Bishop <amb@gedanken.demon.co.uk>
+Vincent Blut <vincent.debian@free.fr>
+Stephan I. Boettcher <stephan@nevis1.columbia.edu>
+David Bohman <debohman@gmail.com>
+Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
+Leigh Brown <leigh@solinno.co.uk>
+Erik Bryer <ebryer@spots.ab.ca>
+Jonathan Cameron <jic23@cam.ac.uk>
+Bryan Christianson <bryan@whatroute.net>
+Juliusz Chroboczek <jch@pps.jussieu.fr>
+Kamil Dudka <kdudka@redhat.com>
+Christian Ehrhardt <christian.ehrhardt@canonical.com>
+Paul Elliott <pelliott@io.com>
+Robert Fairley <rfairley@redhat.com>
+Stefan R. Filipek <srfilipek@gmail.com>
+Mike Fleetwood <mike@rockover.demon.co.uk>
+Alexander Gretencord <arutha@gmx.de>
+Andrew Griffiths <agriffit@redhat.com>
+Walter Haidinger <walter.haidinger@gmx.at>
+Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
+John Hasler <john@dhh.gt.org>
+Tjalling Hattink <t.hattink@fugro.nl>
+Liam Hatton <me@liamhatton.com>
+Jachym Holecek <jakym@volny.cz>
+Håkan Johansson <f96hajo@chalmers.se>
+Jim Knoble <jmknoble@pobox.com>
+Antti Jrvinen <costello@iki.fi>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Eric Lammerts <eric@lammerts.org>
+Stefan Lucke <stefan@lucke.in-berlin.de>
+Victor Lum <viclum@vanu.com>
+Kevin Lyda <kevin@ie.suberic.net>
+Paul Menzel <paulepanter@users.sourceforge.net>
+Vladimir Michl <vladimir.michl@seznam.cz>
+Victor Moroz <vim@prv.adlum.ru>
+Kalle Olavi Niemitalo  <tosi@stekt.oulu.fi>
+Frank Otto <sandwichmacher@web.de>
+Denny Page <dennypage@me.com>
+Chris Perl <cperl@janestreet.com>
+Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
+Andreas Piesk <apiesk@virbus.de>
+Baruch Siach <baruch@tkos.co.il>
+Foster Snowhill <forst@forstwoof.ru>
+Andreas Steinmetz <ast@domdv.de>
+NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
+Timo Teras <timo.teras@iki.fi>
+Bill Unruh <unruh@physics.ubc.ca>
+Stephen Wadeley <swadeley@redhat.com>
+Bernhard Weiss <lisnablagh@web.de>
+Wolfgang Weisselberg <weissel@netcologne.de>
+Bernhard M. Wiedemann <bwiedemann@suse.de>
+Joachim Wiedorn <ad_debian@joonet.de>
+Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
+Michael Witten <mfwitten@gmail.com>
+Doug Woodward <dougw@whistler.com>
+Thomas Zajic <zlatko@zlatko.fdns.net>
+
+Many other people have contributed bug reports and suggestions.  We are sorry
+we cannot identify all of you individually.
diff --git a/chrony_3_3/addressing.h b/chrony/addressing.h
similarity index 89%
rename from chrony_3_3/addressing.h
rename to chrony/addressing.h
index 9ecc18b..3e311fa 100644
--- a/chrony_3_3/addressing.h
+++ b/chrony/addressing.h
@@ -30,16 +30,19 @@
 #include "sysincl.h"
 
 /* This type is used to represent an IPv4 address or IPv6 address.
+   Addresses which are not resolved yet can be represented with an ID.
    All parts are in HOST order, NOT network order. */
 
 #define IPADDR_UNSPEC 0
 #define IPADDR_INET4 1
 #define IPADDR_INET6 2
+#define IPADDR_ID 3
 
 typedef struct {
   union { 
     uint32_t in4;
     uint8_t in6[16];
+    uint32_t id;
   } addr;
   uint16_t family;
   uint16_t _pad;
@@ -47,8 +50,10 @@
 
 typedef struct {
   IPAddr ip_addr;
-  unsigned short port;
-} NTP_Remote_Address;
+  uint16_t port;
+} IPSockAddr;
+
+typedef IPSockAddr NTP_Remote_Address;
 
 #define INVALID_IF_INDEX -1
 
diff --git a/chrony_3_3/addrfilt.c b/chrony/addrfilt.c
similarity index 99%
rename from chrony_3_3/addrfilt.c
rename to chrony/addrfilt.c
index dd16700..6208b46 100644
--- a/chrony_3_3/addrfilt.c
+++ b/chrony/addrfilt.c
@@ -247,6 +247,8 @@
           set_subnet(&table->base6, ip6, 4, 0, new_state, delete_children) == ADF_SUCCESS)
         return ADF_SUCCESS;
       break;
+    default:
+      break;
   }
 
   return ADF_BADSUBNET;
@@ -359,9 +361,9 @@
     case IPADDR_INET6:
       split_ip6(ip_addr, ip6);
       return check_ip_in_node(&table->base6, ip6);
+    default:
+      return 0;
   }
-
-  return 0;
 }
 
 /* ================================================== */
diff --git a/chrony_3_3/addrfilt.h b/chrony/addrfilt.h
similarity index 100%
rename from chrony_3_3/addrfilt.h
rename to chrony/addrfilt.h
diff --git a/chrony_3_3/array.c b/chrony/array.c
similarity index 100%
rename from chrony_3_3/array.c
rename to chrony/array.c
diff --git a/chrony_3_3/array.h b/chrony/array.h
similarity index 100%
rename from chrony_3_3/array.h
rename to chrony/array.h
diff --git a/chrony_3_3/candm.h b/chrony/candm.h
similarity index 85%
rename from chrony_3_3/candm.h
rename to chrony/candm.h
index 6ffbb39..dd99413 100644
--- a/chrony_3_3/candm.h
+++ b/chrony/candm.h
@@ -100,7 +100,16 @@
 #define REQ_ADD_SERVER3 60
 #define REQ_ADD_PEER3 61
 #define REQ_SHUTDOWN 62
-#define N_REQUEST_TYPES 63
+#define REQ_ONOFFLINE 63
+#define REQ_ADD_SOURCE 64
+#define REQ_NTP_SOURCE_NAME 65
+#define REQ_RESET_SOURCES 66
+#define REQ_AUTH_DATA 67
+#define REQ_CLIENT_ACCESSES_BY_INDEX3 68
+#define REQ_SELECT_DATA 69
+#define REQ_RELOAD_SOURCES 70
+#define REQ_DOFFSET2 71
+#define N_REQUEST_TYPES 72
 
 /* Structure used to exchange timespecs independent of time_t size */
 typedef struct {
@@ -244,6 +253,11 @@
   int32_t EOR;
 } REQ_Ac_Check;
 
+/* Source types in NTP source requests */
+#define REQ_ADDSRC_SERVER 1
+#define REQ_ADDSRC_PEER 2
+#define REQ_ADDSRC_POOL 3
+
 /* Flags used in NTP source requests */
 #define REQ_ADDSRC_ONLINE 0x1
 #define REQ_ADDSRC_AUTOOFFLINE 0x2
@@ -254,9 +268,13 @@
 #define REQ_ADDSRC_REQUIRE 0x40
 #define REQ_ADDSRC_INTERLEAVED 0x80
 #define REQ_ADDSRC_BURST 0x100
+#define REQ_ADDSRC_NTS 0x200
+#define REQ_ADDSRC_COPY 0x400
+#define REQ_ADDSRC_EF_EXP1 0x800
 
 typedef struct {
-  IPAddr ip_addr;
+  uint32_t type;
+  uint8_t name[256];
   uint32_t port;
   int32_t minpoll;
   int32_t maxpoll;
@@ -268,6 +286,7 @@
   int32_t min_samples;
   int32_t max_samples;
   uint32_t authkey;
+  uint32_t nts_port;
   Float max_delay;
   Float max_delay_ratio;
   Float max_delay_dev_ratio;
@@ -275,7 +294,9 @@
   Float asymmetry;
   Float offset;
   uint32_t flags;
-  uint32_t reserved[4];
+  int32_t filter_length;
+  uint32_t cert_set;
+  uint32_t reserved[2];
   int32_t EOR;
 } REQ_NTP_Source;
 
@@ -290,8 +311,7 @@
 } REQ_Dfreq;
 
 typedef struct {
-  int32_t sec;
-  int32_t usec;
+  Float doffset;
   int32_t EOR;
 } REQ_Doffset;
 
@@ -307,6 +327,8 @@
 typedef struct {
   uint32_t first_index;
   uint32_t n_clients;
+  uint32_t min_hits;
+  uint32_t reset;
   int32_t EOR;
 } REQ_ClientAccessesByIndex;
 
@@ -333,6 +355,21 @@
   int32_t EOR;
 } REQ_NTPData;
 
+typedef struct {
+  IPAddr ip_addr;
+  int32_t EOR;
+} REQ_NTPSourceName;
+
+typedef struct {
+  IPAddr ip_addr;
+  int32_t EOR;
+} REQ_AuthData;
+
+typedef struct {
+  uint32_t index;
+  int32_t EOR;
+} REQ_SelectData;
+
 /* ================================================== */
 
 #define PKT_TYPE_CMD_REQUEST 1
@@ -369,9 +406,10 @@
    domain socket.
 
    Version 6 (no authentication) : changed format of client accesses by index
-   (using new request/reply types) and manual timestamp, added new fields and
+   (two times), delta offset, and manual timestamp, added new fields and
    flags to NTP source request and report, made length of manual list constant,
-   added new commands: ntpdata, refresh, serverstats, shutdown
+   added new commands: authdata, ntpdata, onoffline, refresh, reset,
+   selectdata, serverstats, shutdown, sourcename
  */
 
 #define PROTO_VERSION_NUMBER 6
@@ -385,8 +423,8 @@
 #define PROTO_VERSION_PADDING 6
 
 /* The maximum length of padding in request packet, currently
-   defined by MANUAL_LIST */
-#define MAX_PADDING_LENGTH 396
+   defined by CLIENT_ACCESSES_BY_INDEX3 */
+#define MAX_PADDING_LENGTH 484
 
 /* ================================================== */
 
@@ -435,6 +473,9 @@
     REQ_ReselectDistance reselect_distance;
     REQ_SmoothTime smoothtime;
     REQ_NTPData ntp_data;
+    REQ_NTPSourceName ntp_source_name;
+    REQ_AuthData auth_data;
+    REQ_SelectData select_data;
   } data; /* Command specific parameters */
 
   /* Padding used to prevent traffic amplification.  It only defines the
@@ -471,7 +512,13 @@
 #define RPY_NTP_DATA 16
 #define RPY_MANUAL_TIMESTAMP2 17
 #define RPY_MANUAL_LIST2 18
-#define N_REPLY_TYPES 19
+#define RPY_NTP_SOURCE_NAME 19
+#define RPY_AUTH_DATA 20
+#define RPY_CLIENT_ACCESSES_BY_INDEX3 21
+#define RPY_SERVER_STATS2 22
+#define RPY_SELECT_DATA 23
+#define RPY_SERVER_STATS3 24
+#define N_REPLY_TYPES 25
 
 /* Status codes */
 #define STT_SUCCESS 0
@@ -484,8 +531,7 @@
 #define STT_BADSUBNET 7
 #define STT_ACCESSALLOWED 8
 #define STT_ACCESSDENIED 9
-/* Deprecated */
-#define STT_NOHOSTACCESS 10
+#define STT_NOHOSTACCESS 10 /* Deprecated */
 #define STT_SOURCEALREADYKNOWN 11
 #define STT_TOOMANYSOURCES 12
 #define STT_NORTC 13
@@ -495,6 +541,7 @@
 #define STT_INVALIDAF 17
 #define STT_BADPKTVERSION 18
 #define STT_BADPKTLENGTH 19
+#define STT_INVALIDNAME 21
 
 typedef struct {
   int32_t EOR;
@@ -509,17 +556,12 @@
 #define RPY_SD_MD_PEER   1
 #define RPY_SD_MD_REF    2
 
-#define RPY_SD_ST_SYNC 0
-#define RPY_SD_ST_UNREACH 1
+#define RPY_SD_ST_SELECTED 0
+#define RPY_SD_ST_NONSELECTABLE 1
 #define RPY_SD_ST_FALSETICKER 2
 #define RPY_SD_ST_JITTERY 3
-#define RPY_SD_ST_CANDIDATE 4
-#define RPY_SD_ST_OUTLIER 5
-
-#define RPY_SD_FLAG_NOSELECT 0x1
-#define RPY_SD_FLAG_PREFER 0x2
-#define RPY_SD_FLAG_TRUST 0x4
-#define RPY_SD_FLAG_REQUIRE 0x8
+#define RPY_SD_ST_UNSELECTED 4
+#define RPY_SD_ST_SELECTABLE 5
 
 typedef struct {
   IPAddr ip_addr;
@@ -588,14 +630,17 @@
 typedef struct {
   IPAddr ip;
   uint32_t ntp_hits;
+  uint32_t nke_hits;
   uint32_t cmd_hits;
   uint32_t ntp_drops;
+  uint32_t nke_drops;
   uint32_t cmd_drops;
   int8_t ntp_interval;
+  int8_t nke_interval;
   int8_t cmd_interval;
   int8_t ntp_timeout_interval;
-  int8_t pad;
   uint32_t last_ntp_hit_ago;
+  uint32_t last_nke_hit_ago;
   uint32_t last_cmd_hit_ago;
 } RPY_ClientAccesses_Client;
 
@@ -609,10 +654,16 @@
 
 typedef struct {
   uint32_t ntp_hits;
+  uint32_t nke_hits;
   uint32_t cmd_hits;
   uint32_t ntp_drops;
+  uint32_t nke_drops;
   uint32_t cmd_drops;
   uint32_t log_drops;
+  uint32_t ntp_auth_hits;
+  uint32_t ntp_interleaved_hits;
+  uint32_t ntp_timestamps;
+  uint32_t ntp_span_seconds;
   int32_t EOR;
 } RPY_ServerStats;
 
@@ -687,6 +738,50 @@
 } RPY_NTPData;
 
 typedef struct {
+  uint8_t name[256];
+  int32_t EOR;
+} RPY_NTPSourceName;
+
+#define RPY_AD_MD_NONE 0
+#define RPY_AD_MD_SYMMETRIC 1
+#define RPY_AD_MD_NTS 2
+
+typedef struct {
+  uint16_t mode;
+  uint16_t key_type;
+  uint32_t key_id;
+  uint16_t key_length;
+  uint16_t ke_attempts;
+  uint32_t last_ke_ago;
+  uint16_t cookies;
+  uint16_t cookie_length;
+  uint16_t nak;
+  uint16_t pad;
+  int32_t EOR;
+} RPY_AuthData;
+
+#define RPY_SD_OPTION_NOSELECT 0x1
+#define RPY_SD_OPTION_PREFER 0x2
+#define RPY_SD_OPTION_TRUST 0x4
+#define RPY_SD_OPTION_REQUIRE 0x8
+
+typedef struct {
+  uint32_t ref_id;
+  IPAddr ip_addr;
+  uint8_t state_char;
+  uint8_t authentication;
+  uint8_t leap;
+  uint8_t pad;
+  uint16_t conf_options;
+  uint16_t eff_options;
+  uint32_t last_sample_ago;
+  Float score;
+  Float lo_limit;
+  Float hi_limit;
+  int32_t EOR;
+} RPY_SelectData;
+
+typedef struct {
   uint8_t version;
   uint8_t pkt_type;
   uint8_t res1;
@@ -715,6 +810,9 @@
     RPY_Activity activity;
     RPY_Smoothing smoothing;
     RPY_NTPData ntp_data;
+    RPY_NTPSourceName ntp_source_name;
+    RPY_AuthData auth_data;
+    RPY_SelectData select_data;
   } data; /* Reply specific parameters */
 
 } CMD_Reply;
diff --git a/chrony_3_3/client.c b/chrony/client.c
similarity index 76%
rename from chrony_3_3/client.c
rename to chrony/client.c
index 3c605c1..ed551c6 100644
--- a/chrony_3_3/client.c
+++ b/chrony/client.c
@@ -3,8 +3,8 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Lonnie Abelbeck  2016
- * Copyright (C) Miroslav Lichvar  2009-2018
+ * Copyright (C) Lonnie Abelbeck  2016, 2018
+ * Copyright (C) Miroslav Lichvar  2009-2021
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -33,51 +33,49 @@
 
 #include "array.h"
 #include "candm.h"
+#include "cmac.h"
 #include "logging.h"
 #include "memory.h"
 #include "nameserv.h"
 #include "getdate.h"
 #include "cmdparse.h"
 #include "pktlength.h"
+#include "socket.h"
 #include "util.h"
 
 #ifdef FEAT_READLINE
-#ifdef USE_EDITLINE
 #include <editline/readline.h>
-#else
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
 #endif
 
 /* ================================================== */
 
-union sockaddr_all {
-  struct sockaddr_in in4;
-#ifdef FEAT_IPV6
-  struct sockaddr_in6 in6;
-#endif
-  struct sockaddr_un un;
-  struct sockaddr sa;
+struct Address {
+  SCK_AddressType type;
+  union {
+    IPSockAddr ip;
+    char *path;
+  } addr;
 };
 
-static ARR_Instance sockaddrs;
+static ARR_Instance server_addresses;
 
 static int sock_fd = -1;
 
-static int quit = 0;
+static volatile int quit = 0;
 
 static int on_terminal = 0;
 
 static int no_dns = 0;
 
+static int source_names = 0;
+
 static int csv_mode = 0;
 
 /* ================================================== */
 /* Log a message. This is a minimalistic replacement of the logging.c
    implementation to avoid linking with it and other modules. */
 
-int log_debug_enabled = 0;
+LOG_Severity log_min_severity = LOGS_INFO;
 
 void LOG_Message(LOG_Severity severity,
 #if DEBUG > 0
@@ -87,6 +85,9 @@
 {
   va_list ap;
 
+  if (severity < log_min_severity)
+    return;
+
   va_start(ap, format);
   vfprintf(stderr, format, ap);
   putc('\n', stderr);
@@ -111,7 +112,7 @@
     char *cmd;
 
     rl_attempted_completion_function = command_name_completion;
-    rl_basic_word_break_characters = "\t\n\r";
+    rl_basic_word_break_characters = " \t\n\r";
 
     /* save line only if not empty */
     cmd = readline(prompt);
@@ -145,15 +146,15 @@
 /* ================================================== */
 
 static ARR_Instance
-get_sockaddrs(const char *hostnames, int port)
+get_addresses(const char *hostnames, int port)
 {
+  struct Address *addr;
   ARR_Instance addrs;
   char *hostname, *s1, *s2;
   IPAddr ip_addrs[DNS_MAX_ADDRESSES];
-  union sockaddr_all *addr;
   int i;
 
-  addrs = ARR_CreateInstance(sizeof (union sockaddr_all));
+  addrs = ARR_CreateInstance(sizeof (*addr));
   s1 = Strdup(hostnames);
 
   /* Parse the comma-separated list of hostnames */
@@ -164,11 +165,9 @@
 
     /* hostname starting with / is considered a path of Unix domain socket */
     if (hostname[0] == '/') {
-      addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
-      if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >=
-          sizeof (addr->un.sun_path))
-        LOG_FATAL("Unix socket path too long");
-      addr->un.sun_family = AF_UNIX;
+      addr = ARR_GetNewElement(addrs);
+      addr->type = SCK_ADDR_UNIX;
+      addr->addr.path = Strdup(hostname);
     } else {
       if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
         DEBUG_LOG("Could not get IP address for %s", hostname);
@@ -176,8 +175,10 @@
       }
 
       for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
-        addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
-        UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr);
+        addr = ARR_GetNewElement(addrs);
+        addr->type = SCK_ADDR_IP;
+        addr->addr.ip.ip_addr = ip_addrs[i];
+        addr->addr.ip.port = port;
         DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
       }
     }
@@ -188,69 +189,59 @@
 }
 
 /* ================================================== */
+
+static void
+free_addresses(ARR_Instance addresses)
+{
+  struct Address *addr;
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(addresses); i++) {
+    addr = ARR_GetElement(addresses, i);
+
+    if (addr->type == SCK_ADDR_UNIX)
+      Free(addr->addr.path);
+  }
+
+  ARR_DestroyInstance(addresses);
+}
+
+/* ================================================== */
 /* Initialise the socket used to talk to the daemon */
 
 static int
-prepare_socket(union sockaddr_all *addr)
+open_socket(struct Address *addr)
 {
-  socklen_t addr_len;
-  char *dir;
+  char *dir, *local_addr;
+  size_t local_addr_len;
 
-  switch (addr->sa.sa_family) {
-    case AF_UNIX:
-      addr_len = sizeof (addr->un);
+  switch (addr->type) {
+    case SCK_ADDR_IP:
+      sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
       break;
-    case AF_INET:
-      addr_len = sizeof (addr->in4);
+    case SCK_ADDR_UNIX:
+      /* Construct path of our socket.  Use the same directory as the server
+         socket and include our process ID to allow multiple chronyc instances
+         running at the same time. */
+
+      dir = UTI_PathToDir(addr->addr.path);
+      local_addr_len = strlen(dir) + 50;
+      local_addr = Malloc(local_addr_len);
+
+      snprintf(local_addr, local_addr_len, "%s/chronyc.%d.sock", dir, (int)getpid());
+
+      sock_fd = SCK_OpenUnixDatagramSocket(addr->addr.path, local_addr,
+                                           SCK_FLAG_ALL_PERMISSIONS);
+      Free(dir);
+      Free(local_addr);
+
       break;
-#ifdef FEAT_IPV6
-    case AF_INET6:
-      addr_len = sizeof (addr->in6);
-      break;
-#endif
     default:
       assert(0);
   }
 
-  sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
-
-  if (sock_fd < 0) {
-    DEBUG_LOG("Could not create socket : %s", strerror(errno));
+  if (sock_fd < 0)
     return 0;
-  }
-
-  if (addr->sa.sa_family == AF_UNIX) {
-    struct sockaddr_un sa_un;
-
-    /* Construct path of our socket.  Use the same directory as the server
-       socket and include our process ID to allow multiple chronyc instances
-       running at the same time. */
-    dir = UTI_PathToDir(addr->un.sun_path);
-    if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path),
-                 "%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path))
-      LOG_FATAL("Unix socket path too long");
-    Free(dir);
-
-    sa_un.sun_family = AF_UNIX;
-    unlink(sa_un.sun_path);
-
-    /* Bind the socket to the path */
-    if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) {
-      DEBUG_LOG("Could not bind socket : %s", strerror(errno));
-      return 0;
-    }
-
-    /* Allow server without root privileges to send replies to our socket */
-    if (chmod(sa_un.sun_path, 0666) < 0) {
-      DEBUG_LOG("Could not change socket permissions : %s", strerror(errno));
-      return 0;
-    }
-  }
-
-  if (connect(sock_fd, &addr->sa, addr_len) < 0) {
-    DEBUG_LOG("Could not connect socket : %s", strerror(errno));
-    return 0;
-  }
 
   return 1;
 }
@@ -260,20 +251,11 @@
 static void
 close_io(void)
 {
-  union sockaddr_all addr;
-  socklen_t addr_len = sizeof (addr);
-
   if (sock_fd < 0)
     return;
 
-  /* Remove our Unix domain socket */
-  if (getsockname(sock_fd, &addr.sa, &addr_len) < 0)
-    LOG_FATAL("getsockname() failed : %s", strerror(errno));
-  if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) &&
-      addr.sa.sa_family == AF_UNIX)
-    unlink(addr.un.sun_path);
-
-  close(sock_fd);
+  SCK_RemoveSocket(sock_fd);
+  SCK_CloseSocket(sock_fd);
   sock_fd = -1;
 }
 
@@ -283,7 +265,7 @@
 open_io(void)
 {
   static unsigned int address_index = 0;
-  union sockaddr_all *addr;
+  struct Address *addr;
 
   /* If a socket is already opened, close it and try the next address */
   if (sock_fd >= 0) {
@@ -292,11 +274,10 @@
   }
 
   /* Find an address for which a socket can be opened and connected */
-  for (; address_index < ARR_GetSize(sockaddrs); address_index++) {
-    addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index);
-    DEBUG_LOG("Opening connection to %s", UTI_SockaddrToString(&addr->sa));
+  for (; address_index < ARR_GetSize(server_addresses); address_index++) {
+    addr = ARR_GetElement(server_addresses, address_index);
 
-    if (prepare_socket(addr))
+    if (open_socket(addr))
       return 1;
 
     close_io();
@@ -334,6 +315,9 @@
       for (; i < 16; i++)
         mask->addr.in6[i] = 0x0;
       break;
+    case IPADDR_ID:
+      mask->family = IPADDR_UNSPEC;
+      break;
     default:
       assert(0);
   }
@@ -342,6 +326,20 @@
 /* ================================================== */
 
 static int
+parse_source_address(char *word, IPAddr *address)
+{
+  if (UTI_StringToIdIP(word, address))
+    return 1;
+
+  if (DNS_Name2IPAddress(word, address, 1) == DNS_Success)
+    return 1;
+
+  return 0;
+}
+
+/* ================================================== */
+
+static int
 read_mask_address(char *line, IPAddr *mask, IPAddr *address)
 {
   unsigned int bits;
@@ -367,7 +365,7 @@
         }
       }
     } else {
-      if (DNS_Name2IPAddress(p, address, 1) == DNS_Success) {
+      if (parse_source_address(p, address)) {
         bits_to_mask(-1, address->family, mask);
         return 1;
       } else {
@@ -426,6 +424,14 @@
 
 /* ================================================== */
 
+static void
+process_cmd_onoffline(CMD_Request *msg, char *line)
+{
+  msg->command = htons(REQ_ONOFFLINE);
+}
+
+/* ================================================== */
+
 static int
 read_address_integer(char *line, IPAddr *address, int *value)
 {
@@ -439,7 +445,7 @@
     LOG(LOGS_ERR, "Invalid syntax for address value");
     ok = 0;
   } else {
-    if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
+    if (!parse_source_address(hostname, address)) {
       LOG(LOGS_ERR, "Could not get address for hostname");
       ok = 0;
     } else {
@@ -467,7 +473,7 @@
     LOG(LOGS_ERR, "Invalid syntax for address value");
     ok = 0;
   } else {
-    if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
+    if (!parse_source_address(hostname, address)) {
       LOG(LOGS_ERR, "Could not get address for hostname");
       ok = 0;
     } else {
@@ -773,205 +779,21 @@
 /* ================================================== */
 
 static int
-parse_allow_deny(CMD_Request *msg, char *line)
+process_cmd_allowdeny(CMD_Request *msg, char *line, int cmd, int allcmd)
 {
-  unsigned long a, b, c, d;
-  int n, specified_subnet_bits;
+  int all, subnet_bits;
   IPAddr ip;
-  char *p;
   
-  p = line;
-  if (!*p) {
-    /* blank line - applies to all addresses */
-    ip.family = IPADDR_UNSPEC;
-    UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
-    msg->data.allow_deny.subnet_bits = htonl(0);
-  } else {
-    char *slashpos;
-    slashpos = strchr(p, '/');
-    if (slashpos) *slashpos = 0;
-    
-    n = 0;
-    if (!UTI_StringToIP(p, &ip) &&
-        (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) {
-
-      /* Try to parse as the name of a machine */
-      if (slashpos || DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
-        LOG(LOGS_ERR, "Could not read address");
-        return 0;
-      } else {
-        UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
-        if (ip.family == IPADDR_INET6)
-          msg->data.allow_deny.subnet_bits = htonl(128);
-        else
-          msg->data.allow_deny.subnet_bits = htonl(32);
-      }        
-    } else {
-      
-      if (n == 0) {
-        if (ip.family == IPADDR_INET6)
-          msg->data.allow_deny.subnet_bits = htonl(128);
-        else
-          msg->data.allow_deny.subnet_bits = htonl(32);
-      } else {
-        ip.family = IPADDR_INET4;
-
-        a &= 0xff;
-        b &= 0xff;
-        c &= 0xff;
-        d &= 0xff;
-        
-        switch (n) {
-          case 1:
-            ip.addr.in4 = htonl((a<<24));
-            msg->data.allow_deny.subnet_bits = htonl(8);
-            break;
-          case 2:
-            ip.addr.in4 = htonl((a<<24) | (b<<16));
-            msg->data.allow_deny.subnet_bits = htonl(16);
-            break;
-          case 3:
-            ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8));
-            msg->data.allow_deny.subnet_bits = htonl(24);
-            break;
-          case 4:
-            ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8) | d);
-            msg->data.allow_deny.subnet_bits = htonl(32);
-            break;
-          default:
-            assert(0);
-        }
-      }
-
-      UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
-
-      if (slashpos) {
-        n = sscanf(slashpos+1, "%d", &specified_subnet_bits);
-        if (n == 1) {
-          msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits);
-        } else {
-          LOG(LOGS_WARN, "Warning: badly formatted subnet size, using %d",
-              (int)ntohl(msg->data.allow_deny.subnet_bits));
-        }
-      } 
-    }
-  }
-  return 1;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_allow(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_ALLOW);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_allowall(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_ALLOWALL);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_deny(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_DENY);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_denyall(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_DENYALL);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_cmdallow(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_CMDALLOW);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_cmdallowall(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_CMDALLOWALL);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_cmddeny(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_CMDDENY);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-process_cmd_cmddenyall(CMD_Request *msg, char *line)
-{
-  int status;
-  msg->command = htons(REQ_CMDDENYALL);
-  status = parse_allow_deny(msg, line);
-  return status;
-}
-
-/* ================================================== */
-
-static int
-accheck_getaddr(char *line, IPAddr *addr)
-{
-  unsigned long a, b, c, d;
-  IPAddr ip;
-  char *p;
-  p = line;
-  if (!*p) {
+  if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) {
+    LOG(LOGS_ERR, "Could not read address");
     return 0;
-  } else {
-    if (sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d) == 4) {
-      addr->family = IPADDR_INET4;
-      addr->addr.in4 = (a<<24) | (b<<16) | (c<<8) | d;
-      return 1;
-    } else {
-      if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
-        return 0;
-      } else {
-        *addr = ip;
-        return 1;
-      }
-    }
   }
+
+  msg->command = htons(all ? allcmd : cmd);
+  UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
+  msg->data.allow_deny.subnet_bits = htonl(subnet_bits);
+
+  return 1;
 }
 
 /* ================================================== */
@@ -981,7 +803,7 @@
 {
   IPAddr ip;
   msg->command = htons(REQ_ACCHECK);
-  if (accheck_getaddr(line, &ip)) {
+  if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
     UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
     return 1;
   } else {    
@@ -997,7 +819,7 @@
 {
   IPAddr ip;
   msg->command = htons(REQ_CMDACCHECK);
-  if (accheck_getaddr(line, &ip)) {
+  if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) {
     UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
     return 1;
   } else {    
@@ -1008,73 +830,76 @@
 
 /* ================================================== */
 
-static void
+static int
 process_cmd_dfreq(CMD_Request *msg, char *line)
 {
   double dfreq;
+
   msg->command = htons(REQ_DFREQ);
-  if (sscanf(line, "%lf", &dfreq) == 1) {
-    msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq);
-  } else {
-    msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(0.0);
-  }
-}
 
-/* ================================================== */
-
-static void
-cvt_to_sec_usec(double x, long *sec, long *usec) {
-  long s, us;
-  s = (long) x;
-  us = (long)(0.5 + 1.0e6 * (x - (double) s));
-  while (us >= 1000000) {
-    us -= 1000000;
-    s += 1;
+  if (sscanf(line, "%lf", &dfreq) != 1) {
+    LOG(LOGS_ERR, "Invalid value");
+    return 0;
   }
-  while (us < 0) {
-    us += 1000000;
-    s -= 1;
-  }
-  
-  *sec = s;
-  *usec = us;
-}
 
-/* ================================================== */
-
-static void
-process_cmd_doffset(CMD_Request *msg, char *line)
-{
-  double doffset;
-  long sec, usec;
-  msg->command = htons(REQ_DOFFSET);
-  if (sscanf(line, "%lf", &doffset) == 1) {
-    cvt_to_sec_usec(doffset, &sec, &usec);
-    msg->data.doffset.sec = htonl(sec);
-    msg->data.doffset.usec = htonl(usec);
-  } else {
-    msg->data.doffset.sec = htonl(0);
-    msg->data.doffset.usec = htonl(0);
-  }
+  msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq);
+  return 1;
 }
 
 /* ================================================== */
 
 static int
-process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
+process_cmd_doffset(CMD_Request *msg, char *line)
+{
+  double doffset;
+
+  msg->command = htons(REQ_DOFFSET2);
+
+  if (sscanf(line, "%lf", &doffset) != 1) {
+    LOG(LOGS_ERR, "Invalid value");
+    return 0;
+  }
+
+  msg->data.doffset.doffset = UTI_FloatHostToNetwork(doffset);
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_add_source(CMD_Request *msg, char *line)
 {
   CPS_NTP_Source data;
   IPAddr ip_addr;
-  int result = 0, status;
-  const char *opt_name;
+  int result = 0, status, type;
+  const char *opt_name, *word;
   
+  msg->command = htons(REQ_ADD_SOURCE);
+
+  word = line;
+  line = CPS_SplitWord(line);
+
+  if (!strcasecmp(word, "server")) {
+    type = REQ_ADDSRC_SERVER;
+  } else if (!strcasecmp(word, "peer")) {
+    type = REQ_ADDSRC_PEER;
+  } else if (!strcasecmp(word, "pool")) {
+    type = REQ_ADDSRC_POOL;
+  } else {
+    LOG(LOGS_ERR, "Invalid syntax for add command");
+    return 0;
+  }
+
   status = CPS_ParseNTPSourceAdd(line, &data);
   switch (status) {
     case 0:
       LOG(LOGS_ERR, "Invalid syntax for add command");
       break;
     default:
-      if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
+      /* Verify that the address is resolvable (chronyc and chronyd are
+         assumed to be running on the same host) */
+      if (strlen(data.name) >= sizeof (msg->data.ntp_source.name) ||
+          DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
         LOG(LOGS_ERR, "Invalid host/IP address");
         break;
       }
@@ -1085,8 +910,12 @@
         break;
       }
 
-      msg->data.ntp_source.port = htonl((unsigned long) data.port);
-      UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr);
+      msg->data.ntp_source.type = htonl(type);
+      if (strlen(data.name) >= sizeof (msg->data.ntp_source.name))
+        assert(0);
+      strncpy((char *)msg->data.ntp_source.name, data.name,
+              sizeof (msg->data.ntp_source.name));
+      msg->data.ntp_source.port = htonl(data.port);
       msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
       msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll);
       msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll);
@@ -1097,6 +926,7 @@
       msg->data.ntp_source.min_samples = htonl(data.params.min_samples);
       msg->data.ntp_source.max_samples = htonl(data.params.max_samples);
       msg->data.ntp_source.authkey = htonl(data.params.authkey);
+      msg->data.ntp_source.nts_port = htonl(data.params.nts_port);
       msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay);
       msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio);
       msg->data.ntp_source.max_delay_dev_ratio =
@@ -1105,15 +935,20 @@
       msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry);
       msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
       msg->data.ntp_source.flags = htonl(
-          (data.params.online ? REQ_ADDSRC_ONLINE : 0) |
+          (data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) |
           (data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
           (data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
           (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
           (data.params.burst ? REQ_ADDSRC_BURST : 0) |
+          (data.params.nts ? REQ_ADDSRC_NTS : 0) |
+          (data.params.copy ? REQ_ADDSRC_COPY : 0) |
+          (data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) |
           (data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
           (data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
           (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
           (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
+      msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
+      msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
       memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
 
       result = 1;
@@ -1127,24 +962,6 @@
 /* ================================================== */
 
 static int
-process_cmd_add_server(CMD_Request *msg, char *line)
-{
-  msg->command = htons(REQ_ADD_SERVER3);
-  return process_cmd_add_server_or_peer(msg, line);
-}
-
-/* ================================================== */
-
-static int
-process_cmd_add_peer(CMD_Request *msg, char *line)
-{
-  msg->command = htons(REQ_ADD_PEER3);
-  return process_cmd_add_server_or_peer(msg, line);
-}
-
-/* ================================================== */
-
-static int
 process_cmd_delete(CMD_Request *msg, char *line)
 {
   char *hostname;
@@ -1159,7 +976,7 @@
     LOG(LOGS_ERR, "Invalid syntax for address");
     ok = 0;
   } else {
-    if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) {
+    if (!parse_source_address(hostname, &address)) {
       LOG(LOGS_ERR, "Could not get address for hostname");
       ok = 0;
     } else {
@@ -1188,28 +1005,35 @@
                           "Wait until synchronised in specified limits\0"
     "\0\0"
     "Time sources:\0\0"
-    "sources [-v]\0Display information about current sources\0"
-    "sourcestats [-v]\0Display statistics about collected measurements\0"
+    "sources [-a] [-v]\0Display information about current sources\0"
+    "sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
+    "selectdata [-a] [-v]\0Display information about source selection\0"
     "reselect\0Force reselecting synchronisation source\0"
     "reselectdist <dist>\0Modify reselection distance\0"
     "\0\0"
     "NTP sources:\0\0"
     "activity\0Check how many NTP sources are online/offline\0"
+    "authdata [-a] [-v]\0Display information about authentication\0"
     "ntpdata [<address>]\0Display information about last valid measurement\0"
-    "add server <address> [options]\0Add new NTP server\0"
-    "add peer <address> [options]\0Add new NTP peer\0"
+    "add server <name> [options]\0Add new NTP server\0"
+    "add pool <name> [options]\0Add new pool of NTP servers\0"
+    "add peer <name> [options]\0Add new NTP peer\0"
     "delete <address>\0Remove server or peer\0"
-    "burst <n-good>/<n-max> [<mask>/<address>]\0Start rapid set of measurements\0"
+    "burst <n-good>/<n-max> [[<mask>/]<address>]\0Start rapid set of measurements\0"
     "maxdelay <address> <delay>\0Modify maximum valid sample delay\0"
     "maxdelayratio <address> <ratio>\0Modify maximum valid delay/minimum ratio\0"
     "maxdelaydevratio <address> <ratio>\0Modify maximum valid delay/deviation ratio\0"
     "minpoll <address> <poll>\0Modify minimum polling interval\0"
     "maxpoll <address> <poll>\0Modify maximum polling interval\0"
     "minstratum <address> <stratum>\0Modify minimum stratum\0"
-    "offline [<mask>/<address>]\0Set sources in subnet to offline status\0"
-    "online [<mask>/<address>]\0Set sources in subnet to online status\0"
+    "offline [[<mask>/]<address>]\0Set sources in subnet to offline status\0"
+    "online [[<mask>/]<address>]\0Set sources in subnet to online status\0"
+    "onoffline\0Set all sources to online or offline status\0"
+    "\0according to network configuration\0"
     "polltarget <address> <target>\0Modify poll target\0"
     "refresh\0Refresh IP addresses\0"
+    "reload sources\0Re-read *.sources files\0"
+    "sourcename <address>\0Display original name\0"
     "\0\0"
     "Manual time input:\0\0"
     "manual off|on|reset\0Disable/enable/reset settime command\0"
@@ -1219,7 +1043,7 @@
     "\0(e.g. Sep 25, 2015 16:30:05 or 16:30:05)\0"
     "\0\0NTP access:\0\0"
     "accheck <address>\0Check whether address is allowed\0"
-    "clients\0Report on clients that have accessed the server\0"
+    "clients [-p <packets>] [-k] [-r]\0Report on clients that accessed the server\0"
     "serverstats\0Display statistics of the server\0"
     "allow [<subnet>]\0Allow access to subnet as a default\0"
     "allow all [<subnet>]\0Allow access to subnet and all children\0"
@@ -1244,8 +1068,9 @@
     "\0\0"
     "Other daemon commands:\0\0"
     "cyclelogs\0Close and re-open log files\0"
-    "dump\0Dump all measurements to save files\0"
-    "rekey\0Re-read keys from key file\0"
+    "dump\0Dump measurements and NTS keys/cookies\0"
+    "rekey\0Re-read keys\0"
+    "reset sources\0Drop all measurements\0"
     "shutdown\0Stop daemon\0"
     "\0\0"
     "Client commands:\0\0"
@@ -1267,33 +1092,64 @@
 }
 
 /* ================================================== */
-/* Tab-completion when editline/readline is available */
+/* Tab-completion when editline is available */
 
 #ifdef FEAT_READLINE
+
+enum {
+  TAB_COMPLETE_BASE_CMDS,
+  TAB_COMPLETE_ADD_OPTS,
+  TAB_COMPLETE_MANUAL_OPTS,
+  TAB_COMPLETE_RELOAD_OPTS,
+  TAB_COMPLETE_RESET_OPTS,
+  TAB_COMPLETE_SOURCES_OPTS,
+  TAB_COMPLETE_SOURCESTATS_OPTS,
+  TAB_COMPLETE_AUTHDATA_OPTS,
+  TAB_COMPLETE_SELECTDATA_OPTS,
+  TAB_COMPLETE_MAX_INDEX
+};
+
+static int tab_complete_index;
+
 static char *
 command_name_generator(const char *text, int state)
 {
-  const char *name, *names[] = {
-    "accheck", "activity", "add peer", "add server", "allow", "burst",
+  const char *name, **names[TAB_COMPLETE_MAX_INDEX];
+  const char *base_commands[] = {
+    "accheck", "activity", "add", "allow", "authdata", "burst",
     "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
     "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
-    "manual on", "manual off", "manual delete", "manual list", "manual reset",
-    "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
-    "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
-    "polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
-    "retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
-    "smoothtime", "sources", "sources -v", "sourcestats", "sourcestats -v",
+    "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
+    "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
+    "polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
+    "retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
+    "smoothtime", "sourcename", "sources", "sourcestats",
     "timeout", "tracking", "trimrtc", "waitsync", "writertc",
     NULL
   };
+  const char *add_options[] = { "peer", "pool", "server", NULL };
+  const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
+  const char *reset_options[] = { "sources", NULL };
+  const char *reload_options[] = { "sources", NULL };
+  const char *common_source_options[] = { "-a", "-v", NULL };
   static int list_index, len;
 
+  names[TAB_COMPLETE_BASE_CMDS] = base_commands;
+  names[TAB_COMPLETE_ADD_OPTS] = add_options;
+  names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
+  names[TAB_COMPLETE_RELOAD_OPTS] = reload_options;
+  names[TAB_COMPLETE_RESET_OPTS] = reset_options;
+  names[TAB_COMPLETE_AUTHDATA_OPTS] = common_source_options;
+  names[TAB_COMPLETE_SELECTDATA_OPTS] = common_source_options;
+  names[TAB_COMPLETE_SOURCES_OPTS] = common_source_options;
+  names[TAB_COMPLETE_SOURCESTATS_OPTS] = common_source_options;
+
   if (!state) {
     list_index = 0;
     len = strlen(text);
   }
 
-  while ((name = names[list_index++])) {
+  while ((name = names[tab_complete_index][list_index++])) {
     if (strncmp(name, text, len) == 0) {
       return strdup(name);
     }
@@ -1307,7 +1163,33 @@
 static char **
 command_name_completion(const char *text, int start, int end)
 {
+  char first[32];
+
+  snprintf(first, MIN(sizeof (first), start + 1), "%s", rl_line_buffer);
   rl_attempted_completion_over = 1;
+
+  if (!strcmp(first, "add ")) {
+    tab_complete_index = TAB_COMPLETE_ADD_OPTS;
+  } else if (!strcmp(first, "authdata ")) {
+    tab_complete_index = TAB_COMPLETE_AUTHDATA_OPTS;
+  } else if (!strcmp(first, "manual ")) {
+    tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
+  } else if (!strcmp(first, "reload ")) {
+    tab_complete_index = TAB_COMPLETE_RELOAD_OPTS;
+  } else if (!strcmp(first, "reset ")) {
+    tab_complete_index = TAB_COMPLETE_RESET_OPTS;
+  } else if (!strcmp(first, "selectdata ")) {
+    tab_complete_index = TAB_COMPLETE_SELECTDATA_OPTS;
+  } else if (!strcmp(first, "sources ")) {
+    tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
+  } else if (!strcmp(first, "sourcestats ")) {
+    tab_complete_index = TAB_COMPLETE_SOURCESTATS_OPTS;
+  } else if (first[0] == '\0') {
+    tab_complete_index = TAB_COMPLETE_BASE_CMDS;
+  } else {
+    return NULL;
+  }
+
   return rl_completion_matches(text, command_name_generator);
 }
 #endif
@@ -1375,12 +1257,8 @@
         return 0;
       }
 
-      if (send(sock_fd, (void *)request, command_length, 0) < 0) {
-        DEBUG_LOG("Could not send %d bytes : %s", command_length, strerror(errno));
+      if (SCK_Send(sock_fd, (void *)request, command_length, 0) < 0)
         return 0;
-      }
-
-      DEBUG_LOG("Sent %d bytes", command_length);
     }
 
     UTI_TimevalToTimespec(&tv, &ts_now);
@@ -1416,16 +1294,11 @@
       /* Timeout must have elapsed, try a resend? */
       new_attempt = 1;
     } else {
-      recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_Reply), 0);
+      recv_status = SCK_Receive(sock_fd, reply, sizeof (*reply), 0);
       
       if (recv_status < 0) {
-        /* If we get connrefused here, it suggests the sendto is
-           going to a dead port */
-        DEBUG_LOG("Could not receive : %s", strerror(errno));
         new_attempt = 1;
       } else {
-        DEBUG_LOG("Received %d bytes", recv_status);
-        
         read_length = recv_status;
         
         /* Check if the header is valid */
@@ -1556,6 +1429,9 @@
       case STT_INACTIVE:
         printf("519 Client logging is not active in the daemon");
         break;
+      case STT_INVALIDNAME:
+        printf("521 Invalid name");
+        break;
       default:
         printf("520 Got unexpected error from daemon");
     }
@@ -1588,17 +1464,17 @@
   if (s == (uint32_t)-1) {
     printf("   -");
   } else if (s < 1200) {
-    printf("%4ld", s);
+    printf("%4lu", s);
   } else if (s < 36000) {
-    printf("%3ldm", s / 60);
+    printf("%3lum", s / 60);
   } else if (s < 345600) {
-    printf("%3ldh", s / 3600);
+    printf("%3luh", s / 3600);
   } else {
     d = s / 86400;
     if (d > 999) {
-      printf("%3ldy", d / 365);
+      printf("%3luy", d / 365);
     } else {
-      printf("%3ldd", d);
+      printf("%3lud", d);
     }
   }
 }
@@ -1836,19 +1712,19 @@
         integer = va_arg(ap, int);
         switch (integer) {
           case LEAP_Normal:
-            string = "Normal";
+            string = width != 1 ? "Normal" : "N";
             break;
           case LEAP_InsertSecond:
-            string = "Insert second";
+            string = width != 1 ? "Insert second" : "+";
             break;
           case LEAP_DeleteSecond:
-            string = "Delete second";
+            string = width != 1 ? "Delete second" : "-";
             break;
           case LEAP_Unsynchronised:
-            string = "Not synchronised";
+            string = width != 1 ? "Not synchronised" : "?";
             break;
           default:
-            string = "Invalid";
+            string = width != 1 ? "Invalid" : "?";
             break;
         }
         printf("%s", string);
@@ -1987,12 +1863,40 @@
 
 /* ================================================== */
 
+static int
+get_source_name(IPAddr *ip_addr, char *buf, int size)
+{
+  CMD_Request request;
+  CMD_Reply reply;
+  int i;
+
+  request.command = htons(REQ_NTP_SOURCE_NAME);
+  UTI_IPHostToNetwork(ip_addr, &request.data.ntp_source_name.ip_addr);
+  if (!request_reply(&request, &reply, RPY_NTP_SOURCE_NAME, 0) ||
+      reply.data.ntp_source_name.name[sizeof (reply.data.ntp_source_name.name) - 1] != '\0' ||
+      snprintf(buf, size, "%s", (char *)reply.data.ntp_source_name.name) >= size)
+    return 0;
+
+  /* Make sure the name is printable */
+  for (i = 0; i < size && buf[i] != '\0'; i++) {
+    if (!isgraph((unsigned char)buf[i]))
+      return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
 static void
 format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id,
-            IPAddr *ip_addr)
+            int source, IPAddr *ip_addr)
 {
   if (ref) {
     snprintf(buf, size, "%s", UTI_RefidToString(ref_id));
+  } else if (source && source_names) {
+    if (!get_source_name(ip_addr, buf, size))
+      snprintf(buf, size, "?");
   } else if (no_dns || csv_mode) {
     snprintf(buf, size, "%s", UTI_IPToString(ip_addr));
   } else {
@@ -2006,12 +1910,42 @@
 
 /* ================================================== */
 
-static int
-check_for_verbose_flag(char *line)
+static void
+parse_sources_options(char *line, int *all, int *verbose)
 {
-  if (!csv_mode && !strcmp(line, "-v"))
-    return 1;
-  return 0;
+  char *opt;
+
+  *all = *verbose = 0;
+
+  while (*line) {
+    opt = line;
+    line = CPS_SplitWord(line);
+    if (!strcmp(opt, "-a"))
+      *all = 1;
+    else if (!strcmp(opt, "-v"))
+      *verbose = !csv_mode;
+  }
+}
+
+/* ================================================== */
+
+static int
+process_cmd_sourcename(char *line)
+{
+  IPAddr ip_addr;
+  char name[256];
+
+  if (!parse_source_address(line, &ip_addr)) {
+    LOG(LOGS_ERR, "Could not read address");
+    return 0;
+  }
+
+  if (!get_source_name(&ip_addr, name, sizeof (name)))
+    return 0;
+
+  print_report("%s\n", name, REPORT_END);
+
+  return 1;
 }
 
 /* ================================================== */
@@ -2023,24 +1957,22 @@
   CMD_Reply reply;
   IPAddr ip_addr;
   uint32_t i, mode, n_sources;
-  char name[50], mode_ch, state_ch;
-  int verbose;
+  char name[256], mode_ch, state_ch;
+  int all, verbose;
 
-  /* Check whether to output verbose headers */
-  verbose = check_for_verbose_flag(line);
+  parse_sources_options(line, &all, &verbose);
   
   request.command = htons(REQ_N_SOURCES);
   if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
     return 0;
 
   n_sources = ntohl(reply.data.n_sources.n_sources);
-  print_info_field("210 Number of sources = %lu\n", (unsigned long)n_sources);
 
   if (verbose) {
     printf("\n");
     printf("  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.\n");
-    printf(" / .- Source state '*' = current synced, '+' = combined , '-' = not combined,\n");
-    printf("| /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.\n");
+    printf(" / .- Source state '*' = current best, '+' = combined, '-' = not combined,\n");
+    printf("| /             'x' = may be in error, '~' = too variable, '?' = unusable.\n");
     printf("||                                                 .- xxxx [ yyyy ] +/- zzzz\n");
     printf("||      Reachability register (octal) -.           |  xxxx = adjusted offset,\n");
     printf("||      Log2(Polling interval) --.      |          |  yyyy = measured offset,\n");
@@ -2060,9 +1992,12 @@
 
     mode = ntohs(reply.data.source_data.mode);
     UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
+    if (!all && ip_addr.family == IPADDR_ID)
+      continue;
+
     format_name(name, sizeof (name), 25,
                 mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
-                ip_addr.addr.in4, &ip_addr);
+                ip_addr.addr.in4, 1, &ip_addr);
 
     switch (mode) {
       case RPY_SD_MD_CLIENT:
@@ -2079,10 +2014,10 @@
     }
 
     switch (ntohs(reply.data.source_data.state)) {
-      case RPY_SD_ST_SYNC:
+      case RPY_SD_ST_SELECTED:
         state_ch = '*';
         break;
-      case RPY_SD_ST_UNREACH:
+      case RPY_SD_ST_NONSELECTABLE:
         state_ch = '?';
         break;
       case RPY_SD_ST_FALSETICKER:
@@ -2091,10 +2026,10 @@
       case RPY_SD_ST_JITTERY:
         state_ch = '~';
         break;
-      case RPY_SD_ST_CANDIDATE:
+      case RPY_SD_ST_UNSELECTED:
         state_ch = '+';
         break;
-      case RPY_SD_ST_OUTLIER:
+      case RPY_SD_ST_SELECTABLE:
         state_ch = '-';
         break;
       default:
@@ -2129,18 +2064,17 @@
   CMD_Request request;
   CMD_Reply reply;
   uint32_t i, n_sources;
-  int verbose = 0;
-  char name[50];
+  int all, verbose;
+  char name[256];
   IPAddr ip_addr;
 
-  verbose = check_for_verbose_flag(line);
+  parse_sources_options(line, &all, &verbose);
 
   request.command = htons(REQ_N_SOURCES);
   if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
     return 0;
 
   n_sources = ntohl(reply.data.n_sources.n_sources);
-  print_info_field("210 Number of sources = %lu\n", (unsigned long)n_sources);
 
   if (verbose) {
     printf("                             .- Number of sample points in measurement set.\n");
@@ -2165,8 +2099,11 @@
       return 0;
 
     UTI_IPNetworkToHost(&reply.data.sourcestats.ip_addr, &ip_addr);
+    if (!all && ip_addr.family == IPADDR_ID)
+      continue;
+
     format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
-                ntohl(reply.data.sourcestats.ref_id), &ip_addr);
+                ntohl(reply.data.sourcestats.ref_id), 1, &ip_addr);
 
     print_report("%-25s %3U %3U  %I %+P %P  %+S  %S\n",
                  name,
@@ -2192,7 +2129,7 @@
   CMD_Reply reply;
   IPAddr ip_addr;
   uint32_t ref_id;
-  char name[50];
+  char name[256];
   struct timespec ref_time;
   
   request.command = htons(REQ_TRACKING);
@@ -2203,7 +2140,7 @@
 
   UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
   format_name(name, sizeof (name), sizeof (name),
-              ip_addr.family == IPADDR_UNSPEC, ref_id, &ip_addr);
+              ip_addr.family == IPADDR_UNSPEC, ref_id, 1, &ip_addr);
 
   UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
 
@@ -2240,6 +2177,91 @@
 /* ================================================== */
 
 static int
+process_cmd_authdata(char *line)
+{
+  CMD_Request request;
+  CMD_Reply reply;
+  IPAddr ip_addr;
+  uint32_t i, source_mode, n_sources;
+  int all, verbose;
+  const char *mode_str;
+  char name[256];
+
+  parse_sources_options(line, &all, &verbose);
+
+  request.command = htons(REQ_N_SOURCES);
+  if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
+    return 0;
+
+  n_sources = ntohl(reply.data.n_sources.n_sources);
+
+  if (verbose) {
+    printf(    "                             .- Auth. mechanism (NTS, SK - symmetric key)\n");
+    printf(    "                            |   Key length -.  Cookie length (bytes) -.\n");
+    printf(    "                            |       (bits)  |  Num. of cookies --.    |\n");
+    printf(    "                            |               |  Key est. attempts  |   |\n");
+    printf(    "                            |               |           |         |   |\n");
+  }
+
+  print_header("Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen");
+
+  /*           "NNNNNNNNNNNNNNNNNNNNNNNNNNN MMMM KKKKK AAAA LLLL LLLL AAAA NNNN CCCC LLLL" */
+
+  for (i = 0; i < n_sources; i++) {
+    request.command = htons(REQ_SOURCE_DATA);
+    request.data.source_data.index = htonl(i);
+    if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
+      return 0;
+
+    source_mode = ntohs(reply.data.source_data.mode);
+    if (source_mode != RPY_SD_MD_CLIENT && source_mode != RPY_SD_MD_PEER)
+      continue;
+
+    UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
+    if (!all && ip_addr.family == IPADDR_ID)
+      continue;
+
+    request.command = htons(REQ_AUTH_DATA);
+    request.data.auth_data.ip_addr = reply.data.source_data.ip_addr;
+    if (!request_reply(&request, &reply, RPY_AUTH_DATA, 0))
+      return 0;
+
+    format_name(name, sizeof (name), 25, 0, 0, 1, &ip_addr);
+
+    switch (ntohs(reply.data.auth_data.mode)) {
+      case RPY_AD_MD_NONE:
+        mode_str = "-";
+        break;
+      case RPY_AD_MD_SYMMETRIC:
+        mode_str = "SK";
+        break;
+      case RPY_AD_MD_NTS:
+        mode_str = "NTS";
+        break;
+      default:
+        mode_str = "?";
+        break;
+    }
+
+    print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
+                 name, mode_str,
+                 (unsigned long)ntohl(reply.data.auth_data.key_id),
+                 ntohs(reply.data.auth_data.key_type),
+                 ntohs(reply.data.auth_data.key_length),
+                 (unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
+                 ntohs(reply.data.auth_data.ke_attempts),
+                 ntohs(reply.data.auth_data.nak),
+                 ntohs(reply.data.auth_data.cookies),
+                 ntohs(reply.data.auth_data.cookie_length),
+                 REPORT_END);
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
 process_cmd_ntpdata(char *line)
 {
   CMD_Request request;
@@ -2263,7 +2285,7 @@
 
   for (i = 0; i < n_sources; i++) {
     if (specified_addr) {
-      if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
+      if (!parse_source_address(line, &remote_addr)) {
         LOG(LOGS_ERR, "Could not get address for hostname");
         return 0;
       }
@@ -2278,6 +2300,8 @@
         continue;
 
       UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr);
+      if (!UTI_IsIPReal(&remote_addr))
+        continue;
     }
 
     request.command = htons(REQ_NTP_DATA);
@@ -2354,25 +2378,113 @@
 /* ================================================== */
 
 static int
+process_cmd_selectdata(char *line)
+{
+  CMD_Request request;
+  CMD_Reply reply;
+  uint32_t i, n_sources;
+  int all, verbose, conf_options, eff_options;
+  char name[256];
+  IPAddr ip_addr;
+
+  parse_sources_options(line, &all, &verbose);
+
+  request.command = htons(REQ_N_SOURCES);
+  if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
+    return 0;
+
+  n_sources = ntohl(reply.data.n_sources.n_sources);
+
+  if (verbose) {
+    printf(    "  .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
+    printf(    " /           ~ - jittery, w/W - waits for others, T - not trusted,\n");
+    printf(    "|            x - falseticker, P - not preferred, U - waits for update,\n");
+    printf(    "|            S - stale, O - orphan, + - combined, * - best.\n");
+    printf(    "|        Effective options ------.  (N - noselect, P - prefer\n");
+    printf(    "|       Configured options -.     \\  T - trust, R - require)\n");
+    printf(    "|   Auth. enabled (Y/N) -.   \\     \\     Offset interval --.\n");
+    printf(    "|                        |    |     |                       |\n");
+  }
+
+  print_header("S Name/IP Address        Auth COpts EOpts Last Score     Interval  Leap");
+
+  /*           "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS IIIIIII IIIIIII  L" */
+
+  for (i = 0; i < n_sources; i++) {
+    request.command = htons(REQ_SELECT_DATA);
+    request.data.source_data.index = htonl(i);
+    if (!request_reply(&request, &reply, RPY_SELECT_DATA, 0))
+      return 0;
+
+    UTI_IPNetworkToHost(&reply.data.select_data.ip_addr, &ip_addr);
+    if (!all && ip_addr.family == IPADDR_ID)
+      continue;
+
+    format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
+                ntohl(reply.data.select_data.ref_id), 1, &ip_addr);
+
+    conf_options = ntohs(reply.data.select_data.conf_options);
+    eff_options = ntohs(reply.data.select_data.eff_options);
+
+    print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S  %1L\n",
+                 reply.data.select_data.state_char,
+                 name,
+                 reply.data.select_data.authentication ? 'Y' : 'N',
+                 conf_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
+                 conf_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
+                 conf_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
+                 conf_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
+                 '-',
+                 eff_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
+                 eff_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
+                 eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
+                 eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
+                 '-',
+                 (unsigned long)ntohl(reply.data.select_data.last_sample_ago),
+                 UTI_FloatNetworkToHost(reply.data.select_data.score),
+                 UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
+                 UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
+                 reply.data.select_data.leap,
+                 REPORT_END);
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
 process_cmd_serverstats(char *line)
 {
   CMD_Request request;
   CMD_Reply reply;
 
   request.command = htons(REQ_SERVER_STATS);
-  if (!request_reply(&request, &reply, RPY_SERVER_STATS, 0))
+  if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0))
     return 0;
 
   print_report("NTP packets received       : %U\n"
                "NTP packets dropped        : %U\n"
                "Command packets received   : %U\n"
                "Command packets dropped    : %U\n"
-               "Client log records dropped : %U\n",
+               "Client log records dropped : %U\n"
+               "NTS-KE connections accepted: %U\n"
+               "NTS-KE connections dropped : %U\n"
+               "Authenticated NTP packets  : %U\n"
+               "Interleaved NTP packets    : %U\n"
+               "NTP timestamps held        : %U\n"
+               "NTP timestamp span         : %U\n",
                (unsigned long)ntohl(reply.data.server_stats.ntp_hits),
                (unsigned long)ntohl(reply.data.server_stats.ntp_drops),
                (unsigned long)ntohl(reply.data.server_stats.cmd_hits),
                (unsigned long)ntohl(reply.data.server_stats.cmd_drops),
                (unsigned long)ntohl(reply.data.server_stats.log_drops),
+               (unsigned long)ntohl(reply.data.server_stats.nke_hits),
+               (unsigned long)ntohl(reply.data.server_stats.nke_drops),
+               (unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
+               (unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
+               (unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
+               (unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
                REPORT_END);
 
   return 1;
@@ -2470,20 +2582,46 @@
   CMD_Request request;
   CMD_Reply reply;
   IPAddr ip;
-  uint32_t i, n_clients, next_index, n_indices;
+  uint32_t i, n_clients, next_index, n_indices, min_hits, reset;
   RPY_ClientAccesses_Client *client;
-  char name[50];
+  char header[80], name[50], *opt, *arg;
+  int nke;
 
   next_index = 0;
+  min_hits = 0;
+  reset = 0;
+  nke = 0;
 
-  print_header("Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last");
+  while (*line) {
+    opt = line;
+    line = CPS_SplitWord(line);
+    if (strcmp(opt, "-k") == 0) {
+      nke = 1;
+    } else if (strcmp(opt, "-p") == 0) {
+      arg = line;
+      line = CPS_SplitWord(line);
+      if (sscanf(arg, "%"SCNu32, &min_hits) != 1) {
+        LOG(LOGS_ERR, "Invalid syntax for clients command");
+        return 0;
+      }
+    } else if (strcmp(opt, "-r") == 0) {
+      reset = 1;
+    }
+  }
+
+  snprintf(header, sizeof (header),
+           "Hostname                      NTP   Drop Int IntL Last  %6s   Drop Int  Last",
+           nke ? "NTS-KE" : "Cmd");
+  print_header(header);
 
   while (1) {
-    request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX2);
+    request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX3);
     request.data.client_accesses_by_index.first_index = htonl(next_index);
     request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES);
+    request.data.client_accesses_by_index.min_hits = htonl(min_hits);
+    request.data.client_accesses_by_index.reset = htonl(reset);
 
-    if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX2, 0))
+    if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX3, 0))
       return 0;
 
     n_clients = ntohl(reply.data.client_accesses_by_index.n_clients);
@@ -2499,7 +2637,7 @@
       if (ip.family == IPADDR_UNSPEC)
         continue;
 
-      format_name(name, sizeof (name), 25, 0, 0, &ip);
+      format_name(name, sizeof (name), 25, 0, 0, 0, &ip);
 
       print_report("%-25s  %6U  %5U  %C  %C  %I  %6U  %5U  %C  %I\n",
                    name,
@@ -2508,10 +2646,11 @@
                    client->ntp_interval,
                    client->ntp_timeout_interval,
                    (unsigned long)ntohl(client->last_ntp_hit_ago),
-                   (unsigned long)ntohl(client->cmd_hits),
-                   (unsigned long)ntohl(client->cmd_drops),
-                   client->cmd_interval,
-                   (unsigned long)ntohl(client->last_cmd_hit_ago),
+                   (unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits),
+                   (unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops),
+                   nke ? client->nke_interval : client->cmd_interval,
+                   (unsigned long)ntohl(nke ? client->last_nke_hit_ago :
+                                              client->last_cmd_hit_ago),
                    REPORT_END);
     }
 
@@ -2716,6 +2855,36 @@
 /* ================================================== */
 
 static int
+process_cmd_reload(CMD_Request *msg, char *line)
+{
+  if (!strcmp(line, "sources")) {
+    msg->command = htons(REQ_RELOAD_SOURCES);
+  } else {
+    LOG(LOGS_ERR, "Invalid syntax for reload command");
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_reset(CMD_Request *msg, char *line)
+{
+  if (!strcmp(line, "sources")) {
+    msg->command = htons(REQ_RESET_SOURCES);
+  } else {
+    LOG(LOGS_ERR, "Invalid syntax for reset command");
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
 process_cmd_waitsync(char *line)
 {
   CMD_Request request;
@@ -2830,28 +2999,48 @@
 static int
 process_cmd_keygen(char *line)
 {
-  char hash_name[17];
+  unsigned int i, args, cmac_length, length, id = 1, bits = 160;
   unsigned char key[512];
-  unsigned int i, length, id = 1, bits = 160;
+  const char *type;
+  char *words[3];
 
 #ifdef FEAT_SECHASH
-  snprintf(hash_name, sizeof (hash_name), "SHA1");
+  type = "SHA1";
 #else
-  snprintf(hash_name, sizeof (hash_name), "MD5");
+  type = "MD5";
 #endif
 
-  if (sscanf(line, "%u %16s %d", &id, hash_name, &bits))
-    ;
+  args = UTI_SplitString(line, words, 3);
+  if (args >= 2)
+    type = words[1];
 
-  length = CLAMP(10, (bits + 7) / 8, sizeof (key));
-  if (HSH_GetHashId(hash_name) < 0) {
-    LOG(LOGS_ERR, "Unknown hash function %s", hash_name);
+  if (args > 3 ||
+      (args >= 1 && sscanf(words[0], "%u", &id) != 1) ||
+      (args >= 3 && sscanf(words[2], "%u", &bits) != 1)) {
+    LOG(LOGS_ERR, "Invalid syntax for keygen command");
     return 0;
   }
 
+#ifdef HAVE_CMAC
+  cmac_length = CMC_GetKeyLength(UTI_CmacNameToAlgorithm(type));
+#else
+  cmac_length = 0;
+#endif
+
+  if (HSH_GetHashId(UTI_HashNameToAlgorithm(type)) >= 0) {
+    length = (bits + 7) / 8;
+  } else if (cmac_length > 0) {
+    length = cmac_length;
+  } else {
+    LOG(LOGS_ERR, "Unknown hash function or cipher %s", type);
+    return 0;
+  }
+
+  length = CLAMP(10, length, sizeof (key));
+
   UTI_GetRandomBytesUrandom(key, length);
 
-  printf("%u %s HEX:", id, hash_name);
+  printf("%u %s HEX:", id, type);
   for (i = 0; i < length; i++)
     printf("%02hhX", key[i]);
   printf("\n");
@@ -2890,16 +3079,13 @@
   } else if (!strcmp(command, "activity")) {
     do_normal_submit = 0;
     ret = process_cmd_activity(line);
-  } else if (!strcmp(command, "add") && !strncmp(line, "peer", 4)) {
-    do_normal_submit = process_cmd_add_peer(&tx_message, CPS_SplitWord(line));
-  } else if (!strcmp(command, "add") && !strncmp(line, "server", 6)) {
-    do_normal_submit = process_cmd_add_server(&tx_message, CPS_SplitWord(line));
+  } else if (!strcmp(command, "add")) {
+    do_normal_submit = process_cmd_add_source(&tx_message, line);
   } else if (!strcmp(command, "allow")) {
-    if (!strncmp(line, "all", 3)) {
-      do_normal_submit = process_cmd_allowall(&tx_message, CPS_SplitWord(line));
-    } else {
-      do_normal_submit = process_cmd_allow(&tx_message, line);
-    }
+    do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_ALLOW, REQ_ALLOWALL);
+  } else if (!strcmp(command, "authdata")) {
+    do_normal_submit = 0;
+    ret = process_cmd_authdata(line);
   } else if (!strcmp(command, "burst")) {
     do_normal_submit = process_cmd_burst(&tx_message, line);
   } else if (!strcmp(command, "clients")) {
@@ -2908,35 +3094,22 @@
   } else if (!strcmp(command, "cmdaccheck")) {
     do_normal_submit = process_cmd_cmdaccheck(&tx_message, line);
   } else if (!strcmp(command, "cmdallow")) {
-    if (!strncmp(line, "all", 3)) {
-      do_normal_submit = process_cmd_cmdallowall(&tx_message, CPS_SplitWord(line));
-    } else {
-      do_normal_submit = process_cmd_cmdallow(&tx_message, line);
-    }
+    do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDALLOW, REQ_CMDALLOWALL);
   } else if (!strcmp(command, "cmddeny")) {
-    if (!strncmp(line, "all", 3)) {
-      line = CPS_SplitWord(line);
-      do_normal_submit = process_cmd_cmddenyall(&tx_message, line);
-    } else {
-      do_normal_submit = process_cmd_cmddeny(&tx_message, line);
-    }
+    do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDDENY, REQ_CMDDENYALL);
   } else if (!strcmp(command, "cyclelogs")) {
     process_cmd_cyclelogs(&tx_message, line);
   } else if (!strcmp(command, "delete")) {
     do_normal_submit = process_cmd_delete(&tx_message, line);
   } else if (!strcmp(command, "deny")) {
-    if (!strncmp(line, "all", 3)) {
-      do_normal_submit = process_cmd_denyall(&tx_message, CPS_SplitWord(line));
-    } else {
-      do_normal_submit = process_cmd_deny(&tx_message, line);
-    }
+    do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_DENY, REQ_DENYALL);
   } else if (!strcmp(command, "dfreq")) {
-    process_cmd_dfreq(&tx_message, line);
+    do_normal_submit = process_cmd_dfreq(&tx_message, line);
   } else if (!strcmp(command, "dns")) {
     ret = process_cmd_dns(line);
     do_normal_submit = 0;
   } else if (!strcmp(command, "doffset")) {
-    process_cmd_doffset(&tx_message, line);
+    do_normal_submit = process_cmd_doffset(&tx_message, line);
   } else if (!strcmp(command, "dump")) {
     process_cmd_dump(&tx_message, line);
   } else if (!strcmp(command, "exit")) {
@@ -2984,6 +3157,8 @@
     do_normal_submit = process_cmd_offline(&tx_message, line);
   } else if (!strcmp(command, "online")) {
     do_normal_submit = process_cmd_online(&tx_message, line);
+  } else if (!strcmp(command, "onoffline")) {
+    process_cmd_onoffline(&tx_message, line);
   } else if (!strcmp(command, "polltarget")) {
     do_normal_submit = process_cmd_polltarget(&tx_message, line);
   } else if (!strcmp(command, "quit")) {
@@ -2994,16 +3169,23 @@
     process_cmd_refresh(&tx_message, line);
   } else if (!strcmp(command, "rekey")) {
     process_cmd_rekey(&tx_message, line);
+  } else if (!strcmp(command, "reload")) {
+    do_normal_submit = process_cmd_reload(&tx_message, line);
   } else if (!strcmp(command, "reselect")) {
     process_cmd_reselect(&tx_message, line);
   } else if (!strcmp(command, "reselectdist")) {
     do_normal_submit = process_cmd_reselectdist(&tx_message, line);
+  } else if (!strcmp(command, "reset")) {
+    do_normal_submit = process_cmd_reset(&tx_message, line);
   } else if (!strcmp(command, "retries")) {
     ret = process_cmd_retries(line);
     do_normal_submit = 0;
   } else if (!strcmp(command, "rtcdata")) {
     do_normal_submit = 0;
     ret = process_cmd_rtcreport(line);
+  } else if (!strcmp(command, "selectdata")) {
+    do_normal_submit = 0;
+    ret = process_cmd_selectdata(line);
   } else if (!strcmp(command, "serverstats")) {
     do_normal_submit = 0;
     ret = process_cmd_serverstats(line);
@@ -3017,6 +3199,9 @@
     ret = process_cmd_smoothing(line);
   } else if (!strcmp(command, "smoothtime")) {
     do_normal_submit = process_cmd_smoothtime(&tx_message, line);
+  } else if (!strcmp(command, "sourcename")) {
+    do_normal_submit = 0;
+    ret = process_cmd_sourcename(line);
   } else if (!strcmp(command, "sources")) {
     do_normal_submit = 0;
     ret = process_cmd_sources(line);
@@ -3106,7 +3291,7 @@
 display_gpl(void)
 {
     printf("chrony version %s\n"
-           "Copyright (C) 1997-2003, 2007, 2009-2018 Richard P. Curnow and others\n"
+           "Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n"
            "chrony comes with ABSOLUTELY NO WARRANTY.  This is free software, and\n"
            "you are welcome to redistribute it under certain conditions.  See the\n"
            "GNU General Public License version 2 for details.\n\n",
@@ -3118,8 +3303,22 @@
 static void
 print_help(const char *progname)
 {
-      printf("Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]\n",
-             progname);
+      printf("Usage: %s [OPTION]... [COMMAND]...\n\n"
+             "Options:\n"
+             "  -4\t\tUse IPv4 addresses only\n"
+             "  -6\t\tUse IPv6 addresses only\n"
+             "  -n\t\tDon't resolve hostnames\n"
+             "  -N\t\tPrint original source names\n"
+             "  -c\t\tEnable CSV format\n"
+#if DEBUG > 0
+             "  -d\t\tEnable debug messages\n"
+#endif
+             "  -m\t\tAccept multiple commands\n"
+             "  -h HOST\tSpecify server (%s)\n"
+             "  -p PORT\tSpecify UDP port (%d)\n"
+             "  -v, --version\tPrint version and exit\n"
+             "      --help\tPrint usage and exit\n",
+             progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1", DEFAULT_CANDM_PORT);
 }
 
 /* ================================================== */
@@ -3141,7 +3340,7 @@
   int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
   int port = DEFAULT_CANDM_PORT;
 
-  /* Parse (undocumented) long command-line options */
+  /* Parse long command-line options */
   for (optind = 1; optind < argc; optind++) {
     if (!strcmp("--help", argv[optind])) {
       print_help(progname);
@@ -3155,7 +3354,7 @@
   optind = 1;
 
   /* Parse short command-line options */
-  while ((opt = getopt(argc, argv, "+46acdf:h:mnp:v")) != -1) {
+  while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) {
     switch (opt) {
       case '4':
       case '6':
@@ -3169,7 +3368,9 @@
         csv_mode = 1;
         break;
       case 'd':
-        log_debug_enabled = 1;
+#if DEBUG > 0
+        log_min_severity = LOGS_DEBUG;
+#endif
         break;
       case 'h':
         hostnames = optarg;
@@ -3180,6 +3381,9 @@
       case 'n':
         no_dns = 1;
         break;
+      case 'N':
+        source_names = 1;
+        break;
       case 'p':
         port = atoi(optarg);
         break;
@@ -3206,9 +3410,10 @@
     hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1";
   }
 
-  UTI_SetQuitSignalsHandler(signal_handler);
+  UTI_SetQuitSignalsHandler(signal_handler, 0);
 
-  sockaddrs = get_sockaddrs(hostnames, port);
+  SCK_Initialise(IPADDR_UNSPEC);
+  server_addresses = get_addresses(hostnames, port);
 
   if (!open_io())
     LOG_FATAL("Could not open connection to daemon");
@@ -3228,8 +3433,8 @@
   }
 
   close_io();
-
-  ARR_DestroyInstance(sockaddrs);
+  free_addresses(server_addresses);
+  SCK_Finalise();
 
   return !ret;
 }
diff --git a/chrony/clientlog.c b/chrony/clientlog.c
new file mode 100644
index 0000000..adf0c59
--- /dev/null
+++ b/chrony/clientlog.c
@@ -0,0 +1,1088 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * Copyright (C) Miroslav Lichvar  2009, 2015-2017, 2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  This module keeps a count of the number of successful accesses by
+  clients, and the times of the last accesses.
+
+  This can be used for status reporting, and (in the case of a
+  server), if it needs to know which clients have made use of its data
+  recently.
+
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "array.h"
+#include "clientlog.h"
+#include "conf.h"
+#include "local.h"
+#include "memory.h"
+#include "ntp.h"
+#include "reports.h"
+#include "util.h"
+#include "logging.h"
+
+#define MAX_SERVICES 3
+
+typedef struct {
+  IPAddr ip_addr;
+  uint32_t last_hit[MAX_SERVICES];
+  uint32_t hits[MAX_SERVICES];
+  uint16_t drops[MAX_SERVICES];
+  uint16_t tokens[MAX_SERVICES];
+  int8_t rate[MAX_SERVICES];
+  int8_t ntp_timeout_rate;
+  uint8_t drop_flags;
+} Record;
+
+/* Hash table of records, there is a fixed number of records per slot */
+static ARR_Instance records;
+
+#define SLOT_BITS 4
+
+/* Number of records in one slot of the hash table */
+#define SLOT_SIZE (1U << SLOT_BITS)
+
+/* Minimum number of slots */
+#define MIN_SLOTS 1
+
+/* Maximum number of slots, this is a hard limit */
+#define MAX_SLOTS (1U << (24 - SLOT_BITS))
+
+/* Number of slots in the hash table */
+static unsigned int slots;
+
+/* Maximum number of slots given memory allocation limit */
+static unsigned int max_slots;
+
+/* Times of last hits are saved as 32-bit fixed point values */
+#define TS_FRAC 4
+#define INVALID_TS 0
+
+/* Static offset included in conversion to the fixed-point timestamps to
+   randomise their alignment */
+static uint32_t ts_offset;
+
+/* Request rates are saved in the record as 8-bit scaled log2 values */
+#define RATE_SCALE 4
+#define MIN_RATE (-14 * RATE_SCALE)
+#define INVALID_RATE -128
+
+/* Response rates are controlled by token buckets.  The capacity and
+   number of tokens spent on response are determined from configured
+   minimum inverval between responses (in log2) and burst length. */
+
+#define MIN_LIMIT_INTERVAL (-15 - TS_FRAC)
+#define MAX_LIMIT_INTERVAL 12
+#define MIN_LIMIT_BURST 1
+#define MAX_LIMIT_BURST 255
+
+static uint16_t max_tokens[MAX_SERVICES];
+static uint16_t tokens_per_hit[MAX_SERVICES];
+
+/* Reduction of token rates to avoid overflow of 16-bit counters.  Negative
+   shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
+static int token_shift[MAX_SERVICES];
+
+/* Rates at which responses are randomly allowed (in log2) when the
+   buckets don't have enough tokens.  This is necessary in order to
+   prevent an attacker sending requests with spoofed source address
+   from blocking responses to the address completely. */
+
+#define MIN_LEAK_RATE 1
+#define MAX_LEAK_RATE 4
+
+static int leak_rate[MAX_SERVICES];
+
+/* Limit intervals in log2 */
+static int limit_interval[MAX_SERVICES];
+
+/* Flag indicating whether facility is turned on or not */
+static int active;
+
+/* RX and TX timestamp saved for clients using interleaved mode */
+typedef struct {
+  uint64_t rx_ts;
+  uint16_t flags;
+  uint16_t slew_epoch;
+  int32_t tx_ts_offset;
+} NtpTimestamps;
+
+/* Flags for NTP timestamps */
+#define NTPTS_DISABLED 1
+#define NTPTS_VALID_TX 2
+
+/* RX->TX map using a circular buffer with ordered timestamps */
+typedef struct {
+  ARR_Instance timestamps;
+  uint32_t first;
+  uint32_t size;
+  uint32_t max_size;
+  uint32_t cached_index;
+  uint64_t cached_rx_ts;
+  uint16_t slew_epoch;
+  double slew_offset;
+} NtpTimestampMap;
+
+static NtpTimestampMap ntp_ts_map;
+
+/* Maximum interval of NTP timestamps in future after a backward step */
+#define NTPTS_FUTURE_LIMIT (1LL << 32) /* 1 second */
+
+/* Maximum number of timestamps moved in the array to insert a new timestamp */
+#define NTPTS_INSERT_LIMIT 64
+
+/* Global statistics */
+static uint32_t total_hits[MAX_SERVICES];
+static uint32_t total_drops[MAX_SERVICES];
+static uint32_t total_ntp_auth_hits;
+static uint32_t total_ntp_interleaved_hits;
+static uint32_t total_record_drops;
+
+#define NSEC_PER_SEC 1000000000U
+
+/* ================================================== */
+
+static int expand_hashtable(void);
+static void handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
+                        double doffset, LCL_ChangeType change_type, void *anything);
+
+/* ================================================== */
+
+static int
+compare_ts(uint32_t x, uint32_t y)
+{
+  if (x == y)
+    return 0;
+  if (y == INVALID_TS)
+    return 1;
+  return (int32_t)(x - y) > 0 ? 1 : -1;
+}
+
+/* ================================================== */
+
+static int
+compare_total_hits(Record *x, Record *y)
+{
+  uint32_t x_hits, y_hits;
+  int i;
+
+  for (i = 0, x_hits = y_hits = 0; i < MAX_SERVICES; i++) {
+    x_hits += x->hits[i];
+    y_hits += y->hits[i];
+  }
+
+  return x_hits > y_hits ? 1 : -1;
+}
+
+/* ================================================== */
+
+static Record *
+get_record(IPAddr *ip)
+{
+  uint32_t last_hit = 0, oldest_hit = 0;
+  Record *record, *oldest_record;
+  unsigned int first, i, j;
+
+  if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
+    return NULL;
+
+  while (1) {
+    /* Get index of the first record in the slot */
+    first = UTI_IPToHash(ip) % slots * SLOT_SIZE;
+
+    for (i = 0, oldest_record = NULL; i < SLOT_SIZE; i++) {
+      record = ARR_GetElement(records, first + i);
+
+      if (!UTI_CompareIPs(ip, &record->ip_addr, NULL))
+        return record;
+
+      if (record->ip_addr.family == IPADDR_UNSPEC)
+        break;
+
+      for (j = 0; j < MAX_SERVICES; j++) {
+        if (j == 0 || compare_ts(last_hit, record->last_hit[j]) < 0)
+          last_hit = record->last_hit[j];
+      }
+
+      if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 ||
+          (oldest_hit == last_hit && compare_total_hits(oldest_record, record) > 0)) {
+        oldest_record = record;
+        oldest_hit = last_hit;
+      }
+    }
+
+    /* If the slot still has an empty record, use it */
+    if (record->ip_addr.family == IPADDR_UNSPEC)
+      break;
+
+    /* Resize the table if possible and try again as the new slot may
+       have some empty records */
+    if (expand_hashtable())
+      continue;
+
+    /* There is no other option, replace the oldest record */
+    record = oldest_record;
+    total_record_drops++;
+    break;
+  }
+
+  record->ip_addr = *ip;
+  for (i = 0; i < MAX_SERVICES; i++)
+    record->last_hit[i] = INVALID_TS;
+  for (i = 0; i < MAX_SERVICES; i++)
+    record->hits[i] = 0;
+  for (i = 0; i < MAX_SERVICES; i++)
+    record->drops[i] = 0;
+  for (i = 0; i < MAX_SERVICES; i++)
+    record->tokens[i] = max_tokens[i];
+  for (i = 0; i < MAX_SERVICES; i++)
+    record->rate[i] = INVALID_RATE;
+  record->ntp_timeout_rate = INVALID_RATE;
+  record->drop_flags = 0;
+
+  return record;
+}
+
+/* ================================================== */
+
+static int
+expand_hashtable(void)
+{
+  ARR_Instance old_records;
+  Record *old_record, *new_record;
+  unsigned int i;
+
+  old_records = records;
+
+  if (2 * slots > max_slots)
+    return 0;
+
+  records = ARR_CreateInstance(sizeof (Record));
+
+  slots = MAX(MIN_SLOTS, 2 * slots);
+  assert(slots <= max_slots);
+
+  ARR_SetSize(records, slots * SLOT_SIZE);
+
+  /* Mark all new records as empty */
+  for (i = 0; i < slots * SLOT_SIZE; i++) {
+    new_record = ARR_GetElement(records, i);
+    new_record->ip_addr.family = IPADDR_UNSPEC;
+  }
+
+  if (!old_records)
+    return 1;
+
+  /* Copy old records to the new hash table */
+  for (i = 0; i < ARR_GetSize(old_records); i++) {
+    old_record = ARR_GetElement(old_records, i);
+    if (old_record->ip_addr.family == IPADDR_UNSPEC)
+      continue;
+
+    new_record = get_record(&old_record->ip_addr);
+
+    assert(new_record);
+    *new_record = *old_record;
+  }
+
+  ARR_DestroyInstance(old_records);
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+set_bucket_params(int interval, int burst, uint16_t *max_tokens,
+                  uint16_t *tokens_per_packet, int *token_shift)
+{
+  interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
+  burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST);
+
+  if (interval >= -TS_FRAC) {
+    /* Find the smallest shift with which the maximum number fits in 16 bits */
+    for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
+      if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
+        break;
+    }
+  } else {
+    /* Coarse rate limiting */
+    *token_shift = interval + TS_FRAC;
+    *tokens_per_packet = 1;
+    burst = MAX(1U << -*token_shift, burst);
+  }
+
+  *tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
+  *max_tokens = *tokens_per_packet * burst;
+
+  DEBUG_LOG("Tokens max %d packet %d shift %d",
+            *max_tokens, *tokens_per_packet, *token_shift);
+}
+
+/* ================================================== */
+
+void
+CLG_Initialise(void)
+{
+  int i, interval, burst, lrate, slots2;
+
+  for (i = 0; i < MAX_SERVICES; i++) {
+    max_tokens[i] = 0;
+    tokens_per_hit[i] = 0;
+    token_shift[i] = 0;
+    leak_rate[i] = 0;
+    limit_interval[i] = MIN_LIMIT_INTERVAL;
+
+    switch (i) {
+      case CLG_NTP:
+        if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
+          continue;
+        break;
+      case CLG_NTSKE:
+        if (!CNF_GetNtsRateLimit(&interval, &burst, &lrate))
+          continue;
+        break;
+      case CLG_CMDMON:
+        if (!CNF_GetCommandRateLimit(&interval, &burst, &lrate))
+          continue;
+        break;
+      default:
+        assert(0);
+    }
+
+    set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
+    leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
+    limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
+  }
+
+  active = !CNF_GetNoClientLog();
+  if (!active) {
+    for (i = 0; i < MAX_SERVICES; i++) {
+      if (leak_rate[i] != 0)
+        LOG_FATAL("Rate limiting cannot be enabled with noclientlog");
+    }
+    return;
+  }
+
+  /* Calculate the maximum number of slots that can be allocated in the
+     configured memory limit.  Take into account expanding of the hash
+     table where two copies exist at the same time. */
+  max_slots = CNF_GetClientLogLimit() /
+              ((sizeof (Record) + sizeof (NtpTimestamps)) * SLOT_SIZE * 3 / 2);
+  max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
+  for (slots2 = 0; 1U << (slots2 + 1) <= max_slots; slots2++)
+    ;
+
+  DEBUG_LOG("Max records %u", 1U << (slots2 + SLOT_BITS));
+
+  slots = 0;
+  records = NULL;
+
+  expand_hashtable();
+
+  UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
+  ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
+
+  ntp_ts_map.timestamps = NULL;
+  ntp_ts_map.first = 0;
+  ntp_ts_map.size = 0;
+  ntp_ts_map.max_size = 1U << (slots2 + SLOT_BITS);
+  ntp_ts_map.cached_index = 0;
+  ntp_ts_map.cached_rx_ts = 0ULL;
+  ntp_ts_map.slew_epoch = 0;
+  ntp_ts_map.slew_offset = 0.0;
+
+  LCL_AddParameterChangeHandler(handle_slew, NULL);
+}
+
+/* ================================================== */
+
+void
+CLG_Finalise(void)
+{
+  if (!active)
+    return;
+
+  ARR_DestroyInstance(records);
+  if (ntp_ts_map.timestamps)
+    ARR_DestroyInstance(ntp_ts_map.timestamps);
+
+  LCL_RemoveParameterChangeHandler(handle_slew, NULL);
+}
+
+/* ================================================== */
+
+static uint32_t
+get_ts_from_timespec(struct timespec *ts)
+{
+  uint32_t sec = ts->tv_sec, nsec = ts->tv_nsec;
+
+  nsec += ts_offset;
+  if (nsec >= NSEC_PER_SEC) {
+    nsec -= NSEC_PER_SEC;
+    sec++;
+  }
+
+  /* This is fast and accurate enough */
+  return sec << TS_FRAC | (140740U * (nsec >> 15)) >> (32 - TS_FRAC);
+}
+
+/* ================================================== */
+
+static void
+update_record(CLG_Service service, Record *record, struct timespec *now)
+{
+  uint32_t interval, now_ts, prev_hit, tokens;
+  int interval2, tshift, mtokens;
+  int8_t *rate;
+
+  now_ts = get_ts_from_timespec(now);
+
+  prev_hit = record->last_hit[service];
+  record->last_hit[service] = now_ts;
+  record->hits[service]++;
+
+  interval = now_ts - prev_hit;
+
+  if (prev_hit == INVALID_TS || (int32_t)interval < 0)
+    return;
+
+  tshift = token_shift[service];
+  mtokens = max_tokens[service];
+
+  if (tshift >= 0)
+    tokens = (now_ts >> tshift) - (prev_hit >> tshift);
+  else if (now_ts - prev_hit > mtokens)
+    tokens = mtokens;
+  else
+    tokens = (now_ts - prev_hit) << -tshift;
+  record->tokens[service] = MIN(record->tokens[service] + tokens, mtokens);
+
+  /* Convert the interval to scaled and rounded log2 */
+  if (interval) {
+    interval += interval >> 1;
+    for (interval2 = -RATE_SCALE * TS_FRAC; interval2 < -MIN_RATE;
+         interval2 += RATE_SCALE) {
+      if (interval <= 1)
+        break;
+      interval >>= 1;
+    }
+  } else {
+    interval2 = -RATE_SCALE * (TS_FRAC + 1);
+  }
+
+  /* For the NTP service, update one of the two rates depending on whether
+     the previous request of the client had a reply or it timed out */
+  rate = service == CLG_NTP && record->drop_flags & (1U << service) ?
+           &record->ntp_timeout_rate : &record->rate[service];
+
+  /* Update the rate in a rough approximation of exponential moving average */
+  if (*rate == INVALID_RATE) {
+    *rate = -interval2;
+  } else {
+    if (*rate < -interval2) {
+      (*rate)++;
+    } else if (*rate > -interval2) {
+      if (*rate > RATE_SCALE * 5 / 2 - interval2)
+        *rate = RATE_SCALE * 5 / 2 - interval2;
+      else
+        *rate = (*rate - interval2 - 1) / 2;
+    }
+  }
+}
+
+/* ================================================== */
+
+static int
+get_index(Record *record)
+{
+  return record - (Record *)ARR_GetElements(records);
+}
+
+/* ================================================== */
+
+int
+CLG_GetClientIndex(IPAddr *client)
+{
+  Record *record;
+
+  record = get_record(client);
+  if (record == NULL)
+    return -1;
+
+  return get_index(record);
+}
+
+/* ================================================== */
+
+static void
+check_service_number(CLG_Service service)
+{
+  assert(service >= 0 && service <= MAX_SERVICES);
+}
+
+/* ================================================== */
+
+int
+CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
+{
+  Record *record;
+
+  check_service_number(service);
+
+  total_hits[service]++;
+
+  record = get_record(client);
+  if (record == NULL)
+    return -1;
+
+  update_record(service, record, now);
+
+  DEBUG_LOG("service %d hits %"PRIu32" rate %d trate %d tokens %d",
+            (int)service, record->hits[service], record->rate[service],
+            service == CLG_NTP ? record->ntp_timeout_rate : INVALID_RATE,
+            record->tokens[service]);
+
+  return get_index(record);
+}
+
+/* ================================================== */
+
+static int
+limit_response_random(int leak_rate)
+{
+  static uint32_t rnd;
+  static int bits_left = 0;
+  int r;
+
+  if (bits_left < leak_rate) {
+    UTI_GetRandomBytes(&rnd, sizeof (rnd));
+    bits_left = 8 * sizeof (rnd);
+  }
+
+  /* Return zero on average once per 2^leak_rate */
+  r = rnd % (1U << leak_rate) ? 1 : 0;
+  rnd >>= leak_rate;
+  bits_left -= leak_rate;
+
+  return r;
+}
+
+/* ================================================== */
+
+int
+CLG_LimitServiceRate(CLG_Service service, int index)
+{
+  Record *record;
+  int drop;
+
+  check_service_number(service);
+
+  if (tokens_per_hit[service] == 0)
+    return 0;
+
+  record = ARR_GetElement(records, index);
+  record->drop_flags &= ~(1U << service);
+
+  if (record->tokens[service] >= tokens_per_hit[service]) {
+    record->tokens[service] -= tokens_per_hit[service];
+    return 0;
+  }
+
+  drop = limit_response_random(leak_rate[service]);
+
+  /* Poorly implemented NTP clients can send requests at a higher rate
+     when they are not getting replies.  If the request rate seems to be more
+     than twice as much as when replies are sent, give up on rate limiting to
+     reduce the amount of traffic.  Invert the sense of the leak to respond to
+     most of the requests, but still keep the estimated rate updated. */
+  if (service == CLG_NTP && record->ntp_timeout_rate != INVALID_RATE &&
+      record->ntp_timeout_rate > record->rate[service] + RATE_SCALE)
+    drop = !drop;
+
+  if (!drop) {
+    record->tokens[service] = 0;
+    return 0;
+  }
+
+  record->drop_flags |= 1U << service;
+  record->drops[service]++;
+  total_drops[service]++;
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+CLG_LogAuthNtpRequest(void)
+{
+  total_ntp_auth_hits++;
+}
+
+/* ================================================== */
+
+int
+CLG_GetNtpMinPoll(void)
+{
+  return limit_interval[CLG_NTP];
+}
+
+/* ================================================== */
+
+static NtpTimestamps *
+get_ntp_tss(uint32_t index)
+{
+  return ARR_GetElement(ntp_ts_map.timestamps,
+                        (ntp_ts_map.first + index) & (ntp_ts_map.max_size - 1));
+}
+
+/* ================================================== */
+
+static int
+find_ntp_rx_ts(uint64_t rx_ts, uint32_t *index)
+{
+  uint64_t rx_x, rx_lo, rx_hi, step;
+  uint32_t i, x, lo, hi;
+
+  if (ntp_ts_map.cached_rx_ts == rx_ts && rx_ts != 0ULL) {
+    *index = ntp_ts_map.cached_index;
+    return 1;
+  }
+
+  if (ntp_ts_map.size == 0) {
+    *index = 0;
+    return 0;
+  }
+
+  lo = 0;
+  hi = ntp_ts_map.size - 1;
+  rx_lo = get_ntp_tss(lo)->rx_ts;
+  rx_hi = get_ntp_tss(hi)->rx_ts;
+
+  /* Check for ts < lo before ts > hi to trim timestamps from "future" later
+     if both conditions are true to not break the order of the endpoints.
+     Compare timestamps by their difference to allow adjacent NTP eras. */
+  if ((int64_t)(rx_ts - rx_lo) < 0) {
+    *index = 0;
+    return 0;
+  } else if ((int64_t)(rx_ts - rx_hi) > 0) {
+    *index = ntp_ts_map.size;
+    return 0;
+  }
+
+  /* Perform a combined linear interpolation and binary search */
+
+  for (i = 0; ; i++) {
+    if (rx_ts == rx_hi) {
+      *index = ntp_ts_map.cached_index = hi;
+      ntp_ts_map.cached_rx_ts = rx_ts;
+      return 1;
+    } else if (rx_ts == rx_lo) {
+      *index = ntp_ts_map.cached_index = lo;
+      ntp_ts_map.cached_rx_ts = rx_ts;
+      return 1;
+    } else if (lo + 1 == hi) {
+      *index = hi;
+      return 0;
+    }
+
+    if (hi - lo > 3 && i % 2 == 0) {
+      step = (rx_hi - rx_lo) / (hi - lo);
+      if (step == 0)
+        step = 1;
+      x = lo + (rx_ts - rx_lo) / step;
+    } else {
+      x = lo + (hi - lo) / 2;
+    }
+
+    if (x <= lo)
+      x = lo + 1;
+    else if (x >= hi)
+      x = hi - 1;
+
+    rx_x = get_ntp_tss(x)->rx_ts;
+
+    if ((int64_t)(rx_x - rx_ts) <= 0) {
+      lo = x;
+      rx_lo = rx_x;
+    } else {
+      hi = x;
+      rx_hi = rx_x;
+    }
+  }
+}
+
+/* ================================================== */
+
+static uint64_t
+ntp64_to_int64(NTP_int64 *ts)
+{
+  return (uint64_t)ntohl(ts->hi) << 32 | ntohl(ts->lo);
+}
+
+/* ================================================== */
+
+static void
+int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
+{
+  ntp_ts->hi = htonl(ts >> 32);
+  ntp_ts->lo = htonl(ts);
+}
+
+/* ================================================== */
+
+static uint32_t
+push_ntp_tss(uint32_t index)
+{
+  if (ntp_ts_map.size < ntp_ts_map.max_size) {
+    ntp_ts_map.size++;
+  } else {
+    ntp_ts_map.first = (ntp_ts_map.first + 1) % (ntp_ts_map.max_size);
+    if (index > 0)
+      index--;
+  }
+
+  return index;
+}
+
+/* ================================================== */
+
+static void
+set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
+{
+  struct timespec ts;
+
+  if (!tx_ts) {
+    tss->flags &= ~NTPTS_VALID_TX;
+    return;
+  }
+
+  UTI_Ntp64ToTimespec(rx_ts, &ts);
+  UTI_DiffTimespecs(&ts, tx_ts, &ts);
+
+  if (ts.tv_sec < -2 || ts.tv_sec > 1) {
+    tss->flags &= ~NTPTS_VALID_TX;
+    return;
+  }
+
+  tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
+  tss->flags |= NTPTS_VALID_TX;
+}
+
+/* ================================================== */
+
+static void
+get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
+{
+  int32_t offset = tss->tx_ts_offset;
+  NTP_int64 ntp_ts;
+
+  if (tss->flags & NTPTS_VALID_TX) {
+    int64_to_ntp64(tss->rx_ts, &ntp_ts);
+    UTI_Ntp64ToTimespec(&ntp_ts, tx_ts);
+    if (offset >= (int32_t)NSEC_PER_SEC) {
+      offset -= NSEC_PER_SEC;
+      tx_ts->tv_sec++;
+    }
+    tx_ts->tv_nsec += offset;
+    UTI_NormaliseTimespec(tx_ts);
+  } else {
+    UTI_ZeroTimespec(tx_ts);
+  }
+}
+
+/* ================================================== */
+
+void
+CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
+{
+  NtpTimestamps *tss;
+  uint32_t i, index;
+  uint64_t rx;
+
+  if (!active)
+    return;
+
+  /* Allocate the array on first use */
+  if (!ntp_ts_map.timestamps) {
+    ntp_ts_map.timestamps = ARR_CreateInstance(sizeof (NtpTimestamps));
+    ARR_SetSize(ntp_ts_map.timestamps, ntp_ts_map.max_size);
+  }
+
+  rx = ntp64_to_int64(rx_ts);
+
+  if (rx == 0ULL)
+    return;
+
+  /* Disable the RX timestamp if it already exists to avoid responding
+     with a wrong TX timestamp */
+  if (find_ntp_rx_ts(rx, &index)) {
+    get_ntp_tss(index)->flags |= NTPTS_DISABLED;
+    return;
+  }
+
+  assert(index <= ntp_ts_map.size);
+
+  if (index == ntp_ts_map.size) {
+    /* Increase the size or drop the oldest timestamp to make room for
+       the new timestamp */
+    index = push_ntp_tss(index);
+  } else {
+    /* Trim timestamps in distant future after backward step */
+    while (index < ntp_ts_map.size &&
+           get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - rx > NTPTS_FUTURE_LIMIT)
+      ntp_ts_map.size--;
+
+    /* Insert the timestamp if it is close to the latest timestamp.
+       Otherwise, replace the closest older or the oldest timestamp. */
+    if (index + NTPTS_INSERT_LIMIT >= ntp_ts_map.size) {
+      index = push_ntp_tss(index);
+      for (i = ntp_ts_map.size - 1; i > index; i--)
+        *get_ntp_tss(i) = *get_ntp_tss(i - 1);
+    } else {
+      if (index > 0)
+        index--;
+    }
+  }
+
+  ntp_ts_map.cached_index = index;
+  ntp_ts_map.cached_rx_ts = rx;
+
+  tss = get_ntp_tss(index);
+  tss->rx_ts = rx;
+  tss->flags = 0;
+  tss->slew_epoch = ntp_ts_map.slew_epoch;
+  set_ntp_tx_offset(tss, rx_ts, tx_ts);
+
+  DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
+            index, ntp_ts_map.first, ntp_ts_map.size);
+}
+
+/* ================================================== */
+
+static void
+handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
+            double doffset, LCL_ChangeType change_type, void *anything)
+{
+  /* Drop all timestamps on unknown step */
+  if (change_type == LCL_ChangeUnknownStep) {
+    ntp_ts_map.size = 0;
+    ntp_ts_map.cached_rx_ts = 0ULL;
+  }
+
+  ntp_ts_map.slew_epoch++;
+  ntp_ts_map.slew_offset = doffset;
+}
+
+/* ================================================== */
+
+void
+CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
+{
+  uint32_t index;
+
+  if (!ntp_ts_map.timestamps)
+    return;
+
+  if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
+    return;
+
+  /* If the RX timestamp was captured before the last correction of the clock,
+     remove the adjustment from the TX timestamp */
+  if ((uint16_t)(get_ntp_tss(index)->slew_epoch + 1U) == ntp_ts_map.slew_epoch)
+    UTI_AddDoubleToTimespec(tx_ts, ntp_ts_map.slew_offset, tx_ts);
+}
+
+/* ================================================== */
+
+void
+CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
+{
+  uint32_t index;
+
+  if (!ntp_ts_map.timestamps)
+    return;
+
+  if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
+    return;
+
+  set_ntp_tx_offset(get_ntp_tss(index), rx_ts, tx_ts);
+}
+
+/* ================================================== */
+
+int
+CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
+{
+  NtpTimestamps *tss;
+  uint32_t index;
+
+  if (!ntp_ts_map.timestamps)
+    return 0;
+
+  if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
+    return 0;
+
+  tss = get_ntp_tss(index);
+
+  if (tss->flags & NTPTS_DISABLED)
+    return 0;
+
+  get_ntp_tx(tss, tx_ts);
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+CLG_DisableNtpTimestamps(NTP_int64 *rx_ts)
+{
+  uint32_t index;
+
+  if (!ntp_ts_map.timestamps)
+    return;
+
+  if (find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
+    get_ntp_tss(index)->flags |= NTPTS_DISABLED;
+
+  /* This assumes the function is called only to prevent multiple
+     interleaved responses to the same timestamp */
+  total_ntp_interleaved_hits++;
+}
+
+/* ================================================== */
+
+int
+CLG_GetNumberOfIndices(void)
+{
+  if (!active)
+    return -1;
+
+  return ARR_GetSize(records);
+}
+
+/* ================================================== */
+
+static int get_interval(int rate)
+{
+  if (rate == INVALID_RATE)
+    return 127;
+
+  rate += rate > 0 ? RATE_SCALE / 2 : -RATE_SCALE / 2;
+
+  return rate / -RATE_SCALE;
+}
+
+/* ================================================== */
+
+static uint32_t get_last_ago(uint32_t x, uint32_t y)
+{
+  if (y == INVALID_TS || (int32_t)(x - y) < 0)
+    return -1;
+
+  return (x - y) >> TS_FRAC;
+}
+
+/* ================================================== */
+
+int
+CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
+                                 RPT_ClientAccessByIndex_Report *report, struct timespec *now)
+{
+  Record *record;
+  uint32_t now_ts;
+  int i, r;
+
+  if (!active || index < 0 || index >= ARR_GetSize(records))
+    return 0;
+
+  record = ARR_GetElement(records, index);
+
+  if (record->ip_addr.family == IPADDR_UNSPEC)
+    return 0;
+
+  if (min_hits == 0) {
+    r = 1;
+  } else {
+    for (i = r = 0; i < MAX_SERVICES; i++) {
+      if (record->hits[i] >= min_hits) {
+        r = 1;
+        break;
+      }
+    }
+  }
+
+  if (r) {
+    now_ts = get_ts_from_timespec(now);
+
+    report->ip_addr = record->ip_addr;
+    report->ntp_hits = record->hits[CLG_NTP];
+    report->nke_hits = record->hits[CLG_NTSKE];
+    report->cmd_hits = record->hits[CLG_CMDMON];
+    report->ntp_drops = record->drops[CLG_NTP];
+    report->nke_drops = record->drops[CLG_NTSKE];
+    report->cmd_drops = record->drops[CLG_CMDMON];
+    report->ntp_interval = get_interval(record->rate[CLG_NTP]);
+    report->nke_interval = get_interval(record->rate[CLG_NTSKE]);
+    report->cmd_interval = get_interval(record->rate[CLG_CMDMON]);
+    report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
+    report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTP]);
+    report->last_nke_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTSKE]);
+    report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_CMDMON]);
+  }
+
+  if (reset) {
+    for (i = 0; i < MAX_SERVICES; i++) {
+      record->hits[i] = 0;
+      record->drops[i] = 0;
+    }
+  }
+
+  return r;
+}
+
+/* ================================================== */
+
+void
+CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
+{
+  report->ntp_hits = total_hits[CLG_NTP];
+  report->nke_hits = total_hits[CLG_NTSKE];
+  report->cmd_hits = total_hits[CLG_CMDMON];
+  report->ntp_drops = total_drops[CLG_NTP];
+  report->nke_drops = total_drops[CLG_NTSKE];
+  report->cmd_drops = total_drops[CLG_CMDMON];
+  report->log_drops = total_record_drops;
+  report->ntp_auth_hits = total_ntp_auth_hits;
+  report->ntp_interleaved_hits = total_ntp_interleaved_hits;
+  report->ntp_timestamps = ntp_ts_map.size;
+  report->ntp_span_seconds = ntp_ts_map.size > 1 ?
+                             (get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
+                              get_ntp_tss(0)->rx_ts) >> 32 : 0;
+}
diff --git a/chrony_3_3/clientlog.h b/chrony/clientlog.h
similarity index 61%
rename from chrony_3_3/clientlog.h
rename to chrony/clientlog.h
index 552c767..2a5565e 100644
--- a/chrony_3_3/clientlog.h
+++ b/chrony/clientlog.h
@@ -31,20 +31,33 @@
 #include "sysincl.h"
 #include "reports.h"
 
+typedef enum {
+  CLG_NTP = 0,
+  CLG_NTSKE,
+  CLG_CMDMON,
+} CLG_Service;
+
 extern void CLG_Initialise(void);
 extern void CLG_Finalise(void);
 extern int CLG_GetClientIndex(IPAddr *client);
-extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
-extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
-extern int CLG_LimitNTPResponseRate(int index);
-extern int CLG_LimitCommandResponseRate(int index);
-extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
+extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
+extern int CLG_LimitServiceRate(CLG_Service service, int index);
+extern void CLG_LogAuthNtpRequest(void);
 extern int CLG_GetNtpMinPoll(void);
 
+/* Functions to save and retrieve timestamps for server interleaved mode */
+extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts);
+extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
+extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
+extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
+extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
+
 /* And some reporting functions, for use by chronyc. */
 
 extern int CLG_GetNumberOfIndices(void);
-extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now);
+extern int CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
+                                            RPT_ClientAccessByIndex_Report *report,
+                                            struct timespec *now);
 extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
 
 #endif /* GOT_CLIENTLOG_H */
diff --git a/chrony/cmac.h b/chrony/cmac.h
new file mode 100644
index 0000000..935820d
--- /dev/null
+++ b/chrony/cmac.h
@@ -0,0 +1,48 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for CMAC.
+
+  */
+
+#ifndef GOT_CMAC_H
+#define GOT_CMAC_H
+
+/* Avoid overlapping with the hash enumeration */
+typedef enum {
+  CMC_INVALID = 0,
+  CMC_AES128 = 13,
+  CMC_AES256 = 14,
+} CMC_Algorithm;
+
+typedef struct CMC_Instance_Record *CMC_Instance;
+
+extern int CMC_GetKeyLength(CMC_Algorithm algorithm);
+extern CMC_Instance CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key,
+                                       int length);
+extern int CMC_Hash(CMC_Instance inst, const void *in, int in_len,
+                    unsigned char *out, int out_len);
+extern void CMC_DestroyInstance(CMC_Instance inst);
+
+#endif
+
diff --git a/chrony/cmac_gnutls.c b/chrony/cmac_gnutls.c
new file mode 100644
index 0000000..d1cd550
--- /dev/null
+++ b/chrony/cmac_gnutls.c
@@ -0,0 +1,189 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2021
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ **********************************************************************
+
+  =======================================================================
+
+  CMAC using the GnuTLS library
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include <gnutls/crypto.h>
+
+#include "cmac.h"
+#include "hash.h"
+#include "logging.h"
+#include "memory.h"
+
+struct CMC_Instance_Record {
+  gnutls_mac_algorithm_t algorithm;
+  gnutls_hmac_hd_t mac;
+};
+
+/* ================================================== */
+
+static int instance_counter = 0;
+static int gnutls_initialised = 0;
+
+/* ================================================== */
+
+static void
+init_gnutls(void)
+{
+  int r;
+
+  if (gnutls_initialised)
+    return;
+
+  r = gnutls_global_init();
+  if (r < 0)
+    LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
+
+  DEBUG_LOG("Initialised");
+  gnutls_initialised = 1;
+}
+
+/* ================================================== */
+
+static void
+deinit_gnutls(void)
+{
+  assert(gnutls_initialised);
+  gnutls_global_deinit();
+  gnutls_initialised = 0;
+  DEBUG_LOG("Deinitialised");
+}
+
+/* ================================================== */
+
+static gnutls_mac_algorithm_t
+get_mac_algorithm(CMC_Algorithm algorithm)
+{
+  switch (algorithm) {
+    case CMC_AES128:
+      return GNUTLS_MAC_AES_CMAC_128;
+    case CMC_AES256:
+      return GNUTLS_MAC_AES_CMAC_256;
+    default:
+      return GNUTLS_MAC_UNKNOWN;
+  }
+}
+
+/* ================================================== */
+
+int
+CMC_GetKeyLength(CMC_Algorithm algorithm)
+{
+  gnutls_mac_algorithm_t malgo = get_mac_algorithm(algorithm);
+  int len;
+
+  if (malgo == GNUTLS_MAC_UNKNOWN)
+    return 0;
+
+  len = gnutls_hmac_get_key_size(malgo);
+
+  if (len < 0)
+    return 0;
+
+  return len;
+}
+
+/* ================================================== */
+
+CMC_Instance
+CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
+{
+  gnutls_hmac_hd_t handle;
+  CMC_Instance inst;
+
+  int r;
+
+  if (instance_counter == 0)
+    init_gnutls();
+
+  if (length <= 0 || length != CMC_GetKeyLength(algorithm))
+    goto error;
+
+  r = gnutls_hmac_init(&handle, get_mac_algorithm(algorithm), key, length);
+  if (r < 0) {
+    DEBUG_LOG("Could not initialise %s : %s", "mac", gnutls_strerror(r));
+    goto error;
+  }
+
+  inst = MallocNew(struct CMC_Instance_Record);
+  inst->algorithm = get_mac_algorithm(algorithm);
+  inst->mac = handle;
+
+  instance_counter++;
+
+  return inst;
+
+error:
+  if (instance_counter == 0)
+    deinit_gnutls();
+  return NULL;
+}
+
+/* ================================================== */
+
+int
+CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
+{
+  unsigned char buf[MAX_HASH_LENGTH];
+  int hash_len;
+
+  if (in_len < 0 || out_len < 0)
+    return 0;
+
+  hash_len = gnutls_hmac_get_len(inst->algorithm);
+
+  if (out_len > hash_len)
+    out_len = hash_len;
+
+  if (hash_len > sizeof (buf))
+    return 0;
+
+  if (gnutls_hmac(inst->mac, in, in_len) < 0) {
+    /* Reset the state */
+    gnutls_hmac_output(inst->mac, buf);
+    return 0;
+  }
+
+  gnutls_hmac_output(inst->mac, buf);
+  memcpy(out, buf, out_len);
+
+  return out_len;
+}
+
+/* ================================================== */
+
+void
+CMC_DestroyInstance(CMC_Instance inst)
+{
+  gnutls_hmac_deinit(inst->mac, NULL);
+  Free(inst);
+
+  instance_counter--;
+  if (instance_counter == 0)
+    deinit_gnutls();
+}
diff --git a/chrony/cmac_nettle.c b/chrony/cmac_nettle.c
new file mode 100644
index 0000000..5b2c0d4
--- /dev/null
+++ b/chrony/cmac_nettle.c
@@ -0,0 +1,117 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Support for AES128 and AES256 CMAC in Nettle.
+
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include <nettle/cmac.h>
+
+#include "cmac.h"
+#include "memory.h"
+
+struct CMC_Instance_Record {
+  int key_length;
+  union {
+    struct cmac_aes128_ctx aes128;
+    struct cmac_aes256_ctx aes256;
+  } context;
+};
+
+/* ================================================== */
+
+int
+CMC_GetKeyLength(CMC_Algorithm algorithm)
+{
+  if (algorithm == CMC_AES128)
+    return AES128_KEY_SIZE;
+  else if (algorithm == CMC_AES256)
+    return AES256_KEY_SIZE;
+  return 0;
+}
+
+/* ================================================== */
+
+CMC_Instance
+CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
+{
+  CMC_Instance inst;
+
+  if (length <= 0 || length != CMC_GetKeyLength(algorithm))
+    return NULL;
+
+  inst = MallocNew(struct CMC_Instance_Record);
+  inst->key_length = length;
+
+  switch (length) {
+    case AES128_KEY_SIZE:
+      cmac_aes128_set_key(&inst->context.aes128, key);
+      break;
+    case AES256_KEY_SIZE:
+      cmac_aes256_set_key(&inst->context.aes256, key);
+      break;
+    default:
+      assert(0);
+  }
+
+  return inst;
+}
+
+/* ================================================== */
+
+int
+CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
+{
+  if (in_len < 0 || out_len < 0)
+    return 0;
+
+  if (out_len > CMAC128_DIGEST_SIZE)
+    out_len = CMAC128_DIGEST_SIZE;
+
+  switch (inst->key_length) {
+    case AES128_KEY_SIZE:
+      cmac_aes128_update(&inst->context.aes128, in_len, in);
+      cmac_aes128_digest(&inst->context.aes128, out_len, out);
+      break;
+    case AES256_KEY_SIZE:
+      cmac_aes256_update(&inst->context.aes256, in_len, in);
+      cmac_aes256_digest(&inst->context.aes256, out_len, out);
+      break;
+    default:
+      assert(0);
+  }
+
+  return out_len;
+}
+
+/* ================================================== */
+
+void
+CMC_DestroyInstance(CMC_Instance inst)
+{
+  Free(inst);
+}
diff --git a/chrony_3_3/cmdmon.c b/chrony/cmdmon.c
similarity index 76%
rename from chrony_3_3/cmdmon.c
rename to chrony/cmdmon.c
index 3bd8d91..89f5b95 100644
--- a/chrony_3_3/cmdmon.c
+++ b/chrony/cmdmon.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2009-2016
+ * Copyright (C) Miroslav Lichvar  2009-2016, 2018-2021
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -38,11 +38,13 @@
 #include "ntp_sources.h"
 #include "ntp_core.h"
 #include "smooth.h"
+#include "socket.h"
 #include "sources.h"
 #include "sourcestats.h"
 #include "reference.h"
 #include "manual.h"
 #include "memory.h"
+#include "nts_ke_server.h"
 #include "local.h"
 #include "addrfilt.h"
 #include "conf.h"
@@ -53,21 +55,15 @@
 
 /* ================================================== */
 
-union sockaddr_all {
-  struct sockaddr_in in4;
-#ifdef FEAT_IPV6
-  struct sockaddr_in6 in6;
-#endif
-  struct sockaddr_un un;
-  struct sockaddr sa;
-};
+#define INVALID_SOCK_FD (-5)
 
 /* File descriptors for command and monitoring sockets */
 static int sock_fdu;
 static int sock_fd4;
-#ifdef FEAT_IPV6
 static int sock_fd6;
-#endif
+
+/* Flag indicating the IPv4 socket is bound to an address */
+static int bound_sock_fd4;
 
 /* Flag indicating whether this module has been initialised or not */
 static int initialised = 0;
@@ -139,6 +135,15 @@
   PERMIT_AUTH, /* ADD_SERVER3 */
   PERMIT_AUTH, /* ADD_PEER3 */
   PERMIT_AUTH, /* SHUTDOWN */
+  PERMIT_AUTH, /* ONOFFLINE */
+  PERMIT_AUTH, /* ADD_SOURCE */
+  PERMIT_OPEN, /* NTP_SOURCE_NAME */
+  PERMIT_AUTH, /* RESET_SOURCES */
+  PERMIT_AUTH, /* AUTH_DATA */
+  PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
+  PERMIT_AUTH, /* SELECT_DATA */
+  PERMIT_AUTH, /* RELOAD_SOURCES */
+  PERMIT_AUTH, /* DOFFSET2 */
 };
 
 /* ================================================== */
@@ -154,99 +159,48 @@
 /* ================================================== */
 
 static int
-prepare_socket(int family, int port_number)
+open_socket(int family)
 {
-  int sock_fd;
-  socklen_t my_addr_len;
-  union sockaddr_all my_addr;
-  IPAddr bind_address;
-  int on_off = 1;
-
-  sock_fd = socket(family, SOCK_DGRAM, 0);
-  if (sock_fd < 0) {
-    LOG(LOGS_ERR, "Could not open %s command socket : %s",
-        UTI_SockaddrFamilyToString(family), strerror(errno));
-    return -1;
-  }
-
-  /* Close on exec */
-  UTI_FdSetCloexec(sock_fd);
-
-  if (family != AF_UNIX) {
-    /* Allow reuse of port number */
-    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) {
-      LOG(LOGS_ERR, "Could not set reuseaddr socket options");
-      /* Don't quit - we might survive anyway */
-    }
-
-#ifdef IP_FREEBIND
-    /* Allow binding to address that doesn't exist yet */
-    if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
-      LOG(LOGS_ERR, "Could not set free bind socket option");
-    }
-#endif
-
-#ifdef FEAT_IPV6
-    if (family == AF_INET6) {
-#ifdef IPV6_V6ONLY
-      /* Receive IPv6 packets only */
-      if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
-        LOG(LOGS_ERR, "Could not request IPV6_V6ONLY socket option");
-      }
-#endif
-    }
-#endif
-  }
-
-  memset(&my_addr, 0, sizeof (my_addr));
+  const char *local_path, *iface;
+  IPSockAddr local_addr;
+  int sock_fd, port;
 
   switch (family) {
-    case AF_INET:
-      my_addr_len = sizeof (my_addr.in4);
-      my_addr.in4.sin_family = family;
-      my_addr.in4.sin_port = htons((unsigned short)port_number);
+    case IPADDR_INET4:
+    case IPADDR_INET6:
+      port = CNF_GetCommandPort();
+      if (port == 0 || !SCK_IsIpFamilyEnabled(family))
+        return INVALID_SOCK_FD;
 
-      CNF_GetBindCommandAddress(IPADDR_INET4, &bind_address);
+      CNF_GetBindCommandAddress(family, &local_addr.ip_addr);
+      local_addr.port = port;
+      iface = CNF_GetBindCommandInterface();
 
-      if (bind_address.family == IPADDR_INET4)
-        my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
-      else
-        my_addr.in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+      sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, iface, SCK_FLAG_RX_DEST_ADDR);
+      if (sock_fd < 0) {
+        LOG(LOGS_ERR, "Could not open command socket on %s",
+            UTI_IPSockAddrToString(&local_addr));
+        return INVALID_SOCK_FD;
+      }
+
+      if (family == IPADDR_INET4)
+        bound_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
+
       break;
-#ifdef FEAT_IPV6
-    case AF_INET6:
-      my_addr_len = sizeof (my_addr.in6);
-      my_addr.in6.sin6_family = family;
-      my_addr.in6.sin6_port = htons((unsigned short)port_number);
+    case IPADDR_UNSPEC:
+      local_path = CNF_GetBindCommandPath();
 
-      CNF_GetBindCommandAddress(IPADDR_INET6, &bind_address);
+      sock_fd = SCK_OpenUnixDatagramSocket(NULL, local_path, 0);
+      if (sock_fd < 0) {
+        LOG(LOGS_ERR, "Could not open command socket on %s", local_path);
+        return INVALID_SOCK_FD;
+      }
 
-      if (bind_address.family == IPADDR_INET6)
-        memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
-            sizeof (my_addr.in6.sin6_addr.s6_addr));
-      else
-        my_addr.in6.sin6_addr = in6addr_loopback;
-      break;
-#endif
-    case AF_UNIX:
-      my_addr_len = sizeof (my_addr.un);
-      my_addr.un.sun_family = family;
-      if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s",
-                   CNF_GetBindCommandPath()) >= sizeof (my_addr.un.sun_path))
-        LOG_FATAL("Unix socket path too long");
-      unlink(my_addr.un.sun_path);
       break;
     default:
       assert(0);
   }
 
-  if (bind(sock_fd, &my_addr.sa, my_addr_len) < 0) {
-    LOG(LOGS_ERR, "Could not bind %s command socket : %s",
-        UTI_SockaddrFamilyToString(family), strerror(errno));
-    close(sock_fd);
-    return -1;
-  }
-
   /* Register handler for read events on the socket */
   SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_from_cmd_socket, NULL);
 
@@ -289,39 +243,21 @@
 /* ================================================== */
 
 void
-CAM_Initialise(int family)
+CAM_Initialise(void)
 {
-  int port_number;
-
   assert(!initialised);
   assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
   do_size_checks();
 
   initialised = 1;
-  sock_fdu = -1;
-  port_number = CNF_GetCommandPort();
 
-  if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET4))
-    sock_fd4 = prepare_socket(AF_INET, port_number);
-  else
-    sock_fd4 = -1;
-#ifdef FEAT_IPV6
-  if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET6))
-    sock_fd6 = prepare_socket(AF_INET6, port_number);
-  else
-    sock_fd6 = -1;
-#endif
+  bound_sock_fd4 = 0;
 
-  if (port_number && sock_fd4 < 0
-#ifdef FEAT_IPV6
-      && sock_fd6 < 0
-#endif
-      ) {
-    LOG_FATAL("Could not open any command socket");
-  }
+  sock_fdu = INVALID_SOCK_FD;
+  sock_fd4 = open_socket(IPADDR_INET4);
+  sock_fd6 = open_socket(IPADDR_INET6);
 
   access_auth_table = ADF_CreateTable();
-
 }
 
 /* ================================================== */
@@ -329,24 +265,24 @@
 void
 CAM_Finalise(void)
 {
-  if (sock_fdu >= 0) {
+  if (sock_fdu != INVALID_SOCK_FD) {
     SCH_RemoveFileHandler(sock_fdu);
-    close(sock_fdu);
-    unlink(CNF_GetBindCommandPath());
+    SCK_RemoveSocket(sock_fdu);
+    SCK_CloseSocket(sock_fdu);
+    sock_fdu = INVALID_SOCK_FD;
   }
-  sock_fdu = -1;
-  if (sock_fd4 >= 0) {
+
+  if (sock_fd4 != INVALID_SOCK_FD) {
     SCH_RemoveFileHandler(sock_fd4);
-    close(sock_fd4);
+    SCK_CloseSocket(sock_fd4);
+    sock_fd4 = INVALID_SOCK_FD;
   }
-  sock_fd4 = -1;
-#ifdef FEAT_IPV6
-  if (sock_fd6 >= 0) {
+
+  if (sock_fd6 != INVALID_SOCK_FD) {
     SCH_RemoveFileHandler(sock_fd6);
-    close(sock_fd6);
+    SCK_CloseSocket(sock_fd6);
+    sock_fd6 = INVALID_SOCK_FD;
   }
-  sock_fd6 = -1;
-#endif
 
   ADF_DestroyTable(access_auth_table);
 
@@ -360,51 +296,38 @@
 {
   /* This is separated from CAM_Initialise() as it needs to be called when
      the process has already dropped the root privileges */
-  if (CNF_GetBindCommandPath()[0])
-    sock_fdu = prepare_socket(AF_UNIX, 0);
+  if (CNF_GetBindCommandPath())
+    sock_fdu = open_socket(IPADDR_UNSPEC);
 }
 
 /* ================================================== */
 
 static void
-transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to)
+transmit_reply(int sock_fd, int request_length, SCK_Message *message)
 {
-  int status;
-  int tx_message_length;
-  int sock_fd;
-  socklen_t addrlen;
+  message->length = PKL_ReplyLength((CMD_Reply *)message->data);
 
-  switch (where_to->sa.sa_family) {
-    case AF_INET:
-      sock_fd = sock_fd4;
-      addrlen = sizeof (where_to->in4);
-      break;
-#ifdef FEAT_IPV6
-    case AF_INET6:
-      sock_fd = sock_fd6;
-      addrlen = sizeof (where_to->in6);
-      break;
-#endif
-    case AF_UNIX:
-      sock_fd = sock_fdu;
-      addrlen = sizeof (where_to->un);
-      break;
-    default:
-      assert(0);
-  }
-
-  tx_message_length = PKL_ReplyLength(msg);
-  status = sendto(sock_fd, (void *) msg, tx_message_length, 0,
-                  &where_to->sa, addrlen);
-
-  if (status < 0) {
-    DEBUG_LOG("Could not send to %s fd %d : %s",
-              UTI_SockaddrToString(&where_to->sa), sock_fd, strerror(errno));
+  if (request_length < message->length) {
+    DEBUG_LOG("Response longer than request req_len=%d res_len=%d",
+              request_length, message->length);
     return;
   }
 
-  DEBUG_LOG("Sent %d bytes to %s fd %d", status,
-            UTI_SockaddrToString(&where_to->sa), sock_fd);
+  /* Don't require responses to non-link-local addresses to use the same
+     interface */
+  if (message->addr_type == SCK_ADDR_IP &&
+      !SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr))
+    message->if_index = INVALID_IF_INDEX;
+
+#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
+  /* On FreeBSD a local IPv4 address cannot be specified on bound socket */
+  if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
+      (sock_fd != sock_fd4 || bound_sock_fd4))
+    message->local_addr.ip.family = IPADDR_UNSPEC;
+#endif
+
+  if (!SCK_SendMessage(sock_fd, message, 0))
+    return;
 }
   
 /* ================================================== */
@@ -413,6 +336,8 @@
 handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message)
 {
   SRC_DumpSources();
+  NSR_DumpAuthData();
+  NKS_DumpKeys();
 }
 
 /* ================================================== */
@@ -424,7 +349,7 @@
 
   UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask);
   UTI_IPNetworkToHost(&rx_message->data.online.address, &address);
-  if (!NSR_TakeSourcesOnline(&mask, &address))
+  if (!NSR_SetConnectivity(&mask, &address, SRC_ONLINE))
     tx_message->status = htons(STT_NOSUCHSOURCE);
 }
 
@@ -437,13 +362,25 @@
 
   UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask);
   UTI_IPNetworkToHost(&rx_message->data.offline.address, &address);
-  if (!NSR_TakeSourcesOffline(&mask, &address))
+  if (!NSR_SetConnectivity(&mask, &address, SRC_OFFLINE))
     tx_message->status = htons(STT_NOSUCHSOURCE);
 }
 
 /* ================================================== */
 
 static void
+handle_onoffline(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+  IPAddr address, mask;
+
+  address.family = mask.family = IPADDR_UNSPEC;
+  if (!NSR_SetConnectivity(&mask, &address, SRC_MAYBE_ONLINE))
+    ;
+}
+
+/* ================================================== */
+
+static void
 handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message)
 {
   IPAddr address, mask;
@@ -658,11 +595,8 @@
     tx_message->data.source_data.stratum = htons(report.stratum);
     tx_message->data.source_data.poll    = htons(report.poll);
     switch (report.state) {
-      case RPT_SYNC:
-        tx_message->data.source_data.state   = htons(RPY_SD_ST_SYNC);
-        break;
-      case RPT_UNREACH:
-        tx_message->data.source_data.state   = htons(RPY_SD_ST_UNREACH);
+      case RPT_NONSELECTABLE:
+        tx_message->data.source_data.state   = htons(RPY_SD_ST_NONSELECTABLE);
         break;
       case RPT_FALSETICKER:
         tx_message->data.source_data.state   = htons(RPY_SD_ST_FALSETICKER);
@@ -670,11 +604,14 @@
       case RPT_JITTERY:
         tx_message->data.source_data.state   = htons(RPY_SD_ST_JITTERY);
         break;
-      case RPT_CANDIDATE:
-        tx_message->data.source_data.state   = htons(RPY_SD_ST_CANDIDATE);
+      case RPT_SELECTABLE:
+        tx_message->data.source_data.state   = htons(RPY_SD_ST_SELECTABLE);
         break;
-      case RPT_OUTLIER:
-        tx_message->data.source_data.state   = htons(RPY_SD_ST_OUTLIER);
+      case RPT_UNSELECTED:
+        tx_message->data.source_data.state   = htons(RPY_SD_ST_UNSELECTED);
+        break;
+      case RPT_SELECTED:
+        tx_message->data.source_data.state   = htons(RPY_SD_ST_SELECTED);
         break;
     }
     switch (report.mode) {
@@ -688,11 +625,7 @@
         tx_message->data.source_data.mode    = htons(RPY_SD_MD_REF);
         break;
     }
-    tx_message->data.source_data.flags =
-                  htons((report.sel_options & SRC_SELECT_PREFER ? RPY_SD_FLAG_PREFER : 0) |
-                        (report.sel_options & SRC_SELECT_NOSELECT ? RPY_SD_FLAG_NOSELECT : 0) |
-                        (report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0) |
-                        (report.sel_options & SRC_SELECT_REQUIRE ? RPY_SD_FLAG_REQUIRE : 0));
+    tx_message->data.source_data.flags = htons(0);
     tx_message->data.source_data.reachability = htons(report.reachability);
     tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago);
     tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas);
@@ -709,6 +642,7 @@
 handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message)
 {
   KEY_Reload();
+  NKS_ReloadKeys();
 }
 
 /* ================================================== */
@@ -770,14 +704,41 @@
 /* ================================================== */
 
 static void
-handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_message)
+handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
 {
-  NTP_Remote_Address rem_addr;
+  NTP_Source_Type type;
   SourceParameters params;
   NSR_Status status;
+  char *name;
+  int pool, port;
   
-  UTI_IPNetworkToHost(&rx_message->data.ntp_source.ip_addr, &rem_addr.ip_addr);
-  rem_addr.port = (unsigned short)(ntohl(rx_message->data.ntp_source.port));
+  switch (ntohl(rx_message->data.ntp_source.type)) {
+    case REQ_ADDSRC_SERVER:
+      type = NTP_SERVER;
+      pool = 0;
+      break;
+    case REQ_ADDSRC_PEER:
+      type = NTP_PEER;
+      pool = 0;
+      break;
+    case REQ_ADDSRC_POOL:
+      type = NTP_SERVER;
+      pool = 1;
+      break;
+    default:
+      tx_message->status = htons(STT_INVALID);
+      return;
+  }
+
+  name = (char *)rx_message->data.ntp_source.name;
+
+  /* Make sure the name is terminated */
+  if (name[sizeof (rx_message->data.ntp_source.name) - 1] != '\0') {
+      tx_message->status = htons(STT_INVALIDNAME);
+      return;
+  }
+
+  port = ntohl(rx_message->data.ntp_source.port);
   params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
   params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
   params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
@@ -787,7 +748,10 @@
   params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
   params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
   params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
+  params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
   params.authkey = ntohl(rx_message->data.ntp_source.authkey);
+  params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
+  params.cert_set = ntohl(rx_message->data.ntp_source.cert_set);
   params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
   params.max_delay_ratio =
     UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
@@ -797,30 +761,40 @@
   params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
   params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
 
-  params.online  = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
+  params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
+                        SRC_ONLINE : SRC_OFFLINE;
   params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
   params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
   params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
   params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
+  params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
+  params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
+  params.ext_fields =
+    ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
   params.sel_options =
     (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
     (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
     (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
     (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
 
-  status = NSR_AddSource(&rem_addr, type, &params);
+  status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
   switch (status) {
     case NSR_Success:
       break;
+    case NSR_UnresolvedName:
+      /* Try to resolve the name now */
+      NSR_ResolveSources();
+      break;
     case NSR_AlreadyInUse:
       tx_message->status = htons(STT_SOURCEALREADYKNOWN);
       break;
     case NSR_TooManySources:
       tx_message->status = htons(STT_TOOMANYSOURCES);
       break;
-    case NSR_InvalidAF:
-      tx_message->status = htons(STT_INVALIDAF);
+    case NSR_InvalidName:
+      tx_message->status = htons(STT_INVALIDNAME);
       break;
+    case NSR_InvalidAF:
     case NSR_NoSuchSource:
       assert(0);
       break;
@@ -832,13 +806,12 @@
 static void
 handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
 {
-  NTP_Remote_Address rem_addr;
   NSR_Status status;
+  IPAddr ip_addr;
   
-  UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr);
-  rem_addr.port = 0;
+  UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &ip_addr);
   
-  status = NSR_RemoveSource(&rem_addr);
+  status = NSR_RemoveSource(&ip_addr);
   switch (status) {
     case NSR_Success:
       break;
@@ -848,6 +821,8 @@
     case NSR_TooManySources:
     case NSR_AlreadyInUse:
     case NSR_InvalidAF:
+    case NSR_InvalidName:
+    case NSR_UnresolvedName:
       assert(0);
       break;
   }
@@ -886,13 +861,14 @@
 static void
 handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
 {
-  long sec, usec;
   double doffset;
-  sec = (int32_t)ntohl(rx_message->data.doffset.sec);
-  usec = (int32_t)ntohl(rx_message->data.doffset.usec);
-  doffset = (double) sec + 1.0e-6 * (double) usec;
-  LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
-  LCL_AccumulateOffset(doffset, 0.0);
+
+  doffset = UTI_FloatNetworkToHost(rx_message->data.doffset.doffset);
+  if (!LCL_AccumulateOffset(doffset, 0.0)) {
+    tx_message->status = htons(STT_FAILED);
+  } else {
+    LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
+  }
 }
 
 /* ================================================== */
@@ -1050,7 +1026,7 @@
   RPT_ClientAccessByIndex_Report report;
   RPY_ClientAccesses_Client *client;
   int n_indices;
-  uint32_t i, j, req_first_index, req_n_clients;
+  uint32_t i, j, req_first_index, req_n_clients, req_min_hits, req_reset;
   struct timespec now;
 
   SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1059,6 +1035,8 @@
   req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients);
   if (req_n_clients > MAX_CLIENT_ACCESSES)
     req_n_clients = MAX_CLIENT_ACCESSES;
+  req_min_hits = ntohl(rx_message->data.client_accesses_by_index.min_hits);
+  req_reset = ntohl(rx_message->data.client_accesses_by_index.reset);
 
   n_indices = CLG_GetNumberOfIndices();
   if (n_indices < 0) {
@@ -1066,24 +1044,28 @@
     return;
   }
 
-  tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
+  tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX3);
   tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
 
   for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
-    if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
+    if (!CLG_GetClientAccessReportByIndex(i, req_reset, req_min_hits, &report, &now))
       continue;
 
     client = &tx_message->data.client_accesses_by_index.clients[j++];
 
     UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
     client->ntp_hits = htonl(report.ntp_hits);
+    client->nke_hits = htonl(report.nke_hits);
     client->cmd_hits = htonl(report.cmd_hits);
     client->ntp_drops = htonl(report.ntp_drops);
+    client->nke_drops = htonl(report.nke_drops);
     client->cmd_drops = htonl(report.cmd_drops);
     client->ntp_interval = report.ntp_interval;
+    client->nke_interval = report.nke_interval;
     client->cmd_interval = report.cmd_interval;
     client->ntp_timeout_interval = report.ntp_timeout_interval;
     client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
+    client->last_nke_hit_ago = htonl(report.last_nke_hit_ago);
     client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
   }
 
@@ -1185,12 +1167,18 @@
   RPT_ServerStatsReport report;
 
   CLG_GetServerStatsReport(&report);
-  tx_message->reply = htons(RPY_SERVER_STATS);
+  tx_message->reply = htons(RPY_SERVER_STATS3);
   tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
+  tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
   tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
   tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
+  tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
   tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
   tx_message->data.server_stats.log_drops = htonl(report.log_drops);
+  tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
+  tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
+  tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
+  tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
 }
 
 /* ================================================== */
@@ -1247,97 +1235,216 @@
 }
 
 /* ================================================== */
+
+static void
+handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+  IPAddr addr;
+  char *name;
+
+  UTI_IPNetworkToHost(&rx_message->data.ntp_source_name.ip_addr, &addr);
+  name = NSR_GetName(&addr);
+
+  if (!name) {
+    tx_message->status = htons(STT_NOSUCHSOURCE);
+    return;
+  }
+
+  tx_message->reply = htons(RPY_NTP_SOURCE_NAME);
+
+  /* Avoid compiler warning */
+  if (strlen(name) >= sizeof (tx_message->data.ntp_source_name.name))
+    memcpy(tx_message->data.ntp_source_name.name, name,
+           sizeof (tx_message->data.ntp_source_name.name));
+  else
+    strncpy((char *)tx_message->data.ntp_source_name.name, name,
+            sizeof (tx_message->data.ntp_source_name.name));
+}
+
+/* ================================================== */
+
+static void
+handle_reload_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+  CNF_ReloadSources();
+}
+
+/* ================================================== */
+
+static void
+handle_reset_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+  struct timespec cooked_now, now;
+
+  SRC_ResetSources();
+  SCH_GetLastEventTime(&cooked_now, NULL, &now);
+  LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0);
+}
+
+/* ================================================== */
+
+static void
+handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+  RPT_AuthReport report;
+  IPAddr ip_addr;
+
+  UTI_IPNetworkToHost(&rx_message->data.auth_data.ip_addr, &ip_addr);
+
+  if (!NSR_GetAuthReport(&ip_addr, &report)) {
+    tx_message->status = htons(STT_NOSUCHSOURCE);
+    return;
+  }
+
+  tx_message->reply = htons(RPY_AUTH_DATA);
+
+  switch (report.mode) {
+    case NTP_AUTH_NONE:
+      tx_message->data.auth_data.mode = htons(RPY_AD_MD_NONE);
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      tx_message->data.auth_data.mode = htons(RPY_AD_MD_SYMMETRIC);
+      break;
+    case NTP_AUTH_NTS:
+      tx_message->data.auth_data.mode = htons(RPY_AD_MD_NTS);
+      break;
+    default:
+      break;
+  }
+
+  tx_message->data.auth_data.key_type = htons(report.key_type);
+  tx_message->data.auth_data.key_id = htonl(report.key_id);
+  tx_message->data.auth_data.key_length = htons(report.key_length);
+  tx_message->data.auth_data.ke_attempts = htons(report.ke_attempts);
+  tx_message->data.auth_data.last_ke_ago = htonl(report.last_ke_ago);
+  tx_message->data.auth_data.cookies = htons(report.cookies);
+  tx_message->data.auth_data.cookie_length = htons(report.cookie_length);
+  tx_message->data.auth_data.nak = htons(report.nak);
+}
+
+/* ================================================== */
+
+static uint16_t
+convert_select_options(int options)
+{
+  return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
+         (options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
+         (options & SRC_SELECT_TRUST ? RPY_SD_OPTION_TRUST : 0) |
+         (options & SRC_SELECT_REQUIRE ? RPY_SD_OPTION_REQUIRE : 0);
+}
+
+/* ================================================== */
+
+static void
+handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+  RPT_SelectReport report;
+
+  if (!SRC_GetSelectReport(ntohl(rx_message->data.select_data.index), &report)) {
+    tx_message->status = htons(STT_NOSUCHSOURCE);
+    return;
+  }
+
+  tx_message->reply = htons(RPY_SELECT_DATA);
+
+  tx_message->data.select_data.ref_id = htonl(report.ref_id);
+  UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr);
+  tx_message->data.select_data.state_char = report.state_char;
+  tx_message->data.select_data.authentication = report.authentication;
+  tx_message->data.select_data.leap = report.leap;
+  tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
+  tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
+  tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
+  tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
+  tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
+  tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
+}
+
+/* ================================================== */
 /* Read a packet and process it */
 
 static void
 read_from_cmd_socket(int sock_fd, int event, void *anything)
 {
+  SCK_Message *sck_message;
   CMD_Request rx_message;
   CMD_Reply tx_message;
-  int status, read_length, expected_length, rx_message_length;
+  IPAddr loopback_addr, remote_ip;
+  int read_length, expected_length;
   int localhost, allowed, log_index;
-  union sockaddr_all where_from;
-  socklen_t from_length;
-  IPAddr remote_ip;
-  unsigned short remote_port, rx_command;
+  uint16_t rx_command;
   struct timespec now, cooked_now;
 
-  rx_message_length = sizeof(rx_message);
-  from_length = sizeof(where_from);
-
-  status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0,
-                    &where_from.sa, &from_length);
-
-  if (status < 0) {
-    LOG(LOGS_WARN, "Error [%s] reading from control socket %d",
-        strerror(errno), sock_fd);
+  sck_message = SCK_ReceiveMessage(sock_fd, 0);
+  if (!sck_message)
     return;
-  }
 
-  if (from_length > sizeof (where_from) ||
-      from_length <= sizeof (where_from.sa.sa_family)) {
-    DEBUG_LOG("Read command packet without source address");
-    return;
-  }
-
-  read_length = status;
+  read_length = sck_message->length;
 
   /* Get current time cheaply */
   SCH_GetLastEventTime(&cooked_now, NULL, &now);
 
-  UTI_SockaddrToIPAndPort(&where_from.sa, &remote_ip, &remote_port);
+  /* Check if it's from localhost (127.0.0.1, ::1, or Unix domain),
+     or an authorised address */
+  switch (sck_message->addr_type) {
+    case SCK_ADDR_IP:
+      assert(sock_fd == sock_fd4 || sock_fd == sock_fd6);
+      remote_ip = sck_message->remote_addr.ip.ip_addr;
+      SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
+      localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
 
-  /* Check if it's from localhost (127.0.0.1, ::1, or Unix domain) */
-  switch (remote_ip.family) {
-    case IPADDR_INET4:
-      assert(sock_fd == sock_fd4);
-      localhost = remote_ip.addr.in4 == INADDR_LOOPBACK;
-      break;
-#ifdef FEAT_IPV6
-    case IPADDR_INET6:
-      assert(sock_fd == sock_fd6);
-      localhost = !memcmp(remote_ip.addr.in6, &in6addr_loopback,
-                          sizeof (in6addr_loopback));
-      break;
-#endif
-    case IPADDR_UNSPEC:
-      /* This should be the Unix domain socket */
-      if (where_from.sa.sa_family != AF_UNIX)
+      if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
+        DEBUG_LOG("Unauthorised host %s",
+                  UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
         return;
+      }
+
+      assert(remote_ip.family != IPADDR_UNSPEC);
+
+      break;
+    case SCK_ADDR_UNIX:
       assert(sock_fd == sock_fdu);
+      remote_ip.family = IPADDR_UNSPEC;
       localhost = 1;
       break;
     default:
-      assert(0);
-  }
-
-  DEBUG_LOG("Received %d bytes from %s fd %d",
-            status, UTI_SockaddrToString(&where_from.sa), sock_fd);
-
-  if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) {
-    /* The client is not allowed access, so don't waste any more time
-       on him.  Note that localhost is always allowed access
-       regardless of the defined access rules - otherwise, we could
-       shut ourselves out completely! */
-    return;
+      DEBUG_LOG("Unexpected address type");
+      return;
   }
 
   if (read_length < offsetof(CMD_Request, data) ||
       read_length < offsetof(CMD_Reply, data) ||
-      rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
-      rx_message.res1 != 0 ||
-      rx_message.res2 != 0) {
-
+      read_length > sizeof (CMD_Request)) {
     /* We don't know how to process anything like this or an error reply
        would be larger than the request */
+    DEBUG_LOG("Unexpected length");
+    return;
+  }
+
+  memcpy(&rx_message, sck_message->data, read_length);
+
+  if (rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
+      rx_message.res1 != 0 ||
+      rx_message.res2 != 0) {
     DEBUG_LOG("Command packet dropped");
     return;
   }
 
+  log_index = CLG_LogServiceAccess(CLG_CMDMON, &remote_ip, &cooked_now);
+
+  /* Don't reply to all requests from hosts other than localhost if the rate
+     is excessive */
+  if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
+      DEBUG_LOG("Command packet discarded to limit response rate");
+      return;
+  }
+
   expected_length = PKL_CommandLength(&rx_message);
   rx_command = ntohs(rx_message.command);
 
   memset(&tx_message, 0, sizeof (tx_message));
+  sck_message->data = &tx_message;
+  sck_message->length = 0;
 
   tx_message.version = PROTO_VERSION_NUMBER;
   tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
@@ -1352,7 +1459,7 @@
 
     if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
       tx_message.status = htons(STT_BADPKTVERSION);
-      transmit_reply(&tx_message, &where_from);
+      transmit_reply(sock_fd, read_length, sck_message);
     }
     return;
   }
@@ -1362,7 +1469,7 @@
     DEBUG_LOG("Command packet has invalid command %d", rx_command);
 
     tx_message.status = htons(STT_INVALID);
-    transmit_reply(&tx_message, &where_from);
+    transmit_reply(sock_fd, read_length, sck_message);
     return;
   }
 
@@ -1371,21 +1478,12 @@
               expected_length);
 
     tx_message.status = htons(STT_BADPKTLENGTH);
-    transmit_reply(&tx_message, &where_from);
+    transmit_reply(sock_fd, read_length, sck_message);
     return;
   }
 
   /* OK, we have a valid message.  Now dispatch on message type and process it. */
 
-  log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now);
-
-  /* Don't reply to all requests from hosts other than localhost if the rate
-     is excessive */
-  if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) {
-      DEBUG_LOG("Command packet discarded to limit response rate");
-      return;
-  }
-
   if (rx_command >= N_REQUEST_TYPES) {
     /* This should be already handled */
     assert(0);
@@ -1393,7 +1491,7 @@
     /* Check level of authority required to issue the command.  All commands
        from the Unix domain socket (which is accessible only by the root and
        chrony user/group) are allowed. */
-    if (where_from.sa.sa_family == AF_UNIX) {
+    if (remote_ip.family == IPADDR_UNSPEC) {
       assert(sock_fd == sock_fdu);
       allowed = 1;
     } else {
@@ -1532,12 +1630,8 @@
           handle_cmdaccheck(&rx_message, &tx_message);
           break;
 
-        case REQ_ADD_SERVER3:
-          handle_add_source(NTP_SERVER, &rx_message, &tx_message);
-          break;
-
-        case REQ_ADD_PEER3:
-          handle_add_source(NTP_PEER, &rx_message, &tx_message);
+        case REQ_ADD_SOURCE:
+          handle_add_source(&rx_message, &tx_message);
           break;
 
         case REQ_DEL_SOURCE:
@@ -1552,7 +1646,7 @@
           handle_dfreq(&rx_message, &tx_message);
           break;
 
-        case REQ_DOFFSET:
+        case REQ_DOFFSET2:
           handle_doffset(&rx_message, &tx_message);
           break;
 
@@ -1584,7 +1678,7 @@
           handle_cyclelogs(&rx_message, &tx_message);
           break;
 
-        case REQ_CLIENT_ACCESSES_BY_INDEX2:
+        case REQ_CLIENT_ACCESSES_BY_INDEX3:
           handle_client_accesses_by_index(&rx_message, &tx_message);
           break;
 
@@ -1636,6 +1730,30 @@
           handle_shutdown(&rx_message, &tx_message);
           break;
 
+        case REQ_ONOFFLINE:
+          handle_onoffline(&rx_message, &tx_message);
+          break;
+
+        case REQ_NTP_SOURCE_NAME:
+          handle_ntp_source_name(&rx_message, &tx_message);
+          break;
+
+        case REQ_RESET_SOURCES:
+          handle_reset_sources(&rx_message, &tx_message);
+          break;
+
+        case REQ_AUTH_DATA:
+          handle_auth_data(&rx_message, &tx_message);
+          break;
+
+        case REQ_SELECT_DATA:
+          handle_select_data(&rx_message, &tx_message);
+          break;
+
+        case REQ_RELOAD_SOURCES:
+          handle_reload_sources(&rx_message, &tx_message);
+          break;
+
         default:
           DEBUG_LOG("Unhandled command %d", rx_command);
           tx_message.status = htons(STT_FAILED);
@@ -1647,19 +1765,7 @@
   }
 
   /* Transmit the response */
-  {
-    /* Include a simple way to lose one message in three to test resend */
-
-    static int do_it=1;
-
-    if (do_it) {
-      transmit_reply(&tx_message, &where_from);
-    }
-
-#if 0
-    do_it = ((do_it + 1) % 3);
-#endif
-  }
+  transmit_reply(sock_fd, read_length, sck_message);
 }
 
 /* ================================================== */
diff --git a/chrony_3_3/cmdmon.h b/chrony/cmdmon.h
similarity index 96%
rename from chrony_3_3/cmdmon.h
rename to chrony/cmdmon.h
index 5b717d2..86356b9 100644
--- a/chrony_3_3/cmdmon.h
+++ b/chrony/cmdmon.h
@@ -29,7 +29,7 @@
 
 #include "addressing.h"
 
-extern void CAM_Initialise(int family);
+extern void CAM_Initialise(void);
 
 extern void CAM_Finalise(void);
 
diff --git a/chrony_3_3/cmdparse.c b/chrony/cmdparse.c
similarity index 71%
rename from chrony_3_3/cmdparse.c
rename to chrony/cmdparse.c
index a228530..d8ab61e 100644
--- a/chrony_3_3/cmdparse.c
+++ b/chrony/cmdparse.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2013-2014, 2016
+ * Copyright (C) Miroslav Lichvar  2013-2014, 2016, 2021
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -43,12 +43,13 @@
 CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
 {
   char *hostname, *cmd;
+  uint32_t ef_type;
   int n;
   
   src->port = SRC_DEFAULT_PORT;
   src->params.minpoll = SRC_DEFAULT_MINPOLL;
   src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
-  src->params.online = 1;
+  src->params.connectivity = SRC_ONLINE;
   src->params.auto_offline = 0;
   src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
   src->params.burst = 0;
@@ -59,9 +60,15 @@
   src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
   src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
   src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
+  src->params.filter_length = 0;
   src->params.interleaved = 0;
   src->params.sel_options = 0;
+  src->params.nts = 0;
+  src->params.nts_port = SRC_DEFAULT_NTSPORT;
+  src->params.copy = 0;
+  src->params.ext_fields = 0;
   src->params.authkey = INACTIVE_AUTHKEY;
+  src->params.cert_set = SRC_DEFAULT_CERTSET;
   src->params.max_delay = SRC_DEFAULT_MAXDELAY;
   src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
   src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
@@ -87,10 +94,12 @@
       src->params.auto_offline = 1;
     } else if (!strcasecmp(cmd, "burst")) {
       src->params.burst = 1;
+    } else if (!strcasecmp(cmd, "copy")) {
+      src->params.copy = 1;
     } else if (!strcasecmp(cmd, "iburst")) {
       src->params.iburst = 1;
     } else if (!strcasecmp(cmd, "offline")) {
-      src->params.online = 0;
+      src->params.connectivity = SRC_OFFLINE;
     } else if (!strcasecmp(cmd, "noselect")) {
       src->params.sel_options |= SRC_SELECT_NOSELECT;
     } else if (!strcasecmp(cmd, "prefer")) {
@@ -99,6 +108,9 @@
       src->params.sel_options |= SRC_SELECT_REQUIRE;
     } else if (!strcasecmp(cmd, "trust")) {
       src->params.sel_options |= SRC_SELECT_TRUST;
+    } else if (!strcasecmp(cmd, "certset")) {
+      if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
+        return 0;
     } else if (!strcasecmp(cmd, "key")) {
       if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
           src->params.authkey == INACTIVE_AUTHKEY)
@@ -106,6 +118,19 @@
     } else if (!strcasecmp(cmd, "asymmetry")) {
       if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
         return 0;
+    } else if (!strcasecmp(cmd, "extfield")) {
+      if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
+        return 0;
+      switch (ef_type) {
+        case NTP_EF_EXP1:
+          src->params.ext_fields |= NTP_EF_FLAG_EXP1;
+          break;
+        default:
+          return 0;
+      }
+    } else if (!strcasecmp(cmd, "filter")) {
+      if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
+        return 0;
     } else if (!strcasecmp(cmd, "maxdelay")) {
       if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
         return 0;
@@ -136,11 +161,16 @@
     } else if (!strcasecmp(cmd, "minstratum")) {
       if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
         return 0;
+    } else if (!strcasecmp(cmd, "nts")) {
+      src->params.nts = 1;
+    } else if (!strcasecmp(cmd, "ntsport")) {
+      if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
+        return 0;
     } else if (!strcasecmp(cmd, "offset")) {
       if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
         return 0;
     } else if (!strcasecmp(cmd, "port")) {
-      if (sscanf(line, "%hu%n", &src->port, &n) != 1)
+      if (sscanf(line, "%d%n", &src->port, &n) != 1)
         return 0;
     } else if (!strcasecmp(cmd, "polltarget")) {
       if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
@@ -164,6 +194,85 @@
 /* ================================================== */
 
 int
+CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
+{
+  char *p, *net, *slash;
+  uint32_t a, b, c;
+  int bits, len, n;
+
+  p = CPS_SplitWord(line);
+
+  if (strcmp(line, "all") == 0) {
+    *all = 1;
+    net = p;
+    p = CPS_SplitWord(p);
+  } else {
+    *all = 0;
+    net = line;
+  }
+
+  /* Make sure there are no other arguments */
+  if (*p)
+    return 0;
+
+  /* No specified address or network means all IPv4 and IPv6 addresses */
+  if (!*net) {
+    ip->family = IPADDR_UNSPEC;
+    *subnet_bits = 0;
+    return 1;
+  }
+
+  slash = strchr(net, '/');
+  if (slash) {
+    if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0)
+      return 0;
+    *slash = '\0';
+  } else {
+    bits = -1;
+  }
+
+  if (UTI_StringToIP(net, ip)) {
+    if (bits >= 0)
+      *subnet_bits = bits;
+    else
+      *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
+    return 1;
+  }
+
+  /* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal
+     numbers.  This is different than the numbers-and-dots notation accepted
+     by inet_aton()! */
+
+  a = b = c = 0;
+  n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len);
+
+  if (n > 0 && !net[len]) {
+    if (a > 255 || b > 255 || c > 255)
+      return 0;
+
+    ip->family = IPADDR_INET4;
+    ip->addr.in4 = (a << 24) | (b << 16) | (c << 8);
+
+    if (bits >= 0)
+      *subnet_bits = bits;
+    else
+      *subnet_bits = n * 8;
+
+    return 1;
+  }
+
+  /* The last possibility is a hostname */
+  if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) {
+    *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
+    return 1;
+  }
+
+  return 0;
+}
+
+/* ================================================== */
+
+int
 CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
 {
   int n;
@@ -257,7 +366,7 @@
 /* ================================================== */
 
 int
-CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
+CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
 {
   char *s1, *s2, *s3, *s4;
 
@@ -274,10 +383,10 @@
     return 0;
 
   if (*s3) {
-    *hash = s2;
+    *type = s2;
     *key = s3;
   } else {
-    *hash = "MD5";
+    *type = "MD5";
     *key = s2;
   }
 
diff --git a/chrony_3_3/cmdparse.h b/chrony/cmdparse.h
similarity index 88%
rename from chrony_3_3/cmdparse.h
rename to chrony/cmdparse.h
index 19f4bb7..fd1eb43 100644
--- a/chrony_3_3/cmdparse.h
+++ b/chrony/cmdparse.h
@@ -32,13 +32,16 @@
 
 typedef struct {
   char *name;
-  unsigned short port;
+  int port;
   SourceParameters params;
 } CPS_NTP_Source;
 
 /* Parse a command to add an NTP server or peer */
 extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
   
+/* Parse a command to allow/deny access */
+extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
+
 /* Parse a command to enable local reference */
 extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
 
@@ -49,6 +52,6 @@
 extern char *CPS_SplitWord(char *line);
 
 /* Parse a key from keyfile */
-extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key);
+extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
 
 #endif /* GOT_CMDPARSE_H */
diff --git a/chrony_3_3/conf.c b/chrony/conf.c
similarity index 69%
rename from chrony_3_3/conf.c
rename to chrony/conf.c
index 3859d74..06ea1e5 100644
--- a/chrony_3_3/conf.c
+++ b/chrony/conf.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2009-2017
+ * Copyright (C) Miroslav Lichvar  2009-2017, 2020
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -33,8 +33,10 @@
 #include "conf.h"
 #include "ntp_sources.h"
 #include "ntp_core.h"
+#include "nts_ke.h"
 #include "refclock.h"
 #include "cmdmon.h"
+#include "socket.h"
 #include "srcparams.h"
 #include "logging.h"
 #include "nameserv.h"
@@ -43,6 +45,12 @@
 #include "util.h"
 
 /* ================================================== */
+
+#define MAX_LINE_LENGTH 2048
+#define MAX_CONF_DIRS 10
+#define MAX_INCLUDE_LEVEL 10
+
+/* ================================================== */
 /* Forward prototypes */
 
 static int parse_string(char *line, char **result);
@@ -51,11 +59,13 @@
 static int parse_null(char *line);
 
 static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
+static void parse_authselectmode(char *);
 static void parse_bindacqaddress(char *);
 static void parse_bindaddress(char *);
 static void parse_bindcmdaddress(char *);
 static void parse_broadcast(char *);
 static void parse_clientloglimit(char *);
+static void parse_confdir(char *);
 static void parse_fallbackdrift(char *);
 static void parse_hwtimestamp(char *);
 static void parse_include(char *);
@@ -66,16 +76,20 @@
 static void parse_mailonchange(char *);
 static void parse_makestep(char *);
 static void parse_maxchange(char *);
+static void parse_ntsserver(char *, ARR_Instance files);
+static void parse_ntstrustedcerts(char *);
 static void parse_ratelimit(char *line, int *enabled, int *interval,
                             int *burst, int *leak);
 static void parse_refclock(char *);
 static void parse_smoothtime(char *);
-static void parse_source(char *line, NTP_Source_Type type, int pool);
+static void parse_source(char *line, char *type, int fatal);
+static void parse_sourcedir(char *);
 static void parse_tempcomp(char *);
 
 /* ================================================== */
 /* Configuration variables */
 
+static int print_config = 0;
 static int restarted = 0;
 static char *rtc_device;
 static int acquisition_port = -1;
@@ -88,7 +102,9 @@
 static double max_clock_error = 1.0; /* in ppm */
 static double max_drift = 500000.0; /* in ppm */
 static double max_slew_rate = 1e6 / 12.0; /* in ppm */
+static double clock_precision = 0.0; /* in seconds */
 
+static SRC_AuthSelectMode authselect_mode = SRC_AUTHSELECT_MIX;
 static double max_distance = 3.0;
 static double max_jitter = 1.0;
 static double reselect_distance = 1e-4;
@@ -105,8 +121,8 @@
 static int do_log_refclocks = 0;
 static int do_log_tempcomp = 0;
 static int log_banner = 32;
-static char *logdir;
-static char *dumpdir;
+static char *logdir = NULL;
+static char *dumpdir = NULL;
 
 static int enable_local=0;
 static int local_stratum;
@@ -180,21 +196,33 @@
    the loopback address will be used */
 static IPAddr bind_cmd_address4, bind_cmd_address6;
 
+/* Interface names to bind the NTP server, NTP client, and command socket */
+static char *bind_ntp_iface = NULL;
+static char *bind_acq_iface = NULL;
+static char *bind_cmd_iface = NULL;
+
 /* Path to the Unix domain command socket. */
-static char *bind_cmd_path;
+static char *bind_cmd_path = NULL;
+
+/* Differentiated Services Code Point (DSCP) in transmitted NTP packets */
+static int ntp_dscp = 0;
 
 /* Path to Samba (ntp_signd) socket. */
 static char *ntp_signd_socket = NULL;
 
 /* Filename to use for storing pid of running chronyd, to prevent multiple
  * chronyds being started. */
-static char *pidfile;
+static char *pidfile = NULL;
 
 /* Rate limiting parameters */
 static int ntp_ratelimit_enabled = 0;
 static int ntp_ratelimit_interval = 3;
 static int ntp_ratelimit_burst = 8;
 static int ntp_ratelimit_leak = 2;
+static int nts_ratelimit_enabled = 0;
+static int nts_ratelimit_interval = 6;
+static int nts_ratelimit_burst = 8;
+static int nts_ratelimit_leak = 2;
 static int cmd_ratelimit_enabled = 0;
 static int cmd_ratelimit_interval = -4;
 static int cmd_ratelimit_burst = 8;
@@ -223,9 +251,31 @@
 /* Name of the user to which will be dropped root privileges. */
 static char *user;
 
+/* NTS server and client configuration */
+static char *nts_dump_dir = NULL;
+static char *nts_ntp_server = NULL;
+static ARR_Instance nts_server_cert_files; /* array of (char *) */
+static ARR_Instance nts_server_key_files; /* array of (char *) */
+static int nts_server_port = NKE_PORT;
+static int nts_server_processes = 1;
+static int nts_server_connections = 100;
+static int nts_refresh = 2419200; /* 4 weeks */
+static int nts_rotate = 604800; /* 1 week */
+static ARR_Instance nts_trusted_certs_paths; /* array of (char *) */
+static ARR_Instance nts_trusted_certs_ids; /* array of uint32_t */
+
+/* Number of clock updates needed to enable certificate time checks */
+static int no_cert_time_check = 0;
+
+/* Flag disabling use of system trusted certificates */
+static int no_system_cert = 0;
+
 /* Array of CNF_HwTsInterface */
 static ARR_Instance hwts_interfaces;
 
+/* PTP event port (disabled by default) */
+static int ptp_port = 0;
+
 typedef struct {
   NTP_Source_Type type;
   int pool;
@@ -234,6 +284,10 @@
 
 /* Array of NTP_Source */
 static ARR_Instance ntp_sources;
+/* Array of (char *) */
+static ARR_Instance ntp_source_dirs;
+/* Array of uint32_t corresponding to ntp_sources (for sourcedirs reload) */
+static ARR_Instance ntp_source_ids;
 
 /* Array of RefclockParameters */
 static ARR_Instance refclock_sources;
@@ -250,8 +304,7 @@
 static ARR_Instance cmd_restrictions;
 
 typedef struct {
-  IPAddr addr;
-  unsigned short port;
+  NTP_Remote_Address addr;
   int interval;
 } NTP_Broadcast_Destination;
 
@@ -265,6 +318,8 @@
 static const char *processed_file;
 static const char *processed_command;
 
+static int include_level = 0;
+
 /* ================================================== */
 
 static void
@@ -331,26 +386,36 @@
 
   init_sources = ARR_CreateInstance(sizeof (IPAddr));
   ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
+  ntp_source_dirs = ARR_CreateInstance(sizeof (char *));
+  ntp_source_ids = ARR_CreateInstance(sizeof (uint32_t));
   refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
   broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination));
 
   ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
   cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
 
-  dumpdir = Strdup("");
-  logdir = Strdup("");
+  nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
+  nts_server_key_files = ARR_CreateInstance(sizeof (char *));
+  nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
+  nts_trusted_certs_ids = ARR_CreateInstance(sizeof (uint32_t));
+
   rtc_device = Strdup(DEFAULT_RTC_DEVICE);
   hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
   user = Strdup(DEFAULT_USER);
 
   if (client_only) {
     cmd_port = ntp_port = 0;
-    bind_cmd_path = Strdup("");
-    pidfile = Strdup("");
   } else {
     bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
     pidfile = Strdup(DEFAULT_PID_FILE);
   }
+
+  SCK_GetAnyLocalIPAddress(IPADDR_INET4, &bind_address4);
+  SCK_GetAnyLocalIPAddress(IPADDR_INET6, &bind_address6);
+  SCK_GetAnyLocalIPAddress(IPADDR_INET4, &bind_acq_address4);
+  SCK_GetAnyLocalIPAddress(IPADDR_INET6, &bind_acq_address6);
+  SCK_GetLoopbackIPAddress(IPADDR_INET4, &bind_cmd_address4);
+  SCK_GetLoopbackIPAddress(IPADDR_INET6, &bind_cmd_address6);
 }
 
 /* ================================================== */
@@ -366,21 +431,43 @@
 
   for (i = 0; i < ARR_GetSize(ntp_sources); i++)
     Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name);
+  for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++)
+    Free(*(char **)ARR_GetElement(ntp_source_dirs, i));
+  for (i = 0; i < ARR_GetSize(refclock_sources); i++) {
+    Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_name);
+    Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_parameter);
+  }
+  for (i = 0; i < ARR_GetSize(nts_server_cert_files); i++)
+    Free(*(char **)ARR_GetElement(nts_server_cert_files, i));
+  for (i = 0; i < ARR_GetSize(nts_server_key_files); i++)
+    Free(*(char **)ARR_GetElement(nts_server_key_files, i));
+  for (i = 0; i < ARR_GetSize(nts_trusted_certs_paths); i++)
+    Free(*(char **)ARR_GetElement(nts_trusted_certs_paths, i));
 
   ARR_DestroyInstance(init_sources);
   ARR_DestroyInstance(ntp_sources);
+  ARR_DestroyInstance(ntp_source_dirs);
+  ARR_DestroyInstance(ntp_source_ids);
   ARR_DestroyInstance(refclock_sources);
   ARR_DestroyInstance(broadcasts);
 
   ARR_DestroyInstance(ntp_restrictions);
   ARR_DestroyInstance(cmd_restrictions);
 
+  ARR_DestroyInstance(nts_server_cert_files);
+  ARR_DestroyInstance(nts_server_key_files);
+  ARR_DestroyInstance(nts_trusted_certs_paths);
+  ARR_DestroyInstance(nts_trusted_certs_ids);
+
   Free(drift_file);
   Free(dumpdir);
   Free(hwclock_file);
   Free(keys_file);
   Free(leapsec_tz);
   Free(logdir);
+  Free(bind_ntp_iface);
+  Free(bind_acq_iface);
+  Free(bind_cmd_iface);
   Free(bind_cmd_path);
   Free(ntp_signd_socket);
   Free(pidfile);
@@ -390,6 +477,16 @@
   Free(mail_user_on_change);
   Free(tempcomp_sensor_file);
   Free(tempcomp_point_file);
+  Free(nts_dump_dir);
+  Free(nts_ntp_server);
+}
+
+/* ================================================== */
+
+void
+CNF_EnablePrint(void)
+{
+  print_config = 1;
 }
 
 /* ================================================== */
@@ -399,23 +496,22 @@
 CNF_ReadFile(const char *filename)
 {
   FILE *in;
-  char line[2048];
+  char line[MAX_LINE_LENGTH + 1];
   int i;
 
-  in = fopen(filename, "r");
-  if (!in) {
-    LOG_FATAL("Could not open configuration file %s : %s",
-              filename, strerror(errno));
-    return;
-  }
+  include_level++;
+  if (include_level > MAX_INCLUDE_LEVEL)
+    LOG_FATAL("Maximum include level reached");
 
-  DEBUG_LOG("Reading %s", filename);
+  in = UTI_OpenFile(NULL, filename, NULL, 'R', 0);
 
   for (i = 1; fgets(line, sizeof(line), in); i++) {
     CNF_ParseLine(filename, i, line);
   }
 
   fclose(in);
+
+  include_level--;
 }
 
 /* ================================================== */
@@ -430,31 +526,50 @@
   processed_file = filename;
   line_number = number;
 
+  /* Detect truncated line */
+  if (strlen(line) >= MAX_LINE_LENGTH)
+    other_parse_error("String too long");
+
   /* Remove extra white-space and comments */
   CPS_NormalizeLine(line);
 
   /* Skip blank lines */
-  if (!*line)
+  if (!*line) {
+    processed_file = NULL;
     return;
+  }
 
   /* We have a real line, now try to match commands */
   processed_command = command = line;
   p = CPS_SplitWord(line);
 
+  if (print_config && strcasecmp(command, "include") && strcasecmp(command, "confdir"))
+    printf("%s%s%s\n", command, p[0] != '\0' ? " " : "", p);
+
   if (!strcasecmp(command, "acquisitionport")) {
     parse_int(p, &acquisition_port);
   } else if (!strcasecmp(command, "allow")) {
     parse_allow_deny(p, ntp_restrictions, 1);
+  } else if (!strcasecmp(command, "authselectmode")) {
+    parse_authselectmode(p);
   } else if (!strcasecmp(command, "bindacqaddress")) {
     parse_bindacqaddress(p);
+  } else if (!strcasecmp(command, "bindacqdevice")) {
+    parse_string(p, &bind_acq_iface);
   } else if (!strcasecmp(command, "bindaddress")) {
     parse_bindaddress(p);
   } else if (!strcasecmp(command, "bindcmdaddress")) {
     parse_bindcmdaddress(p);
+  } else if (!strcasecmp(command, "bindcmddevice")) {
+    parse_string(p, &bind_cmd_iface);
+  } else if (!strcasecmp(command, "binddevice")) {
+    parse_string(p, &bind_ntp_iface);
   } else if (!strcasecmp(command, "broadcast")) {
     parse_broadcast(p);
   } else if (!strcasecmp(command, "clientloglimit")) {
     parse_clientloglimit(p);
+  } else if (!strcasecmp(command, "clockprecision")) {
+    parse_double(p, &clock_precision);
   } else if (!strcasecmp(command, "cmdallow")) {
     parse_allow_deny(p, cmd_restrictions, 1);
   } else if (!strcasecmp(command, "cmddeny")) {
@@ -466,12 +581,16 @@
                     &cmd_ratelimit_burst, &cmd_ratelimit_leak);
   } else if (!strcasecmp(command, "combinelimit")) {
     parse_double(p, &combine_limit);
+  } else if (!strcasecmp(command, "confdir")) {
+    parse_confdir(p);
   } else if (!strcasecmp(command, "corrtimeratio")) {
     parse_double(p, &correction_time_ratio);
   } else if (!strcasecmp(command, "deny")) {
     parse_allow_deny(p, ntp_restrictions, 0);
   } else if (!strcasecmp(command, "driftfile")) {
     parse_string(p, &drift_file);
+  } else if (!strcasecmp(command, "dscp")) {
+    parse_int(p, &ntp_dscp);
   } else if (!strcasecmp(command, "dumpdir")) {
     parse_string(p, &dumpdir);
   } else if (!strcasecmp(command, "dumponexit")) {
@@ -520,6 +639,8 @@
     parse_double(p, &max_drift);
   } else if (!strcasecmp(command, "maxjitter")) {
     parse_double(p, &max_jitter);
+  } else if (!strcasecmp(command, "maxntsconnections")) {
+    parse_int(p, &nts_server_connections);
   } else if (!strcasecmp(command, "maxsamples")) {
     parse_int(p, &max_samples);
   } else if (!strcasecmp(command, "maxslewrate")) {
@@ -530,18 +651,46 @@
     parse_int(p, &min_samples);
   } else if (!strcasecmp(command, "minsources")) {
     parse_int(p, &min_sources);
+  } else if (!strcasecmp(command, "nocerttimecheck")) {
+    parse_int(p, &no_cert_time_check);
   } else if (!strcasecmp(command, "noclientlog")) {
     no_client_log = parse_null(p);
+  } else if (!strcasecmp(command, "nosystemcert")) {
+    no_system_cert = parse_null(p);
   } else if (!strcasecmp(command, "ntpsigndsocket")) {
     parse_string(p, &ntp_signd_socket);
+  } else if (!strcasecmp(command, "ntsratelimit")) {
+    parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
+                    &nts_ratelimit_burst, &nts_ratelimit_leak);
+  } else if (!strcasecmp(command, "ntscachedir") ||
+             !strcasecmp(command, "ntsdumpdir")) {
+    parse_string(p, &nts_dump_dir);
+  } else if (!strcasecmp(command, "ntsntpserver")) {
+    parse_string(p, &nts_ntp_server);
+  } else if (!strcasecmp(command, "ntsport")) {
+    parse_int(p, &nts_server_port);
+  } else if (!strcasecmp(command, "ntsprocesses")) {
+    parse_int(p, &nts_server_processes);
+  } else if (!strcasecmp(command, "ntsrefresh")) {
+    parse_int(p, &nts_refresh);
+  } else if (!strcasecmp(command, "ntsrotate")) {
+    parse_int(p, &nts_rotate);
+  } else if (!strcasecmp(command, "ntsservercert")) {
+    parse_ntsserver(p, nts_server_cert_files);
+  } else if (!strcasecmp(command, "ntsserverkey")) {
+    parse_ntsserver(p, nts_server_key_files);
+  } else if (!strcasecmp(command, "ntstrustedcerts")) {
+    parse_ntstrustedcerts(p);
   } else if (!strcasecmp(command, "peer")) {
-    parse_source(p, NTP_PEER, 0);
+    parse_source(p, command, 1);
   } else if (!strcasecmp(command, "pidfile")) {
     parse_string(p, &pidfile);
   } else if (!strcasecmp(command, "pool")) {
-    parse_source(p, NTP_SERVER, 1);
+    parse_source(p, command, 1);
   } else if (!strcasecmp(command, "port")) {
     parse_int(p, &ntp_port);
+  } else if (!strcasecmp(command, "ptpport")) {
+    parse_int(p, &ptp_port);
   } else if (!strcasecmp(command, "ratelimit")) {
     parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
                     &ntp_ratelimit_burst, &ntp_ratelimit_leak);
@@ -562,9 +711,11 @@
   } else if (!strcasecmp(command, "sched_priority")) {
     parse_int(p, &sched_priority);
   } else if (!strcasecmp(command, "server")) {
-    parse_source(p, NTP_SERVER, 0);
+    parse_source(p, command, 1);
   } else if (!strcasecmp(command, "smoothtime")) {
     parse_smoothtime(p);
+  } else if (!strcasecmp(command, "sourcedir")) {
+    parse_sourcedir(p);
   } else if (!strcasecmp(command, "stratumweight")) {
     parse_double(p, &stratum_weight);
   } else if (!strcasecmp(command, "tempcomp")) {
@@ -577,8 +728,10 @@
              !strcasecmp(command, "linux_hz")) {
     LOG(LOGS_WARN, "%s directive is no longer supported", command);
   } else {
-    other_parse_error("Invalid command");
+    other_parse_error("Invalid directive");
   }
+
+  processed_file = processed_command = NULL;
 }
 
 /* ================================================== */
@@ -630,15 +783,31 @@
 /* ================================================== */
 
 static void
-parse_source(char *line, NTP_Source_Type type, int pool)
+parse_source(char *line, char *type, int fatal)
 {
   NTP_Source source;
 
-  source.type = type;
-  source.pool = pool;
+  if (strcasecmp(type, "peer") == 0) {
+    source.type = NTP_PEER;
+    source.pool = 0;
+  } else if (strcasecmp(type, "pool") == 0) {
+    source.type = NTP_SERVER;
+    source.pool = 1;
+  } else if (strcasecmp(type, "server") == 0) {
+    source.type = NTP_SERVER;
+    source.pool = 0;
+  } else {
+    if (fatal)
+      command_parse_error();
+    return;
+  }
+
+  /* Avoid comparing uninitialized data in compare_sources() */
+  memset(&source.params, 0, sizeof (source.params));
 
   if (!CPS_ParseNTPSourceAdd(line, &source.params)) {
-    command_parse_error();
+    if (fatal)
+      command_parse_error();
     return;
   }
 
@@ -649,6 +818,17 @@
 /* ================================================== */
 
 static void
+parse_sourcedir(char *line)
+{
+  char *s;
+
+  s = Strdup(line);
+  ARR_AppendElement(ntp_source_dirs, &s);
+}
+
+/* ================================================== */
+
+static void
 parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
 {
   int n, val;
@@ -1000,107 +1180,77 @@
 /* ================================================== */
 
 static void
+parse_ntsserver(char *line, ARR_Instance files)
+{
+  char *file = NULL;
+
+  parse_string(line, &file);
+  ARR_AppendElement(files, &file);
+}
+
+/* ================================================== */
+
+static void
+parse_ntstrustedcerts(char *line)
+{
+  uint32_t id;
+  char *path;
+
+  if (get_number_of_args(line) == 2) {
+    path = CPS_SplitWord(line);
+    if (sscanf(line, "%"SCNu32, &id) != 1)
+      command_parse_error();
+  } else {
+    check_number_of_args(line, 1);
+    path = line;
+    id = 0;
+  }
+
+  path = Strdup(path);
+
+  ARR_AppendElement(nts_trusted_certs_paths, &path);
+  ARR_AppendElement(nts_trusted_certs_ids, &id);
+}
+
+/* ================================================== */
+
+static void
 parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
 {
-  char *p;
-  unsigned long a, b, c, d, n;
-  int all = 0;
-  AllowDeny *new_node = NULL;
-  IPAddr ip_addr;
+  int all, subnet_bits;
+  AllowDeny *node;
+  IPAddr ip;
 
-  p = line;
+  if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits))
+    command_parse_error();
 
-  if (!strncmp(p, "all", 3)) {
-    all = 1;
-    p = CPS_SplitWord(line);
-  }
-
-  if (!*p) {
-    /* Empty line applies to all addresses */
-    new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
-    new_node->allow = allow;
-    new_node->all = all;
-    new_node->ip.family = IPADDR_UNSPEC;
-    new_node->subnet_bits = 0;
-  } else {
-    char *slashpos;
-    slashpos = strchr(p, '/');
-    if (slashpos) *slashpos = 0;
-
-    check_number_of_args(p, 1);
-    n = 0;
-    if (UTI_StringToIP(p, &ip_addr) ||
-        (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) >= 1) {
-      new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
-      new_node->allow = allow;
-      new_node->all = all;
-
-      if (n == 0) {
-        new_node->ip = ip_addr;
-        if (ip_addr.family == IPADDR_INET6)
-          new_node->subnet_bits = 128;
-        else
-          new_node->subnet_bits = 32;
-      } else {
-        new_node->ip.family = IPADDR_INET4;
-
-        a &= 0xff;
-        b &= 0xff;
-        c &= 0xff;
-        d &= 0xff;
-        
-        switch (n) {
-          case 1:
-            new_node->ip.addr.in4 = (a<<24);
-            new_node->subnet_bits = 8;
-            break;
-          case 2:
-            new_node->ip.addr.in4 = (a<<24) | (b<<16);
-            new_node->subnet_bits = 16;
-            break;
-          case 3:
-            new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8);
-            new_node->subnet_bits = 24;
-            break;
-          case 4:
-            new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8) | d;
-            new_node->subnet_bits = 32;
-            break;
-          default:
-            assert(0);
-        }
-      }
-      
-      if (slashpos) {
-        int specified_subnet_bits, n;
-        n = sscanf(slashpos+1, "%d", &specified_subnet_bits);
-        if (n == 1) {
-          new_node->subnet_bits = specified_subnet_bits;
-        } else {
-          command_parse_error();
-        }
-      }
-
-    } else {
-      if (!slashpos && DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
-        new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
-        new_node->allow = allow;
-        new_node->all = all;
-        new_node->ip = ip_addr;
-        if (ip_addr.family == IPADDR_INET6)
-          new_node->subnet_bits = 128;
-        else
-          new_node->subnet_bits = 32;
-      } else {
-        command_parse_error();
-      }      
-    }
-  }
+  node = ARR_GetNewElement(restrictions);
+  node->allow = allow;
+  node->all = all;
+  node->ip = ip;
+  node->subnet_bits = subnet_bits;
 }
   
 /* ================================================== */
 
 static void
+parse_authselectmode(char *line)
+{
+  if (!strcasecmp(line, "require"))
+    authselect_mode = SRC_AUTHSELECT_REQUIRE;
+  else if (!strcasecmp(line, "prefer"))
+    authselect_mode = SRC_AUTHSELECT_PREFER;
+  else if (!strcasecmp(line, "mix"))
+    authselect_mode = SRC_AUTHSELECT_MIX;
+  else if (!strcasecmp(line, "ignore"))
+    authselect_mode = SRC_AUTHSELECT_IGNORE;
+  else
+    command_parse_error();
+}
+
+/* ================================================== */
+
+static void
 parse_bindacqaddress(char *line)
 {
   IPAddr ip;
@@ -1147,8 +1297,10 @@
   if (line[0] == '/') {
     parse_string(line, &bind_cmd_path);
     /* / disables the socket */
-    if (!strcmp(bind_cmd_path, "/"))
-        bind_cmd_path[0] = '\0';
+    if (strcmp(bind_cmd_path, "/") == 0) {
+      Free(bind_cmd_path);
+      bind_cmd_path = NULL;
+    }
   } else if (UTI_StringToIP(line, &ip)) {
     if (ip.family == IPADDR_INET4)
       bind_cmd_address4 = ip;
@@ -1201,8 +1353,8 @@
   }
 
   destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts);
-  destination->addr = ip;
-  destination->port = port;
+  destination->addr.ip_addr = ip;
+  destination->addr.port = port;
   destination->interval = interval;
 }
 
@@ -1291,6 +1443,8 @@
   iface = ARR_GetNewElement(hwts_interfaces);
   iface->name = Strdup(p);
   iface->minpoll = 0;
+  iface->min_samples = 2;
+  iface->max_samples = 16;
   iface->nocrossts = 0;
   iface->rxfilter = CNF_HWTS_RXFILTER_ANY;
   iface->precision = 100.0e-9;
@@ -1300,9 +1454,15 @@
   for (p = line; *p; line += n, p = line) {
     line = CPS_SplitWord(line);
 
-    if (!strcasecmp(p, "minpoll")) {
+    if (!strcasecmp(p, "maxsamples")) {
+      if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
+        break;
+    } else if (!strcasecmp(p, "minpoll")) {
       if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
         break;
+    } else if (!strcasecmp(p, "minsamples")) {
+      if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
+        break;
     } else if (!strcasecmp(p, "precision")) {
       if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
         break;
@@ -1319,6 +1479,8 @@
         iface->rxfilter = CNF_HWTS_RXFILTER_NONE;
       else if (!strcasecmp(filter, "ntp"))
         iface->rxfilter = CNF_HWTS_RXFILTER_NTP;
+      else if (!strcasecmp(filter, "ptp"))
+        iface->rxfilter = CNF_HWTS_RXFILTER_PTP;
       else if (!strcasecmp(filter, "all"))
         iface->rxfilter = CNF_HWTS_RXFILTER_ALL;
       else
@@ -1337,6 +1499,86 @@
 
 /* ================================================== */
 
+static const char *
+get_basename(const char *path)
+{
+  const char *b = strrchr(path, '/');
+  return b ? b + 1 : path;
+}
+
+/* ================================================== */
+
+static int
+compare_basenames(const void *a, const void *b)
+{
+  return strcmp(get_basename(*(const char * const *)a),
+                get_basename(*(const char * const *)b));
+}
+
+/* ================================================== */
+
+static int
+search_dirs(char *line, const char *suffix, void (*file_handler)(const char *path))
+{
+  char *dirs[MAX_CONF_DIRS], buf[MAX_LINE_LENGTH], *path;
+  size_t i, j, k, locations, n_dirs;
+  glob_t gl;
+
+  n_dirs = UTI_SplitString(line, dirs, MAX_CONF_DIRS);
+  if (n_dirs < 1 || n_dirs > MAX_CONF_DIRS)
+    return 0;
+
+  /* Get the paths of all config files in the specified directories */
+  for (i = 0; i < n_dirs; i++) {
+    if (snprintf(buf, sizeof (buf), "%s/*%s", dirs[i], suffix) >= sizeof (buf))
+      assert(0);
+    if (glob(buf, GLOB_NOSORT | (i > 0 ? GLOB_APPEND : 0), NULL, &gl) != 0)
+      ;
+  }
+
+  if (gl.gl_pathc > 0) {
+    /* Sort the paths by filenames */
+    qsort(gl.gl_pathv, gl.gl_pathc, sizeof (gl.gl_pathv[0]), compare_basenames);
+
+    for (i = 0; i < gl.gl_pathc; i += locations) {
+      /* Count directories containing files with this name */
+      for (j = i + 1, locations = 1; j < gl.gl_pathc; j++, locations++) {
+        if (compare_basenames(&gl.gl_pathv[i], &gl.gl_pathv[j]) != 0)
+          break;
+      }
+
+      /* Read the first file of this name in the order of the directive */
+      for (j = 0; j < n_dirs; j++) {
+        for (k = 0; k < locations; k++) {
+          path = gl.gl_pathv[i + k];
+          if (strncmp(path, dirs[j], strlen(dirs[j])) == 0 &&
+              strlen(dirs[j]) + 1 + strlen(get_basename(path)) == strlen(path)) {
+            file_handler(path);
+            break;
+          }
+        }
+        if (k < locations)
+          break;
+      }
+    }
+  }
+
+  globfree(&gl);
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+parse_confdir(char *line)
+{
+  if (!search_dirs(line, ".conf", CNF_ReadFile))
+    command_parse_error();
+}
+
+/* ================================================== */
+
 static void
 parse_include(char *line)
 {
@@ -1366,13 +1608,147 @@
 
 /* ================================================== */
 
+static void
+load_source_file(const char *filename)
+{
+  char line[MAX_LINE_LENGTH + 1];
+  FILE *f;
+
+  f = UTI_OpenFile(NULL, filename, NULL, 'r', 0);
+  if (!f)
+    return;
+
+  while (fgets(line, sizeof (line), f)) {
+    /* Require lines to be terminated */
+    if (line[0] == '\0' || line[strlen(line) - 1] != '\n')
+      break;
+
+    CPS_NormalizeLine(line);
+    if (line[0] == '\0')
+      continue;
+
+    parse_source(CPS_SplitWord(line), line, 0);
+  }
+
+  fclose(f);
+}
+
+/* ================================================== */
+
+static int
+compare_sources(const void *a, const void *b)
+{
+  const NTP_Source *sa = a, *sb = b;
+  int d;
+
+  if (!sa->params.name)
+    return -1;
+  if (!sb->params.name)
+    return 1;
+  if ((d = strcmp(sa->params.name, sb->params.name)) != 0)
+    return d;
+  if ((d = (int)(sa->type) - (int)(sb->type)) != 0)
+    return d;
+  if ((d = sa->pool - sb->pool) != 0)
+    return d;
+  if ((d = sa->params.port - sb->params.port) != 0)
+    return d;
+  return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params));
+}
+
+/* ================================================== */
+
+static void
+reload_source_dirs(void)
+{
+  NTP_Source *prev_sources, *new_sources, *source;
+  unsigned int i, j, prev_size, new_size, unresolved;
+  uint32_t *prev_ids, *new_ids;
+  char buf[MAX_LINE_LENGTH];
+  NSR_Status s;
+  int d;
+
+  prev_size = ARR_GetSize(ntp_source_ids);
+  if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size)
+    assert(0);
+
+  /* Save the current sources and their configuration IDs */
+  prev_ids = MallocArray(uint32_t, prev_size);
+  memcpy(prev_ids, ARR_GetElements(ntp_source_ids), prev_size * sizeof (prev_ids[0]));
+  prev_sources = MallocArray(NTP_Source, prev_size);
+  memcpy(prev_sources, ARR_GetElements(ntp_sources), prev_size * sizeof (prev_sources[0]));
+
+  /* Load the sources again */
+  ARR_SetSize(ntp_sources, 0);
+  for (i = 0; i < ARR_GetSize(ntp_source_dirs); i++) {
+    if (snprintf(buf, sizeof (buf), "%s",
+                 *(char **)ARR_GetElement(ntp_source_dirs, i)) >= sizeof (buf))
+      assert(0);
+    search_dirs(buf, ".sources", load_source_file);
+  }
+
+  /* Add new and remove existing sources according to the new configuration.
+     Avoid removing and adding the same source again to keep its state. */
+
+  new_size = ARR_GetSize(ntp_sources);
+  new_sources = ARR_GetElements(ntp_sources);
+  ARR_SetSize(ntp_source_ids, new_size);
+  new_ids = ARR_GetElements(ntp_source_ids);
+  unresolved = 0;
+
+  qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
+
+  for (i = j = 0; i < prev_size || j < new_size; ) {
+    if (i < prev_size && j < new_size)
+      d = compare_sources(&prev_sources[i], &new_sources[j]);
+    else
+      d = i < prev_size ? -1 : 1;
+
+    if (d < 0) {
+      /* Remove the missing source */
+      if (prev_sources[i].params.name[0] != '\0')
+        NSR_RemoveSourcesById(prev_ids[i]);
+      i++;
+    } else if (d > 0) {
+      /* Add a newly configured source */
+      source = &new_sources[j];
+      s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
+                              source->type, &source->params.params, &new_ids[j]);
+
+      if (s == NSR_UnresolvedName) {
+        unresolved++;
+      } else if (s != NSR_Success) {
+        LOG(LOGS_ERR, "Could not add source %s", source->params.name);
+
+        /* Mark the source as not present */
+        source->params.name[0] = '\0';
+      }
+      j++;
+    } else {
+      /* Keep the existing source */
+      new_ids[j] = prev_ids[i];
+      i++, j++;
+    }
+  }
+
+  for (i = 0; i < prev_size; i++)
+    Free(prev_sources[i].params.name);
+  Free(prev_sources);
+  Free(prev_ids);
+
+  if (unresolved > 0)
+    NSR_ResolveSources();
+}
+
+/* ================================================== */
+
 void
 CNF_CreateDirs(uid_t uid, gid_t gid)
 {
   char *dir;
 
   /* Create a directory for the Unix domain command socket */
-  if (bind_cmd_path[0]) {
+  if (bind_cmd_path) {
     dir = UTI_PathToDir(bind_cmd_path);
     UTI_CreateDirAndParents(dir, 0770, uid, gid);
 
@@ -1381,16 +1757,19 @@
        domain sockets are ignored on some systems (e.g. Solaris). */
     if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) {
       LOG(LOGS_WARN, "Disabled command socket %s", bind_cmd_path);
-      bind_cmd_path[0] = '\0';
+      Free(bind_cmd_path);
+      bind_cmd_path = NULL;
     }
 
     Free(dir);
   }
 
-  if (logdir[0])
-    UTI_CreateDirAndParents(logdir, 0755, uid, gid);
-  if (dumpdir[0])
-    UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
+  if (logdir)
+    UTI_CreateDirAndParents(logdir, 0750, uid, gid);
+  if (dumpdir)
+    UTI_CreateDirAndParents(dumpdir, 0750, uid, gid);
+  if (nts_dump_dir)
+    UTI_CreateDirAndParents(nts_dump_dir, 0750, uid, gid);
 }
 
 /* ================================================== */
@@ -1407,13 +1786,13 @@
     /* Get the default NTP params */
     CPS_ParseNTPSourceAdd(dummy_hostname, &cps_source);
 
-    /* Add the address as an offline iburst server */
+    /* Add the address as a server specified with the iburst option */
     ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i);
     ntp_addr.port = cps_source.port;
     cps_source.params.iburst = 1;
-    cps_source.params.online = 0;
 
-    NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params);
+    if (NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params, NULL) != NSR_Success)
+      LOG(LOGS_ERR, "Could not add source %s", UTI_IPToString(&ntp_addr.ip_addr));
   }
 
   ARR_SetSize(init_sources, 0);
@@ -1426,15 +1805,22 @@
 {
   NTP_Source *source;
   unsigned int i;
+  NSR_Status s;
 
   for (i = 0; i < ARR_GetSize(ntp_sources); i++) {
     source = (NTP_Source *)ARR_GetElement(ntp_sources, i);
-    NSR_AddSourceByName(source->params.name, source->params.port,
-                        source->pool, source->type, &source->params.params);
+
+    s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
+                            source->type, &source->params.params, NULL);
+    if (s != NSR_Success && s != NSR_UnresolvedName)
+      LOG(LOGS_ERR, "Could not add source %s", source->params.name);
+
     Free(source->params.name);
   }
 
   ARR_SetSize(ntp_sources, 0);
+
+  reload_source_dirs();
 }
 
 /* ================================================== */
@@ -1442,10 +1828,14 @@
 void
 CNF_AddRefclocks(void)
 {
+  RefclockParameters *refclock;
   unsigned int i;
 
   for (i = 0; i < ARR_GetSize(refclock_sources); i++) {
-    RCL_AddRefclock((RefclockParameters *)ARR_GetElement(refclock_sources, i));
+    refclock = ARR_GetElement(refclock_sources, i);
+    RCL_AddRefclock(refclock);
+    Free(refclock->driver_name);
+    Free(refclock->driver_parameter);
   }
 
   ARR_SetSize(refclock_sources, 0);
@@ -1461,8 +1851,7 @@
 
   for (i = 0; i < ARR_GetSize(broadcasts); i++) {
     destination = (NTP_Broadcast_Destination *)ARR_GetElement(broadcasts, i);
-    NCR_AddBroadcastDestination(&destination->addr, destination->port,
-                                destination->interval);
+    NCR_AddBroadcastDestination(&destination->addr, destination->interval);
   }
 
   ARR_SetSize(broadcasts, 0);
@@ -1470,6 +1859,14 @@
 
 /* ================================================== */
 
+void
+CNF_ReloadSources(void)
+{
+  reload_source_dirs();
+}
+
+/* ================================================== */
+
 int
 CNF_GetNTPPort(void)
 {
@@ -1631,6 +2028,14 @@
 
 /* ================================================== */
 
+SRC_AuthSelectMode
+CNF_GetAuthSelectMode(void)
+{
+  return authselect_mode;
+}
+
+/* ================================================== */
+
 double
 CNF_GetMaxSlewRate(void)
 {
@@ -1640,6 +2045,14 @@
 /* ================================================== */
 
 double
+CNF_GetClockPrecision(void)
+{
+  return clock_precision;
+}
+
+/* ================================================== */
+
+double
 CNF_GetMaxDistance(void)
 {
   return max_distance;
@@ -1849,6 +2262,30 @@
 /* ================================================== */
 
 char *
+CNF_GetBindNtpInterface(void)
+{
+  return bind_ntp_iface;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetBindAcquisitionInterface(void)
+{
+  return bind_acq_iface;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetBindCommandInterface(void)
+{
+  return bind_cmd_iface;
+}
+
+/* ================================================== */
+
+char *
 CNF_GetBindCommandPath(void)
 {
   return bind_cmd_path;
@@ -1869,6 +2306,14 @@
 
 /* ================================================== */
 
+int
+CNF_GetNtpDscp(void)
+{
+  return ntp_dscp;
+}
+
+/* ================================================== */
+
 char *
 CNF_GetNtpSigndSocket(void)
 {
@@ -1927,6 +2372,16 @@
 
 /* ================================================== */
 
+int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak)
+{
+  *interval = nts_ratelimit_interval;
+  *burst = nts_ratelimit_burst;
+  *leak = nts_ratelimit_leak;
+  return nts_ratelimit_enabled;
+}
+
+/* ================================================== */
+
 int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak)
 {
   *interval = cmd_ratelimit_interval;
@@ -2026,3 +2481,111 @@
   *iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index);
   return 1;
 }
+
+/* ================================================== */
+
+int
+CNF_GetPtpPort(void)
+{
+  return ptp_port;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetNtsDumpDir(void)
+{
+  return nts_dump_dir;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetNtsNtpServer(void)
+{
+  return nts_ntp_server;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys)
+{
+  *certs = ARR_GetElements(nts_server_cert_files);
+  *keys = ARR_GetElements(nts_server_key_files);
+
+  if (ARR_GetSize(nts_server_cert_files) != ARR_GetSize(nts_server_key_files))
+    LOG_FATAL("Uneven number of NTS certs and keys");
+
+  return ARR_GetSize(nts_server_cert_files);
+}
+
+/* ================================================== */
+
+int
+CNF_GetNtsServerPort(void)
+{
+  return nts_server_port;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNtsServerProcesses(void)
+{
+  return nts_server_processes;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNtsServerConnections(void)
+{
+  return nts_server_connections;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNtsRefresh(void)
+{
+  return nts_refresh;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNtsRotate(void)
+{
+  return nts_rotate;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids)
+{
+  *paths = ARR_GetElements(nts_trusted_certs_paths);
+  *ids = ARR_GetElements(nts_trusted_certs_ids);
+
+  if (ARR_GetSize(nts_trusted_certs_paths) != ARR_GetSize(nts_trusted_certs_ids))
+    assert(0);
+
+  return ARR_GetSize(nts_trusted_certs_paths);
+}
+
+/* ================================================== */
+
+int
+CNF_GetNoSystemCert(void)
+{
+  return no_system_cert;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNoCertTimeCheck(void)
+{
+  return no_cert_time_check;
+}
diff --git a/chrony_3_3/conf.h b/chrony/conf.h
similarity index 81%
rename from chrony_3_3/conf.h
rename to chrony/conf.h
index 25c98e1..f81f9aa 100644
--- a/chrony_3_3/conf.h
+++ b/chrony/conf.h
@@ -30,10 +30,13 @@
 
 #include "addressing.h"
 #include "reference.h"
+#include "sources.h"
 
 extern void CNF_Initialise(int restarted, int client_only);
 extern void CNF_Finalise(void);
 
+extern void CNF_EnablePrint(void);
+
 extern char *CNF_GetRtcDevice(void);
 
 extern void CNF_ReadFile(const char *filename);
@@ -46,6 +49,8 @@
 extern void CNF_AddBroadcasts(void);
 extern void CNF_AddRefclocks(void);
 
+extern void CNF_ReloadSources(void);
+
 extern int CNF_GetAcquisitionPort(void);
 extern int CNF_GetNTPPort(void);
 extern char *CNF_GetDriftFile(void);
@@ -74,7 +79,11 @@
 extern void CNF_GetBindAddress(int family, IPAddr *addr);
 extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
 extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
+extern char *CNF_GetBindNtpInterface(void);
+extern char *CNF_GetBindAcquisitionInterface(void);
+extern char *CNF_GetBindCommandInterface(void);
 extern char *CNF_GetBindCommandPath(void);
+extern int CNF_GetNtpDscp(void);
 extern char *CNF_GetNtpSigndSocket(void);
 extern char *CNF_GetPidFile(void);
 extern REF_LeapMode CNF_GetLeapSecMode(void);
@@ -86,7 +95,9 @@
 extern double CNF_GetMaxDrift(void);
 extern double CNF_GetCorrectionTimeRatio(void);
 extern double CNF_GetMaxSlewRate(void);
+extern double CNF_GetClockPrecision(void);
 
+extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void);
 extern double CNF_GetMaxDistance(void);
 extern double CNF_GetMaxJitter(void);
 extern double CNF_GetReselectDistance(void);
@@ -101,6 +112,7 @@
 extern int CNF_GetLockMemory(void);
 
 extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
+extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
 extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
 extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
 extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
@@ -122,12 +134,15 @@
   CNF_HWTS_RXFILTER_ANY,
   CNF_HWTS_RXFILTER_NONE,
   CNF_HWTS_RXFILTER_NTP,
+  CNF_HWTS_RXFILTER_PTP,
   CNF_HWTS_RXFILTER_ALL,
 } CNF_HwTs_RxFilter;
 
 typedef struct {
   char *name;
   int minpoll;
+  int min_samples;
+  int max_samples;
   int nocrossts;
   CNF_HwTs_RxFilter rxfilter;
   double precision;
@@ -137,4 +152,18 @@
 
 extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
 
+extern int CNF_GetPtpPort(void);
+
+extern char *CNF_GetNtsDumpDir(void);
+extern char *CNF_GetNtsNtpServer(void);
+extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
+extern int CNF_GetNtsServerPort(void);
+extern int CNF_GetNtsServerProcesses(void);
+extern int CNF_GetNtsServerConnections(void);
+extern int CNF_GetNtsRefresh(void);
+extern int CNF_GetNtsRotate(void);
+extern int CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids);
+extern int CNF_GetNoSystemCert(void);
+extern int CNF_GetNoCertTimeCheck(void);
+
 #endif /* GOT_CONF_H */
diff --git a/chrony_3_3/config.h b/chrony/config.h
similarity index 75%
rename from chrony_3_3/config.h
rename to chrony/config.h
index 863d3b5..c648083 100644
--- a/chrony_3_3/config.h
+++ b/chrony/config.h
@@ -4,18 +4,16 @@
 #define FEAT_NTP 1
 #define FEAT_REFCLOCK 1
 // HAVE_LONG_TIME_T is now a build-time option (see BUILD)
-//efine HAVE_LONG_TIME_T 1
-#define NTP_ERA_SPLIT (1522971079LL - 18250 * 24 * 3600)
-#define HAVE_STDINT_H 1
-#define HAVE_INTTYPES_H 1
+// #define HAVE_LONG_TIME_T 1
+#define NTP_ERA_SPLIT (1640121731LL - 18250 * 24 * 3600)
 #define HAVE_IN_PKTINFO 1
 #define FEAT_IPV6 1
 #define _GNU_SOURCE 1
 #define HAVE_IN6_PKTINFO 1
 #define HAVE_CLOCK_GETTIME 1
-#define HAVE_GETADDRINFO 1
 #define FEAT_ASYNCDNS 1
 #define USE_PTHREAD_ASYNCDNS 1
+#define HAVE_GETRANDOM 1
 #define HAVE_RECVMMSG 1
 #define HAVE_LINUX_TIMESTAMPING 1
 #define HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
@@ -23,16 +21,17 @@
 #define HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW 1
 #define FEAT_RTC 1
 #define FEAT_PHC 1
-#define HAVE_SCHED_SETSCHEDULER 1
+#define HAVE_PTHREAD_SETSCHEDPARAM 1
 #define HAVE_MLOCKALL 1
+#define HAVE_SETRLIMIT_MEMLOCK 1
 #define FORCE_DNSRETRY 1
 #define DEFAULT_CONF_FILE "/etc/chrony.conf"
 #define DEFAULT_HWCLOCK_FILE ""
-#define DEFAULT_PID_FILE "/var/run/chronyd.pid"
+#define DEFAULT_PID_FILE "/var/run/chrony/chronyd.pid"
 #define DEFAULT_RTC_DEVICE "/dev/rtc"
 #define DEFAULT_USER "root"
 #define DEFAULT_COMMAND_SOCKET "/var/run/chrony/chronyd.sock"
 #define MAIL_PROGRAM "/usr/lib/sendmail"
 #define CHRONYC_FEATURES "-READLINE -SECHASH +IPV6 -DEBUG"
-#define CHRONYD_FEATURES "+CMDMON +NTP +REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS -SECHASH +IPV6 -DEBUG"
-#define CHRONY_VERSION "3.3"
+#define CHRONYD_FEATURES "+CMDMON +NTP +REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS -NTS -SECHASH +IPV6 -DEBUG"
+#define CHRONY_VERSION "4.2"
diff --git a/chrony/config.log b/chrony/config.log
new file mode 100644
index 0000000..87927b7
--- /dev/null
+++ b/chrony/config.log
@@ -0,0 +1,363 @@
+conftest.c:
+#include "config.h"
+int main(int argc, char **argv) {
+
+return 0; }
+gcc -o conftest conftest.c
+
+conftest.c:
+#include "config.h"
+int main(int argc, char **argv) {
+
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+int main(int argc, char **argv) {
+
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+pkg-config --version
+0.29.2
+
+conftest.c:
+#include "config.h"
+#include <time.h>
+int main(int argc, char **argv) {
+
+  char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
+  return x[0];
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <math.h>
+int main(int argc, char **argv) {
+return (int) pow(2.0, log(sqrt((double)argc)));
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+/usr/bin/ld: /tmp/cci08Egu.o: in function `main':
+/google/src/cloud/elrom/chrony/google3/third_party/chrony/chrony_4_2/conftest.c:4: undefined reference to `log'
+/usr/bin/ld: /google/src/cloud/elrom/chrony/google3/third_party/chrony/chrony_4_2/conftest.c:4: undefined reference to `pow'
+/usr/bin/ld: /google/src/cloud/elrom/chrony/google3/third_party/chrony/chrony_4_2/conftest.c:4: undefined reference to `sqrt'
+collect2: error: ld returned 1 exit status
+
+conftest.c:
+#include "config.h"
+#include <math.h>
+int main(int argc, char **argv) {
+return (int) pow(2.0, log(sqrt((double)argc)));
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -lm -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+int main(int argc, char **argv) {
+
+  struct in_pktinfo ipi;
+  return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+int main(int argc, char **argv) {
+
+    struct sockaddr_in6 n;
+    char p[100];
+    n.sin6_addr = in6addr_any;
+    n.sin6_scope_id = 0;
+    return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -lm -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+int main(int argc, char **argv) {
+
+    return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+conftest.c: In function ‘main’:
+conftest.c:6:20: error: invalid application of ‘sizeof’ to incomplete type ‘struct in6_pktinfo’
+    6 |     return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;
+      |                    ^~~~~~
+
+conftest.c:
+#include "config.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+int main(int argc, char **argv) {
+return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -D_GNU_SOURCE -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+int main(int argc, char **argv) {
+return open("/dev/null", O_NOFOLLOW);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -lm -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <time.h>
+int main(int argc, char **argv) {
+clock_gettime(CLOCK_REALTIME, NULL);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+int main(int argc, char **argv) {
+return getaddrinfo(0, 0, 0, 0);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -lm -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <pthread.h>
+int main(int argc, char **argv) {
+
+    pthread_t thread;
+    return (int)pthread_create(&thread, NULL, (void *)1, NULL);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <stdlib.h>
+int main(int argc, char **argv) {
+arc4random_buf(NULL, 0);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+conftest.c: In function ‘main’:
+conftest.c:4:1: warning: implicit declaration of function ‘arc4random_buf’ [-Wimplicit-function-declaration]
+    4 | arc4random_buf(NULL, 0);
+      | ^~~~~~~~~~~~~~
+/usr/bin/ld: /tmp/ccnvCm0o.o: in function `main':
+/google/src/cloud/elrom/chrony/google3/third_party/chrony/chrony_4_2/conftest.c:4: undefined reference to `arc4random_buf'
+collect2: error: ld returned 1 exit status
+
+conftest.c:
+#include "config.h"
+#include <stdlib.h>
+#include <sys/random.h>
+int main(int argc, char **argv) {
+return getrandom(NULL, 256, 0);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/socket.h>
+int main(int argc, char **argv) {
+
+  struct mmsghdr hdr;
+  return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -lm -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/net_tstamp.h>
+#include <linux/errqueue.h>
+#include <linux/ptp_clock.h>
+int main(int argc, char **argv) {
+
+    int val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE |
+              SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG;
+    return sizeof (struct scm_timestamping) + SCM_TSTAMP_SND + PTP_SYS_OFFSET +
+           setsockopt(0, SOL_SOCKET, SO_SELECT_ERR_QUEUE + SO_TIMESTAMPING,
+                      &val, sizeof (val));
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/net_tstamp.h>
+int main(int argc, char **argv) {
+
+    struct scm_ts_pktinfo pktinfo;
+    pktinfo.if_index = pktinfo.pkt_length = 0;
+    return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
+           SCM_TIMESTAMPING_PKTINFO +
+           SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <inttypes.h>
+#include <time.h>
+#include <sys/timepps.h>
+int main(int argc, char **argv) {
+
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+conftest.c:4:10: fatal error: sys/timepps.h: No such file or directory
+    4 | #include <sys/timepps.h>
+      |          ^~~~~~~~~~~~~~~
+compilation terminated.
+
+conftest.c:
+#include "config.h"
+#include <inttypes.h>
+#include <time.h>
+#include <timepps.h>
+int main(int argc, char **argv) {
+
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+conftest.c:4:10: fatal error: timepps.h: No such file or directory
+    4 | #include <timepps.h>
+      |          ^~~~~~~~~~~
+compilation terminated.
+
+conftest.c:
+#include "config.h"
+#include <sys/ioctl.h>
+#include <linux/rtc.h>
+int main(int argc, char **argv) {
+ioctl(1, RTC_UIE_ON&RTC_UIE_OFF&RTC_RD_TIME&RTC_SET_TIME, 0&RTC_UF);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/ioctl.h>
+#include <linux/ptp_clock.h>
+int main(int argc, char **argv) {
+ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <pthread.h>
+#include <sched.h>
+int main(int argc, char **argv) {
+
+     struct sched_param sched;
+     sched_get_priority_max(SCHED_FIFO);
+     pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/mman.h>
+int main(int argc, char **argv) {
+
+     mlockall(MCL_CURRENT|MCL_FUTURE);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+conftest.c:
+#include "config.h"
+#include <sys/resource.h>
+int main(int argc, char **argv) {
+
+     struct rlimit rlim;
+     rlim.rlim_max = rlim.rlim_cur = RLIM_INFINITY;
+     setrlimit(RLIMIT_MEMLOCK, &rlim);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+
+Package nettle was not found in the pkg-config search path.
+Perhaps you should add the directory containing `nettle.pc'
+to the PKG_CONFIG_PATH environment variable
+No package 'nettle' found
+Package nettle was not found in the pkg-config search path.
+Perhaps you should add the directory containing `nettle.pc'
+to the PKG_CONFIG_PATH environment variable
+No package 'nettle' found
+conftest.c:
+#include "config.h"
+#include <nettle/nettle-meta.h>
+#include <nettle/sha2.h>
+int main(int argc, char **argv) {
+return nettle_hashes[0]->context_size;
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+conftest.c:2:10: fatal error: nettle/nettle-meta.h: No such file or directory
+    2 | #include <nettle/nettle-meta.h>
+      |          ^~~~~~~~~~~~~~~~~~~~~~
+compilation terminated.
+
+conftest.c:
+#include "config.h"
+#include <tomcrypt.h>
+int main(int argc, char **argv) {
+hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -I/usr/include/tomcrypt -o conftest conftest.c -ltomcrypt -pie -Wl,-z,relro,-z,now
+conftest.c:2:10: fatal error: tomcrypt.h: No such file or directory
+    2 | #include <tomcrypt.h>
+      |          ^~~~~~~~~~~~
+compilation terminated.
+
+Package gnutls was not found in the pkg-config search path.
+Perhaps you should add the directory containing `gnutls.pc'
+to the PKG_CONFIG_PATH environment variable
+No package 'gnutls' found
+Package gnutls was not found in the pkg-config search path.
+Perhaps you should add the directory containing `gnutls.pc'
+to the PKG_CONFIG_PATH environment variable
+No package 'gnutls' found
+conftest.c:
+#include "config.h"
+#include <gnutls/crypto.h>
+int main(int argc, char **argv) {
+
+      return gnutls_hash(NULL, NULL, 0);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -pie -Wl,-z,relro,-z,now
+conftest.c:2:10: fatal error: gnutls/crypto.h: No such file or directory
+    2 | #include <gnutls/crypto.h>
+      |          ^~~~~~~~~~~~~~~~~
+compilation terminated.
+
+Package gnutls was not found in the pkg-config search path.
+Perhaps you should add the directory containing `gnutls.pc'
+to the PKG_CONFIG_PATH environment variable
+No package 'gnutls' found
+Package gnutls was not found in the pkg-config search path.
+Perhaps you should add the directory containing `gnutls.pc'
+to the PKG_CONFIG_PATH environment variable
+No package 'gnutls' found
+conftest.c:
+#include "config.h"
+#include <gnutls/gnutls.h>
+int main(int argc, char **argv) {
+
+      return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
+        gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
+        gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);
+return 0; }
+gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o conftest conftest.c -lm -pie -Wl,-z,relro,-z,now
+conftest.c:2:10: fatal error: gnutls/gnutls.h: No such file or directory
+    2 | #include <gnutls/gnutls.h>
+      |          ^~~~~~~~~~~~~~~~~
+compilation terminated.
+
diff --git a/chrony_3_3/configure b/chrony/configure
similarity index 77%
rename from chrony_3_3/configure
rename to chrony/configure
index 25773de..8041d4e 100755
--- a/chrony_3_3/configure
+++ b/chrony/configure
@@ -5,7 +5,8 @@
 #
 # Copyright (C) Richard P. Curnow  1997-2003
 # Copyright (C) Bryan Christianson  2016
-# Copyright (C) Miroslav Lichvar  2009, 2012-2018
+# Copyright (C) Miroslav Lichvar  2009, 2012-2021
+# Copyright (C) Stefan R. Filipek  2019
 #
 # =======================================================================
 
@@ -32,13 +33,13 @@
     echo "int main(int argc, char **argv) {"
     echo "$code"
     echo "return 0; }"
-  ) > docheck.c
+  ) > conftest.c
 
-  echo "docheck.c:" >> config.log
-  cat docheck.c >> config.log
-  echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \
+  echo "conftest.c:" >> config.log
+  cat conftest.c >> config.log
+  echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
     $MYLDFLAGS >> config.log
-  $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \
+  $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
     $MYLDFLAGS >> config.log 2>&1
 
   if [ $? -eq 0 ]
@@ -49,11 +50,39 @@
     echo "No"
     result=1
   fi
-  rm -f docheck.c docheck
+  rm -f conftest.c conftest
   echo >> config.log
   return $result
 }
 #}}}
+#{{{ test_executable
+test_executable () {
+  name=$1
+  executable=$2
+  options=$3
+
+  printf "%s" "Checking for $name : "
+
+  echo $executable $options >> config.log
+  $executable $options >> config.log 2>&1
+
+  if [ $? -eq 0 ]
+  then
+    echo "Yes"
+    result=0
+  else
+    echo "No"
+    result=1
+  fi
+  echo >> config.log
+  return $result
+}
+#}}}
+#{{{ pkg_config
+pkg_config () {
+  $PKG_CONFIG "$@" 2>> config.log
+}
+#}}}
 #{{{ usage
 usage () {
   cat <<EOF
@@ -79,15 +108,13 @@
 
 For better control, use the options below.
   --disable-readline     Disable line editing support
-  --without-readline     Don't use GNU readline even if it is available
   --without-editline     Don't use editline even if it is available
-  --with-readline-includes=DIR Specify where readline include directory is
-  --with-readline-library=DIR Specify where readline lib directory is
-  --with-ncurses-library=DIR Specify where ncurses lib directory is
   --disable-sechash      Disable support for hashes other than MD5
   --without-nettle       Don't use nettle even if it is available
   --without-nss          Don't use NSS even if it is available
   --without-tomcrypt     Don't use libtomcrypt even if it is available
+  --disable-nts          Disable NTS support
+  --without-gnutls       Don't use gnutls even if it is available
   --disable-cmdmon       Disable command and monitoring support
   --disable-ntp          Disable NTP support
   --disable-refclock     Disable reference clock support
@@ -108,7 +135,7 @@
                          since 1970-01-01 [50*365 days ago]
   --with-user=USER       Specify default chronyd user [root]
   --with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
-  --with-pidfile=PATH    Specify default pidfile [/var/run/chronyd.pid]
+  --with-pidfile=PATH    Specify default pidfile [/var/run/chrony/chronyd.pid]
   --with-rtcdevice=PATH  Specify default path to RTC device [/dev/rtc]
   --with-sendmail=PATH   Path to sendmail binary [/usr/lib/sendmail]
   --enable-debug         Enable debugging support
@@ -136,6 +163,11 @@
               headers in a nonstandard directory <include dir>
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
               nonstandard directory <lib dir>
+  PKG_CONFIG  path to pkg-config utility
+  PKG_CONFIG_PATH
+              directories to add to pkg-config's search path
+  PKG_CONFIG_LIBDIR
+              path overriding pkg-config's built-in search path
 
 Use these variables to override the choices made by \`configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -153,13 +185,6 @@
   fi
 }
 #}}}
-#{{{ pkg_config
-pkg_config () {
-  type pkg-config > /dev/null 2> /dev/null || return 1
-
-  pkg-config $@ 2> /dev/null
-}
-#}}}
 #{{{ get_features
 get_features () {
   ff=1
@@ -185,23 +210,24 @@
 VERSION=`uname -r`
 MACHINE=`uname -m`
 
+LIBS=""
 EXTRA_LIBS=""
 EXTRA_CLI_LIBS=""
 EXTRA_OBJECTS=""
-EXTRA_DEFS=""
-SYSDEFS=""
+EXTRA_CLI_OBJECTS=""
 
 feat_debug=0
 feat_cmdmon=1
 feat_ntp=1
 feat_refclock=1
 feat_readline=1
-try_readline=1
 try_editline=1
 feat_sechash=1
 try_nettle=1
 try_nss=1
 try_tomcrypt=1
+feat_nts=1
+try_gnutls=1
 feat_rtc=1
 try_rtc=0
 feat_droproot=1
@@ -210,9 +236,6 @@
 feat_scfilter=0
 try_seccomp=-1
 priv_ops=""
-readline_lib=""
-readline_inc=""
-ncurses_lib=""
 feat_ipv6=1
 feat_phc=1
 try_phc=0
@@ -227,9 +250,10 @@
 try_timestamping=0
 feat_ntp_signd=0
 ntp_era_split=""
+use_pthread=0
 default_user="root"
 default_hwclockfile=""
-default_pidfile="/var/run/chronyd.pid"
+default_pidfile="/var/run/chrony/chronyd.pid"
 default_rtcdevice="/dev/rtc"
 mail_program="/usr/lib/sendmail"
 
@@ -242,21 +266,9 @@
     --disable-readline )
       feat_readline=0
     ;;
-    --without-readline )
-      try_readline=0
-    ;;
     --without-editline )
       try_editline=0
     ;;
-    --with-readline-library=* )
-      readline_lib=-L`echo $option | sed -e 's/^.*=//;'`
-    ;;
-    --with-readline-includes=* )
-      readline_inc=-I`echo $option | sed -e 's/^.*=//;'`
-    ;;
-    --with-ncurses-library=* )
-      ncurses_lib=-L`echo $option | sed -e 's/^.*=//;'`
-    ;;
     --prefix=* | --install_prefix=* )
       SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'`
     ;;
@@ -371,6 +383,12 @@
     --without-tomcrypt )
       try_tomcrypt=0
     ;;
+    --disable-nts )
+      feat_nts=0
+    ;;
+    --without-gnutls )
+      try_gnutls=0
+    ;;
     --host-system=* )
       OPERATINGSYSTEM=`echo $option | sed -e 's/^.*=//;'`
     ;;
@@ -395,7 +413,7 @@
 
 case $OPERATINGSYSTEM in
     Linux)
-        EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o"
+        EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o sys_posix.o"
         [ $try_libcap != "0" ] && try_libcap=1
         try_rtc=1
         [ $try_seccomp != "0" ] && try_seccomp=1
@@ -410,7 +428,9 @@
         # recvmmsg() seems to be broken on FreeBSD 11.0 and it's just
         # a wrapper around recvmsg()
         try_recvmmsg=0
-        EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
+        EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
+        try_setsched=1
+        try_lockmem=1
         add_def FREEBSD
         if [ $feat_droproot = "1" ]; then
           add_def FEAT_PRIVDROP
@@ -419,15 +439,16 @@
         echo "Configuring for $SYSTEM"
     ;;
     NetBSD)
-        EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
+        EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
         try_clockctl=1
+        try_setsched=1
+        try_lockmem=1
         add_def NETBSD
         echo "Configuring for $SYSTEM"
     ;;
     Darwin)
         EXTRA_OBJECTS="sys_macosx.o"
-        EXTRA_LIBS="-lresolv"
-        EXTRA_CLI_LIBS="-lresolv"
+        LIBS="$LIBS -lresolv"
         add_def MACOSX
         if [ $feat_droproot = "1" ]; then
           add_def FEAT_PRIVDROP
@@ -445,9 +466,10 @@
         echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
     ;;
     SunOS)
-        EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
-        EXTRA_LIBS="-lsocket -lnsl -lresolv"
-        EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
+        EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
+        LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
+        try_setsched=1
+        try_lockmem=1
         add_def SOLARIS
         # These are needed to have msg_control in struct msghdr
         add_def __EXTENSIONS__
@@ -457,7 +479,7 @@
           add_def FEAT_PRIVDROP
           priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
         fi
-        echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")" 
+        echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
     ;;                                                                        
     * )
         echo "error: $SYSTEM is not supported (yet?)"
@@ -477,7 +499,7 @@
 
 if [ $feat_ntp = "1" ]; then
   add_def FEAT_NTP
-  EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o"
+  EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o"
   if [ $feat_ntp_signd = "1" ]; then
     add_def FEAT_SIGND
     EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
@@ -545,6 +567,16 @@
   MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
 fi
 
+if [ "x$PKG_CONFIG" = "x" ]; then
+  PKG_CONFIG=pkg-config
+fi
+
+if ! test_executable "pkg-config" $PKG_CONFIG --version; then
+  try_nettle=0
+  try_nss=0
+  try_gnutls=0
+fi
+
 if test_code '64-bit time_t' 'time.h' '' '' '
   char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
   return x[0];'
@@ -584,25 +616,15 @@
 fi
 
 MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));'
-if test_code 'math' 'math.h' '' '' "$MATHCODE"; then
-  LIBS=""
-else
+if ! test_code 'math' 'math.h' '' '' "$MATHCODE"; then
   if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then
-    LIBS="-lm"
+    LIBS="$LIBS -lm"
   else
     echo "error: could not compile/link a program which uses sqrt(), log(), pow()"
     exit 1
   fi
 fi
   
-if test_code '<stdint.h>' 'stdint.h' '' '' ''; then
-  add_def HAVE_STDINT_H
-fi
-
-if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
-  add_def HAVE_INTTYPES_H
-fi
-
 if test_code 'struct in_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
   struct in_pktinfo ipi;
   return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;'
@@ -611,10 +633,11 @@
 fi
 
 if [ $feat_ipv6 = "1" ] && \
-  test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$EXTRA_LIBS" '
+  test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$LIBS" '
     struct sockaddr_in6 n;
     char p[100];
     n.sin6_addr = in6addr_any;
+    n.sin6_scope_id = 0;
     return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
 then
   add_def FEAT_IPV6
@@ -632,6 +655,20 @@
   fi
 fi
 
+if ! test_code 'O_NOFOLLOW flag' 'sys/types.h sys/stat.h fcntl.h' '' "$LIBS" \
+  'return open("/dev/null", O_NOFOLLOW);'
+then
+  if test_code 'O_NOFOLLOW flag with _GNU_SOURCE' 'sys/types.h sys/stat.h fcntl.h' \
+    '-D_GNU_SOURCE' "$LIBS" \
+    'return open("/dev/null", O_NOFOLLOW);'
+  then
+    add_def _GNU_SOURCE
+  else
+    echo "error: open() does not support O_NOFOLLOW flag"
+    exit 1
+  fi
+fi
+
 if [ $try_clock_gettime = "1" ]; then
   if test_code 'clock_gettime()' 'time.h' '' '' \
     'clock_gettime(CLOCK_REALTIME, NULL);'
@@ -647,40 +684,42 @@
   fi
 fi
 
-if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
+if ! test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \
   'return getaddrinfo(0, 0, 0, 0);'
 then
-  add_def HAVE_GETADDRINFO
+  echo "error: getaddrinfo() not found"
+  exit 1
 fi
 
 if [ $feat_asyncdns = "1" ] && \
-  test_code 'pthread' 'pthread.h' '-pthread' '' \
-    'return pthread_create((void *)1, NULL, (void *)1, NULL);'
+  test_code 'pthread' 'pthread.h' '-pthread' '' '
+    pthread_t thread;
+    return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
 then
   add_def FEAT_ASYNCDNS
   add_def USE_PTHREAD_ASYNCDNS
   EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
-  MYCFLAGS="$MYCFLAGS -pthread"
+  use_pthread=1
 fi
 
 if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
   add_def HAVE_ARC4RANDOM
-fi
-
-if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
-    'return getrandom(NULL, 256, 0);'; then
-  add_def HAVE_GETRANDOM
+else
+  if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
+      'return getrandom(NULL, 256, 0);'; then
+    add_def HAVE_GETRANDOM
+  fi
 fi
 
 RECVMMSG_CODE='
   struct mmsghdr hdr;
   return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
 if [ $try_recvmmsg = "1" ]; then
-  if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
+  if test_code 'recvmmsg()' 'sys/socket.h' '' "$LIBS" "$RECVMMSG_CODE"; then
     add_def HAVE_RECVMMSG
   else
     if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
-      "$EXTRA_LIBS" "$RECVMMSG_CODE"
+      "$LIBS" "$RECVMMSG_CODE"
     then
       add_def _GNU_SOURCE
       add_def HAVE_RECVMMSG
@@ -715,11 +754,11 @@
 
 timepps_h=""
 if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
-  if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
+  if test_code '<sys/timepps.h>' 'inttypes.h time.h sys/timepps.h' '' '' ''; then
     timepps_h="sys/timepps.h"
     add_def HAVE_SYS_TIMEPPS_H
   else
-    if test_code '<timepps.h>' 'timepps.h' '' '' ''; then
+    if test_code '<timepps.h>' 'inttypes.h time.h timepps.h' '' '' ''; then
       timepps_h="timepps.h"
       add_def HAVE_TIMEPPS_H
     fi
@@ -727,10 +766,11 @@
 fi
 
 if [ "x$timepps_h" != "x" ] && \
-  test_code 'PPSAPI' "string.h $timepps_h" '' '' '
+  test_code 'PPSAPI' "inttypes.h string.h time.h $timepps_h" '' '' '
     pps_handle_t h = 0;
     pps_info_t i;
     struct timespec ts;
+    ts.tv_sec = ts.tv_nsec = 0;
     return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);'
 then
   add_def FEAT_PPS
@@ -759,10 +799,12 @@
     'seccomp_init(SCMP_ACT_KILL);'
 then
   add_def FEAT_SCFILTER
-  # NAME2IPADDRESS shouldn't be enabled with other operations as the helper
-  # process works on one request at the time and the async resolver could
-  # block the main thread
-  priv_ops="NAME2IPADDRESS RELOADDNS"
+  if [ $feat_ntp = "1" ]; then
+    # NAME2IPADDRESS shouldn't be enabled together with a privops operation
+    # used by the main thread as the helper process works on one request at
+    # a time and the async resolver would block the main thread
+    priv_ops="NAME2IPADDRESS RELOADDNS"
+  fi
   EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
 fi
 
@@ -794,25 +836,34 @@
 
 if [ $try_setsched = "1" ] && \
   test_code \
-    'sched_setscheduler()' \
-    'sched.h' '' '' '
+    'pthread_setschedparam()' \
+    'pthread.h sched.h' '-pthread' '' '
      struct sched_param sched;
      sched_get_priority_max(SCHED_FIFO);
-     sched_setscheduler(0, SCHED_FIFO, &sched);'
+     pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
 then
-  add_def HAVE_SCHED_SETSCHEDULER
+  add_def HAVE_PTHREAD_SETSCHEDPARAM
+  use_pthread=1
 fi
 
 if [ $try_lockmem = "1" ] && \
   test_code \
     'mlockall()' \
-    'sys/mman.h sys/resource.h' '' '' '
-     struct rlimit rlim;
-     setrlimit(RLIMIT_MEMLOCK, &rlim);
+    'sys/mman.h' '' '' '
      mlockall(MCL_CURRENT|MCL_FUTURE);'
 then
   add_def HAVE_MLOCKALL
 fi
+if [ $try_lockmem = "1" ] && \
+  test_code \
+    'setrlimit(RLIMIT_MEMLOCK, ...)' \
+    'sys/resource.h' '' '' '
+     struct rlimit rlim;
+     rlim.rlim_max = rlim.rlim_cur = RLIM_INFINITY;
+     setrlimit(RLIMIT_MEMLOCK, &rlim);'
+then
+  add_def HAVE_SETRLIMIT_MEMLOCK
+fi
 
 if [ $feat_forcednsretry = "1" ]
 then
@@ -822,37 +873,11 @@
 READLINE_LINK=""
 if [ $feat_readline = "1" ]; then
   if [ $try_editline = "1" ]; then
-    if test_code editline 'stdio.h editline/readline.h' \
-      "$readline_inc" "$readline_lib -ledit" \
+    if test_code editline 'stdio.h editline/readline.h' '' '-ledit' \
       'add_history(readline("prompt"));'
     then
       add_def FEAT_READLINE
-      add_def USE_EDITLINE
-      MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
-      READLINE_LINK="$readline_lib -ledit"
-    fi
-  fi
-
-  if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
-    if test_code readline 'stdio.h readline/readline.h readline/history.h' \
-      "$readline_inc" "$readline_lib -lreadline" \
-      'add_history(readline("prompt"));'
-    then
-      add_def FEAT_READLINE
-      MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
-      READLINE_LINK="$readline_lib -lreadline"
-    fi
-  fi
-
-  if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
-    if test_code 'readline with -lncurses' \
-      'stdio.h readline/readline.h readline/history.h' \
-      "$readline_inc" "$readline_lib $ncurses_lib -lreadline -lncurses" \
-      'add_history(readline("prompt"));'
-    then
-      add_def FEAT_READLINE
-      MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
-      READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
+      READLINE_LINK="-ledit"
     fi
   fi
 
@@ -871,22 +896,28 @@
   then
     HASH_OBJ="hash_nettle.o"
     HASH_LINK="$test_link"
-    LIBS="$LIBS $HASH_LINK"
     MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
     add_def FEAT_SECHASH
+
+    if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
+      'cmac128_update(NULL, NULL, NULL, 0, NULL);'
+    then
+      add_def HAVE_CMAC
+      EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
+      EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_nettle.o"
+    fi
   fi
 fi
 
 if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ]  && [ $try_nss = "1" ]; then
   test_cflags="`pkg_config --cflags nss`"
-  test_link="`pkg_config --libs-only-L nss` -lfreebl3"
+  test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3"
   if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
     "$test_cflags" "$test_link" \
     'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
   then
     HASH_OBJ="hash_nss.o"
     HASH_LINK="$test_link"
-    LIBS="$LIBS $HASH_LINK"
     MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
     add_def FEAT_SECHASH
   fi
@@ -898,12 +929,89 @@
   then
     HASH_OBJ="hash_tomcrypt.o"
     HASH_LINK="-ltomcrypt"
-    LIBS="$LIBS $HASH_LINK"
     MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/tomcrypt"
     add_def FEAT_SECHASH
   fi
 fi
 
+if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
+  test_cflags="`pkg_config --cflags gnutls`"
+  test_link="`pkg_config --libs gnutls`"
+  if test_code 'gnutls' 'gnutls/crypto.h' \
+    "$test_cflags" "$test_link" '
+      return gnutls_hash(NULL, NULL, 0);'
+  then
+    HASH_OBJ="hash_gnutls.o"
+    HASH_LINK="$test_link"
+    MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
+    add_def FEAT_SECHASH
+
+    if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
+      'return gnutls_hmac_init(NULL, GNUTLS_MAC_AES_CMAC_128, NULL, 0);'
+    then
+      add_def HAVE_CMAC
+      EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
+      EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
+    fi
+  fi
+fi
+
+EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
+EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
+LIBS="$LIBS $HASH_LINK"
+
+if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
+  if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
+    test_cflags=""
+    test_link=""
+  else
+    test_cflags="`pkg_config --cflags gnutls`"
+    test_link="`pkg_config --libs gnutls`"
+  fi
+  if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
+    "$test_cflags" "$test_link $LIBS" '
+      return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
+        gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
+        gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
+  then
+    if test_code 'SIV in nettle' \
+      'nettle/siv-cmac.h' "" "$LIBS" \
+      'siv_cmac_aes128_set_key(NULL, NULL);'
+    then
+      EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
+      add_def HAVE_SIV
+      add_def HAVE_NETTLE_SIV_CMAC
+    else
+      if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
+        "$test_cflags" "$test_link $LIBS" '
+          return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
+      then
+        EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
+        add_def HAVE_SIV
+      else
+        if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
+          'aes128_set_encrypt_key(NULL, NULL);'
+        then
+          EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
+          add_def HAVE_SIV
+        fi
+      fi
+    fi
+
+    if grep '#define HAVE_SIV' config.h > /dev/null; then
+      EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
+      EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
+      LIBS="$LIBS $test_link"
+      MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
+      add_def FEAT_NTS
+    fi
+  fi
+fi
+
+if [ $use_pthread = "1" ]; then
+  MYCFLAGS="$MYCFLAGS -pthread"
+fi
+
 SYSCONFDIR=/etc
 if [ "x$SETSYSCONFDIR" != "x" ]; then
   SYSCONFDIR=$SETSYSCONFDIR
@@ -969,7 +1077,7 @@
 
 common_features="`get_features SECHASH IPV6 DEBUG`"
 chronyc_features="`get_features READLINE`"
-chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`"
+chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS NTS`"
 add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
 add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
 echo "Features : $chronyd_features $chronyc_features $common_features"
@@ -985,15 +1093,15 @@
 for f in Makefile doc/Makefile test/unit/Makefile
 do
   echo Creating $f
-  sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
+  sed -e "s%@EXTRA_OBJS@%${EXTRA_OBJECTS}%;\
+          s%@EXTRA_CLI_OBJS@%${EXTRA_CLI_OBJECTS}%;\
           s%@CC@%${MYCC}%;\
           s%@CFLAGS@%${MYCFLAGS}%;\
           s%@CPPFLAGS@%${MYCPPFLAGS}%;\
-          s%@LIBS@%${LIBS}%;\
           s%@LDFLAGS@%${MYLDFLAGS}%;\
+          s%@LIBS@%${LIBS}%;\
           s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
           s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
-          s%@HASH_OBJ@%${HASH_OBJ}%;\
           s%@SYSCONFDIR@%${SYSCONFDIR}%;\
           s%@BINDIR@%${BINDIR}%;\
           s%@SBINDIR@%${SBINDIR}%;\
diff --git a/chrony_3_3/contrib/andrew_bishop_1 b/chrony/contrib/andrew_bishop_1
similarity index 100%
rename from chrony_3_3/contrib/andrew_bishop_1
rename to chrony/contrib/andrew_bishop_1
diff --git a/chrony_3_3/contrib/andrew_bishop_2 b/chrony/contrib/andrew_bishop_2
similarity index 100%
rename from chrony_3_3/contrib/andrew_bishop_2
rename to chrony/contrib/andrew_bishop_2
diff --git a/chrony_3_3/contrib/bryan_christianson_1/README.txt b/chrony/contrib/bryan_christianson_1/README.txt
similarity index 100%
rename from chrony_3_3/contrib/bryan_christianson_1/README.txt
rename to chrony/contrib/bryan_christianson_1/README.txt
diff --git a/chrony_3_3/contrib/bryan_christianson_1/chronylogrotate.sh b/chrony/contrib/bryan_christianson_1/chronylogrotate.sh
similarity index 100%
rename from chrony_3_3/contrib/bryan_christianson_1/chronylogrotate.sh
rename to chrony/contrib/bryan_christianson_1/chronylogrotate.sh
diff --git a/chrony_3_3/contrib/bryan_christianson_1/org.tuxfamily.chronyc.plist b/chrony/contrib/bryan_christianson_1/org.tuxfamily.chronyc.plist
similarity index 100%
rename from chrony_3_3/contrib/bryan_christianson_1/org.tuxfamily.chronyc.plist
rename to chrony/contrib/bryan_christianson_1/org.tuxfamily.chronyc.plist
diff --git a/chrony_3_3/contrib/bryan_christianson_1/org.tuxfamily.chronyd.plist b/chrony/contrib/bryan_christianson_1/org.tuxfamily.chronyd.plist
similarity index 100%
rename from chrony_3_3/contrib/bryan_christianson_1/org.tuxfamily.chronyd.plist
rename to chrony/contrib/bryan_christianson_1/org.tuxfamily.chronyd.plist
diff --git a/chrony_3_3/contrib/erik_bryer_1 b/chrony/contrib/erik_bryer_1
similarity index 100%
rename from chrony_3_3/contrib/erik_bryer_1
rename to chrony/contrib/erik_bryer_1
diff --git a/chrony_3_3/contrib/ken_gillett_1 b/chrony/contrib/ken_gillett_1
similarity index 100%
rename from chrony_3_3/contrib/ken_gillett_1
rename to chrony/contrib/ken_gillett_1
diff --git a/chrony_3_3/contrib/stephan_boettcher_1 b/chrony/contrib/stephan_boettcher_1
similarity index 100%
rename from chrony_3_3/contrib/stephan_boettcher_1
rename to chrony/contrib/stephan_boettcher_1
diff --git a/chrony_3_3/contrib/wolfgang_weisselberg1 b/chrony/contrib/wolfgang_weisselberg1
similarity index 100%
rename from chrony_3_3/contrib/wolfgang_weisselberg1
rename to chrony/contrib/wolfgang_weisselberg1
diff --git a/chrony_3_3/doc/Makefile b/chrony/doc/Makefile
similarity index 96%
rename from chrony_3_3/doc/Makefile
rename to chrony/doc/Makefile
index 76f74b7..b1e993e 100644
--- a/chrony_3_3/doc/Makefile
+++ b/chrony/doc/Makefile
@@ -15,10 +15,10 @@
 DOCDIR = /usr/local/share/doc/chrony
 CHRONYRUNDIR = /var/run/chrony
 CHRONYVARDIR = /var/lib/chrony
-CHRONY_VERSION = 3.3
+CHRONY_VERSION = 4.2
 DEFAULT_USER = root
 DEFAULT_HWCLOCK_FILE = 
-DEFAULT_PID_FILE = /var/run/chronyd.pid
+DEFAULT_PID_FILE = /var/run/chrony/chronyd.pid
 DEFAULT_RTC_DEVICE = /dev/rtc
 
 SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
diff --git a/chrony_3_3/doc/Makefile.in b/chrony/doc/Makefile.in
similarity index 100%
rename from chrony_3_3/doc/Makefile.in
rename to chrony/doc/Makefile.in
diff --git a/chrony_3_3/doc/chrony.conf.adoc b/chrony/doc/chrony.conf.adoc
similarity index 70%
rename from chrony_3_3/doc/chrony.conf.adoc
rename to chrony/doc/chrony.conf.adoc
index be39a27..21d2ce6 100644
--- a/chrony_3_3/doc/chrony.conf.adoc
+++ b/chrony/doc/chrony.conf.adoc
@@ -3,7 +3,7 @@
 // Copyright (C) Richard P. Curnow  1997-2003
 // Copyright (C) Stephen Wadeley  2016
 // Copyright (C) Bryan Christianson  2017
-// Copyright (C) Miroslav Lichvar  2009-2017
+// Copyright (C) Miroslav Lichvar  2009-2021
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of version 2 of the GNU General Public License as
@@ -32,12 +32,14 @@
 == DESCRIPTION
 
 This file configures the *chronyd* daemon. The compiled-in location is
-_@SYSCONFDIR@/chrony.conf_, but other locations can be specified on the
+_@SYSCONFDIR@/chrony.conf_. Other locations can be specified on the
 *chronyd* command line with the *-f* option.
 
 Each directive in the configuration file is placed on a separate line. The
-following sections describe each of the directives in turn. The directives can
-occur in any order in the file and they are not case-sensitive.
+following sections describe each of the directives in turn. The directives are
+not case-sensitive. Generally, the directives can occur in any order in the file
+and if a directive is specified multiple times, only the last one will be
+effective. Exceptions are noted in the descriptions.
 
 The configuration directives can also be specified directly on the *chronyd*
 command line. In this case each argument is parsed as a new line and the
@@ -61,39 +63,43 @@
 synchronise its system time to that of the server, but the server's system time
 will never be influenced by that of a client.
 +
-The *server* directive is immediately followed by either the name of the
-server, or its IP address. The *server* directive supports the following
-options:
+This directive can be used multiple times to specify multiple servers.
++
+The directive is immediately followed by either the name of the
+server, or its IP address. It supports the following options:
 +
 *minpoll* _poll_:::
 This option specifies the minimum interval between requests sent to the server
 as a power of 2 in seconds. For example, *minpoll 5* would mean that the
 polling interval should not drop below 32 seconds. The default is 6 (64
-seconds), the minimum is -4 (1/16th of a second), and the maximum is 24 (6
+seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
 months). Note that intervals shorter than 6 (64 seconds) should generally not
 be used with public servers on the Internet, because it might be considered
-abuse.
+abuse. A sub-second interval will be enabled only when the server is reachable
+and the round-trip delay is shorter than 10 milliseconds, i.e. the server
+should be in a local network.
 *maxpoll* _poll_:::
 This option specifies the maximum interval between requests sent to the server
 as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
 interval should stay at or below 9 (512 seconds). The default is 10 (1024
-seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
+seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
+months).
 *iburst*:::
-With this option, the interval between the first four requests sent to the
-server will be 2 seconds instead of the interval specified by the *minpoll*
-option, which allows *chronyd* to make the first update of the clock shortly
-after start.
+With this option, *chronyd* will start with a burst of 4-8 requests in order to
+make the first update of the clock sooner. It will also repeat the burst every
+time the source is switched from the offline state to online with the
+<<chronyc.adoc#online,*online*>> command in *chronyc*.
 *burst*:::
-With this option, *chronyd* will shorten the interval between up to four
-requests to 2 seconds when it cannot get a good measurement from the server.
-The number of requests in the burst is limited by the current polling interval
-to keep the average interval at or above the minimum interval, i.e. the current
-interval needs to be at least two times longer than the minimum interval in
-order to allow a burst with two requests.
+With this option, *chronyd* will send a burst of up to 4 requests when it
+cannot get a good measurement from the
+server. The number of requests in the burst is limited by the current polling
+interval to keep the average interval at or above the minimum interval, i.e.
+the current interval needs to be at least two times longer than the minimum
+interval in order to allow a burst with two requests.
 *key* _ID_:::
 The NTP protocol supports a message authentication code (MAC) to prevent
 computers having their system time upset by rogue packets being sent to them.
-The MAC is generated as a function of a password specified in the key file,
+The MAC is generated as a function of a key specified in the key file,
 which is specified by the <<keyfile,*keyfile*>> directive.
 +
 The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
@@ -104,6 +110,18 @@
 If the server is running *ntpd* and the output size of the hash function used
 by the key is longer than 160 bits (e.g. SHA256), the *version* option needs to
 be set to 4 for compatibility.
+*nts*:::
+This option enables authentication using the Network Time Security (NTS)
+mechanism. Unlike with the *key* option, the server and client do not need to
+share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using
+the Transport Layer Security (TLS) protocol to get the keys and cookies
+required by NTS for authentication of NTP packets.
+*certset* _ID_:::
+This option specifies which set of trusted certificates should be used to verify
+the server's certificate when the *nts* option is enabled. Sets of certificates
+can be specified with the <<ntstrustedcerts,*ntstrustedcerts*>> directive. The
+default set is 0, which by default contains certificates of the system's
+default trusted certificate authorities.
 *maxdelay* _delay_:::
 *chronyd* uses the network round-trip delay to the server to determine how
 accurate a particular measurement is likely to be. Long round-trip delays
@@ -113,9 +131,9 @@
 For small variations in the round-trip delay, *chronyd* uses a weighting scheme
 when processing the measurements. However, beyond a certain level of delay the
 measurements are likely to be so corrupted as to be useless. (This is
-particularly so on dial-up or other slow links, where a long delay probably
-indicates a highly asymmetric delay caused by the response waiting behind a lot
-of packets related to a download of some sort).
+particularly so on wireless networks and other slow links, where a long delay
+probably indicates a highly asymmetric delay caused by the response waiting
+behind a lot of packets related to a download of some sort).
 +
 If the user knows that round trip delays above a certain level should cause the
 measurement to be ignored, this level can be defined with the *maxdelay*
@@ -126,7 +144,7 @@
 This option is similar to the *maxdelay* option above. *chronyd* keeps a record
 of the minimum round-trip delay amongst the previous measurements that it has
 buffered. If a measurement has a round trip delay that is greater than the
-maxdelayratio times the minimum delay, it will be rejected.
+specified ratio times the minimum delay, it will be rejected.
 *maxdelaydevratio* _ratio_:::
 If a measurement has a ratio of the increase in the round-trip delay from the
 minimum delay amongst the previous measurements to the standard deviation of
@@ -158,19 +176,23 @@
 *maxsamples* _samples_:::
 Set the maximum number of samples kept for this source. This overrides the
 <<maxsamples,*maxsamples*>> directive.
+*filter* _samples_:::
+This option enables a median filter to reduce noise in NTP measurements. The
+filter will reduce the specified number of samples to a single sample. It is
+intended to be used with very short polling intervals in local networks where
+it is acceptable to generate a lot of NTP traffic.
 *offline*:::
 If the server will not be reachable when *chronyd* is started, the *offline*
 option can be specified. *chronyd* will not try to poll the server until it is
 enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
 *chronyc*).
 *auto_offline*:::
-With this option, the server will be assumed to have gone offline when two
-requests have been sent to it without receiving a response. This option avoids
+With this option, the server will be assumed to have gone offline when sending
+a request fails, e.g. due to a missing route to the network. This option avoids
 the need to run the <<chronyc.adoc#offline,*offline*>> command from *chronyc*
-when disconnecting the network link, if it is safe to assume that the requests
-and responses will not be dropped in the network, e.g. in a trusted local
-network. (It will still be necessary to use the <<chronyc.adoc#online,*online*>>
-command when the link has been established, to enable measurements to start.)
+when disconnecting the network link. (It will still be necessary to use the
+<<chronyc.adoc#online,*online*>> command when the link has been established, to
+enable measurements to start.)
 *prefer*:::
 Prefer this source over sources without the *prefer* option.
 *noselect*:::
@@ -187,14 +209,14 @@
 order to improve the accuracy of the clock. They can be selected and used for
 synchronisation only if they agree with the trusted and required source.
 *xleave*:::
-This option enables an interleaved mode which allows the server or the peer to
-send transmit timestamps captured after the actual transmission (e.g. when the
-server or the peer is running *chronyd* with software (kernel) or hardware
-timestamping). This can significantly improve the accuracy of the measurements.
+This option enables the interleaved mode of NTP. It enables the server to
+respond with more accurate transmit timestamps (e.g. kernel or hardware
+timestamps), which cannot be contained in the transmitted packet itself and
+need to refer to a previous packet instead. This can significantly improve the
+accuracy and stability of the measurements.
 +
 The interleaved mode is compatible with servers that support only the basic
-mode, but peers must both support and have enabled the interleaved mode,
-otherwise the synchronisation will work only in one direction. Note that even
+mode. Note that even
 servers that support the interleaved mode might respond in the basic mode as
 the interleaved mode requires the servers to keep some state for each client
 and the state might be dropped when there are too many clients (e.g.
@@ -214,6 +236,9 @@
 This option allows the UDP port on which the server understands NTP requests to
 be specified. For normal servers this option should not be required (the
 default is 123, the standard NTP port).
+*ntsport* _port_:::
+This option specifies the TCP port on which the server is listening for NTS-KE
+connections when the *nts* option is enabled. The default is 4460.
 *presend* _poll_:::
 If the timing measurements being made by *chronyd* are the only network data
 passing between two computers, you might find that some measurements are badly
@@ -236,23 +261,52 @@
 will be sent to the server a short time (2 seconds) before making the actual
 measurement.
 +
-The *presend* option cannot be used in the *peer* directive. If it is used
-with the *xleave* option, *chronyd* will send two extra packets instead of one.
+If the *presend* option is used together with the *xleave* option, *chronyd*
+will send two extra packets instead of one.
 *minstratum* _stratum_:::
 When the synchronisation source is selected from available sources, sources
 with lower stratum are normally slightly preferred. This option can be used to
 increase stratum of the source to the specified minimum, so *chronyd* will
-avoid selecting that source. This is useful with low stratum sources that are
+avoid selecting that source. This is useful with low-stratum sources that are
 known to be unreliable or inaccurate and which should be used only when other
 sources are unreachable.
 *version* _version_:::
 This option sets the NTP version of packets sent to the server. This can be
 useful when the server runs an old NTP implementation that does not respond to
-requests using a newer version. The default version depends on whether a key is
-specified by the *key* option and which authentication hash function the key
-is using. If the output size of the hash function is longer than 160 bits, the
-default version is 3 for compatibility with older *chronyd* servers. Otherwise,
+requests using a newer version. The default version depends on other options.
+If the *extfield* or *xleave* option is used, the default version is 4. If
+those options are not used and the *key* option specifies a key using a hash
+function with output size longer than 160 bits (e.g. SHA256), the default
+version is 3 for compatibility with older *chronyd* servers. In other cases,
 the default version is 4.
+*copy*:::
+This option specifies that the server and client are closely related, their
+configuration does not allow a synchronisation loop to form between them, and
+the client is allowed to assume the reference ID and stratum of the server.
+This is useful when multiple instances of `chronyd` are running on one computer
+(e.g. for security or performance reasons), one primarily operating as a client
+to synchronise the system clock and other instances started with the *-x*
+option to operate as NTP servers for other computers with their NTP clocks
+synchronised to the first instance.
+*extfield* _type_:::
+This option enables an NTPv4 extension field specified by its type as a
+hexadecimal number. It will be included in requests sent to the server and
+processed in received responses if the server supports it. Note that some
+server implementations do not respond to requests containing an unknown
+extension field (*chronyd* as a server responded to such requests since
+version 2.0).
++
+The following extension field can be enabled by this option:
++
+_F323_::::
+This is an experimental extension field for some improvements that were
+proposed for the next version of the NTP protocol (NTPv5). The field contains
+root delay and dispersion in higher resolution and a monotonic receive
+timestamp, which enables a frequency transfer between the server and client. It
+can significantly improve stability of the synchronization. Generally, it
+should be expected to work only between servers and clients running the same
+version of *chronyd*.
+{blank}:::
 
 [[pool]]*pool* _name_ [_option_]...::
 The syntax of this directive is similar to that for the <<server,*server*>>
@@ -260,18 +314,22 @@
 a single NTP server. The pool name is expected to resolve to multiple addresses
 which might change over time.
 +
+This directive can be used multiple times to specify multiple pools.
++
 All options valid in the <<server,*server*>> directive can be used in this
 directive too. There is one option specific to the *pool* directive:
-*maxsources* sets the maximum number of sources that can be used from the pool,
-the default value is 4.
 +
-On start, when the pool name is resolved, *chronyd* will add up to 16 sources,
-one for each resolved address. When the number of sources from which at least
-one valid reply was received reaches the number specified by the *maxsources*
-option, the other sources will be removed. When a pool source is unreachable,
+*maxsources* _sources_:::
+This option sets the desired number of sources to be used from the pool.
+*chronyd* will repeatedly try to resolve the name until it gets this number of
+sources responding to requests. The default value is 4 and the maximum value is
+16.
++
+{blank}::
+When an NTP source is unreachable,
 marked as a falseticker, or has a distance larger than the limit set by the
 <<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
-source with a newly resolved address from the pool.
+source with a newly resolved address of the name.
 +
 An example of the *pool* directive is
 +
@@ -288,6 +346,14 @@
 ephemeral symmetric associations and does not need to be configured with an
 address of this host. *chronyd* does not support ephemeral associations.
 +
+This directive can be used multiple times to specify multiple peers.
++
+The following options of the *server* directive do not work in the *peer*
+directive: *iburst*, *burst*, *nts*, *presend*, *copy*.
++
+When using the *xleave* option, both peers must support and have enabled the
+interleaved mode, otherwise the synchronisation will work in one direction
+only.
 When a key is specified by the *key* option to enable authentication, both
 peers must use the same key and the same key number.
 +
@@ -310,19 +376,8 @@
 <<server,*server*>> directive on both hosts) instead.
 
 [[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...::
-In normal operation, *chronyd* slews the time when it needs to adjust the
-system clock. For example, to correct a system clock which is 1 second slow,
-*chronyd* slightly increases the amount by which the system clock is advanced
-on each clock interrupt, until the error is removed. Note that at no time does
-time run backwards with this method.
-+
-On most Unix systems it is not desirable to step the system clock, because many
-programs rely on time advancing monotonically forwards.
-+
-When the *chronyd* daemon is initially started, it is possible that the system
-clock is considerably in error. Attempting to correct such an error by slewing
-might not be sensible, since it might take several hours to correct the error by
-this means.
+(This directive is deprecated in favour of the <<makestep,*makestep*>>
+directive.)
 +
 The purpose of the *initstepslew* directive is to allow *chronyd* to make a
 rapid measurement of the system clock error at boot time, and to correct the
@@ -343,36 +398,39 @@
 An example of the use of the directive is:
 +
 ----
-initstepslew 30 foo.example.net bar.example.net
+initstepslew 30 foo.example.net bar.example.net baz.example.net
 ----
 +
-where 2 NTP servers are used to make the measurement. The _30_ indicates that
+where 3 NTP servers are used to make the measurement. The _30_ indicates that
 if the system's error is found to be 30 seconds or less, a slew will be used to
 correct it; if the error is above 30 seconds, a step will be used.
 +
 The *initstepslew* directive can also be used in an isolated LAN environment,
 where the clocks are set manually. The most stable computer is chosen as the
-master, and the other computers are slaved to it. If each of the slaves is
-configured with the <<local,*local*>> directive, the master can be set up with
-an *initstepslew* directive which references some or all of the slaves. Then,
-if the master machine has to be rebooted, the slaves can be relied on to act
-analogously to a flywheel and preserve the time for a short period while the
-master completes its reboot.
+primary server and the other computers are its clients. If each of the clients
+is configured with the <<local,*local*>> directive, the server can be set up
+with an *initstepslew* directive which references some or all of the clients.
+Then, if the server machine has to be rebooted, the clients can be relied on to
+act analogously to a flywheel and preserve the time for a short period while
+the server completes its reboot.
 +
 The *initstepslew* directive is functionally similar to a combination of the
 <<makestep,*makestep*>> and <<server,*server*>> directives with the *iburst*
 option. The main difference is that the *initstepslew* servers are used only
 before normal operation begins and that the foreground *chronyd* process waits
-for *initstepslew* to finish before exiting. This is useful to prevent programs
-started in the boot sequence after *chronyd* from reading the clock before it
-has been stepped.
+for *initstepslew* to finish before exiting. This prevent programs started in
+the boot sequence after *chronyd* from reading the clock before it has been
+stepped. With the *makestep* directive, the
+<<chronyc.adoc#waitsync,*waitsync*>> command of *chronyc* can be used instead.
 
-[[refclock]]*refclock* _driver_ _parameter_[:__option__,...] [_option_]...::
+[[refclock]]*refclock* _driver_ _parameter_[:__option__]... [_option_]...::
 The *refclock* directive specifies a hardware reference clock to be used as a
 time source. It has two mandatory parameters, a driver name and a
 driver-specific parameter. The two parameters are followed by zero or more
 refclock options. Some drivers have special options, which can be appended to
-the driver-specific parameter (separated by the *:* and *,* characters).
+the driver-specific parameter using the *:* character.
++
+This directive can be used multiple times to specify multiple reference clocks.
 +
 There are four drivers included in *chronyd*:
 +
@@ -389,7 +447,7 @@
 synchronisation. With this option, it will use clear events (falling edge)
 instead.
 +
-:::
+{blank}:::
 Examples:
 +
 ----
@@ -408,7 +466,7 @@
 This option specifies the permissions of the shared memory segment created by
 *chronyd*. They are specified as a numeric mode. The default value is 0600
 (read-write access for owner only).
-:::
+{blank}:::
 +
 Examples:
 +
@@ -462,17 +520,17 @@
 This option enables timestamping of clear events (falling edge) instead of
 assert events (rising edge) in the PPS mode. This may not work with some
 clocks.
-:::
+{blank}:::
 +
 Examples:
 +
 ----
 refclock PHC /dev/ptp0 poll 0 dpoll -2 offset -37
 refclock PHC /dev/ptp1:nocrossts poll 3 pps
-refclock PHC /dev/ptp2:extpps,pin=1 width 0.2 poll 2
+refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
 ----
 +
-::
+{blank}::
 The *refclock* directive supports the following options:
 +
 *poll* _poll_:::
@@ -586,12 +644,13 @@
 time to be provided.)
 
 [[acquisitionport]]*acquisitionport* _port_::
-By default, *chronyd* uses a separate client socket for each configured server
-and their source port is chosen arbitrarily by the operating system. However,
-you can use the *acquisitionport* directive to explicitly specify a port and
-use only one socket (per IPv4 or IPv6 address family) for all configured servers.
-This can be useful for getting through some firewalls. If set to 0, the source
-port of the socket will be chosen arbitrarily.
+By default, *chronyd* as an NTP client opens a new socket for each request with
+the source port chosen randomly by the operating system. The *acquisitionport*
+directive can be used to specify the source port and use only one socket (per
+IPv4 or IPv6 address family) for all configured servers. This can be useful for
+getting through some firewalls. It should not be used if not necessary as there
+is a small impact on security of the client. If set to 0, the source port of
+the permanent socket will be chosen randomly by the operating system.
 +
 It can be set to the same port as is used by the NTP server (which can be
 configured with the <<port,*port*>> directive) to use only one socket for all
@@ -607,14 +666,40 @@
 You could then persuade the firewall administrator to open that port.
 
 [[bindacqaddress]]*bindacqaddress* _address_::
-The *bindacqaddress* directive sets the network interface to which
-*chronyd* will bind its NTP client sockets. The syntax is similar to the
-<<bindaddress,*bindaddress*>> and <<bindcmdaddress,*bindcmdaddress*>>
+The *bindacqaddress* directive specifies a local IP address to which
+*chronyd* will bind its NTP and NTS-KE client sockets. The syntax is similar to
+the <<bindaddress,*bindaddress*>> and <<bindcmdaddress,*bindcmdaddress*>>
 directives.
 +
 For each of the IPv4 and IPv6 protocols, only one *bindacqaddress* directive
 can be specified.
 
+[[bindacqdevice]]*bindacqdevice* _interface_::
+The *bindacqdevice* directive binds the client sockets to a network device
+specified by the interface name. This can be useful when the local address is
+dynamic, or to enable an NTP source specified with a link-local IPv6 address.
+This directive can specify only one interface and it is supported on Linux
+only.
++
+An example of the directive is:
++
+----
+bindacqdevice eth0
+----
+
+[[dscp]]*dscp* _point_::
+The *dscp* directive sets the Differentiated Services Code Point (DSCP) in
+transmitted NTP packets to the specified value. It can improve stability of NTP
+measurements in local networks where switches or routers are configured to
+prioritise forwarding of packets with specific DSCP values. The default value
+is 0 and the maximum value is 63.
++
+An example of the directive (setting the Expedited Forwarding class) is:
++
+----
+dscp 46
+----
+
 [[dumpdir]]*dumpdir* _directory_::
 To compute the rate of gain or loss of time, *chronyd* has to store a
 measurement history for each of the time sources it uses.
@@ -632,6 +717,12 @@
 directory where the measurement histories are saved when *chronyd* exits,
 or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
 +
+If the directory does not exist, it will be created automatically.
++
+The *-r* option of *chronyd* enables loading of the dump files on start. All
+dump files found in the directory will be removed after start, even if the *-r*
+option is not present.
++
 An example of the directive is:
 +
 ----
@@ -648,15 +739,172 @@
 individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
 directives. The default value is 0, which disables the configurable limit. The
 useful range is 4 to 64.
++
+As a special case, setting *maxsamples* to 1 disables frequency tracking in
+order to make the sources immediately selectable with only one sample. This can
+be useful when *chronyd* is started with the *-q* or *-Q* option.
 
 [[minsamples]]*minsamples* _samples_::
 The *minsamples* directive sets the default minimum number of samples that
 *chronyd* should keep for each source. This setting can be overridden for
 individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
 directives. The default value is 6. The useful range is 4 to 64.
++
+Forcing *chronyd* to keep more samples than it would normally keep reduces
+noise in the estimated frequency and offset, but slows down the response to
+changes in the frequency and offset of the clock. The offsets in the
+<<chronyc.adoc#tracking,*tracking*>> and
+<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
+_statistics.log_ files) may be smaller than the actual offsets.
+
+[[ntsdumpdir1]]*ntsdumpdir* _directory_::
+This directive specifies a directory for the client to save NTS cookies it
+received from the server in order to avoid making an NTS-KE request when
+*chronyd* is started again. The cookies are saved separately for each NTP
+source in files named by the IP address of the NTS-KE server (e.g.
+_1.2.3.4.nts_). By default, the client does not save the cookies.
++
+If the directory does not exist, it will be created automatically.
++
+An example of the directive is:
++
+----
+ntsdumpdir @CHRONYVARDIR@
+----
++
+This directory is used also by the <<ntsdumpdir2,NTS server>> to save keys.
+
+[[ntsrefresh]]*ntsrefresh* _interval_::
+This directive specifies the maximum interval between NTS-KE handshakes (in
+seconds) in order to refresh the keys authenticating NTP packets. The default
+value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years).
+
+[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
+This directive specifies a file or directory containing certificates (in the
+PEM format) of trusted certificate authorities (CA) which can be used to
+verify certificates of NTS servers.
++
+The optional _set-ID_ argument is a number in the range 0 through 2^32-1, which
+selects the set of certificates where certificates from the specified file
+or directory are added. The default ID is 0, which is a set containing the
+system's default trusted CAs (unless the *nosystemcert* directive is present).
+All other sets are empty by default. A set of certificates can be selected for
+verification of an NTS server by the *certset* option in the *server* or *pool*
+directive.
++
+This directive can be used multiple times to specify one or more sets of
+trusted certificates, each containing certificates from one or more files
+and/or directories.
++
+It is not necessary to restart *chronyd* in order to reload the certificates if
+they change (e.g. after a renewal).
++
+An example is:
++
+----
+ntstrustedcerts /etc/pki/nts/foo.crt
+ntstrustedcerts 1 /etc/pki/nts/bar.crt
+ntstrustedcerts 1 /etc/pki/nts/baz.crt
+ntstrustedcerts 2 /etc/pki/nts/qux.crt
+----
+
+[[nosystemcert]]*nosystemcert*::
+This directive disables the system's default trusted CAs. Only certificates
+specified by the *ntstrustedcerts* directive will be trusted.
+
+[[nocerttimecheck]]*nocerttimecheck* _limit_::
+This directive disables the checks of the activation and expiration times of
+certificates for the specified number of clock updates. It allows the NTS
+authentication mechanism to be used on computers which start with wrong time
+(e.g. due to not having an RTC or backup battery). Disabling the time checks
+has important security implications and should be used only as a last resort,
+preferably with a minimal number of trusted certificates. The default value is
+0, which means the time checks are always enabled.
++
+An example of the directive is:
++
+----
+nocerttimecheck 1
+----
++
+This would disable the time checks until the clock is updated for the first
+time, assuming the first update corrects the clock and later checks can work
+with correct time.
 
 === Source selection
 
+[[authselectmode]]*authselectmode* _mode_::
+NTP sources can be specified with the *key* or *nts* option to enable
+authentication to limit the impact of man-in-the-middle attacks. The
+attackers can drop or delay NTP packets (up to the *maxdelay* and
+<<maxdistance,*maxdistance*>> limits), but they cannot modify the timestamps
+contained in the packets. The attack can cause only a limited slew or step, and
+also cause the clock to run faster or slower than real time (up to double of
+the <<maxdrift,*maxdrift*>> limit).
++
+When authentication is enabled for an NTP source, it is important to disable
+unauthenticated NTP sources which could be exploited in the attack, e.g. if
+they are not reachable only over a trusted network. Alternatively, the source
+selection can be configured with the *require* and *trust* options to
+synchronise to the unauthenticated sources only if they agree with the
+authenticated sources and might have a positive impact on the accuracy of the
+clock. Note that in this case the impact of the attack is higher. The attackers
+cannot cause an arbitrarily large step or slew, but they have more control over
+the frequency of the clock and can cause *chronyd* to report false information,
+e.g. a significantly smaller root delay and dispersion.
++
+This directive determines the default selection options for authenticated and
+unauthenticated sources in order to simplify the configuration with the
+configuration file and *chronyc* commands. It sets a policy for authentication.
++
+Sources specified with the *noselect* option are ignored (not counted as either
+authenticated or unauthenticated), and they always have only the selection
+options specified in the configuration.
++
+There are four modes:
++
+*require*:::
+Authentication is strictly required for NTP sources in this mode. If any
+unauthenticated NTP sources are specified, they will automatically get the
+*noselect* option to prevent them from being selected for synchronisation.
+*prefer*:::
+In this mode, authentication is optional and preferred. If it is enabled for at
+least one NTP source, all unauthenticated NTP sources will get the *noselect*
+option.
+*mix*:::
+In this mode, authentication is optional and synchronisation to a mix of
+authenticated and unauthenticated NTP sources is allowed. If both authenticated
+and unauthenticated NTP sources are specified, all authenticated NTP sources
+and reference clocks will get the *require* and *trust* options to prevent
+synchronisation to unauthenticated NTP sources if they do not agree with a
+majority of the authenticated sources and reference clocks. This is the default
+mode.
+*ignore*:::
+In this mode, authentication is ignored in the source selection. All sources
+will have only the selection options that were specified in the configuration
+file, or *chronyc* command. This was the behaviour of *chronyd* in versions
+before 4.0.
+{blank}::
++
+As an example, the following configuration using the default *mix* mode:
++
+----
+server foo.example.net nts
+server bar.example.net nts
+server baz.example.net
+refclock SHM 0
+----
++
+is equivalent to the following configuration using the *ignore* mode:
++
+----
+authselectmode ignore
+server foo.example.net nts require trust
+server bar.example.net nts require trust
+server baz.example.net
+refclock SHM 0 require trust
+----
+
 [[combinelimit]]*combinelimit* _limit_::
 When *chronyd* has multiple sources available for synchronisation, it has to
 select one source as the synchronisation source. The measured offsets and
@@ -667,17 +915,19 @@
 algorithm. Their synchronisation distance has to be shorter than the distance
 of the selected source multiplied by the value of the limit. Also, their
 measured frequencies have to be close to the frequency of the selected source.
+If the selected source was specified with the *prefer* option, it can be
+combined only with other sources specified with this option.
 +
 By default, the limit is 3. Setting the limit to 0 effectively disables the
 source combining algorithm and only the selected source will be used to control
 the system clock.
 
 [[maxdistance]]*maxdistance* _distance_::
-The *maxdistance* directive sets the maximum allowed root distance of the
-sources to not be rejected by the source selection algorithm. The distance
-includes the accumulated dispersion, which might be large when the source is no
-longer synchronised, and half of the total round-trip delay to the primary
-source.
+The *maxdistance* directive sets the maximum root distance of a source to be
+acceptable for synchronisation of the clock. Sources that have a distance
+larger than the specified distance will be rejected. The distance estimates the
+maximum error of the source. It includes the root dispersion and half of the
+root delay (round-trip time) accumulated on the path to the primary source.
 +
 By default, the maximum root distance is 3 seconds.
 +
@@ -721,6 +971,27 @@
 
 === System clock
 
+[[clockprecision]]*clockprecision* _precision_::
+The *clockprecision* directive specifies the precision of the system clock (in
+seconds). It is used by *chronyd* to estimate the minimum noise in NTP
+measurements and randomise low-order bits of timestamps in NTP responses. By
+default, the precision is measured on start as the minimum time to read the
+clock.
++
+The measured value works well in most cases. However, it generally
+overestimates the precision and it can be sensitive to the CPU speed, which can
+change over time to save power. In some cases with a high-precision clocksource
+(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the
+precision on the server to a smaller value can improve stability of clients'
+NTP measurements. The server's precision is reported on clients by the
+<<chronyc.adoc#ntpdata,*ntpdata*>> command.
++
+An example setting the precision to 8 nanoseconds is:
++
+----
+clockprecision 8e-9
+----
+
 [[corrtimeratio]]*corrtimeratio* _ratio_::
 When *chronyd* is slewing the system clock to correct an offset, the rate at
 which it is slewing adds to the frequency error of the clock. On all supported
@@ -828,8 +1099,12 @@
 *ignore*:::
 No correction is applied to the clock for the leap second. The clock will be
 corrected later in normal operation when new measurements are made and the
-estimated offset includes the one second error.
-::
+estimated offset includes the one second error. This option is particularly
+useful when multiple *chronyd* instances are running on the system, one
+controlling the system clock and others started with the *-x* option, which
+should rely on the first instance to correct the system clock and ignore it for
+the correction of their own NTP clock running on top of the system clock.
+{blank}::
 +
 When serving time to NTP clients that cannot be configured to correct their
 clocks for a leap second by slewing, or to clients that would correct at
@@ -838,7 +1113,7 @@
 enable a server leap smear.
 +
 When smearing a leap second, the leap status is suppressed on the server and
-the served time is corrected slowly be slewing instead of stepping. The clients
+the served time is corrected slowly by slewing instead of stepping. The clients
 do not need any special configuration as they do not know there is any leap
 second and they follow the server time which eventually brings them back to
 UTC. Care must be taken to ensure they use only NTP servers which smear the
@@ -852,7 +1127,7 @@
 ----
 leapsecmode slew
 maxslewrate 1000
-smoothtime 400 0.001 leaponly
+smoothtime 400 0.001024 leaponly
 ----
 +
 The first directive is necessary to disable the clock step which would reset
@@ -860,17 +1135,23 @@
 local clock to 1000 ppm, which improves the stability of the smoothing process
 when the local correction starts and ends. The third directive enables the
 server time smoothing process. It will start when the clock gets to 00:00:00
-UTC and it will take 17 hours 34 minutes to finish. The frequency offset will
-be changing by 0.001 ppm per second and will reach a maximum of 31.623 ppm. The
-*leaponly* option makes the duration of the leap smear constant and allows the
-clients to safely synchronise with multiple identically configured leap
-smearing servers.
+UTC and it will take 62500 seconds (about 17.36 hours) to finish. The frequency
+offset will be changing by 0.001024 ppm per second and will reach a maximum of
+32 ppm in 31250 seconds. The *leaponly* option makes the duration of the leap
+smear constant and allows the clients to safely synchronise with multiple
+identically configured leap smearing servers.
++
+The duration of the leap smear can be calculated from the specified wander as
++
+----
+duration = sqrt(4 / wander)
+----
 
 [[leapsectz]]*leapsectz* _timezone_::
-This directive specifies a timezone in the system tz database which *chronyd*
-can use to determine when will the next leap second occur and what is the
-current offset between TAI and UTC. It will periodically check if 23:59:59 and
-23:59:60 are valid times in the timezone. This typically works with the
+This directive specifies a timezone in the system timezone database which
+*chronyd* can use to determine when will the next leap second occur and what is
+the current offset between TAI and UTC. It will periodically check if 23:59:59
+and 23:59:60 are valid times in the timezone. This normally works with the
 _right/UTC_ timezone.
 +
 When a leap second is announced, the timezone needs to be updated at least 12
@@ -907,16 +1188,17 @@
 [[makestep]]*makestep* _threshold_ _limit_::
 Normally *chronyd* will cause the system to gradually correct any time offset,
 by slowing down or speeding up the clock as required. In certain situations,
-the system clock might be so far adrift that this slewing process would take a
-very long time to correct the system clock.
+e.g. when *chronyd* is initially started, the system clock might be so far
+adrift that this slewing process would take a very long time to correct the
+system clock.
 +
 This directive forces *chronyd* to step the system clock if the adjustment is
 larger than a threshold value, but only if there were no more clock updates
-since *chronyd* was started than a specified limit (a negative value can be
-used to disable the limit).
+since *chronyd* was started than the specified limit. A negative value disables
+the limit.
 +
-This is particularly useful when using reference clocks, because the
-<<initstepslew,*initstepslew*>> directive works only with NTP sources.
+On most systems it is desirable to step the system clock only on boot, before
+starting programs that rely on time advancing monotonically forwards.
 +
 An example of the use of this directive is:
 +
@@ -960,7 +1242,7 @@
 system clock. It limits the frequency adjustment that *chronyd* is allowed to
 use to correct the measured drift. It is an additional limit to the maximum
 adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
-on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
+on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on illumos).
 +
 By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
 limited by the system driver rather than this directive.
@@ -978,8 +1260,8 @@
 estimate might be so unreliable that it should not be used. By default, the
 threshold is 1000 ppm.
 +
-Typical values for _skew-in-ppm_ might be 100 for a dial-up connection to
-servers over a phone line, and 5 or 10 for a computer on a LAN.
+Typical values for _skew-in-ppm_ might be 100 for NTP sources polled over a
+wireless network, and 10 or smaller for sources on a local wired network.
 +
 It should be noted that this is not the only means of protection against using
 unreliable estimates. At all times, *chronyd* keeps track of both the estimated
@@ -999,14 +1281,10 @@
 +
 For each system there is a maximum frequency offset of the clock that can be set
 by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
-is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
+is 5000 ppm, and on illumos it is 32500 ppm. Also, due to a kernel limitation,
 setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
 ppm and 5000 ppm will effectively set it to 500 ppm.
 +
-In early beta releases of macOS 13 this capability is disabled because of a
-system kernel bug. When the kernel bug is fixed, chronyd will detect this and
-re-enable the capability (see above limitations) with no recompilation required.
-+
 By default, the maximum slew rate is set to 83333.333 ppm (one twelfth).
 
 [[tempcomp]]
@@ -1034,7 +1312,7 @@
 The frequency compensation is calculated (in ppm) as
 +
 ----
-k0 + (T - T0) * k1 + (T - T0)^2 * k2
+comp = k0 + (T - T0) * k1 + (T - T0)^2 * k2
 ----
 +
 The result has to be between -10 ppm and 10 ppm, otherwise the measurement is
@@ -1085,38 +1363,34 @@
 
 [[allow]]*allow* [*all*] [_subnet_]::
 The *allow* directive is used to designate a particular subnet from which NTP
-clients are allowed to access the computer as an NTP server.
+clients are allowed to access the computer as an NTP server. It also controls
+access of NTS-KE clients when NTS is enabled on the server.
 +
 The default is that no clients are allowed access, i.e. *chronyd* operates
 purely as an NTP client. If the *allow* directive is used, *chronyd* will be
 both a client of its servers, and a server to other clients.
 +
+This directive can be used multiple times.
++
 Examples of the use of the directive are as follows:
 +
 ----
 allow 1.2.3.4
-allow 1.2
+allow 3.4.5.0/24
 allow 3.4.5
-allow 6.7.8/22
-allow 6.7.8.9/22
 allow 2001:db8::/32
 allow 0/0
 allow ::/0
 allow
 ----
 +
-The first directive allows a node with IPv4 address _1.2.3.4_ to be an NTP
-client of this computer.
-The second directive allows any node with an IPv4 address of the form _1.2.x.y_
-(with _x_ and _y_ arbitrary) to be an NTP client of this computer. Likewise,
-the third directive allows any node with an IPv4 address of the form _3.4.5.x_
-to have client NTP access. The fourth and fifth forms allow access from any
-node with an IPv4 address of the form _6.7.8.x_, _6.7.9.x_, _6.7.10.x_ or
-_6.7.11.x_ (with _x_ arbitrary), i.e. the value 22 is the number of bits
-defining the specified subnet. In the fifth form, the final byte is ignored.
-The sixth form is used for IPv6 addresses. The seventh and eighth forms allow
-access by any IPv4 and IPv6 node respectively. The ninth forms allows access by
-any node (IPv4 or IPv6).
+The first directive allows access from an IPv4 address. The second directive
+allows access from all computers in an IPv4 subnet specified in the CIDR
+notation. The third directive specifies the same subnet using a simpler
+notation where the prefix length is determined by the number of dots. The
+fourth directive specifies an IPv6 subnet. The fifth and sixth directives allow
+access from all IPv4 and IPv6 addresses respectively. The seventh directive
+allows access from all addresses (both IPv4 or IPv6).
 +
 A second form of the directive, *allow all*, has a greater effect, depending on
 the ordering of directives in the configuration file. To illustrate the effect,
@@ -1124,32 +1398,43 @@
 +
 ----
 allow 1.2.3.4
-deny 1.2.3
-allow 1.2
+deny 1.2.3.0/24
+allow 1.2.0.0/16
 ----
 +
 and
 +
 ----
 allow 1.2.3.4
-deny 1.2.3
-allow all 1.2
+deny 1.2.3.0/24
+allow all 1.2.0.0/16
 ----
 +
 In the first example, the effect is the same regardless of what order the three
-directives are given in. So the _1.2.x.y_ subnet is allowed access, except for
-the _1.2.3.x_ subnet, which is denied access, however the host _1.2.3.4_ is
-allowed access.
+directives are given in. So the _1.2.0.0/16_ subnet is allowed access, except
+for the _1.2.3.0/24_ subnet, which is denied access, however the host _1.2.3.4_
+is allowed access.
 +
-In the second example, the *allow all 1.2* directives overrides the effect of
-_any_ previous directive relating to a subnet within the specified subnet.
-Within a configuration file this capability is probably rather moot; however,
-it is of greater use for reconfiguration at run-time via *chronyc* with the
-<<chronyc.adoc#allow,*allow all*>> command.
+In the second example, the *allow all 1.2.0.0/16* directive overrides the
+effect of _any_ previous directive relating to a subnet within the specified
+subnet. Within a configuration file this capability is probably rather moot;
+however, it is of greater use for reconfiguration at run-time via *chronyc*
+with the <<chronyc.adoc#allow,*allow all*>> command.
 +
-The directive allows a hostname to be specified instead of an IP address, but
-the name must be resolvable when *chronyd* is started (i.e. *chronyd* needs
-to be started when the network is already up and DNS is working).
+The rules are internally represented as a tree of tables with one level per
+four bits of the IPv4 or IPv6 address. The order of the *allow* and *deny*
+directives matters if they modify the same records of one table, i.e. if one
+subnet is included in the other subnet and their prefix lengths are at the same
+level. For example, _1.2.3.0/28_ and _1.2.3.0/29_ are in different tables, but
+_1.2.3.0/25_ and _1.2.3.0/28_ are in the same table. The configuration can be
+verified for individual addresses with the <<chronyc.adoc#accheck,*accheck*>>
+command in *chronyc*.
++
+A hostname can be specified in the directives instead of the IP address, but
+the name must be resolvable when *chronyd* is started, i.e. the network is
+already up and DNS is working. If the hostname resolves to multiple addresses,
+only the first address (in the order returned by the system resolver) will be
+allowed or denied.
 +
 Note, if the <<initstepslew,*initstepslew*>> directive is used in the
 configuration file, each of the computers listed in that directive must allow
@@ -1157,18 +1442,19 @@
 
 [[deny]]*deny* [*all*] [_subnet_]::
 This is similar to the <<allow,*allow*>> directive, except that it denies NTP
-client access to a particular subnet or host, rather than allowing it.
+and NTS-KE client access to a particular subnet or host, rather than allowing
+it.
 +
-The syntax is identical.
+The syntax is identical and the directive can be used multiple times too.
 +
 There is also a *deny all* directive with similar behaviour to the *allow all*
 directive.
 
 [[bindaddress]]*bindaddress* _address_::
-The *bindaddress* directive binds the socket on which *chronyd* listens for NTP
-requests to a local address of the computer. On systems other than Linux, the
-address of the computer needs to be already configured when *chronyd* is
-started.
+The *bindaddress* directive binds the sockets on which *chronyd* listens for
+NTP and NTS-KE requests to a local address of the computer. On systems other
+than Linux, the address of the computer needs to be already configured when
+*chronyd* is started.
 +
 An example of the use of the directive is:
 +
@@ -1180,23 +1466,36 @@
 directive can be specified. Therefore, it is not useful on computers which
 should serve NTP on multiple network interfaces.
 
+[[binddevice]]*binddevice* _interface_::
+The *binddevice* directive binds the NTP and NTS-KE server sockets to a network
+device specified by the interface name. This directive can specify only one
+interface and it is supported on Linux only.
++
+An example of the directive is:
++
+----
+binddevice eth0
+----
+
 [[broadcast]]*broadcast* _interval_ _address_ [_port_]::
 The *broadcast* directive is used to declare a broadcast address to which
 chronyd should send packets in the NTP broadcast mode (i.e. make *chronyd* act
 as a broadcast server). Broadcast clients on that subnet will be able to
 synchronise.
 +
+This directive can be used multiple times to specify multiple addresses.
++
 The syntax is as follows:
 +
 ----
-broadcast 30 192.168.1.255
-broadcast 60 192.168.2.255 12123
-broadcast 60 ff02::101
+broadcast 32 192.168.1.255
+broadcast 64 192.168.2.255 12123
+broadcast 64 ff02::101
 ----
 +
 In the first example, the destination port defaults to UDP port 123 (the normal NTP
 port). In the second example, the destination port is specified as 12123. The
-first parameter in each case (30 or 60 respectively) is the interval in seconds
+first parameter in each case (32 or 64 respectively) is the interval in seconds
 between broadcast packets being sent. The second parameter in each case is the
 broadcast address to send the packet to. This should correspond to the
 broadcast address of one of the network interfaces on the computer where
@@ -1219,11 +1518,12 @@
 This directive specifies the maximum amount of memory that *chronyd* is allowed
 to allocate for logging of client accesses and the state that *chronyd* as an
 NTP server needs to support the interleaved mode for its clients. The default
-limit is 524288 bytes, which is sufficient for monitoring about four thousand
-clients at the same time.
-+
-In older *chrony* versions if the limit was set to 0, the memory allocation was
-unlimited.
+limit is 524288 bytes, which enables monitoring of up to 4096 IP addresses at
+the same time and holding NTP timestamps for up to 4096 clients using the
+interleaved mode (depending on uniformity of their polling interval). The
+number of addresses and timestamps is always a power of 2. The maximum
+effective value is 2147483648 (2 GB), which corresponds to 16777216 addresses
+and timestamps.
 +
 An example of the use of this directive is:
 +
@@ -1285,16 +1585,18 @@
 that poll more than one server. Each server needs to be configured to poll all
 other servers with the *local* directive. This ensures only the server with the
 smallest reference ID has the local reference active and others are
-synchronised to it. When that server fails, another will take over.
+synchronised to it. If that server stops responding, the server with the second
+smallest reference ID will take over when its local reference mode activates
+(root distance reaches the threshold configured by the *distance* option).
 +
 The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
 *tos orphan* command).
-::
+{blank}::
 +
 An example of the directive is:
 +
 ----
-local stratum 10 orphan
+local stratum 10 orphan distance 0.1
 ----
 
 [[ntpsigndsocket]]*ntpsigndsocket* _directory_::
@@ -1314,6 +1616,100 @@
 ntpsigndsocket /var/lib/samba/ntp_signd
 ----
 
+[[ntsport]]*ntsport* _port_::
+This directive specifies the TCP port on which *chronyd* will provide the NTS
+Key Establishment (NTS-KE) service. The default port is 4460.
++
+The port will be open only when a certificate and key is specified by the
+*ntsservercert* and *ntsserverkey* directives.
+
+[[ntsservercert]]*ntsservercert* _file_::
+This directive specifies a file containing a certificate in the PEM format
+for *chronyd* to operate as an NTS server. The file should also include
+any intermediate certificates that the clients will need to validate the
+server's certificate. The file needs to be readable by the user under which
+*chronyd* is running after dropping root privileges.
++
+This directive can be used multiple times to specify multiple certificates for
+different names of the server.
++
+The files are loaded only once. *chronyd* needs to be restarted in order to
+load a renewed certificate. The <<ntsdumpdir,*ntsdumpdir*>> and
+<<dumpdir,*dumpdir*>> directives with the *-r* option of *chronyd* are
+recommended for a near-seamless server operation.
+
+[[ntsserverkey]]*ntsserverkey* _file_::
+This directive specifies a file containing a private key in the PEM format
+for *chronyd* to operate as an NTS server. The file needs to be readable by
+the user under which *chronyd* is running after dropping root privileges. For
+security reasons, it should not be readable by other users.
++
+This directive can be used multiple times to specify multiple keys. The number
+of keys must be the same as the number of certificates and the corresponding
+files must be specified in the same order.
+
+[[ntsprocesses]]*ntsprocesses* _processes_::
+This directive specifies how many helper processes will *chronyd* operating
+as an NTS server start for handling client NTS-KE requests in order to improve
+performance with multi-core CPUs and multithreading. If set to 0, no helper
+process will be started and all NTS-KE requests will be handled by the main
+*chronyd* process. The default value is 1.
+
+[[maxntsconnections]]*maxntsconnections* _connections_::
+This directive specifies the maximum number of concurrent NTS-KE connections
+per process that the NTS server will accept. The default value is 100. The
+maximum practical value is half of the system *FD_SETSIZE* constant (usually
+1024).
+
+[[ntsdumpdir2]]*ntsdumpdir* _directory_::
+This directive specifies a directory where *chronyd* operating as an NTS server
+can save the keys which encrypt NTS cookies provided to clients. The keys are
+saved to a single file named _ntskeys_. When *chronyd* is restarted, reloading
+the keys allows the clients to continue using old cookies and avoids a storm of
+NTS-KE requests. By default, the server does not save the keys.
++
+An example of the directive is:
++
+----
+ntsdumpdir @CHRONYVARDIR@
+----
++
+This directory is used also by the <<ntsdumpdir1,NTS client>> to save NTS cookies.
+
+[[ntsntpserver]]*ntsntpserver* _hostname_::
+This directive specifies the hostname (as a fully qualified domain name) or
+address of the NTP server(s) which is
+provided in the NTS-KE response to the clients. It allows the NTS-KE server to
+be separated from the NTP server. However, the servers need to share the keys,
+i.e. external key management needs to be enabled by setting
+<<ntsrotate,*ntsrotate*>> to 0. By default, no hostname or address is provided
+to the clients, which means they should use the same server for NTS-KE and NTP.
+
+[[ntsrotate]]*ntsrotate* _interval_::
+This directive specifies the rotation interval (in seconds) of the server key
+which encrypts the NTS cookies. New keys are generated automatically from the
+_/dev/urandom_ device. The server keeps two previous keys to give the clients
+time to get new cookies encrypted by the latest key. The interval is measured
+as the server's operating time, i.e. the actual interval can be longer if
+*chronyd* is not running continuously. The default interval is 604800 seconds
+(1 week). The maximum value is 2^31-1 (68 years).
++
+The automatic rotation of the keys can be disabled by setting *ntsrotate* to 0.
+In this case the keys are assumed to be managed externally. *chronyd* will not
+save the keys to the _ntskeys_ file and will reload the keys from the file when
+the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
+be periodically copied from another server running *chronyd* (which does
+not have *ntsrotate* set to 0) in order to have one or more servers dedicated
+to NTS-KE. The NTS-KE servers need to be configured with the
+<<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
+server.
++
+An example of the directive is:
++
+----
+ntsrotate 2592000
+----
+
 [[port]]*port* _port_::
 This option allows you to configure the port on which *chronyd* will listen for
 NTP requests. The port will be open only when an address is allowed by the
@@ -1340,20 +1736,20 @@
 The *ratelimit* directive supports a number of options (which can be defined
 in any order):
 +
-*interval*:::
+*interval* _interval_:::
 This option sets the minimum interval between responses. It is defined as a
 power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
 is -19 (524288 packets per second) and the maximum value is 12 (one packet per
 4096 seconds). Note that with values below -4 the rate limiting is coarse
 (responses are allowed in bursts, even if the interval between them is shorter
 than the specified interval).
-*burst*:::
+*burst* _responses_:::
 This option sets the maximum number of responses that can be sent in a burst,
 temporarily exceeding the limit specified by the *interval* option. This is
 useful for clients that make rapid measurements on start (e.g. *chronyd* with
 the *iburst* option). The default value is 8. The minimum value is 1 and the
 maximum value is 255.
-*leak*:::
+*leak* _rate_:::
 This option sets the rate at which responses are randomly allowed even if the
 limits specified by the *interval* and *burst* options are exceeded. This is
 necessary to prevent an attacker who is sending requests with a spoofed
@@ -1361,7 +1757,7 @@
 rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
 least every fourth request has a response. The minimum value is 1 and the
 maximum value is 4.
-::
+{blank}::
 +
 An example use of the directive is:
 +
@@ -1373,6 +1769,17 @@
 more than once per 2 seconds, or sending packets in bursts of more than 16
 packets, by up to 75% (with default *leak* of 2).
 
+[[ntsratelimit]]*ntsratelimit* [_option_]...::
+This directive enables rate limiting of NTS-KE requests. It is similar to the
+<<ratelimit,*ratelimit*>> directive, except the default interval is 6
+(1 connection per 64 seconds).
++
+An example of the use of the directive is:
++
+----
+ntsratelimit interval 3 burst 1
+----
+
 [[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
 The *smoothtime* directive can be used to enable smoothing of the time that
 *chronyd* serves to its clients to make it easier for them to track it and keep
@@ -1426,8 +1833,8 @@
 === Command and monitoring access
 
 [[bindcmdaddress]]*bindcmdaddress* _address_::
-The *bindcmdaddress* directive allows you to specify an IP address of an
-interface on which *chronyd* will listen for monitoring command packets (issued
+The *bindcmdaddress* directive specifies a local IP address to which *chronyd*
+will bind the UDP socket listening for monitoring command packets (issued
 by *chronyc*). On systems other than Linux, the address of the interface needs
 to be already configured when *chronyd* is started.
 +
@@ -1438,9 +1845,10 @@
 path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be
 disabled by setting the path to _/_.
 +
-By default, *chronyd* binds to the loopback interface (with addresses
-_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen
-for command packets on all interfaces, you can add the lines:
+By default, *chronyd* binds the UDP sockets to the addresses _127.0.0.1_ and
+_::1_ (i.e. the loopback interface). This blocks all access except from
+localhost. To listen for command packets on all interfaces, you can add the
+lines:
 +
 ----
 bindcmdaddress 0.0.0.0
@@ -1458,6 +1866,17 @@
 bindcmdaddress /var/run/chrony/chronyd.sock
 ----
 
+[[bindcmddevice]]*bindcmddevice* _interface_::
+The *bindcmddevice* directive binds the UDP command sockets to a network device
+specified by the interface name. This directive can specify only one interface
+and it is supported on Linux only.
++
+An example of the directive is:
++
+----
+bindcmddevice eth0
+----
+
 [[cmdallow]]*cmdallow* [*all*] [_subnet_]::
 This is similar to the <<allow,*allow*>> directive, except that it allows
 monitoring access (rather than NTP client access) to a particular subnet or
@@ -1530,7 +1949,8 @@
 automatically. When the system clock is synchronised and the estimated error
 between the two clocks is larger than the specified threshold, *chronyd* will
 trim the RTC as if the <<chronyc.adoc#trimrtc,*trimrtc*>> command in *chronyc*
-was issued.
+was issued. The trimming operation is accurate to only about 1 second, which is
+the minimum effective threshold.
 +
 This directive is effective only with the <<rtcfile,*rtcfile*>> directive.
 +
@@ -1587,8 +2007,8 @@
 The directive takes no arguments. It is equivalent to specifying the *-u*
 switch to the Linux *hwclock* program.
 +
-Note that this setting is overridden when the <<hwclockfile,*hwclockfile*>>
-directive is specified.
+Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
+and is not relevant for the <<rtcsync,*rtcsync*>> directive.
 
 [[rtcsync]]*rtcsync*::
 The *rtcsync* directive enables a mode where the system time is periodically
@@ -1836,7 +2256,7 @@
 . Applied compensation in ppm, positive means the system clock is running
   faster than it would be without the compensation. [3.6600e-01]
 +
-::
+{blank}::
 An example of the directive is:
 +
 ----
@@ -1869,8 +2289,9 @@
 0.1 seconds starts to be compensated.
 
 [[logdir]]*logdir* _directory_::
-This directive allows the directory where log files are written to be
-specified.
+This directive specifies the directory for writing log files enabled by the
+*log* directive. If the directory does not exist, it will be created
+automatically.
 +
 An example of the use of this directive is:
 +
@@ -1898,6 +2319,57 @@
 
 === Miscellaneous
 
+[[confdir]]*confdir* _directory_...::
+The *confdir* directive includes configuration files with the _.conf_ suffix
+from a directory. The files are included in the lexicographical order of the
+file names.
++
+Multiple directories (up to 10) can be specified with a single *confdir*
+directive. In this case, if multiple directories contain a file with the same
+name, only the first file in the order of the specified directories will be
+included. This enables a fragmented configuration where existing fragments can
+be replaced by adding files to a different directory.
++
+This directive can be used multiple times.
++
+An example of the directive is:
++
+----
+confdir @SYSCONFDIR@/chrony.d
+----
+
+[[sourcedir]]*sourcedir* _directory_...::
+The *sourcedir* directive is identical to the *confdir* directive, except the
+configuration files have the _.sources_ suffix, they can only specify NTP
+sources (i.e. the *server*, *pool*, and *peer* directives), they are expected
+to have all lines terminated by the newline character, and they can be
+reloaded by the <<chronyc.adoc#reload,*reload sources*>> command in
+*chronyc*. It is particularly useful with dynamic sources like NTP servers
+received from a DHCP server, which can be written to a file specific to the
+network interface by a networking script.
++
+This directive can be used multiple times.
++
+An example of the directive is:
++
+----
+sourcedir /var/run/chrony-dhcp
+----
+
+[[include]]*include* _pattern_::
+The *include* directive includes a configuration file, or multiple configuration
+files if a wildcard pattern is specified. Unlike with the *confdir* directive,
+the full name of the files needs to be specified and at least one file is
+required to exist.
++
+This directive can be used multiple times.
++
+An example of the directive is:
++
+----
+include @SYSCONFDIR@/chrony.d/*.conf
+----
+
 [[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
 This directive enables hardware timestamping of NTP packets sent to and
 received from the specified network interface. The network interface controller
@@ -1916,7 +2388,7 @@
 capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
 _SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive
 filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for
-timestamping of received packets. Timestamping of packets received from bridged
+timestamping of received NTP packets. Timestamping of packets received on bridged
 and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is
 running, no other process (e.g. a PTP daemon) should be working with the NIC
 clock.
@@ -1927,8 +2399,9 @@
 measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
 *chronyc*.
 +
-If the specified interface is _*_, *chronyd* will try to enable HW timestamping
-on all available interfaces.
+This directive can be used multiple times to enable HW timestamping on multiple
+interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
+timestamping on all available interfaces.
 +
 The *hwtimestamp* directive has the following options:
 +
@@ -1938,6 +2411,12 @@
 interval of all NTP sources and the minimum expected polling interval of NTP
 clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
 of a second).
+*minsamples* _samples_:::
+This option specifies the minimum number of readings kept for tracking of the
+NIC clock. The default value is 2.
+*maxsamples* _samples_:::
+This option specifies the maximum number of readings kept for tracking of the
+NIC clock. The default value is 16.
 *precision* _precision_:::
 This option specifies the assumed precision of reading of the NIC clock. The
 default value is 100e-9 (100 nanoseconds).
@@ -1960,16 +2439,20 @@
 Enables timestamping of all received packets.
 _ntp_::::
 Enables timestamping of received NTP packets.
+_ptp_::::
+Enables timestamping of received PTP packets.
 _none_::::
 Disables timestamping of received packets.
-:::
-The most specific filter for timestamping NTP packets which is supported by the
-NIC is selected by default. Some NICs can timestamp only PTP packets, which
-limits the selection to the _none_ filter. Forcing timestamping of all packets
-with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be
-useful when packets are received from or on a non-standard UDP port (e.g.
-specified by the *port* directive).
-::
+{blank}:::
+The most specific filter for timestamping of NTP packets supported by the NIC
+is selected by default. Some NICs can timestamp PTP packets only. By default,
+they will be configured with the _none_ filter and expected to provide hardware
+timestamps for transmitted packets only. Timestamping of PTP packets is useful
+with NTP-over-PTP enabled by the <<chrony.conf.adoc#ptpport,*ptpport*>>
+directive. Forcing timestamping of all packets with the _all_ filter could be
+useful if the NIC supported both the _all_ and _ntp_ filters, and it should
+timestamp both NTP and PTP packets, or NTP packets on a different UDP port.
+{blank}::
 +
 Examples of the directive are:
 +
@@ -1979,20 +2462,11 @@
 hwtimestamp *
 ----
 
-[[include]]*include* _pattern_::
-The *include* directive includes a configuration file or multiple configuration
-files if a wildcard pattern is specified. This can be useful when maintaining
-configuration on multiple hosts to keep the differences in separate files.
-+
-An example of the directive is:
-+
-----
-include @SYSCONFDIR@/chrony.d/*.conf
-----
-
 [[keyfile]]*keyfile* _file_::
-This directive is used to specify the location of the file containing ID-key
-pairs for authentication of NTP packets.
+This directive is used to specify the location of the file containing symmetric
+keys which are shared between NTP servers and clients, or peers, in order to
+authenticate NTP packets with a message authentication code (MAC) using a
+cryptographic hash function or cipher.
 +
 The format of the directive is shown in the example below:
 +
@@ -2007,82 +2481,120 @@
 10 tulip
 11 hyacinth
 20 MD5 ASCII:crocus
-25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c
+25 SHA1 HEX:933F62BE1D604E68A81B557F18CFA200483F5B70
+30 AES128 HEX:7EA62AE64D190114D46D5A082F948EC1
+31 AES256 HEX:37DDCBC67BB902BCB8E995977FAB4D2B5642F5B32EBCEEE421921D97E5CBFE39
  ...
 ----
 +
-Each line consists of an ID, name of an authentication hash function (optional),
-and a password. The ID can be any unsigned integer in the range 1 through
-2^32-1. The default hash function is *MD5*, which is always supported.
+Each line consists of an ID, optional type, and key.
 +
+The ID can be any positive integer in the range 1 through 2^32-1.
++
+The type is a name of a cryptographic hash function or cipher which is used to
+generate and verify the MAC. The default type is *MD5*, which is always
+supported.
 If *chronyd* was built with enabled support for hashing using a crypto library
 (nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
 *SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
-*chronyd* using, some or all of the following functions may also be available:
-*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *RMD128*, *RMD160*, *RMD256*,
-*RMD320*, *TIGER*, *WHIRLPOOL*.
+*chronyd* using, some of the following hash functions and ciphers may
+also be available:
+*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *TIGER*, *WHIRLPOOL*, *AES128*,
+*AES256*.
 +
-The password can be specified as a string of characters not containing white
+The key can be specified as a string of ASCII characters not containing white
 space with an optional *ASCII:* prefix, or as a hexadecimal number with the
 *HEX:* prefix. The maximum length of the line is 2047 characters.
+If the type is a cipher, the length of the key must match the cipher (i.e. 128
+bits for AES128 and 256 bits for AES256).
 +
-The password is used with the hash function to generate and verify a message
-authentication code (MAC) in NTP packets. It is recommended to use SHA1, or
-stronger, hash function with random passwords specified in the hexadecimal
-format that have at least 128 bits. *chronyd* will log a warning to
-syslog on start if a source is specified in the configuration file with a key
-that has password shorter than 80 bits.
+It is recommended to use randomly generated keys, specified in the hexadecimal
+format, which are at least 128 bits long (i.e. they have at least 32 characters
+after the *HEX:* prefix). *chronyd* will log a warning to syslog on start if a
+source is specified in the configuration file with a key shorter than 80 bits.
++
+The recommended key types are AES ciphers and SHA3 hash functions. MD5 should
+be avoided unless no other type is supported on the server and client, or
+peers.
 +
 The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
 generate random keys for the key file. By default, it generates 160-bit MD5 or
 SHA1 keys.
++
+For security reasons, the file should be readable only by root and the user
+under which *chronyd* is normally running (to allow *chronyd* to re-read the
+file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
 
 [[lock_all]]*lock_all*::
-The *lock_all* directive will lock chronyd into RAM so that it will never be
-paged out. This mode is only supported on Linux. This directive uses the Linux
-*mlockall()* system call to prevent *chronyd* from ever being swapped out. This
-should result in lower and more consistent latency. It should not have
-significant impact on performance as *chronyd's* memory usage is modest. The
-*mlockall(2)* man page has more details.
+The *lock_all* directive will lock the *chronyd* process into RAM so that it
+will never be paged out. This can result in lower and more consistent latency.
+The directive is supported on Linux, FreeBSD, NetBSD, and illumos.
 
 [[pidfile]]*pidfile* _file_::
-*chronyd* always writes its process ID (PID) to a file, and checks this file on
-startup to see if another *chronyd* might already be running on the system. By
-default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
-allows the name to be changed, e.g.:
+Unless *chronyd* is started with the *-Q* option, it writes its process ID
+(PID) to a file, and checks this file on startup to see if another *chronyd*
+might already be running on the system. By default, the file used is
+_@DEFAULT_PID_FILE@_. The *pidfile* directive allows the name to be changed,
+e.g.:
 +
 ----
 pidfile /run/chronyd.pid
 ----
 
-[[sched_priority]]*sched_priority* _priority_::
-On Linux, the *sched_priority* directive will select the SCHED_FIFO real-time
-scheduler at the specified priority (which must be between 0 and 100). On
-macOS, this option must have either a value of 0 (the default) to disable the
-thread time constraint policy or 1 for the policy to be enabled. Other systems
-do not support this option.
+[[ptpport]]*ptpport* _port_::
+The *ptpport* directive enables *chronyd* to send and receive NTP messages
+contained in PTP event messages (NTP-over-PTP) to enable hardware timestamping
+on NICs which cannot timestamp NTP packets, but can timestamp unicast PTP
+packets. The port recognized by the NICs is 319 (PTP event port). The default
+value is 0 (disabled).
 +
-On Linux, this directive uses the *sched_setscheduler()* system call to
-instruct the kernel to use the SCHED_FIFO first-in, first-out real-time
-scheduling policy for *chronyd* with the specified priority. This means that
-whenever *chronyd* is ready to run it will run, interrupting whatever else is
-running unless it is a higher priority real-time process. This should not
-impact performance as *chronyd* resource requirements are modest, but it should
-result in lower and more consistent latency since *chronyd* will not need to
-wait for the scheduler to get around to running it. You should not use this
-unless you really need it. The *sched_setscheduler(2)* man page has more
-details.
+The NTP-over-PTP support is experimental. The protocol and configuration can
+change in future. It should be used only in local networks and expected to work
+only between servers and clients running the same version of *chronyd*.
++
+The PTP port will be open even if *chronyd* is not configured to operate as a
+server or client. The directive does not change the default protocol of
+specified NTP sources. Each NTP source that should use NTP-over-PTP needs to
+be specified with the *port* option set to the PTP port. To actually enable
+hardware timestamping on NICs which can timestamp PTP packets only, the
+*rxfilter* option of the *hwtimestamp* directive needs to be set to _ptp_.
++
+An example of client configuration is:
++
+----
+server foo.example.net minpoll 0 maxpoll 0 xleave port 319
+hwtimestamp * rxfilter ptp
+ptpport 319
+----
+
+[[sched_priority]]*sched_priority* _priority_::
+On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
+select the SCHED_FIFO real-time scheduler at the specified priority (which must
+be between 0 and 100). On macOS, this option must have either a value of 0 (the
+default) to disable the thread time constraint policy or 1 for the policy to be
+enabled.
++
+On systems other than macOS, this directive uses the *pthread_setschedparam()*
+system call to instruct the kernel to use the SCHED_FIFO first-in, first-out
+real-time scheduling policy for *chronyd* with the specified priority. This
+means that whenever *chronyd* is ready to run it will run, interrupting
+whatever else is running unless it is a higher priority real-time process. This
+should not impact performance as *chronyd* resource requirements are modest,
+but it should result in lower and more consistent latency since *chronyd* will
+not need to wait for the scheduler to get around to running it. You should not
+use this unless you really need it. The *pthread_setschedparam(3)* man page has
+more details.
 +
 On macOS, this directive uses the *thread_policy_set()* kernel call to
-specify real-time scheduling. As noted for Linux, you should not use this
-directive unless you really need it.
+specify real-time scheduling. As noted above, you should not use this directive
+unless you really need it.
 
 [[user]]*user* _user_::
 The *user* directive sets the name of the system user to which *chronyd* will
 switch after start in order to drop root privileges.
 +
 On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
-On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
+On macOS, FreeBSD, NetBSD and illumos *chronyd* forks into two processes.
 The child process retains root privileges, but can only perform a very limited
 range of privileged system calls on behalf of the parent.
 +
@@ -2109,7 +2621,7 @@
   stratum 1 and stratum 2 servers. You should find one or more servers that are
   near to you. Check that their access policy allows you to use their
   facilities.
-* Use public servers from the http://www.pool.ntp.org/[pool.ntp.org] project.
+* Use public servers from the https://www.pool.ntp.org/[pool.ntp.org] project.
 
 Assuming that your NTP servers are called _foo.example.net_, _bar.example.net_
 and _baz.example.net_, your _chrony.conf_ file could contain as a minimum:
@@ -2148,6 +2660,20 @@
 rtcsync
 ----
 
+If the servers (or pool) support the Network Time Security (NTS)
+authentication mechanism and *chronyd* is compiled with NTS support, the *nts*
+option will enable a secure synchronisation to the servers. The configuration
+file could look like:
+
+----
+server foo.example.net iburst nts
+server bar.example.net iburst nts
+server baz.example.net iburst nts
+driftfile @CHRONYVARDIR@/drift
+makestep 1.0 3
+rtcsync
+----
+
 === NTP client with infrequent connection to NTP servers
 
 This section shows how to configure *chronyd* for computers that have
@@ -2195,25 +2721,25 @@
 === Isolated networks
 
 This section shows how to configure *chronyd* for computers that never have
-network conectivity to any computer which ultimately derives its time from a
+network connectivity to any computer which ultimately derives its time from a
 reference clock.
 
-In this situation, one computer is selected to be the master timeserver. The
-other computers are either direct clients of the master, or clients of clients.
+In this situation, one computer is selected to be the primary timeserver. The
+other computers are either direct clients of the server, or clients of clients.
 
 The <<local,*local*>> directive enables a local reference mode, which allows
 *chronyd* to appear synchronised even when it is not.
 
-The rate value in the master's drift file needs to be set to the average rate
-at which the master gains or loses time. *chronyd* includes support for this,
+The rate value in the server's drift file needs to be set to the average rate
+at which the server gains or loses time. *chronyd* includes support for this,
 in the form of the <<manual,*manual*>> directive and the
 <<chronyc.adoc#settime,*settime*>> command in the *chronyc* program.
 
-If the master is rebooted, *chronyd* can re-read the drift rate from the drift
-file. However, the master has no accurate estimate of the current time. To get
-around this, the system can be configured so that the master can initially set
+If the server is rebooted, *chronyd* can re-read the drift rate from the drift
+file. However, the server has no accurate estimate of the current time. To get
+around this, the system can be configured so that the server can initially set
 itself to a '`majority-vote`' of selected clients' times; this allows the
-clients to '`flywheel`' the master while it is rebooting.
+clients to '`flywheel`' the server while it is rebooting.
 
 The <<smoothtime,*smoothtime*>> directive is useful when the clocks of the
 clients need to stay close together when the local time is adjusted by the
@@ -2222,8 +2748,8 @@
 the local time is ready to be served. After that point, any adjustments will be
 smoothed out.
 
-A typical configuration file for the master (called _master_) might be
-(assuming the clients and the master are in the _192.168.165.x_ subnet):
+A typical configuration file for the server (called _ntp.local_) might be
+(assuming the clients and the server are in the _192.168.165.x_ subnet):
 
 ----
 initstepslew 1 client1 client3 client6
@@ -2235,11 +2761,11 @@
 rtcsync
 ----
 
-For the clients that have to resynchronise the master when it restarts,
+For the clients that have to resynchronise the server when it restarts,
 the configuration file might be:
 
 ----
-server master iburst
+server ntp.local iburst
 driftfile @CHRONYVARDIR@/drift
 allow 192.168.165.0/24
 makestep 1.0 3
@@ -2249,22 +2775,22 @@
 The rest of the clients would be the same, except that the *allow* directive is
 not required.
 
-If there is no suitable computer to be designated as the master, or there is a
-requirement to keep the clients synchronised even when it fails, the *orphan*
-option of the *local* directive enables a special mode where the master is
-selected from multiple computers automatically. They all need to use the same
-*local* configuration and poll one another. The server with the smallest
-reference ID (which is based on its IP address) will take the role of the
-master and others will be synchronised to it. When it fails, the server with
-the second smallest reference ID will take over and so on.
+If there is no suitable computer to be designated as the primary server, or
+there is a requirement to keep the clients synchronised even when it fails, the
+*orphan* option of the *local* directive enables a special mode where the
+server is selected from multiple computers automatically. They all need to use
+the same *local* configuration and poll one another. The server with the
+smallest reference ID (which is based on its IP address) will take the role of
+the primary server and others will be synchronised to it. When it fails, the
+server with the second smallest reference ID will take over and so on.
 
 A configuration file for the first server might be (assuming there are three
-servers called _master1_, _master2_, and _master3_):
+servers called _ntp1.local_, _ntp2.local_, and _ntp3.local_):
 
 ----
-initstepslew 1 master2 master3
-server master2
-server master3
+initstepslew 1 ntp2.local ntp3.local
+server ntp2.local
+server ntp3.local
 driftfile @CHRONYVARDIR@/drift
 local stratum 8 orphan
 manual
@@ -2387,7 +2913,7 @@
 === Public NTP server
 
 *chronyd* can be configured to operate as a public NTP server, e.g. to join the
-http://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration
+https://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration
 is similar to the NTP client with permanent connection, except it needs to
 allow client access from all addresses. It is recommended to find at least four
 good servers (e.g. from the pool, or on the NTP homepage). If the server has a
diff --git a/chrony_3_3/doc/chrony.conf.man.in b/chrony/doc/chrony.conf.man.in
similarity index 68%
rename from chrony_3_3/doc/chrony.conf.man.in
rename to chrony/doc/chrony.conf.man.in
index 8a023bf..8713321 100644
--- a/chrony_3_3/doc/chrony.conf.man.in
+++ b/chrony/doc/chrony.conf.man.in
@@ -1,23 +1,32 @@
 '\" t
 .\"     Title: chrony.conf
-.\"    Author: [see the "AUTHORS" section]
-.\" Generator: Asciidoctor 1.5.6.1
-.\"      Date: 2018-04-04
+.\"    Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.15
+.\"      Date: 2021-12-16
 .\"    Manual: Configuration Files
 .\"    Source: chrony @CHRONY_VERSION@
 .\"  Language: English
 .\"
-.TH "CHRONY.CONF" "5" "2018-04-04" "chrony @CHRONY_VERSION@" "Configuration Files"
+.TH "CHRONY.CONF" "5" "2021-12-16" "chrony @CHRONY_VERSION@" "Configuration Files"
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .ss \n[.ss] 0
 .nh
 .ad l
 .de URL
-\\$2 \(laURL: \\$1 \(ra\\$3
+\fI\\$2\fP <\\$1>\\$3
 ..
-.if \n[.g] .mso www.tmac
-.LINKSTYLE blue R < >
+.als MTO URL
+.if \n[.g] \{\
+.  mso www.tmac
+.  am URL
+.    ad l
+.  .
+.  am MTO
+.    ad l
+.  .
+.  LINKSTYLE blue R < >
+.\}
 .SH "NAME"
 chrony.conf \- chronyd configuration file
 .SH "SYNOPSIS"
@@ -26,12 +35,14 @@
 .SH "DESCRIPTION"
 .sp
 This file configures the \fBchronyd\fP daemon. The compiled\-in location is
-\fI@SYSCONFDIR@/chrony.conf\fP, but other locations can be specified on the
+\fI@SYSCONFDIR@/chrony.conf\fP. Other locations can be specified on the
 \fBchronyd\fP command line with the \fB\-f\fP option.
 .sp
 Each directive in the configuration file is placed on a separate line. The
-following sections describe each of the directives in turn. The directives can
-occur in any order in the file and they are not case\-sensitive.
+following sections describe each of the directives in turn. The directives are
+not case\-sensitive. Generally, the directives can occur in any order in the file
+and if a directive is specified multiple times, only the last one will be
+effective. Exceptions are noted in the descriptions.
 .sp
 The configuration directives can also be specified directly on the \fBchronyd\fP
 command line. In this case each argument is parsed as a new line and the
@@ -54,19 +65,22 @@
 synchronise its system time to that of the server, but the server\(cqs system time
 will never be influenced by that of a client.
 .sp
-The \fBserver\fP directive is immediately followed by either the name of the
-server, or its IP address. The \fBserver\fP directive supports the following
-options:
+This directive can be used multiple times to specify multiple servers.
+.sp
+The directive is immediately followed by either the name of the
+server, or its IP address. It supports the following options:
 .sp
 \fBminpoll\fP \fIpoll\fP
 .RS 4
 This option specifies the minimum interval between requests sent to the server
 as a power of 2 in seconds. For example, \fBminpoll 5\fP would mean that the
 polling interval should not drop below 32 seconds. The default is 6 (64
-seconds), the minimum is \-4 (1/16th of a second), and the maximum is 24 (6
+seconds), the minimum is \-6 (1/64th of a second), and the maximum is 24 (6
 months). Note that intervals shorter than 6 (64 seconds) should generally not
 be used with public servers on the Internet, because it might be considered
-abuse.
+abuse. A sub\-second interval will be enabled only when the server is reachable
+and the round\-trip delay is shorter than 10 milliseconds, i.e. the server
+should be in a local network.
 .RE
 .sp
 \fBmaxpoll\fP \fIpoll\fP
@@ -74,32 +88,33 @@
 This option specifies the maximum interval between requests sent to the server
 as a power of 2 in seconds. For example, \fBmaxpoll 9\fP indicates that the polling
 interval should stay at or below 9 (512 seconds). The default is 10 (1024
-seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
+seconds), the minimum is \-6 (1/64th of a second), and the maximum is 24 (6
+months).
 .RE
 .sp
 \fBiburst\fP
 .RS 4
-With this option, the interval between the first four requests sent to the
-server will be 2 seconds instead of the interval specified by the \fBminpoll\fP
-option, which allows \fBchronyd\fP to make the first update of the clock shortly
-after start.
+With this option, \fBchronyd\fP will start with a burst of 4\-8 requests in order to
+make the first update of the clock sooner. It will also repeat the burst every
+time the source is switched from the offline state to online with the
+\fBonline\fP command in \fBchronyc\fP.
 .RE
 .sp
 \fBburst\fP
 .RS 4
-With this option, \fBchronyd\fP will shorten the interval between up to four
-requests to 2 seconds when it cannot get a good measurement from the server.
-The number of requests in the burst is limited by the current polling interval
-to keep the average interval at or above the minimum interval, i.e. the current
-interval needs to be at least two times longer than the minimum interval in
-order to allow a burst with two requests.
+With this option, \fBchronyd\fP will send a burst of up to 4 requests when it
+cannot get a good measurement from the
+server. The number of requests in the burst is limited by the current polling
+interval to keep the average interval at or above the minimum interval, i.e.
+the current interval needs to be at least two times longer than the minimum
+interval in order to allow a burst with two requests.
 .RE
 .sp
 \fBkey\fP \fIID\fP
 .RS 4
 The NTP protocol supports a message authentication code (MAC) to prevent
 computers having their system time upset by rogue packets being sent to them.
-The MAC is generated as a function of a password specified in the key file,
+The MAC is generated as a function of a key specified in the key file,
 which is specified by the \fBkeyfile\fP directive.
 .sp
 The \fBkey\fP option specifies which key (with an ID in the range 1 through 2^32\-1)
@@ -112,6 +127,24 @@
 be set to 4 for compatibility.
 .RE
 .sp
+\fBnts\fP
+.RS 4
+This option enables authentication using the Network Time Security (NTS)
+mechanism. Unlike with the \fBkey\fP option, the server and client do not need to
+share a key in a key file. NTS has a Key Establishment (NTS\-KE) protocol using
+the Transport Layer Security (TLS) protocol to get the keys and cookies
+required by NTS for authentication of NTP packets.
+.RE
+.sp
+\fBcertset\fP \fIID\fP
+.RS 4
+This option specifies which set of trusted certificates should be used to verify
+the server\(cqs certificate when the \fBnts\fP option is enabled. Sets of certificates
+can be specified with the \fBntstrustedcerts\fP directive. The
+default set is 0, which by default contains certificates of the system\(cqs
+default trusted certificate authorities.
+.RE
+.sp
 \fBmaxdelay\fP \fIdelay\fP
 .RS 4
 \fBchronyd\fP uses the network round\-trip delay to the server to determine how
@@ -122,9 +155,9 @@
 For small variations in the round\-trip delay, \fBchronyd\fP uses a weighting scheme
 when processing the measurements. However, beyond a certain level of delay the
 measurements are likely to be so corrupted as to be useless. (This is
-particularly so on dial\-up or other slow links, where a long delay probably
-indicates a highly asymmetric delay caused by the response waiting behind a lot
-of packets related to a download of some sort).
+particularly so on wireless networks and other slow links, where a long delay
+probably indicates a highly asymmetric delay caused by the response waiting
+behind a lot of packets related to a download of some sort).
 .sp
 If the user knows that round trip delays above a certain level should cause the
 measurement to be ignored, this level can be defined with the \fBmaxdelay\fP
@@ -138,7 +171,7 @@
 This option is similar to the \fBmaxdelay\fP option above. \fBchronyd\fP keeps a record
 of the minimum round\-trip delay amongst the previous measurements that it has
 buffered. If a measurement has a round trip delay that is greater than the
-maxdelayratio times the minimum delay, it will be rejected.
+specified ratio times the minimum delay, it will be rejected.
 .RE
 .sp
 \fBmaxdelaydevratio\fP \fIratio\fP
@@ -190,6 +223,14 @@
 \fBmaxsamples\fP directive.
 .RE
 .sp
+\fBfilter\fP \fIsamples\fP
+.RS 4
+This option enables a median filter to reduce noise in NTP measurements. The
+filter will reduce the specified number of samples to a single sample. It is
+intended to be used with very short polling intervals in local networks where
+it is acceptable to generate a lot of NTP traffic.
+.RE
+.sp
 \fBoffline\fP
 .RS 4
 If the server will not be reachable when \fBchronyd\fP is started, the \fBoffline\fP
@@ -200,13 +241,12 @@
 .sp
 \fBauto_offline\fP
 .RS 4
-With this option, the server will be assumed to have gone offline when two
-requests have been sent to it without receiving a response. This option avoids
+With this option, the server will be assumed to have gone offline when sending
+a request fails, e.g. due to a missing route to the network. This option avoids
 the need to run the \fBoffline\fP command from \fBchronyc\fP
-when disconnecting the network link, if it is safe to assume that the requests
-and responses will not be dropped in the network, e.g. in a trusted local
-network. (It will still be necessary to use the \fBonline\fP
-command when the link has been established, to enable measurements to start.)
+when disconnecting the network link. (It will still be necessary to use the
+\fBonline\fP command when the link has been established, to
+enable measurements to start.)
 .RE
 .sp
 \fBprefer\fP
@@ -238,14 +278,14 @@
 .sp
 \fBxleave\fP
 .RS 4
-This option enables an interleaved mode which allows the server or the peer to
-send transmit timestamps captured after the actual transmission (e.g. when the
-server or the peer is running \fBchronyd\fP with software (kernel) or hardware
-timestamping). This can significantly improve the accuracy of the measurements.
+This option enables the interleaved mode of NTP. It enables the server to
+respond with more accurate transmit timestamps (e.g. kernel or hardware
+timestamps), which cannot be contained in the transmitted packet itself and
+need to refer to a previous packet instead. This can significantly improve the
+accuracy and stability of the measurements.
 .sp
 The interleaved mode is compatible with servers that support only the basic
-mode, but peers must both support and have enabled the interleaved mode,
-otherwise the synchronisation will work only in one direction. Note that even
+mode. Note that even
 servers that support the interleaved mode might respond in the basic mode as
 the interleaved mode requires the servers to keep some state for each client
 and the state might be dropped when there are too many clients (e.g.
@@ -273,6 +313,12 @@
 default is 123, the standard NTP port).
 .RE
 .sp
+\fBntsport\fP \fIport\fP
+.RS 4
+This option specifies the TCP port on which the server is listening for NTS\-KE
+connections when the \fBnts\fP option is enabled. The default is 4460.
+.RE
+.sp
 \fBpresend\fP \fIpoll\fP
 .RS 4
 If the timing measurements being made by \fBchronyd\fP are the only network data
@@ -288,22 +334,20 @@
 prior to the actual measurement. For example, with the following option
 included in a \fBserver\fP directive:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 presend 9
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 when the polling interval is 512 seconds or more, an extra NTP client packet
 will be sent to the server a short time (2 seconds) before making the actual
 measurement.
 .sp
-The \fBpresend\fP option cannot be used in the \fBpeer\fP directive. If it is used
-with the \fBxleave\fP option, \fBchronyd\fP will send two extra packets instead of one.
+If the \fBpresend\fP option is used together with the \fBxleave\fP option, \fBchronyd\fP
+will send two extra packets instead of one.
 .RE
 .sp
 \fBminstratum\fP \fIstratum\fP
@@ -311,7 +355,7 @@
 When the synchronisation source is selected from available sources, sources
 with lower stratum are normally slightly preferred. This option can be used to
 increase stratum of the source to the specified minimum, so \fBchronyd\fP will
-avoid selecting that source. This is useful with low stratum sources that are
+avoid selecting that source. This is useful with low\-stratum sources that are
 known to be unreliable or inaccurate and which should be used only when other
 sources are unreachable.
 .RE
@@ -320,12 +364,52 @@
 .RS 4
 This option sets the NTP version of packets sent to the server. This can be
 useful when the server runs an old NTP implementation that does not respond to
-requests using a newer version. The default version depends on whether a key is
-specified by the \fBkey\fP option and which authentication hash function the key
-is using. If the output size of the hash function is longer than 160 bits, the
-default version is 3 for compatibility with older \fBchronyd\fP servers. Otherwise,
+requests using a newer version. The default version depends on other options.
+If the \fBextfield\fP or \fBxleave\fP option is used, the default version is 4. If
+those options are not used and the \fBkey\fP option specifies a key using a hash
+function with output size longer than 160 bits (e.g. SHA256), the default
+version is 3 for compatibility with older \fBchronyd\fP servers. In other cases,
 the default version is 4.
 .RE
+.sp
+\fBcopy\fP
+.RS 4
+This option specifies that the server and client are closely related, their
+configuration does not allow a synchronisation loop to form between them, and
+the client is allowed to assume the reference ID and stratum of the server.
+This is useful when multiple instances of \f(CRchronyd\fP are running on one computer
+(e.g. for security or performance reasons), one primarily operating as a client
+to synchronise the system clock and other instances started with the \fB\-x\fP
+option to operate as NTP servers for other computers with their NTP clocks
+synchronised to the first instance.
+.RE
+.sp
+\fBextfield\fP \fItype\fP
+.RS 4
+This option enables an NTPv4 extension field specified by its type as a
+hexadecimal number. It will be included in requests sent to the server and
+processed in received responses if the server supports it. Note that some
+server implementations do not respond to requests containing an unknown
+extension field (\fBchronyd\fP as a server responded to such requests since
+version 2.0).
+.sp
+The following extension field can be enabled by this option:
+.sp
+\fIF323\fP
+.RS 4
+This is an experimental extension field for some improvements that were
+proposed for the next version of the NTP protocol (NTPv5). The field contains
+root delay and dispersion in higher resolution and a monotonic receive
+timestamp, which enables a frequency transfer between the server and client. It
+can significantly improve stability of the synchronization. Generally, it
+should be expected to work only between servers and clients running the same
+version of \fBchronyd\fP.
+.RE
+.RE
+.sp
+
+.RS 4
+.RE
 .RE
 .sp
 \fBpool\fP \fIname\fP [\fIoption\fP]...
@@ -335,30 +419,36 @@
 a single NTP server. The pool name is expected to resolve to multiple addresses
 which might change over time.
 .sp
+This directive can be used multiple times to specify multiple pools.
+.sp
 All options valid in the \fBserver\fP directive can be used in this
 directive too. There is one option specific to the \fBpool\fP directive:
-\fBmaxsources\fP sets the maximum number of sources that can be used from the pool,
-the default value is 4.
 .sp
-On start, when the pool name is resolved, \fBchronyd\fP will add up to 16 sources,
-one for each resolved address. When the number of sources from which at least
-one valid reply was received reaches the number specified by the \fBmaxsources\fP
-option, the other sources will be removed. When a pool source is unreachable,
+\fBmaxsources\fP \fIsources\fP
+.RS 4
+This option sets the desired number of sources to be used from the pool.
+\fBchronyd\fP will repeatedly try to resolve the name until it gets this number of
+sources responding to requests. The default value is 4 and the maximum value is
+16.
+.RE
+.RE
+.sp
+
+.RS 4
+When an NTP source is unreachable,
 marked as a falseticker, or has a distance larger than the limit set by the
 \fBmaxdistance\fP directive, \fBchronyd\fP will try to replace the
-source with a newly resolved address from the pool.
+source with a newly resolved address of the name.
 .sp
 An example of the \fBpool\fP directive is
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 pool pool.ntp.org iburst maxsources 3
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBpeer\fP \fIhostname\fP [\fIoption\fP]...
@@ -371,6 +461,14 @@
 ephemeral symmetric associations and does not need to be configured with an
 address of this host. \fBchronyd\fP does not support ephemeral associations.
 .sp
+This directive can be used multiple times to specify multiple peers.
+.sp
+The following options of the \fBserver\fP directive do not work in the \fBpeer\fP
+directive: \fBiburst\fP, \fBburst\fP, \fBnts\fP, \fBpresend\fP, \fBcopy\fP.
+.sp
+When using the \fBxleave\fP option, both peers must support and have enabled the
+interleaved mode, otherwise the synchronisation will work in one direction
+only.
 When a key is specified by the \fBkey\fP option to enable authentication, both
 peers must use the same key and the same key number.
 .sp
@@ -395,19 +493,8 @@
 .sp
 \fBinitstepslew\fP \fIstep\-threshold\fP [\fIhostname\fP]...
 .RS 4
-In normal operation, \fBchronyd\fP slews the time when it needs to adjust the
-system clock. For example, to correct a system clock which is 1 second slow,
-\fBchronyd\fP slightly increases the amount by which the system clock is advanced
-on each clock interrupt, until the error is removed. Note that at no time does
-time run backwards with this method.
-.sp
-On most Unix systems it is not desirable to step the system clock, because many
-programs rely on time advancing monotonically forwards.
-.sp
-When the \fBchronyd\fP daemon is initially started, it is possible that the system
-clock is considerably in error. Attempting to correct such an error by slewing
-might not be sensible, since it might take several hours to correct the error by
-this means.
+(This directive is deprecated in favour of the \fBmakestep\fP
+directive.)
 .sp
 The purpose of the \fBinitstepslew\fP directive is to allow \fBchronyd\fP to make a
 rapid measurement of the system clock error at boot time, and to correct the
@@ -427,45 +514,46 @@
 .sp
 An example of the use of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-initstepslew 30 foo.example.net bar.example.net
+.fam C
+initstepslew 30 foo.example.net bar.example.net baz.example.net
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
-where 2 NTP servers are used to make the measurement. The \fI30\fP indicates that
+where 3 NTP servers are used to make the measurement. The \fI30\fP indicates that
 if the system\(cqs error is found to be 30 seconds or less, a slew will be used to
 correct it; if the error is above 30 seconds, a step will be used.
 .sp
 The \fBinitstepslew\fP directive can also be used in an isolated LAN environment,
 where the clocks are set manually. The most stable computer is chosen as the
-master, and the other computers are slaved to it. If each of the slaves is
-configured with the \fBlocal\fP directive, the master can be set up with
-an \fBinitstepslew\fP directive which references some or all of the slaves. Then,
-if the master machine has to be rebooted, the slaves can be relied on to act
-analogously to a flywheel and preserve the time for a short period while the
-master completes its reboot.
+primary server and the other computers are its clients. If each of the clients
+is configured with the \fBlocal\fP directive, the server can be set up
+with an \fBinitstepslew\fP directive which references some or all of the clients.
+Then, if the server machine has to be rebooted, the clients can be relied on to
+act analogously to a flywheel and preserve the time for a short period while
+the server completes its reboot.
 .sp
 The \fBinitstepslew\fP directive is functionally similar to a combination of the
 \fBmakestep\fP and \fBserver\fP directives with the \fBiburst\fP
 option. The main difference is that the \fBinitstepslew\fP servers are used only
 before normal operation begins and that the foreground \fBchronyd\fP process waits
-for \fBinitstepslew\fP to finish before exiting. This is useful to prevent programs
-started in the boot sequence after \fBchronyd\fP from reading the clock before it
-has been stepped.
+for \fBinitstepslew\fP to finish before exiting. This prevent programs started in
+the boot sequence after \fBchronyd\fP from reading the clock before it has been
+stepped. With the \fBmakestep\fP directive, the
+\fBwaitsync\fP command of \fBchronyc\fP can be used instead.
 .RE
 .sp
-\fBrefclock\fP \fIdriver\fP \fIparameter\fP[:\fIoption\fP,...] [\fIoption\fP]...
+\fBrefclock\fP \fIdriver\fP \fIparameter\fP[:\fIoption\fP]... [\fIoption\fP]...
 .RS 4
 The \fBrefclock\fP directive specifies a hardware reference clock to be used as a
 time source. It has two mandatory parameters, a driver name and a
 driver\-specific parameter. The two parameters are followed by zero or more
 refclock options. Some drivers have special options, which can be appended to
-the driver\-specific parameter (separated by the \fB:\fP and \fB,\fP characters).
+the driver\-specific parameter using the \fB:\fP character.
+.sp
+This directive can be used multiple times to specify multiple reference clocks.
 .sp
 There are four drivers included in \fBchronyd\fP:
 .sp
@@ -490,17 +578,15 @@
 .RS 4
 Examples:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 refclock PPS /dev/pps0 lock NMEA refid GPS
 refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
 refclock PPS /dev/pps1:clear refid GPS2
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBSHM\fP
@@ -523,16 +609,14 @@
 .sp
 Examples:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 refclock SHM 0 poll 3 refid GPS1
 refclock SHM 1:perm=0644 refid GPS2
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBSOCK\fP
@@ -549,15 +633,13 @@
 where \fBgpsd\fP expects the socket to be created is described in the \fBgpsd(8)\fP man
 page. For example:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 refclock SOCK /var/run/chrony.ttyS0.sock
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBPHC\fP
@@ -610,17 +692,15 @@
 .sp
 Examples:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 refclock PHC /dev/ptp0 poll 0 dpoll \-2 offset \-37
 refclock PHC /dev/ptp1:nocrossts poll 3 pps
-refclock PHC /dev/ptp2:extpps,pin=1 width 0.2 poll 2
+refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .RE
 .sp
@@ -805,12 +885,13 @@
 .sp
 \fBacquisitionport\fP \fIport\fP
 .RS 4
-By default, \fBchronyd\fP uses a separate client socket for each configured server
-and their source port is chosen arbitrarily by the operating system. However,
-you can use the \fBacquisitionport\fP directive to explicitly specify a port and
-use only one socket (per IPv4 or IPv6 address family) for all configured servers.
-This can be useful for getting through some firewalls. If set to 0, the source
-port of the socket will be chosen arbitrarily.
+By default, \fBchronyd\fP as an NTP client opens a new socket for each request with
+the source port chosen randomly by the operating system. The \fBacquisitionport\fP
+directive can be used to specify the source port and use only one socket (per
+IPv4 or IPv6 address family) for all configured servers. This can be useful for
+getting through some firewalls. It should not be used if not necessary as there
+is a small impact on security of the client. If set to 0, the source port of
+the permanent socket will be chosen randomly by the operating system.
 .sp
 It can be set to the same port as is used by the NTP server (which can be
 configured with the \fBport\fP directive) to use only one socket for all
@@ -818,15 +899,13 @@
 .sp
 An example of the \fBacquisitionport\fP directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 acquisitionport 1123
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This would change the source port used for client requests to UDP port 1123.
 You could then persuade the firewall administrator to open that port.
@@ -834,15 +913,53 @@
 .sp
 \fBbindacqaddress\fP \fIaddress\fP
 .RS 4
-The \fBbindacqaddress\fP directive sets the network interface to which
-\fBchronyd\fP will bind its NTP client sockets. The syntax is similar to the
-\fBbindaddress\fP and \fBbindcmdaddress\fP
+The \fBbindacqaddress\fP directive specifies a local IP address to which
+\fBchronyd\fP will bind its NTP and NTS\-KE client sockets. The syntax is similar to
+the \fBbindaddress\fP and \fBbindcmdaddress\fP
 directives.
 .sp
 For each of the IPv4 and IPv6 protocols, only one \fBbindacqaddress\fP directive
 can be specified.
 .RE
 .sp
+\fBbindacqdevice\fP \fIinterface\fP
+.RS 4
+The \fBbindacqdevice\fP directive binds the client sockets to a network device
+specified by the interface name. This can be useful when the local address is
+dynamic, or to enable an NTP source specified with a link\-local IPv6 address.
+This directive can specify only one interface and it is supported on Linux
+only.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+bindacqdevice eth0
+.fam
+.fi
+.if n .RE
+.RE
+.sp
+\fBdscp\fP \fIpoint\fP
+.RS 4
+The \fBdscp\fP directive sets the Differentiated Services Code Point (DSCP) in
+transmitted NTP packets to the specified value. It can improve stability of NTP
+measurements in local networks where switches or routers are configured to
+prioritise forwarding of packets with specific DSCP values. The default value
+is 0 and the maximum value is 63.
+.sp
+An example of the directive (setting the Expedited Forwarding class) is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+dscp 46
+.fam
+.fi
+.if n .RE
+.RE
+.sp
 \fBdumpdir\fP \fIdirectory\fP
 .RS 4
 To compute the rate of gain or loss of time, \fBchronyd\fP has to store a
@@ -861,17 +978,21 @@
 directory where the measurement histories are saved when \fBchronyd\fP exits,
 or the \fBdump\fP command in \fBchronyc\fP is issued.
 .sp
+If the directory does not exist, it will be created automatically.
+.sp
+The \fB\-r\fP option of \fBchronyd\fP enables loading of the dump files on start. All
+dump files found in the directory will be removed after start, even if the \fB\-r\fP
+option is not present.
+.sp
 An example of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 dumpdir @CHRONYRUNDIR@
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 A source whose IP address is \fI1.2.3.4\fP would have its measurement history saved
 in the file \fI@CHRONYRUNDIR@/1.2.3.4.dat\fP. History of reference clocks is saved
@@ -885,6 +1006,10 @@
 individual sources in the \fBserver\fP and \fBrefclock\fP
 directives. The default value is 0, which disables the configurable limit. The
 useful range is 4 to 64.
+.sp
+As a special case, setting \fBmaxsamples\fP to 1 disables frequency tracking in
+order to make the sources immediately selectable with only one sample. This can
+be useful when \fBchronyd\fP is started with the \fB\-q\fP or \fB\-Q\fP option.
 .RE
 .sp
 \fBminsamples\fP \fIsamples\fP
@@ -893,9 +1018,208 @@
 \fBchronyd\fP should keep for each source. This setting can be overridden for
 individual sources in the \fBserver\fP and \fBrefclock\fP
 directives. The default value is 6. The useful range is 4 to 64.
+.sp
+Forcing \fBchronyd\fP to keep more samples than it would normally keep reduces
+noise in the estimated frequency and offset, but slows down the response to
+changes in the frequency and offset of the clock. The offsets in the
+\fBtracking\fP and
+\fBsourcestats\fP reports (and the \fItracking.log\fP and
+\fIstatistics.log\fP files) may be smaller than the actual offsets.
+.RE
+.sp
+\fBntsdumpdir\fP \fIdirectory\fP
+.RS 4
+This directive specifies a directory for the client to save NTS cookies it
+received from the server in order to avoid making an NTS\-KE request when
+\fBchronyd\fP is started again. The cookies are saved separately for each NTP
+source in files named by the IP address of the NTS\-KE server (e.g.
+\fI1.2.3.4.nts\fP). By default, the client does not save the cookies.
+.sp
+If the directory does not exist, it will be created automatically.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+ntsdumpdir @CHRONYVARDIR@
+.fam
+.fi
+.if n .RE
+.sp
+This directory is used also by the NTS server to save keys.
+.RE
+.sp
+\fBntsrefresh\fP \fIinterval\fP
+.RS 4
+This directive specifies the maximum interval between NTS\-KE handshakes (in
+seconds) in order to refresh the keys authenticating NTP packets. The default
+value is 2419200 (4 weeks) and the maximum value is 2^31\-1 (68 years).
+.RE
+.sp
+\fBntstrustedcerts\fP [\fIset\-ID\fP] \fIfile\fP|\fIdirectory\fP
+.RS 4
+This directive specifies a file or directory containing certificates (in the
+PEM format) of trusted certificate authorities (CA) which can be used to
+verify certificates of NTS servers.
+.sp
+The optional \fIset\-ID\fP argument is a number in the range 0 through 2^32\-1, which
+selects the set of certificates where certificates from the specified file
+or directory are added. The default ID is 0, which is a set containing the
+system\(cqs default trusted CAs (unless the \fBnosystemcert\fP directive is present).
+All other sets are empty by default. A set of certificates can be selected for
+verification of an NTS server by the \fBcertset\fP option in the \fBserver\fP or \fBpool\fP
+directive.
+.sp
+This directive can be used multiple times to specify one or more sets of
+trusted certificates, each containing certificates from one or more files
+and/or directories.
+.sp
+It is not necessary to restart \fBchronyd\fP in order to reload the certificates if
+they change (e.g. after a renewal).
+.sp
+An example is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+ntstrustedcerts /etc/pki/nts/foo.crt
+ntstrustedcerts 1 /etc/pki/nts/bar.crt
+ntstrustedcerts 1 /etc/pki/nts/baz.crt
+ntstrustedcerts 2 /etc/pki/nts/qux.crt
+.fam
+.fi
+.if n .RE
+.RE
+.sp
+\fBnosystemcert\fP
+.RS 4
+This directive disables the system\(cqs default trusted CAs. Only certificates
+specified by the \fBntstrustedcerts\fP directive will be trusted.
+.RE
+.sp
+\fBnocerttimecheck\fP \fIlimit\fP
+.RS 4
+This directive disables the checks of the activation and expiration times of
+certificates for the specified number of clock updates. It allows the NTS
+authentication mechanism to be used on computers which start with wrong time
+(e.g. due to not having an RTC or backup battery). Disabling the time checks
+has important security implications and should be used only as a last resort,
+preferably with a minimal number of trusted certificates. The default value is
+0, which means the time checks are always enabled.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+nocerttimecheck 1
+.fam
+.fi
+.if n .RE
+.sp
+This would disable the time checks until the clock is updated for the first
+time, assuming the first update corrects the clock and later checks can work
+with correct time.
 .RE
 .SS "Source selection"
 .sp
+\fBauthselectmode\fP \fImode\fP
+.RS 4
+NTP sources can be specified with the \fBkey\fP or \fBnts\fP option to enable
+authentication to limit the impact of man\-in\-the\-middle attacks. The
+attackers can drop or delay NTP packets (up to the \fBmaxdelay\fP and
+\fBmaxdistance\fP limits), but they cannot modify the timestamps
+contained in the packets. The attack can cause only a limited slew or step, and
+also cause the clock to run faster or slower than real time (up to double of
+the \fBmaxdrift\fP limit).
+.sp
+When authentication is enabled for an NTP source, it is important to disable
+unauthenticated NTP sources which could be exploited in the attack, e.g. if
+they are not reachable only over a trusted network. Alternatively, the source
+selection can be configured with the \fBrequire\fP and \fBtrust\fP options to
+synchronise to the unauthenticated sources only if they agree with the
+authenticated sources and might have a positive impact on the accuracy of the
+clock. Note that in this case the impact of the attack is higher. The attackers
+cannot cause an arbitrarily large step or slew, but they have more control over
+the frequency of the clock and can cause \fBchronyd\fP to report false information,
+e.g. a significantly smaller root delay and dispersion.
+.sp
+This directive determines the default selection options for authenticated and
+unauthenticated sources in order to simplify the configuration with the
+configuration file and \fBchronyc\fP commands. It sets a policy for authentication.
+.sp
+Sources specified with the \fBnoselect\fP option are ignored (not counted as either
+authenticated or unauthenticated), and they always have only the selection
+options specified in the configuration.
+.sp
+There are four modes:
+.sp
+\fBrequire\fP
+.RS 4
+Authentication is strictly required for NTP sources in this mode. If any
+unauthenticated NTP sources are specified, they will automatically get the
+\fBnoselect\fP option to prevent them from being selected for synchronisation.
+.RE
+.sp
+\fBprefer\fP
+.RS 4
+In this mode, authentication is optional and preferred. If it is enabled for at
+least one NTP source, all unauthenticated NTP sources will get the \fBnoselect\fP
+option.
+.RE
+.sp
+\fBmix\fP
+.RS 4
+In this mode, authentication is optional and synchronisation to a mix of
+authenticated and unauthenticated NTP sources is allowed. If both authenticated
+and unauthenticated NTP sources are specified, all authenticated NTP sources
+and reference clocks will get the \fBrequire\fP and \fBtrust\fP options to prevent
+synchronisation to unauthenticated NTP sources if they do not agree with a
+majority of the authenticated sources and reference clocks. This is the default
+mode.
+.RE
+.sp
+\fBignore\fP
+.RS 4
+In this mode, authentication is ignored in the source selection. All sources
+will have only the selection options that were specified in the configuration
+file, or \fBchronyc\fP command. This was the behaviour of \fBchronyd\fP in versions
+before 4.0.
+.RE
+.RE
+.sp
+
+.RS 4
+.sp
+As an example, the following configuration using the default \fBmix\fP mode:
+.sp
+.if n .RS 4
+.nf
+.fam C
+server foo.example.net nts
+server bar.example.net nts
+server baz.example.net
+refclock SHM 0
+.fam
+.fi
+.if n .RE
+.sp
+is equivalent to the following configuration using the \fBignore\fP mode:
+.sp
+.if n .RS 4
+.nf
+.fam C
+authselectmode ignore
+server foo.example.net nts require trust
+server bar.example.net nts require trust
+server baz.example.net
+refclock SHM 0 require trust
+.fam
+.fi
+.if n .RE
+.RE
+.sp
 \fBcombinelimit\fP \fIlimit\fP
 .RS 4
 When \fBchronyd\fP has multiple sources available for synchronisation, it has to
@@ -907,6 +1231,8 @@
 algorithm. Their synchronisation distance has to be shorter than the distance
 of the selected source multiplied by the value of the limit. Also, their
 measured frequencies have to be close to the frequency of the selected source.
+If the selected source was specified with the \fBprefer\fP option, it can be
+combined only with other sources specified with this option.
 .sp
 By default, the limit is 3. Setting the limit to 0 effectively disables the
 source combining algorithm and only the selected source will be used to control
@@ -915,11 +1241,11 @@
 .sp
 \fBmaxdistance\fP \fIdistance\fP
 .RS 4
-The \fBmaxdistance\fP directive sets the maximum allowed root distance of the
-sources to not be rejected by the source selection algorithm. The distance
-includes the accumulated dispersion, which might be large when the source is no
-longer synchronised, and half of the total round\-trip delay to the primary
-source.
+The \fBmaxdistance\fP directive sets the maximum root distance of a source to be
+acceptable for synchronisation of the clock. Sources that have a distance
+larger than the specified distance will be rejected. The distance estimates the
+maximum error of the source. It includes the root dispersion and half of the
+root delay (round\-trip time) accumulated on the path to the primary source.
 .sp
 By default, the maximum root distance is 3 seconds.
 .sp
@@ -971,6 +1297,33 @@
 .RE
 .SS "System clock"
 .sp
+\fBclockprecision\fP \fIprecision\fP
+.RS 4
+The \fBclockprecision\fP directive specifies the precision of the system clock (in
+seconds). It is used by \fBchronyd\fP to estimate the minimum noise in NTP
+measurements and randomise low\-order bits of timestamps in NTP responses. By
+default, the precision is measured on start as the minimum time to read the
+clock.
+.sp
+The measured value works well in most cases. However, it generally
+overestimates the precision and it can be sensitive to the CPU speed, which can
+change over time to save power. In some cases with a high\-precision clocksource
+(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the
+precision on the server to a smaller value can improve stability of clients\(aq
+NTP measurements. The server\(cqs precision is reported on clients by the
+\fBntpdata\fP command.
+.sp
+An example setting the precision to 8 nanoseconds is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+clockprecision 8e\-9
+.fam
+.fi
+.if n .RE
+.RE
+.sp
 \fBcorrtimeratio\fP \fIratio\fP
 .RS 4
 When \fBchronyd\fP is slewing the system clock to correct an offset, the rate at
@@ -1018,15 +1371,13 @@
 .sp
 An example of the driftfile directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 driftfile @CHRONYVARDIR@/drift
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBfallbackdrift\fP \fImin\-interval\fP \fImax\-interval\fP
@@ -1041,15 +1392,13 @@
 update to switch between fallback drifts. They are defined as a power of 2 (in
 seconds). The syntax is as follows:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 fallbackdrift 16 19
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 In this example, the minimum interval is 16 (18 hours) and the maximum interval is
 19 (6 days). The system clock frequency will be set to the first fallback 18
@@ -1107,7 +1456,11 @@
 .RS 4
 No correction is applied to the clock for the leap second. The clock will be
 corrected later in normal operation when new measurements are made and the
-estimated offset includes the one second error.
+estimated offset includes the one second error. This option is particularly
+useful when multiple \fBchronyd\fP instances are running on the system, one
+controlling the system clock and others started with the \fB\-x\fP option, which
+should rely on the first instance to correct the system clock and ignore it for
+the correction of their own NTP clock running on top of the system clock.
 .RE
 .RE
 .sp
@@ -1121,7 +1474,7 @@
 enable a server leap smear.
 .sp
 When smearing a leap second, the leap status is suppressed on the server and
-the served time is corrected slowly be slewing instead of stepping. The clients
+the served time is corrected slowly by slewing instead of stepping. The clients
 do not need any special configuration as they do not know there is any leap
 second and they follow the server time which eventually brings them back to
 UTC. Care must be taken to ensure they use only NTP servers which smear the
@@ -1132,36 +1485,44 @@
 .sp
 A recommended configuration to enable a server leap smear is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 leapsecmode slew
 maxslewrate 1000
-smoothtime 400 0.001 leaponly
+smoothtime 400 0.001024 leaponly
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The first directive is necessary to disable the clock step which would reset
 the smoothing process. The second directive limits the slewing rate of the
 local clock to 1000 ppm, which improves the stability of the smoothing process
 when the local correction starts and ends. The third directive enables the
 server time smoothing process. It will start when the clock gets to 00:00:00
-UTC and it will take 17 hours 34 minutes to finish. The frequency offset will
-be changing by 0.001 ppm per second and will reach a maximum of 31.623 ppm. The
-\fBleaponly\fP option makes the duration of the leap smear constant and allows the
-clients to safely synchronise with multiple identically configured leap
-smearing servers.
+UTC and it will take 62500 seconds (about 17.36 hours) to finish. The frequency
+offset will be changing by 0.001024 ppm per second and will reach a maximum of
+32 ppm in 31250 seconds. The \fBleaponly\fP option makes the duration of the leap
+smear constant and allows the clients to safely synchronise with multiple
+identically configured leap smearing servers.
+.sp
+The duration of the leap smear can be calculated from the specified wander as
+.sp
+.if n .RS 4
+.nf
+.fam C
+duration = sqrt(4 / wander)
+.fam
+.fi
+.if n .RE
 .RE
 .sp
 \fBleapsectz\fP \fItimezone\fP
 .RS 4
-This directive specifies a timezone in the system tz database which \fBchronyd\fP
-can use to determine when will the next leap second occur and what is the
-current offset between TAI and UTC. It will periodically check if 23:59:59 and
-23:59:60 are valid times in the timezone. This typically works with the
+This directive specifies a timezone in the system timezone database which
+\fBchronyd\fP can use to determine when will the next leap second occur and what is
+the current offset between TAI and UTC. It will periodically check if 23:59:59
+and 23:59:60 are valid times in the timezone. This normally works with the
 \fIright/UTC\fP timezone.
 .sp
 When a leap second is announced, the timezone needs to be updated at least 12
@@ -1183,57 +1544,52 @@
 .sp
 An example of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 leapsectz right/UTC
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The following shell command verifies that the timezone contains leap seconds
 and can be used with this directive:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 $ TZ=right/UTC date \-d \(aqDec 31 2008 23:59:60\(aq
 Wed Dec 31 23:59:60 UTC 2008
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBmakestep\fP \fIthreshold\fP \fIlimit\fP
 .RS 4
 Normally \fBchronyd\fP will cause the system to gradually correct any time offset,
 by slowing down or speeding up the clock as required. In certain situations,
-the system clock might be so far adrift that this slewing process would take a
-very long time to correct the system clock.
+e.g. when \fBchronyd\fP is initially started, the system clock might be so far
+adrift that this slewing process would take a very long time to correct the
+system clock.
 .sp
 This directive forces \fBchronyd\fP to step the system clock if the adjustment is
 larger than a threshold value, but only if there were no more clock updates
-since \fBchronyd\fP was started than a specified limit (a negative value can be
-used to disable the limit).
+since \fBchronyd\fP was started than the specified limit. A negative value disables
+the limit.
 .sp
-This is particularly useful when using reference clocks, because the
-\fBinitstepslew\fP directive works only with NTP sources.
+On most systems it is desirable to step the system clock only on boot, before
+starting programs that rely on time advancing monotonically forwards.
 .sp
 An example of the use of this directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 makestep 0.1 3
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This would step the system clock if the adjustment is larger than 0.1 seconds, but
 only in the first three clock updates.
@@ -1250,15 +1606,13 @@
 .sp
 An example of the use of this directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 maxchange 1000 1 2
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 After the first clock update, \fBchronyd\fP will check the offset on every clock
 update, it will ignore two adjustments larger than 1000 seconds and exit on
@@ -1283,7 +1637,7 @@
 system clock. It limits the frequency adjustment that \fBchronyd\fP is allowed to
 use to correct the measured drift. It is an additional limit to the maximum
 adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
-on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
+on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on illumos).
 .sp
 By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
 limited by the system driver rather than this directive.
@@ -1291,7 +1645,7 @@
 .sp
 \fBmaxupdateskew\fP \fIskew\-in\-ppm\fP
 .RS 4
-One of \fBchronyd\fP\(cqs tasks is to work out how fast or slow the computer\(cqs clock
+One of \fBchronyd\fP\(aqs tasks is to work out how fast or slow the computer\(cqs clock
 runs relative to its reference sources. In addition, it computes an estimate of
 the error bounds around the estimated value.
 .sp
@@ -1303,8 +1657,8 @@
 estimate might be so unreliable that it should not be used. By default, the
 threshold is 1000 ppm.
 .sp
-Typical values for \fIskew\-in\-ppm\fP might be 100 for a dial\-up connection to
-servers over a phone line, and 5 or 10 for a computer on a LAN.
+Typical values for \fIskew\-in\-ppm\fP might be 100 for NTP sources polled over a
+wireless network, and 10 or smaller for sources on a local wired network.
 .sp
 It should be noted that this is not the only means of protection against using
 unreliable estimates. At all times, \fBchronyd\fP keeps track of both the estimated
@@ -1326,14 +1680,10 @@
 .sp
 For each system there is a maximum frequency offset of the clock that can be set
 by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
-is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
+is 5000 ppm, and on illumos it is 32500 ppm. Also, due to a kernel limitation,
 setting \fBmaxslewrate\fP on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
 ppm and 5000 ppm will effectively set it to 500 ppm.
 .sp
-In early beta releases of macOS 13 this capability is disabled because of a
-system kernel bug. When the kernel bug is fixed, chronyd will detect this and
-re\-enable the capability (see above limitations) with no recompilation required.
-.sp
 By default, the maximum slew rate is set to 83333.333 ppm (one twelfth).
 .RE
 .sp
@@ -1360,15 +1710,13 @@
 .sp
 The frequency compensation is calculated (in ppm) as
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-k0 + (T \- T0) * k1 + (T \- T0)^2 * k2
+.fam C
+comp = k0 + (T \- T0) * k1 + (T \- T0)^2 * k2
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The result has to be between \-10 ppm and 10 ppm, otherwise the measurement is
 considered invalid and will be ignored. The \fIk0\fP coefficient can be adjusted to
@@ -1376,15 +1724,13 @@
 .sp
 An example of the use is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 26000 0.0 0.000183 0.0
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The measured temperature will be read from the file in the Linux sysfs
 filesystem every 30 seconds. When the temperature is 26000 (26 degrees
@@ -1397,22 +1743,19 @@
 .sp
 An example is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 /etc/chrony.tempcomp
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 where the \fI/etc/chrony.tempcomp\fP file could have
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 20000 1.0
 21000 0.64
 22000 0.36
@@ -1424,10 +1767,9 @@
 28000 0.36
 29000 0.64
 30000 1.0
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 Valid measurements with corresponding compensations are logged to the
 \fItempcomp.log\fP file if enabled by the \fBlog tempcomp\fP directive.
@@ -1437,89 +1779,90 @@
 \fBallow\fP [\fBall\fP] [\fIsubnet\fP]
 .RS 4
 The \fBallow\fP directive is used to designate a particular subnet from which NTP
-clients are allowed to access the computer as an NTP server.
+clients are allowed to access the computer as an NTP server. It also controls
+access of NTS\-KE clients when NTS is enabled on the server.
 .sp
 The default is that no clients are allowed access, i.e. \fBchronyd\fP operates
 purely as an NTP client. If the \fBallow\fP directive is used, \fBchronyd\fP will be
 both a client of its servers, and a server to other clients.
 .sp
+This directive can be used multiple times.
+.sp
 Examples of the use of the directive are as follows:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 allow 1.2.3.4
-allow 1.2
+allow 3.4.5.0/24
 allow 3.4.5
-allow 6.7.8/22
-allow 6.7.8.9/22
 allow 2001:db8::/32
 allow 0/0
 allow ::/0
 allow
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
-The first directive allows a node with IPv4 address \fI1.2.3.4\fP to be an NTP
-client of this computer.
-The second directive allows any node with an IPv4 address of the form \fI1.2.x.y\fP
-(with \fIx\fP and \fIy\fP arbitrary) to be an NTP client of this computer. Likewise,
-the third directive allows any node with an IPv4 address of the form \fI3.4.5.x\fP
-to have client NTP access. The fourth and fifth forms allow access from any
-node with an IPv4 address of the form \fI6.7.8.x\fP, \fI6.7.9.x\fP, \fI6.7.10.x\fP or
-\fI6.7.11.x\fP (with \fIx\fP arbitrary), i.e. the value 22 is the number of bits
-defining the specified subnet. In the fifth form, the final byte is ignored.
-The sixth form is used for IPv6 addresses. The seventh and eighth forms allow
-access by any IPv4 and IPv6 node respectively. The ninth forms allows access by
-any node (IPv4 or IPv6).
+The first directive allows access from an IPv4 address. The second directive
+allows access from all computers in an IPv4 subnet specified in the CIDR
+notation. The third directive specifies the same subnet using a simpler
+notation where the prefix length is determined by the number of dots. The
+fourth directive specifies an IPv6 subnet. The fifth and sixth directives allow
+access from all IPv4 and IPv6 addresses respectively. The seventh directive
+allows access from all addresses (both IPv4 or IPv6).
 .sp
 A second form of the directive, \fBallow all\fP, has a greater effect, depending on
 the ordering of directives in the configuration file. To illustrate the effect,
 consider the two examples:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 allow 1.2.3.4
-deny 1.2.3
-allow 1.2
+deny 1.2.3.0/24
+allow 1.2.0.0/16
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 and
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 allow 1.2.3.4
-deny 1.2.3
-allow all 1.2
+deny 1.2.3.0/24
+allow all 1.2.0.0/16
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 In the first example, the effect is the same regardless of what order the three
-directives are given in. So the \fI1.2.x.y\fP subnet is allowed access, except for
-the \fI1.2.3.x\fP subnet, which is denied access, however the host \fI1.2.3.4\fP is
-allowed access.
+directives are given in. So the \fI1.2.0.0/16\fP subnet is allowed access, except
+for the \fI1.2.3.0/24\fP subnet, which is denied access, however the host \fI1.2.3.4\fP
+is allowed access.
 .sp
-In the second example, the \fBallow all 1.2\fP directives overrides the effect of
-\fIany\fP previous directive relating to a subnet within the specified subnet.
-Within a configuration file this capability is probably rather moot; however,
-it is of greater use for reconfiguration at run\-time via \fBchronyc\fP with the
-\fBallow all\fP command.
+In the second example, the \fBallow all 1.2.0.0/16\fP directive overrides the
+effect of \fIany\fP previous directive relating to a subnet within the specified
+subnet. Within a configuration file this capability is probably rather moot;
+however, it is of greater use for reconfiguration at run\-time via \fBchronyc\fP
+with the \fBallow all\fP command.
 .sp
-The directive allows a hostname to be specified instead of an IP address, but
-the name must be resolvable when \fBchronyd\fP is started (i.e. \fBchronyd\fP needs
-to be started when the network is already up and DNS is working).
+The rules are internally represented as a tree of tables with one level per
+four bits of the IPv4 or IPv6 address. The order of the \fBallow\fP and \fBdeny\fP
+directives matters if they modify the same records of one table, i.e. if one
+subnet is included in the other subnet and their prefix lengths are at the same
+level. For example, \fI1.2.3.0/28\fP and \fI1.2.3.0/29\fP are in different tables, but
+\fI1.2.3.0/25\fP and \fI1.2.3.0/28\fP are in the same table. The configuration can be
+verified for individual addresses with the \fBaccheck\fP
+command in \fBchronyc\fP.
+.sp
+A hostname can be specified in the directives instead of the IP address, but
+the name must be resolvable when \fBchronyd\fP is started, i.e. the network is
+already up and DNS is working. If the hostname resolves to multiple addresses,
+only the first address (in the order returned by the system resolver) will be
+allowed or denied.
 .sp
 Note, if the \fBinitstepslew\fP directive is used in the
 configuration file, each of the computers listed in that directive must allow
@@ -1529,9 +1872,10 @@
 \fBdeny\fP [\fBall\fP] [\fIsubnet\fP]
 .RS 4
 This is similar to the \fBallow\fP directive, except that it denies NTP
-client access to a particular subnet or host, rather than allowing it.
+and NTS\-KE client access to a particular subnet or host, rather than allowing
+it.
 .sp
-The syntax is identical.
+The syntax is identical and the directive can be used multiple times too.
 .sp
 There is also a \fBdeny all\fP directive with similar behaviour to the \fBallow all\fP
 directive.
@@ -1539,28 +1883,43 @@
 .sp
 \fBbindaddress\fP \fIaddress\fP
 .RS 4
-The \fBbindaddress\fP directive binds the socket on which \fBchronyd\fP listens for NTP
-requests to a local address of the computer. On systems other than Linux, the
-address of the computer needs to be already configured when \fBchronyd\fP is
-started.
+The \fBbindaddress\fP directive binds the sockets on which \fBchronyd\fP listens for
+NTP and NTS\-KE requests to a local address of the computer. On systems other
+than Linux, the address of the computer needs to be already configured when
+\fBchronyd\fP is started.
 .sp
 An example of the use of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 bindaddress 192.168.1.1
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 Currently, for each of the IPv4 and IPv6 protocols, only one \fBbindaddress\fP
 directive can be specified. Therefore, it is not useful on computers which
 should serve NTP on multiple network interfaces.
 .RE
 .sp
+\fBbinddevice\fP \fIinterface\fP
+.RS 4
+The \fBbinddevice\fP directive binds the NTP and NTS\-KE server sockets to a network
+device specified by the interface name. This directive can specify only one
+interface and it is supported on Linux only.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+binddevice eth0
+.fam
+.fi
+.if n .RE
+.RE
+.sp
 \fBbroadcast\fP \fIinterval\fP \fIaddress\fP [\fIport\fP]
 .RS 4
 The \fBbroadcast\fP directive is used to declare a broadcast address to which
@@ -1568,23 +1927,23 @@
 as a broadcast server). Broadcast clients on that subnet will be able to
 synchronise.
 .sp
+This directive can be used multiple times to specify multiple addresses.
+.sp
 The syntax is as follows:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-broadcast 30 192.168.1.255
-broadcast 60 192.168.2.255 12123
-broadcast 60 ff02::101
+.fam C
+broadcast 32 192.168.1.255
+broadcast 64 192.168.2.255 12123
+broadcast 64 ff02::101
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 In the first example, the destination port defaults to UDP port 123 (the normal NTP
 port). In the second example, the destination port is specified as 12123. The
-first parameter in each case (30 or 60 respectively) is the interval in seconds
+first parameter in each case (32 or 64 respectively) is the interval in seconds
 between broadcast packets being sent. The second parameter in each case is the
 broadcast address to send the packet to. This should correspond to the
 broadcast address of one of the network interfaces on the computer where
@@ -1609,23 +1968,22 @@
 This directive specifies the maximum amount of memory that \fBchronyd\fP is allowed
 to allocate for logging of client accesses and the state that \fBchronyd\fP as an
 NTP server needs to support the interleaved mode for its clients. The default
-limit is 524288 bytes, which is sufficient for monitoring about four thousand
-clients at the same time.
-.sp
-In older \fBchrony\fP versions if the limit was set to 0, the memory allocation was
-unlimited.
+limit is 524288 bytes, which enables monitoring of up to 4096 IP addresses at
+the same time and holding NTP timestamps for up to 4096 clients using the
+interleaved mode (depending on uniformity of their polling interval). The
+number of addresses and timestamps is always a power of 2. The maximum
+effective value is 2147483648 (2 GB), which corresponds to 16777216 addresses
+and timestamps.
 .sp
 An example of the use of this directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 clientloglimit 1048576
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBnoclientlog\fP
@@ -1675,15 +2033,13 @@
 The current root distance can be calculated from root delay and root dispersion
 (reported by the \fBtracking\fP command in \fBchronyc\fP) as:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 distance = delay / 2 + dispersion
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBorphan\fP
@@ -1698,9 +2054,11 @@
 that poll more than one server. Each server needs to be configured to poll all
 other servers with the \fBlocal\fP directive. This ensures only the server with the
 smallest reference ID has the local reference active and others are
-synchronised to it. When that server fails, another will take over.
+synchronised to it. If that server stops responding, the server with the second
+smallest reference ID will take over when its local reference mode activates
+(root distance reaches the threshold configured by the \fBdistance\fP option).
 .sp
-The \fBorphan\fP mode is compatible with the \fBntpd\fP\(cqs orphan mode (enabled by the
+The \fBorphan\fP mode is compatible with the \fBntpd\fP\(aqs orphan mode (enabled by the
 \fBtos orphan\fP command).
 .RE
 .RE
@@ -1710,15 +2068,13 @@
 .sp
 An example of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-local stratum 10 orphan
+.fam C
+local stratum 10 orphan distance 0.1
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBntpsigndsocket\fP \fIdirectory\fP
@@ -1735,15 +2091,131 @@
 .sp
 An example of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 ntpsigndsocket /var/lib/samba/ntp_signd
+.fam
 .fi
-.if n \{\
+.if n .RE
 .RE
-.\}
+.sp
+\fBntsport\fP \fIport\fP
+.RS 4
+This directive specifies the TCP port on which \fBchronyd\fP will provide the NTS
+Key Establishment (NTS\-KE) service. The default port is 4460.
+.sp
+The port will be open only when a certificate and key is specified by the
+\fBntsservercert\fP and \fBntsserverkey\fP directives.
+.RE
+.sp
+\fBntsservercert\fP \fIfile\fP
+.RS 4
+This directive specifies a file containing a certificate in the PEM format
+for \fBchronyd\fP to operate as an NTS server. The file should also include
+any intermediate certificates that the clients will need to validate the
+server\(cqs certificate. The file needs to be readable by the user under which
+\fBchronyd\fP is running after dropping root privileges.
+.sp
+This directive can be used multiple times to specify multiple certificates for
+different names of the server.
+.sp
+The files are loaded only once. \fBchronyd\fP needs to be restarted in order to
+load a renewed certificate. The \fBntsdumpdir\fP and
+\fBdumpdir\fP directives with the \fB\-r\fP option of \fBchronyd\fP are
+recommended for a near\-seamless server operation.
+.RE
+.sp
+\fBntsserverkey\fP \fIfile\fP
+.RS 4
+This directive specifies a file containing a private key in the PEM format
+for \fBchronyd\fP to operate as an NTS server. The file needs to be readable by
+the user under which \fBchronyd\fP is running after dropping root privileges. For
+security reasons, it should not be readable by other users.
+.sp
+This directive can be used multiple times to specify multiple keys. The number
+of keys must be the same as the number of certificates and the corresponding
+files must be specified in the same order.
+.RE
+.sp
+\fBntsprocesses\fP \fIprocesses\fP
+.RS 4
+This directive specifies how many helper processes will \fBchronyd\fP operating
+as an NTS server start for handling client NTS\-KE requests in order to improve
+performance with multi\-core CPUs and multithreading. If set to 0, no helper
+process will be started and all NTS\-KE requests will be handled by the main
+\fBchronyd\fP process. The default value is 1.
+.RE
+.sp
+\fBmaxntsconnections\fP \fIconnections\fP
+.RS 4
+This directive specifies the maximum number of concurrent NTS\-KE connections
+per process that the NTS server will accept. The default value is 100. The
+maximum practical value is half of the system \fBFD_SETSIZE\fP constant (usually
+1024).
+.RE
+.sp
+\fBntsdumpdir\fP \fIdirectory\fP
+.RS 4
+This directive specifies a directory where \fBchronyd\fP operating as an NTS server
+can save the keys which encrypt NTS cookies provided to clients. The keys are
+saved to a single file named \fIntskeys\fP. When \fBchronyd\fP is restarted, reloading
+the keys allows the clients to continue using old cookies and avoids a storm of
+NTS\-KE requests. By default, the server does not save the keys.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+ntsdumpdir @CHRONYVARDIR@
+.fam
+.fi
+.if n .RE
+.sp
+This directory is used also by the NTS client to save NTS cookies.
+.RE
+.sp
+\fBntsntpserver\fP \fIhostname\fP
+.RS 4
+This directive specifies the hostname (as a fully qualified domain name) or
+address of the NTP server(s) which is
+provided in the NTS\-KE response to the clients. It allows the NTS\-KE server to
+be separated from the NTP server. However, the servers need to share the keys,
+i.e. external key management needs to be enabled by setting
+\fBntsrotate\fP to 0. By default, no hostname or address is provided
+to the clients, which means they should use the same server for NTS\-KE and NTP.
+.RE
+.sp
+\fBntsrotate\fP \fIinterval\fP
+.RS 4
+This directive specifies the rotation interval (in seconds) of the server key
+which encrypts the NTS cookies. New keys are generated automatically from the
+\fI/dev/urandom\fP device. The server keeps two previous keys to give the clients
+time to get new cookies encrypted by the latest key. The interval is measured
+as the server\(cqs operating time, i.e. the actual interval can be longer if
+\fBchronyd\fP is not running continuously. The default interval is 604800 seconds
+(1 week). The maximum value is 2^31\-1 (68 years).
+.sp
+The automatic rotation of the keys can be disabled by setting \fBntsrotate\fP to 0.
+In this case the keys are assumed to be managed externally. \fBchronyd\fP will not
+save the keys to the \fIntskeys\fP file and will reload the keys from the file when
+the \fBrekey\fP command is issued in \fBchronyc\fP. The file can
+be periodically copied from another server running \fBchronyd\fP (which does
+not have \fBntsrotate\fP set to 0) in order to have one or more servers dedicated
+to NTS\-KE. The NTS\-KE servers need to be configured with the
+\fBntsntpserver\fP directive to point the clients to the right NTP
+server.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+ntsrotate 2592000
+.fam
+.fi
+.if n .RE
 .RE
 .sp
 \fBport\fP \fIport\fP
@@ -1775,7 +2247,7 @@
 The \fBratelimit\fP directive supports a number of options (which can be defined
 in any order):
 .sp
-\fBinterval\fP
+\fBinterval\fP \fIinterval\fP
 .RS 4
 This option sets the minimum interval between responses. It is defined as a
 power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
@@ -1785,7 +2257,7 @@
 than the specified interval).
 .RE
 .sp
-\fBburst\fP
+\fBburst\fP \fIresponses\fP
 .RS 4
 This option sets the maximum number of responses that can be sent in a burst,
 temporarily exceeding the limit specified by the \fBinterval\fP option. This is
@@ -1794,7 +2266,7 @@
 maximum value is 255.
 .RE
 .sp
-\fBleak\fP
+\fBleak\fP \fIrate\fP
 .RS 4
 This option sets the rate at which responses are randomly allowed even if the
 limits specified by the \fBinterval\fP and \fBburst\fP options are exceeded. This is
@@ -1811,21 +2283,36 @@
 .sp
 An example use of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 ratelimit interval 1 burst 16
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This would reduce the response rate for IP addresses sending packets on average
 more than once per 2 seconds, or sending packets in bursts of more than 16
 packets, by up to 75% (with default \fBleak\fP of 2).
 .RE
 .sp
+\fBntsratelimit\fP [\fIoption\fP]...
+.RS 4
+This directive enables rate limiting of NTS\-KE requests. It is similar to the
+\fBratelimit\fP directive, except the default interval is 6
+(1 connection per 64 seconds).
+.sp
+An example of the use of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+ntsratelimit interval 3 burst 1
+.fam
+.fi
+.if n .RE
+.RE
+.sp
 \fBsmoothtime\fP \fImax\-freq\fP \fImax\-wander\fP [\fBleaponly\fP]
 .RS 4
 The \fBsmoothtime\fP directive can be used to enable smoothing of the time that
@@ -1867,34 +2354,30 @@
 An example suitable for clients using \fBntpd\fP and 1024 second polling interval
 could be:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 smoothtime 400 0.001
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 An example suitable for clients using \fBchronyd\fP on Linux could be:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 smoothtime 50000 0.01
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .SS "Command and monitoring access"
 .sp
 \fBbindcmdaddress\fP \fIaddress\fP
 .RS 4
-The \fBbindcmdaddress\fP directive allows you to specify an IP address of an
-interface on which \fBchronyd\fP will listen for monitoring command packets (issued
+The \fBbindcmdaddress\fP directive specifies a local IP address to which \fBchronyd\fP
+will bind the UDP socket listening for monitoring command packets (issued
 by \fBchronyc\fP). On systems other than Linux, the address of the interface needs
 to be already configured when \fBchronyd\fP is started.
 .sp
@@ -1905,20 +2388,19 @@
 path of the socket is \fI@CHRONYRUNDIR@/chronyd.sock\fP. The socket can be
 disabled by setting the path to \fI/\fP.
 .sp
-By default, \fBchronyd\fP binds to the loopback interface (with addresses
-\fI127.0.0.1\fP and \fI::1\fP). This blocks all access except from localhost. To listen
-for command packets on all interfaces, you can add the lines:
+By default, \fBchronyd\fP binds the UDP sockets to the addresses \fI127.0.0.1\fP and
+\fI::1\fP (i.e. the loopback interface). This blocks all access except from
+localhost. To listen for command packets on all interfaces, you can add the
+lines:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 bindcmdaddress 0.0.0.0
 bindcmdaddress ::
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 to the configuration file.
 .sp
@@ -1927,15 +2409,30 @@
 .sp
 An example that sets the path of the Unix domain command socket is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 bindcmdaddress /var/run/chrony/chronyd.sock
+.fam
 .fi
-.if n \{\
+.if n .RE
 .RE
-.\}
+.sp
+\fBbindcmddevice\fP \fIinterface\fP
+.RS 4
+The \fBbindcmddevice\fP directive binds the UDP command sockets to a network device
+specified by the interface name. This directive can specify only one interface
+and it is supported on Linux only.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+bindcmddevice eth0
+.fam
+.fi
+.if n .RE
 .RE
 .sp
 \fBcmdallow\fP [\fBall\fP] [\fIsubnet\fP]
@@ -1975,15 +2472,13 @@
 .sp
 An example shows the syntax:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 cmdport 257
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This would make \fBchronyd\fP use UDP 257 as its command port. (\fBchronyc\fP would
 need to be run with the \fB\-p 257\fP switch to inter\-operate correctly.)
@@ -1998,15 +2493,13 @@
 .sp
 An example of the use of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 cmdratelimit interval 2
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .SS "Real\-time clock (RTC)"
 .sp
@@ -2021,15 +2514,13 @@
 .sp
 An example of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 hwclockfile /etc/adjtime
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBrtcautotrim\fP \fIthreshold\fP
@@ -2038,21 +2529,20 @@
 automatically. When the system clock is synchronised and the estimated error
 between the two clocks is larger than the specified threshold, \fBchronyd\fP will
 trim the RTC as if the \fBtrimrtc\fP command in \fBchronyc\fP
-was issued.
+was issued. The trimming operation is accurate to only about 1 second, which is
+the minimum effective threshold.
 .sp
 This directive is effective only with the \fBrtcfile\fP directive.
 .sp
 An example of the use of this directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 rtcautotrim 30
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This would set the threshold error to 30 seconds.
 .RE
@@ -2070,15 +2560,13 @@
 .sp
 An example of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 rtcfile @CHRONYVARDIR@/rtc
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 \fBchronyd\fP saves information in this file when it exits and when the \fBwritertc\fP
 command is issued in \fBchronyc\fP. The information saved is the RTC\(cqs error at
@@ -2095,8 +2583,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 You are running Linux.
 .RE
@@ -2106,8 +2594,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 The kernel is compiled with extended real\-time clock support (i.e. the
 \fI/dev/rtc\fP device is capable of doing useful things).
@@ -2118,8 +2606,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 You do not have other applications that need to make use of \fI/dev/rtc\fP at all.
 .RE
@@ -2142,8 +2630,8 @@
 The directive takes no arguments. It is equivalent to specifying the \fB\-u\fP
 switch to the Linux \fBhwclock\fP program.
 .sp
-Note that this setting is overridden when the \fBhwclockfile\fP
-directive is specified.
+Note that this setting is overridden by the \fBhwclockfile\fP file
+and is not relevant for the \fBrtcsync\fP directive.
 .RE
 .sp
 \fBrtcsync\fP
@@ -2176,16 +2664,14 @@
 actually appears as a single line in the file) from the log file is shown
 below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 2016\-11\-09 05:40:50 203.0.113.15    N  2 111 111 1111  10 10 1.0 \(rs
    \-4.966e\-03  2.296e\-01  1.577e\-05  1.615e\-01  7.446e\-03 CB00717B 4B D K
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows (the quantities in square brackets are the values
 from the example line above):
@@ -2195,8 +2681,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 Date [2015\-10\-13]
 .RE
@@ -2206,8 +2692,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the
 local time zone. [05:40:50]
@@ -2218,8 +2704,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 IP address of server or peer from which measurement came [203.0.113.15]
 .RE
@@ -2229,8 +2715,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 Leap status (\fIN\fP means normal, \fI+\fP means that the last minute of the current
 month has 61 seconds, \fI\-\fP means that the last minute of the month has 59
@@ -2242,8 +2728,8 @@
 \h'-04' 5.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 5." 4.2
+.  sp -1
+.  IP " 5." 4.2
 .\}
 Stratum of remote computer. [2]
 .RE
@@ -2253,8 +2739,8 @@
 \h'-04' 6.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 6." 4.2
+.  sp -1
+.  IP " 6." 4.2
 .\}
 RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111]
 .RE
@@ -2264,8 +2750,8 @@
 \h'-04' 7.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 7." 4.2
+.  sp -1
+.  IP " 7." 4.2
 .\}
 RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
 .RE
@@ -2275,8 +2761,8 @@
 \h'-04' 8.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 8." 4.2
+.  sp -1
+.  IP " 8." 4.2
 .\}
 Tests for maximum delay, maximum delay ratio and maximum delay dev ratio,
 against defined parameters, and a test for synchronisation loop (1=pass,
@@ -2288,8 +2774,8 @@
 \h'-04' 9.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 9." 4.2
+.  sp -1
+.  IP " 9." 4.2
 .\}
 Local poll [10]
 .RE
@@ -2299,8 +2785,8 @@
 \h'-04' 10.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 10." 4.2
+.  sp -1
+.  IP " 10." 4.2
 .\}
 Remote poll [10]
 .RE
@@ -2310,8 +2796,8 @@
 \h'-04' 11.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 11." 4.2
+.  sp -1
+.  IP " 11." 4.2
 .\}
 \(oqScore\(cq (an internal score within each polling level used to decide when to
 increase or decrease the polling level. This is adjusted based on number of
@@ -2323,8 +2809,8 @@
 \h'-04' 12.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 12." 4.2
+.  sp -1
+.  IP " 12." 4.2
 .\}
 The estimated local clock error (\fItheta\fP in RFC 5905). Positive indicates
 that the local clock is slow of the remote source. [\-4.966e\-03]
@@ -2335,8 +2821,8 @@
 \h'-04' 13.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 13." 4.2
+.  sp -1
+.  IP " 13." 4.2
 .\}
 The peer delay (\fIdelta\fP in RFC 5905). [2.296e\-01]
 .RE
@@ -2346,8 +2832,8 @@
 \h'-04' 14.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 14." 4.2
+.  sp -1
+.  IP " 14." 4.2
 .\}
 The peer dispersion (\fIepsilon\fP in RFC 5905). [1.577e\-05]
 .RE
@@ -2357,8 +2843,8 @@
 \h'-04' 15.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 15." 4.2
+.  sp -1
+.  IP " 15." 4.2
 .\}
 The root delay (\fIDELTA\fP in RFC 5905). [1.615e\-01]
 .RE
@@ -2368,8 +2854,8 @@
 \h'-04' 16.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 16." 4.2
+.  sp -1
+.  IP " 16." 4.2
 .\}
 The root dispersion (\fIEPSILON\fP in RFC 5905). [7.446e\-03]
 .RE
@@ -2379,8 +2865,8 @@
 \h'-04' 17.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 17." 4.2
+.  sp -1
+.  IP " 17." 4.2
 .\}
 Reference ID of the server\(cqs source as a hexadecimal number. [CB00717B]
 .RE
@@ -2390,8 +2876,8 @@
 \h'-04' 18.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 18." 4.2
+.  sp -1
+.  IP " 18." 4.2
 .\}
 NTP mode of the received packet (\fI1\fP=active peer, \fI2\fP=passive peer,
 \fI4\fP=server, \fIB\fP=basic, \fII\fP=interleaved). [4B]
@@ -2402,8 +2888,8 @@
 \h'-04' 19.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 19." 4.2
+.  sp -1
+.  IP " 19." 4.2
 .\}
 Source of the local transmit timestamp
 (\fID\fP=daemon, \fIK\fP=kernel, \fIH\fP=hardware). [D]
@@ -2414,8 +2900,8 @@
 \h'-04' 20.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 20." 4.2
+.  sp -1
+.  IP " 20." 4.2
 .\}
 Source of the local receive timestamp
 (\fID\fP=daemon, \fIK\fP=kernel, \fIH\fP=hardware). [K]
@@ -2436,16 +2922,14 @@
 \fIstatistics.log\fP. An example line (which actually appears as a single line in
 the file) from the log file is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 2016\-08\-10 05:40:50 203.0.113.15     6.261e\-03 \-3.247e\-03 \(rs
      2.220e\-03  1.874e\-06  1.080e\-06 7.8e\-02  16   0   8  0.00
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows (the quantities in square brackets are the values
 from the example line above):
@@ -2455,8 +2939,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 Date [2015\-07\-22]
 .RE
@@ -2466,8 +2950,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 Hour:Minute:Second. Note that the date\-time pair is expressed in
 UTC, not the local time zone. [05:40:50]
@@ -2478,8 +2962,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 IP address of server or peer from which measurement comes [203.0.113.15]
 .RE
@@ -2489,8 +2973,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 The estimated standard deviation of the measurements from the source (in
 seconds). [6.261e\-03]
@@ -2501,8 +2985,8 @@
 \h'-04' 5.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 5." 4.2
+.  sp -1
+.  IP " 5." 4.2
 .\}
 The estimated offset of the source (in seconds, positive means the local
 clock is estimated to be fast, in this case). [\-3.247e\-03]
@@ -2513,8 +2997,8 @@
 \h'-04' 6.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 6." 4.2
+.  sp -1
+.  IP " 6." 4.2
 .\}
 The estimated standard deviation of the offset estimate (in seconds).
 [2.220e\-03]
@@ -2525,8 +3009,8 @@
 \h'-04' 7.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 7." 4.2
+.  sp -1
+.  IP " 7." 4.2
 .\}
 The estimated rate at which the local clock is gaining or losing time
 relative to the source (in seconds per second, positive means the local clock
@@ -2540,8 +3024,8 @@
 \h'-04' 8.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 8." 4.2
+.  sp -1
+.  IP " 8." 4.2
 .\}
 The estimated error in the rate value (in seconds per second). [1.080e\-06].
 .RE
@@ -2551,8 +3035,8 @@
 \h'-04' 9.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 9." 4.2
+.  sp -1
+.  IP " 9." 4.2
 .\}
 The ratio of |old_rate \- new_rate| / old_rate_error. Large values
 indicate the statistics are not modelling the source very well. [7.8e\-02]
@@ -2563,8 +3047,8 @@
 \h'-04' 10.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 10." 4.2
+.  sp -1
+.  IP " 10." 4.2
 .\}
 The number of measurements currently being used for the regression
 algorithm. [16]
@@ -2575,8 +3059,8 @@
 \h'-04' 11.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 11." 4.2
+.  sp -1
+.  IP " 11." 4.2
 .\}
 The new starting index (the oldest sample has index 0; this is the method
 used to prune old samples when it no longer looks like the measurements fit a
@@ -2588,8 +3072,8 @@
 \h'-04' 12.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 12." 4.2
+.  sp -1
+.  IP " 12." 4.2
 .\}
 The number of runs. The number of runs of regression residuals with the same
 sign is computed. If this is too small it indicates that the measurements are
@@ -2604,8 +3088,8 @@
 \h'-04' 13.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 13." 4.2
+.  sp -1
+.  IP " 13." 4.2
 .\}
 The estimated or configured asymmetry of network jitter on the path to the
 source which was used to correct the measured offsets. The asymmetry can be
@@ -2622,16 +3106,14 @@
 actually appears as a single line in the file) from the log file is shown
 below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 2017\-08\-22 13:22:36 203.0.113.15     2     \-3.541      0.075 \-8.621e\-06 N \(rs
             2  2.940e\-03 \-2.084e\-04  1.534e\-02  3.472e\-04  8.304e\-03
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows (the quantities in square brackets are the
 values from the example line above) :
@@ -2641,8 +3123,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 Date [2017\-08\-22]
 .RE
@@ -2652,8 +3134,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the
 local time zone. [13:22:36]
@@ -2664,8 +3146,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 The IP address of the server or peer to which the local system is synchronised.
 [203.0.113.15]
@@ -2676,8 +3158,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 The stratum of the local system. [2]
 .RE
@@ -2687,8 +3169,8 @@
 \h'-04' 5.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 5." 4.2
+.  sp -1
+.  IP " 5." 4.2
 .\}
 The local system frequency (in ppm, positive means the local system runs fast
 of UTC). [\-3.541]
@@ -2699,8 +3181,8 @@
 \h'-04' 6.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 6." 4.2
+.  sp -1
+.  IP " 6." 4.2
 .\}
 The error bounds on the frequency (in ppm). [0.075]
 .RE
@@ -2710,8 +3192,8 @@
 \h'-04' 7.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 7." 4.2
+.  sp -1
+.  IP " 7." 4.2
 .\}
 The estimated local offset at the epoch, which is normally corrected by
 slewing the local clock (in seconds, positive indicates the clock is fast of
@@ -2723,8 +3205,8 @@
 \h'-04' 8.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 8." 4.2
+.  sp -1
+.  IP " 8." 4.2
 .\}
 Leap status (\fIN\fP means normal, \fI+\fP means that the last minute of this month
 has 61 seconds, \fI\-\fP means that the last minute of the month has 59 seconds,
@@ -2736,8 +3218,8 @@
 \h'-04' 9.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 9." 4.2
+.  sp -1
+.  IP " 9." 4.2
 .\}
 The number of combined sources. [2]
 .RE
@@ -2747,8 +3229,8 @@
 \h'-04' 10.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 10." 4.2
+.  sp -1
+.  IP " 10." 4.2
 .\}
 The estimated standard deviation of the combined offset (in seconds).
 [2.940e\-03]
@@ -2759,8 +3241,8 @@
 \h'-04' 11.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 11." 4.2
+.  sp -1
+.  IP " 11." 4.2
 .\}
 The remaining offset correction from the previous update (in seconds,
 positive means the system clock is slow of UTC). [\-2.084e\-04]
@@ -2771,8 +3253,8 @@
 \h'-04' 12.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 12." 4.2
+.  sp -1
+.  IP " 12." 4.2
 .\}
 The total of the network path delays to the reference clock to which
 the local clock is ultimately synchronised (in seconds). [1.534e\-02]
@@ -2783,8 +3265,8 @@
 \h'-04' 13.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 13." 4.2
+.  sp -1
+.  IP " 13." 4.2
 .\}
 The total dispersion accumulated through all the servers back to the
 reference clock to which the local clock is ultimately synchronised
@@ -2796,8 +3278,8 @@
 \h'-04' 14.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 14." 4.2
+.  sp -1
+.  IP " 14." 4.2
 .\}
 The maximum estimated error of the system clock in the interval since the
 previous update (in seconds). It includes the offset, remaining offset
@@ -2812,16 +3294,14 @@
 line (which actually appears as a single line in the file) from the \fIrtc.log\fP
 file is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 2015\-07\-22 05:40:50     \-0.037360 1       \-0.037434\(rs
           \-37.948  12   5  120
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows (the quantities in square brackets are the
 values from the example line above):
@@ -2831,8 +3311,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 Date [2015\-07\-22]
 .RE
@@ -2842,8 +3322,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the
 local time zone. [05:40:50]
@@ -2854,8 +3334,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 The measured offset between the RTC and the system clock in seconds.
 Positive indicates that the RTC is fast of the system time [\-0.037360].
@@ -2866,8 +3346,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 Flag indicating whether the regression has produced valid coefficients.
 (1 for yes, 0 for no). [1]
@@ -2878,8 +3358,8 @@
 \h'-04' 5.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 5." 4.2
+.  sp -1
+.  IP " 5." 4.2
 .\}
 Offset at the current time predicted by the regression process. A large
 difference between this value and the measured offset tends to indicate that
@@ -2891,8 +3371,8 @@
 \h'-04' 6.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 6." 4.2
+.  sp -1
+.  IP " 6." 4.2
 .\}
 The rate at which the RTC is losing or gaining time relative to the system
 clock. In ppm, with positive indicating that the RTC is gaining time.
@@ -2904,8 +3384,8 @@
 \h'-04' 7.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 7." 4.2
+.  sp -1
+.  IP " 7." 4.2
 .\}
 The number of measurements used in the regression. [12]
 .RE
@@ -2915,8 +3395,8 @@
 \h'-04' 8.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 8." 4.2
+.  sp -1
+.  IP " 8." 4.2
 .\}
 The number of runs of regression residuals of the same sign. Low values
 indicate that a straight line is no longer a good model of the measured data
@@ -2928,8 +3408,8 @@
 \h'-04' 9.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 9." 4.2
+.  sp -1
+.  IP " 9." 4.2
 .\}
 The measurement interval used prior to the measurement being made (in
 seconds). [120]
@@ -2942,15 +3422,13 @@
 called \fIrefclocks.log\fP. An example line (which actually appears as a single
 line in the file) from the log file is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 2009\-11\-30 14:33:27.000000 PPS2    7 N 1  4.900000e\-07 \-6.741777e\-07  1.000e\-06
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows (the quantities in square brackets are the values
 from the example line above):
@@ -2960,8 +3438,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 Date [2009\-11\-30]
 .RE
@@ -2971,8 +3449,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 Hour:Minute:Second.Microsecond. Note that the date\-time pair is expressed in
 UTC, not the local time zone. [14:33:27.000000]
@@ -2983,8 +3461,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 Reference ID of the reference clock from which the measurement came. [PPS2]
 .RE
@@ -2994,8 +3472,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 Sequence number of driver poll within one polling interval for raw samples,
 or \fI\-\fP for filtered samples. [7]
@@ -3006,8 +3484,8 @@
 \h'-04' 5.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 5." 4.2
+.  sp -1
+.  IP " 5." 4.2
 .\}
 Leap status (\fIN\fP means normal, \fI+\fP means that the last minute of the current
 month has 61 seconds, \fI\-\fP means that the last minute of the month has 59
@@ -3019,8 +3497,8 @@
 \h'-04' 6.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 6." 4.2
+.  sp -1
+.  IP " 6." 4.2
 .\}
 Flag indicating whether the sample comes from PPS source. (1 for yes,
 0 for no, or \fI\-\fP for filtered sample). [1]
@@ -3031,8 +3509,8 @@
 \h'-04' 7.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 7." 4.2
+.  sp -1
+.  IP " 7." 4.2
 .\}
 Local clock error measured by reference clock driver, or \fI\-\fP for filtered sample.
 [4.900000e\-07]
@@ -3043,8 +3521,8 @@
 \h'-04' 8.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 8." 4.2
+.  sp -1
+.  IP " 8." 4.2
 .\}
 Local clock error with applied corrections. Positive indicates that the local
 clock is slow. [\-6.741777e\-07]
@@ -3055,8 +3533,8 @@
 \h'-04' 9.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 9." 4.2
+.  sp -1
+.  IP " 9." 4.2
 .\}
 Assumed dispersion of the sample. [1.000e\-06]
 .RE
@@ -3068,15 +3546,13 @@
 a file called \fItempcomp.log\fP. An example line (which actually appears as a
 single line in the file) from the log file is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 2015\-04\-19 10:39:48  2.8000e+04  3.6600e\-01
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows (the quantities in square brackets are the values
 from the example line above):
@@ -3086,8 +3562,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 Date [2015\-04\-19]
 .RE
@@ -3097,8 +3573,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 Hour:Minute:Second. Note that the date\-time pair is expressed in UTC, not the
 local time zone. [10:39:48]
@@ -3109,8 +3585,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 Temperature read from the sensor. [2.8000e+04]
 .RE
@@ -3120,8 +3596,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 Applied compensation in ppm, positive means the system clock is running
 faster than it would be without the compensation. [3.6600e\-01]
@@ -3133,15 +3609,13 @@
 .RS 4
 An example of the directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 log measurements statistics tracking
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBlogbanner\fP \fIentries\fP
@@ -3165,15 +3639,13 @@
 .sp
 An example of the use is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 logchange 0.1
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 which would cause a syslog message to be generated if a system clock error of over
 0.1 seconds starts to be compensated.
@@ -3181,20 +3653,19 @@
 .sp
 \fBlogdir\fP \fIdirectory\fP
 .RS 4
-This directive allows the directory where log files are written to be
-specified.
+This directive specifies the directory for writing log files enabled by the
+\fBlog\fP directive. If the directory does not exist, it will be created
+automatically.
 .sp
 An example of the use of this directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 logdir /var/log/chrony
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBmailonchange\fP \fIemail\fP \fIthreshold\fP
@@ -3205,15 +3676,13 @@
 .sp
 An example of the use of this directive is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 mailonchange root@localhost 0.5
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This would send a mail message to root if a change of more than 0.5 seconds
 were applied to the system clock.
@@ -3224,6 +3693,75 @@
 .RE
 .SS "Miscellaneous"
 .sp
+\fBconfdir\fP \fIdirectory\fP...
+.RS 4
+The \fBconfdir\fP directive includes configuration files with the \fI.conf\fP suffix
+from a directory. The files are included in the lexicographical order of the
+file names.
+.sp
+Multiple directories (up to 10) can be specified with a single \fBconfdir\fP
+directive. In this case, if multiple directories contain a file with the same
+name, only the first file in the order of the specified directories will be
+included. This enables a fragmented configuration where existing fragments can
+be replaced by adding files to a different directory.
+.sp
+This directive can be used multiple times.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+confdir @SYSCONFDIR@/chrony.d
+.fam
+.fi
+.if n .RE
+.RE
+.sp
+\fBsourcedir\fP \fIdirectory\fP...
+.RS 4
+The \fBsourcedir\fP directive is identical to the \fBconfdir\fP directive, except the
+configuration files have the \fI.sources\fP suffix, they can only specify NTP
+sources (i.e. the \fBserver\fP, \fBpool\fP, and \fBpeer\fP directives), they are expected
+to have all lines terminated by the newline character, and they can be
+reloaded by the \fBreload sources\fP command in
+\fBchronyc\fP. It is particularly useful with dynamic sources like NTP servers
+received from a DHCP server, which can be written to a file specific to the
+network interface by a networking script.
+.sp
+This directive can be used multiple times.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+sourcedir /var/run/chrony\-dhcp
+.fam
+.fi
+.if n .RE
+.RE
+.sp
+\fBinclude\fP \fIpattern\fP
+.RS 4
+The \fBinclude\fP directive includes a configuration file, or multiple configuration
+files if a wildcard pattern is specified. Unlike with the \fBconfdir\fP directive,
+the full name of the files needs to be specified and at least one file is
+required to exist.
+.sp
+This directive can be used multiple times.
+.sp
+An example of the directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+include @SYSCONFDIR@/chrony.d/*.conf
+.fam
+.fi
+.if n .RE
+.RE
+.sp
 \fBhwtimestamp\fP \fIinterface\fP [\fIoption\fP]...
 .RS 4
 This directive enables hardware timestamping of NTP packets sent to and
@@ -3243,7 +3781,7 @@
 capabilities should include \fISOF_TIMESTAMPING_RAW_HARDWARE\fP,
 \fISOF_TIMESTAMPING_TX_HARDWARE\fP, and \fISOF_TIMESTAMPING_RX_HARDWARE\fP. Receive
 filter \fIHWTSTAMP_FILTER_ALL\fP, or \fIHWTSTAMP_FILTER_NTP_ALL\fP, is necessary for
-timestamping of received packets. Timestamping of packets received from bridged
+timestamping of received NTP packets. Timestamping of packets received on bridged
 and bonded interfaces is supported on Linux 4.13 and newer. When \fBchronyd\fP is
 running, no other process (e.g. a PTP daemon) should be working with the NIC
 clock.
@@ -3254,8 +3792,9 @@
 measurements\fP directive, and the \fBntpdata\fP report in
 \fBchronyc\fP.
 .sp
-If the specified interface is \fI*\fP, \fBchronyd\fP will try to enable HW timestamping
-on all available interfaces.
+This directive can be used multiple times to enable HW timestamping on multiple
+interfaces. If the specified interface is \fI*\fP, \fBchronyd\fP will try to enable HW
+timestamping on all available interfaces.
 .sp
 The \fBhwtimestamp\fP directive has the following options:
 .sp
@@ -3268,6 +3807,18 @@
 of a second).
 .RE
 .sp
+\fBminsamples\fP \fIsamples\fP
+.RS 4
+This option specifies the minimum number of readings kept for tracking of the
+NIC clock. The default value is 2.
+.RE
+.sp
+\fBmaxsamples\fP \fIsamples\fP
+.RS 4
+This option specifies the maximum number of readings kept for tracking of the
+NIC clock. The default value is 16.
+.RE
+.sp
 \fBprecision\fP \fIprecision\fP
 .RS 4
 This option specifies the assumed precision of reading of the NIC clock. The
@@ -3310,6 +3861,11 @@
 Enables timestamping of received NTP packets.
 .RE
 .sp
+\fIptp\fP
+.RS 4
+Enables timestamping of received PTP packets.
+.RE
+.sp
 \fInone\fP
 .RS 4
 Disables timestamping of received packets.
@@ -3318,12 +3874,14 @@
 .sp
 
 .RS 4
-The most specific filter for timestamping NTP packets which is supported by the
-NIC is selected by default. Some NICs can timestamp only PTP packets, which
-limits the selection to the \fInone\fP filter. Forcing timestamping of all packets
-with the \fIall\fP filter when the NIC supports both \fIall\fP and \fIntp\fP filters can be
-useful when packets are received from or on a non\-standard UDP port (e.g.
-specified by the \fBport\fP directive).
+The most specific filter for timestamping of NTP packets supported by the NIC
+is selected by default. Some NICs can timestamp PTP packets only. By default,
+they will be configured with the \fInone\fP filter and expected to provide hardware
+timestamps for transmitted packets only. Timestamping of PTP packets is useful
+with NTP\-over\-PTP enabled by the \fBptpport\fP
+directive. Forcing timestamping of all packets with the \fIall\fP filter could be
+useful if the NIC supported both the \fIall\fP and \fIntp\fP filters, and it should
+timestamp both NTP and PTP packets, or NTP packets on a different UDP port.
 .RE
 .RE
 .sp
@@ -3332,149 +3890,168 @@
 .sp
 Examples of the directive are:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 hwtimestamp eth0
 hwtimestamp eth1 txcomp 300e\-9 rxcomp 645e\-9
 hwtimestamp *
+.fam
 .fi
-.if n \{\
-.RE
-.\}
-.RE
-.sp
-\fBinclude\fP \fIpattern\fP
-.RS 4
-The \fBinclude\fP directive includes a configuration file or multiple configuration
-files if a wildcard pattern is specified. This can be useful when maintaining
-configuration on multiple hosts to keep the differences in separate files.
-.sp
-An example of the directive is:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-include @SYSCONFDIR@/chrony.d/*.conf
-.fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBkeyfile\fP \fIfile\fP
 .RS 4
-This directive is used to specify the location of the file containing ID\-key
-pairs for authentication of NTP packets.
+This directive is used to specify the location of the file containing symmetric
+keys which are shared between NTP servers and clients, or peers, in order to
+authenticate NTP packets with a message authentication code (MAC) using a
+cryptographic hash function or cipher.
 .sp
 The format of the directive is shown in the example below:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 keyfile @SYSCONFDIR@/chrony.keys
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The argument is simply the name of the file containing the ID\-key pairs. The
 format of the file is shown below:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 10 tulip
 11 hyacinth
 20 MD5 ASCII:crocus
-25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c
+25 SHA1 HEX:933F62BE1D604E68A81B557F18CFA200483F5B70
+30 AES128 HEX:7EA62AE64D190114D46D5A082F948EC1
+31 AES256 HEX:37DDCBC67BB902BCB8E995977FAB4D2B5642F5B32EBCEEE421921D97E5CBFE39
  ...
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
-Each line consists of an ID, name of an authentication hash function (optional),
-and a password. The ID can be any unsigned integer in the range 1 through
-2^32\-1. The default hash function is \fBMD5\fP, which is always supported.
+Each line consists of an ID, optional type, and key.
 .sp
+The ID can be any positive integer in the range 1 through 2^32\-1.
+.sp
+The type is a name of a cryptographic hash function or cipher which is used to
+generate and verify the MAC. The default type is \fBMD5\fP, which is always
+supported.
 If \fBchronyd\fP was built with enabled support for hashing using a crypto library
 (nettle, nss, or libtomcrypt), the following functions are available: \fBMD5\fP,
 \fBSHA1\fP, \fBSHA256\fP, \fBSHA384\fP, \fBSHA512\fP. Depending on which library and version is
-\fBchronyd\fP using, some or all of the following functions may also be available:
-\fBSHA3\-224\fP, \fBSHA3\-256\fP, \fBSHA3\-384\fP, \fBSHA3\-512\fP, \fBRMD128\fP, \fBRMD160\fP, \fBRMD256\fP,
-\fBRMD320\fP, \fBTIGER\fP, \fBWHIRLPOOL\fP.
+\fBchronyd\fP using, some of the following hash functions and ciphers may
+also be available:
+\fBSHA3\-224\fP, \fBSHA3\-256\fP, \fBSHA3\-384\fP, \fBSHA3\-512\fP, \fBTIGER\fP, \fBWHIRLPOOL\fP, \fBAES128\fP,
+\fBAES256\fP.
 .sp
-The password can be specified as a string of characters not containing white
+The key can be specified as a string of ASCII characters not containing white
 space with an optional \fBASCII:\fP prefix, or as a hexadecimal number with the
 \fBHEX:\fP prefix. The maximum length of the line is 2047 characters.
+If the type is a cipher, the length of the key must match the cipher (i.e. 128
+bits for AES128 and 256 bits for AES256).
 .sp
-The password is used with the hash function to generate and verify a message
-authentication code (MAC) in NTP packets. It is recommended to use SHA1, or
-stronger, hash function with random passwords specified in the hexadecimal
-format that have at least 128 bits. \fBchronyd\fP will log a warning to
-syslog on start if a source is specified in the configuration file with a key
-that has password shorter than 80 bits.
+It is recommended to use randomly generated keys, specified in the hexadecimal
+format, which are at least 128 bits long (i.e. they have at least 32 characters
+after the \fBHEX:\fP prefix). \fBchronyd\fP will log a warning to syslog on start if a
+source is specified in the configuration file with a key shorter than 80 bits.
+.sp
+The recommended key types are AES ciphers and SHA3 hash functions. MD5 should
+be avoided unless no other type is supported on the server and client, or
+peers.
 .sp
 The \fBkeygen\fP command of \fBchronyc\fP can be used to
 generate random keys for the key file. By default, it generates 160\-bit MD5 or
 SHA1 keys.
+.sp
+For security reasons, the file should be readable only by root and the user
+under which \fBchronyd\fP is normally running (to allow \fBchronyd\fP to re\-read the
+file when the \fBrekey\fP command is issued by \fBchronyc\fP).
 .RE
 .sp
 \fBlock_all\fP
 .RS 4
-The \fBlock_all\fP directive will lock chronyd into RAM so that it will never be
-paged out. This mode is only supported on Linux. This directive uses the Linux
-\fBmlockall()\fP system call to prevent \fBchronyd\fP from ever being swapped out. This
-should result in lower and more consistent latency. It should not have
-significant impact on performance as \fBchronyd\(cqs\fP memory usage is modest. The
-\fBmlockall(2)\fP man page has more details.
+The \fBlock_all\fP directive will lock the \fBchronyd\fP process into RAM so that it
+will never be paged out. This can result in lower and more consistent latency.
+The directive is supported on Linux, FreeBSD, NetBSD, and illumos.
 .RE
 .sp
 \fBpidfile\fP \fIfile\fP
 .RS 4
-\fBchronyd\fP always writes its process ID (PID) to a file, and checks this file on
-startup to see if another \fBchronyd\fP might already be running on the system. By
-default, the file used is \fI@DEFAULT_PID_FILE@\fP. The \fBpidfile\fP directive
-allows the name to be changed, e.g.:
+Unless \fBchronyd\fP is started with the \fB\-Q\fP option, it writes its process ID
+(PID) to a file, and checks this file on startup to see if another \fBchronyd\fP
+might already be running on the system. By default, the file used is
+\fI@DEFAULT_PID_FILE@\fP. The \fBpidfile\fP directive allows the name to be changed,
+e.g.:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 pidfile /run/chronyd.pid
+.fam
 .fi
-.if n \{\
+.if n .RE
 .RE
-.\}
+.sp
+\fBptpport\fP \fIport\fP
+.RS 4
+The \fBptpport\fP directive enables \fBchronyd\fP to send and receive NTP messages
+contained in PTP event messages (NTP\-over\-PTP) to enable hardware timestamping
+on NICs which cannot timestamp NTP packets, but can timestamp unicast PTP
+packets. The port recognized by the NICs is 319 (PTP event port). The default
+value is 0 (disabled).
+.sp
+The NTP\-over\-PTP support is experimental. The protocol and configuration can
+change in future. It should be used only in local networks and expected to work
+only between servers and clients running the same version of \fBchronyd\fP.
+.sp
+The PTP port will be open even if \fBchronyd\fP is not configured to operate as a
+server or client. The directive does not change the default protocol of
+specified NTP sources. Each NTP source that should use NTP\-over\-PTP needs to
+be specified with the \fBport\fP option set to the PTP port. To actually enable
+hardware timestamping on NICs which can timestamp PTP packets only, the
+\fBrxfilter\fP option of the \fBhwtimestamp\fP directive needs to be set to \fIptp\fP.
+.sp
+An example of client configuration is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+server foo.example.net minpoll 0 maxpoll 0 xleave port 319
+hwtimestamp * rxfilter ptp
+ptpport 319
+.fam
+.fi
+.if n .RE
 .RE
 .sp
 \fBsched_priority\fP \fIpriority\fP
 .RS 4
-On Linux, the \fBsched_priority\fP directive will select the SCHED_FIFO real\-time
-scheduler at the specified priority (which must be between 0 and 100). On
-macOS, this option must have either a value of 0 (the default) to disable the
-thread time constraint policy or 1 for the policy to be enabled. Other systems
-do not support this option.
+On Linux, FreeBSD, NetBSD, and illumos, the \fBsched_priority\fP directive will
+select the SCHED_FIFO real\-time scheduler at the specified priority (which must
+be between 0 and 100). On macOS, this option must have either a value of 0 (the
+default) to disable the thread time constraint policy or 1 for the policy to be
+enabled.
 .sp
-On Linux, this directive uses the \fBsched_setscheduler()\fP system call to
-instruct the kernel to use the SCHED_FIFO first\-in, first\-out real\-time
-scheduling policy for \fBchronyd\fP with the specified priority. This means that
-whenever \fBchronyd\fP is ready to run it will run, interrupting whatever else is
-running unless it is a higher priority real\-time process. This should not
-impact performance as \fBchronyd\fP resource requirements are modest, but it should
-result in lower and more consistent latency since \fBchronyd\fP will not need to
-wait for the scheduler to get around to running it. You should not use this
-unless you really need it. The \fBsched_setscheduler(2)\fP man page has more
-details.
+On systems other than macOS, this directive uses the \fBpthread_setschedparam()\fP
+system call to instruct the kernel to use the SCHED_FIFO first\-in, first\-out
+real\-time scheduling policy for \fBchronyd\fP with the specified priority. This
+means that whenever \fBchronyd\fP is ready to run it will run, interrupting
+whatever else is running unless it is a higher priority real\-time process. This
+should not impact performance as \fBchronyd\fP resource requirements are modest,
+but it should result in lower and more consistent latency since \fBchronyd\fP will
+not need to wait for the scheduler to get around to running it. You should not
+use this unless you really need it. The \fBpthread_setschedparam(3)\fP man page has
+more details.
 .sp
 On macOS, this directive uses the \fBthread_policy_set()\fP kernel call to
-specify real\-time scheduling. As noted for Linux, you should not use this
-directive unless you really need it.
+specify real\-time scheduling. As noted above, you should not use this directive
+unless you really need it.
 .RE
 .sp
 \fBuser\fP \fIuser\fP
@@ -3483,7 +4060,7 @@
 switch after start in order to drop root privileges.
 .sp
 On Linux, \fBchronyd\fP needs to be compiled with support for the \fBlibcap\fP library.
-On macOS, FreeBSD, NetBSD and Solaris \fBchronyd\fP forks into two processes.
+On macOS, FreeBSD, NetBSD and illumos \fBchronyd\fP forks into two processes.
 The child process retains root privileges, but can only perform a very limited
 range of privileged system calls on behalf of the parent.
 .sp
@@ -3505,8 +4082,8 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
 Your institution might already operate servers on its network.
 Contact your system administrator to find out.
@@ -3517,8 +4094,8 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
 Your ISP probably has one or more NTP servers available for its
 customers.
@@ -3529,8 +4106,8 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
 Somewhere under the NTP homepage there is a list of public
 stratum 1 and stratum 2 servers. You should find one or more servers that are
@@ -3543,28 +4120,26 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
 Use public servers from the \c
-.URL "http://www.pool.ntp.org/" "pool.ntp.org" " "
+.URL "https://www.pool.ntp.org/" "pool.ntp.org" ""
 project.
 .RE
 .sp
 Assuming that your NTP servers are called \fIfoo.example.net\fP, \fIbar.example.net\fP
 and \fIbaz.example.net\fP, your \fIchrony.conf\fP file could contain as a minimum:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 server foo.example.net
 server bar.example.net
 server baz.example.net
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 However, you will probably want to include some of the other directives. The
 \fBdriftfile\fP, \fBmakestep\fP and \fBrtcsync\fP
@@ -3573,38 +4148,52 @@
 synchronisation. The smallest useful configuration file would look something
 like:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 server foo.example.net iburst
 server bar.example.net iburst
 server baz.example.net iburst
 driftfile @CHRONYVARDIR@/drift
 makestep 1.0 3
 rtcsync
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 When using a pool of NTP servers (one name is used for multiple servers which
 might change over time), it is better to specify them with the \fBpool\fP
 directive instead of multiple \fBserver\fP directives. The configuration file could
 in this case look like:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 pool pool.ntp.org iburst
 driftfile @CHRONYVARDIR@/drift
 makestep 1.0 3
 rtcsync
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
+.sp
+If the servers (or pool) support the Network Time Security (NTS)
+authentication mechanism and \fBchronyd\fP is compiled with NTS support, the \fBnts\fP
+option will enable a secure synchronisation to the servers. The configuration
+file could look like:
+.sp
+.if n .RS 4
+.nf
+.fam C
+server foo.example.net iburst nts
+server bar.example.net iburst nts
+server baz.example.net iburst nts
+driftfile @CHRONYVARDIR@/drift
+makestep 1.0 3
+rtcsync
+.fam
+.fi
+.if n .RE
 .SS "NTP client with infrequent connection to NTP servers"
 .sp
 This section shows how to configure \fBchronyd\fP for computers that have
@@ -3617,20 +4206,18 @@
 \fIbar.example.net\fP and \fIbaz.example.net\fP, your \fIchrony.conf\fP file would now
 contain:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 server foo.example.net offline
 server bar.example.net offline
 server baz.example.net offline
 driftfile @CHRONYVARDIR@/drift
 makestep 1.0 3
 rtcsync
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The \fBoffline\fP keyword indicates that the servers start in an offline state, and
 that they should not be contacted until \fBchronyd\fP receives notification from
@@ -3642,52 +4229,48 @@
 used to connect to the Internet and that \fBchronyc\fP has been installed at
 \fI@BINDIR@/chronyc\fP, the script \fI/etc/ppp/ip\-up\fP would include:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 @BINDIR@/chronyc online
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 and the script \fI/etc/ppp/ip\-down\fP would include:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 @BINDIR@/chronyc offline
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
-\fBchronyd\fP\(cqs polling of the servers would now only occur whilst the machine is
+\fBchronyd\fP\(aqs polling of the servers would now only occur whilst the machine is
 actually connected to the Internet.
 .SS "Isolated networks"
 .sp
 This section shows how to configure \fBchronyd\fP for computers that never have
-network conectivity to any computer which ultimately derives its time from a
+network connectivity to any computer which ultimately derives its time from a
 reference clock.
 .sp
-In this situation, one computer is selected to be the master timeserver. The
-other computers are either direct clients of the master, or clients of clients.
+In this situation, one computer is selected to be the primary timeserver. The
+other computers are either direct clients of the server, or clients of clients.
 .sp
 The \fBlocal\fP directive enables a local reference mode, which allows
 \fBchronyd\fP to appear synchronised even when it is not.
 .sp
-The rate value in the master\(cqs drift file needs to be set to the average rate
-at which the master gains or loses time. \fBchronyd\fP includes support for this,
+The rate value in the server\(cqs drift file needs to be set to the average rate
+at which the server gains or loses time. \fBchronyd\fP includes support for this,
 in the form of the \fBmanual\fP directive and the
 \fBsettime\fP command in the \fBchronyc\fP program.
 .sp
-If the master is rebooted, \fBchronyd\fP can re\-read the drift rate from the drift
-file. However, the master has no accurate estimate of the current time. To get
-around this, the system can be configured so that the master can initially set
+If the server is rebooted, \fBchronyd\fP can re\-read the drift rate from the drift
+file. However, the server has no accurate estimate of the current time. To get
+around this, the system can be configured so that the server can initially set
 itself to a \(oqmajority\-vote\(cq of selected clients\(aq times; this allows the
-clients to \(oqflywheel\(cq the master while it is rebooting.
+clients to \(oqflywheel\(cq the server while it is rebooting.
 .sp
 The \fBsmoothtime\fP directive is useful when the clocks of the
 clients need to stay close together when the local time is adjusted by the
@@ -3696,13 +4279,12 @@
 the local time is ready to be served. After that point, any adjustments will be
 smoothed out.
 .sp
-A typical configuration file for the master (called \fImaster\fP) might be
-(assuming the clients and the master are in the \fI192.168.165.x\fP subnet):
+A typical configuration file for the server (called \fIntp.local\fP) might be
+(assuming the clients and the server are in the \fI192.168.165.x\fP subnet):
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 initstepslew 1 client1 client3 client6
 driftfile @CHRONYVARDIR@/drift
 local stratum 8
@@ -3710,59 +4292,54 @@
 allow 192.168.165.0/24
 smoothtime 400 0.01
 rtcsync
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
-For the clients that have to resynchronise the master when it restarts,
+For the clients that have to resynchronise the server when it restarts,
 the configuration file might be:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-server master iburst
+.fam C
+server ntp.local iburst
 driftfile @CHRONYVARDIR@/drift
 allow 192.168.165.0/24
 makestep 1.0 3
 rtcsync
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The rest of the clients would be the same, except that the \fBallow\fP directive is
 not required.
 .sp
-If there is no suitable computer to be designated as the master, or there is a
-requirement to keep the clients synchronised even when it fails, the \fBorphan\fP
-option of the \fBlocal\fP directive enables a special mode where the master is
-selected from multiple computers automatically. They all need to use the same
-\fBlocal\fP configuration and poll one another. The server with the smallest
-reference ID (which is based on its IP address) will take the role of the
-master and others will be synchronised to it. When it fails, the server with
-the second smallest reference ID will take over and so on.
+If there is no suitable computer to be designated as the primary server, or
+there is a requirement to keep the clients synchronised even when it fails, the
+\fBorphan\fP option of the \fBlocal\fP directive enables a special mode where the
+server is selected from multiple computers automatically. They all need to use
+the same \fBlocal\fP configuration and poll one another. The server with the
+smallest reference ID (which is based on its IP address) will take the role of
+the primary server and others will be synchronised to it. When it fails, the
+server with the second smallest reference ID will take over and so on.
 .sp
 A configuration file for the first server might be (assuming there are three
-servers called \fImaster1\fP, \fImaster2\fP, and \fImaster3\fP):
+servers called \fIntp1.local\fP, \fIntp2.local\fP, and \fIntp3.local\fP):
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-initstepslew 1 master2 master3
-server master2
-server master3
+.fam C
+initstepslew 1 ntp2.local ntp3.local
+server ntp2.local
+server ntp3.local
 driftfile @CHRONYVARDIR@/drift
 local stratum 8 orphan
 manual
 allow 192.168.165.0/24
 rtcsync
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The other servers would be the same, except the hostnames in the \fBinitstepslew\fP
 and \fBserver\fP directives would be modified to specify the other servers. Their
@@ -3837,10 +4414,9 @@
 .sp
 For the \fIchrony.conf\fP file, the following can be used as an example.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 server foo.example.net maxdelay 0.4 offline
 server bar.example.net maxdelay 0.4 offline
 server baz.example.net maxdelay 0.4 offline
@@ -3851,10 +4427,9 @@
 maxupdateskew 100.0
 dumpdir @CHRONYVARDIR@
 rtcfile @CHRONYVARDIR@/rtc
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 \fBpppd\fP is used for connecting to the Internet. This runs two scripts
 \fI/etc/ppp/ip\-up\fP and \fI/etc/ppp/ip\-down\fP when the link goes online and offline
@@ -3862,31 +4437,27 @@
 .sp
 The relevant part of the \fI/etc/ppp/ip\-up\fP file is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 @BINDIR@/chronyc online
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 and the relevant part of the \fI/etc/ppp/ip\-down\fP script is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 @BINDIR@/chronyc \-m offline dump writertc
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 \fBchronyd\fP is started during the boot sequence with the \fB\-r\fP and \fB\-s\fP options.
 It might need to be started before any software that depends on the system clock
-not jumping or moving backwards, depending on the directives in \fBchronyd\fP\(cqs
+not jumping or moving backwards, depending on the directives in \fBchronyd\fP\(aqs
 configuration file.
 .sp
 For the system shutdown, \fBchronyd\fP should receive a SIGTERM several seconds
@@ -3895,7 +4466,7 @@
 .SS "Public NTP server"
 .sp
 \fBchronyd\fP can be configured to operate as a public NTP server, e.g. to join the
-.URL "http://www.pool.ntp.org/en/join.html" "pool.ntp.org" " "
+.URL "https://www.pool.ntp.org/en/join.html" "pool.ntp.org" ""
 project. The configuration
 is similar to the NTP client with permanent connection, except it needs to
 allow client access from all addresses. It is recommended to find at least four
@@ -3916,10 +4487,9 @@
 .sp
 The configuration file could look like:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 server foo.example.net iburst
 server bar.example.net iburst
 server baz.example.net iburst
@@ -3931,10 +4501,9 @@
 leapsectz right/UTC
 driftfile @CHRONYVARDIR@/drift
 dumpdir @CHRONYRUNDIR@
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .SH "SEE ALSO"
 .sp
 \fBchronyc(1)\fP, \fBchronyd(8)\fP
diff --git a/chrony_3_3/doc/chronyc.adoc b/chrony/doc/chronyc.adoc
similarity index 73%
rename from chrony_3_3/doc/chronyc.adoc
rename to chrony/doc/chronyc.adoc
index c987907..cfb5eb8 100644
--- a/chrony_3_3/doc/chronyc.adoc
+++ b/chrony/doc/chronyc.adoc
@@ -2,7 +2,7 @@
 //
 // Copyright (C) Richard P. Curnow  1997-2003
 // Copyright (C) Stephen Wadeley  2016
-// Copyright (C) Miroslav Lichvar  2009-2017
+// Copyright (C) Miroslav Lichvar  2009-2017, 2019-2020
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of version 2 of the GNU General Public License as
@@ -39,7 +39,7 @@
 If no commands are specified on the command line, *chronyc* will expect input
 from the user. The prompt _chronyc>_ will be displayed when it is being run
 from a terminal. If *chronyc*'s input or output are redirected from or to a file,
-the prompt is not shown.
+the prompt will not be shown.
 
 There are two ways *chronyc* can access *chronyd*. One is the Internet
 Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
@@ -51,7 +51,8 @@
 
 Only the following monitoring commands, which do not affect the behaviour of
 *chronyd*, are allowed from the network: *activity*, *manual list*,
-*rtcdata*, *smoothing*, *sources*, *sourcestats*, *tracking*, *waitsync*. The
+*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
+*waitsync*. The
 set of hosts from which *chronyd* will accept these commands can be configured
 with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
 configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
@@ -59,9 +60,7 @@
 
 All other commands are allowed only through the Unix domain socket. When sent
 over the network, *chronyd* will respond with a '`Not authorised`' error, even
-if it is from localhost. In chrony versions before 2.2 they were allowed
-from the network if they were authenticated with a password, but that is no
-longer supported.
+if it is from localhost.
 
 Having full access to *chronyd* via *chronyc* is more or less equivalent to
 being able to modify the *chronyd*'s configuration file and restart it.
@@ -78,11 +77,17 @@
 This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
 DNS lookups. Long addresses will not be truncated to fit into the column.
 
+*-N*::
+This option enables printing of original hostnames or IP addresses of NTP
+sources that were specified in the configuration file, or *chronyc* commands.
+Without the *-n* and *-N* option, the printed hostnames are obtained from
+reverse DNS lookups and can be different from the specified hostnames.
+
 *-c*::
 This option enables printing of reports in a comma-separated values (CSV)
-format. IP addresses will not be resolved to hostnames, time will be printed as
-number of seconds since the epoch and values in seconds will not be converted
-to other units.
+format. Reverse DNS lookups will be disabled, time will be printed as number of
+seconds since the epoch, and values in seconds will not be converted to other
+units.
 
 *-d*::
 This option enables printing of debugging messages if *chronyc* was compiled
@@ -112,10 +117,14 @@
 *-a*::
 This option is ignored and is provided only for compatibility.
 
-*-v*::
+*-v*, *--version*::
 With this option *chronyc* displays its version number on the terminal and
 exits.
 
+*--help*::
+With this option *chronyc* displays a help message on the terminal and
+exits.
+
 == COMMANDS
 
 This section describes each of the commands available within the *chronyc*
@@ -175,10 +184,12 @@
 and then returning to the system clock's normal speed. A consequence of this is
 that there will be a period when the system clock (as read by other programs)
 will be different from *chronyd*'s estimate of the current true time (which it
-reports to NTP clients when it is operating in server mode). The value reported
+reports to NTP clients when it is operating as a server). The value reported
 on this line is the difference due to this effect.
 *Last offset*:::
-This is the estimated local offset on the last clock update.
+This is the estimated local offset on the last clock update. A positive value
+indicates the local time (as previously estimated true time) was ahead of the
+time sources.
 *RMS offset*:::
 This is a long-term average of the offset value.
 *Frequency*:::
@@ -284,15 +295,18 @@
 
 === Time sources
 
-[[sources]]*sources* [*-v*]::
+[[sources]]*sources* [*-a*] [*-v*]::
 This command displays information about the current time sources that *chronyd*
 is accessing.
 +
-The optional argument *-v* can be specified, meaning _verbose_. In this case,
+If the *-a* option is specified, all sources are displayed, including those that
+do not have a known address yet. Such sources have an identifier in the format
+_ID#XXXXXXXXXX_, which can be used in other commands expecting a source address.
++
+The *-v* option enables a verbose output. In this case,
 extra caption lines are shown as a reminder of the meanings of the columns.
 +
 ----
-210 Number of sources = 3
 MS Name/IP address         Stratum Poll Reach LastRx Last sample
 ===============================================================================
 #* GPS0                          0   4   377    11   -479ns[ -621ns] +/-  134ns
@@ -306,18 +320,23 @@
 This indicates the mode of the source. _^_ means a server, _=_ means a peer
 and _#_ indicates a locally connected reference clock.
 *S*:::
-This column indicates the state of the source.
-* _*_ indicates the source to which *chronyd* is currently synchronised.
-* _+_ indicates acceptable sources which are combined with the selected
-  source.
-* _-_ indicates acceptable sources which are excluded by the combining
-  algorithm.
-* _?_ indicates sources to which connectivity has been lost or whose packets
-  do not pass all tests. It is also shown at start-up, until at least 3 samples
-  have been gathered from it.
-* _x_ indicates a clock which *chronyd* thinks is a falseticker (i.e. its
-  time is inconsistent with a majority of other sources).
+This column indicates the selection state of the source.
+* _*_ indicates the best source which is currently selected for
+  synchronisation.
+* _+_ indicates other sources selected for synchronisation, which are combined
+  with the best source.
+* _-_ indicates a source which is considered to be selectable for
+  synchronisation, but not currently selected.
+* _x_ indicates a source which *chronyd* thinks is a falseticker (i.e. its
+  time is inconsistent with a majority of other sources, or sources specified
+  with the *trust* option).
 * _~_ indicates a source whose time appears to have too much variability.
+* _?_ indicates a source which is not considered to be selectable for
+  synchronisation for other reasons (e.g. unreachable, not synchronised, or
+  does not have enough measurements).
+{blank}:::
+The <<selectdata,*selectdata*>> command can be used to get more details about
+the selection state.
 *Name/IP address*:::
 This shows the name or the IP address of the source, or reference ID for reference
 clocks.
@@ -338,8 +357,9 @@
 the source. A value of 377 indicates that a valid reply was received for all
 from the last eight transmissions.
 *LastRx*:::
-This column shows how long ago the last sample was received from the source.
-This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
+This column shows how long ago the last good sample (which is shown in the next
+column) was received from the source. Measurements that failed some tests are
+ignored. This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
 minutes, hours, days, or years.
 *Last sample*:::
 This column shows the offset between the local clock and the source at the
@@ -352,18 +372,21 @@
 the measurement. Positive offsets indicate that the local clock is ahead of
 the source.
 
-[[sourcestats]]*sourcestats* [*-v*]::
+[[sourcestats]]*sourcestats* [*-a*] [*-v*]::
 The *sourcestats* command displays information about the drift rate and offset
 estimation process for each of the sources currently being examined by
 *chronyd*.
 +
-The optional argument *-v* can be specified, meaning _verbose_. In this case,
+If the *-a* option is specified, all sources are displayed, including those that
+do not have a known address yet. Such sources have an identifier in the format
+_ID#XXXXXXXXXX_, which can be used in other commands expecting a source address.
++
+The *-v* option enables a verbose output. In this case,
 extra caption lines are shown as a reminder of the meanings of the columns.
 +
 An example report is:
 +
 ----
-210 Number of sources = 1
 Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
 ===============================================================================
 foo.example.net            11   5   46m     -0.001      0.045      1us    25us
@@ -399,6 +422,98 @@
 *Std Dev*:::
 This is the estimated sample standard deviation.
 
+[[selectdata]]*selectdata* [*-a*] [*-v*]::
+The *selectdata* command displays information specific to the selection of time
+sources. If the *-a* option is specified, all sources are displayed, including
+those that do not have a known address yet. With the *-v* option, extra caption
+lines are shown as a reminder of the meanings of the columns.
++
+An example of the output is shown below.
++
+----
+S Name/IP Address        Auth COpts EOpts Last Score     Interval  Leap
+=======================================================================
+D foo.example.net           Y ----- --TR-    4   1.0   -61ms   +62ms  N
+* bar.example.net           N ----- -----    0   1.0 -6846us +7305us  N
++ baz.example.net           N ----- -----   10   1.0 -7381us +7355us  N
+----
++
+The columns are as follows:
++
+*S*:::
+This column indicates the state of the source after the last source selection.
+It is similar to the state reported by the *sources* command, but more
+states are reported.
+{blank}:::
+The following states indicate the source is not considered selectable for
+synchronisation:
+* _N_ - has the *noselect* option.
+* _M_ - does not have enough measurements.
+* _d_ - has a root distance larger than the maximum distance (configured by the
+        <<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
+* _~_ - has a jitter larger than the maximum jitter (configured by the
+        <<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
+* _w_ - waits for other sources to get out of the _M_ state.
+* _S_ - has older measurements than other sources.
+* _O_ - has a stratum equal or larger than the orphan stratum (configured by
+        the <<chrony.conf.adoc#local,*local*>> directive).
+* _T_ - does not fully agree with sources that have the *trust* option.
+* _x_ - does not agree with other sources (falseticker).
+{blank}:::
+The following states indicate the source is considered selectable, but it is
+not currently used for synchronisation:
+* _W_ - waits for other sources to be selectable (required by the
+        <<chrony.conf.adoc#minsources,*minsources*>> directive, or
+        the *require* option of another source).
+* _P_ - another selectable source is preferred due to the *prefer* option.
+* _U_ - waits for a new measurement (after selecting a different best source).
+* _D_ - has, or recently had, a root distance which is too large to be combined
+        with other sources (configured by the
+        <<chrony.conf.adoc#combinelimit,*combinelimit*>> directive).
+{blank}:::
+The following states indicate the source is used for synchronisation of the
+local clock:
+* _+_ - combined with the best source.
+* _*_ - selected as the best source to update the reference data (e.g. root
+        delay, root dispersion).
+*Name/IP address*:::
+This column shows the name or IP address of the source if it is an NTP server,
+or the reference ID if it is a reference clock.
+*Auth*:::
+This column indicites whether an authentication mechanism is enabled for the
+source. _Y_ means yes and _N_ means no.
+*COpts*:::
+This column displays the configured selection options of the source.
+* _N_ indicates the *noselect* option.
+* _P_ indicates the *prefer* option.
+* _T_ indicates the *trust* option.
+* _R_ indicates the *require* option.
+*EOpts*:::
+This column displays the current effective selection options of the source,
+which can be different from the configured options due to the authentication
+selection mode (configured by the
+<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the
+same as in the *COpts* column.
+*Last*:::
+This column displays how long ago was the last measurement of the source made
+when the selection was performed.
+*Score*:::
+This column displays the current score against the source in the _*_ state. The
+scoring system avoids frequent reselection when multiple sources have a similar
+root distance. A value larger than 1 indicates this source was better than the
+_*_ source in recent selections. If the score reaches 10, the best source will
+be reselected and the scores will be reset to 1.
+*Interval*:::
+This column displays the lower and upper endpoint of the interval which was
+expected to contain the true offset of the local clock considering the root
+distance at the time of the selection.
+*Leap*:::
+This column displays the current leap status of the source.
+* _N_ indicates the normal status (no leap second).
+* _+_ indicates that a leap second will be inserted at the end of the month.
+* _-_ indicates that a leap second will be deleted at the end of the month.
+* _?_ indicates the unknown status (i.e. no valid measurement was made).
+
 [[reselect]]*reselect*::
 To avoid excessive switching between sources, *chronyd* can stay synchronised
 to a source even when it is not currently the best one among the available
@@ -439,10 +554,82 @@
 the name of the server or peer was not resolved to an address yet; this source is
 not visible in the *sources* and *sourcestats* reports.
 
+[[authdata]]*authdata* [*-a*]::
+The *authdata* command displays information specific to authentication of NTP
+sources. If the *-a* option is specified, all sources are displayed, including
+those that do not have a known address yet. An example of the output is
+shown below.
++
+----
+Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+foo.example.net              NTS     1   15  256 135m    0    0    8  100
+bar.example.net               SK    30   13  128    -    0    0    0    0
+baz.example.net                -     0    0    0    -    0    0    0    0
+----
++
+The columns are as follows:
++
+*Name/IP address*:::
+This column shows the name or the IP address of the source.
+*Mode*:::
+This column shows which mechanism authenticates NTP packets received from the
+source. _NTS_ means Network Time Security, _SK_ means a symmetric key, and _-_
+means authentication is disabled.
+*KeyID*:::
+This column shows an identifier of the key used for authentication. With a
+symmetric key, it is the ID from the <<chrony.conf.adoc#keyfile,key file>>.
+With NTS, it is a number starting at zero and incremented by one with each
+successful key establishment using the NTS-KE protocol, i.e. it shows how many
+times the key establishment was performed with this source.
+*Type*:::
+This columns shows an identifier of the algorithm used for authentication.
+With a symmetric key, it is the hash function or cipher specified in the key
+file. With NTS, it is an authenticated encryption with associated data (AEAD)
+algorithm, which is negotiated in the NTS-KE protocol. The following values can
+be reported:
+* 1: MD5
+* 2: SHA1
+* 3: SHA256
+* 4: SHA384
+* 5: SHA512
+* 6: SHA3-224
+* 7: SHA3-256
+* 8: SHA3-384
+* 9: SHA3-512
+* 10: TIGER
+* 11: WHIRLPOOL
+* 13: AES128
+* 14: AES256
+* 15: AEAD-AES-SIV-CMAC-256
+*KLen*:::
+This column shows the length of the key in bits.
+*Last*:::
+This column shows how long ago the last successful key establishment was
+performed. It is in seconds, or letters _m_, _h_, _d_ or _y_ indicate minutes,
+hours, days, or years.
+*Atmp*:::
+This column shows the number of attempts to perform the key establishment since
+the last successful key establishment. A number larger than 1 indicates a
+problem with the network or server.
+*NAK*:::
+This column shows whether an NTS NAK was received since the last request.
+A NAK indicates that authentication failed on the server side due to
+*chronyd* using a cookie which is no longer valid and that it needs to perform
+the key establishment again in order to get new cookies.
+*Cook*:::
+This column shows the number of NTS cookies that *chronyd* currently has. If
+the key establishment was successful, a number smaller than 8 indicates a
+problem with the network or server.
+*CLen*:::
+This column shows the length in bytes of the NTS cookie which will be used in
+the next request.
+
 [[ntpdata]]*ntpdata* [_address_]::
 The *ntpdata* command displays the last valid measurement and other
-NTP-specific information about the specified NTP source, or all NTP sources if
-no address was specified. An example of the output is shown below.
+NTP-specific information about the specified NTP source, or all NTP sources
+(with a known address) if no address was specified. An example of the output is
+shown below.
 +
 ----
 Remote address  : 203.0.113.15 (CB00710F)
@@ -525,15 +712,13 @@
 *Total valid RX*:::
 The number of valid packets received from the source.
 
-[[add_peer]]*add peer* _address_ [_option_]...::
+[[add_peer]]*add peer* _name_ [_option_]...::
 The *add peer* command allows a new NTP peer to be added whilst
 *chronyd* is running.
 +
 Following the words *add peer*, the syntax of the following
-parameters and options is similar to that for the
+parameters and options is identical to that for the
 <<chrony.conf.adoc#peer,*peer*>> directive in the configuration file.
-The following peer options can be set in the command: *port*, *minpoll*,
-*maxpoll*, *presend*, *maxdelayratio*, *maxdelay*, *key*.
 +
 An example of using this command is shown below.
 +
@@ -541,15 +726,27 @@
 add peer foo.example.net minpoll 6 maxpoll 10 key 25
 ----
 
-[[add_server]]*add server* _address_ [_option_]...::
+[[add_pool]]*add pool* _name_ [_option_]...::
+The *add pool* command allows a pool of NTP servers to be added whilst
+*chronyd* is running.
++
+Following the words *add pool*, the syntax of the following parameters and
+options is identical to that for the <<chrony.conf.adoc#pool,*pool*>>
+directive in the configuration file.
++
+An example of using this command is shown below:
++
+----
+add pool foo.example.net maxsources 3 iburst
+----
+
+[[add_server]]*add server* _name_ [_option_]...::
 The *add server* command allows a new NTP server to be added whilst
 *chronyd* is running.
 +
 Following the words *add server*, the syntax of the following parameters and
-options is similar to that for the <<chrony.conf.adoc#server,*server*>>
+options is identical to that for the <<chrony.conf.adoc#server,*server*>>
 directive in the configuration file.
-The following server options can be set in the command: *port*, *minpoll*,
-*maxpoll*, *presend*, *maxdelayratio*, *maxdelay*, *key*.
 +
 An example of using this command is shown below:
 +
@@ -600,7 +797,7 @@
 _address_:::
 This is an IP address or a hostname. The burst command is applied only to
 that source.
-::
+{blank}::
 +
 If no _mask_ or _masked-address_ arguments are provided, every source will be
 matched.
@@ -687,7 +884,8 @@
 this.
 +
 There are four forms of the *offline* command. The first form is a wildcard,
-meaning all sources. The second form allows an IP address mask and a masked
+meaning all sources (including sources that do not have a known address yet).
+The second form allows an IP address mask and a masked
 address to be specified. The third form uses CIDR notation. The fourth form
 uses an IP address or a hostname. These forms are illustrated below.
 +
@@ -722,6 +920,14 @@
 +
 The syntax is identical to that of the <<offline,*offline*>> command.
 
+[[onoffline]]
+*onoffline*::
+The *onoffline* command tells *chronyd* to switch all sources that have a known
+address to the online or
+offline status according to the current network configuration. A source is
+considered online if it is possible to send requests to it, i.e. a network
+route to the source is present.
+
 [[polltarget]]*polltarget* _address_ _polltarget_::
 The *polltarget* command is used to modify the poll target for one of the
 current set of sources. It is equivalent to the *polltarget* option in the
@@ -736,6 +942,20 @@
 automatically after 8 polling intervals, but this command can still be useful
 to replace them immediately and not wait until they are marked as unreachable.
 
+[[reload]]*reload* *sources*::
+The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
+from the directories specified by the
+<<chrony.conf.adoc#sourcedir,*sourcedir*>> directive.
+
+[[sourcename]]*sourcename* _address_::
+The *sourcename* command prints the original hostname or address that was
+specified for an NTP source in the configuration file, or the *add* command.
+This command is an alternative to the *-N* option, which can be useful in
+scripts.
++
+Note that different NTP sources can share the same name, e.g. servers from a
+pool.
+
 === Manual time input
 
 [[manual]]
@@ -772,7 +992,7 @@
 . The regression residual at this point, in seconds. This allows '`outliers`'
   to be easily spotted, so that they can be deleted using the *manual delete*
   command.
-::
+{blank}::
 +
 The *delete* form of the command deletes a single sample. The parameter is the
 index of the sample, as shown in the first column of the output from *manual
@@ -843,10 +1063,17 @@
 all*, *deny*, and *deny all* commands specified either via *chronyc*, or in
 *chronyd*'s configuration file.
 
-[[clients]]*clients*::
+[[clients]]*clients* [*-p* _packets_] [*-k*] [*-r*]::
 This command shows a list of clients that have accessed the server, through
-either the NTP or command ports. It does not include accesses over
-the Unix domain command socket. There are no arguments.
+the NTP, command, or NTS-KE port. It does not include accesses over the Unix
+domain command socket.
++
+The *-p* option specifies the minimum number of received NTP or command
+packets, or accepted NTS-KE connections, needed to include a client in the
+list. The default value is 0, i.e. all clients are reported. With the *-k*
+option the last four columns will show the NTS-KE accesses instead of command
+accesses. If the *-r* option is specified, *chronyd* will reset the counters of
+received and dropped packets or connections after reporting the current values.
 +
 An example of the output is:
 +
@@ -871,20 +1098,18 @@
 . The average interval between NTP packets.
 . The average interval between NTP packets after limiting the response rate.
 . Time since the last NTP packet was received
-. The number of command packets received from the client.
-. The number of command packets dropped to limit the response rate.
-. The average interval between command packets.
-. Time since the last command packet was received.
+. The number of command packets or NTS-KE connections received/accepted from
+  the client.
+. The number of command packets or NTS-KE connections dropped to limit the
+  response rate.
+. The average interval between command packets or NTS-KE connections.
+. Time since the last command packet or NTS-KE connection was
+  received/accepted.
 
 [[serverstats]]*serverstats*::
-The *serverstats* command displays how many valid NTP and command requests
-*chronyd* as a server received from clients, how many of them were dropped to
-limit the response rate as configured by the
-<<chrony.conf.adoc#ratelimit,*ratelimit*>> and
-<<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directives, and how many
-client log records were dropped due to the memory limit configured by the
-<<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive. An example of
-the output is shown below.
+The *serverstats* command displays NTP and command server statistics.
++
+An example of the output is shown below.
 +
 ----
 NTP packets received       : 1598
@@ -892,7 +1117,50 @@
 Command packets received   : 19
 Command packets dropped    : 0
 Client log records dropped : 0
+NTS-KE connections accepted: 3
+NTS-KE connections dropped : 0
+Authenticated NTP packets  : 189
+Interleaved NTP packets    : 43
+NTP timestamps held        : 44
+NTP timestamp span         : 120
 ----
++
+The fields have the following meaning:
++
+*NTP packets received*:::
+The number of valid NTP requests received by the server.
+*NTP packets dropped*:::
+The number of NTP requests dropped by the server due to rate limiting
+(configured by the <<chrony.conf.adoc#ratelimit,*ratelimit*>> directive).
+*Command packets received*:::
+The number of command requests received by the server.
+*Command packets dropped*:::
+The number of command requests dropped by the server due to rate limiting
+(configured by the <<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directive).
+*Client log records dropped*:::
+The number of client log records dropped by the server to limit the memory use
+(configured by the <<chrony.conf.adoc#clientloglimit,*clientloglimit*>>
+directive).
+*NTS-KE connections accepted*:::
+The number of NTS-KE connections accepted by the server.
+*NTS-KE connections dropped*:::
+The number of NTS-KE connections dropped by the server due to rate limiting
+(configured by the <<chrony.conf.adoc#ntsratelimit,*ntsratelimit*>> directive).
+*Authenticated NTP packets*:::
+The number of received NTP requests that were authenticated (with a symmetric
+key or NTS).
+*Interleaved NTP packets*:::
+The number of received NTP requests that were detected to be in the interleaved
+mode.
+*NTP timestamps held*:::
+The number of pairs of receive and transmit timestamps that the server is
+currently holding in memory for clients using the interleaved mode.
+*NTP timestamp span*:::
+The interval (in seconds) covered by the currently held NTP timestamps.
+{blank}::
++
+Note that the numbers reported by this overflow to zero after 4294967295
+(32-bit values).
 
 [[allow]]*allow* [*all*] [_subnet_]::
 The effect of the allow command is identical to the
@@ -901,11 +1169,8 @@
 The syntax is illustrated in the following examples:
 +
 ----
-allow foo.example.net
-allow all 1.2
-allow 3.4.5
-allow 6.7.8/22
-allow 6.7.8.9/22
+allow 1.2.3.4
+allow all 3.4.5.0/24
 allow 2001:db8:789a::/48
 allow 0/0
 allow ::/0
@@ -920,11 +1185,8 @@
 The syntax is illustrated in the following examples:
 +
 ----
-deny foo.example.net
-deny all 1.2
-deny 3.4.5
-deny 6.7.8/22
-deny 6.7.8.9/22
+deny 1.2.3.4
+deny all 3.4.5.0/24
 deny 2001:db8:789a::/48
 deny 0/0
 deny ::/0
@@ -1077,7 +1339,7 @@
   error).
 . Save the RTC parameters to the RTC file (specified with the
   <<chrony.conf.adoc#rtcfile,*rtcfile*>> directive in the configuration file).
-::
+{blank}::
 +
 The last step is done as a precaution against the computer suffering a power
 failure before either the daemon exits or the <<writertc,*writertc*>> command
@@ -1110,25 +1372,36 @@
 ----
 # mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log
 # chronyc cyclelogs
-# ls -l /var/log/chrony
--rw-r--r--   1 root     root            0 Jun  8 18:17 measurements.log
--rw-r--r--   1 root     root        12345 Jun  8 18:17 measurements1.log
-# rm -f measurements1.log
+# rm /var/log/chrony/measurements1.log
 ----
 
 [[dump]]*dump*::
 The *dump* command causes *chronyd* to write its current history of
 measurements for each of its sources to dump files in the directory specified
 in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
+directive and also write server NTS keys and client NTS cookies to the
+directory specified by the <<chrony.conf.adoc#ntsdumpdir1,*ntsdumpdir*>>
 directive. Note that *chronyd* does this automatically when it exits. This
-command is mainly useful for inspection of the history whilst *chronyd* is
-running.
+command is mainly useful for inspection whilst *chronyd* is running.
 
 [[rekey]]*rekey*::
 The *rekey* command causes *chronyd* to re-read the key file specified in the
-configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
+configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive. It
+also re-reads the server NTS keys if
+<<chrony.conf.adoc#ntsdumpdir2,*ntsdumpdir*>> is specified and
+<<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the
+configuration file.
 
-[[rekey]]*shutdown*::
+[[reset]]*reset* *sources*::
+The *reset sources* command causes *chronyd* to drop all measurements and
+switch to the unsynchronised state. This command can help *chronyd* with
+recovery when the measurements are known to be no longer valid or accurate,
+e.g. due to moving the computer to a different network, or resuming the
+computer from a low-power state (which resets the system clock). *chronyd* will
+drop the measurements automatically when it detects the clock has made an
+unexpected jump, but the detection is not completely reliable.
+
+[[shutdown]]*shutdown*::
 The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
 the process the SIGTERM signal.
 
@@ -1180,10 +1453,10 @@
 +
 The command has three optional arguments. The first argument is the key number
 (by default 1), which will be specified with the *key* option of the *server*
-or *peer* directives in the configuration file. The second argument is the hash
-function (by default SHA1 or MD5 if SHA1 is not available) and the third
-argument is the number of bits the key should have, between 80 and 4096 bits
-(by default 160 bits).
+or *peer* directives in the configuration file. The second argument is the name
+of the hash function or cipher (by default SHA1, or MD5 if SHA1 is not
+available). The third argument is the length of the key in bits if a hash
+function was selected, between 80 and 4096 bits (by default 160 bits).
 +
 An example is:
 +
@@ -1193,7 +1466,13 @@
 +
 which generates a 256-bit SHA1 key with number 73. The printed line should
 then be securely transferred and added to the key files on both server and
-client, or peers.
+client, or peers. A different key should be generated for each client or peer.
++
+An example using the AES128 cipher is:
++
+----
+keygen 151 AES128
+----
 
 [[exit]]*exit*::
 [[quit]]*quit*::
diff --git a/chrony_3_3/doc/chronyc.man.in b/chrony/doc/chronyc.man.in
similarity index 68%
rename from chrony_3_3/doc/chronyc.man.in
rename to chrony/doc/chronyc.man.in
index 75999a5..d4768bd 100644
--- a/chrony_3_3/doc/chronyc.man.in
+++ b/chrony/doc/chronyc.man.in
@@ -1,23 +1,32 @@
 '\" t
 .\"     Title: chronyc
-.\"    Author: [see the "AUTHORS" section]
-.\" Generator: Asciidoctor 1.5.6.1
-.\"      Date: 2018-04-04
+.\"    Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.15
+.\"      Date: 2021-12-16
 .\"    Manual: User manual
 .\"    Source: chrony @CHRONY_VERSION@
 .\"  Language: English
 .\"
-.TH "CHRONYC" "1" "2018-04-04" "chrony @CHRONY_VERSION@" "User manual"
+.TH "CHRONYC" "1" "2021-12-16" "chrony @CHRONY_VERSION@" "User manual"
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .ss \n[.ss] 0
 .nh
 .ad l
 .de URL
-\\$2 \(laURL: \\$1 \(ra\\$3
+\fI\\$2\fP <\\$1>\\$3
 ..
-.if \n[.g] .mso www.tmac
-.LINKSTYLE blue R < >
+.als MTO URL
+.if \n[.g] \{\
+.  mso www.tmac
+.  am URL
+.    ad l
+.  .
+.  am MTO
+.    ad l
+.  .
+.  LINKSTYLE blue R < >
+.\}
 .SH "NAME"
 chronyc \- command\-line interface for chrony daemon
 .SH "SYNOPSIS"
@@ -26,13 +35,13 @@
 .SH "DESCRIPTION"
 .sp
 \fBchronyc\fP is a command\-line interface program which can be used to monitor
-\fBchronyd\fP\(cqs performance and to change various operating parameters whilst it is
+\fBchronyd\fP\(aqs performance and to change various operating parameters whilst it is
 running.
 .sp
 If no commands are specified on the command line, \fBchronyc\fP will expect input
 from the user. The prompt \fIchronyc>\fP will be displayed when it is being run
-from a terminal. If \fBchronyc\fP\(cqs input or output are redirected from or to a file,
-the prompt is not shown.
+from a terminal. If \fBchronyc\fP\(aqs input or output are redirected from or to a file,
+the prompt will not be shown.
 .sp
 There are two ways \fBchronyc\fP can access \fBchronyd\fP. One is the Internet
 Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
@@ -44,20 +53,19 @@
 .sp
 Only the following monitoring commands, which do not affect the behaviour of
 \fBchronyd\fP, are allowed from the network: \fBactivity\fP, \fBmanual list\fP,
-\fBrtcdata\fP, \fBsmoothing\fP, \fBsources\fP, \fBsourcestats\fP, \fBtracking\fP, \fBwaitsync\fP. The
+\fBrtcdata\fP, \fBsmoothing\fP, \fBsourcename\fP, \fBsources\fP, \fBsourcestats\fP, \fBtracking\fP,
+\fBwaitsync\fP. The
 set of hosts from which \fBchronyd\fP will accept these commands can be configured
-with the \fBcmdallow\fP directive in the \fBchronyd\fP\(cqs
+with the \fBcmdallow\fP directive in the \fBchronyd\fP\(aqs
 configuration file or the \fBcmdallow\fP command in \fBchronyc\fP. By
 default, the commands are accepted only from localhost (127.0.0.1 or ::1).
 .sp
 All other commands are allowed only through the Unix domain socket. When sent
 over the network, \fBchronyd\fP will respond with a \(oqNot authorised\(cq error, even
-if it is from localhost. In chrony versions before 2.2 they were allowed
-from the network if they were authenticated with a password, but that is no
-longer supported.
+if it is from localhost.
 .sp
 Having full access to \fBchronyd\fP via \fBchronyc\fP is more or less equivalent to
-being able to modify the \fBchronyd\fP\(cqs configuration file and restart it.
+being able to modify the \fBchronyd\fP\(aqs configuration file and restart it.
 .SH "OPTIONS"
 .sp
 \fB\-4\fP
@@ -76,12 +84,20 @@
 DNS lookups. Long addresses will not be truncated to fit into the column.
 .RE
 .sp
+\fB\-N\fP
+.RS 4
+This option enables printing of original hostnames or IP addresses of NTP
+sources that were specified in the configuration file, or \fBchronyc\fP commands.
+Without the \fB\-n\fP and \fB\-N\fP option, the printed hostnames are obtained from
+reverse DNS lookups and can be different from the specified hostnames.
+.RE
+.sp
 \fB\-c\fP
 .RS 4
 This option enables printing of reports in a comma\-separated values (CSV)
-format. IP addresses will not be resolved to hostnames, time will be printed as
-number of seconds since the epoch and values in seconds will not be converted
-to other units.
+format. Reverse DNS lookups will be disabled, time will be printed as number of
+seconds since the epoch, and values in seconds will not be converted to other
+units.
 .RE
 .sp
 \fB\-d\fP
@@ -124,11 +140,17 @@
 This option is ignored and is provided only for compatibility.
 .RE
 .sp
-\fB\-v\fP
+\fB\-v\fP, \fB\-\-version\fP
 .RS 4
 With this option \fBchronyc\fP displays its version number on the terminal and
 exits.
 .RE
+.sp
+\fB\-\-help\fP
+.RS 4
+With this option \fBchronyc\fP displays a help message on the terminal and
+exits.
+.RE
 .SH "COMMANDS"
 .sp
 This section describes each of the commands available within the \fBchronyc\fP
@@ -140,10 +162,9 @@
 The \fBtracking\fP command displays parameters about the system\(cqs clock
 performance. An example of the output is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 Reference ID    : CB00710F (foo.example.net)
 Stratum         : 3
 Ref time (UTC)  : Fri Jan 27 09:49:17 2017
@@ -157,10 +178,9 @@
 Root dispersion : 0.001100737 seconds
 Update interval : 64.2 seconds
 Leap status     : Normal
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The fields are explained as follows:
 .sp
@@ -203,14 +223,16 @@
 speeding up or slowing down the system clock until the error has been removed,
 and then returning to the system clock\(cqs normal speed. A consequence of this is
 that there will be a period when the system clock (as read by other programs)
-will be different from \fBchronyd\fP\(cqs estimate of the current true time (which it
-reports to NTP clients when it is operating in server mode). The value reported
+will be different from \fBchronyd\fP\(aqs estimate of the current true time (which it
+reports to NTP clients when it is operating as a server). The value reported
 on this line is the difference due to this effect.
 .RE
 .sp
 \fBLast offset\fP
 .RS 4
-This is the estimated local offset on the last clock update.
+This is the estimated local offset on the last clock update. A positive value
+indicates the local time (as previously estimated true time) was ahead of the
+time sources.
 .RE
 .sp
 \fBRMS offset\fP
@@ -265,15 +287,13 @@
 An absolute bound on the computer\(cqs clock accuracy (assuming the stratum\-1
 computer is correct) is given by:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 clock_error <= |system_time_offset| + root_dispersion + (0.5 * root_delay)
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBUpdate interval\fP
@@ -308,16 +328,14 @@
 needed, without waiting for \fBchronyd\fP to complete the measurement and update
 the clock.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 makestep 0.1 1
 burst 1/2
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 BE WARNED: Certain software will be seriously affected by such jumps in the
 system time. (That is the reason why \fBchronyd\fP uses slewing normally.)
@@ -348,15 +366,13 @@
 .sp
 An example is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 waitsync 60 0.01
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 which will wait up to about 10 minutes (60 times 10 seconds) for \fBchronyd\fP to
 synchronise to a source and the remaining correction to be less than 10
@@ -364,28 +380,29 @@
 .RE
 .SS "Time sources"
 .sp
-\fBsources\fP [\fB\-v\fP]
+\fBsources\fP [\fB\-a\fP] [\fB\-v\fP]
 .RS 4
 This command displays information about the current time sources that \fBchronyd\fP
 is accessing.
 .sp
-The optional argument \fB\-v\fP can be specified, meaning \fIverbose\fP. In this case,
+If the \fB\-a\fP option is specified, all sources are displayed, including those that
+do not have a known address yet. Such sources have an identifier in the format
+\fIID#XXXXXXXXXX\fP, which can be used in other commands expecting a source address.
+.sp
+The \fB\-v\fP option enables a verbose output. In this case,
 extra caption lines are shown as a reminder of the meanings of the columns.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-210 Number of sources = 3
+.fam C
 MS Name/IP address         Stratum Poll Reach LastRx Last sample
 ===============================================================================
 #* GPS0                          0   4   377    11   \-479ns[ \-621ns] +/\-  134ns
 ^? foo.example.net               2   6   377    23   \-923us[ \-924us] +/\-   43ms
 ^+ bar.example.net               1   6   377    21  \-2629us[\-2619us] +/\-   86ms
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows:
 .sp
@@ -397,17 +414,18 @@
 .sp
 \fBS\fP
 .RS 4
-This column indicates the state of the source.
+This column indicates the selection state of the source.
 .sp
 .RS 4
 .ie n \{\
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
-\fI*\fP indicates the source to which \fBchronyd\fP is currently synchronised.
+\fI*\fP indicates the best source which is currently selected for
+synchronisation.
 .RE
 .sp
 .RS 4
@@ -415,11 +433,11 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
-\fI+\fP indicates acceptable sources which are combined with the selected
-source.
+\fI+\fP indicates other sources selected for synchronisation, which are combined
+with the best source.
 .RE
 .sp
 .RS 4
@@ -427,11 +445,11 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
-\fI\-\fP indicates acceptable sources which are excluded by the combining
-algorithm.
+\fI\-\fP indicates a source which is considered to be selectable for
+synchronisation, but not currently selected.
 .RE
 .sp
 .RS 4
@@ -439,12 +457,12 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
-\fI?\fP indicates sources to which connectivity has been lost or whose packets
-do not pass all tests. It is also shown at start\-up, until at least 3 samples
-have been gathered from it.
+\fIx\fP indicates a source which \fBchronyd\fP thinks is a falseticker (i.e. its
+time is inconsistent with a majority of other sources, or sources specified
+with the \fBtrust\fP option).
 .RE
 .sp
 .RS 4
@@ -452,23 +470,30 @@
 \h'-04'\(bu\h'+03'\c
 .\}
 .el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-\fIx\fP indicates a clock which \fBchronyd\fP thinks is a falseticker (i.e. its
-time is inconsistent with a majority of other sources).
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
+.  sp -1
+.  IP \(bu 2.3
 .\}
 \fI~\fP indicates a source whose time appears to have too much variability.
 .RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fI?\fP indicates a source which is not considered to be selectable for
+synchronisation for other reasons (e.g. unreachable, not synchronised, or
+does not have enough measurements).
+.RE
+.RE
+.sp
+
+.RS 4
+The \fBselectdata\fP command can be used to get more details about
+the selection state.
 .RE
 .sp
 \fBName/IP address\fP
@@ -504,8 +529,9 @@
 .sp
 \fBLastRx\fP
 .RS 4
-This column shows how long ago the last sample was received from the source.
-This is normally in seconds. The letters \fIm\fP, \fIh\fP, \fId\fP or \fIy\fP indicate
+This column shows how long ago the last good sample (which is shown in the next
+column) was received from the source. Measurements that failed some tests are
+ignored. This is normally in seconds. The letters \fIm\fP, \fIh\fP, \fId\fP or \fIy\fP indicate
 minutes, hours, days, or years.
 .RE
 .sp
@@ -523,29 +549,30 @@
 .RE
 .RE
 .sp
-\fBsourcestats\fP [\fB\-v\fP]
+\fBsourcestats\fP [\fB\-a\fP] [\fB\-v\fP]
 .RS 4
 The \fBsourcestats\fP command displays information about the drift rate and offset
 estimation process for each of the sources currently being examined by
 \fBchronyd\fP.
 .sp
-The optional argument \fB\-v\fP can be specified, meaning \fIverbose\fP. In this case,
+If the \fB\-a\fP option is specified, all sources are displayed, including those that
+do not have a known address yet. Such sources have an identifier in the format
+\fIID#XXXXXXXXXX\fP, which can be used in other commands expecting a source address.
+.sp
+The \fB\-v\fP option enables a verbose output. In this case,
 extra caption lines are shown as a reminder of the meanings of the columns.
 .sp
 An example report is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-210 Number of sources = 1
+.fam C
 Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
 ===============================================================================
 foo.example.net            11   5   46m     \-0.001      0.045      1us    25us
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as follows:
 .sp
@@ -601,6 +628,369 @@
 .RE
 .RE
 .sp
+\fBselectdata\fP [\fB\-a\fP] [\fB\-v\fP]
+.RS 4
+The \fBselectdata\fP command displays information specific to the selection of time
+sources. If the \fB\-a\fP option is specified, all sources are displayed, including
+those that do not have a known address yet. With the \fB\-v\fP option, extra caption
+lines are shown as a reminder of the meanings of the columns.
+.sp
+An example of the output is shown below.
+.sp
+.if n .RS 4
+.nf
+.fam C
+S Name/IP Address        Auth COpts EOpts Last Score     Interval  Leap
+=======================================================================
+D foo.example.net           Y \-\-\-\-\- \-\-TR\-    4   1.0   \-61ms   +62ms  N
+* bar.example.net           N \-\-\-\-\- \-\-\-\-\-    0   1.0 \-6846us +7305us  N
++ baz.example.net           N \-\-\-\-\- \-\-\-\-\-   10   1.0 \-7381us +7355us  N
+.fam
+.fi
+.if n .RE
+.sp
+The columns are as follows:
+.sp
+\fBS\fP
+.RS 4
+This column indicates the state of the source after the last source selection.
+It is similar to the state reported by the \fBsources\fP command, but more
+states are reported.
+.RE
+.sp
+
+.RS 4
+The following states indicate the source is not considered selectable for
+synchronisation:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIN\fP \- has the \fBnoselect\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIM\fP \- does not have enough measurements.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fId\fP \- has a root distance larger than the maximum distance (configured by the
+\fBmaxdistance\fP directive).
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fI~\fP \- has a jitter larger than the maximum jitter (configured by the
+\fBmaxjitter\fP directive).
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIw\fP \- waits for other sources to get out of the \fIM\fP state.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIS\fP \- has older measurements than other sources.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIO\fP \- has a stratum equal or larger than the orphan stratum (configured by
+the \fBlocal\fP directive).
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIT\fP \- does not fully agree with sources that have the \fBtrust\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIx\fP \- does not agree with other sources (falseticker).
+.RE
+.RE
+.sp
+
+.RS 4
+The following states indicate the source is considered selectable, but it is
+not currently used for synchronisation:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIW\fP \- waits for other sources to be selectable (required by the
+\fBminsources\fP directive, or
+the \fBrequire\fP option of another source).
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIP\fP \- another selectable source is preferred due to the \fBprefer\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIU\fP \- waits for a new measurement (after selecting a different best source).
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fID\fP \- has, or recently had, a root distance which is too large to be combined
+with other sources (configured by the
+\fBcombinelimit\fP directive).
+.RE
+.RE
+.sp
+
+.RS 4
+The following states indicate the source is used for synchronisation of the
+local clock:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fI+\fP \- combined with the best source.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fI*\fP \- selected as the best source to update the reference data (e.g. root
+delay, root dispersion).
+.RE
+.RE
+.sp
+\fBName/IP address\fP
+.RS 4
+This column shows the name or IP address of the source if it is an NTP server,
+or the reference ID if it is a reference clock.
+.RE
+.sp
+\fBAuth\fP
+.RS 4
+This column indicites whether an authentication mechanism is enabled for the
+source. \fIY\fP means yes and \fIN\fP means no.
+.RE
+.sp
+\fBCOpts\fP
+.RS 4
+This column displays the configured selection options of the source.
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIN\fP indicates the \fBnoselect\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIP\fP indicates the \fBprefer\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIT\fP indicates the \fBtrust\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIR\fP indicates the \fBrequire\fP option.
+.RE
+.RE
+.sp
+\fBEOpts\fP
+.RS 4
+This column displays the current effective selection options of the source,
+which can be different from the configured options due to the authentication
+selection mode (configured by the
+\fBauthselmode\fP directive). The symbols are the
+same as in the \fBCOpts\fP column.
+.RE
+.sp
+\fBLast\fP
+.RS 4
+This column displays how long ago was the last measurement of the source made
+when the selection was performed.
+.RE
+.sp
+\fBScore\fP
+.RS 4
+This column displays the current score against the source in the \fI*\fP state. The
+scoring system avoids frequent reselection when multiple sources have a similar
+root distance. A value larger than 1 indicates this source was better than the
+\fI*\fP source in recent selections. If the score reaches 10, the best source will
+be reselected and the scores will be reset to 1.
+.RE
+.sp
+\fBInterval\fP
+.RS 4
+This column displays the lower and upper endpoint of the interval which was
+expected to contain the true offset of the local clock considering the root
+distance at the time of the selection.
+.RE
+.sp
+\fBLeap\fP
+.RS 4
+This column displays the current leap status of the source.
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fIN\fP indicates the normal status (no leap second).
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fI+\fP indicates that a leap second will be inserted at the end of the month.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fI\-\fP indicates that a leap second will be deleted at the end of the month.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+\fI?\fP indicates the unknown status (i.e. no valid measurement was made).
+.RE
+.RE
+.RE
+.sp
 \fBreselect\fP
 .RS 4
 To avoid excessive switching between sources, \fBchronyd\fP can stay synchronised
@@ -660,16 +1050,262 @@
 .RE
 .RE
 .sp
+\fBauthdata\fP [\fB\-a\fP]
+.RS 4
+The \fBauthdata\fP command displays information specific to authentication of NTP
+sources. If the \fB\-a\fP option is specified, all sources are displayed, including
+those that do not have a known address yet. An example of the output is
+shown below.
+.sp
+.if n .RS 4
+.nf
+.fam C
+Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+foo.example.net              NTS     1   15  256 135m    0    0    8  100
+bar.example.net               SK    30   13  128    \-    0    0    0    0
+baz.example.net                \-     0    0    0    \-    0    0    0    0
+.fam
+.fi
+.if n .RE
+.sp
+The columns are as follows:
+.sp
+\fBName/IP address\fP
+.RS 4
+This column shows the name or the IP address of the source.
+.RE
+.sp
+\fBMode\fP
+.RS 4
+This column shows which mechanism authenticates NTP packets received from the
+source. \fINTS\fP means Network Time Security, \fISK\fP means a symmetric key, and \fI\-\fP
+means authentication is disabled.
+.RE
+.sp
+\fBKeyID\fP
+.RS 4
+This column shows an identifier of the key used for authentication. With a
+symmetric key, it is the ID from the key file.
+With NTS, it is a number starting at zero and incremented by one with each
+successful key establishment using the NTS\-KE protocol, i.e. it shows how many
+times the key establishment was performed with this source.
+.RE
+.sp
+\fBType\fP
+.RS 4
+This columns shows an identifier of the algorithm used for authentication.
+With a symmetric key, it is the hash function or cipher specified in the key
+file. With NTS, it is an authenticated encryption with associated data (AEAD)
+algorithm, which is negotiated in the NTS\-KE protocol. The following values can
+be reported:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+1: MD5
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+2: SHA1
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+3: SHA256
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+4: SHA384
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+5: SHA512
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+6: SHA3\-224
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+7: SHA3\-256
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+8: SHA3\-384
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+9: SHA3\-512
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+10: TIGER
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+11: WHIRLPOOL
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+13: AES128
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+14: AES256
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.  sp -1
+.  IP \(bu 2.3
+.\}
+15: AEAD\-AES\-SIV\-CMAC\-256
+.RE
+.RE
+.sp
+\fBKLen\fP
+.RS 4
+This column shows the length of the key in bits.
+.RE
+.sp
+\fBLast\fP
+.RS 4
+This column shows how long ago the last successful key establishment was
+performed. It is in seconds, or letters \fIm\fP, \fIh\fP, \fId\fP or \fIy\fP indicate minutes,
+hours, days, or years.
+.RE
+.sp
+\fBAtmp\fP
+.RS 4
+This column shows the number of attempts to perform the key establishment since
+the last successful key establishment. A number larger than 1 indicates a
+problem with the network or server.
+.RE
+.sp
+\fBNAK\fP
+.RS 4
+This column shows whether an NTS NAK was received since the last request.
+A NAK indicates that authentication failed on the server side due to
+\fBchronyd\fP using a cookie which is no longer valid and that it needs to perform
+the key establishment again in order to get new cookies.
+.RE
+.sp
+\fBCook\fP
+.RS 4
+This column shows the number of NTS cookies that \fBchronyd\fP currently has. If
+the key establishment was successful, a number smaller than 8 indicates a
+problem with the network or server.
+.RE
+.sp
+\fBCLen\fP
+.RS 4
+This column shows the length in bytes of the NTS cookie which will be used in
+the next request.
+.RE
+.RE
+.sp
 \fBntpdata\fP [\fIaddress\fP]
 .RS 4
 The \fBntpdata\fP command displays the last valid measurement and other
-NTP\-specific information about the specified NTP source, or all NTP sources if
-no address was specified. An example of the output is shown below.
+NTP\-specific information about the specified NTP source, or all NTP sources
+(with a known address) if no address was specified. An example of the output is
+shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 Remote address  : 203.0.113.15 (CB00710F)
 Remote port     : 123
 Local address   : 203.0.113.74 (CB00714A)
@@ -696,10 +1332,9 @@
 Total TX        : 24
 Total RX        : 24
 Total valid RX  : 24
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The fields are explained as follows:
 .sp
@@ -787,52 +1422,64 @@
 .RE
 .RE
 .sp
-\fBadd peer\fP \fIaddress\fP [\fIoption\fP]...
+\fBadd peer\fP \fIname\fP [\fIoption\fP]...
 .RS 4
 The \fBadd peer\fP command allows a new NTP peer to be added whilst
 \fBchronyd\fP is running.
 .sp
 Following the words \fBadd peer\fP, the syntax of the following
-parameters and options is similar to that for the
+parameters and options is identical to that for the
 \fBpeer\fP directive in the configuration file.
-The following peer options can be set in the command: \fBport\fP, \fBminpoll\fP,
-\fBmaxpoll\fP, \fBpresend\fP, \fBmaxdelayratio\fP, \fBmaxdelay\fP, \fBkey\fP.
 .sp
 An example of using this command is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 add peer foo.example.net minpoll 6 maxpoll 10 key 25
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
-\fBadd server\fP \fIaddress\fP [\fIoption\fP]...
+\fBadd pool\fP \fIname\fP [\fIoption\fP]...
+.RS 4
+The \fBadd pool\fP command allows a pool of NTP servers to be added whilst
+\fBchronyd\fP is running.
+.sp
+Following the words \fBadd pool\fP, the syntax of the following parameters and
+options is identical to that for the \fBpool\fP
+directive in the configuration file.
+.sp
+An example of using this command is shown below:
+.sp
+.if n .RS 4
+.nf
+.fam C
+add pool foo.example.net maxsources 3 iburst
+.fam
+.fi
+.if n .RE
+.RE
+.sp
+\fBadd server\fP \fIname\fP [\fIoption\fP]...
 .RS 4
 The \fBadd server\fP command allows a new NTP server to be added whilst
 \fBchronyd\fP is running.
 .sp
 Following the words \fBadd server\fP, the syntax of the following parameters and
-options is similar to that for the \fBserver\fP
+options is identical to that for the \fBserver\fP
 directive in the configuration file.
-The following server options can be set in the command: \fBport\fP, \fBminpoll\fP,
-\fBmaxpoll\fP, \fBpresend\fP, \fBmaxdelayratio\fP, \fBmaxdelay\fP, \fBkey\fP.
 .sp
 An example of using this command is shown below:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 add server foo.example.net minpoll 6 maxpoll 10 key 25
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBdelete\fP \fIaddress\fP
@@ -874,7 +1521,7 @@
 .sp
 \fImask\fP
 .RS 4
-This is an IP address with which the IP address of each of \fBchronyd\fP\(cqs
+This is an IP address with which the IP address of each of \fBchronyd\fP\(aqs
 sources is to be masked.
 .RE
 .sp
@@ -905,15 +1552,13 @@
 .sp
 An example of the two\-argument form of the command is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 burst 2/10
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This will cause \fBchronyd\fP to attempt to get two good measurements from each
 source, stopping after two have been obtained, but in no event will it try more
@@ -921,16 +1566,14 @@
 .sp
 Examples of the four\-argument form of the command are:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 burst 2/10 255.255.0.0/1.2.0.0
 burst 2/10 2001:db8:789a::/48
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 In the first case, the two out of ten sampling will only be applied to sources
 whose IPv4 addresses are of the form \fI1.2.x.y\fP, where \fIx\fP and \fIy\fP are
@@ -939,15 +1582,13 @@
 .sp
 Example of the three\-argument form of the command is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 burst 2/10 foo.example.net
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBmaxdelay\fP \fIaddress\fP \fIdelay\fP
@@ -1014,22 +1655,21 @@
 this.
 .sp
 There are four forms of the \fBoffline\fP command. The first form is a wildcard,
-meaning all sources. The second form allows an IP address mask and a masked
+meaning all sources (including sources that do not have a known address yet).
+The second form allows an IP address mask and a masked
 address to be specified. The third form uses CIDR notation. The fourth form
 uses an IP address or a hostname. These forms are illustrated below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 offline
 offline 255.255.255.0/1.2.3.0
 offline 2001:db8:789a::/48
 offline foo.example.net
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The second form means that the \fBoffline\fP command is to be applied to any source
 whose IPv4 address is in the \fI1.2.3\fP subnet. (The host\(cqs address is logically
@@ -1040,16 +1680,14 @@
 .sp
 The wildcard form of the address is equivalent to:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 offline 0.0.0.0/0.0.0.0
 offline ::/0
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBonline\fP [\fIaddress\fP], \fBonline\fP [\fImasked\-address\fP/\fImasked\-bits\fP], \fBonline\fP [\fImask\fP/\fImasked\-address\fP]
@@ -1061,6 +1699,15 @@
 The syntax is identical to that of the \fBoffline\fP command.
 .RE
 .sp
+\fBonoffline\fP
+.RS 4
+The \fBonoffline\fP command tells \fBchronyd\fP to switch all sources that have a known
+address to the online or
+offline status according to the current network configuration. A source is
+considered online if it is possible to send requests to it, i.e. a network
+route to the source is present.
+.RE
+.sp
 \fBpolltarget\fP \fIaddress\fP \fIpolltarget\fP
 .RS 4
 The \fBpolltarget\fP command is used to modify the poll target for one of the
@@ -1078,6 +1725,24 @@
 automatically after 8 polling intervals, but this command can still be useful
 to replace them immediately and not wait until they are marked as unreachable.
 .RE
+.sp
+\fBreload\fP \fBsources\fP
+.RS 4
+The \fBreload sources\fP command causes \fBchronyd\fP to re\-read all \fI*.sources\fP files
+from the directories specified by the
+\fBsourcedir\fP directive.
+.RE
+.sp
+\fBsourcename\fP \fIaddress\fP
+.RS 4
+The \fBsourcename\fP command prints the original hostname or address that was
+specified for an NTP source in the configuration file, or the \fBadd\fP command.
+This command is an alternative to the \fB\-N\fP option, which can be useful in
+scripts.
+.sp
+Note that different NTP sources can share the same name, e.g. servers from a
+pool.
+.RE
 .SS "Manual time input"
 .sp
 \fBmanual\fP \fBon\fP, \fBmanual\fP \fBoff\fP, \fBmanual\fP \fBdelete\fP \fIindex\fP, \fBmanual\fP \fBlist\fP, \fBmanual\fP \fBreset\fP
@@ -1092,18 +1757,16 @@
 The \fBlist\fP form of the command lists all the samples currently stored in
 \fBchronyd\fP. The output is illustrated below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 210 n_samples = 1
 #    Date  Time(UTC)    Slewed   Original   Residual
 ====================================================
  0 27Jan99 22:09:20       0.00       0.97       0.00
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The columns are as as follows:
 .sp
@@ -1112,8 +1775,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 The sample index (used for the \fBmanual delete\fP command).
 .RE
@@ -1123,8 +1786,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 The date and time of the sample.
 .RE
@@ -1134,8 +1797,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 The system clock error when the timestamp was entered, adjusted to allow
 for changes made to the system clock since.
@@ -1146,8 +1809,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 The system clock error when the timestamp was entered, as it originally was
 (without allowing for changes to the system clock since).
@@ -1158,8 +1821,8 @@
 \h'-04' 5.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 5." 4.2
+.  sp -1
+.  IP " 5." 4.2
 .\}
 The regression residual at this point, in seconds. This allows \(oqoutliers\(cq
 to be easily spotted, so that they can be deleted using the \fBmanual delete\fP
@@ -1214,17 +1877,15 @@
 .sp
 Examples of inputs that are valid are shown below:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 settime 16:30
 settime 16:30:05
 settime Nov 21, 2015 16:30:05
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 For a full description of getdate, see the getdate documentation
 (bundled, for example, with the source for GNU tar).
@@ -1238,43 +1899,46 @@
 .sp
 Examples of use, showing a named host and a numeric IP address, are as follows:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 accheck foo.example.net
 accheck 1.2.3.4
 accheck 2001:db8::1
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 This command can be used to examine the effect of a series of \fBallow\fP, \fBallow
 all\fP, \fBdeny\fP, and \fBdeny all\fP commands specified either via \fBchronyc\fP, or in
-\fBchronyd\fP\(cqs configuration file.
+\fBchronyd\fP\(aqs configuration file.
 .RE
 .sp
-\fBclients\fP
+\fBclients\fP [\fB\-p\fP \fIpackets\fP] [\fB\-k\fP] [\fB\-r\fP]
 .RS 4
 This command shows a list of clients that have accessed the server, through
-either the NTP or command ports. It does not include accesses over
-the Unix domain command socket. There are no arguments.
+the NTP, command, or NTS\-KE port. It does not include accesses over the Unix
+domain command socket.
+.sp
+The \fB\-p\fP option specifies the minimum number of received NTP or command
+packets, or accepted NTS\-KE connections, needed to include a client in the
+list. The default value is 0, i.e. all clients are reported. With the \fB\-k\fP
+option the last four columns will show the NTS\-KE accesses instead of command
+accesses. If the \fB\-r\fP option is specified, \fBchronyd\fP will reset the counters of
+received and dropped packets or connections after reporting the current values.
 .sp
 An example of the output is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
 ===============================================================================
 localhost                       2      0   2   \-   133      15      0  \-1     7
 foo.example.net                12      0   6   \-    23       0      0   \-     \-
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 Each row shows the data for a single host. Only hosts that have passed the host
 access checks (set with the \fBallow\fP, \fBdeny\fP,
@@ -1289,8 +1953,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 The hostname of the client.
 .RE
@@ -1300,8 +1964,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 The number of NTP packets received from the client.
 .RE
@@ -1311,8 +1975,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 The number of NTP packets dropped to limit the response rate.
 .RE
@@ -1322,8 +1986,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 The average interval between NTP packets.
 .RE
@@ -1333,8 +1997,8 @@
 \h'-04' 5.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 5." 4.2
+.  sp -1
+.  IP " 5." 4.2
 .\}
 The average interval between NTP packets after limiting the response rate.
 .RE
@@ -1344,8 +2008,8 @@
 \h'-04' 6.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 6." 4.2
+.  sp -1
+.  IP " 6." 4.2
 .\}
 Time since the last NTP packet was received
 .RE
@@ -1355,10 +2019,11 @@
 \h'-04' 7.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 7." 4.2
+.  sp -1
+.  IP " 7." 4.2
 .\}
-The number of command packets received from the client.
+The number of command packets or NTS\-KE connections received/accepted from
+the client.
 .RE
 .sp
 .RS 4
@@ -1366,10 +2031,11 @@
 \h'-04' 8.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 8." 4.2
+.  sp -1
+.  IP " 8." 4.2
 .\}
-The number of command packets dropped to limit the response rate.
+The number of command packets or NTS\-KE connections dropped to limit the
+response rate.
 .RE
 .sp
 .RS 4
@@ -1377,10 +2043,10 @@
 \h'-04' 9.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 9." 4.2
+.  sp -1
+.  IP " 9." 4.2
 .\}
-The average interval between command packets.
+The average interval between command packets or NTS\-KE connections.
 .RE
 .sp
 .RS 4
@@ -1388,37 +2054,109 @@
 \h'-04' 10.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 10." 4.2
+.  sp -1
+.  IP " 10." 4.2
 .\}
-Time since the last command packet was received.
+Time since the last command packet or NTS\-KE connection was
+received/accepted.
 .RE
 .RE
 .sp
 \fBserverstats\fP
 .RS 4
-The \fBserverstats\fP command displays how many valid NTP and command requests
-\fBchronyd\fP as a server received from clients, how many of them were dropped to
-limit the response rate as configured by the
-\fBratelimit\fP and
-\fBcmdratelimit\fP directives, and how many
-client log records were dropped due to the memory limit configured by the
-\fBclientloglimit\fP directive. An example of
-the output is shown below.
+The \fBserverstats\fP command displays NTP and command server statistics.
 .sp
-.if n \{\
-.RS 4
-.\}
+An example of the output is shown below.
+.sp
+.if n .RS 4
 .nf
+.fam C
 NTP packets received       : 1598
 NTP packets dropped        : 8
 Command packets received   : 19
 Command packets dropped    : 0
 Client log records dropped : 0
+NTS\-KE connections accepted: 3
+NTS\-KE connections dropped : 0
+Authenticated NTP packets  : 189
+Interleaved NTP packets    : 43
+NTP timestamps held        : 44
+NTP timestamp span         : 120
+.fam
 .fi
-.if n \{\
+.if n .RE
+.sp
+The fields have the following meaning:
+.sp
+\fBNTP packets received\fP
+.RS 4
+The number of valid NTP requests received by the server.
 .RE
-.\}
+.sp
+\fBNTP packets dropped\fP
+.RS 4
+The number of NTP requests dropped by the server due to rate limiting
+(configured by the \fBratelimit\fP directive).
+.RE
+.sp
+\fBCommand packets received\fP
+.RS 4
+The number of command requests received by the server.
+.RE
+.sp
+\fBCommand packets dropped\fP
+.RS 4
+The number of command requests dropped by the server due to rate limiting
+(configured by the \fBcmdratelimit\fP directive).
+.RE
+.sp
+\fBClient log records dropped\fP
+.RS 4
+The number of client log records dropped by the server to limit the memory use
+(configured by the \fBclientloglimit\fP
+directive).
+.RE
+.sp
+\fBNTS\-KE connections accepted\fP
+.RS 4
+The number of NTS\-KE connections accepted by the server.
+.RE
+.sp
+\fBNTS\-KE connections dropped\fP
+.RS 4
+The number of NTS\-KE connections dropped by the server due to rate limiting
+(configured by the \fBntsratelimit\fP directive).
+.RE
+.sp
+\fBAuthenticated NTP packets\fP
+.RS 4
+The number of received NTP requests that were authenticated (with a symmetric
+key or NTS).
+.RE
+.sp
+\fBInterleaved NTP packets\fP
+.RS 4
+The number of received NTP requests that were detected to be in the interleaved
+mode.
+.RE
+.sp
+\fBNTP timestamps held\fP
+.RS 4
+The number of pairs of receive and transmit timestamps that the server is
+currently holding in memory for clients using the interleaved mode.
+.RE
+.sp
+\fBNTP timestamp span\fP
+.RS 4
+The interval (in seconds) covered by the currently held NTP timestamps.
+.RE
+.RE
+.sp
+
+.RS 4
+.sp
+Note that the numbers reported by this overflow to zero after 4294967295
+(32\-bit values).
 .RE
 .sp
 \fBallow\fP [\fBall\fP] [\fIsubnet\fP]
@@ -1428,24 +2166,19 @@
 .sp
 The syntax is illustrated in the following examples:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-allow foo.example.net
-allow all 1.2
-allow 3.4.5
-allow 6.7.8/22
-allow 6.7.8.9/22
+.fam C
+allow 1.2.3.4
+allow all 3.4.5.0/24
 allow 2001:db8:789a::/48
 allow 0/0
 allow ::/0
 allow
 allow all
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBdeny\fP [\fBall\fP] [\fIsubnet\fP]
@@ -1455,24 +2188,19 @@
 .sp
 The syntax is illustrated in the following examples:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
-deny foo.example.net
-deny all 1.2
-deny 3.4.5
-deny 6.7.8/22
-deny 6.7.8.9/22
+.fam C
+deny 1.2.3.4
+deny all 3.4.5.0/24
 deny 2001:db8:789a::/48
 deny 0/0
 deny ::/0
 deny
 deny all
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBlocal\fP [\fIoption\fP]..., \fBlocal\fP \fBoff\fP
@@ -1496,20 +2224,18 @@
 \fBsmoothtime\fP directive. An example of the
 output is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 Active         : Yes
 Offset         : +1.000268817 seconds
 Frequency      : \-0.142859 ppm
 Wander         : \-0.010000 ppm per second
 Last update    : 17.8 seconds ago
 Remaining time : 19988.4 seconds
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The fields are explained as follows:
 .sp
@@ -1566,17 +2292,15 @@
 .sp
 Examples of use are as follows:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 cmdaccheck foo.example.net
 cmdaccheck 1.2.3.4
 cmdaccheck 2001:db8::1
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBcmdallow\fP [\fBall\fP] [\fIsubnet\fP]
@@ -1600,20 +2324,18 @@
 .sp
 An example output is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 RTC ref time (GMT) : Sat May 30 07:25:56 2015
 Number of samples  : 10
 Number of runs     : 5
 Sample span period :  549
 RTC is fast by     :    \-1.632736 seconds
 RTC gains time at  :  \-107.623 ppm
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 The fields have the following meaning:
 .sp
@@ -1650,7 +2372,7 @@
 the time was at the reference time (above). If this value is large, you
 might (or might not) want to use the \fBtrimrtc\fP command to bring the
 RTC into line with the system clock. (Note, a large error will not affect
-\fBchronyd\fP\(cqs operation, unless it becomes so big as to start causing rounding
+\fBchronyd\fP\(aqs operation, unless it becomes so big as to start causing rounding
 errors.)
 .RE
 .sp
@@ -1678,8 +2400,8 @@
 \h'-04' 1.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 1." 4.2
+.  sp -1
+.  IP " 1." 4.2
 .\}
 Remember the currently estimated gain or loss rate of the RTC and flush the
 previous measurements.
@@ -1690,8 +2412,8 @@
 \h'-04' 2.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 2." 4.2
+.  sp -1
+.  IP " 2." 4.2
 .\}
 Step the real\-time clock to bring it within a second of the system clock.
 .RE
@@ -1701,8 +2423,8 @@
 \h'-04' 3.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 3." 4.2
+.  sp -1
+.  IP " 3." 4.2
 .\}
 Make several measurements to accurately determine the new offset between
 the RTC and the system clock (i.e. the remaining fraction of a second
@@ -1714,8 +2436,8 @@
 \h'-04' 4.\h'+01'\c
 .\}
 .el \{\
-.sp -1
-.IP " 4." 4.2
+.  sp -1
+.  IP " 4." 4.2
 .\}
 Save the RTC parameters to the RTC file (specified with the
 \fBrtcfile\fP directive in the configuration file).
@@ -1752,24 +2474,19 @@
 .sp
 \fBcyclelogs\fP
 .RS 4
-The \fBcyclelogs\fP command causes all of \fBchronyd\fP\(cqs open log files to be closed
+The \fBcyclelogs\fP command causes all of \fBchronyd\fP\(aqs open log files to be closed
 and re\-opened. This allows them to be renamed so that they can be periodically
 purged. An example of how to do this is shown below.
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 # mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log
 # chronyc cyclelogs
-# ls \-l /var/log/chrony
-\-rw\-r\-\-r\-\-   1 root     root            0 Jun  8 18:17 measurements.log
-\-rw\-r\-\-r\-\-   1 root     root        12345 Jun  8 18:17 measurements1.log
-# rm \-f measurements1.log
+# rm /var/log/chrony/measurements1.log
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .RE
 .sp
 \fBdump\fP
@@ -1777,15 +2494,31 @@
 The \fBdump\fP command causes \fBchronyd\fP to write its current history of
 measurements for each of its sources to dump files in the directory specified
 in the configuration file by the \fBdumpdir\fP
+directive and also write server NTS keys and client NTS cookies to the
+directory specified by the \fBntsdumpdir\fP
 directive. Note that \fBchronyd\fP does this automatically when it exits. This
-command is mainly useful for inspection of the history whilst \fBchronyd\fP is
-running.
+command is mainly useful for inspection whilst \fBchronyd\fP is running.
 .RE
 .sp
 \fBrekey\fP
 .RS 4
 The \fBrekey\fP command causes \fBchronyd\fP to re\-read the key file specified in the
-configuration file by the \fBkeyfile\fP directive.
+configuration file by the \fBkeyfile\fP directive. It
+also re\-reads the server NTS keys if
+\fBntsdumpdir\fP is specified and
+automatic rotation is disabled in the
+configuration file.
+.RE
+.sp
+\fBreset\fP \fBsources\fP
+.RS 4
+The \fBreset sources\fP command causes \fBchronyd\fP to drop all measurements and
+switch to the unsynchronised state. This command can help \fBchronyd\fP with
+recovery when the measurements are known to be no longer valid or accurate,
+e.g. due to moving the computer to a different network, or resuming the
+computer from a low\-power state (which resets the system clock). \fBchronyd\fP will
+drop the measurements automatically when it detects the clock has made an
+unexpected jump, but the detection is not completely reliable.
 .RE
 .sp
 \fBshutdown\fP
@@ -1862,26 +2595,34 @@
 .sp
 The command has three optional arguments. The first argument is the key number
 (by default 1), which will be specified with the \fBkey\fP option of the \fBserver\fP
-or \fBpeer\fP directives in the configuration file. The second argument is the hash
-function (by default SHA1 or MD5 if SHA1 is not available) and the third
-argument is the number of bits the key should have, between 80 and 4096 bits
-(by default 160 bits).
+or \fBpeer\fP directives in the configuration file. The second argument is the name
+of the hash function or cipher (by default SHA1, or MD5 if SHA1 is not
+available). The third argument is the length of the key in bits if a hash
+function was selected, between 80 and 4096 bits (by default 160 bits).
 .sp
 An example is:
 .sp
-.if n \{\
-.RS 4
-.\}
+.if n .RS 4
 .nf
+.fam C
 keygen 73 SHA1 256
+.fam
 .fi
-.if n \{\
-.RE
-.\}
+.if n .RE
 .sp
 which generates a 256\-bit SHA1 key with number 73. The printed line should
 then be securely transferred and added to the key files on both server and
-client, or peers.
+client, or peers. A different key should be generated for each client or peer.
+.sp
+An example using the AES128 cipher is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+keygen 151 AES128
+.fam
+.fi
+.if n .RE
 .RE
 .sp
 \fBexit\fP, \fBquit\fP
diff --git a/chrony_3_3/doc/chronyd.adoc b/chrony/doc/chronyd.adoc
similarity index 60%
rename from chrony_3_3/doc/chronyd.adoc
rename to chrony/doc/chronyd.adoc
index fc79078..7ba991d 100644
--- a/chrony_3_3/doc/chronyd.adoc
+++ b/chrony/doc/chronyd.adoc
@@ -41,7 +41,7 @@
 will read them from a configuration file. The compiled-in default location of
 the file is _@SYSCONFDIR@/chrony.conf_.
 
-Information messages and warnings will be logged to syslog.
+Informational messages, warnings, and errors will be logged to syslog.
 
 == OPTIONS
 
@@ -55,20 +55,32 @@
 
 *-f* _file_::
 This option can be used to specify an alternate location for the configuration
-file (default _@SYSCONFDIR@/chrony.conf_).
+file. The compiled-in default value is _@SYSCONFDIR@/chrony.conf_.
 
 *-n*::
 When run in this mode, the program will not detach itself from the terminal.
 
 *-d*::
 When run in this mode, the program will not detach itself from the terminal,
-and all messages will be written to the terminal instead of syslog. When
-*chronyd* was compiled with debugging support, this option can be used twice to
-print also debugging messages.
+and all messages will be written to the terminal instead of syslog. If
+*chronyd* was compiled with enabled support for debugging, this option can be
+used twice to enable debug messages.
 
 *-l* _file_::
-This option specifies a file which should be used for logging instead of syslog
-or terminal.
+This option enables writing of log messages to a file instead of syslog or the
+terminal.
+
+*-L* _level_::
+This option specifies the minimum severity level of messages to be written to
+the log file, syslog, or terminal. The following levels can be specified:
+0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
+default value is 0.
+
+*-p*::
+When run in this mode, *chronyd* will print the configuration and exit. It will
+not detach from the terminal. This option can be used to verify the syntax of
+the configuration and get the whole configuration, even if it is split into
+multiple files and read by the *include* or *confdir* directive.
 
 *-q*::
 When run in this mode, *chronyd* will set the system clock once and exit. It
@@ -81,14 +93,14 @@
 
 *-r*::
 This option will try to reload and then delete files containing sample
-histories for each of the servers and reference clocks being used. These
-histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
-*chronyc*, or by setting the <<chrony.conf.adoc#dumponexit,*dumponexit*>>
+histories for each of the servers and reference clocks being used. The
+files are expected to be in the directory specified by the
+<<chrony.conf.adoc#dumpdir,*dumpdir*>>
 directive in the configuration file. This option is useful if you want to stop
 and restart *chronyd* briefly for any reason, e.g. to install a new version.
 However, it should be used only on systems where the kernel can maintain clock
 compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
-Solaris, and macOS 10.13 or later).
+illumos, and macOS 10.13 or later).
 
 *-R*::
 When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -125,49 +137,73 @@
 *-u* _user_::
 This option sets the name of the system user to which *chronyd* will switch
 after start in order to drop root privileges. It overrides the
-<<chrony.conf.adoc#user,*user*>> directive (default _@DEFAULT_USER@_).
+<<chrony.conf.adoc#user,*user*>> directive. The compiled-in default value is
+_@DEFAULT_USER@_.
 +
 On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
-On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
+On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
 The child process retains root privileges, but can only perform a very limited
 range of privileged system calls on behalf of the parent.
 
+*-U*::
+This option disables a check for root privileges to allow *chronyd* to be
+started under a non-root user, assuming the process will have all capabilities
+(e.g. provided by the service manager) and access to all files, directories,
+and devices, needed to operate correctly in the specified configuration. Note
+that different capabilities might be needed with different configurations and
+different Linux kernel versions. Starting *chronyd* under a non-root user is
+not recommended when the configuration is not known, or at least limited to
+specific directives.
+
 *-F* _level_::
-This option configures a system call filter when *chronyd* is compiled with
-support for the Linux secure computing (seccomp) facility. In level 1 the
-process is killed when a forbidden system call is made, in level -1 the SIGSYS
-signal is thrown instead and in level 0 the filter is disabled (default 0).
+This option configures system call filters loaded by *chronyd* processes if it
+was compiled with support for the Linux secure computing (seccomp) facility.
+Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
+levels 1 and 2, *chronyd* will be killed if it makes a system call which is
+blocked by the filters. The level can be specified as a negative number to
+trigger the SIGSYS signal instead of SIGKILL, which can be useful for
+debugging. The default value is 0.
 +
-It's recommended to enable the filter only when it's known to work on the
-version of the system where *chrony* is installed as the filter needs to allow
-also system calls made from libraries that *chronyd* is using (e.g. libc) and
-different versions or implementations of the libraries may make different
-system calls. If the filter is missing some system call, *chronyd* could be
-killed even in normal operation.
+At level 1, the filters allow only selected system calls that are normally
+expected to be made by *chronyd*. Other system calls are blocked. This level is
+recommended only if it is known to work on the version of the system where
+*chrony* is installed. The filters need to allow also system calls made by
+libraries that *chronyd* is using (e.g. libc), but different versions or
+implementations of the libraries might make different system calls. If the
+filters are missing a system call, *chronyd* could be killed even in normal
+operation.
++
+At level 2, the filters block only a small number of specific system calls
+(e.g. fork and exec). This approach should avoid false positives, but the
+protection of the system against a compromised *chronyd* process is much more
+limited.
++
+The filters cannot be enabled with the *mailonchange* directive.
 
 *-P* _priority_::
-On Linux, this option will select the SCHED_FIFO real-time scheduler at the
-specified priority (which must be between 0 and 100). On macOS, this option
-must have either a value of 0 (the default) to disable the thread time
-constraint policy or 1 for the policy to be enabled. Other systems do not
-support this option.
+On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
+real-time scheduler at the specified priority (which must be between 0 and
+100). On macOS, this option must have either a value of 0 to disable the thread
+time constraint policy or 1 for the policy to be enabled. Other systems do not
+support this option. The default value is 0.
 
 *-m*::
 This option will lock *chronyd* into RAM so that it will never be paged out.
-This mode is only supported on Linux.
+This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
 
 *-x*::
 This option disables the control of the system clock. *chronyd* will not try to
 make any adjustments of the clock. It will assume the clock is free running and
 still track its offset and frequency relative to the estimated true time. This
-option allows *chronyd* to run without the capability to adjust or set the
-system clock (e.g. in some containers) in order to operate as an NTP server. It
-is not recommended to run *chronyd* (with or without *-x*) when another process
-is controlling the system clock.
+option allows *chronyd* to be started without the capability to adjust or set
+the system clock (e.g. in some containers) to operate as an NTP server.
 
-*-v*::
+*-v*, *--version*::
 With this option *chronyd* will print version number to the terminal and exit.
 
+*-h*, *--help*::
+With this option *chronyd* will print a help message to the terminal and exit.
+
 == FILES
 
 _@SYSCONFDIR@/chrony.conf_
diff --git a/chrony/doc/chronyd.man.in b/chrony/doc/chronyd.man.in
new file mode 100644
index 0000000..9aa4ff5
--- /dev/null
+++ b/chrony/doc/chronyd.man.in
@@ -0,0 +1,264 @@
+'\" t
+.\"     Title: chronyd
+.\"    Author: [see the "AUTHOR(S)" section]
+.\" Generator: Asciidoctor 2.0.15
+.\"      Date: 2021-12-16
+.\"    Manual: System Administration
+.\"    Source: chrony @CHRONY_VERSION@
+.\"  Language: English
+.\"
+.TH "CHRONYD" "8" "2021-12-16" "chrony @CHRONY_VERSION@" "System Administration"
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.ss \n[.ss] 0
+.nh
+.ad l
+.de URL
+\fI\\$2\fP <\\$1>\\$3
+..
+.als MTO URL
+.if \n[.g] \{\
+.  mso www.tmac
+.  am URL
+.    ad l
+.  .
+.  am MTO
+.    ad l
+.  .
+.  LINKSTYLE blue R < >
+.\}
+.SH "NAME"
+chronyd \- chrony daemon
+.SH "SYNOPSIS"
+.sp
+\fBchronyd\fP [\fIOPTION\fP]... [\fIDIRECTIVE\fP]...
+.SH "DESCRIPTION"
+.sp
+\fBchronyd\fP is a daemon for synchronisation of the system clock. It can
+synchronise the clock with NTP servers, reference clocks (e.g. a GPS receiver),
+and manual input using wristwatch and keyboard via \fBchronyc\fP. It can also
+operate as an NTPv4 (RFC 5905) server and peer to provide a time service to
+other computers in the network.
+.sp
+If no configuration directives are specified on the command line, \fBchronyd\fP
+will read them from a configuration file. The compiled\-in default location of
+the file is \fI@SYSCONFDIR@/chrony.conf\fP.
+.sp
+Informational messages, warnings, and errors will be logged to syslog.
+.SH "OPTIONS"
+.sp
+\fB\-4\fP
+.RS 4
+With this option hostnames will be resolved only to IPv4 addresses and only
+IPv4 sockets will be created.
+.RE
+.sp
+\fB\-6\fP
+.RS 4
+With this option hostnames will be resolved only to IPv6 addresses and only
+IPv6 sockets will be created.
+.RE
+.sp
+\fB\-f\fP \fIfile\fP
+.RS 4
+This option can be used to specify an alternate location for the configuration
+file. The compiled\-in default value is \fI@SYSCONFDIR@/chrony.conf\fP.
+.RE
+.sp
+\fB\-n\fP
+.RS 4
+When run in this mode, the program will not detach itself from the terminal.
+.RE
+.sp
+\fB\-d\fP
+.RS 4
+When run in this mode, the program will not detach itself from the terminal,
+and all messages will be written to the terminal instead of syslog. If
+\fBchronyd\fP was compiled with enabled support for debugging, this option can be
+used twice to enable debug messages.
+.RE
+.sp
+\fB\-l\fP \fIfile\fP
+.RS 4
+This option enables writing of log messages to a file instead of syslog or the
+terminal.
+.RE
+.sp
+\fB\-L\fP \fIlevel\fP
+.RS 4
+This option specifies the minimum severity level of messages to be written to
+the log file, syslog, or terminal. The following levels can be specified:
+0 (informational), 1 (warning), 2 (non\-fatal error), and 3 (fatal error). The
+default value is 0.
+.RE
+.sp
+\fB\-p\fP
+.RS 4
+When run in this mode, \fBchronyd\fP will print the configuration and exit. It will
+not detach from the terminal. This option can be used to verify the syntax of
+the configuration and get the whole configuration, even if it is split into
+multiple files and read by the \fBinclude\fP or \fBconfdir\fP directive.
+.RE
+.sp
+\fB\-q\fP
+.RS 4
+When run in this mode, \fBchronyd\fP will set the system clock once and exit. It
+will not detach from the terminal.
+.RE
+.sp
+\fB\-Q\fP
+.RS 4
+This option is similar to the \fB\-q\fP option, except it only prints the offset
+without making any corrections of the clock and it allows \fBchronyd\fP to be
+started without root privileges.
+.RE
+.sp
+\fB\-r\fP
+.RS 4
+This option will try to reload and then delete files containing sample
+histories for each of the servers and reference clocks being used. The
+files are expected to be in the directory specified by the
+\fBdumpdir\fP
+directive in the configuration file. This option is useful if you want to stop
+and restart \fBchronyd\fP briefly for any reason, e.g. to install a new version.
+However, it should be used only on systems where the kernel can maintain clock
+compensation whilst not under \fBchronyd\fP\(aqs control (i.e. Linux, FreeBSD, NetBSD,
+illumos, and macOS 10.13 or later).
+.RE
+.sp
+\fB\-R\fP
+.RS 4
+When this option is used, the \fBinitstepslew\fP
+directive and the \fBmakestep\fP directive used with
+a positive limit will be ignored. This option is useful when restarting
+\fBchronyd\fP and can be used in conjunction with the \fB\-r\fP option.
+.RE
+.sp
+\fB\-s\fP
+.RS 4
+This option will set the system clock from the computer\(cqs real\-time clock (RTC)
+or to the last modification time of the file specified by the
+\fBdriftfile\fP directive. Real\-time clocks are
+supported only on Linux.
+.sp
+If used in conjunction with the \fB\-r\fP flag, \fBchronyd\fP will attempt to preserve
+the old samples after setting the system clock from the RTC. This can be used
+to allow \fBchronyd\fP to perform long term averaging of the gain or loss rate
+across system reboots, and is useful for systems with intermittent access to
+network that are shut down when not in use. For this to work well, it relies
+on \fBchronyd\fP having been able to determine accurate statistics for the
+difference between the RTC and system clock last time the computer was on.
+.sp
+If the last modification time of the drift file is later than both the current
+time and the RTC time, the system time will be set to it to restore the time
+when \fBchronyd\fP was previously stopped. This is useful on computers that have no
+RTC or the RTC is broken (e.g. it has no battery).
+.RE
+.sp
+\fB\-t\fP \fItimeout\fP
+.RS 4
+This option sets a timeout (in seconds) after which \fBchronyd\fP will exit. If the
+clock is not synchronised, it will exit with a non\-zero status. This is useful
+with the \fB\-q\fP or \fB\-Q\fP option to shorten the maximum time waiting for
+measurements, or with the \fB\-r\fP option to limit the time when \fBchronyd\fP is
+running, but still allow it to adjust the frequency of the system clock.
+.RE
+.sp
+\fB\-u\fP \fIuser\fP
+.RS 4
+This option sets the name of the system user to which \fBchronyd\fP will switch
+after start in order to drop root privileges. It overrides the
+\fBuser\fP directive. The compiled\-in default value is
+\fI@DEFAULT_USER@\fP.
+.sp
+On Linux, \fBchronyd\fP needs to be compiled with support for the \fBlibcap\fP library.
+On macOS, FreeBSD, NetBSD, and illumos \fBchronyd\fP forks into two processes.
+The child process retains root privileges, but can only perform a very limited
+range of privileged system calls on behalf of the parent.
+.RE
+.sp
+\fB\-U\fP
+.RS 4
+This option disables a check for root privileges to allow \fBchronyd\fP to be
+started under a non\-root user, assuming the process will have all capabilities
+(e.g. provided by the service manager) and access to all files, directories,
+and devices, needed to operate correctly in the specified configuration. Note
+that different capabilities might be needed with different configurations and
+different Linux kernel versions. Starting \fBchronyd\fP under a non\-root user is
+not recommended when the configuration is not known, or at least limited to
+specific directives.
+.RE
+.sp
+\fB\-F\fP \fIlevel\fP
+.RS 4
+This option configures system call filters loaded by \fBchronyd\fP processes if it
+was compiled with support for the Linux secure computing (seccomp) facility.
+Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
+levels 1 and 2, \fBchronyd\fP will be killed if it makes a system call which is
+blocked by the filters. The level can be specified as a negative number to
+trigger the SIGSYS signal instead of SIGKILL, which can be useful for
+debugging. The default value is 0.
+.sp
+At level 1, the filters allow only selected system calls that are normally
+expected to be made by \fBchronyd\fP. Other system calls are blocked. This level is
+recommended only if it is known to work on the version of the system where
+\fBchrony\fP is installed. The filters need to allow also system calls made by
+libraries that \fBchronyd\fP is using (e.g. libc), but different versions or
+implementations of the libraries might make different system calls. If the
+filters are missing a system call, \fBchronyd\fP could be killed even in normal
+operation.
+.sp
+At level 2, the filters block only a small number of specific system calls
+(e.g. fork and exec). This approach should avoid false positives, but the
+protection of the system against a compromised \fBchronyd\fP process is much more
+limited.
+.sp
+The filters cannot be enabled with the \fBmailonchange\fP directive.
+.RE
+.sp
+\fB\-P\fP \fIpriority\fP
+.RS 4
+On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
+real\-time scheduler at the specified priority (which must be between 0 and
+100). On macOS, this option must have either a value of 0 to disable the thread
+time constraint policy or 1 for the policy to be enabled. Other systems do not
+support this option. The default value is 0.
+.RE
+.sp
+\fB\-m\fP
+.RS 4
+This option will lock \fBchronyd\fP into RAM so that it will never be paged out.
+This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
+.RE
+.sp
+\fB\-x\fP
+.RS 4
+This option disables the control of the system clock. \fBchronyd\fP will not try to
+make any adjustments of the clock. It will assume the clock is free running and
+still track its offset and frequency relative to the estimated true time. This
+option allows \fBchronyd\fP to be started without the capability to adjust or set
+the system clock (e.g. in some containers) to operate as an NTP server.
+.RE
+.sp
+\fB\-v\fP, \fB\-\-version\fP
+.RS 4
+With this option \fBchronyd\fP will print version number to the terminal and exit.
+.RE
+.sp
+\fB\-h\fP, \fB\-\-help\fP
+.RS 4
+With this option \fBchronyd\fP will print a help message to the terminal and exit.
+.RE
+.SH "FILES"
+.sp
+\fI@SYSCONFDIR@/chrony.conf\fP
+.SH "SEE ALSO"
+.sp
+\fBchronyc(1)\fP, \fBchrony.conf(5)\fP
+.SH "BUGS"
+.sp
+For instructions on how to report bugs, please visit
+.URL "https://chrony.tuxfamily.org/" "" "."
+.SH "AUTHORS"
+.sp
+chrony was written by Richard Curnow, Miroslav Lichvar, and others.
\ No newline at end of file
diff --git a/chrony/doc/faq.adoc b/chrony/doc/faq.adoc
new file mode 100644
index 0000000..d1b4cf3
--- /dev/null
+++ b/chrony/doc/faq.adoc
@@ -0,0 +1,915 @@
+// This file is part of chrony
+//
+// Copyright (C) Richard P. Curnow  1997-2003
+// Copyright (C) Miroslav Lichvar  2014-2016, 2020-2021
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of version 2 of the GNU General Public License as
+// published by the Free Software Foundation.
+//
+// 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, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+= Frequently Asked Questions
+:toc:
+:numbered:
+
+== `chrony` compared to other programs
+
+=== How does `chrony` compare to `ntpd`?
+
+`chrony` and `ntpd` are two different implementations of the Network Time
+Protocol (NTP).
+
+`chrony` is a newer implementation, which was designed to work well in a wider
+range of conditions. It can usually synchronise the system clock faster and
+with better time accuracy. It has many features, but it does not implement some
+of the less useful NTP modes like broadcast client or multicast server/client.
+
+If your computer is connected to the Internet only for few minutes at a time,
+the network connection is often congested, you turn your computer off or
+suspend it frequently, the clock is not very stable (e.g. there are rapid
+changes in the temperature or it is a virtual machine), or you want to use NTP
+on an isolated network with no hardware reference clocks in sight, `chrony`
+will probably work better for you.
+
+For a more detailed comparison of features and performance, see the
+https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
+website.
+
+== Configuration issues
+
+=== What is the minimum recommended configuration for an NTP client?
+
+First, the client needs to know which NTP servers it should ask for the current
+time. They are specified by the `server` or `pool` directive. The `pool`
+directive is used with names that resolve to multiple addresses of different
+servers. For reliable operation, the client should have at least three servers.
+
+The `iburst` option enables a burst of requests to speed up the initial
+synchronisation.
+
+To stabilise the initial synchronisation on the next start, the estimated drift
+of the system clock is saved to a file specified by the `driftfile` directive.
+
+If the system clock can be far from the true time after boot for any reason,
+`chronyd` should be allowed to correct it quickly by stepping instead of
+slewing, which would take a very long time. The `makestep` directive does
+that.
+
+In order to keep the real-time clock (RTC) close to the true time, so the
+system time is reasonably close to the true time when it is initialised on the
+next boot from the RTC, the `rtcsync` directive enables a mode in which the
+system time is periodically copied to the RTC. It is supported on Linux and
+macOS.
+
+If you wanted to use public NTP servers from the
+https://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
+could be:
+
+----
+pool pool.ntp.org iburst
+driftfile /var/lib/chrony/drift
+makestep 1 3
+rtcsync
+----
+
+=== How do I make an NTP server?
+
+By default, `chronyd` does not operate as an NTP server. You need to add an
+`allow` directive to the _chrony.conf_ file in order for `chronyd` to open the
+server NTP port and respond to client requests.
+
+----
+allow 192.168.1.0/24
+----
+
+An `allow` directive with no specified subnet allows access from all IPv4 and
+IPv6 addresses.
+
+=== Should all computers on a LAN be clients of an external server?
+
+It depends on the requirements. Usually, the best configuration is to make one
+computer the server, with the others as clients of it. Add a `local` directive
+to the server's _chrony.conf_ file. This configuration will be better because
+
+* the load on the external connection is less
+* the load on the external NTP server(s) is less
+* if your external connection goes down, the computers on the LAN
+  will maintain a common time with each other.
+
+=== Must I specify servers by IP address if DNS is not available on `chronyd` start?
+
+No, `chronyd` will keep trying to resolve
+the names specified by the `server`, `pool`, and `peer` directives in an
+increasing interval until it succeeds. The `online` command can be issued from
+`chronyc` to force `chronyd` to try to resolve the names immediately.
+
+=== How can I make `chronyd` more secure?
+
+If you do not need to use `chronyc`, or you want to run `chronyc` only
+under the root or _chrony_ user (which can access `chronyd` through a Unix
+domain socket), you can disable the IPv4 and IPv6 command sockets (by default
+listening on localhost) by adding `cmdport 0` to the configuration file.
+
+You can specify an unprivileged user with the `-u` option, or the `user`
+directive in the _chrony.conf_ file, to which `chronyd` will switch after start
+in order to drop root privileges. The configure script has a `--with-user`
+option, which sets the default user. On Linux, `chronyd` needs to be compiled
+with support for the `libcap` library. On other systems, `chronyd` forks into
+two processes. The child process retains root privileges, but can only perform
+a very limited range of privileged system calls on behalf of the parent.
+
+Also, if `chronyd` is compiled with support for the Linux secure computing
+(seccomp) facility, you can enable a system call filter with the `-F` option.
+It will significantly reduce the kernel attack surface and possibly prevent
+kernel exploits from the `chronyd` process if it is compromised. It is
+recommended to enable the filter only when it is known to work on the version of
+the system where `chrony` is installed as the filter needs to allow also system
+calls made from libraries that `chronyd` is using (e.g. libc) and different
+versions or implementations of the libraries might make different system calls.
+If the filter is missing some system call, `chronyd` could be killed even in
+normal operation.
+
+=== How can I make the system clock more secure?
+
+An NTP client synchronising the system clock to an NTP server is susceptible to
+various attacks, which can break applications and network protocols relying on
+accuracy of the clock (e.g. DNSSEC, Kerberos, TLS, WireGuard).
+
+Generally, a man-in-the-middle (MITM) attacker between the client and server
+can
+
+* make fake responses, or modify real responses from the server, to create an
+  arbitrarily large time and frequency offset, make the server appear more
+  accurate, insert a leap second, etc.
+* delay the requests and/or responses to create a limited time offset and
+  temporarily also a limited frequency offset
+* drop the requests or responses to prevent updates of the clock with new
+  measurements
+* redirect the requests to a different server
+
+The attacks can be combined for a greater effect. The attacker can delay
+packets to create a significant frequency offset first and then drop all
+subsequent packets to let the clock quickly drift away from the true time.
+The attacker might also be able to control the server's clock.
+
+Some attacks cannot be prevented. Monitoring is needed for detection, e.g. the
+reachability register in the `sources` report shows missing packets. The extent
+to which the attacker can control the client's clock depends on its
+configuration.
+
+Enable authentication to prevent `chronyd` from accepting modified, fake, or
+redirected packets. It can be enabled with a symmetric key specified by the
+`key` option, or Network Time Security (NTS) by the `nts` option (supported
+since `chrony` version 4.0). The server needs to support the selected
+authentication mechanism. Symmetric keys have to be configured on both client
+and server, and each client must have its own key (one per server).
+
+The maximum offset that the attacker can insert in an NTP measurement by
+delaying packets can be limited by the `maxdelay` option. The default value is
+3 seconds. The measured delay is reported as the peer delay in the `ntpdata`
+report and `measurements` log. Set the `maxdelay` option to a value larger than
+the maximum value that is normally observed. Note that the delay can increase
+significantly even when not under an attack, e.g. when the network is congested
+or the routing has changed.
+
+The maximum accepted change in time offset between clock updates can be limited
+by the `maxchange` directive. Larger changes in the offset will be ignored or
+cause `chronyd` to exit. Note that the attacker can get around this limit by
+splitting the offset into multiple smaller offsets and/or creating a large
+frequency offset. When this directive is used, `chronyd` will have to be
+restarted after a successful attack. It will not be able to recover on its own.
+It must not be restarted automatically (e.g. by the service manager).
+
+The impact of a large accepted time offset can be reduced by disabling clock
+steps, i.e. by not using the `makestep` and `initstepslew` directives. The
+offset will be slowly corrected by speeding up or slowing down the clock at a
+rate which can be limited by the `maxslewrate` directive. Disabling clock steps
+completely is practical only if the clock cannot gain a larger error on its
+own, e.g. when the computer is shut down or suspended, and the `maxslewrate`
+limit is large enough to correct an expected error in an acceptable time. The
+`rtcfile` directive with the `-s` option can be used to compensate for the RTC
+drift.
+
+A more practical approach is to enable `makestep` for a limited number of clock
+updates (the 2nd argument of the directive) and limit the offset change in all
+updates by the `maxchange` directive. The attacker will be able to make only a
+limited step and only if the attack starts in a short window after booting the
+computer, or when `chronyd` is restarted without the `-R` option.
+
+The frequency offset can be limited by the `maxdrift` directive. The measured
+frequency offset is reported in the drift file, `tracking` report, and
+`tracking` log. Set `maxdrift` to a value larger than the maximum absolute
+value that is normally observed. Note that the frequency of the clock can
+change due to aging of the crystal, differences in calibration of the clock
+source between reboots, migrated virtual machine, etc. A typical computer clock
+has a drift smaller than 100 parts per million (ppm), but much larger drifts
+are possible (e.g. in some virtual machines).
+
+Use only trusted servers, which you expect to be well configured and managed,
+using authentication for their own servers, etc. Use multiple servers, ideally
+in different locations. The attacker will have to deal with a majority of the
+servers in order to pass the source selection and update the clock with a large
+offset. Use the `minsources` directive to increase the required number of
+selectable sources to make the selection more robust.
+
+Do not specify servers as peers. The symmetric mode is less secure than the
+client/server mode. If not authenticated, it is vulnerable to off-path
+denial-of-service attacks, and even when it is authenticated, it is still
+susceptible to replay attacks.
+
+Mixing of authenticated and unauthenticated servers should generally be
+avoided. If mixing is necessary (e.g. for a more accurate and stable
+synchronisation to a closer server which does not support authentication), the
+authenticated servers should be configured as trusted and required to not allow
+the unauthenticated servers to override the authenticated servers in the source
+selection. Since `chrony` version 4.0, the selection options are enabled in
+such a case automatically. This behaviour can be disabled or modified by the
+`authselmode` directive.
+
+An example of a client configuration limiting the impact of the attacks could
+be
+
+----
+server foo.example.net iburst nts maxdelay 0.1
+server bar.example.net iburst nts maxdelay 0.2
+server baz.example.net iburst nts maxdelay 0.05
+server qux.example.net iburst nts maxdelay 0.1
+server quux.example.net iburst nts maxdelay 0.1
+minsources 3
+maxchange 100 0 0
+makestep 0.001 1
+maxdrift 100
+maxslewrate 100
+driftfile /var/lib/chrony/drift
+ntsdumpdir /var/lib/chrony
+rtcsync
+----
+
+=== How can I improve the accuracy of the system clock with NTP sources?
+
+Select NTP servers that are well synchronised, stable and close to your
+network. It is better to use more than one server. Three or four is usually
+recommended as the minimum, so `chronyd` can detect servers that serve false
+time and combine measurements from multiple sources.
+
+If you have a network card with hardware timestamping supported on Linux, it
+can be enabled by the `hwtimestamp` directive. It should make local receive and
+transmit timestamps of NTP packets much more stable and accurate.
+
+The `server` directive has some useful options: `minpoll`, `maxpoll`,
+`polltarget`, `maxdelay`, `maxdelayratio`, `maxdelaydevratio`, `xleave`,
+`filter`.
+
+The first three options set the minimum and maximum allowed polling interval,
+and how should be the actual interval adjusted in the specified range. Their
+default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
+`maxpoll` and 8 (samples) for `polltarget`. The default values should be used
+for general servers on the Internet. With your own NTP servers, or if you have
+permission to poll some servers more frequently, setting these options for
+shorter polling intervals might significantly improve the accuracy of the
+system clock.
+
+The optimal polling interval depends mainly on two factors, stability of the
+network latency and stability of the system clock (which mainly depends on the
+temperature sensitivity of the crystal oscillator and the maximum rate of the
+temperature change).
+
+Generally, if the `sourcestats` command usually reports a small number of
+samples retained for a source (e.g. fewer than 16), a shorter polling interval
+should be considered. If the number of samples is usually at the maximum of 64,
+a longer polling interval might work better.
+
+An example of the directive for an NTP server on the Internet that you are
+allowed to poll frequently could be
+
+----
+server foo.example.net minpoll 4 maxpoll 6 polltarget 16
+----
+
+An example using shorter polling intervals with a server located in the same
+LAN could be
+
+----
+server ntp.local minpoll 2 maxpoll 4 polltarget 30
+----
+
+The maxdelay options are useful to ignore measurements with an unusually large
+delay (e.g. due to congestion in the network) and improve the stability of the
+synchronisation. The `maxdelaydevratio` option could be added to the example
+with local NTP server
+
+----
+server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
+----
+
+If your server supports the interleaved mode (e.g. it is running `chronyd`),
+the `xleave` option should be added to the `server` directive to enable the
+server to provide the client with more accurate transmit timestamps (kernel or
+preferably hardware). For example:
+
+----
+server ntp.local minpoll 2 maxpoll 4 xleave
+----
+
+When combined with local hardware timestamping, good network switches, and even
+shorter polling intervals, a sub-microsecond accuracy and stability of a few
+tens of nanoseconds might be possible. For example:
+
+----
+server ntp.local minpoll 0 maxpoll 0 xleave
+hwtimestamp eth0
+----
+
+For best stability, the CPU should be running at a constant frequency (i.e.
+disabled power saving and performance boosting). Energy-Efficient Ethernet
+(EEE) should be disabled in the network. The switches should be configured to
+prioritize NTP packets, especially if the network is expected to be heavily
+loaded. The `dscp` directive can be used to set the Differentiated Services
+Code Point in transmitted NTP packets if needed.
+
+If it is acceptable for NTP clients in the network to send requests at a high
+rate, a sub-second polling interval can be specified. A median filter
+can be enabled in order to update the clock at a reduced rate with more stable
+measurements. For example:
+
+----
+server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
+hwtimestamp eth0 minpoll -6
+----
+
+As an experimental feature added in version 4.2, `chronyd` supports an NTPv4
+extension field containing an additional timestamp to enable frequency transfer
+and significantly improve stability of synchronisation. It can be enabled by
+the `extfield F323` option. For example:
+
+----
+server ntp.local minpoll 0 maxpoll 0 xleave extfield F323
+----
+
+=== Does `chronyd` have an ntpdate mode?
+
+Yes. With the `-q` option `chronyd` will set the system clock once and exit.
+With the `-Q` option it will print the measured offset without setting the
+clock. If you do not want to use a configuration file, NTP servers can be
+specified on the command line. For example:
+
+----
+# chronyd -q 'pool pool.ntp.org iburst'
+----
+
+The command above would normally take about 5 seconds if the servers were
+well synchronised and responding to all requests. If not synchronised or
+responding, it would take about 10 seconds for `chronyd` to give up and exit
+with a non-zero status. A faster configuration is possible. A single server can
+be used instead of four servers, the number of measurements can be reduced with
+the `maxsamples` option to one (supported since `chrony` version 4.0), and a
+timeout can be specified with the `-t` option. The following command would take
+only up to about one second.
+
+----
+# chronyd -q -t 1 'server pool.ntp.org iburst maxsamples 1'
+----
+
+It is not recommended to run `chronyd` with the `-q` option periodically (e.g.
+from a cron job) as a replacement for the daemon mode, because it performs
+significantly worse (e.g. the clock is stepped and its frequency is not
+corrected). If you must run it this way and you are using a public NTP server,
+make sure `chronyd` does not always start around the first second of a minute,
+e.g. by adding a random sleep before the `chronyd` command. Public servers
+typically receive large bursts of requests around the first second as there is
+a large number of NTP clients started from cron with no delay.
+
+=== Can `chronyd` be configured to control the clock like `ntpd`?
+
+It is not possible to perfectly emulate `ntpd`, but there are some options that
+can configure `chronyd` to behave more like `ntpd` if there is a reason to
+prefer that.
+
+In the following example the `minsamples` directive slows down the response to
+changes in the frequency and offset of the clock. The `maxslewrate` and
+`corrtimeratio` directives reduce the maximum frequency error due to an offset
+correction and the `maxdrift` directive reduces the maximum assumed frequency
+error of the clock. The `makestep` directive enables a step threshold and the
+`maxchange` directive enables a panic threshold. The `maxclockerror` directive
+increases the minimum dispersion rate.
+
+----
+minsamples 32
+maxslewrate 500
+corrtimeratio 100
+maxdrift 500
+makestep 0.128 -1
+maxchange 1000 1 1
+maxclockerror 15
+----
+
+Note that increasing `minsamples` might cause the offsets in the `tracking` and
+`sourcestats` reports/logs to be significantly smaller than the actual offsets
+and be unsuitable for monitoring.
+
+=== Can NTP server be separated from NTP client?
+
+Yes, it is possible to run multiple instances of `chronyd` on a computer at the
+same time. One can operate primarily as an NTP client to synchronise the system
+clock and another as a server for other computers. If they use the same
+filesystem, they need to be configured with different pidfiles, Unix domain
+command sockets, and any other file or directory specified in the configuration
+file. If they run in the same network namespace, they need to use different NTP
+and command ports, or bind the ports to different addresses or interfaces.
+
+The server instance should be started with the `-x` option to prevent it from
+adjusting the system clock and interfering with the client instance. It can be
+configured as a client to synchronise its NTP clock to other servers, or the
+client instance running on the same computer. In the latter case, the `copy`
+option (added in `chrony` version 4.1) can be used to assume the reference ID
+and stratum of the client instance, which enables detection of synchronisation
+loops with its own clients.
+
+On Linux, starting with `chrony` version 4.0, it is possible to run multiple
+server instances sharing a port to better utilise multiple cores of the CPU.
+Note that for rate limiting and client/server interleaved mode to work well
+it is necessary that all packets received from the same address are handled by
+the same server instance.
+
+An example configuration of the client instance could be
+
+----
+pool pool.ntp.org iburst
+allow 127.0.0.1
+port 11123
+driftfile /var/lib/chrony/drift
+makestep 1 3
+rtcsync
+----
+
+and configuration of the first server instance could be
+
+----
+server 127.0.0.1 port 11123 minpoll 0 maxpoll 0 copy
+allow
+cmdport 11323
+bindcmdaddress /var/run/chrony/chronyd-server1.sock
+pidfile /var/run/chronyd-server1.pid
+driftfile /var/lib/chrony/drift-server1
+----
+
+=== Should be a leap smear enabled on NTP server?
+
+With the `smoothtime` and `leapsecmode` directives it is possible to enable a
+server leap smear in order to hide leap seconds from clients and force them to
+follow a slow server's adjustment instead.
+
+This feature should be used only in local networks and only when necessary,
+e.g. when the clients cannot be configured to handle the leap seconds as
+needed, or their number is so large that configuring them all would be
+impractical. The clients should use only one leap-smearing server, or multiple
+identically configured leap-smearing servers. Note that some clients can get
+leap seconds from other sources (e.g. with the `leapsectz` directive in
+`chrony`) and they will not work correctly with a leap smearing server.
+
+=== Does `chrony` support PTP?
+
+No, the Precision Time Protocol (PTP) is not supported as a protocol for
+synchronisation of clocks and there are no plans
+to support it. It is a complex protocol, which shares some issues with the
+NTP broadcast mode. One of the main differences between NTP and PTP is that PTP
+was designed to be easily supported in hardware (e.g. network switches and
+routers) in order to make more stable and accurate measurements. PTP relies on
+the hardware support. NTP does not rely on any support in the hardware, but if
+it had the same support as PTP, it could perform equally well.
+
+On Linux, `chrony` supports hardware clocks that some NICs have for PTP. They
+are called PTP hardware clocks (PHC). They can be used as reference clocks
+(specified by the `refclock` directive) and for hardware timestamping of NTP
+packets (enabled by the `hwtimestamp` directive) if the NIC can timestamp other
+packets than PTP, which is usually the case at least for transmitted packets.
+The `ethtool -T` command can be used to verify the timestamping support.
+
+As an experimental feature added in version 4.2, `chrony` can use PTP as a
+transport for NTP messages (NTP over PTP) to enable hardware timestamping on
+hardware which can timestamp PTP packets only. It can be enabled by the
+`ptpport` directive.
+
+=== What happened to the `commandkey` and `generatecommandkey` directives?
+
+They were removed in version 2.2. Authentication is no longer supported in the
+command protocol. Commands that required authentication are now allowed only
+through a Unix domain socket, which is accessible only by the root and _chrony_
+users. If you need to configure `chronyd` remotely or locally without the root
+password, please consider using ssh and/or sudo to run `chronyc` under the root
+or _chrony_ user on the host where `chronyd` is running.
+
+== Computer is not synchronising
+
+This is the most common problem. There are a number of reasons, see the
+following questions.
+
+=== Behind a firewall?
+
+Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
+is zero, it means `chronyd` did not get any valid responses from the NTP server
+you are trying to use. If there is a firewall between you and the server, the
+packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see
+if you are getting any responses from the server.
+
+When `chronyd` is receiving responses from the servers, the output of the
+`sources` command issued few minutes after `chronyd` start might look like
+this:
+
+----
+MS Name/IP address         Stratum Poll Reach LastRx Last sample
+===============================================================================
+^* foo.example.net               2   6   377    34   +484us[ -157us] +/-   30ms
+^- bar.example.net               2   6   377    34    +33ms[  +32ms] +/-   47ms
+^+ baz.example.net               3   6   377    35  -1397us[-2033us] +/-   60ms
+----
+
+=== Are NTP servers specified with the `offline` option?
+
+Check that the ``chronyc``'s `online` and `offline` commands are used
+appropriately (e.g. in the system networking scripts). The `activity` command
+prints the number of sources that are currently online and offline. For
+example:
+
+----
+200 OK
+3 sources online
+0 sources offline
+0 sources doing burst (return to online)
+0 sources doing burst (return to offline)
+0 sources with unknown address
+----
+
+=== Is name resolution working correctly?
+
+NTP servers specified by their hostname (instead of an IP address) have to have
+their names resolved before `chronyd` can send any requests to them. If the
+`activity` command prints a non-zero number of sources with unknown address,
+there is an issue with the resolution. Typically, a DNS server is specified in
+_/etc/resolv.conf_. Make sure it is working correctly.
+
+Since `chrony` version 4.0, you can run `chronyc -N sources -a` command to
+print all sources, even those that do not have a known address yet, with their
+names as they were specified in the configuration. This can be useful to verify
+that the names specified in the configuration are used as expected.
+
+=== Is `chronyd` allowed to step the system clock?
+
+By default, `chronyd` adjusts the clock gradually by slowing it down or
+speeding it up. If the clock is too far from the true time, it will take
+a long time to correct the error. The `System time` value printed by the
+``chronyc``'s `tracking` command is the remaining correction that needs to be
+applied to the system clock.
+
+The `makestep` directive can be used to allow `chronyd` to step the clock. For
+example, if _chrony.conf_ had
+
+----
+makestep 1 3
+----
+
+the clock would be stepped in the first three updates if its offset was larger
+than one second. Normally, it is recommended to allow the step only in the first
+few updates, but in some cases (e.g. a computer without an RTC or virtual
+machine which can be suspended and resumed with an incorrect time) it might be
+necessary to allow the step on any clock update. The example above would change
+to
+
+----
+makestep 1 -1
+----
+
+=== Using NTS?
+
+The Network Time Security (NTS) mechanism uses Transport Layer Security (TLS)
+to establish the keys needed for authentication of NTP packets.
+
+Run the `authdata` command to check whether the key establishment was
+successful:
+
+----
+# chronyc -N authdata
+Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+foo.example.net              NTS     1   15  256  33m    0    0    8  100
+bar.example.net              NTS     1   15  256  33m    0    0    8  100
+baz.example.net              NTS     1   15  256  33m    0    0    8  100
+----
+
+The KeyID, Type, and KLen columns should have non-zero values. If they are
+zero, check the system log for error messages from `chronyd`. One possible
+cause of failure is a firewall blocking the client's connection to the server's
+TCP port 4460.
+
+Another possible cause of failure is a certificate that is failing to verify
+because the client's clock is wrong. This is a chicken-and-egg problem with NTS.
+You might need to manually correct the date, or temporarily disable NTS, in
+order to get NTS working. If your computer has an RTC and it is backed up by a
+good battery, this operation should be needed only once, assuming the RTC will
+be set periodically with the `rtcsync` directive, or compensated with the
+`rtcfile` directive and the `-s` option.
+
+If the computer does not have an RTC or battery, you can use the `-s` option
+without `rtcfile` directive to restore time of the last shutdown or reboot from
+the drift file. The clock will start behind the true time, but if the computer
+was not shut down for too long and the server's certificate was not renewed too
+close to its expiration, it should be sufficient for the time checks to
+succeed.
+
+As a last resort, you can disable the time checks by the `nocerttimecheck`
+directive. This has some important security implications. To reduce the
+security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
+to disable the system's default trusted certificate authorities and trust only
+a minimal set of selected authorities needed to validate the certificates of
+used NTP servers.
+
+=== Using a Windows NTP server?
+
+A common issue with Windows NTP servers is that they report a very large root
+dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
+server for being too inaccurate. The `sources` command might show a valid
+measurement, but the server is not selected for synchronisation. You can check
+the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
+
+The `maxdistance` value needs to be increased in _chrony.conf_ to enable
+synchronisation to such a server. For example:
+
+----
+maxdistance 16.0
+----
+
+=== An unreachable source is selected?
+
+When `chronyd` is configured with multiple time sources, it tries to select the
+most accurate and stable sources for synchronisation of the system clock. They
+are marked with the _*_ or _+_ symbol in the report printed by the `sources`
+command.
+
+When the best source (marked with the _*_ symbol) becomes unreachable (e.g. NTP
+server stops responding), `chronyd` will not immediately switch
+to the second best source in an attempt to minimise the error of the clock. It
+will let the clock run free for as long as its estimated error (in terms of
+root distance) based on previous measurements is smaller than the estimated
+error of the second source, and there is still an interval which contains some
+measurements from both sources.
+
+If the first source was significantly better than the second source, it can
+take many hours before the second source is selected, depending on its polling
+interval. You can force a faster reselection by increasing the clock error rate
+(`maxclockerror` directive), shortening the polling interval (`maxpoll`
+option), or reducing the number of samples (`maxsamples` option).
+
+=== Does selected source drop new measurements?
+
+`chronyd` can drop a large number of successive NTP measurements if they are
+not passing some of the NTP tests. The `sources` command can report for a
+selected source the fully-reachable value of 377 in the Reach column and at the
+same time a LastRx value that is much larger than the current polling interval.
+If the source is online, this indicates that a number of measurements was
+dropped. You can use the `ntpdata` command to check the NTP tests for the last
+measurement. Usually, it is the test C which fails.
+
+This can be an issue when there is a long-lasting increase in the measured
+delay, e.g. due to a routing change in the network. Unfortunately, `chronyd`
+does not know for how long it should wait for the delay to come back to the
+original values, or whether it is a permanent increase and it should start from
+scratch.
+
+The test C is an adaptive filter. It can take many hours before it accepts
+a measurement with the larger delay, and even much longer before it drops all
+measurements with smaller delay, which determine an expected delay used by the
+test. You can use the `reset sources` command to drop all measurements
+immediately (available in chrony 4.0 and later). If this issue happens
+frequently, you can effectively disable the test by setting the
+`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
+recovery by increasing the clock error rate with the `maxclockerror` directive.
+
+=== Using a PPS reference clock?
+
+A pulse-per-second (PPS) reference clock requires a non-PPS time source to
+determine which second of UTC corresponds to each pulse. If it is another
+reference clock specified with the `lock` option in the `refclock` directive,
+the offset between the two reference clocks must be smaller than 0.2 seconds in
+order for the PPS reference clock to work. With NMEA reference clocks it is
+common to have a larger offset. It needs to be corrected with the `offset`
+option.
+
+One approach to find out a good value of the `offset` option is to configure
+the reference clocks with the `noselect` option and compare them to an NTP
+server. For example, if the `sourcestats` command showed
+
+----
+Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
+==============================================================================
+PPS0                        0   0     0     +0.000   2000.000     +0ns  4000ms
+NMEA                       58  30   231    -96.494     38.406   +504ms  6080us
+foo.example.net             7   3   200     -2.991     16.141   -107us   492us
+----
+
+the offset of the NMEA source would need to be increased by about 0.504
+seconds. It does not have to be very accurate. As long as the offset of the
+NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
+able to determine the seconds corresponding to the pulses and allow the samples
+to be used for synchronisation.
+
+== Issues with `chronyc`
+
+=== I keep getting the error `506 Cannot talk to daemon`
+
+When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on
+the computer where `chronyd` is running) has a `cmdallow` entry for the
+computer you are running `chronyc` on and an appropriate `bindcmdaddress`
+directive. This is not necessary for localhost.
+
+Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux,
+`ps -auxw`) to see if it is running. Or try `netstat -a` and see if the UDP
+port 323 is listening. If `chronyd` is not running, you might have a problem
+with the way you are trying to start it (e.g. at boot time).
+
+Perhaps you have a firewall set up in a way that blocks packets on the UDP
+port 323. You need to amend the firewall configuration in this case.
+
+=== I keep getting the error `501 Not authorised`
+
+This error indicates that `chronyc` sent the command to `chronyd` using a UDP
+socket instead of the Unix domain socket (e.g. _/var/run/chrony/chronyd.sock_),
+which is required for some commands. For security reasons, only the root and
+_chrony_ users are allowed to access the socket.
+
+It is also possible that the socket does not exist. `chronyd` will not create
+the socket if the directory has a wrong owner or permissions. In this case
+there should be an error message from `chronyd` in the system log.
+
+=== What is the reference ID reported by the `tracking` command?
+
+The reference ID is a 32-bit value used in NTP to prevent synchronisation
+loops.
+
+In `chrony` versions before 3.0 it was printed in the
+quad-dotted notation, even if the reference source did not actually have an
+IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
+for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
+reference clocks, the reference ID is the value specified with the `refid`
+option in the `refclock` directive.
+
+Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
+confusion with IPv4 addresses.
+
+If you need to get the IP address of the current reference source, use the `-n`
+option to disable resolving of IP addresses and read the second field (printed
+in parentheses) on the `Reference ID` line.
+
+=== Is the `chronyc` / `chronyd` protocol documented anywhere?
+
+Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
+(`chronyc` side).
+
+== Real-time clock issues
+
+=== What is the real-time clock (RTC)?
+
+This is the clock which keeps the time even when your computer is turned off.
+It is used to initialise the system clock on boot. It normally does not drift
+more than few seconds per day.
+
+There are two approaches how `chronyd` can work with it. One is to use the
+`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
+the RTC from the system clock every 11 minutes. `chronyd` itself will not touch
+the RTC. If the computer is not turned off for a long time, the RTC should
+still be close to the true time when the system clock will be initialised from
+it on the next boot.
+
+The other option is to use the `rtcfile` directive, which tells `chronyd` to
+monitor the rate at which the RTC gains or loses time. When `chronyd` is
+started with the `-s` option on the next boot, it will set the system time from
+the RTC and also compensate for the drift it has measured previously. The
+`rtcautotrim` directive can be used to keep the RTC close to the true time, but
+it is not strictly necessary if its only purpose is to set the system clock when
+`chronyd` is started on boot. See the documentation for details.
+
+=== Does `hwclock` have to be disabled?
+
+The `hwclock` program is run by default in the boot and/or shutdown
+scripts in some Linux installations. With the kernel RTC synchronisation
+(`rtcsync` directive), the RTC will be set also every 11 minutes as long as the
+system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring
+(`rtcfile` directive), it is important to disable `hwclock` in the shutdown
+procedure. If you do not do that, it will overwrite the RTC with a new value, unknown
+to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will
+compensate this (wrong) time with its estimate of how far the RTC has drifted
+whilst the power was off, giving a meaningless initial system time.
+
+There is no need to remove `hwclock` from the boot process, as long as `chronyd`
+is started after it has run.
+
+=== I just keep getting the `513 RTC driver not running` message
+
+For the real-time clock support to work, you need the following three
+things
+
+* an RTC in your computer
+* a Linux kernel with enabled RTC support
+* an `rtcfile` directive in your _chrony.conf_ file
+
+=== I get `Could not open /dev/rtc, Device or resource busy` in my syslog file
+
+Some other program running on the system might be using the device.
+
+=== When I start `chronyd`, the log says `Could not enable RTC interrupt : Invalid argument` (or it may say `disable`)
+
+Your real-time clock hardware might not support the required ioctl requests:
+
+* `RTC_UIE_ON`
+* `RTC_UIE_OFF`
+
+A possible solution could be to build the Linux kernel with support for software
+emulation instead; try enabling the following configuration option when building
+the Linux kernel:
+
+* `CONFIG_RTC_INTF_DEV_UIE_EMUL`
+
+=== What if my computer does not have an RTC or backup battery?
+
+In this case you can still use the `-s` option to set the system clock to the
+last modification time of the drift file, which should correspond to the system
+time when `chronyd` was previously stopped. The initial system time will be
+increasing across reboots and applications started after `chronyd` will not
+observe backward steps.
+
+== NTP-specific issues
+
+=== Can `chronyd` be driven from broadcast/multicast NTP servers?
+
+No, the broadcast/multicast client mode is not supported and there is currently
+no plan to implement it. While this mode can simplify configuration
+of clients in large networks, it is inherently less accurate and less secure
+(even with authentication) than the ordinary client/server mode.
+
+When configuring a large number of clients in a network, it is recommended to
+use the `pool` directive with a DNS name which resolves to addresses of
+multiple NTP servers. The clients will automatically replace the servers when
+they become unreachable, or otherwise unsuitable for synchronisation, with new
+servers from the pool.
+
+Even with very modest hardware, an NTP server can serve time to hundreds of
+thousands of clients using the ordinary client/server mode.
+
+=== Can `chronyd` transmit broadcast NTP packets?
+
+Yes, the `broadcast` directive can be used to enable the broadcast server mode
+to serve time to clients in the network which support the broadcast client mode
+(it is not supported in `chronyd`). Note that this mode should generally be
+avoided. See the previous question.
+
+=== Can `chronyd` keep the system clock a fixed offset away from real time?
+
+Yes. Starting from version 3.0, an offset can be specified by the `offset`
+option for all time sources in the _chrony.conf_ file.
+
+=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
+
+`chronyd` will keep trying to access the sources that it thinks are online, and
+it will take longer before new measurements are actually made and the clock is
+corrected when the network is connected again. If the sources were set to
+offline, `chronyd` would make new measurements immediately after issuing the
+`online` command.
+
+Unless the network connection lasts only few minutes (less than the maximum
+polling interval), the delay is usually not a problem, and it might be acceptable
+to keep all sources online all the time.
+
+=== Why is an offset measured between two computers synchronised to each another?
+
+When two computers are synchronised to each other using the client/server or
+symmetric NTP mode, there is an expectation that NTP measurements between the
+two computers made on both ends show an average offset close to zero.
+
+With `chronyd` that can be expected only when the interleaved mode is enabled
+by the `xleave` option. Otherwise, `chronyd` will use different transmit
+timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
+synchronisation of its own clock, which will cause the other computer to
+measure a significant offset.
+
+== Operating systems
+
+=== Does `chrony` support Windows?
+
+No. The `chronyc` program (the command-line client used for configuring
+`chronyd` while it is running) has been successfully built and run under
+Cygwin in the past. `chronyd` is not portable, because part of it is
+very system-dependent. It needs adapting to work with Windows'
+equivalent of the adjtimex() call, and it needs to be made to work as a
+service.
+
+=== Are there any plans to support Windows?
+
+We have no plans to do this. Anyone is welcome to pick this work up and
+contribute it back to the project.
diff --git a/chrony_3_3/doc/installation.adoc b/chrony/doc/installation.adoc
similarity index 78%
rename from chrony_3_3/doc/installation.adoc
rename to chrony/doc/installation.adoc
index eea9088..35ce9bf 100644
--- a/chrony_3_3/doc/installation.adoc
+++ b/chrony/doc/installation.adoc
@@ -22,6 +22,26 @@
 code is supplied in the form of a gzipped tar file, which unpacks to a
 subdirectory identifying the name and version of the program.
 
+A C compiler (e.g. `gcc` or `clang`) and GNU Make are needed to build `chrony`.
+The following libraries with their development files, and programs, are needed
+to enable optional features:
+
+* pkg-config: detection of development libraries
+* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
+* libcap: dropping root privileges on Linux (`DROPROOT`)
+* libseccomp: system call filter on Linux (`SCFILTER`)
+* GnuTLS and Nettle: Network Time Security (`NTS`)
+* Editline: line editing in `chronyc` (`READLINE`)
+* timepps.h header: PPS reference clock
+* Asciidoctor: documentation in HTML format
+* Bash: test suite
+
+The following programs are needed when building `chrony` from the git
+repository instead of a released tar file:
+
+* Asciidoctor: manual pages
+* Bison: parser for chronyc settime command
+
 After unpacking the source code, change directory into it, and type
 
 ----
@@ -74,13 +94,13 @@
 
 If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
 https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
-http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
+https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
 `chronyd` will be built with support for other cryptographic hash functions
 than MD5, which can be used for NTP authentication with a symmetric key. If you
 don't want to enable the support, specify the `--disable-sechash` flag to
 `configure`.
 
-If development files for the editline or readline library are available,
+If development files for the editline library are available,
 `chronyc` will be built with line editing support. If you don't want this,
 specify the `--disable-readline` flag to `configure`.
 
@@ -150,43 +170,6 @@
 the kernel attack surface and possibly prevent kernel exploits from `chronyd`
 if it is compromised.
 
-== Support for line editing libraries
-
-`chronyc` can be built with support for line editing, this allows you to use
-the cursor keys to replay and edit old commands. Two libraries are supported
-which provide such functionality, editline and GNU readline.
-
-Please note that readline since version 6.0 is licensed under GPLv3+ which is
-incompatible with chrony's license GPLv2. You should use editline instead if
-you don't want to use older readline versions.
-
-The `configure` script will automatically enable the line editing support if
-one of the supported libraries is available. If they are both available, the
-editline library will be used.
-
-If you don't want to use it (in which case `chronyc` will use a minimal command
-line interface), invoke `configure` like this:
-
-----
-./configure --disable-readline other-options...
-----
-
-If you have editline, readline or ncurses installed in locations that aren't
-normally searched by the compiler and linker, you need to use extra options:
-
-`--with-readline-includes=directory_name`::
-  This defines the name of the directory above the one where `readline.h` is.
-  `readline.h` is assumed to be in `editline` or `readline` subdirectory of the
-  named directory.
-
-`--with-readline-library=directory_name`::
-  This defines the directory containing the `libedit.a` or `libedit.so` file,
-  or `libreadline.a` or `libreadline.so` file.
-
-`--with-ncurses-library=directory_name`::
-  This defines the directory containing the `libncurses.a` or `libncurses.so`
-  file.
-
 == Extra options for package builders
 
 The `configure` and `make` procedures have some extra options that may be
diff --git a/chrony/examples/chrony-wait.service b/chrony/examples/chrony-wait.service
new file mode 100644
index 0000000..72b028f
--- /dev/null
+++ b/chrony/examples/chrony-wait.service
@@ -0,0 +1,47 @@
+[Unit]
+Description=Wait for chrony to synchronize system clock
+Documentation=man:chronyc(1)
+After=chronyd.service
+Requires=chronyd.service
+Before=time-sync.target
+Wants=time-sync.target
+
+[Service]
+Type=oneshot
+# Wait for chronyd to update the clock and the remaining
+# correction to be less than 0.1 seconds
+ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 0 0.1 0.0 1
+# Wait for at most 3 minutes
+TimeoutStartSec=180
+RemainAfterExit=yes
+StandardOutput=null
+
+CapabilityBoundingSet=
+DevicePolicy=closed
+DynamicUser=yes
+IPAddressAllow=localhost
+IPAddressDeny=any
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+PrivateDevices=yes
+PrivateUsers=yes
+ProcSubset=pid
+ProtectClock=yes
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+ProtectProc=invisible
+ProtectSystem=strict
+RestrictAddressFamilies=AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged @resources
+UMask=0777
+
+[Install]
+WantedBy=multi-user.target
diff --git a/chrony_3_3/examples/chrony.conf.example1 b/chrony/examples/chrony.conf.example1
similarity index 100%
rename from chrony_3_3/examples/chrony.conf.example1
rename to chrony/examples/chrony.conf.example1
diff --git a/chrony_3_3/examples/chrony.conf.example2 b/chrony/examples/chrony.conf.example2
similarity index 76%
rename from chrony_3_3/examples/chrony.conf.example2
rename to chrony/examples/chrony.conf.example2
index 21be153..bf2bbdd 100644
--- a/chrony_3_3/examples/chrony.conf.example2
+++ b/chrony/examples/chrony.conf.example2
@@ -1,5 +1,5 @@
 # Use public servers from the pool.ntp.org project.
-# Please consider joining the pool (http://www.pool.ntp.org/join.html).
+# Please consider joining the pool (https://www.pool.ntp.org/join.html).
 pool pool.ntp.org iburst
 
 # Record the rate at which the system clock gains/losses time.
@@ -25,9 +25,18 @@
 # Serve time even if not synchronized to a time source.
 #local stratum 10
 
+# Require authentication (nts or key option) for all NTP sources.
+#authselectmode require
+
 # Specify file containing keys for NTP authentication.
 #keyfile /etc/chrony.keys
 
+# Save NTS keys and cookies.
+ntsdumpdir /var/lib/chrony
+
+# Insert/delete leap seconds by slewing instead of stepping.
+#leapsecmode slew
+
 # Get TAI-UTC offset and leap seconds from the system tz database.
 #leapsectz right/UTC
 
diff --git a/chrony_3_3/examples/chrony.conf.example3 b/chrony/examples/chrony.conf.example3
similarity index 83%
rename from chrony_3_3/examples/chrony.conf.example3
rename to chrony/examples/chrony.conf.example3
index 05a4e98..4e3e3a8 100644
--- a/chrony_3_3/examples/chrony.conf.example3
+++ b/chrony/examples/chrony.conf.example3
@@ -57,6 +57,20 @@
 
 ! maxdrift 100
 
+# By default, chronyd allows synchronisation to an unauthenticated NTP
+# source (i.e. specified without the nts and key options) if it agrees with
+# a majority of authenticated NTP sources, or if no authenticated source is
+# specified.  If you don't want chronyd to ever synchronise to an
+# unauthenticated NTP source, uncomment the first from the following lines.
+# If you don't want to synchronise to an unauthenticated NTP source only
+# when an authenticated source is specified, uncomment the second line.
+# If you want chronyd to ignore authentication in the source selection,
+# uncomment the third line.
+
+! authselectmode require
+! authselectmode prefer
+! authselectmode ignore
+
 #######################################################################
 ### FILENAMES ETC
 # Chrony likes to keep information about your computer's clock in files.
@@ -72,22 +86,37 @@
 
 ! keyfile /etc/chrony.keys
 
+# If you specify an NTP server with the nts option to enable authentication
+# with the Network Time Security (NTS) mechanism, or enable server NTS with
+# the ntsservercert and ntsserverkey directives below, the following line will
+# allow the client/server to save the NTS keys and cookies in order to reduce
+# the number of key establishments (NTS-KE sessions).
+
+ntsdumpdir /var/lib/chrony
+
+# If chronyd is configured to act as an NTP server and you want to enable NTS
+# for its clients, you will need a TLS certificate and private key.  Uncomment
+# and edit the following lines to specify the locations of the certificate and
+# key.
+
+! ntsservercert /etc/.../foo.example.net.crt
+! ntsserverkey /etc/.../foo.example.net.key
+
 # chronyd can save the measurement history for the servers to files when
-# it it exits.  This is useful in 2 situations:
+# it exits.  This is useful in 2 situations:
 #
-# 1. On Linux, if you stop chronyd and restart it with '-r' (e.g. after
+# 1. If you stop chronyd and restart it with the '-r' option (e.g. after
 # an upgrade), the old measurements will still be relevant when chronyd
 # is restarted.  This will reduce the time needed to get accurate
-# gain/loss measurements, especially with a dial-up link.
+# gain/loss measurements.
 #
-# 2. Again on Linux, if you use the RTC support and start chronyd with
+# 2. On Linux, if you use the RTC support and start chronyd with
 # '-r -s' on bootup, measurements from the last boot will still be
 # useful (the real time clock is used to 'flywheel' chronyd between
 # boots).
 #
-# Enable these two options to use this.
+# Uncomment the following line to use this.
 
-! dumponexit
 ! dumpdir /var/lib/chrony
 
 # chronyd writes its process ID to a file.  If you try to start a second
@@ -95,7 +124,7 @@
 # still running and bail out.  If you want to change the path to the PID
 # file, uncomment this line and edit it.  The default path is shown.
 
-! pidfile /var/run/chronyd.pid
+! pidfile /var/run/chrony/chronyd.pid
 
 # If the system timezone database is kept up to date and includes the
 # right/UTC timezone, chronyd can use it to determine the current
@@ -117,6 +146,18 @@
 ! makestep 1.0 3
 
 #######################################################################
+### LEAP SECONDS
+# A leap second is an occasional one-second correction of the UTC
+# time scale.  By default, chronyd tells the kernel to insert/delete
+# the leap second, which makes a backward/forward step to correct the
+# clock for it.  As with the makestep directive, this jump can upset
+# some applications.  If you prefer chronyd to make a gradual
+# correction, causing the clock to be off for a longer time, uncomment
+# the following line.
+
+! leapsecmode slew
+
+#######################################################################
 ### LOGGING
 # If you want to log information about the time measurements chronyd has
 # gathered, you might want to enable the following lines.  You probably
@@ -135,8 +176,6 @@
 #######################################################################
 ### ACTING AS AN NTP SERVER
 # You might want the computer to be an NTP server for other computers.
-# e.g.  you might be running chronyd on a dial-up machine that has a LAN
-# sitting behind it with several 'satellite' computers on it.
 #
 # By default, chronyd does not allow any clients to access it.  You need
 # to explicitly enable access using 'allow' and 'deny' directives.
@@ -152,15 +191,6 @@
 # You can have as many allow and deny directives as you need.  The order
 # is unimportant.
 
-# If you want chronyd to act as an NTP broadcast server, enable and edit
-# (and maybe copy) the following line.  This means that a broadcast
-# packet is sent to the address 192.168.1.255 every 60 seconds.  The
-# address MUST correspond to the broadcast address of one of the network
-# interfaces on your machine.  If you have multiple network interfaces,
-# add a broadcast line for each.
-
-! broadcast 60 192.168.1.255
-
 # If you want to present your computer's time for others to synchronise
 # with, even if you don't seem to be synchronised to any NTP servers
 # yourself, enable the following line.  The value 10 may be varied
diff --git a/chrony/examples/chrony.keys.example b/chrony/examples/chrony.keys.example
new file mode 100644
index 0000000..65b6be2
--- /dev/null
+++ b/chrony/examples/chrony.keys.example
@@ -0,0 +1,13 @@
+# This is an example chrony keys file.  It enables authentication of NTP
+# packets with symmetric keys when its location is specified by the keyfile
+# directive in chrony.conf(5).  It should be readable only by root and the
+# user under which chronyd is running.
+#
+# Don't use the example keys!  It's recommended to generate random keys using
+# the chronyc keygen command.
+
+# Examples of valid keys:
+
+#1 MD5 AVeryLongAndRandomPassword
+#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
+#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
diff --git a/chrony_3_3/examples/chrony.logrotate b/chrony/examples/chrony.logrotate
similarity index 100%
rename from chrony_3_3/examples/chrony.logrotate
rename to chrony/examples/chrony.logrotate
diff --git a/chrony/examples/chrony.nm-dispatcher.dhcp b/chrony/examples/chrony.nm-dispatcher.dhcp
new file mode 100644
index 0000000..6ea4c37
--- /dev/null
+++ b/chrony/examples/chrony.nm-dispatcher.dhcp
@@ -0,0 +1,43 @@
+#!/bin/sh
+# This is a NetworkManager dispatcher script for chronyd to update
+# its NTP sources passed from DHCP options. Note that this script is
+# specific to NetworkManager-dispatcher due to use of the
+# DHCP4_NTP_SERVERS environment variable.
+
+export LC_ALL=C
+
+interface=$1
+action=$2
+
+chronyc=/usr/bin/chronyc
+default_server_options=iburst
+server_dir=/var/run/chrony-dhcp
+
+dhcp_server_file=$server_dir/$interface.sources
+# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager.
+nm_dhcp_servers=$DHCP4_NTP_SERVERS
+
+add_servers_from_dhcp() {
+    rm -f "$dhcp_server_file"
+    for server in $nm_dhcp_servers; do
+        echo "server $server $default_server_options" >> "$dhcp_server_file"
+    done
+    $chronyc reload sources > /dev/null 2>&1 || :
+}
+
+clear_servers_from_dhcp() {
+    if [ -f "$dhcp_server_file" ]; then
+        rm -f "$dhcp_server_file"
+        $chronyc reload sources > /dev/null 2>&1 || :
+    fi
+}
+
+mkdir -p $server_dir
+
+if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then
+    add_servers_from_dhcp
+elif [ "$action" = "down" ]; then
+    clear_servers_from_dhcp
+fi
+
+exit 0
diff --git a/chrony/examples/chrony.nm-dispatcher.onoffline b/chrony/examples/chrony.nm-dispatcher.onoffline
new file mode 100644
index 0000000..34cfa0d
--- /dev/null
+++ b/chrony/examples/chrony.nm-dispatcher.onoffline
@@ -0,0 +1,17 @@
+#!/bin/sh
+# This is a NetworkManager dispatcher / networkd-dispatcher script for
+# chronyd to set its NTP sources online or offline when a network interface
+# is configured or removed
+
+export LC_ALL=C
+
+chronyc=/usr/bin/chronyc
+
+# For NetworkManager consider only up/down events
+[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
+
+# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
+
+$chronyc onoffline > /dev/null 2>&1
+
+exit 0
diff --git a/chrony/examples/chronyd.service b/chrony/examples/chronyd.service
new file mode 100644
index 0000000..4fb930e
--- /dev/null
+++ b/chrony/examples/chronyd.service
@@ -0,0 +1,49 @@
+[Unit]
+Description=NTP client/server
+Documentation=man:chronyd(8) man:chrony.conf(5)
+After=ntpdate.service sntp.service ntpd.service
+Conflicts=ntpd.service systemd-timesyncd.service
+ConditionCapability=CAP_SYS_TIME
+
+[Service]
+Type=forking
+PIDFile=/run/chrony/chronyd.pid
+EnvironmentFile=-/etc/sysconfig/chronyd
+ExecStart=/usr/sbin/chronyd $OPTIONS
+
+CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
+CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE
+CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_MKNOD CAP_SYS_ADMIN
+CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_PACCT
+CapabilityBoundingSet=~CAP_SYS_PTRACE CAP_SYS_RAWIO CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM
+DeviceAllow=char-pps rw
+DeviceAllow=char-ptp rw
+DeviceAllow=char-rtc rw
+DevicePolicy=closed
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateTmp=yes
+ProcSubset=pid
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+ProtectProc=invisible
+ProtectSystem=strict
+ReadWritePaths=/run /var/lib/chrony -/var/log
+RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
+RestrictNamespaces=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @swap
+
+# Adjust restrictions for /usr/sbin/sendmail (mailonchange directive)
+NoNewPrivileges=no
+ReadWritePaths=-/var/spool
+RestrictAddressFamilies=AF_NETLINK
+
+[Install]
+WantedBy=multi-user.target
diff --git a/chrony_3_3/getdate.c b/chrony/getdate.c
similarity index 70%
rename from chrony_3_3/getdate.c
rename to chrony/getdate.c
index c0c502a..71e8df9 100644
--- a/chrony_3_3/getdate.c
+++ b/chrony/getdate.c
@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.7.6.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
+   Inc.
 
    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
@@ -15,7 +16,7 @@
    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/>.  */
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 /* As a special exception, you may create a larger work that contains
    part or all of the Bison parser skeleton and distribute that work
@@ -33,6 +34,10 @@
 /* C LALR(1) parser skeleton written by Richard Stallman, by
    simplifying the original so-called "semantic" parser.  */
 
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
+
 /* All symbols defined below should begin with yy or YY, to avoid
    infringing on user name space.  This should be done even for local
    variables, as they might otherwise be expanded by user macros.
@@ -40,11 +45,11 @@
    define necessary library symbols; they are noted "INFRINGES ON
    USER NAME SPACE" below.  */
 
-/* Identify Bison output.  */
-#define YYBISON 1
+/* Identify Bison output, and Bison version.  */
+#define YYBISON 30706
 
-/* Bison version.  */
-#define YYBISON_VERSION "3.0.4"
+/* Bison version string.  */
+#define YYBISON_VERSION "3.7.6"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -61,8 +66,8 @@
 
 
 
-/* Copy the first part of user declarations.  */
-#line 1 "getdate.y" /* yacc.c:339  */
+/* First part of user prologue.  */
+#line 1 "getdate.y"
 
 /*
 **  Originally written by Steven M. Bellovin <smb@research.att.com> while
@@ -235,23 +240,28 @@
 static int	yyRelYear;
 
 
-#line 239 "getdate.c" /* yacc.c:339  */
+#line 244 "getdate.c"
 
-# ifndef YY_NULLPTR
-#  if defined __cplusplus && 201103L <= __cplusplus
-#   define YY_NULLPTR nullptr
+# ifndef YY_CAST
+#  ifdef __cplusplus
+#   define YY_CAST(Type, Val) static_cast<Type> (Val)
+#   define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
 #  else
-#   define YY_NULLPTR 0
+#   define YY_CAST(Type, Val) ((Type) (Val))
+#   define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
 #  endif
 # endif
-
-/* Enabling verbose error messages.  */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
+# ifndef YY_NULLPTR
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
+#  else
+#   define YY_NULLPTR ((void*)0)
+#  endif
+# endif
 
 
 /* Debug traces.  */
@@ -262,43 +272,47 @@
 extern int yydebug;
 #endif
 
-/* Token type.  */
+/* Token kinds.  */
 #ifndef YYTOKENTYPE
 # define YYTOKENTYPE
   enum yytokentype
   {
-    tAGO = 258,
-    tDAY = 259,
-    tDAY_UNIT = 260,
-    tDAYZONE = 261,
-    tDST = 262,
-    tHOUR_UNIT = 263,
-    tID = 264,
-    tMERIDIAN = 265,
-    tMINUTE_UNIT = 266,
-    tMONTH = 267,
-    tMONTH_UNIT = 268,
-    tSEC_UNIT = 269,
-    tSNUMBER = 270,
-    tUNUMBER = 271,
-    tYEAR_UNIT = 272,
-    tZONE = 273
+    YYEMPTY = -2,
+    YYEOF = 0,                     /* "end of file"  */
+    YYerror = 256,                 /* error  */
+    YYUNDEF = 257,                 /* "invalid token"  */
+    tAGO = 258,                    /* tAGO  */
+    tDAY = 259,                    /* tDAY  */
+    tDAY_UNIT = 260,               /* tDAY_UNIT  */
+    tDAYZONE = 261,                /* tDAYZONE  */
+    tDST = 262,                    /* tDST  */
+    tHOUR_UNIT = 263,              /* tHOUR_UNIT  */
+    tID = 264,                     /* tID  */
+    tMERIDIAN = 265,               /* tMERIDIAN  */
+    tMINUTE_UNIT = 266,            /* tMINUTE_UNIT  */
+    tMONTH = 267,                  /* tMONTH  */
+    tMONTH_UNIT = 268,             /* tMONTH_UNIT  */
+    tSEC_UNIT = 269,               /* tSEC_UNIT  */
+    tSNUMBER = 270,                /* tSNUMBER  */
+    tUNUMBER = 271,                /* tUNUMBER  */
+    tYEAR_UNIT = 272,              /* tYEAR_UNIT  */
+    tZONE = 273                    /* tZONE  */
   };
+  typedef enum yytokentype yytoken_kind_t;
 #endif
 
 /* Value type.  */
 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-
 union YYSTYPE
 {
-#line 177 "getdate.y" /* yacc.c:355  */
+#line 177 "getdate.y"
 
     int			Number;
     enum _MERIDIAN	Meridian;
 
-#line 300 "getdate.c" /* yacc.c:355  */
-};
+#line 314 "getdate.c"
 
+};
 typedef union YYSTYPE YYSTYPE;
 # define YYSTYPE_IS_TRIVIAL 1
 # define YYSTYPE_IS_DECLARED 1
@@ -310,37 +324,134 @@
 int yyparse (void);
 
 
+/* Symbol kind.  */
+enum yysymbol_kind_t
+{
+  YYSYMBOL_YYEMPTY = -2,
+  YYSYMBOL_YYEOF = 0,                      /* "end of file"  */
+  YYSYMBOL_YYerror = 1,                    /* error  */
+  YYSYMBOL_YYUNDEF = 2,                    /* "invalid token"  */
+  YYSYMBOL_tAGO = 3,                       /* tAGO  */
+  YYSYMBOL_tDAY = 4,                       /* tDAY  */
+  YYSYMBOL_tDAY_UNIT = 5,                  /* tDAY_UNIT  */
+  YYSYMBOL_tDAYZONE = 6,                   /* tDAYZONE  */
+  YYSYMBOL_tDST = 7,                       /* tDST  */
+  YYSYMBOL_tHOUR_UNIT = 8,                 /* tHOUR_UNIT  */
+  YYSYMBOL_tID = 9,                        /* tID  */
+  YYSYMBOL_tMERIDIAN = 10,                 /* tMERIDIAN  */
+  YYSYMBOL_tMINUTE_UNIT = 11,              /* tMINUTE_UNIT  */
+  YYSYMBOL_tMONTH = 12,                    /* tMONTH  */
+  YYSYMBOL_tMONTH_UNIT = 13,               /* tMONTH_UNIT  */
+  YYSYMBOL_tSEC_UNIT = 14,                 /* tSEC_UNIT  */
+  YYSYMBOL_tSNUMBER = 15,                  /* tSNUMBER  */
+  YYSYMBOL_tUNUMBER = 16,                  /* tUNUMBER  */
+  YYSYMBOL_tYEAR_UNIT = 17,                /* tYEAR_UNIT  */
+  YYSYMBOL_tZONE = 18,                     /* tZONE  */
+  YYSYMBOL_19_ = 19,                       /* ':'  */
+  YYSYMBOL_20_ = 20,                       /* ','  */
+  YYSYMBOL_21_ = 21,                       /* '/'  */
+  YYSYMBOL_YYACCEPT = 22,                  /* $accept  */
+  YYSYMBOL_spec = 23,                      /* spec  */
+  YYSYMBOL_item = 24,                      /* item  */
+  YYSYMBOL_time = 25,                      /* time  */
+  YYSYMBOL_zone = 26,                      /* zone  */
+  YYSYMBOL_day = 27,                       /* day  */
+  YYSYMBOL_date = 28,                      /* date  */
+  YYSYMBOL_rel = 29,                       /* rel  */
+  YYSYMBOL_relunit = 30,                   /* relunit  */
+  YYSYMBOL_number = 31,                    /* number  */
+  YYSYMBOL_o_merid = 32                    /* o_merid  */
+};
+typedef enum yysymbol_kind_t yysymbol_kind_t;
 
-/* Copy the second part of user declarations.  */
 
-#line 317 "getdate.c" /* yacc.c:358  */
+
 
 #ifdef short
 # undef short
 #endif
 
-#ifdef YYTYPE_UINT8
-typedef YYTYPE_UINT8 yytype_uint8;
-#else
-typedef unsigned char yytype_uint8;
+/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure
+   <limits.h> and (if available) <stdint.h> are included
+   so that the code can choose integer types of a good width.  */
+
+#ifndef __PTRDIFF_MAX__
+# include <limits.h> /* INFRINGES ON USER NAME SPACE */
+# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stdint.h> /* INFRINGES ON USER NAME SPACE */
+#  define YY_STDINT_H
+# endif
 #endif
 
-#ifdef YYTYPE_INT8
-typedef YYTYPE_INT8 yytype_int8;
+/* Narrow types that promote to a signed type and that can represent a
+   signed or unsigned integer of at least N bits.  In tables they can
+   save space and decrease cache pressure.  Promoting to a signed type
+   helps avoid bugs in integer arithmetic.  */
+
+#ifdef __INT_LEAST8_MAX__
+typedef __INT_LEAST8_TYPE__ yytype_int8;
+#elif defined YY_STDINT_H
+typedef int_least8_t yytype_int8;
 #else
 typedef signed char yytype_int8;
 #endif
 
-#ifdef YYTYPE_UINT16
-typedef YYTYPE_UINT16 yytype_uint16;
+#ifdef __INT_LEAST16_MAX__
+typedef __INT_LEAST16_TYPE__ yytype_int16;
+#elif defined YY_STDINT_H
+typedef int_least16_t yytype_int16;
 #else
-typedef unsigned short int yytype_uint16;
+typedef short yytype_int16;
 #endif
 
-#ifdef YYTYPE_INT16
-typedef YYTYPE_INT16 yytype_int16;
+/* Work around bug in HP-UX 11.23, which defines these macros
+   incorrectly for preprocessor constants.  This workaround can likely
+   be removed in 2023, as HPE has promised support for HP-UX 11.23
+   (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of
+   <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>.  */
+#ifdef __hpux
+# undef UINT_LEAST8_MAX
+# undef UINT_LEAST16_MAX
+# define UINT_LEAST8_MAX 255
+# define UINT_LEAST16_MAX 65535
+#endif
+
+#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST8_TYPE__ yytype_uint8;
+#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST8_MAX <= INT_MAX)
+typedef uint_least8_t yytype_uint8;
+#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX
+typedef unsigned char yytype_uint8;
 #else
-typedef short int yytype_int16;
+typedef short yytype_uint8;
+#endif
+
+#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST16_TYPE__ yytype_uint16;
+#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST16_MAX <= INT_MAX)
+typedef uint_least16_t yytype_uint16;
+#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX
+typedef unsigned short yytype_uint16;
+#else
+typedef int yytype_uint16;
+#endif
+
+#ifndef YYPTRDIFF_T
+# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__
+#  define YYPTRDIFF_T __PTRDIFF_TYPE__
+#  define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__
+# elif defined PTRDIFF_MAX
+#  ifndef ptrdiff_t
+#   include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  endif
+#  define YYPTRDIFF_T ptrdiff_t
+#  define YYPTRDIFF_MAXIMUM PTRDIFF_MAX
+# else
+#  define YYPTRDIFF_T long
+#  define YYPTRDIFF_MAXIMUM LONG_MAX
+# endif
 #endif
 
 #ifndef YYSIZE_T
@@ -348,15 +459,28 @@
 #  define YYSIZE_T __SIZE_TYPE__
 # elif defined size_t
 #  define YYSIZE_T size_t
-# elif ! defined YYSIZE_T
+# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
 #  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
 #  define YYSIZE_T size_t
 # else
-#  define YYSIZE_T unsigned int
+#  define YYSIZE_T unsigned
 # endif
 #endif
 
-#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+#define YYSIZE_MAXIMUM                                  \
+  YY_CAST (YYPTRDIFF_T,                                 \
+           (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1)  \
+            ? YYPTRDIFF_MAXIMUM                         \
+            : YY_CAST (YYSIZE_T, -1)))
+
+#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X))
+
+
+/* Stored state numbers (used for stacks). */
+typedef yytype_int8 yy_state_t;
+
+/* State numbers in computations.  */
+typedef int yy_state_fast_t;
 
 #ifndef YY_
 # if defined YYENABLE_NLS && YYENABLE_NLS
@@ -370,47 +494,37 @@
 # endif
 #endif
 
-#ifndef YY_ATTRIBUTE
-# if (defined __GNUC__                                               \
-      && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)))  \
-     || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
-#  define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
 # else
-#  define YY_ATTRIBUTE(Spec) /* empty */
+#  define YY_ATTRIBUTE_PURE
 # endif
 #endif
 
-#ifndef YY_ATTRIBUTE_PURE
-# define YY_ATTRIBUTE_PURE   YY_ATTRIBUTE ((__pure__))
-#endif
-
 #ifndef YY_ATTRIBUTE_UNUSED
-# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
-#endif
-
-#if !defined _Noreturn \
-     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
-# if defined _MSC_VER && 1200 <= _MSC_VER
-#  define _Noreturn __declspec (noreturn)
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
 # else
-#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+#  define YY_ATTRIBUTE_UNUSED
 # endif
 #endif
 
 /* Suppress unused-variable warnings by "using" E.  */
 #if ! defined lint || defined __GNUC__
-# define YYUSE(E) ((void) (E))
+# define YY_USE(E) ((void) (E))
 #else
-# define YYUSE(E) /* empty */
+# define YY_USE(E) /* empty */
 #endif
 
-#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
 /* Suppress an incorrect diagnostic about yylval being uninitialized.  */
-# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
-    _Pragma ("GCC diagnostic push") \
-    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                            \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")              \
     _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
-# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END      \
     _Pragma ("GCC diagnostic pop")
 #else
 # define YY_INITIAL_VALUE(Value) Value
@@ -423,8 +537,22 @@
 # define YY_INITIAL_VALUE(Value) /* Nothing. */
 #endif
 
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN                          \
+    _Pragma ("GCC diagnostic push")                            \
+    _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END            \
+    _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
 
-#if ! defined yyoverflow || YYERROR_VERBOSE
+
+#define YY_ASSERT(E) ((void) (0 && (E)))
+
+#if !defined yyoverflow
 
 /* The parser invokes alloca or malloc; define the necessary symbols.  */
 
@@ -489,8 +617,7 @@
 #   endif
 #  endif
 # endif
-#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
-
+#endif /* !defined yyoverflow */
 
 #if (! defined yyoverflow \
      && (! defined __cplusplus \
@@ -499,17 +626,17 @@
 /* A type that is properly aligned for any stack member.  */
 union yyalloc
 {
-  yytype_int16 yyss_alloc;
+  yy_state_t yyss_alloc;
   YYSTYPE yyvs_alloc;
 };
 
 /* The size of the maximum gap between one aligned stack and the next.  */
-# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1)
 
 /* The size of an array large to enough to hold all stacks, each with
    N elements.  */
 # define YYSTACK_BYTES(N) \
-     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+     ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \
       + YYSTACK_GAP_MAXIMUM)
 
 # define YYCOPY_NEEDED 1
@@ -522,11 +649,11 @@
 # define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
     do                                                                  \
       {                                                                 \
-        YYSIZE_T yynewbytes;                                            \
+        YYPTRDIFF_T yynewbytes;                                         \
         YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
         Stack = &yyptr->Stack_alloc;                                    \
-        yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
-        yyptr += yynewbytes / sizeof (*yyptr);                          \
+        yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / YYSIZEOF (*yyptr);                        \
       }                                                                 \
     while (0)
 
@@ -538,12 +665,12 @@
 # ifndef YYCOPY
 #  if defined __GNUC__ && 1 < __GNUC__
 #   define YYCOPY(Dst, Src, Count) \
-      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+      __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src)))
 #  else
 #   define YYCOPY(Dst, Src, Count)              \
       do                                        \
         {                                       \
-          YYSIZE_T yyi;                         \
+          YYPTRDIFF_T yyi;                      \
           for (yyi = 0; yyi < (Count); yyi++)   \
             (Dst)[yyi] = (Src)[yyi];            \
         }                                       \
@@ -566,17 +693,20 @@
 /* YYNSTATES -- Number of states.  */
 #define YYNSTATES  61
 
-/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
-   by yylex, with out-of-bounds checking.  */
-#define YYUNDEFTOK  2
+/* YYMAXUTOK -- Last valid token kind.  */
 #define YYMAXUTOK   273
 
-#define YYTRANSLATE(YYX)                                                \
-  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
+#define YYTRANSLATE(YYX)                                \
+  (0 <= (YYX) && (YYX) <= YYMAXUTOK                     \
+   ? YY_CAST (yysymbol_kind_t, yytranslate[YYX])        \
+   : YYSYMBOL_YYUNDEF)
 
 /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
-   as returned by yylex, without out-of-bounds checking.  */
-static const yytype_uint8 yytranslate[] =
+   as returned by yylex.  */
+static const yytype_int8 yytranslate[] =
 {
        0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -610,7 +740,7 @@
 
 #if YYDEBUG
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
-static const yytype_uint16 yyrline[] =
+static const yytype_int16 yyrline[] =
 {
        0,   193,   193,   194,   197,   200,   203,   206,   209,   212,
      215,   221,   227,   236,   242,   254,   257,   261,   266,   270,
@@ -621,38 +751,52 @@
 };
 #endif
 
-#if YYDEBUG || YYERROR_VERBOSE || 0
+/** Accessing symbol of state STATE.  */
+#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State])
+
+#if YYDEBUG || 0
+/* The user-facing name of the symbol whose (internal) number is
+   YYSYMBOL.  No bounds checking.  */
+static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED;
+
 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
    First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
 static const char *const yytname[] =
 {
-  "$end", "error", "$undefined", "tAGO", "tDAY", "tDAY_UNIT", "tDAYZONE",
-  "tDST", "tHOUR_UNIT", "tID", "tMERIDIAN", "tMINUTE_UNIT", "tMONTH",
-  "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tYEAR_UNIT",
-  "tZONE", "':'", "','", "'/'", "$accept", "spec", "item", "time", "zone",
-  "day", "date", "rel", "relunit", "number", "o_merid", YY_NULLPTR
+  "\"end of file\"", "error", "\"invalid token\"", "tAGO", "tDAY",
+  "tDAY_UNIT", "tDAYZONE", "tDST", "tHOUR_UNIT", "tID", "tMERIDIAN",
+  "tMINUTE_UNIT", "tMONTH", "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER",
+  "tUNUMBER", "tYEAR_UNIT", "tZONE", "':'", "','", "'/'", "$accept",
+  "spec", "item", "time", "zone", "day", "date", "rel", "relunit",
+  "number", "o_merid", YY_NULLPTR
 };
+
+static const char *
+yysymbol_name (yysymbol_kind_t yysymbol)
+{
+  return yytname[yysymbol];
+}
 #endif
 
-# ifdef YYPRINT
+#ifdef YYPRINT
 /* YYTOKNUM[NUM] -- (External) token number corresponding to the
    (internal) symbol number NUM (which must be that of a token).  */
-static const yytype_uint16 yytoknum[] =
+static const yytype_int16 yytoknum[] =
 {
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
      265,   266,   267,   268,   269,   270,   271,   272,   273,    58,
       44,    47
 };
-# endif
+#endif
 
-#define YYPACT_NINF -20
+#define YYPACT_NINF (-20)
 
-#define yypact_value_is_default(Yystate) \
-  (!!((Yystate) == (-20)))
+#define yypact_value_is_default(Yyn) \
+  ((Yyn) == YYPACT_NINF)
 
-#define YYTABLE_NINF -1
+#define YYTABLE_NINF (-1)
 
-#define yytable_value_is_error(Yytable_value) \
+#define yytable_value_is_error(Yyn) \
   0
 
   /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
@@ -671,7 +815,7 @@
   /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
      Performed when YYTABLE does not specify something else to do.  Zero
      means the default is an error.  */
-static const yytype_uint8 yydefact[] =
+static const yytype_int8 yydefact[] =
 {
        2,     0,     1,    18,    39,    16,    42,    45,     0,    36,
       48,     0,    49,    33,    15,     3,     4,     5,     7,     6,
@@ -692,14 +836,14 @@
   /* YYDEFGOTO[NTERM-NUM].  */
 static const yytype_int8 yydefgoto[] =
 {
-      -1,     1,    15,    16,    17,    18,    19,    20,    21,    22,
+       0,     1,    15,    16,    17,    18,    19,    20,    21,    22,
       55
 };
 
   /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
      positive, shift that token.  If negative, reduce the rule whose
      number is the opposite.  If YYTABLE_NINF, syntax error.  */
-static const yytype_uint8 yytable[] =
+static const yytype_int8 yytable[] =
 {
        2,    23,    52,    24,     3,     4,     5,    59,     6,    46,
       47,     7,     8,     9,    10,    11,    12,    13,    14,    31,
@@ -709,7 +853,7 @@
       60
 };
 
-static const yytype_uint8 yycheck[] =
+static const yytype_int8 yycheck[] =
 {
        0,    20,    10,    16,     4,     5,     6,    15,     8,    15,
       16,    11,    12,    13,    14,    15,    16,    17,    18,     4,
@@ -721,7 +865,7 @@
 
   /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
      symbol of state STATE-NUM.  */
-static const yytype_uint8 yystos[] =
+static const yytype_int8 yystos[] =
 {
        0,    23,     0,     4,     5,     6,     8,    11,    12,    13,
       14,    15,    16,    17,    18,    24,    25,    26,    27,    28,
@@ -733,7 +877,7 @@
 };
 
   /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
-static const yytype_uint8 yyr1[] =
+static const yytype_int8 yyr1[] =
 {
        0,    22,    23,    23,    24,    24,    24,    24,    24,    24,
       25,    25,    25,    25,    25,    26,    26,    26,    27,    27,
@@ -744,7 +888,7 @@
 };
 
   /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
-static const yytype_uint8 yyr2[] =
+static const yytype_int8 yyr2[] =
 {
        0,     2,     0,     2,     1,     1,     1,     1,     1,     1,
        2,     4,     4,     6,     6,     1,     1,     2,     1,     2,
@@ -755,10 +899,10 @@
 };
 
 
+enum { YYENOMEM = -2 };
+
 #define yyerrok         (yyerrstatus = 0)
 #define yyclearin       (yychar = YYEMPTY)
-#define YYEMPTY         (-2)
-#define YYEOF           0
 
 #define YYACCEPT        goto yyacceptlab
 #define YYABORT         goto yyabortlab
@@ -767,27 +911,26 @@
 
 #define YYRECOVERING()  (!!yyerrstatus)
 
-#define YYBACKUP(Token, Value)                                  \
-do                                                              \
-  if (yychar == YYEMPTY)                                        \
-    {                                                           \
-      yychar = (Token);                                         \
-      yylval = (Value);                                         \
-      YYPOPSTACK (yylen);                                       \
-      yystate = *yyssp;                                         \
-      goto yybackup;                                            \
-    }                                                           \
-  else                                                          \
-    {                                                           \
-      yyerror (YY_("syntax error: cannot back up")); \
-      YYERROR;                                                  \
-    }                                                           \
-while (0)
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == YYEMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
 
-/* Error token number */
-#define YYTERROR        1
-#define YYERRCODE       256
-
+/* Backward compatibility with an undocumented macro.
+   Use YYerror or YYUNDEF. */
+#define YYERRCODE YYUNDEF
 
 
 /* Enable debugging if requested.  */
@@ -805,54 +948,58 @@
 } while (0)
 
 /* This macro is provided for backward compatibility. */
-#ifndef YY_LOCATION_PRINT
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-#endif
+# ifndef YY_LOCATION_PRINT
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
 
 
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                    \
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)                    \
 do {                                                                      \
   if (yydebug)                                                            \
     {                                                                     \
       YYFPRINTF (stderr, "%s ", Title);                                   \
       yy_symbol_print (stderr,                                            \
-                  Type, Value); \
+                  Kind, Value); \
       YYFPRINTF (stderr, "\n");                                           \
     }                                                                     \
 } while (0)
 
 
-/*----------------------------------------.
-| Print this symbol's value on YYOUTPUT.  |
-`----------------------------------------*/
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
 
 static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+yy_symbol_value_print (FILE *yyo,
+                       yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
 {
-  FILE *yyo = yyoutput;
-  YYUSE (yyo);
+  FILE *yyoutput = yyo;
+  YY_USE (yyoutput);
   if (!yyvaluep)
     return;
 # ifdef YYPRINT
-  if (yytype < YYNTOKENS)
-    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+  if (yykind < YYNTOKENS)
+    YYPRINT (yyo, yytoknum[yykind], *yyvaluep);
 # endif
-  YYUSE (yytype);
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YY_USE (yykind);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
 }
 
 
-/*--------------------------------.
-| Print this symbol on YYOUTPUT.  |
-`--------------------------------*/
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
 
 static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+yy_symbol_print (FILE *yyo,
+                 yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
 {
-  YYFPRINTF (yyoutput, "%s %s (",
-             yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+  YYFPRINTF (yyo, "%s %s (",
+             yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind));
 
-  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
-  YYFPRINTF (yyoutput, ")");
+  yy_symbol_value_print (yyo, yykind, yyvaluep);
+  YYFPRINTF (yyo, ")");
 }
 
 /*------------------------------------------------------------------.
@@ -861,7 +1008,7 @@
 `------------------------------------------------------------------*/
 
 static void
-yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop)
 {
   YYFPRINTF (stderr, "Stack now");
   for (; yybottom <= yytop; yybottom++)
@@ -884,21 +1031,21 @@
 `------------------------------------------------*/
 
 static void
-yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule)
+yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp,
+                 int yyrule)
 {
-  unsigned long int yylno = yyrline[yyrule];
+  int yylno = yyrline[yyrule];
   int yynrhs = yyr2[yyrule];
   int yyi;
-  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n",
              yyrule - 1, yylno);
   /* The symbols being reduced.  */
   for (yyi = 0; yyi < yynrhs; yyi++)
     {
       YYFPRINTF (stderr, "   $%d = ", yyi + 1);
       yy_symbol_print (stderr,
-                       yystos[yyssp[yyi + 1 - yynrhs]],
-                       &(yyvsp[(yyi + 1) - (yynrhs)])
-                                              );
+                       YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]),
+                       &yyvsp[(yyi + 1) - (yynrhs)]);
       YYFPRINTF (stderr, "\n");
     }
 }
@@ -913,8 +1060,8 @@
    multiple parsers can coexist.  */
 int yydebug;
 #else /* !YYDEBUG */
-# define YYDPRINTF(Args)
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YYDPRINTF(Args) ((void) 0)
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)
 # define YY_STACK_PRINT(Bottom, Top)
 # define YY_REDUCE_PRINT(Rule)
 #endif /* !YYDEBUG */
@@ -937,249 +1084,30 @@
 #endif
 
 
-#if YYERROR_VERBOSE
 
-# ifndef yystrlen
-#  if defined __GLIBC__ && defined _STRING_H
-#   define yystrlen strlen
-#  else
-/* Return the length of YYSTR.  */
-static YYSIZE_T
-yystrlen (const char *yystr)
-{
-  YYSIZE_T yylen;
-  for (yylen = 0; yystr[yylen]; yylen++)
-    continue;
-  return yylen;
-}
-#  endif
-# endif
 
-# ifndef yystpcpy
-#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
-#   define yystpcpy stpcpy
-#  else
-/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
-   YYDEST.  */
-static char *
-yystpcpy (char *yydest, const char *yysrc)
-{
-  char *yyd = yydest;
-  const char *yys = yysrc;
 
-  while ((*yyd++ = *yys++) != '\0')
-    continue;
-
-  return yyd - 1;
-}
-#  endif
-# endif
-
-# ifndef yytnamerr
-/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
-   quotes and backslashes, so that it's suitable for yyerror.  The
-   heuristic is that double-quoting is unnecessary unless the string
-   contains an apostrophe, a comma, or backslash (other than
-   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
-   null, do not copy; instead, return the length of what the result
-   would have been.  */
-static YYSIZE_T
-yytnamerr (char *yyres, const char *yystr)
-{
-  if (*yystr == '"')
-    {
-      YYSIZE_T yyn = 0;
-      char const *yyp = yystr;
-
-      for (;;)
-        switch (*++yyp)
-          {
-          case '\'':
-          case ',':
-            goto do_not_strip_quotes;
-
-          case '\\':
-            if (*++yyp != '\\')
-              goto do_not_strip_quotes;
-            /* Fall through.  */
-          default:
-            if (yyres)
-              yyres[yyn] = *yyp;
-            yyn++;
-            break;
-
-          case '"':
-            if (yyres)
-              yyres[yyn] = '\0';
-            return yyn;
-          }
-    do_not_strip_quotes: ;
-    }
-
-  if (! yyres)
-    return yystrlen (yystr);
-
-  return yystpcpy (yyres, yystr) - yyres;
-}
-# endif
-
-/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
-   about the unexpected token YYTOKEN for the state stack whose top is
-   YYSSP.
-
-   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
-   not large enough to hold the message.  In that case, also set
-   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
-   required number of bytes is too large to store.  */
-static int
-yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
-                yytype_int16 *yyssp, int yytoken)
-{
-  YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
-  YYSIZE_T yysize = yysize0;
-  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
-  /* Internationalized format string. */
-  const char *yyformat = YY_NULLPTR;
-  /* Arguments of yyformat. */
-  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
-  /* Number of reported tokens (one for the "unexpected", one per
-     "expected"). */
-  int yycount = 0;
-
-  /* There are many possibilities here to consider:
-     - If this state is a consistent state with a default action, then
-       the only way this function was invoked is if the default action
-       is an error action.  In that case, don't check for expected
-       tokens because there are none.
-     - The only way there can be no lookahead present (in yychar) is if
-       this state is a consistent state with a default action.  Thus,
-       detecting the absence of a lookahead is sufficient to determine
-       that there is no unexpected or expected token to report.  In that
-       case, just report a simple "syntax error".
-     - Don't assume there isn't a lookahead just because this state is a
-       consistent state with a default action.  There might have been a
-       previous inconsistent state, consistent state with a non-default
-       action, or user semantic action that manipulated yychar.
-     - Of course, the expected token list depends on states to have
-       correct lookahead information, and it depends on the parser not
-       to perform extra reductions after fetching a lookahead from the
-       scanner and before detecting a syntax error.  Thus, state merging
-       (from LALR or IELR) and default reductions corrupt the expected
-       token list.  However, the list is correct for canonical LR with
-       one exception: it will still contain any token that will not be
-       accepted due to an error action in a later state.
-  */
-  if (yytoken != YYEMPTY)
-    {
-      int yyn = yypact[*yyssp];
-      yyarg[yycount++] = yytname[yytoken];
-      if (!yypact_value_is_default (yyn))
-        {
-          /* Start YYX at -YYN if negative to avoid negative indexes in
-             YYCHECK.  In other words, skip the first -YYN actions for
-             this state because they are default actions.  */
-          int yyxbegin = yyn < 0 ? -yyn : 0;
-          /* Stay within bounds of both yycheck and yytname.  */
-          int yychecklim = YYLAST - yyn + 1;
-          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
-          int yyx;
-
-          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
-            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
-                && !yytable_value_is_error (yytable[yyx + yyn]))
-              {
-                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
-                  {
-                    yycount = 1;
-                    yysize = yysize0;
-                    break;
-                  }
-                yyarg[yycount++] = yytname[yyx];
-                {
-                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
-                  if (! (yysize <= yysize1
-                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
-                    return 2;
-                  yysize = yysize1;
-                }
-              }
-        }
-    }
-
-  switch (yycount)
-    {
-# define YYCASE_(N, S)                      \
-      case N:                               \
-        yyformat = S;                       \
-      break
-      YYCASE_(0, YY_("syntax error"));
-      YYCASE_(1, YY_("syntax error, unexpected %s"));
-      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
-      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
-      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
-      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
-# undef YYCASE_
-    }
-
-  {
-    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
-    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
-      return 2;
-    yysize = yysize1;
-  }
-
-  if (*yymsg_alloc < yysize)
-    {
-      *yymsg_alloc = 2 * yysize;
-      if (! (yysize <= *yymsg_alloc
-             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
-        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
-      return 1;
-    }
-
-  /* Avoid sprintf, as that infringes on the user's name space.
-     Don't have undefined behavior even if the translation
-     produced a string with the wrong number of "%s"s.  */
-  {
-    char *yyp = *yymsg;
-    int yyi = 0;
-    while ((*yyp = *yyformat) != '\0')
-      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
-        {
-          yyp += yytnamerr (yyp, yyarg[yyi++]);
-          yyformat += 2;
-        }
-      else
-        {
-          yyp++;
-          yyformat++;
-        }
-  }
-  return 0;
-}
-#endif /* YYERROR_VERBOSE */
 
 /*-----------------------------------------------.
 | Release the memory associated to this symbol.  |
 `-----------------------------------------------*/
 
 static void
-yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+yydestruct (const char *yymsg,
+            yysymbol_kind_t yykind, YYSTYPE *yyvaluep)
 {
-  YYUSE (yyvaluep);
+  YY_USE (yyvaluep);
   if (!yymsg)
     yymsg = "Deleting";
-  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+  YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
 
   YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
-  YYUSE (yytype);
+  YY_USE (yykind);
   YY_IGNORE_MAYBE_UNINITIALIZED_END
 }
 
 
-
-
-/* The lookahead symbol.  */
+/* Lookahead token kind.  */
 int yychar;
 
 /* The semantic value of the lookahead symbol.  */
@@ -1188,6 +1116,8 @@
 int yynerrs;
 
 
+
+
 /*----------.
 | yyparse.  |
 `----------*/
@@ -1195,43 +1125,36 @@
 int
 yyparse (void)
 {
-    int yystate;
+    yy_state_fast_t yystate = 0;
     /* Number of tokens to shift before error messages enabled.  */
-    int yyerrstatus;
+    int yyerrstatus = 0;
 
-    /* The stacks and their tools:
-       'yyss': related to states.
-       'yyvs': related to semantic values.
-
-       Refer to the stacks through separate pointers, to allow yyoverflow
+    /* Refer to the stacks through separate pointers, to allow yyoverflow
        to reallocate them elsewhere.  */
 
-    /* The state stack.  */
-    yytype_int16 yyssa[YYINITDEPTH];
-    yytype_int16 *yyss;
-    yytype_int16 *yyssp;
+    /* Their size.  */
+    YYPTRDIFF_T yystacksize = YYINITDEPTH;
 
-    /* The semantic value stack.  */
+    /* The state stack: array, bottom, top.  */
+    yy_state_t yyssa[YYINITDEPTH];
+    yy_state_t *yyss = yyssa;
+    yy_state_t *yyssp = yyss;
+
+    /* The semantic value stack: array, bottom, top.  */
     YYSTYPE yyvsa[YYINITDEPTH];
-    YYSTYPE *yyvs;
-    YYSTYPE *yyvsp;
-
-    YYSIZE_T yystacksize;
+    YYSTYPE *yyvs = yyvsa;
+    YYSTYPE *yyvsp = yyvs;
 
   int yyn;
+  /* The return value of yyparse.  */
   int yyresult;
-  /* Lookahead token as an internal (translated) token number.  */
-  int yytoken = 0;
+  /* Lookahead symbol kind.  */
+  yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY;
   /* The variables used to return semantic value and location from the
      action routines.  */
   YYSTYPE yyval;
 
-#if YYERROR_VERBOSE
-  /* Buffer for error messages, and its allocated size.  */
-  char yymsgbuf[128];
-  char *yymsg = yymsgbuf;
-  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
+
 
 #define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
 
@@ -1239,58 +1162,60 @@
      Keep to zero when no symbol should be popped.  */
   int yylen = 0;
 
-  yyssp = yyss = yyssa;
-  yyvsp = yyvs = yyvsa;
-  yystacksize = YYINITDEPTH;
-
   YYDPRINTF ((stderr, "Starting parse\n"));
 
-  yystate = 0;
-  yyerrstatus = 0;
-  yynerrs = 0;
   yychar = YYEMPTY; /* Cause a token to be read.  */
   goto yysetstate;
 
+
 /*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate.  |
+| yynewstate -- push a new state, which is found in yystate.  |
 `------------------------------------------------------------*/
- yynewstate:
+yynewstate:
   /* In all cases, when you get here, the value and location stacks
      have just been pushed.  So pushing a state here evens the stacks.  */
   yyssp++;
 
- yysetstate:
-  *yyssp = yystate;
+
+/*--------------------------------------------------------------------.
+| yysetstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+  YY_ASSERT (0 <= yystate && yystate < YYNSTATES);
+  YY_IGNORE_USELESS_CAST_BEGIN
+  *yyssp = YY_CAST (yy_state_t, yystate);
+  YY_IGNORE_USELESS_CAST_END
+  YY_STACK_PRINT (yyss, yyssp);
 
   if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    goto yyexhaustedlab;
+#else
     {
       /* Get the current used size of the three stacks, in elements.  */
-      YYSIZE_T yysize = yyssp - yyss + 1;
+      YYPTRDIFF_T yysize = yyssp - yyss + 1;
 
-#ifdef yyoverflow
+# if defined yyoverflow
       {
         /* Give user a chance to reallocate the stack.  Use copies of
            these so that the &'s don't force the real ones into
            memory.  */
+        yy_state_t *yyss1 = yyss;
         YYSTYPE *yyvs1 = yyvs;
-        yytype_int16 *yyss1 = yyss;
 
         /* Each stack pointer address is followed by the size of the
            data in use in that stack, in bytes.  This used to be a
            conditional around just the two extra args, but that might
            be undefined if yyoverflow is a macro.  */
         yyoverflow (YY_("memory exhausted"),
-                    &yyss1, yysize * sizeof (*yyssp),
-                    &yyvs1, yysize * sizeof (*yyvsp),
+                    &yyss1, yysize * YYSIZEOF (*yyssp),
+                    &yyvs1, yysize * YYSIZEOF (*yyvsp),
                     &yystacksize);
-
         yyss = yyss1;
         yyvs = yyvs1;
       }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
-      goto yyexhaustedlab;
-# else
+# else /* defined YYSTACK_RELOCATE */
       /* Extend the stack our own way.  */
       if (YYMAXDEPTH <= yystacksize)
         goto yyexhaustedlab;
@@ -1299,9 +1224,10 @@
         yystacksize = YYMAXDEPTH;
 
       {
-        yytype_int16 *yyss1 = yyss;
+        yy_state_t *yyss1 = yyss;
         union yyalloc *yyptr =
-          (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+          YY_CAST (union yyalloc *,
+                   YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
         if (! yyptr)
           goto yyexhaustedlab;
         YYSTACK_RELOCATE (yyss_alloc, yyss);
@@ -1311,30 +1237,30 @@
           YYSTACK_FREE (yyss1);
       }
 # endif
-#endif /* no yyoverflow */
 
       yyssp = yyss + yysize - 1;
       yyvsp = yyvs + yysize - 1;
 
-      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
-                  (unsigned long int) yystacksize));
+      YY_IGNORE_USELESS_CAST_BEGIN
+      YYDPRINTF ((stderr, "Stack size increased to %ld\n",
+                  YY_CAST (long, yystacksize)));
+      YY_IGNORE_USELESS_CAST_END
 
       if (yyss + yystacksize - 1 <= yyssp)
         YYABORT;
     }
-
-  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
 
   if (yystate == YYFINAL)
     YYACCEPT;
 
   goto yybackup;
 
+
 /*-----------.
 | yybackup.  |
 `-----------*/
 yybackup:
-
   /* Do appropriate processing given the current state.  Read a
      lookahead token if we need one and don't already have one.  */
 
@@ -1345,18 +1271,29 @@
 
   /* Not known => get a lookahead token if don't already have one.  */
 
-  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  /* YYCHAR is either empty, or end-of-input, or a valid lookahead.  */
   if (yychar == YYEMPTY)
     {
-      YYDPRINTF ((stderr, "Reading a token: "));
+      YYDPRINTF ((stderr, "Reading a token\n"));
       yychar = yylex ();
     }
 
   if (yychar <= YYEOF)
     {
-      yychar = yytoken = YYEOF;
+      yychar = YYEOF;
+      yytoken = YYSYMBOL_YYEOF;
       YYDPRINTF ((stderr, "Now at end of input.\n"));
     }
+  else if (yychar == YYerror)
+    {
+      /* The scanner already issued an error message, process directly
+         to error recovery.  But do not keep the error token as
+         lookahead, it is too special and may lead us to an endless
+         loop in error recovery. */
+      yychar = YYUNDEF;
+      yytoken = YYSYMBOL_YYerror;
+      goto yyerrlab1;
+    }
   else
     {
       yytoken = YYTRANSLATE (yychar);
@@ -1384,15 +1321,13 @@
 
   /* Shift the lookahead token.  */
   YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
-
-  /* Discard the shifted token.  */
-  yychar = YYEMPTY;
-
   yystate = yyn;
   YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
   *++yyvsp = yylval;
   YY_IGNORE_MAYBE_UNINITIALIZED_END
 
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
   goto yynewstate;
 
 
@@ -1407,7 +1342,7 @@
 
 
 /*-----------------------------.
-| yyreduce -- Do a reduction.  |
+| yyreduce -- do a reduction.  |
 `-----------------------------*/
 yyreduce:
   /* yyn is the number of a rule to reduce with.  */
@@ -1427,71 +1362,71 @@
   YY_REDUCE_PRINT (yyn);
   switch (yyn)
     {
-        case 4:
-#line 197 "getdate.y" /* yacc.c:1646  */
-    {
+  case 4: /* item: time  */
+#line 197 "getdate.y"
+               {
 	    yyHaveTime++;
 	}
-#line 1436 "getdate.c" /* yacc.c:1646  */
+#line 1371 "getdate.c"
     break;
 
-  case 5:
-#line 200 "getdate.y" /* yacc.c:1646  */
-    {
+  case 5: /* item: zone  */
+#line 200 "getdate.y"
+               {
 	    yyHaveZone++;
 	}
-#line 1444 "getdate.c" /* yacc.c:1646  */
+#line 1379 "getdate.c"
     break;
 
-  case 6:
-#line 203 "getdate.y" /* yacc.c:1646  */
-    {
+  case 6: /* item: date  */
+#line 203 "getdate.y"
+               {
 	    yyHaveDate++;
 	}
-#line 1452 "getdate.c" /* yacc.c:1646  */
+#line 1387 "getdate.c"
     break;
 
-  case 7:
-#line 206 "getdate.y" /* yacc.c:1646  */
-    {
+  case 7: /* item: day  */
+#line 206 "getdate.y"
+              {
 	    yyHaveDay++;
 	}
-#line 1460 "getdate.c" /* yacc.c:1646  */
+#line 1395 "getdate.c"
     break;
 
-  case 8:
-#line 209 "getdate.y" /* yacc.c:1646  */
-    {
+  case 8: /* item: rel  */
+#line 209 "getdate.y"
+              {
 	    yyHaveRel++;
 	}
-#line 1468 "getdate.c" /* yacc.c:1646  */
+#line 1403 "getdate.c"
     break;
 
-  case 10:
-#line 215 "getdate.y" /* yacc.c:1646  */
-    {
+  case 10: /* time: tUNUMBER tMERIDIAN  */
+#line 215 "getdate.y"
+                             {
 	    yyHour = (yyvsp[-1].Number);
 	    yyMinutes = 0;
 	    yySeconds = 0;
 	    yyMeridian = (yyvsp[0].Meridian);
 	}
-#line 1479 "getdate.c" /* yacc.c:1646  */
+#line 1414 "getdate.c"
     break;
 
-  case 11:
-#line 221 "getdate.y" /* yacc.c:1646  */
-    {
+  case 11: /* time: tUNUMBER ':' tUNUMBER o_merid  */
+#line 221 "getdate.y"
+                                        {
 	    yyHour = (yyvsp[-3].Number);
 	    yyMinutes = (yyvsp[-1].Number);
 	    yySeconds = 0;
 	    yyMeridian = (yyvsp[0].Meridian);
 	}
-#line 1490 "getdate.c" /* yacc.c:1646  */
+#line 1425 "getdate.c"
     break;
 
-  case 12:
-#line 227 "getdate.y" /* yacc.c:1646  */
-    {
+  case 12: /* time: tUNUMBER ':' tUNUMBER tSNUMBER  */
+#line 227 "getdate.y"
+                                         {
 	    yyHour = (yyvsp[-3].Number);
 	    yyMinutes = (yyvsp[-1].Number);
 	    yyMeridian = MER24;
@@ -1500,23 +1435,23 @@
 			  ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60
 			  : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60));
 	}
-#line 1504 "getdate.c" /* yacc.c:1646  */
+#line 1439 "getdate.c"
     break;
 
-  case 13:
-#line 236 "getdate.y" /* yacc.c:1646  */
-    {
+  case 13: /* time: tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid  */
+#line 236 "getdate.y"
+                                                     {
 	    yyHour = (yyvsp[-5].Number);
 	    yyMinutes = (yyvsp[-3].Number);
 	    yySeconds = (yyvsp[-1].Number);
 	    yyMeridian = (yyvsp[0].Meridian);
 	}
-#line 1515 "getdate.c" /* yacc.c:1646  */
+#line 1450 "getdate.c"
     break;
 
-  case 14:
-#line 242 "getdate.y" /* yacc.c:1646  */
-    {
+  case 14: /* time: tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER  */
+#line 242 "getdate.y"
+                                                      {
 	    yyHour = (yyvsp[-5].Number);
 	    yyMinutes = (yyvsp[-3].Number);
 	    yySeconds = (yyvsp[-1].Number);
@@ -1526,72 +1461,72 @@
 			  ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60
 			  : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60));
 	}
-#line 1530 "getdate.c" /* yacc.c:1646  */
+#line 1465 "getdate.c"
     break;
 
-  case 15:
-#line 254 "getdate.y" /* yacc.c:1646  */
-    {
+  case 15: /* zone: tZONE  */
+#line 254 "getdate.y"
+                {
 	    yyTimezone = (yyvsp[0].Number);
 	}
-#line 1538 "getdate.c" /* yacc.c:1646  */
+#line 1473 "getdate.c"
     break;
 
-  case 16:
-#line 257 "getdate.y" /* yacc.c:1646  */
-    {
+  case 16: /* zone: tDAYZONE  */
+#line 257 "getdate.y"
+                   {
 	    yyTimezone = (yyvsp[0].Number) - 60;
 	}
-#line 1546 "getdate.c" /* yacc.c:1646  */
+#line 1481 "getdate.c"
     break;
 
-  case 17:
-#line 261 "getdate.y" /* yacc.c:1646  */
-    {
+  case 17: /* zone: tZONE tDST  */
+#line 261 "getdate.y"
+                     {
 	    yyTimezone = (yyvsp[-1].Number) - 60;
 	}
-#line 1554 "getdate.c" /* yacc.c:1646  */
+#line 1489 "getdate.c"
     break;
 
-  case 18:
-#line 266 "getdate.y" /* yacc.c:1646  */
-    {
+  case 18: /* day: tDAY  */
+#line 266 "getdate.y"
+               {
 	    yyDayOrdinal = 1;
 	    yyDayNumber = (yyvsp[0].Number);
 	}
-#line 1563 "getdate.c" /* yacc.c:1646  */
+#line 1498 "getdate.c"
     break;
 
-  case 19:
-#line 270 "getdate.y" /* yacc.c:1646  */
-    {
+  case 19: /* day: tDAY ','  */
+#line 270 "getdate.y"
+                   {
 	    yyDayOrdinal = 1;
 	    yyDayNumber = (yyvsp[-1].Number);
 	}
-#line 1572 "getdate.c" /* yacc.c:1646  */
+#line 1507 "getdate.c"
     break;
 
-  case 20:
-#line 274 "getdate.y" /* yacc.c:1646  */
-    {
+  case 20: /* day: tUNUMBER tDAY  */
+#line 274 "getdate.y"
+                        {
 	    yyDayOrdinal = (yyvsp[-1].Number);
 	    yyDayNumber = (yyvsp[0].Number);
 	}
-#line 1581 "getdate.c" /* yacc.c:1646  */
+#line 1516 "getdate.c"
     break;
 
-  case 21:
-#line 280 "getdate.y" /* yacc.c:1646  */
-    {
+  case 21: /* date: tUNUMBER '/' tUNUMBER  */
+#line 280 "getdate.y"
+                                {
 	    yyMonth = (yyvsp[-2].Number);
 	    yyDay = (yyvsp[0].Number);
 	}
-#line 1590 "getdate.c" /* yacc.c:1646  */
+#line 1525 "getdate.c"
     break;
 
-  case 22:
-#line 284 "getdate.y" /* yacc.c:1646  */
-    {
+  case 22: /* date: tUNUMBER '/' tUNUMBER '/' tUNUMBER  */
+#line 284 "getdate.y"
+                                             {
 	  /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
 	     The goal in recognizing YYYY/MM/DD is solely to support legacy
 	     machine-generated dates like those in an RCS log listing.  If
@@ -1609,72 +1544,72 @@
 	      yyYear = (yyvsp[0].Number);
 	    }
 	}
-#line 1613 "getdate.c" /* yacc.c:1646  */
+#line 1548 "getdate.c"
     break;
 
-  case 23:
-#line 302 "getdate.y" /* yacc.c:1646  */
-    {
+  case 23: /* date: tUNUMBER tSNUMBER tSNUMBER  */
+#line 302 "getdate.y"
+                                     {
 	    /* ISO 8601 format.  yyyy-mm-dd.  */
 	    yyYear = (yyvsp[-2].Number);
 	    yyMonth = -(yyvsp[-1].Number);
 	    yyDay = -(yyvsp[0].Number);
 	}
-#line 1624 "getdate.c" /* yacc.c:1646  */
+#line 1559 "getdate.c"
     break;
 
-  case 24:
-#line 308 "getdate.y" /* yacc.c:1646  */
-    {
+  case 24: /* date: tUNUMBER tMONTH tSNUMBER  */
+#line 308 "getdate.y"
+                                   {
 	    /* e.g. 17-JUN-1992.  */
 	    yyDay = (yyvsp[-2].Number);
 	    yyMonth = (yyvsp[-1].Number);
 	    yyYear = -(yyvsp[0].Number);
 	}
-#line 1635 "getdate.c" /* yacc.c:1646  */
+#line 1570 "getdate.c"
     break;
 
-  case 25:
-#line 314 "getdate.y" /* yacc.c:1646  */
-    {
+  case 25: /* date: tMONTH tUNUMBER  */
+#line 314 "getdate.y"
+                          {
 	    yyMonth = (yyvsp[-1].Number);
 	    yyDay = (yyvsp[0].Number);
 	}
-#line 1644 "getdate.c" /* yacc.c:1646  */
+#line 1579 "getdate.c"
     break;
 
-  case 26:
-#line 318 "getdate.y" /* yacc.c:1646  */
-    {
+  case 26: /* date: tMONTH tUNUMBER ',' tUNUMBER  */
+#line 318 "getdate.y"
+                                       {
 	    yyMonth = (yyvsp[-3].Number);
 	    yyDay = (yyvsp[-2].Number);
 	    yyYear = (yyvsp[0].Number);
 	}
-#line 1654 "getdate.c" /* yacc.c:1646  */
+#line 1589 "getdate.c"
     break;
 
-  case 27:
-#line 323 "getdate.y" /* yacc.c:1646  */
-    {
+  case 27: /* date: tUNUMBER tMONTH  */
+#line 323 "getdate.y"
+                          {
 	    yyMonth = (yyvsp[0].Number);
 	    yyDay = (yyvsp[-1].Number);
 	}
-#line 1663 "getdate.c" /* yacc.c:1646  */
+#line 1598 "getdate.c"
     break;
 
-  case 28:
-#line 327 "getdate.y" /* yacc.c:1646  */
-    {
+  case 28: /* date: tUNUMBER tMONTH tUNUMBER  */
+#line 327 "getdate.y"
+                                   {
 	    yyMonth = (yyvsp[-1].Number);
 	    yyDay = (yyvsp[-2].Number);
 	    yyYear = (yyvsp[0].Number);
 	}
-#line 1673 "getdate.c" /* yacc.c:1646  */
+#line 1608 "getdate.c"
     break;
 
-  case 29:
-#line 334 "getdate.y" /* yacc.c:1646  */
-    {
+  case 29: /* rel: relunit tAGO  */
+#line 334 "getdate.y"
+                       {
 	    yyRelSeconds = -yyRelSeconds;
 	    yyRelMinutes = -yyRelMinutes;
 	    yyRelHour = -yyRelHour;
@@ -1682,156 +1617,156 @@
 	    yyRelMonth = -yyRelMonth;
 	    yyRelYear = -yyRelYear;
 	}
-#line 1686 "getdate.c" /* yacc.c:1646  */
+#line 1621 "getdate.c"
     break;
 
-  case 31:
-#line 345 "getdate.y" /* yacc.c:1646  */
-    {
+  case 31: /* relunit: tUNUMBER tYEAR_UNIT  */
+#line 345 "getdate.y"
+                              {
 	    yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1694 "getdate.c" /* yacc.c:1646  */
+#line 1629 "getdate.c"
     break;
 
-  case 32:
-#line 348 "getdate.y" /* yacc.c:1646  */
-    {
+  case 32: /* relunit: tSNUMBER tYEAR_UNIT  */
+#line 348 "getdate.y"
+                              {
 	    yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1702 "getdate.c" /* yacc.c:1646  */
+#line 1637 "getdate.c"
     break;
 
-  case 33:
-#line 351 "getdate.y" /* yacc.c:1646  */
-    {
+  case 33: /* relunit: tYEAR_UNIT  */
+#line 351 "getdate.y"
+                     {
 	    yyRelYear += (yyvsp[0].Number);
 	}
-#line 1710 "getdate.c" /* yacc.c:1646  */
+#line 1645 "getdate.c"
     break;
 
-  case 34:
-#line 354 "getdate.y" /* yacc.c:1646  */
-    {
+  case 34: /* relunit: tUNUMBER tMONTH_UNIT  */
+#line 354 "getdate.y"
+                               {
 	    yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1718 "getdate.c" /* yacc.c:1646  */
+#line 1653 "getdate.c"
     break;
 
-  case 35:
-#line 357 "getdate.y" /* yacc.c:1646  */
-    {
+  case 35: /* relunit: tSNUMBER tMONTH_UNIT  */
+#line 357 "getdate.y"
+                               {
 	    yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1726 "getdate.c" /* yacc.c:1646  */
+#line 1661 "getdate.c"
     break;
 
-  case 36:
-#line 360 "getdate.y" /* yacc.c:1646  */
-    {
+  case 36: /* relunit: tMONTH_UNIT  */
+#line 360 "getdate.y"
+                      {
 	    yyRelMonth += (yyvsp[0].Number);
 	}
-#line 1734 "getdate.c" /* yacc.c:1646  */
+#line 1669 "getdate.c"
     break;
 
-  case 37:
-#line 363 "getdate.y" /* yacc.c:1646  */
-    {
+  case 37: /* relunit: tUNUMBER tDAY_UNIT  */
+#line 363 "getdate.y"
+                             {
 	    yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1742 "getdate.c" /* yacc.c:1646  */
+#line 1677 "getdate.c"
     break;
 
-  case 38:
-#line 366 "getdate.y" /* yacc.c:1646  */
-    {
+  case 38: /* relunit: tSNUMBER tDAY_UNIT  */
+#line 366 "getdate.y"
+                             {
 	    yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1750 "getdate.c" /* yacc.c:1646  */
+#line 1685 "getdate.c"
     break;
 
-  case 39:
-#line 369 "getdate.y" /* yacc.c:1646  */
-    {
+  case 39: /* relunit: tDAY_UNIT  */
+#line 369 "getdate.y"
+                    {
 	    yyRelDay += (yyvsp[0].Number);
 	}
-#line 1758 "getdate.c" /* yacc.c:1646  */
+#line 1693 "getdate.c"
     break;
 
-  case 40:
-#line 372 "getdate.y" /* yacc.c:1646  */
-    {
+  case 40: /* relunit: tUNUMBER tHOUR_UNIT  */
+#line 372 "getdate.y"
+                              {
 	    yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1766 "getdate.c" /* yacc.c:1646  */
+#line 1701 "getdate.c"
     break;
 
-  case 41:
-#line 375 "getdate.y" /* yacc.c:1646  */
-    {
+  case 41: /* relunit: tSNUMBER tHOUR_UNIT  */
+#line 375 "getdate.y"
+                              {
 	    yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1774 "getdate.c" /* yacc.c:1646  */
+#line 1709 "getdate.c"
     break;
 
-  case 42:
-#line 378 "getdate.y" /* yacc.c:1646  */
-    {
+  case 42: /* relunit: tHOUR_UNIT  */
+#line 378 "getdate.y"
+                     {
 	    yyRelHour += (yyvsp[0].Number);
 	}
-#line 1782 "getdate.c" /* yacc.c:1646  */
+#line 1717 "getdate.c"
     break;
 
-  case 43:
-#line 381 "getdate.y" /* yacc.c:1646  */
-    {
+  case 43: /* relunit: tUNUMBER tMINUTE_UNIT  */
+#line 381 "getdate.y"
+                                {
 	    yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1790 "getdate.c" /* yacc.c:1646  */
+#line 1725 "getdate.c"
     break;
 
-  case 44:
-#line 384 "getdate.y" /* yacc.c:1646  */
-    {
+  case 44: /* relunit: tSNUMBER tMINUTE_UNIT  */
+#line 384 "getdate.y"
+                                {
 	    yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1798 "getdate.c" /* yacc.c:1646  */
+#line 1733 "getdate.c"
     break;
 
-  case 45:
-#line 387 "getdate.y" /* yacc.c:1646  */
-    {
+  case 45: /* relunit: tMINUTE_UNIT  */
+#line 387 "getdate.y"
+                       {
 	    yyRelMinutes += (yyvsp[0].Number);
 	}
-#line 1806 "getdate.c" /* yacc.c:1646  */
+#line 1741 "getdate.c"
     break;
 
-  case 46:
-#line 390 "getdate.y" /* yacc.c:1646  */
-    {
+  case 46: /* relunit: tUNUMBER tSEC_UNIT  */
+#line 390 "getdate.y"
+                             {
 	    yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1814 "getdate.c" /* yacc.c:1646  */
+#line 1749 "getdate.c"
     break;
 
-  case 47:
-#line 393 "getdate.y" /* yacc.c:1646  */
-    {
+  case 47: /* relunit: tSNUMBER tSEC_UNIT  */
+#line 393 "getdate.y"
+                             {
 	    yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number);
 	}
-#line 1822 "getdate.c" /* yacc.c:1646  */
+#line 1757 "getdate.c"
     break;
 
-  case 48:
-#line 396 "getdate.y" /* yacc.c:1646  */
-    {
+  case 48: /* relunit: tSEC_UNIT  */
+#line 396 "getdate.y"
+                    {
 	    yyRelSeconds += (yyvsp[0].Number);
 	}
-#line 1830 "getdate.c" /* yacc.c:1646  */
+#line 1765 "getdate.c"
     break;
 
-  case 49:
-#line 402 "getdate.y" /* yacc.c:1646  */
-    {
+  case 49: /* number: tUNUMBER  */
+#line 402 "getdate.y"
+          {
 	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
 	      yyYear = (yyvsp[0].Number);
 	    else
@@ -1861,27 +1796,28 @@
 		  }
 	      }
 	  }
-#line 1865 "getdate.c" /* yacc.c:1646  */
+#line 1800 "getdate.c"
     break;
 
-  case 50:
-#line 435 "getdate.y" /* yacc.c:1646  */
-    {
+  case 50: /* o_merid: %empty  */
+#line 435 "getdate.y"
+          {
 	    (yyval.Meridian) = MER24;
 	  }
-#line 1873 "getdate.c" /* yacc.c:1646  */
+#line 1808 "getdate.c"
     break;
 
-  case 51:
-#line 439 "getdate.y" /* yacc.c:1646  */
-    {
+  case 51: /* o_merid: tMERIDIAN  */
+#line 439 "getdate.y"
+          {
 	    (yyval.Meridian) = (yyvsp[0].Meridian);
 	  }
-#line 1881 "getdate.c" /* yacc.c:1646  */
+#line 1816 "getdate.c"
     break;
 
 
-#line 1885 "getdate.c" /* yacc.c:1646  */
+#line 1820 "getdate.c"
+
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -1895,25 +1831,23 @@
      case of YYERROR or YYBACKUP, subsequent parser actions might lead
      to an incorrect destructor call or verbose syntax error message
      before the lookahead is translated.  */
-  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+  YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc);
 
   YYPOPSTACK (yylen);
   yylen = 0;
-  YY_STACK_PRINT (yyss, yyssp);
 
   *++yyvsp = yyval;
 
   /* Now 'shift' the result of the reduction.  Determine what state
      that goes to, based on the state we popped back to and the rule
      number reduced by.  */
-
-  yyn = yyr1[yyn];
-
-  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
-  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
-    yystate = yytable[yystate];
-  else
-    yystate = yydefgoto[yyn - YYNTOKENS];
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
 
   goto yynewstate;
 
@@ -1924,50 +1858,14 @@
 yyerrlab:
   /* Make sure we have latest lookahead translation.  See comments at
      user semantic actions for why this is necessary.  */
-  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
-
+  yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar);
   /* If not already recovering from an error, report this error.  */
   if (!yyerrstatus)
     {
       ++yynerrs;
-#if ! YYERROR_VERBOSE
       yyerror (YY_("syntax error"));
-#else
-# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
-                                        yyssp, yytoken)
-      {
-        char const *yymsgp = YY_("syntax error");
-        int yysyntax_error_status;
-        yysyntax_error_status = YYSYNTAX_ERROR;
-        if (yysyntax_error_status == 0)
-          yymsgp = yymsg;
-        else if (yysyntax_error_status == 1)
-          {
-            if (yymsg != yymsgbuf)
-              YYSTACK_FREE (yymsg);
-            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
-            if (!yymsg)
-              {
-                yymsg = yymsgbuf;
-                yymsg_alloc = sizeof yymsgbuf;
-                yysyntax_error_status = 2;
-              }
-            else
-              {
-                yysyntax_error_status = YYSYNTAX_ERROR;
-                yymsgp = yymsg;
-              }
-          }
-        yyerror (yymsgp);
-        if (yysyntax_error_status == 2)
-          goto yyexhaustedlab;
-      }
-# undef YYSYNTAX_ERROR
-#endif
     }
 
-
-
   if (yyerrstatus == 3)
     {
       /* If just tried and failed to reuse lookahead token after an
@@ -1996,12 +1894,10 @@
 | yyerrorlab -- error raised explicitly by YYERROR.  |
 `---------------------------------------------------*/
 yyerrorlab:
-
-  /* Pacify compilers like GCC when the user code never invokes
-     YYERROR and the label yyerrorlab therefore never appears in user
-     code.  */
-  if (/*CONSTCOND*/ 0)
-     goto yyerrorlab;
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
 
   /* Do not reclaim the symbols of the rule whose action triggered
      this YYERROR.  */
@@ -2018,13 +1914,14 @@
 yyerrlab1:
   yyerrstatus = 3;      /* Each real token shifted decrements this.  */
 
+  /* Pop stack until we find a state that shifts the error token.  */
   for (;;)
     {
       yyn = yypact[yystate];
       if (!yypact_value_is_default (yyn))
         {
-          yyn += YYTERROR;
-          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+          yyn += YYSYMBOL_YYerror;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror)
             {
               yyn = yytable[yyn];
               if (0 < yyn)
@@ -2038,7 +1935,7 @@
 
 
       yydestruct ("Error: popping",
-                  yystos[yystate], yyvsp);
+                  YY_ACCESSING_SYMBOL (yystate), yyvsp);
       YYPOPSTACK (1);
       yystate = *yyssp;
       YY_STACK_PRINT (yyss, yyssp);
@@ -2050,7 +1947,7 @@
 
 
   /* Shift the error token.  */
-  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+  YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp);
 
   yystate = yyn;
   goto yynewstate;
@@ -2063,6 +1960,7 @@
   yyresult = 0;
   goto yyreturn;
 
+
 /*-----------------------------------.
 | yyabortlab -- YYABORT comes here.  |
 `-----------------------------------*/
@@ -2070,16 +1968,21 @@
   yyresult = 1;
   goto yyreturn;
 
-#if !defined yyoverflow || YYERROR_VERBOSE
+
+#if !defined yyoverflow
 /*-------------------------------------------------.
 | yyexhaustedlab -- memory exhaustion comes here.  |
 `-------------------------------------------------*/
 yyexhaustedlab:
   yyerror (YY_("memory exhausted"));
   yyresult = 2;
-  /* Fall through.  */
+  goto yyreturn;
 #endif
 
+
+/*-------------------------------------------------------.
+| yyreturn -- parsing is finished, clean up and return.  |
+`-------------------------------------------------------*/
 yyreturn:
   if (yychar != YYEMPTY)
     {
@@ -2096,20 +1999,18 @@
   while (yyssp != yyss)
     {
       yydestruct ("Cleanup: popping",
-                  yystos[*yyssp], yyvsp);
+                  YY_ACCESSING_SYMBOL (+*yyssp), yyvsp);
       YYPOPSTACK (1);
     }
 #ifndef yyoverflow
   if (yyss != yyssa)
     YYSTACK_FREE (yyss);
 #endif
-#if YYERROR_VERBOSE
-  if (yymsg != yymsgbuf)
-    YYSTACK_FREE (yymsg);
-#endif
+
   return yyresult;
 }
-#line 444 "getdate.y" /* yacc.c:1906  */
+
+#line 444 "getdate.y"
 
 
 /* Include this file down here because bison inserts code above which
diff --git a/chrony_3_3/getdate.h b/chrony/getdate.h
similarity index 100%
rename from chrony_3_3/getdate.h
rename to chrony/getdate.h
diff --git a/chrony_3_3/getdate.y b/chrony/getdate.y
similarity index 100%
rename from chrony_3_3/getdate.y
rename to chrony/getdate.y
diff --git a/chrony_3_3/hash.h b/chrony/hash.h
similarity index 70%
rename from chrony_3_3/hash.h
rename to chrony/hash.h
index 185da66..39abf27 100644
--- a/chrony_3_3/hash.h
+++ b/chrony/hash.h
@@ -31,12 +31,26 @@
 /* length of hash values produced by SHA512 */
 #define MAX_HASH_LENGTH 64
 
-extern int HSH_GetHashId(const char *name);
+typedef enum {
+  HSH_INVALID = 0,
+  HSH_MD5 = 1,
+  HSH_SHA1 = 2,
+  HSH_SHA256 = 3,
+  HSH_SHA384 = 4,
+  HSH_SHA512 = 5,
+  HSH_SHA3_224 = 6,
+  HSH_SHA3_256 = 7,
+  HSH_SHA3_384 = 8,
+  HSH_SHA3_512 = 9,
+  HSH_TIGER = 10,
+  HSH_WHIRLPOOL = 11,
+  HSH_MD5_NONCRYPTO = 10000, /* For NTPv4 reference ID */
+} HSH_Algorithm;
 
-extern unsigned int HSH_Hash(int id,
-    const unsigned char *in1, unsigned int in1_len,
-    const unsigned char *in2, unsigned int in2_len,
-    unsigned char *out, unsigned int out_len);
+extern int HSH_GetHashId(HSH_Algorithm algorithm);
+
+extern int HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
+                    unsigned char *out, int out_len);
 
 extern void HSH_Finalise(void);
 
diff --git a/chrony/hash_gnutls.c b/chrony/hash_gnutls.c
new file mode 100644
index 0000000..269fdd8
--- /dev/null
+++ b/chrony/hash_gnutls.c
@@ -0,0 +1,145 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2021
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ **********************************************************************
+
+  =======================================================================
+
+  Crypto hashing using the GnuTLS library
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include <gnutls/crypto.h>
+
+#include "hash.h"
+#include "logging.h"
+
+struct hash {
+  const HSH_Algorithm algorithm;
+  const gnutls_digest_algorithm_t type;
+  gnutls_hash_hd_t handle;
+};
+
+static struct hash hashes[] = {
+  { HSH_MD5_NONCRYPTO, GNUTLS_DIG_MD5, NULL },
+  { HSH_MD5, GNUTLS_DIG_MD5, NULL },
+  { HSH_SHA1, GNUTLS_DIG_SHA1, NULL },
+  { HSH_SHA256, GNUTLS_DIG_SHA256, NULL },
+  { HSH_SHA384, GNUTLS_DIG_SHA384, NULL },
+  { HSH_SHA512, GNUTLS_DIG_SHA512, NULL },
+  { HSH_SHA3_224, GNUTLS_DIG_SHA3_224, NULL },
+  { HSH_SHA3_256, GNUTLS_DIG_SHA3_256, NULL },
+  { HSH_SHA3_384, GNUTLS_DIG_SHA3_384, NULL },
+  { HSH_SHA3_512, GNUTLS_DIG_SHA3_512, NULL },
+  { 0, 0, NULL }
+};
+
+static int gnutls_initialised = 0;
+
+int
+HSH_GetHashId(HSH_Algorithm algorithm)
+{
+  int id, r;
+
+  if (!gnutls_initialised) {
+    r = gnutls_global_init();
+    if (r < 0)
+      LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
+    gnutls_initialised = 1;
+  }
+
+  for (id = 0; hashes[id].algorithm != 0; id++) {
+    if (hashes[id].algorithm == algorithm)
+      break;
+  }
+
+  if (hashes[id].algorithm == 0)
+    return -1;
+
+  if (hashes[id].handle)
+    return id;
+
+  if (algorithm == HSH_MD5_NONCRYPTO)
+    GNUTLS_FIPS140_SET_LAX_MODE();
+
+  r = gnutls_hash_init(&hashes[id].handle, hashes[id].type);
+
+  if (algorithm == HSH_MD5_NONCRYPTO)
+    GNUTLS_FIPS140_SET_STRICT_MODE();
+
+  if (r < 0) {
+    DEBUG_LOG("Could not initialise %s : %s", "hash", gnutls_strerror(r));
+    hashes[id].handle = NULL;
+    return -1;
+  }
+
+  return id;
+}
+
+int
+HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
+         unsigned char *out, int out_len)
+{
+  unsigned char buf[MAX_HASH_LENGTH];
+  gnutls_hash_hd_t handle;
+  int hash_len;
+
+  if (in1_len < 0 || in2_len < 0 || out_len < 0)
+    return 0;
+
+  handle = hashes[id].handle;
+  hash_len = gnutls_hash_get_len(hashes[id].type);
+
+  if (out_len > hash_len)
+    out_len = hash_len;
+
+  if (hash_len > sizeof (buf))
+    return 0;
+
+  if (gnutls_hash(handle, in1, in1_len) < 0 ||
+      (in2 && gnutls_hash(handle, in2, in2_len) < 0)) {
+    /* Reset the state */
+    gnutls_hash_output(handle, buf);
+    return 0;
+  }
+
+  gnutls_hash_output(handle, buf);
+  memcpy(out, buf, out_len);
+
+  return out_len;
+}
+
+void
+HSH_Finalise(void)
+{
+  int i;
+
+  if (!gnutls_initialised)
+    return;
+
+  for (i = 0; hashes[i].algorithm != 0; i++) {
+    if (hashes[i].handle)
+      gnutls_hash_deinit(hashes[i].handle, NULL);
+  }
+
+  gnutls_global_deinit();
+}
diff --git a/chrony_3_3/hash_intmd5.c b/chrony/hash_intmd5.c
similarity index 79%
rename from chrony_3_3/hash_intmd5.c
rename to chrony/hash_intmd5.c
index 64e0b9c..a64b735 100644
--- a/chrony_3_3/hash_intmd5.c
+++ b/chrony/hash_intmd5.c
@@ -29,27 +29,27 @@
 #include "sysincl.h"
 #include "hash.h"
 #include "memory.h"
+#include "util.h"
 
 #include "md5.c"
 
 static MD5_CTX ctx;
 
 int
-HSH_GetHashId(const char *name)
+HSH_GetHashId(HSH_Algorithm algorithm)
 {
   /* only MD5 is supported */
-  if (strcmp(name, "MD5"))
+  if (algorithm != HSH_MD5 && algorithm != HSH_MD5_NONCRYPTO)
     return -1;
 
   return 0;
 }
 
-unsigned int
-HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
-    const unsigned char *in2, unsigned int in2_len,
-    unsigned char *out, unsigned int out_len)
+int
+HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
+         unsigned char *out, int out_len)
 {
-  if (out_len < 16)
+  if (in1_len < 0 || in2_len < 0 || out_len < 0)
     return 0;
 
   MD5Init(&ctx);
@@ -58,9 +58,11 @@
     MD5Update(&ctx, in2, in2_len);
   MD5Final(&ctx);
 
-  memcpy(out, ctx.digest, 16);
+  out_len = MIN(out_len, 16);
 
-  return 16;
+  memcpy(out, ctx.digest, out_len);
+
+  return out_len;
 }
 
 void
diff --git a/chrony_3_3/hash_nettle.c b/chrony/hash_nettle.c
similarity index 71%
rename from chrony_3_3/hash_nettle.c
rename to chrony/hash_nettle.c
index 2c3501d..4a214f6 100644
--- a/chrony_3_3/hash_nettle.c
+++ b/chrony/hash_nettle.c
@@ -35,37 +35,39 @@
 #include "memory.h"
 
 struct hash {
-  const char *name;
+  const HSH_Algorithm algorithm;
   const char *int_name;
   const struct nettle_hash *nettle_hash;
   void *context;
 };
 
 static struct hash hashes[] = {
-  { "MD5", "md5", NULL, NULL },
-  { "RMD160", "ripemd160", NULL, NULL },
-  { "SHA1", "sha1", NULL, NULL },
-  { "SHA256", "sha256", NULL, NULL },
-  { "SHA384", "sha384", NULL, NULL },
-  { "SHA512", "sha512", NULL, NULL },
-  { "SHA3-224", "sha3_224", NULL, NULL },
-  { "SHA3-256", "sha3_256", NULL, NULL },
-  { "SHA3-384", "sha3_384", NULL, NULL },
-  { "SHA3-512", "sha3_512", NULL, NULL },
-  { NULL, NULL, NULL, NULL }
+  { HSH_MD5, "md5", NULL, NULL },
+  { HSH_SHA1, "sha1", NULL, NULL },
+  { HSH_SHA256, "sha256", NULL, NULL },
+  { HSH_SHA384, "sha384", NULL, NULL },
+  { HSH_SHA512, "sha512", NULL, NULL },
+  { HSH_SHA3_224, "sha3_224", NULL, NULL },
+  { HSH_SHA3_256, "sha3_256", NULL, NULL },
+  { HSH_SHA3_384, "sha3_384", NULL, NULL },
+  { HSH_SHA3_512, "sha3_512", NULL, NULL },
+  { 0, NULL, NULL, NULL }
 };
 
 int
-HSH_GetHashId(const char *name)
+HSH_GetHashId(HSH_Algorithm algorithm)
 {
   int id, nid;
 
-  for (id = 0; hashes[id].name; id++) {
-    if (!strcmp(name, hashes[id].name))
+  if (algorithm == HSH_MD5_NONCRYPTO)
+    algorithm = HSH_MD5;
+
+  for (id = 0; hashes[id].algorithm != 0; id++) {
+    if (hashes[id].algorithm == algorithm)
       break;
   }
 
-  if (!hashes[id].name)
+  if (hashes[id].algorithm == 0)
     return -1;
 
   if (hashes[id].context)
@@ -85,14 +87,16 @@
   return id;
 }
 
-unsigned int
-HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
-         const unsigned char *in2, unsigned int in2_len,
-         unsigned char *out, unsigned int out_len)
+int
+HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
+         unsigned char *out, int out_len)
 {
   const struct nettle_hash *hash;
   void *context;
 
+  if (in1_len < 0 || in2_len < 0 || out_len < 0)
+    return 0;
+
   hash = hashes[id].nettle_hash;
   context = hashes[id].context;
 
@@ -113,7 +117,7 @@
 {
   int i;
 
-  for (i = 0; hashes[i].name; i++) {
+  for (i = 0; hashes[i].algorithm != 0; i++) {
     if (hashes[i].context)
       Free(hashes[i].context);
   }
diff --git a/chrony_3_3/hash_nss.c b/chrony/hash_nss.c
similarity index 69%
rename from chrony_3_3/hash_nss.c
rename to chrony/hash_nss.c
index eb1f050..4302447 100644
--- a/chrony_3_3/hash_nss.c
+++ b/chrony/hash_nss.c
@@ -32,35 +32,39 @@
 #include <nsslowhash.h>
 
 #include "hash.h"
+#include "util.h"
 
 static NSSLOWInitContext *ictx;
 
 struct hash {
   HASH_HashType type;
-  const char *name;
+  HSH_Algorithm algorithm;
   NSSLOWHASHContext *context;
 };
 
 static struct hash hashes[] = {
-  { HASH_AlgMD5, "MD5", NULL },
-  { HASH_AlgSHA1, "SHA1", NULL },
-  { HASH_AlgSHA256, "SHA256", NULL },
-  { HASH_AlgSHA384, "SHA384", NULL },
-  { HASH_AlgSHA512, "SHA512", NULL },
-  { 0, NULL, NULL }
+  { HASH_AlgMD5, HSH_MD5, NULL },
+  { HASH_AlgSHA1, HSH_SHA1, NULL },
+  { HASH_AlgSHA256, HSH_SHA256, NULL },
+  { HASH_AlgSHA384, HSH_SHA384, NULL },
+  { HASH_AlgSHA512, HSH_SHA512, NULL },
+  { 0, 0, NULL }
 };
 
 int
-HSH_GetHashId(const char *name)
+HSH_GetHashId(HSH_Algorithm algorithm)
 {
   int i;
 
-  for (i = 0; hashes[i].name; i++) {
-    if (!strcmp(name, hashes[i].name))
+  if (algorithm == HSH_MD5_NONCRYPTO)
+    algorithm = HSH_MD5;
+
+  for (i = 0; hashes[i].algorithm != 0; i++) {
+    if (hashes[i].algorithm == algorithm)
       break;
   }
 
-  if (!hashes[i].name)
+  if (hashes[i].algorithm == 0)
     return -1; /* not found */
 
   if (!ictx && !(ictx = NSSLOW_Init()))
@@ -73,18 +77,24 @@
   return i;
 }
 
-unsigned int
-HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
-    const unsigned char *in2, unsigned int in2_len,
-    unsigned char *out, unsigned int out_len)
+int
+HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
+         unsigned char *out, int out_len)
 {
+  unsigned char buf[MAX_HASH_LENGTH];
   unsigned int ret = 0;
 
+  if (in1_len < 0 || in2_len < 0 || out_len < 0)
+    return 0;
+
   NSSLOWHASH_Begin(hashes[id].context);
   NSSLOWHASH_Update(hashes[id].context, in1, in1_len);
   if (in2)
     NSSLOWHASH_Update(hashes[id].context, in2, in2_len);
-  NSSLOWHASH_End(hashes[id].context, out, &ret, out_len);
+  NSSLOWHASH_End(hashes[id].context, buf, &ret, sizeof (buf));
+
+  ret = MIN(ret, out_len);
+  memcpy(out, buf, ret);
 
   return ret;
 }
@@ -94,7 +104,7 @@
 {
   int i;
 
-  for (i = 0; hashes[i].name; i++) {
+  for (i = 0; hashes[i].algorithm != 0; i++) {
     if (hashes[i].context)
       NSSLOWHASH_Destroy(hashes[i].context);
   }
diff --git a/chrony/hash_tomcrypt.c b/chrony/hash_tomcrypt.c
new file mode 100644
index 0000000..9c425d2
--- /dev/null
+++ b/chrony/hash_tomcrypt.c
@@ -0,0 +1,126 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2012, 2018
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Routines implementing crypto hashing using tomcrypt library.
+
+  */
+
+#include <tomcrypt.h>
+
+#include "config.h"
+#include "hash.h"
+#include "util.h"
+
+struct hash {
+  HSH_Algorithm algorithm;
+  const char *int_name;
+  const struct ltc_hash_descriptor *desc;
+};
+
+static const struct hash hashes[] = {
+  { HSH_MD5, "md5", &md5_desc },
+#ifdef LTC_SHA1
+  { HSH_SHA1, "sha1", &sha1_desc },
+#endif
+#ifdef LTC_SHA256
+  { HSH_SHA256, "sha256", &sha256_desc },
+#endif
+#ifdef LTC_SHA384
+  { HSH_SHA384, "sha384", &sha384_desc },
+#endif
+#ifdef LTC_SHA512
+  { HSH_SHA512, "sha512", &sha512_desc },
+#endif
+#ifdef LTC_SHA3
+  { HSH_SHA3_224, "sha3-224", &sha3_224_desc },
+  { HSH_SHA3_256, "sha3-256", &sha3_256_desc },
+  { HSH_SHA3_384, "sha3-384", &sha3_384_desc },
+  { HSH_SHA3_512, "sha3-512", &sha3_512_desc },
+#endif
+#ifdef LTC_TIGER
+  { HSH_TIGER, "tiger", &tiger_desc },
+#endif
+#ifdef LTC_WHIRLPOOL
+  { HSH_WHIRLPOOL, "whirlpool", &whirlpool_desc },
+#endif
+  { 0, NULL, NULL }
+};
+
+int
+HSH_GetHashId(HSH_Algorithm algorithm)
+{
+  int i, h;
+
+  if (algorithm == HSH_MD5_NONCRYPTO)
+    algorithm = HSH_MD5;
+
+  for (i = 0; hashes[i].algorithm != 0; i++) {
+    if (hashes[i].algorithm == algorithm)
+      break;
+  }
+
+  if (hashes[i].algorithm == 0)
+    return -1; /* not found */
+
+  h = find_hash(hashes[i].int_name);
+  if (h >= 0)
+    return h; /* already registered */
+  
+  /* register and try again */
+  register_hash(hashes[i].desc);
+
+  return find_hash(hashes[i].int_name);
+}
+
+int
+HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
+         unsigned char *out, int out_len)
+{
+  unsigned char buf[MAX_HASH_LENGTH];
+  unsigned long len;
+  int r;
+
+  if (in1_len < 0 || in2_len < 0 || out_len < 0)
+    return 0;
+
+  len = sizeof (buf);
+  if (in2)
+    r = hash_memory_multi(id, buf, &len,
+                          in1, (unsigned long)in1_len,
+                          in2, (unsigned long)in2_len, NULL, 0);
+  else
+    r = hash_memory(id, in1, in1_len, buf, &len);
+
+  if (r != CRYPT_OK)
+    return 0;
+
+  len = MIN(len, out_len);
+  memcpy(out, buf, len);
+
+  return len;
+}
+
+void
+HSH_Finalise(void)
+{
+}
diff --git a/chrony_3_3/hwclock.c b/chrony/hwclock.c
similarity index 81%
rename from chrony_3_3/hwclock.c
rename to chrony/hwclock.c
index 007d19d..8e13e7e 100644
--- a/chrony_3_3/hwclock.c
+++ b/chrony/hwclock.c
@@ -2,7 +2,7 @@
   chronyd/chronyc - Programs for keeping computer clocks accurate.
 
  **********************************************************************
- * Copyright (C) Miroslav Lichvar  2016-2017
+ * Copyright (C) Miroslav Lichvar  2016-2018
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -36,8 +36,9 @@
 #include "regress.h"
 #include "util.h"
 
-/* Maximum number of samples per clock */
-#define MAX_SAMPLES 16
+/* Minimum and maximum number of samples per clock */
+#define MIN_SAMPLES 2
+#define MAX_SAMPLES 64
 
 /* Maximum acceptable frequency offset of the clock */
 #define MAX_FREQ_OFFSET (2.0 / 3.0)
@@ -49,10 +50,12 @@
 
   /* Samples stored as intervals (uncorrected for frequency error)
      relative to local_ref and hw_ref */
-  double x_data[MAX_SAMPLES];
-  double y_data[MAX_SAMPLES];
+  double *x_data;
+  double *y_data;
 
-  /* Number of samples */
+  /* Minimum, maximum and current number of samples */
+  int min_samples;
+  int max_samples;
   int n_samples;
 
   /* Maximum error of the last sample */
@@ -89,13 +92,21 @@
 /* ================================================== */
 
 HCL_Instance
-HCL_CreateInstance(double min_separation)
+HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
 {
   HCL_Instance clock;
 
+  min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
+  max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
+  max_samples = MAX(min_samples, max_samples);
+
   clock = MallocNew(struct HCL_Instance_Record);
-  clock->x_data[MAX_SAMPLES - 1] = 0.0;
-  clock->y_data[MAX_SAMPLES - 1] = 0.0;
+  clock->x_data = MallocArray(double, max_samples);
+  clock->y_data = MallocArray(double, max_samples);
+  clock->x_data[max_samples - 1] = 0.0;
+  clock->y_data[max_samples - 1] = 0.0;
+  clock->min_samples = min_samples;
+  clock->max_samples = max_samples;
   clock->n_samples = 0;
   clock->valid_coefs = 0;
   clock->min_separation = min_separation;
@@ -110,6 +121,8 @@
 void HCL_DestroyInstance(HCL_Instance clock)
 {
   LCL_RemoveParameterChangeHandler(handle_slew, clock);
+  Free(clock->y_data);
+  Free(clock->x_data);
   Free(clock);
 }
 
@@ -138,7 +151,7 @@
 
   /* Shift old samples */
   if (clock->n_samples) {
-    if (clock->n_samples >= MAX_SAMPLES)
+    if (clock->n_samples >= clock->max_samples)
       clock->n_samples--;
 
     hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
@@ -149,7 +162,7 @@
       DEBUG_LOG("HW clock reset interval=%f", local_delta);
     }
 
-    for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
+    for (i = clock->max_samples - clock->n_samples; i < clock->max_samples; i++) {
       clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
       clock->x_data[i - 1] = clock->x_data[i] - local_delta;
     }
@@ -162,8 +175,8 @@
 
   /* Get new coefficients */
   clock->valid_coefs =
-    RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
-                                 clock->y_data + MAX_SAMPLES - clock->n_samples,
+    RGR_FindBestRobustRegression(clock->x_data + clock->max_samples - clock->n_samples,
+                                 clock->y_data + clock->max_samples - clock->n_samples,
                                  clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
                                  &n_runs, &best_start);
 
@@ -175,7 +188,8 @@
   clock->frequency = raw_freq / local_freq;
 
   /* Drop unneeded samples */
-  clock->n_samples -= best_start;
+  if (clock->n_samples > clock->min_samples)
+    clock->n_samples -= MIN(best_start, clock->n_samples - clock->min_samples);
 
   /* If the fit doesn't cross the error interval of the last sample,
      or the frequency is not sane, drop all samples and start again */
diff --git a/chrony_3_3/hwclock.h b/chrony/hwclock.h
similarity index 92%
rename from chrony_3_3/hwclock.h
rename to chrony/hwclock.h
index f80d09a..3005bae 100644
--- a/chrony_3_3/hwclock.h
+++ b/chrony/hwclock.h
@@ -29,7 +29,8 @@
 typedef struct HCL_Instance_Record *HCL_Instance;
 
 /* Create a new HW clock instance */
-extern HCL_Instance HCL_CreateInstance(double min_separation);
+extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
+                                       double min_separation);
 
 /* Destroy a HW clock instance */
 extern void HCL_DestroyInstance(HCL_Instance clock);
diff --git a/chrony_3_3/keys.c b/chrony/keys.c
similarity index 60%
rename from chrony_3_3/keys.c
rename to chrony/keys.c
index 74b57c4..cc1131c 100644
--- a/chrony_3_3/keys.c
+++ b/chrony/keys.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2012-2016
+ * Copyright (C) Miroslav Lichvar  2012-2016, 2019-2020
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -32,6 +32,7 @@
 
 #include "array.h"
 #include "keys.h"
+#include "cmac.h"
 #include "cmdparse.h"
 #include "conf.h"
 #include "memory.h"
@@ -42,12 +43,23 @@
 /* Consider 80 bits as the absolute minimum for a secure key */
 #define MIN_SECURE_KEY_LENGTH 10
 
+typedef enum {
+  NTP_MAC,
+  CMAC,
+} KeyClass;
+
 typedef struct {
   uint32_t id;
-  char *val;
-  int len;
-  int hash_id;
-  int auth_delay;
+  int type;
+  int length;
+  KeyClass class;
+  union {
+    struct {
+      unsigned char *value;
+      int hash_id;
+    } ntp_mac;
+    CMC_Instance cmac;
+  } data;
 } Key;
 
 static ARR_Instance keys;
@@ -62,9 +74,21 @@
 free_keys(void)
 {
   unsigned int i;
+  Key *key;
 
-  for (i = 0; i < ARR_GetSize(keys); i++)
-    Free(((Key *)ARR_GetElement(keys, i))->val);
+  for (i = 0; i < ARR_GetSize(keys); i++) {
+    key = ARR_GetElement(keys, i);
+    switch (key->class) {
+      case NTP_MAC:
+        Free(key->data.ntp_mac.value);
+        break;
+      case CMAC:
+        CMC_DestroyInstance(key->data.cmac);
+        break;
+      default:
+        assert(0);
+    }
+  }
 
   ARR_SetSize(keys, 0);
   cache_valid = 0;
@@ -98,60 +122,18 @@
 }
 
 /* ================================================== */
+/* Decode key encoded in ASCII or HEX */
 
 static int
-determine_hash_delay(uint32_t key_id)
+decode_key(char *key)
 {
-  NTP_Packet pkt;
-  struct timespec before, after;
-  double diff, min_diff;
-  int i, nsecs;
-
-  for (i = 0; i < 10; i++) {
-    LCL_ReadRawTime(&before);
-    KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH,
-        (unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
-    LCL_ReadRawTime(&after);
-
-    diff = UTI_DiffTimespecsToDouble(&after, &before);
-
-    if (i == 0 || min_diff > diff)
-      min_diff = diff;
-  }
-
-  /* Add on a bit extra to allow for copying, conversions etc */
-  nsecs = 1.0625e9 * min_diff;
-
-  DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
-
-  return nsecs;
-}
-
-/* ================================================== */
-/* Decode password encoded in ASCII or HEX */
-
-static int
-decode_password(char *key)
-{
-  int i, j, len = strlen(key);
-  char buf[3], *p;
+  int len = strlen(key);
 
   if (!strncmp(key, "ASCII:", 6)) {
     memmove(key, key + 6, len - 6);
     return len - 6;
   } else if (!strncmp(key, "HEX:", 4)) {
-    if ((len - 4) % 2)
-      return 0;
-
-    for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
-      buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
-      key[i] = strtol(buf, &p, 16);
-
-      if (p != buf + 2)
-        return 0;
-    }
-
-    return i;
+    return UTI_HexToBytes(key + 4, key, len);
   } else {
     /* assume ASCII */
     return len;
@@ -183,11 +165,13 @@
 void
 KEY_Reload(void)
 {
-  unsigned int i, line_number;
+  unsigned int i, line_number, key_length, cmac_key_length;
   FILE *in;
-  uint32_t key_id;
-  char line[2048], *keyval, *key_file;
-  const char *hashname;
+  char line[2048], *key_file, *key_value;
+  const char *key_type;
+  HSH_Algorithm hash_algorithm;
+  CMC_Algorithm cmac_algorithm;
+  int hash_id;
   Key key;
 
   free_keys();
@@ -198,7 +182,7 @@
   if (!key_file)
     return;
 
-  in = fopen(key_file, "r");
+  in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
   if (!in) {
     LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
     return;
@@ -211,26 +195,56 @@
     if (!*line)
       continue;
 
-    if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
-      LOG(LOGS_WARN, "Could not parse key at line %d in file %s", line_number, key_file);
+    memset(&key, 0, sizeof (key));
+
+    if (!CPS_ParseKey(line, &key.id, &key_type, &key_value)) {
+      LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
       continue;
     }
 
-    key.hash_id = HSH_GetHashId(hashname);
-    if (key.hash_id < 0) {
-      LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id);
+    key_length = decode_key(key_value);
+    if (key_length == 0) {
+      LOG(LOGS_WARN, "Could not decode key %"PRIu32, key.id);
       continue;
     }
 
-    key.len = decode_password(keyval);
-    if (!key.len) {
-      LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id);
+    hash_algorithm = UTI_HashNameToAlgorithm(key_type);
+    cmac_algorithm = UTI_CmacNameToAlgorithm(key_type);
+
+    if (hash_algorithm != 0) {
+      hash_id = HSH_GetHashId(hash_algorithm);
+      if (hash_id < 0) {
+        LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "hash function", key.id);
+        continue;
+      }
+      key.class = NTP_MAC;
+      key.type = hash_algorithm;
+      key.length = key_length;
+      key.data.ntp_mac.value = MallocArray(unsigned char, key_length);
+      memcpy(key.data.ntp_mac.value, key_value, key_length);
+      key.data.ntp_mac.hash_id = hash_id;
+    } else if (cmac_algorithm != 0) {
+      cmac_key_length = CMC_GetKeyLength(cmac_algorithm);
+      if (cmac_key_length == 0) {
+        LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "cipher", key.id);
+        continue;
+      } else if (cmac_key_length != key_length) {
+        LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)",
+            key_type, key.id, 8 * cmac_key_length);
+        continue;
+      }
+
+      key.class = CMAC;
+      key.type = cmac_algorithm;
+      key.length = key_length;
+      key.data.cmac = CMC_CreateInstance(cmac_algorithm, (unsigned char *)key_value,
+                                         key_length);
+      assert(key.data.cmac);
+    } else {
+      LOG(LOGS_WARN, "Invalid type in key %"PRIu32, key.id);
       continue;
     }
 
-    key.id = key_id;
-    key.val = MallocArray(char, key.len);
-    memcpy(key.val, keyval, key.len);
     ARR_AppendElement(keys, &key);
   }
 
@@ -249,9 +263,6 @@
 
   /* Erase any passwords from stack */
   memset(line, 0, sizeof (line));
-
-  for (i = 0; i < ARR_GetSize(keys); i++)
-    get_key(i)->auth_delay = determine_hash_delay(get_key(i)->id);
 }
 
 /* ================================================== */
@@ -308,21 +319,6 @@
 /* ================================================== */
 
 int
-KEY_GetAuthDelay(uint32_t key_id)
-{
-  Key *key;
-
-  key = get_key_by_id(key_id);
-
-  if (!key)
-    return 0;
-
-  return key->auth_delay;
-}
-
-/* ================================================== */
-
-int
 KEY_GetAuthLength(uint32_t key_id)
 {
   unsigned char buf[MAX_HASH_LENGTH];
@@ -333,7 +329,15 @@
   if (!key)
     return 0;
 
-  return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
+  switch (key->class) {
+    case NTP_MAC:
+      return HSH_Hash(key->data.ntp_mac.hash_id, buf, 0, buf, 0, buf, sizeof (buf));
+    case CMAC:
+      return CMC_Hash(key->data.cmac, buf, 0, buf, sizeof (buf));
+    default:
+      assert(0);
+      return 0;
+  }
 }
 
 /* ================================================== */
@@ -348,39 +352,13 @@
   if (!key)
     return 0;
 
-  return key->len >= MIN_SECURE_KEY_LENGTH;
-}
-
-/* ================================================== */
-
-static int
-generate_ntp_auth(int hash_id, const unsigned char *key, int key_len,
-                  const unsigned char *data, int data_len,
-                  unsigned char *auth, int auth_len)
-{
-  return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
-}
-
-/* ================================================== */
-
-static int
-check_ntp_auth(int hash_id, const unsigned char *key, int key_len,
-               const unsigned char *data, int data_len,
-               const unsigned char *auth, int auth_len, int trunc_len)
-{
-  unsigned char buf[MAX_HASH_LENGTH];
-  int hash_len;
-
-  hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
-
-  return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
+  return key->length >= MIN_SECURE_KEY_LENGTH;
 }
 
 /* ================================================== */
 
 int
-KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
-    unsigned char *auth, int auth_len)
+KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits)
 {
   Key *key;
 
@@ -389,14 +367,62 @@
   if (!key)
     return 0;
 
-  return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
-                           data, data_len, auth, auth_len);
+  *type = key->type;
+  *bits = 8 * key->length;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+generate_auth(Key *key, const void *data, int data_len, unsigned char *auth, int auth_len)
+{
+  switch (key->class) {
+    case NTP_MAC:
+      return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value,
+                      key->length, data, data_len, auth, auth_len);
+    case CMAC:
+      return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len);
+    default:
+      return 0;
+  }
+}
+
+/* ================================================== */
+
+static int
+check_auth(Key *key, const void *data, int data_len,
+           const unsigned char *auth, int auth_len, int trunc_len)
+{
+  unsigned char buf[MAX_HASH_LENGTH];
+  int hash_len;
+
+  hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
+
+  return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
 }
 
 /* ================================================== */
 
 int
-KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
+KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len,
+                 unsigned char *auth, int auth_len)
+{
+  Key *key;
+
+  key = get_key_by_id(key_id);
+
+  if (!key)
+    return 0;
+
+  return generate_auth(key, data, data_len, auth, auth_len);
+}
+
+/* ================================================== */
+
+int
+KEY_CheckAuth(uint32_t key_id, const void *data, int data_len,
               const unsigned char *auth, int auth_len, int trunc_len)
 {
   Key *key;
@@ -406,6 +432,5 @@
   if (!key)
     return 0;
 
-  return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
-                        data, data_len, auth, auth_len, trunc_len);
+  return check_auth(key, data, data_len, auth, auth_len, trunc_len);
 }
diff --git a/chrony_3_3/keys.h b/chrony/keys.h
similarity index 81%
rename from chrony_3_3/keys.h
rename to chrony/keys.h
index 82f3a26..c89fea9 100644
--- a/chrony_3_3/keys.h
+++ b/chrony/keys.h
@@ -34,15 +34,14 @@
 
 extern void KEY_Reload(void);
 
-extern int KEY_GetKey(uint32_t key_id, char **key, int *len);
 extern int KEY_KeyKnown(uint32_t key_id);
-extern int KEY_GetAuthDelay(uint32_t key_id);
 extern int KEY_GetAuthLength(uint32_t key_id);
 extern int KEY_CheckKeyLength(uint32_t key_id);
+extern int KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits);
 
-extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data,
-    int data_len, unsigned char *auth, int auth_len);
-extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
+extern int KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len,
+                            unsigned char *auth, int auth_len);
+extern int KEY_CheckAuth(uint32_t key_id, const void *data, int data_len,
                          const unsigned char *auth, int auth_len, int trunc_len);
 
 #endif /* GOT_KEYS_H */
diff --git a/chrony_3_3/linux-errqueue.h b/chrony/linux-errqueue.h
similarity index 100%
rename from chrony_3_3/linux-errqueue.h
rename to chrony/linux-errqueue.h
diff --git a/chrony_3_3/linux-net_tstamp.h b/chrony/linux-net_tstamp.h
similarity index 100%
rename from chrony_3_3/linux-net_tstamp.h
rename to chrony/linux-net_tstamp.h
diff --git a/chrony_3_3/local.c b/chrony/local.c
similarity index 95%
rename from chrony_3_3/local.c
rename to chrony/local.c
index b4baaac..8dbee18 100644
--- a/chrony_3_3/local.c
+++ b/chrony/local.c
@@ -108,8 +108,8 @@
 
 #define NSEC_PER_SEC 1000000000
 
-static void
-calculate_sys_precision(void)
+static double
+measure_clock_precision(void)
 {
   struct timespec ts, old_ts;
   int iters, diff, best;
@@ -135,18 +135,7 @@
 
   assert(best > 0);
 
-  precision_quantum = 1.0e-9 * best;
-
-  /* Get rounded log2 value of the measured precision */
-  precision_log = 0;
-  while (best < 707106781) {
-    precision_log--;
-    best *= 2;
-  }
-
-  assert(precision_log >= -30);
-
-  DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
+  return 1.0e-9 * best;
 }
 
 /* ================================================== */
@@ -170,7 +159,16 @@
   current_freq_ppm = 0.0;
   temp_comp_ppm = 0.0;
 
-  calculate_sys_precision();
+  precision_quantum = CNF_GetClockPrecision();
+  if (precision_quantum <= 0.0)
+    precision_quantum = measure_clock_precision();
+
+  precision_quantum = CLAMP(1.0e-9, precision_quantum, 1.0);
+  precision_log = round(log(precision_quantum) / log(2.0));
+  /* NTP code doesn't support smaller log than -30 */
+  assert(precision_log >= -30);
+
+  DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
 
   /* This is the maximum allowed frequency offset in ppm, the time must
      never stop or run backwards */
@@ -185,13 +183,11 @@
 void
 LCL_Finalise(void)
 {
-  while (change_list.next != &change_list)
-    LCL_RemoveParameterChangeHandler(change_list.next->handler,
-                                     change_list.next->anything);
-
-  while (dispersion_notify_list.next != &dispersion_notify_list)
-    LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
-                                      dispersion_notify_list.next->anything);
+  /* Make sure all handlers have been removed */
+  if (change_list.next != &change_list)
+    assert(0);
+  if (dispersion_notify_list.next != &dispersion_notify_list)
+    assert(0);
 }
 
 /* ================================================== */
@@ -509,7 +505,7 @@
 
 /* ================================================== */
 
-void
+int
 LCL_AccumulateOffset(double offset, double corr_rate)
 {
   struct timespec raw, cooked;
@@ -521,12 +517,14 @@
   LCL_CookTime(&raw, &cooked, NULL);
 
   if (!check_offset(&cooked, offset))
-      return;
+    return 0;
 
   (*drv_accrue_offset)(offset, corr_rate);
 
   /* Dispatch to all handlers */
   invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
+
+  return 1;
 }
 
 /* ================================================== */
@@ -590,7 +588,7 @@
 
 /* ================================================== */
 
-void
+int
 LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
 {
   struct timespec raw, cooked;
@@ -602,7 +600,7 @@
   LCL_CookTime(&raw, &cooked, NULL);
 
   if (!check_offset(&cooked, doffset))
-      return;
+    return 0;
 
   old_freq_ppm = current_freq_ppm;
 
@@ -624,6 +622,8 @@
 
   /* Dispatch to all handlers */
   invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
+
+  return 1;
 }
 
 /* ================================================== */
@@ -691,6 +691,19 @@
 
 /* ================================================== */
 
+void
+LCL_CancelOffsetCorrection(void)
+{
+  struct timespec raw;
+  double correction;
+
+  LCL_ReadRawTime(&raw);
+  LCL_GetOffsetCorrection(&raw, &correction, NULL);
+  LCL_AccumulateOffset(correction, 0.0);
+}
+
+/* ================================================== */
+
 int
 LCL_CanSystemLeap(void)
 {
diff --git a/chrony_3_3/local.h b/chrony/local.h
similarity index 96%
rename from chrony_3_3/local.h
rename to chrony/local.h
index 7ceb76a..63d80e9 100644
--- a/chrony_3_3/local.h
+++ b/chrony/local.h
@@ -149,7 +149,7 @@
    forwards (i.e. it is currently slow of true time).  Provided is also
    a suggested correction rate (correction time * offset). */
 
-extern void LCL_AccumulateOffset(double offset, double corr_rate);
+extern int LCL_AccumulateOffset(double offset, double corr_rate);
 
 /* Routine to apply an immediate offset by doing a sudden step if
    possible. (Intended for use after an initial estimate of offset has
@@ -171,7 +171,7 @@
 
 /* Perform the combination of modifying the frequency and applying
    a slew, in one easy step */
-extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
+extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
 
 /* Routine to read the system precision as a log to base 2 value. */
 extern int LCL_GetSysPrecisionAsLog(void);
@@ -197,6 +197,9 @@
    to a timezone problem. */
 extern int LCL_MakeStep(void);
 
+/* Routine to cancel the outstanding system clock correction */
+extern void LCL_CancelOffsetCorrection(void);
+
 /* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap
    does something */
 extern int LCL_CanSystemLeap(void);
diff --git a/chrony_3_3/localp.h b/chrony/localp.h
similarity index 100%
rename from chrony_3_3/localp.h
rename to chrony/localp.h
diff --git a/chrony_3_3/logging.c b/chrony/logging.c
similarity index 76%
rename from chrony_3_3/logging.c
rename to chrony/logging.c
index 7d9dbb8..d858d2a 100644
--- a/chrony_3_3/logging.c
+++ b/chrony/logging.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2011-2014
+ * Copyright (C) Miroslav Lichvar  2011-2014, 2018-2020
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -29,26 +29,25 @@
 
 #include "sysincl.h"
 
+#include <syslog.h>
+
 #include "conf.h"
 #include "logging.h"
+#include "memory.h"
 #include "util.h"
 
 /* This is used by DEBUG_LOG macro */
-int log_debug_enabled = 0;
+LOG_Severity log_min_severity = LOGS_INFO;
 
 /* ================================================== */
 /* Flag indicating we have initialised */
 static int initialised = 0;
 
-static FILE *file_log;
+static FILE *file_log = NULL;
 static int system_log = 0;
 
 static int parent_fd = 0;
 
-#define DEBUG_LEVEL_PRINT_FUNCTION 2
-#define DEBUG_LEVEL_PRINT_DEBUG 2
-static int debug_level = 0;
-
 struct LogFile {
   const char *name;
   const char *banner;
@@ -63,14 +62,18 @@
 
 static struct LogFile logfiles[MAX_FILELOGS];
 
+/* Global prefix for debug messages */
+static char *debug_prefix;
+
 /* ================================================== */
 /* Init function */
 
 void
 LOG_Initialise(void)
 {
+  debug_prefix = Strdup("");
   initialised = 1;
-  file_log = stderr;
+  LOG_OpenFileLog(NULL);
 }
 
 /* ================================================== */
@@ -87,6 +90,8 @@
 
   LOG_CycleLogFiles();
 
+  Free(debug_prefix);
+
   initialised = 0;
 }
 
@@ -132,17 +137,21 @@
   char buf[2048];
   va_list other_args;
   time_t t;
-  struct tm stm;
+  struct tm *tm;
 
-  if (!system_log && file_log) {
+  assert(initialised);
+
+  if (!system_log && file_log && severity >= log_min_severity) {
     /* Don't clutter up syslog with timestamps and internal debugging info */
     time(&t);
-    stm = *gmtime(&t);
-    strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
-    fprintf(file_log, "%s ", buf);
+    tm = gmtime(&t);
+    if (tm) {
+      strftime(buf, sizeof (buf), "%Y-%m-%dT%H:%M:%SZ", tm);
+      fprintf(file_log, "%s ", buf);
+    }
 #if DEBUG > 0
-    if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
-      fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
+    if (log_min_severity <= LOGS_DEBUG)
+      fprintf(file_log, "%s%s:%d:(%s) ", debug_prefix, filename, line_number, function_name);
 #endif
   }
 
@@ -155,10 +164,12 @@
     case LOGS_INFO:
     case LOGS_WARN:
     case LOGS_ERR:
-      log_message(0, severity, buf);
+      if (severity >= log_min_severity)
+        log_message(0, severity, buf);
       break;
     case LOGS_FATAL:
-      log_message(1, severity, buf);
+      if (severity >= log_min_severity)
+        log_message(1, severity, buf);
 
       /* Send the message also to the foreground process if it is
          still running, or stderr if it is still open */
@@ -169,6 +180,7 @@
         system_log = 0;
         log_message(1, severity, buf);
       }
+      exit(1);
       break;
     default:
       assert(0);
@@ -182,13 +194,18 @@
 {
   FILE *f;
 
-  f = fopen(log_file, "a");
-  if (!f)
-    LOG_FATAL("Could not open log file %s", log_file);
+  if (log_file) {
+    f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640);
+  } else {
+    f = stderr;
+  }
 
   /* Enable line buffering */
   setvbuf(f, NULL, _IOLBF, BUFSIZ);
 
+  if (file_log && file_log != stderr)
+    fclose(file_log);
+
   file_log = f;
 }
 
@@ -204,12 +221,27 @@
 
 /* ================================================== */
 
-void LOG_SetDebugLevel(int level)
+void LOG_SetMinSeverity(LOG_Severity severity)
 {
-  debug_level = level;
-  if (level >= DEBUG_LEVEL_PRINT_DEBUG) {
-    log_debug_enabled = 1;
-  }
+  /* Don't print any debug messages in a non-debug build */
+  log_min_severity = CLAMP(DEBUG > 0 ? LOGS_DEBUG : LOGS_INFO, severity, LOGS_FATAL);
+}
+
+/* ================================================== */
+
+LOG_Severity
+LOG_GetMinSeverity(void)
+{
+  return log_min_severity;
+}
+
+/* ================================================== */
+
+void
+LOG_SetDebugPrefix(const char *prefix)
+{
+  Free(debug_prefix);
+  debug_prefix = Strdup(prefix);
 }
 
 /* ================================================== */
@@ -237,7 +269,10 @@
 LOG_FileID
 LOG_FileOpen(const char *name, const char *banner)
 {
-  assert(n_filelogs < MAX_FILELOGS);
+  if (n_filelogs >= MAX_FILELOGS) {
+    assert(0);
+    return -1;
+  }
 
   logfiles[n_filelogs].name = name;
   logfiles[n_filelogs].banner = banner;
@@ -259,24 +294,20 @@
     return;
 
   if (!logfiles[id].file) {
-    char filename[512], *logdir = CNF_GetLogDir();
+    char *logdir = CNF_GetLogDir();
 
-    if (logdir[0] == '\0') {
+    if (!logdir) {
       LOG(LOGS_WARN, "logdir not specified");
       logfiles[id].name = NULL;
       return;
     }
 
-    if (snprintf(filename, sizeof(filename), "%s/%s.log",
-                 logdir, logfiles[id].name) >= sizeof (filename) ||
-        !(logfiles[id].file = fopen(filename, "a"))) {
-      LOG(LOGS_WARN, "Could not open log file %s", filename);
+    logfiles[id].file = UTI_OpenFile(logdir, logfiles[id].name, ".log", 'a', 0644);
+    if (!logfiles[id].file) {
+      /* Disable the log */
       logfiles[id].name = NULL;
       return;
     }
-
-    /* Close on exec */
-    UTI_FdSetCloexec(fileno(logfiles[id].file));
   }
 
   banner = CNF_GetLogBanner();
@@ -284,7 +315,7 @@
     char bannerline[256];
     int i, bannerlen;
 
-    bannerlen = strlen(logfiles[id].banner);
+    bannerlen = MIN(strlen(logfiles[id].banner), sizeof (bannerline) - 1);
 
     for (i = 0; i < bannerlen; i++)
       bannerline[i] = '=';
diff --git a/chrony_3_3/logging.h b/chrony/logging.h
similarity index 82%
rename from chrony_3_3/logging.h
rename to chrony/logging.h
index 5bb46f5..31bd462 100644
--- a/chrony_3_3/logging.h
+++ b/chrony/logging.h
@@ -31,9 +31,6 @@
 
 #include "sysincl.h"
 
-/* Flag indicating whether debug messages are logged */
-extern int log_debug_enabled;
-
 /* Line logging macros.  If the compiler is GNU C, we take advantage of
    being able to get the function name also. */
 
@@ -55,7 +52,7 @@
 
 #define DEBUG_LOG(...) \
   do { \
-    if (DEBUG && log_debug_enabled) \
+    if (DEBUG && log_min_severity == LOGS_DEBUG) \
       LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
   } while (0)
 
@@ -69,13 +66,16 @@
 
 /* Definition of severity */
 typedef enum {
-  LOGS_INFO,
+  LOGS_DEBUG = -1,
+  LOGS_INFO = 0,
   LOGS_WARN,
   LOGS_ERR,
   LOGS_FATAL,
-  LOGS_DEBUG
 } LOG_Severity;
 
+/* Minimum severity of messages to be logged */
+extern LOG_Severity log_min_severity;
+
 /* Init function */
 extern void LOG_Initialise(void);
 
@@ -92,14 +92,18 @@
 extern void LOG_Message(LOG_Severity severity, const char *format, ...);
 #endif
 
-/* Set debug level:
-   0, 1 - only non-debug messages are logged
-   2    - debug messages are logged too, all messages are prefixed with
-          filename, line, and function name
-   */
-extern void LOG_SetDebugLevel(int level);
+/* Set the minimum severity of a message to be logged or printed to terminal.
+   If the severity is LOGS_DEBUG and DEBUG is enabled, all messages will be
+   prefixed with the filename, line number, and function name. */
+extern void LOG_SetMinSeverity(LOG_Severity severity);
 
-/* Log messages to a file instead of stderr */
+/* Get the minimum severity */
+extern LOG_Severity LOG_GetMinSeverity(void);
+
+/* Set a prefix for debug messages */
+extern void LOG_SetDebugPrefix(const char *prefix);
+
+/* Log messages to a file instead of stderr, or stderr again if NULL */
 extern void LOG_OpenFileLog(const char *log_file);
 
 /* Log messages to syslog instead of stderr */
diff --git a/chrony_3_3/main.c b/chrony/main.c
similarity index 79%
rename from chrony_3_3/main.c
rename to chrony/main.c
index a2202e9..6a9463f 100644
--- a/chrony_3_3/main.c
+++ b/chrony/main.c
@@ -4,7 +4,7 @@
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
  * Copyright (C) John G. Hasler  2009
- * Copyright (C) Miroslav Lichvar  2012-2017
+ * Copyright (C) Miroslav Lichvar  2012-2020
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -38,6 +38,9 @@
 #include "ntp_signd.h"
 #include "ntp_sources.h"
 #include "ntp_core.h"
+#include "nts_ke_server.h"
+#include "nts_ntp_server.h"
+#include "socket.h"
 #include "sources.h"
 #include "sourcestats.h"
 #include "reference.h"
@@ -73,11 +76,18 @@
 static void
 do_platform_checks(void)
 {
+  struct timespec ts;
+
   /* Require at least 32-bit integers, two's complement representation and
      the usual implementation of conversion of unsigned integers */
   assert(sizeof (int) >= 4);
   assert(-1 == ~0);
   assert((int32_t)4294967295U == (int32_t)-1);
+
+  /* Require time_t and tv_nsec in timespec to be signed */
+  ts.tv_sec = -1;
+  ts.tv_nsec = -1;
+  assert(ts.tv_sec < 0 && ts.tv_nsec < 0);
 }
 
 /* ================================================== */
@@ -87,11 +97,11 @@
 {
   const char *pidfile = CNF_GetPidFile();
 
-  if (!pidfile[0])
+  if (!pidfile)
     return;
 
-  /* Don't care if this fails, there's not a lot we can do */
-  unlink(pidfile);
+  if (!UTI_RemoveFile(NULL, pidfile, NULL))
+    ;
 }
 
 /* ================================================== */
@@ -101,9 +111,8 @@
 {
   if (!initialised) exit(exit_status);
   
-  if (CNF_GetDumpDir()[0] != '\0') {
-    SRC_DumpSources();
-  }
+  LCL_CancelOffsetCorrection();
+  SRC_DumpSources();
 
   /* Don't update clock when removing sources */
   REF_SetMode(REF_ModeIgnore);
@@ -112,18 +121,23 @@
   TMC_Finalise();
   MNL_Finalise();
   CLG_Finalise();
+  NKS_Finalise();
+  NNS_Finalise();
   NSD_Finalise();
   NSR_Finalise();
   SST_Finalise();
   NCR_Finalise();
   NIO_Finalise();
   CAM_Finalise();
+
   KEY_Finalise();
   RCL_Finalise();
   SRC_Finalise();
   REF_Finalise();
   RTC_Finalise();
   SYS_Finalise();
+
+  SCK_Finalise();
   SCH_Finalise();
   LCL_Finalise();
   PRV_Finalise();
@@ -134,6 +148,8 @@
   HSH_Finalise();
   LOG_Finalise();
 
+  UTI_ResetGetRandomFunctions();
+
   exit(exit_status);
 }
 
@@ -142,7 +158,6 @@
 static void
 signal_cleanup(int x)
 {
-  if (!initialised) exit(0);
   SCH_QuitProgram();
 }
 
@@ -178,7 +193,7 @@
   NSR_AutoStartSources();
 
   /* Special modes can end only when sources update their reachability.
-     Give up immediatelly if there are no active sources. */
+     Give up immediately if there are no active sources. */
   if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
     REF_SetUnsynchronised();
   }
@@ -253,7 +268,10 @@
   FILE *in;
   int pid, count;
   
-  in = fopen(pidfile, "r");
+  if (!pidfile)
+    return;
+
+  in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
   if (!in)
     return;
 
@@ -278,16 +296,12 @@
   const char *pidfile = CNF_GetPidFile();
   FILE *out;
 
-  if (!pidfile[0])
+  if (!pidfile)
     return;
 
-  out = fopen(pidfile, "w");
-  if (!out) {
-    LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno));
-  } else {
-    fprintf(out, "%d\n", (int)getpid());
-    fclose(out);
-  }
+  out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
+  fprintf(out, "%d\n", (int)getpid());
+  fclose(out);
 }
 
 /* ================================================== */
@@ -370,8 +384,34 @@
 static void
 print_help(const char *progname)
 {
-      printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
-             progname);
+      printf("Usage: %s [OPTION]... [DIRECTIVE]...\n\n"
+             "Options:\n"
+             "  -4\t\tUse IPv4 addresses only\n"
+             "  -6\t\tUse IPv6 addresses only\n"
+             "  -f FILE\tSpecify configuration file (%s)\n"
+             "  -n\t\tDon't run as daemon\n"
+             "  -d\t\tDon't run as daemon and log to stderr\n"
+#if DEBUG > 0
+             "  -d -d\t\tEnable debug messages\n"
+#endif
+             "  -l FILE\tLog to file\n"
+             "  -L LEVEL\tSet logging threshold (0)\n"
+             "  -p\t\tPrint configuration and exit\n"
+             "  -q\t\tSet clock and exit\n"
+             "  -Q\t\tLog offset and exit\n"
+             "  -r\t\tReload dump files\n"
+             "  -R\t\tAdapt configuration for restart\n"
+             "  -s\t\tSet clock from RTC\n"
+             "  -t SECONDS\tExit after elapsed time\n"
+             "  -u USER\tSpecify user (%s)\n"
+             "  -U\t\tDon't check for root\n"
+             "  -F LEVEL\tSet system call filter level (0)\n"
+             "  -P PRIORITY\tSet process priority (0)\n"
+             "  -m\t\tLock memory\n"
+             "  -x\t\tDon't control clock\n"
+             "  -v, --version\tPrint version and exit\n"
+             "  -h, --help\tPrint usage and exit\n",
+             progname, DEFAULT_CONF_FILE, DEFAULT_USER);
 }
 
 /* ================================================== */
@@ -404,16 +444,16 @@
   char *user = NULL, *log_file = NULL;
   struct passwd *pw;
   int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
-  int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
+  int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
   int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
-  int clock_control = 1, system_log = 1;
-  int config_args = 0;
+  int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
+  int user_check = 1, config_args = 0, print_config = 0;
 
   do_platform_checks();
 
   LOG_Initialise();
 
-  /* Parse (undocumented) long command-line options */
+  /* Parse long command-line options */
   for (optind = 1; optind < argc; optind++) {
     if (!strcmp("--help", argv[optind])) {
       print_help(progname);
@@ -427,7 +467,7 @@
   optind = 1;
 
   /* Parse short command-line options */
-  while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
+  while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:Uvx")) != -1) {
     switch (opt) {
       case '4':
       case '6':
@@ -447,12 +487,22 @@
       case 'l':
         log_file = optarg;
         break;
+      case 'L':
+        log_severity = parse_int_arg(optarg);
+        break;
       case 'm':
         lock_memory = 1;
         break;
       case 'n':
         nofork = 1;
         break;
+      case 'p':
+        print_config = 1;
+        user_check = 0;
+        nofork = 1;
+        system_log = 0;
+        log_severity = LOGS_WARN;
+        break;
       case 'P':
         sched_priority = parse_int_arg(optarg);
         break;
@@ -466,6 +516,7 @@
         ref_mode = REF_ModePrintOnce;
         nofork = 1;
         client_only = 1;
+        user_check = 0;
         clock_control = 0;
         system_log = 0;
         break;
@@ -484,6 +535,9 @@
       case 'u':
         user = optarg;
         break;
+      case 'U':
+        user_check = 0;
+        break;
       case 'v':
         print_version();
         return 0;
@@ -496,7 +550,7 @@
     }
   }
 
-  if (getuid() && !client_only)
+  if (user_check && getuid() != 0)
     LOG_FATAL("Not superuser");
 
   /* Turn into a daemon */
@@ -510,13 +564,15 @@
     LOG_OpenSystemLog();
   }
   
-  LOG_SetDebugLevel(debug);
+  LOG_SetMinSeverity(debug >= 2 ? LOGS_DEBUG : log_severity);
   
   LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
 
   DNS_SetAddressFamily(address_family);
 
   CNF_Initialise(restarted, client_only);
+  if (print_config)
+    CNF_EnablePrint();
 
   /* Parse the config file or the remaining command line arguments */
   config_args = argc - optind;
@@ -527,12 +583,12 @@
       CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
   }
 
+  if (print_config)
+    return 0;
+
   /* Check whether another chronyd may already be running */
   check_pidfile();
 
-  /* Write our pidfile to prevent other chronyds running */
-  write_pidfile();
-
   if (!user)
     user = CNF_GetUser();
 
@@ -543,9 +599,17 @@
   /* Create directories for sockets, log files, and dump files */
   CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
 
+  /* Write our pidfile to prevent other instances from running */
+  write_pidfile();
+
   PRV_Initialise();
   LCL_Initialise();
   SCH_Initialise();
+  SCK_Initialise(address_family);
+
+  /* Start helper processes if needed */
+  NKS_PreInitialise(pw->pw_uid, pw->pw_gid, scfilter_level);
+
   SYS_Initialise(clock_control);
   RTC_Initialise(do_init_rtc);
   SRC_Initialise();
@@ -553,8 +617,8 @@
   KEY_Initialise();
 
   /* Open privileged ports before dropping root */
-  CAM_Initialise(address_family);
-  NIO_Initialise(address_family);
+  CAM_Initialise();
+  NIO_Initialise();
   NCR_Initialise();
   CNF_SetupAccessRestrictions();
 
@@ -572,12 +636,17 @@
 
   /* Drop root privileges if the specified user has a non-zero UID */
   if (!geteuid() && (pw->pw_uid || pw->pw_gid))
-    SYS_DropRoot(pw->pw_uid, pw->pw_gid);
+    SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
+
+  if (!geteuid())
+    LOG(LOGS_WARN, "Running with root privileges");
 
   REF_Initialise();
   SST_Initialise();
   NSR_Initialise();
   NSD_Initialise();
+  NNS_Initialise();
+  NKS_Initialise();
   CLG_Initialise();
   MNL_Initialise();
   TMC_Initialise();
@@ -586,12 +655,12 @@
   /* From now on, it is safe to do finalisation on exit */
   initialised = 1;
 
-  UTI_SetQuitSignalsHandler(signal_cleanup);
+  UTI_SetQuitSignalsHandler(signal_cleanup, 1);
 
   CAM_OpenUnixSocket();
 
   if (scfilter_level)
-    SYS_EnableSystemCallFilter(scfilter_level);
+    SYS_EnableSystemCallFilter(scfilter_level, SYS_MAIN_PROCESS);
 
   if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
     ref_mode = REF_ModeInitStepSlew;
@@ -600,7 +669,7 @@
   REF_SetModeEndHandler(reference_mode_end);
   REF_SetMode(ref_mode);
 
-  if (timeout > 0)
+  if (timeout >= 0)
     SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
 
   if (do_init_rtc) {
diff --git a/chrony_3_3/main.h b/chrony/main.h
similarity index 100%
rename from chrony_3_3/main.h
rename to chrony/main.h
diff --git a/chrony_3_3/manual.c b/chrony/manual.c
similarity index 98%
rename from chrony_3_3/manual.c
rename to chrony/manual.c
index bf52d14..98a3aa2 100644
--- a/chrony_3_3/manual.c
+++ b/chrony/manual.c
@@ -92,6 +92,7 @@
 void
 MNL_Finalise(void)
 {
+  LCL_RemoveParameterChangeHandler(slew_samples, NULL);
 }
 
 /* ================================================== */
diff --git a/chrony_3_3/manual.h b/chrony/manual.h
similarity index 100%
rename from chrony_3_3/manual.h
rename to chrony/manual.h
diff --git a/chrony_3_3/md5.c b/chrony/md5.c
similarity index 100%
rename from chrony_3_3/md5.c
rename to chrony/md5.c
diff --git a/chrony_3_3/md5.h b/chrony/md5.h
similarity index 100%
rename from chrony_3_3/md5.h
rename to chrony/md5.h
diff --git a/chrony_3_3/memory.c b/chrony/memory.c
similarity index 100%
rename from chrony_3_3/memory.c
rename to chrony/memory.c
diff --git a/chrony_3_3/memory.h b/chrony/memory.h
similarity index 98%
rename from chrony_3_3/memory.h
rename to chrony/memory.h
index 110cf6a..7ae2c03 100644
--- a/chrony_3_3/memory.h
+++ b/chrony/memory.h
@@ -27,6 +27,8 @@
 #ifndef GOT_MEMORY_H
 #define GOT_MEMORY_H
 
+#include "sysincl.h"
+
 /* Wrappers checking for errors */
 extern void *Malloc(size_t size);
 extern void *Realloc(void *ptr, size_t size);
diff --git a/chrony_3_3/nameserv.c b/chrony/nameserv.c
similarity index 70%
rename from chrony_3_3/nameserv.c
rename to chrony/nameserv.c
index 1cb9608..9f7e648 100644
--- a/chrony_3_3/nameserv.c
+++ b/chrony/nameserv.c
@@ -30,7 +30,11 @@
 
 #include "sysincl.h"
 
+#include <netdb.h>
+#include <resolv.h>
+
 #include "nameserv.h"
+#include "socket.h"
 #include "util.h"
 
 /* ================================================== */
@@ -46,12 +50,24 @@
 DNS_Status 
 DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
 {
-#ifdef HAVE_GETADDRINFO
   struct addrinfo hints, *res, *ai;
   int i, result;
+  IPAddr ip;
 
   max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
 
+  for (i = 0; i < max_addrs; i++)
+    ip_addrs[i].family = IPADDR_UNSPEC;
+
+  /* Avoid calling getaddrinfo() if the name is an IP address */
+  if (UTI_StringToIP(name, &ip)) {
+    if (address_family != IPADDR_UNSPEC && ip.family != address_family)
+      return DNS_Failure;
+    if (max_addrs >= 1)
+      ip_addrs[0] = ip;
+    return DNS_Success;
+  }
+
   memset(&hints, 0, sizeof (hints));
 
   switch (address_family) {
@@ -66,7 +82,7 @@
     default:
       hints.ai_family = AF_UNSPEC;
   }
-  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_socktype = SOCK_DGRAM;
 
   result = getaddrinfo(name, NULL, &hints, &res);
 
@@ -91,6 +107,9 @@
       case AF_INET6:
         if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6)
           continue;
+        /* Don't return an address that would lose a scope ID */
+        if (((struct sockaddr_in6 *)ai->ai_addr)->sin6_scope_id != 0)
+          continue;
         ip_addrs[i].family = IPADDR_INET6;
         memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
                sizeof (ip_addrs->addr.in6));
@@ -100,48 +119,9 @@
     }
   }
 
-  for (; i < max_addrs; i++)
-        ip_addrs[i].family = IPADDR_UNSPEC;
-
   freeaddrinfo(res);
 
   return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure;
-#else
-  struct hostent *host;
-  int i;
-  
-  if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4)
-    return DNS_Failure;
-
-  max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
-
-  host = gethostbyname(name);
-
-  if (host == NULL) {
-    if (h_errno == TRY_AGAIN)
-      return DNS_TryAgain;
-  } else {
-    if (host->h_addrtype != AF_INET || !host->h_addr_list[0])
-      return DNS_Failure;
-
-    for (i = 0; host->h_addr_list[i] && i < max_addrs; i++) {
-      ip_addrs[i].family = IPADDR_INET4;
-      ip_addrs[i].addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[i]);
-    }
-
-    for (; i < max_addrs; i++)
-      ip_addrs[i].family = IPADDR_UNSPEC;
-
-    return DNS_Success;
-  }
-
-#ifdef FORCE_DNSRETRY
-  return DNS_TryAgain;
-#else
-  return DNS_Failure;
-#endif
-
-#endif
 }
 
 /* ================================================== */
@@ -150,35 +130,21 @@
 DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
 {
   char *result = NULL;
-
 #ifdef FEAT_IPV6
-  struct sockaddr_in6 in6;
+  struct sockaddr_in6 saddr;
+#else
+  struct sockaddr_in saddr;
+#endif
+  IPSockAddr ip_saddr;
   socklen_t slen;
   char hbuf[NI_MAXHOST];
 
-  slen = UTI_IPAndPortToSockaddr(ip_addr, 0, (struct sockaddr *)&in6);
-  if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
-    result = hbuf;
-#else
-  struct hostent *host;
-  uint32_t addr;
+  ip_saddr.ip_addr = *ip_addr;
+  ip_saddr.port = 0;
 
-  switch (ip_addr->family) {
-    case IPADDR_INET4:
-      addr = htonl(ip_addr->addr.in4);
-      host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET);
-      break;
-#ifdef FEAT_IPV6
-    case IPADDR_INET6:
-      host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6);
-      break;
-#endif
-    default:
-      host = NULL;
-  }
-  if (host)
-    result = host->h_name;
-#endif
+  slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&saddr, sizeof (saddr));
+  if (!getnameinfo((struct sockaddr *)&saddr, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
+    result = hbuf;
 
   if (result == NULL)
     result = UTI_IPToString(ip_addr);
diff --git a/chrony_3_3/nameserv.h b/chrony/nameserv.h
similarity index 100%
rename from chrony_3_3/nameserv.h
rename to chrony/nameserv.h
diff --git a/chrony_3_3/nameserv_async.c b/chrony/nameserv_async.c
similarity index 95%
rename from chrony_3_3/nameserv_async.c
rename to chrony/nameserv_async.c
index 9ad3d17..118443c 100644
--- a/chrony_3_3/nameserv_async.c
+++ b/chrony/nameserv_async.c
@@ -51,7 +51,7 @@
   int pipe[2];
 };
 
-static int resolving_threads = 0;
+static pthread_mutex_t privops_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /* ================================================== */
 
@@ -60,7 +60,9 @@
 {
   struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
 
+  pthread_mutex_lock(&privops_lock);
   inst->status = PRV_Name2IPAddress(inst->name, inst->addresses, DNS_MAX_ADDRESSES);
+  pthread_mutex_unlock(&privops_lock);
 
   /* Notify the main thread that the result is ready */
   if (write(inst->pipe[1], "", 1) < 0)
@@ -81,8 +83,6 @@
     LOG_FATAL("pthread_join() failed");
   }
 
-  resolving_threads--;
-
   SCH_RemoveFileHandler(inst->pipe[0]);
   close(inst->pipe[0]);
   close(inst->pipe[1]);
@@ -116,9 +116,6 @@
   UTI_FdSetCloexec(inst->pipe[0]);
   UTI_FdSetCloexec(inst->pipe[1]);
 
-  resolving_threads++;
-  assert(resolving_threads <= 1);
-
   if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
     LOG_FATAL("pthread_create() failed");
   }
diff --git a/chrony_3_3/nameserv_async.h b/chrony/nameserv_async.h
similarity index 100%
rename from chrony_3_3/nameserv_async.h
rename to chrony/nameserv_async.h
diff --git a/chrony/ntp.h b/chrony/ntp.h
new file mode 100644
index 0000000..52b2ab5
--- /dev/null
+++ b/chrony/ntp.h
@@ -0,0 +1,182 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file containing common NTP bits and pieces
+  */
+
+#ifndef GOT_NTP_H
+#define GOT_NTP_H
+
+#include "sysincl.h"
+
+#include "hash.h"
+
+typedef struct {
+  uint32_t hi;
+  uint32_t lo;
+} NTP_int64;
+
+typedef uint32_t NTP_int32;
+
+/* The UDP port number used by NTP */
+#define NTP_PORT 123
+
+/* The NTP protocol version that we support */
+#define NTP_VERSION 4
+
+/* Maximum stratum number (infinity) */
+#define NTP_MAX_STRATUM 16
+
+/* Invalid stratum number */
+#define NTP_INVALID_STRATUM 0
+
+/* The minimum and maximum supported length of MAC */
+#define NTP_MIN_MAC_LENGTH (4 + 16)
+#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
+
+/* The minimum valid length of an extension field */
+#define NTP_MIN_EF_LENGTH 16
+
+/* The maximum assumed length of all extension fields in an NTP packet,
+   including a MAC (RFC 5905 doesn't specify a limit on length or number of
+   extension fields in one packet) */
+#define NTP_MAX_EXTENSIONS_LENGTH (1024 + NTP_MAX_MAC_LENGTH)
+
+/* The maximum length of MAC in NTPv4 packets which allows deterministic
+   parsing of extension fields (RFC 7822) */
+#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
+
+/* Type definition for leap bits */
+typedef enum {
+  LEAP_Normal = 0,
+  LEAP_InsertSecond = 1,
+  LEAP_DeleteSecond = 2,
+  LEAP_Unsynchronised = 3
+} NTP_Leap;
+
+typedef enum {
+  MODE_UNDEFINED = 0,
+  MODE_ACTIVE = 1,
+  MODE_PASSIVE = 2,
+  MODE_CLIENT = 3,
+  MODE_SERVER = 4,
+  MODE_BROADCAST = 5
+} NTP_Mode;
+
+typedef struct {
+  uint8_t lvm;
+  uint8_t stratum;
+  int8_t poll;
+  int8_t precision;
+  NTP_int32 root_delay;
+  NTP_int32 root_dispersion;
+  NTP_int32 reference_id;
+  NTP_int64 reference_ts;
+  NTP_int64 originate_ts;
+  NTP_int64 receive_ts;
+  NTP_int64 transmit_ts;
+
+  uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
+} NTP_Packet;
+
+#define NTP_HEADER_LENGTH (int)offsetof(NTP_Packet, extensions)
+
+/* Macros to work with the lvm field */
+#define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3)
+#define NTP_LVM_TO_VERSION(lvm) (((lvm) >> 3) & 0x7)
+#define NTP_LVM_TO_MODE(lvm) ((lvm) & 0x7)
+#define NTP_LVM(leap, version, mode) \
+  ((((leap) << 6) & 0xc0) | (((version) << 3) & 0x38) | ((mode) & 0x07))
+
+/* Special NTP reference IDs */
+#define NTP_REFID_UNSYNC 0x0UL
+#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
+#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
+
+/* Non-authentication extension fields and corresponding internal flags */
+
+#define NTP_EF_EXP1                     0xF323
+
+#define NTP_EF_FLAG_EXP1                0x1
+
+/* Pre-NTPv5 experimental extension field */
+typedef struct {
+  uint32_t magic;
+  NTP_int32 root_delay;
+  NTP_int32 root_dispersion;
+  NTP_int64 mono_receive_ts;
+  uint32_t mono_epoch;
+} NTP_ExtFieldExp1;
+
+#define NTP_EF_EXP1_MAGIC 0xF5BEDD9AU
+
+/* Authentication extension fields */
+
+#define NTP_EF_NTS_UNIQUE_IDENTIFIER    0x0104
+#define NTP_EF_NTS_COOKIE               0x0204
+#define NTP_EF_NTS_COOKIE_PLACEHOLDER   0x0304
+#define NTP_EF_NTS_AUTH_AND_EEF         0x0404
+
+/* Enumeration for authentication modes of NTP packets */
+typedef enum {
+  NTP_AUTH_NONE = 0,            /* No authentication */
+  NTP_AUTH_SYMMETRIC,           /* NTP MAC or CMAC using a symmetric key
+                                   (RFC 1305, RFC 5905, RFC 8573) */
+  NTP_AUTH_MSSNTP,              /* MS-SNTP authenticator field */
+  NTP_AUTH_MSSNTP_EXT,          /* MS-SNTP extended authenticator field */
+  NTP_AUTH_NTS,                 /* Network Time Security (RFC 8915) */
+} NTP_AuthMode;
+
+/* Structure describing an NTP packet */
+typedef struct {
+  int length;
+  int version;
+  NTP_Mode mode;
+
+  int ext_fields;
+  int ext_field_flags;
+
+  struct {
+    NTP_AuthMode mode;
+    struct {
+      int start;
+      int length;
+      uint32_t key_id;
+    } mac;
+  } auth;
+} NTP_PacketInfo;
+
+/* Structure used to save NTP measurements.  time is the local time at which
+   the sample is to be considered to have been made and offset is the offset at
+   the time (positive indicates that the local clock is slow relative to the
+   source).  root_delay/root_dispersion include peer_delay/peer_dispersion. */
+typedef struct {
+  struct timespec time;
+  double offset;
+  double peer_delay;
+  double peer_dispersion;
+  double root_delay;
+  double root_dispersion;
+} NTP_Sample;
+
+#endif /* GOT_NTP_H */
diff --git a/chrony/ntp_auth.c b/chrony/ntp_auth.c
new file mode 100644
index 0000000..58374c5
--- /dev/null
+++ b/chrony/ntp_auth.c
@@ -0,0 +1,386 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019-2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  NTP authentication
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "keys.h"
+#include "logging.h"
+#include "memory.h"
+#include "ntp_auth.h"
+#include "ntp_signd.h"
+#include "nts_ntp.h"
+#include "nts_ntp_client.h"
+#include "nts_ntp_server.h"
+#include "srcparams.h"
+#include "util.h"
+
+/* Structure to hold authentication configuration and state */
+
+struct NAU_Instance_Record {
+  NTP_AuthMode mode;            /* Authentication mode of NTP packets */
+  uint32_t key_id;              /* Identifier of a symmetric key */
+  NNC_Instance nts;             /* Client NTS state */
+};
+
+/* ================================================== */
+
+static int
+generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info)
+{
+  int auth_len, max_auth_len;
+
+  if (info->length + NTP_MIN_MAC_LENGTH > sizeof (*packet)) {
+    DEBUG_LOG("Packet too long");
+    return 0;
+  }
+
+  /* Truncate long MACs in NTPv4 packets to allow deterministic parsing
+     of extension fields (RFC 7822) */
+  max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4;
+  max_auth_len = MIN(max_auth_len, sizeof (*packet) - info->length - 4);
+
+  auth_len = KEY_GenerateAuth(key_id, packet, info->length,
+                              (unsigned char *)packet + info->length + 4, max_auth_len);
+  if (auth_len < NTP_MIN_MAC_LENGTH - 4) {
+    DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
+    return 0;
+  }
+
+  *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
+
+  info->auth.mac.start = info->length;
+  info->auth.mac.length = 4 + auth_len;
+  info->auth.mac.key_id = key_id;
+  info->length += info->auth.mac.length;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
+{
+  int trunc_len;
+
+  if (info->auth.mac.length < NTP_MIN_MAC_LENGTH)
+    return 0;
+
+  trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ?
+              NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH;
+
+  if (!KEY_CheckAuth(info->auth.mac.key_id, packet, info->auth.mac.start,
+                     (unsigned char *)packet + info->auth.mac.start + 4,
+                     info->auth.mac.length - 4, trunc_len - 4))
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static NAU_Instance
+create_instance(NTP_AuthMode mode)
+{
+  NAU_Instance instance;
+
+  instance = MallocNew(struct NAU_Instance_Record);
+  instance->mode = mode;
+  instance->key_id = INACTIVE_AUTHKEY;
+  instance->nts = NULL;
+
+  assert(sizeof (instance->key_id) == 4);
+
+  return instance;
+}
+
+/* ================================================== */
+
+NAU_Instance
+NAU_CreateNoneInstance(void)
+{
+  return create_instance(NTP_AUTH_NONE);
+}
+
+/* ================================================== */
+
+NAU_Instance
+NAU_CreateSymmetricInstance(uint32_t key_id)
+{
+  NAU_Instance instance = create_instance(NTP_AUTH_SYMMETRIC);
+
+  instance->key_id = key_id;
+
+  if (!KEY_KeyKnown(key_id))
+    LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "missing");
+  else if (!KEY_CheckKeyLength(key_id))
+    LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "too short");
+
+  return instance;
+}
+
+/* ================================================== */
+
+NAU_Instance
+NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set,
+                      uint16_t ntp_port)
+{
+  NAU_Instance instance = create_instance(NTP_AUTH_NTS);
+
+  instance->nts = NNC_CreateInstance(nts_address, name, cert_set, ntp_port);
+
+  return instance;
+}
+
+/* ================================================== */
+
+void
+NAU_DestroyInstance(NAU_Instance instance)
+{
+  if (instance->mode == NTP_AUTH_NTS)
+    NNC_DestroyInstance(instance->nts);
+  Free(instance);
+}
+
+/* ================================================== */
+
+int
+NAU_IsAuthEnabled(NAU_Instance instance)
+{
+  return instance->mode != NTP_AUTH_NONE;
+}
+
+/* ================================================== */
+
+int
+NAU_GetSuggestedNtpVersion(NAU_Instance instance)
+{
+  /* If the MAC in NTPv4 packets would be truncated, prefer NTPv3 for
+     compatibility with older chronyd servers */
+  if (instance->mode == NTP_AUTH_SYMMETRIC &&
+      KEY_GetAuthLength(instance->key_id) + sizeof (instance->key_id) > NTP_MAX_V4_MAC_LENGTH)
+    return 3;
+
+  return NTP_VERSION;
+}
+
+/* ================================================== */
+
+int
+NAU_PrepareRequestAuth(NAU_Instance instance)
+{
+  switch (instance->mode) {
+    case NTP_AUTH_NTS:
+      if (!NNC_PrepareForAuth(instance->nts))
+        return 0;
+      break;
+    default:
+      break;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info)
+{
+  switch (instance->mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      if (!generate_symmetric_auth(instance->key_id, request, info))
+        return 0;
+      break;
+    case NTP_AUTH_NTS:
+      if (!NNC_GenerateRequestAuth(instance->nts, request, info))
+        return 0;
+      break;
+    default:
+      assert(0);
+  }
+
+  info->auth.mode = instance->mode;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
+{
+  *kod = 0;
+
+  switch (info->auth.mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      if (!check_symmetric_auth(request, info))
+        return 0;
+      break;
+    case NTP_AUTH_MSSNTP:
+      /* MS-SNTP requests are not authenticated */
+      break;
+    case NTP_AUTH_MSSNTP_EXT:
+      /* Not supported yet */
+      return 0;
+    case NTP_AUTH_NTS:
+      if (!NNS_CheckRequestAuth(request, info, kod))
+        return 0;
+      break;
+    default:
+      return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
+                         NTP_Packet *response, NTP_PacketInfo *response_info,
+                         NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+                         uint32_t kod)
+{
+  switch (request_info->auth.mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      if (!generate_symmetric_auth(request_info->auth.mac.key_id, response, response_info))
+        return 0;
+      break;
+    case NTP_AUTH_MSSNTP:
+      /* Sign the packet asynchronously by ntp_signd */
+      if (!NSD_SignAndSendPacket(request_info->auth.mac.key_id, response, response_info,
+                                 remote_addr, local_addr))
+        return 0;
+      /* Don't send the original packet */
+      return 0;
+    case NTP_AUTH_NTS:
+      if (!NNS_GenerateResponseAuth(request, request_info, response, response_info, kod))
+        return 0;
+      break;
+    default:
+      DEBUG_LOG("Could not authenticate response auth_mode=%d", (int)request_info->auth.mode);
+      return 0;
+  }
+
+  response_info->auth.mode = request_info->auth.mode;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response, NTP_PacketInfo *info)
+{
+  /* The authentication must match the expected mode */
+  if (info->auth.mode != instance->mode)
+    return 0;
+
+  switch (info->auth.mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      /* Check if it is authenticated with the specified key */
+      if (info->auth.mac.key_id != instance->key_id)
+        return 0;
+      /* and that the MAC is valid */
+      if (!check_symmetric_auth(response, info))
+        return 0;
+      break;
+    case NTP_AUTH_NTS:
+      if (!NNC_CheckResponseAuth(instance->nts, response, info))
+        return 0;
+      break;
+    default:
+      return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+NAU_ChangeAddress(NAU_Instance instance, IPAddr *address)
+{
+  switch (instance->mode) {
+    case NTP_AUTH_NONE:
+    case NTP_AUTH_SYMMETRIC:
+      break;
+    case NTP_AUTH_NTS:
+      NNC_ChangeAddress(instance->nts, address);
+      break;
+    default:
+      assert(0);
+  }
+}
+
+/* ================================================== */
+
+void
+NAU_DumpData(NAU_Instance instance)
+{
+  switch (instance->mode) {
+    case NTP_AUTH_NTS:
+      NNC_DumpData(instance->nts);
+      break;
+    default:
+      break;
+  }
+}
+
+/* ================================================== */
+
+void
+NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report)
+{
+  memset(report, 0, sizeof (*report));
+
+  report->mode = instance->mode;
+  report->last_ke_ago = -1;
+
+  switch (instance->mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+      report->key_id = instance->key_id;
+      KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length);
+      break;
+    case NTP_AUTH_NTS:
+      NNC_GetReport(instance->nts, report);
+      break;
+    default:
+      assert(0);
+  }
+}
diff --git a/chrony/ntp_auth.h b/chrony/ntp_auth.h
new file mode 100644
index 0000000..0b8a825
--- /dev/null
+++ b/chrony/ntp_auth.h
@@ -0,0 +1,84 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for NTP authentication
+  */
+
+#ifndef GOT_NTP_AUTH_H
+#define GOT_NTP_AUTH_H
+
+#include "addressing.h"
+#include "ntp.h"
+#include "reports.h"
+
+typedef struct NAU_Instance_Record *NAU_Instance;
+
+/* Create an authenticator instance in a specific mode */
+extern NAU_Instance NAU_CreateNoneInstance(void);
+extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id);
+extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name,
+                                          uint32_t cert_set, uint16_t ntp_port);
+
+/* Destroy an instance */
+extern void NAU_DestroyInstance(NAU_Instance instance);
+
+/* Check if an instance is not in the None mode */
+extern int NAU_IsAuthEnabled(NAU_Instance instance);
+
+/* Get NTP version recommended for better compatibility */
+extern int NAU_GetSuggestedNtpVersion(NAU_Instance instance);
+
+/* Perform operations necessary for NAU_GenerateRequestAuth() */
+extern int NAU_PrepareRequestAuth(NAU_Instance instance);
+
+/* Extend a request with data required by the authentication mode */
+extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
+                                   NTP_PacketInfo *info);
+
+/* Verify that a request is authentic.  If it is not authentic and a non-zero
+   kod code is returned, a KoD response should be sent back. */
+extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);
+
+/* Extend a response with data required by the authentication mode.  This
+   function can be called only if the previous call of NAU_CheckRequestAuth()
+   was on the same request. */
+extern int NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
+                                    NTP_Packet *response, NTP_PacketInfo *response_info,
+                                    NTP_Remote_Address *remote_addr,
+                                    NTP_Local_Address *local_addr,
+                                    uint32_t kod);
+
+/* Verify that a response is authentic */
+extern int NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response,
+                                 NTP_PacketInfo *info);
+
+/* Change an authentication-specific address (e.g. after replacing a source) */
+extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
+
+/* Save authentication-specific data to speed up the next start */
+extern void NAU_DumpData(NAU_Instance instance);
+
+/* Provide a report about the current authentication state */
+extern void NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report);
+
+#endif
diff --git a/chrony_3_3/ntp_core.c b/chrony/ntp_core.c
similarity index 71%
rename from chrony_3_3/ntp_core.c
rename to chrony/ntp_core.c
index c82d792..b73309e 100644
--- a/chrony_3_3/ntp_core.c
+++ b/chrony/ntp_core.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2009-2018
+ * Copyright (C) Miroslav Lichvar  2009-2021
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,19 +30,20 @@
 #include "sysincl.h"
 
 #include "array.h"
+#include "ntp_auth.h"
 #include "ntp_core.h"
+#include "ntp_ext.h"
 #include "ntp_io.h"
-#include "ntp_signd.h"
 #include "memory.h"
 #include "sched.h"
 #include "reference.h"
 #include "local.h"
+#include "samplefilt.h"
 #include "smooth.h"
 #include "sources.h"
 #include "util.h"
 #include "conf.h"
 #include "logging.h"
-#include "keys.h"
 #include "addrfilt.h"
 #include "clientlog.h"
 
@@ -63,16 +64,6 @@
 } OperatingMode;
 
 /* ================================================== */
-/* Enumeration for authentication modes of NTP packets */
-
-typedef enum {
-  AUTH_NONE = 0,                /* No authentication */
-  AUTH_SYMMETRIC,               /* MAC using symmetric key (RFC 1305, RFC 5905) */
-  AUTH_MSSNTP,                  /* MS-SNTP authenticator field */
-  AUTH_MSSNTP_EXT,              /* MS-SNTP extended authenticator field */
-} AuthenticationMode;
-
-/* ================================================== */
 /* Structure used for holding a single peer/server's
    protocol machine */
 
@@ -88,15 +79,18 @@
   SCH_TimeoutID tx_timeout_id;  /* Timeout ID for next transmission */
   int tx_suspended;             /* Boolean indicating we can't transmit yet */
 
+  int auto_iburst;              /* If 1, initiate a burst when going online */
   int auto_burst;               /* If 1, initiate a burst on each poll */
-  int auto_offline;             /* If 1, automatically go offline if server/peer
-                                   isn't responding */
+  int auto_offline;             /* If 1, automatically go offline when requests
+                                   cannot be sent */
 
   int local_poll;               /* Log2 of polling interval at our end */
   int remote_poll;              /* Log2 of server/peer's polling interval (recovered
                                    from received packets) */
   int remote_stratum;           /* Stratum of the server/peer (recovered from
                                    received packets) */
+  double remote_root_delay;     /* Root delay from last valid packet */
+  double remote_root_dispersion;/* Root dispersion from last valid packet */
 
   int presend_minpoll;           /* If the current polling interval is
                                     at least this, an extra client packet
@@ -116,6 +110,8 @@
   int min_stratum;              /* Increase stratum in received packets to the
                                    minimum */
 
+  int copy;                     /* Boolean suppressing own refid and stratum */
+
   int poll_target;              /* Target number of sourcestats samples */
 
   int version;                  /* Version set in packets for server/peer */
@@ -136,9 +132,13 @@
   double offset_correction;     /* Correction applied to measured offset
                                    (e.g. for asymmetry in network delay) */
 
-  AuthenticationMode auth_mode; /* Authentication mode of our requests */
-  uint32_t auth_key_id;          /* The ID of the authentication key to
-                                   use. */
+  int ext_field_flags;          /* Enabled extension fields */
+
+  uint32_t remote_mono_epoch;   /* ID of the source's monotonic scale */
+  double mono_doffset;          /* Accumulated offset between source's
+                                   real-time and monotonic scales */
+
+  NAU_Instance auth;            /* Authentication */
 
   /* Count of transmitted packets since last valid response */
   unsigned int tx_count;
@@ -151,6 +151,7 @@
   int valid_timestamps;
 
   /* Receive and transmit timestamps from the last valid response */
+  NTP_int64 remote_ntp_monorx;
   NTP_int64 remote_ntp_rx;
   NTP_int64 remote_ntp_tx;
 
@@ -195,6 +196,9 @@
 
   SRC_Instance source;
 
+  /* Optional median filter for NTP measurements */
+  SPF_Instance filter;
+
   int burst_good_samples_to_go;
   int burst_total_samples_to_go;
 
@@ -205,6 +209,7 @@
 typedef struct {
   NTP_Remote_Address addr;
   NTP_Local_Address local_addr;
+  NAU_Instance auth;
   int interval;
 } BroadcastDestination;
 
@@ -217,7 +222,7 @@
 
 /* Spacing required between samples for any two servers/peers (to
    minimise risk of network collisions) (in seconds) */
-#define MIN_SAMPLING_SEPARATION 0.02
+#define MIN_SAMPLING_SEPARATION 0.002
 #define MAX_SAMPLING_SEPARATION 0.2
 
 /* Randomness added to spacing between samples for one server/peer */
@@ -226,12 +231,10 @@
 /* Adjustment of the peer polling interval */
 #define PEER_SAMPLING_ADJ 1.1
 
-/* Spacing between samples in burst mode for one server/peer */
-#define BURST_INTERVAL 2.0
-
-/* Time to wait before retransmitting in burst mode, if we did not get
-   a reply to the previous probe */
-#define BURST_TIMEOUT 2.0
+/* Maximum spacing between samples in the burst mode as an absolute
+   value and ratio to the normal polling interval */
+#define MAX_BURST_INTERVAL 2.0
+#define MAX_BURST_POLL_RATIO 0.25
 
 /* Number of samples in initial burst */
 #define IBURST_GOOD_SAMPLES 4
@@ -251,9 +254,6 @@
 /* Maximum allowed dispersion - as defined in RFC 5905 (16 seconds) */
 #define NTP_MAX_DISPERSION 16.0
 
-/* Invalid stratum number */
-#define NTP_INVALID_STRATUM 0
-
 /* Maximum allowed time for server to process client packet */
 #define MAX_SERVER_INTERVAL 4.0
 
@@ -266,19 +266,20 @@
 #define MAX_MAXDELAYDEVRATIO 1.0e6
 
 /* Minimum and maximum allowed poll interval */
-#define MIN_MINPOLL -4
-#define MIN_MAXPOLL 0
+#define MIN_POLL -6
 #define MAX_POLL 24
 
+/* Enable sub-second polling intervals only when the peer delay is not
+   longer than 10 milliseconds to restrict them to local networks */
+#define MIN_NONLAN_POLL 0
+#define MAX_LAN_PEER_DELAY 0.01
+
 /* Kiss-o'-Death codes */
 #define KOD_RATE 0x52415445UL /* RATE */
 
 /* Maximum poll interval set by KoD RATE */
 #define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL
 
-/* Maximum number of missed responses to follow peer's polling interval */
-#define MAX_PEER_POLL_TX 8
-
 /* Maximum number of missed responses to accept samples using old timestamps
    in the interleaved client/server mode */
 #define MAX_CLIENT_INTERLEAVED_TX 4
@@ -287,6 +288,9 @@
    interleaved mode to prefer a sample using previous timestamps */
 #define MAX_INTERLEAVED_L2L_RATIO 0.1
 
+/* Maximum acceptable change in server mono<->real offset */
+#define MAX_MONO_DOFFSET 16.0
+
 /* Invalid socket, different from the one in ntp_io.c */
 #define INVALID_SOCK_FD -2
 
@@ -298,6 +302,11 @@
 
 static ADF_AuthTable access_auth_table;
 
+/* Current offset between monotonic and cooked time, and its epoch ID
+   which is reset on clock steps */
+static double server_mono_offset;
+static uint32_t server_mono_epoch;
+
 /* Characters for printing synchronisation status and timestamping source */
 static const char leap_chars[4] = {'N', '+', '-', '?'};
 static const char tss_chars[3] = {'D', 'K', 'H'};
@@ -308,6 +317,8 @@
 static void transmit_timeout(void *arg);
 static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
 static double get_separation(int poll);
+static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
+static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
 
 /* ================================================== */
 
@@ -382,6 +393,20 @@
 
 /* ================================================== */
 
+static void
+handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
+            double doffset, LCL_ChangeType change_type, void *anything)
+{
+  if (change_type == LCL_ChangeAdjust) {
+    server_mono_offset += doffset;
+  } else {
+    UTI_GetRandomBytes(&server_mono_epoch, sizeof (server_mono_epoch));
+    server_mono_offset = 0.0;
+  }
+}
+
+/* ================================================== */
+
 void
 NCR_Initialise(void)
 {
@@ -398,6 +423,9 @@
   /* Server socket will be opened when access is allowed */
   server_sock_fd4 = INVALID_SOCK_FD;
   server_sock_fd6 = INVALID_SOCK_FD;
+
+  LCL_AddParameterChangeHandler(handle_slew, NULL);
+  handle_slew(NULL, NULL, 0.0, 0.0, LCL_ChangeUnknownStep, NULL);
 }
 
 /* ================================================== */
@@ -407,13 +435,17 @@
 {
   unsigned int i;
 
+  LCL_RemoveParameterChangeHandler(handle_slew, NULL);
+
   if (server_sock_fd4 != INVALID_SOCK_FD)
     NIO_CloseServerSocket(server_sock_fd4);
   if (server_sock_fd6 != INVALID_SOCK_FD)
     NIO_CloseServerSocket(server_sock_fd6);
 
-  for (i = 0; i < ARR_GetSize(broadcasts); i++)
+  for (i = 0; i < ARR_GetSize(broadcasts); i++) {
     NIO_CloseServerSocket(((BroadcastDestination *)ARR_GetElement(broadcasts, i))->local_addr.sock_fd);
+    NAU_DestroyInstance(((BroadcastDestination *)ARR_GetElement(broadcasts, i))->auth);
+  }
 
   ARR_DestroyInstance(broadcasts);
   ADF_DestroyTable(access_auth_table);
@@ -461,11 +493,16 @@
   /* In case the offline period was too short, adjust the delay to keep
      the interval between packets at least as long as the current polling
      interval */
-  SCH_GetLastEventTime(&now, NULL, NULL);
-  last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
-  if (last_tx < 0.0)
-    last_tx = 0.0;
-  delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
+  if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
+    SCH_GetLastEventTime(&now, NULL, NULL);
+    last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
+    if (last_tx < 0.0)
+      last_tx = 0.0;
+    delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
+  } else {
+    delay = 0.0;
+  }
+
   if (delay < INITIAL_DELAY)
     delay = INITIAL_DELAY;
 
@@ -510,7 +547,8 @@
 /* ================================================== */
 
 NCR_Instance
-NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
+NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
+                   SourceParameters *params, const char *name)
 {
   NCR_Instance result;
 
@@ -537,12 +575,13 @@
   result->interleaved = params->interleaved;
 
   result->minpoll = params->minpoll;
-  if (result->minpoll < MIN_MINPOLL)
+  if (result->minpoll < MIN_POLL)
     result->minpoll = SRC_DEFAULT_MINPOLL;
   else if (result->minpoll > MAX_POLL)
     result->minpoll = MAX_POLL;
+
   result->maxpoll = params->maxpoll;
-  if (result->maxpoll < MIN_MAXPOLL)
+  if (result->maxpoll < MIN_POLL)
     result->maxpoll = SRC_DEFAULT_MAXPOLL;
   else if (result->maxpoll > MAX_POLL)
     result->maxpoll = MAX_POLL;
@@ -562,48 +601,55 @@
   result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO);
   result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
   result->offset_correction = params->offset;
+  result->auto_iburst = params->iburst;
   result->auto_burst = params->burst;
   result->auto_offline = params->auto_offline;
+  result->copy = params->copy && result->mode == MODE_CLIENT;
   result->poll_target = params->poll_target;
+  result->ext_field_flags = params->ext_fields;
 
-  result->version = NTP_VERSION;
+  if (params->nts) {
+    IPSockAddr nts_address;
 
-  if (params->authkey == INACTIVE_AUTHKEY) {
-    result->auth_mode = AUTH_NONE;
-    result->auth_key_id = 0;
+    if (result->mode == MODE_ACTIVE)
+      LOG(LOGS_WARN, "NTS not supported with peers");
+
+    nts_address.ip_addr = remote_addr->ip_addr;
+    nts_address.port = params->nts_port;
+
+    result->auth = NAU_CreateNtsInstance(&nts_address, name, params->cert_set,
+                                         result->remote_addr.port);
+  } else if (params->authkey != INACTIVE_AUTHKEY) {
+    result->auth = NAU_CreateSymmetricInstance(params->authkey);
   } else {
-    result->auth_mode = AUTH_SYMMETRIC;
-    result->auth_key_id = params->authkey;
-    if (!KEY_KeyKnown(result->auth_key_id)) {
-      LOG(LOGS_WARN, "Key %"PRIu32" used by source %s is %s",
-          result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr),
-          "missing");
-    } else if (!KEY_CheckKeyLength(result->auth_key_id)) {
-      LOG(LOGS_WARN, "Key %"PRIu32" used by source %s is %s",
-          result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr),
-          "too short");
-    }
-
-    /* If the MAC in NTPv4 packets would be truncated, use version 3 by
-       default for compatibility with older chronyd servers */
-    if (KEY_GetAuthLength(result->auth_key_id) + 4 > NTP_MAX_V4_MAC_LENGTH)
-      result->version = 3;
+    result->auth = NAU_CreateNoneInstance();
   }
 
+  if (result->ext_field_flags || result->interleaved)
+    result->version = NTP_VERSION;
+  else
+    result->version = NAU_GetSuggestedNtpVersion(result->auth);
+
   if (params->version)
     result->version = CLAMP(NTP_MIN_COMPAT_VERSION, params->version, NTP_VERSION);
 
   /* Create a source instance for this NTP source */
   result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr),
-                                         SRC_NTP, params->sel_options,
-                                         &result->remote_addr.ip_addr,
+                                         SRC_NTP, NAU_IsAuthEnabled(result->auth),
+                                         params->sel_options, &result->remote_addr.ip_addr,
                                          params->min_samples, params->max_samples,
                                          params->min_delay, params->asymmetry);
 
+  if (params->filter_length >= 1)
+    result->filter = SPF_CreateInstance(params->filter_length, params->filter_length,
+                                        NTP_MAX_DISPERSION, 0.0);
+  else
+    result->filter = NULL;
+
   result->rx_timeout_id = 0;
   result->tx_timeout_id = 0;
   result->tx_suspended = 1;
-  result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
+  result->opmode = MD_OFFLINE;
   result->local_poll = result->minpoll;
   result->poll_score = 0.0;
   zero_local_timestamp(&result->local_tx);
@@ -613,9 +659,7 @@
   
   NCR_ResetInstance(result);
 
-  if (params->iburst) {
-    NCR_InitiateSampleBurst(result, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES);
-  }
+  set_connectivity(result, params->connectivity);
 
   return result;
 }
@@ -632,6 +676,11 @@
   if (instance->mode == MODE_ACTIVE)
     NIO_CloseServerSocket(instance->local_addr.sock_fd);
 
+  if (instance->filter)
+    SPF_DestroyInstance(instance->filter);
+
+  NAU_DestroyInstance(instance->auth);
+
   /* This will destroy the source instance inside the
      structure, which will cause reselection if this was the
      synchronising source etc. */
@@ -661,9 +710,14 @@
 
   instance->remote_poll = 0;
   instance->remote_stratum = 0;
+  instance->remote_root_delay = 0.0;
+  instance->remote_root_dispersion = 0.0;
+  instance->remote_mono_epoch = 0;
+  instance->mono_doffset = 0.0;
 
   instance->valid_rx = 0;
   instance->valid_timestamps = 0;
+  UTI_ZeroNtp64(&instance->remote_ntp_monorx);
   UTI_ZeroNtp64(&instance->remote_ntp_rx);
   UTI_ZeroNtp64(&instance->remote_ntp_tx);
   UTI_ZeroNtp64(&instance->local_ntp_rx);
@@ -677,6 +731,9 @@
   instance->updated_init_timestamps = 0;
   UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
   zero_local_timestamp(&instance->init_local_rx);
+
+  if (instance->filter)
+    SPF_DropSamples(instance->filter);
 }
 
 /* ================================================== */
@@ -696,10 +753,14 @@
 /* ================================================== */
 
 void
-NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
+NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
 {
   memset(&inst->report, 0, sizeof (inst->report));
   NCR_ResetInstance(inst);
+
+  if (!ntp_only)
+    NAU_ChangeAddress(inst->auth, &remote_addr->ip_addr);
+
   inst->remote_addr = *remote_addr;
 
   if (inst->mode == MODE_CLIENT)
@@ -742,6 +803,13 @@
     inst->local_poll = inst->maxpoll;
     inst->poll_score = 1.0;
   }
+
+  /* Don't allow a sub-second polling interval if the source is not reachable
+     or it is not in a local network according to the measured delay */
+  if (inst->local_poll < MIN_NONLAN_POLL &&
+      (!SRC_IsReachable(inst->source) ||
+       SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY))
+    inst->local_poll = MIN_NONLAN_POLL;
 }
 
 /* ================================================== */
@@ -753,6 +821,9 @@
   int samples;
 
   if (error_in_estimate > peer_distance) {
+    /* If the prediction is not even within +/- the peer distance of the peer,
+       we are clearly not tracking the peer at all well, so we back off the
+       sampling rate depending on just how bad the situation is */
     poll_adj = -log(error_in_estimate / peer_distance) / log(2.0);
   } else {
     samples = SST_Samples(SRC_GetSourcestats(inst->source));
@@ -782,7 +853,7 @@
   /* In symmetric mode, if the peer is responding, use shorter of the local
      and remote poll interval, but not shorter than the minimum */
   if (inst->mode == MODE_ACTIVE && poll > inst->remote_poll &&
-      inst->tx_count < MAX_PEER_POLL_TX)
+      SRC_IsReachable(inst->source))
     poll = MAX(inst->remote_poll, inst->minpoll);
 
   return poll;
@@ -850,7 +921,7 @@
     case MD_BURST_WAS_ONLINE:
     case MD_BURST_WAS_OFFLINE:
       /* Burst modes */
-      delay_time = on_tx ? BURST_TIMEOUT : BURST_INTERVAL;
+      delay_time = MIN(MAX_BURST_INTERVAL, MAX_BURST_POLL_RATIO * delay_time);
       break;
     default:
       assert(0);
@@ -868,6 +939,8 @@
 {
   double separation;
 
+  assert(poll >= MIN_POLL && poll <= MAX_POLL);
+
   /* Allow up to 8 sources using the same short interval to not be limited
      by the separation */
   separation = UTI_Log2ToDouble(poll - 3);
@@ -884,8 +957,7 @@
 {
   NCR_Instance inst = (NCR_Instance)arg;
 
-  DEBUG_LOG("Receive timeout for [%s:%d]",
-            UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
+  DEBUG_LOG("Receive timeout for %s", UTI_IPSockAddrToString(&inst->remote_addr));
 
   inst->rx_timeout_id = 0;
   close_client_socket(inst);
@@ -894,12 +966,48 @@
 /* ================================================== */
 
 static int
+add_ext_exp1(NTP_Packet *message, NTP_PacketInfo *info, struct timespec *rx,
+             double root_delay, double root_dispersion)
+{
+  struct timespec mono_rx;
+  NTP_ExtFieldExp1 exp1;
+  NTP_int64 ts_fuzz;
+
+  memset(&exp1, 0, sizeof (exp1));
+  exp1.magic = htonl(NTP_EF_EXP1_MAGIC);
+
+  if (info->mode != MODE_CLIENT) {
+    exp1.root_delay = UTI_DoubleToNtp32f28(root_delay);
+    exp1.root_dispersion = UTI_DoubleToNtp32f28(root_dispersion);
+    if (rx)
+      UTI_AddDoubleToTimespec(rx, server_mono_offset, &mono_rx);
+    else
+      UTI_ZeroTimespec(&mono_rx);
+    UTI_GetNtp64Fuzz(&ts_fuzz, message->precision);
+    UTI_TimespecToNtp64(&mono_rx, &exp1.mono_receive_ts, &ts_fuzz);
+    exp1.mono_epoch = htonl(server_mono_epoch);
+  }
+
+  if (!NEF_AddField(message, info, NTP_EF_EXP1, &exp1, sizeof (exp1))) {
+    DEBUG_LOG("Could not add EF");
+    return 0;
+  }
+
+  info->ext_field_flags |= NTP_EF_FLAG_EXP1;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
 transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
                 int interleaved, /* Flag enabling interleaved mode */
                 int my_poll, /* The log2 of the local poll interval */
                 int version, /* The NTP version to be set in the packet */
-                int auth_mode, /* The authentication mode */
-                uint32_t key_id, /* The authentication key ID */
+                uint32_t kod, /* KoD code - 0 disabled */
+                int ext_field_flags, /* Extension fields to be included in the packet */
+                NAU_Instance auth, /* The authentication to be used for the packet */
                 NTP_int64 *remote_ntp_rx, /* The receive timestamp from received packet */
                 NTP_int64 *remote_ntp_tx, /* The transmit timestamp from received packet */
                 NTP_Local_Timestamp *local_rx, /* The RX time of the received packet */
@@ -910,13 +1018,16 @@
                 NTP_int64 *local_ntp_tx, /* The transmit timestamp from the previous packet
                                             RESULT : transmit timestamp from this packet */
                 NTP_Remote_Address *where_to, /* Where to address the reponse to */
-                NTP_Local_Address *from /* From what address to send it */
+                NTP_Local_Address *from,      /* From what address to send it */
+                NTP_Packet *request,          /* The received packet if responding */
+                NTP_PacketInfo *request_info  /* and its info */
                 )
 {
+  NTP_PacketInfo info;
   NTP_Packet message;
-  int auth_len, mac_len, length, ret, precision;
   struct timespec local_receive, local_transmit;
   double smooth_offset, local_transmit_err;
+  int ret, precision;
   NTP_int64 ts_fuzz;
 
   /* Parameters read from reference module */
@@ -926,6 +1037,8 @@
   struct timespec our_ref_time;
   double our_root_delay, our_root_dispersion;
 
+  assert(auth || (request && request_info));
+
   /* Don't reply with version higher than ours */
   if (version > NTP_VERSION) {
     version = NTP_VERSION;
@@ -938,6 +1051,10 @@
   smooth_time = 0;
   smooth_offset = 0.0;
 
+  /* Get an initial transmit timestamp.  A more accurate timestamp will be
+     taken later in this function. */
+  SCH_GetLastEventTime(&local_transmit, NULL, NULL);
+
   if (my_mode == MODE_CLIENT) {
     /* Don't reveal local time or state of the clock in client packets */
     precision = 32;
@@ -945,10 +1062,6 @@
     our_root_delay = our_root_dispersion = 0.0;
     UTI_ZeroTimespec(&our_ref_time);
   } else {
-    /* This is accurate enough and cheaper than calling LCL_ReadCookedTime.
-       A more accurate timestamp will be taken later in this function. */
-    SCH_GetLastEventTime(&local_transmit, NULL, NULL);
-
     REF_GetReferenceParams(&local_transmit,
                            &are_we_synchronised, &leap_status,
                            &our_stratum,
@@ -977,6 +1090,12 @@
     local_receive = local_rx->ts;
   }
 
+  if (kod != 0) {
+    leap_status = LEAP_Unsynchronised;
+    our_stratum = NTP_INVALID_STRATUM;
+    our_ref_id = kod;
+  }
+
   /* Generate transmit packet */
   message.lvm = NTP_LVM(leap_status, version, my_mode);
   /* Stratum 16 and larger are invalid */
@@ -988,12 +1107,8 @@
  
   message.poll = my_poll;
   message.precision = precision;
-
-  /* If we're sending a client mode packet and we aren't synchronized yet, 
-     we might have to set up artificial values for some of these parameters */
   message.root_delay = UTI_DoubleToNtp32(our_root_delay);
   message.root_dispersion = UTI_DoubleToNtp32(our_root_dispersion);
-
   message.reference_id = htonl(our_ref_id);
 
   /* Now fill in timestamps */
@@ -1026,59 +1141,32 @@
     UTI_ZeroNtp64(&message.receive_ts);
   }
 
+  if (!parse_packet(&message, NTP_HEADER_LENGTH, &info))
+    return 0;
+
+  if (ext_field_flags) {
+    if (ext_field_flags & NTP_EF_FLAG_EXP1) {
+      if (!add_ext_exp1(&message, &info, smooth_time ? NULL : &local_receive,
+                        our_root_delay, our_root_dispersion))
+        return 0;
+    }
+  }
+
   do {
     /* Prepare random bits which will be added to the transmit timestamp */
     UTI_GetNtp64Fuzz(&ts_fuzz, precision);
 
-    /* Transmit - this our local time right now!  Also, we might need to
-       store this for our own use later, next time we receive a message
-       from the source we're sending to now. */
-    LCL_ReadCookedTime(&local_transmit, &local_transmit_err);
-
-    if (smooth_time)
-      UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
-
-    length = NTP_NORMAL_PACKET_LENGTH;
-
-    /* Authenticate the packet */
-
-    if (auth_mode == AUTH_SYMMETRIC || auth_mode == AUTH_MSSNTP) {
-      /* Pre-compensate the transmit time by approximately how long it will
-         take to generate the authentication data */
-      local_transmit.tv_nsec += auth_mode == AUTH_SYMMETRIC ?
-                                KEY_GetAuthDelay(key_id) : NSD_GetAuthDelay(key_id);
-      UTI_NormaliseTimespec(&local_transmit);
-      UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
-                          &message.transmit_ts, &ts_fuzz);
-
-      if (auth_mode == AUTH_SYMMETRIC) {
-        auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
-                                    offsetof(NTP_Packet, auth_keyid),
-                                    (unsigned char *)&message.auth_data,
-                                    sizeof (message.auth_data));
-        if (!auth_len) {
-          DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
-          return 0;
-        }
-
-        message.auth_keyid = htonl(key_id);
-        mac_len = sizeof (message.auth_keyid) + auth_len;
-
-        /* Truncate MACs in NTPv4 packets to allow deterministic parsing
-           of extension fields (RFC 7822) */
-        if (version == 4 && mac_len > NTP_MAX_V4_MAC_LENGTH)
-          mac_len = NTP_MAX_V4_MAC_LENGTH;
-
-        length += mac_len;
-      } else if (auth_mode == AUTH_MSSNTP) {
-        /* MS-SNTP packets are signed (asynchronously) by ntp_signd */
-        return NSD_SignAndSendPacket(key_id, &message, where_to, from, length);
-      }
-    } else {
-      UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
-                          &message.transmit_ts, &ts_fuzz);
+    /* Get a more accurate transmit timestamp if it needs to be saved in the
+       packet (i.e. in the server, symmetric, and broadcast basic modes) */
+    if (!interleaved && precision < 32) {
+      LCL_ReadCookedTime(&local_transmit, &local_transmit_err);
+      if (smooth_time)
+        UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
     }
 
+    UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
+                        &message.transmit_ts, &ts_fuzz);
+
     /* Do not send a packet with a non-zero transmit timestamp which is
        equal to any of the following timestamps:
        - receive (to allow reliable detection of the interleaved mode)
@@ -1090,9 +1178,43 @@
            UTI_IsEqualAnyNtp64(&message.transmit_ts, &message.receive_ts,
                                &message.originate_ts, local_ntp_tx));
 
-  ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
+  /* Encode in server timestamps a flag indicating RX timestamp to avoid
+     saving all RX timestamps for detection of interleaved requests */
+  if (my_mode == MODE_SERVER || my_mode == MODE_PASSIVE) {
+    message.receive_ts.lo |= htonl(1);
+    message.transmit_ts.lo &= ~htonl(1);
+  }
+
+  /* Generate the authentication data */
+  if (auth) {
+    if (!NAU_GenerateRequestAuth(auth, &message, &info)) {
+      DEBUG_LOG("Could not generate request auth");
+      return 0;
+    }
+  } else {
+    if (!NAU_GenerateResponseAuth(request, request_info, &message, &info,
+                                  where_to, from, kod)) {
+      DEBUG_LOG("Could not generate response auth");
+      return 0;
+    }
+  }
+
+  if (request_info && request_info->length < info.length) {
+    DEBUG_LOG("Response longer than request req_len=%d res_len=%d",
+              request_info->length, info.length);
+    return 0;
+  }
+
+  /* If the transmit timestamp will be saved, get an even more
+     accurate daemon timestamp closer to the transmission */
+  if (local_tx)
+    LCL_ReadCookedTime(&local_transmit, &local_transmit_err);
+
+  ret = NIO_SendPacket(&message, where_to, from, info.length, local_tx != NULL);
 
   if (local_tx) {
+    if (smooth_time)
+      UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
     local_tx->ts = local_transmit;
     local_tx->err = local_transmit_err;
     local_tx->source = NTP_TS_DAEMON;
@@ -1132,7 +1254,7 @@
       /* Start a new burst if the burst option is enabled and the average
          polling interval including the burst will not fall below the
          minimum polling interval */
-      if (inst->auto_burst && inst->local_poll > inst->minpoll && inst->local_poll > 1)
+      if (inst->auto_burst && inst->local_poll > inst->minpoll)
         NCR_InitiateSampleBurst(inst, BURST_GOOD_SAMPLES,
                                 MIN(1 << (inst->local_poll - inst->minpoll),
                                     MAX_BURST_TOTAL_SAMPLES));
@@ -1141,16 +1263,21 @@
       break;
   }
 
-  /* With auto_offline take the source offline on 2nd missed reply */
-  if (inst->auto_offline && inst->tx_count >= 2)
-    NCR_TakeSourceOffline(inst);
-
   if (inst->opmode == MD_OFFLINE) {
     return;
   }
 
-  DEBUG_LOG("Transmit timeout for [%s:%d]",
-      UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
+  DEBUG_LOG("Transmit timeout for %s", UTI_IPSockAddrToString(&inst->remote_addr));
+
+  /* Prepare authentication */
+  if (!NAU_PrepareRequestAuth(inst->auth)) {
+    if (inst->burst_total_samples_to_go > 0)
+      inst->burst_total_samples_to_go--;
+    adjust_poll(inst, 0.25);
+    SRC_UpdateReachability(inst->source, 0);
+    restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
+    return;
+  }
 
   /* Open new client socket */
   if (inst->mode == MODE_CLIENT) {
@@ -1202,13 +1329,13 @@
   }
 
   /* Send the request (which may also be a response in the symmetric mode) */
-  sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version,
-                         inst->auth_mode, inst->auth_key_id,
+  sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version, 0,
+                         inst->ext_field_flags, inst->auth,
                          initial ? NULL : &inst->remote_ntp_rx,
                          initial ? &inst->init_remote_ntp_tx : &inst->remote_ntp_tx,
                          initial ? &inst->init_local_rx : &inst->local_rx,
                          &inst->local_tx, &inst->local_ntp_rx, &inst->local_ntp_tx,
-                         &inst->remote_addr, &local_addr);
+                         &inst->remote_addr, &local_addr, NULL, NULL);
 
   ++inst->tx_count;
   if (sent)
@@ -1228,6 +1355,10 @@
     SRC_UpdateReachability(inst->source, 0);
   }
 
+  /* With auto_offline take the source offline if sending failed */
+  if (!sent && inst->auto_offline)
+    NCR_SetConnectivity(inst, SRC_OFFLINE);
+
   switch (inst->opmode) {
     case MD_BURST_WAS_ONLINE:
       /* When not reachable, don't stop online burst until sending succeeds */
@@ -1237,6 +1368,8 @@
     case MD_BURST_WAS_OFFLINE:
       --inst->burst_total_samples_to_go;
       break;
+    case MD_OFFLINE:
+      return;
     default:
       break;
   }
@@ -1255,38 +1388,12 @@
 /* ================================================== */
 
 static int
-check_packet_format(NTP_Packet *message, int length)
-{
-  int version;
-
-  /* Check version and length */
-
-  version = NTP_LVM_TO_VERSION(message->lvm);
-  if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) {
-    DEBUG_LOG("NTP packet has invalid version %d", version);
-    return 0;
-  } 
-
-  if (length < NTP_NORMAL_PACKET_LENGTH || (unsigned int)length % 4) {
-    DEBUG_LOG("NTP packet has invalid length %d", length);
-    return 0;
-  }
-
-  /* We can't reliably check the packet for invalid extension fields as we
-     support MACs longer than the shortest valid extension field */
-
-  return 1;
-}
-
-/* ================================================== */
-
-static int
 is_zero_data(unsigned char *data, int length)
 {
   int i;
 
   for (i = 0; i < length; i++)
-    if (data[i])
+    if (data[i] != 0)
       return 0;
   return 1;
 }
@@ -1294,83 +1401,117 @@
 /* ================================================== */
 
 static int
-check_packet_auth(NTP_Packet *pkt, int length,
-                  AuthenticationMode *auth_mode, uint32_t *key_id)
+parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
 {
-  int i, version, remainder, ext_length, max_mac_length;
+  int parsed, remainder, ef_length, ef_type, ef_body_length;
   unsigned char *data;
-  uint32_t id;
+  void *ef_body;
 
-  /* Go through extension fields and see if there is a valid MAC */
-
-  version = NTP_LVM_TO_VERSION(pkt->lvm);
-  i = NTP_NORMAL_PACKET_LENGTH;
-  data = (void *)pkt;
-
-  while (1) {
-    remainder = length - i;
-
-    /* Check if the remaining data is a valid MAC.  There is a limit on MAC
-       length in NTPv4 packets to allow deterministic parsing of extension
-       fields (RFC 7822), but we need to support longer MACs to not break
-       compatibility with older chrony clients.  This needs to be done before
-       trying to parse the data as an extension field. */
-
-    max_mac_length = version == 4 && remainder <= NTP_MAX_V4_MAC_LENGTH ?
-                     NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH;
-
-    if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= max_mac_length) {
-      id = ntohl(*(uint32_t *)(data + i));
-      if (KEY_CheckAuth(id, (void *)pkt, i, (void *)(data + i + 4),
-                        remainder - 4, max_mac_length - 4)) {
-        *auth_mode = AUTH_SYMMETRIC;
-        *key_id = id;
-
-        /* If it's an NTPv4 packet with long MAC and no extension fields,
-           rewrite the version in the packet to respond with long MAC too */
-        if (version == 4 && NTP_NORMAL_PACKET_LENGTH + remainder == length &&
-            remainder > NTP_MAX_V4_MAC_LENGTH)
-          pkt->lvm = NTP_LVM(NTP_LVM_TO_LEAP(pkt->lvm), 3, NTP_LVM_TO_MODE(pkt->lvm));
-
-        return 1;
-      }
-    }
-
-    /* Check if this is a valid NTPv4 extension field and skip it.  It should
-       have a 16-bit type, 16-bit length, and data padded to 32 bits. */
-    if (version == 4 && remainder >= NTP_MIN_EXTENSION_LENGTH) {
-      ext_length = ntohs(*(uint16_t *)(data + i + 2));
-      if (ext_length >= NTP_MIN_EXTENSION_LENGTH &&
-          ext_length <= remainder && ext_length % 4 == 0) {
-        i += ext_length;
-        continue;
-      }
-    }
-
-    /* Invalid or missing MAC, or format error */
-    break;
+  if (length < NTP_HEADER_LENGTH || length % 4U != 0) {
+    DEBUG_LOG("NTP packet has invalid length %d", length);
+    return 0;
   }
 
-  /* This is not 100% reliable as a MAC could fail to authenticate and could
-     pass as an extension field, leaving reminder smaller than the minimum MAC
-     length */
-  if (remainder >= NTP_MIN_MAC_LENGTH) {
-    *auth_mode = AUTH_SYMMETRIC;
-    *key_id = ntohl(*(uint32_t *)(data + i));
+  info->length = length;
+  info->version = NTP_LVM_TO_VERSION(packet->lvm);
+  info->mode = NTP_LVM_TO_MODE(packet->lvm);
+  info->ext_fields = 0;
+  info->ext_field_flags = 0;
+  info->auth.mode = NTP_AUTH_NONE;
+
+  if (info->version < NTP_MIN_COMPAT_VERSION || info->version > NTP_MAX_COMPAT_VERSION) {
+    DEBUG_LOG("NTP packet has invalid version %d", info->version);
+    return 0;
+  }
+
+  data = (void *)packet;
+  parsed = NTP_HEADER_LENGTH;
+  remainder = info->length - parsed;
+
+  /* Check if this is a plain NTP packet with no extension fields or MAC */
+  if (remainder <= 0)
+    return 1;
+
+  assert(remainder % 4 == 0);
+
+  /* In NTPv3 and older packets don't have extension fields.  Anything after
+     the header is assumed to be a MAC. */
+  if (info->version <= 3) {
+    info->auth.mode = NTP_AUTH_SYMMETRIC;
+    info->auth.mac.start = parsed;
+    info->auth.mac.length = remainder;
+    info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
 
     /* Check if it is an MS-SNTP authenticator field or extended authenticator
        field with zeroes as digest */
-    if (version == 3 && *key_id) {
-      if (remainder == 20 && is_zero_data(data + i + 4, remainder - 4))
-        *auth_mode = AUTH_MSSNTP;
-      else if (remainder == 72 && is_zero_data(data + i + 8, remainder - 8))
-        *auth_mode = AUTH_MSSNTP_EXT;
+    if (info->version == 3 && info->auth.mac.key_id != 0) {
+      if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4))
+        info->auth.mode = NTP_AUTH_MSSNTP;
+      else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8))
+        info->auth.mode = NTP_AUTH_MSSNTP_EXT;
     }
-  } else {
-    *auth_mode = AUTH_NONE;
-    *key_id = 0;
+
+    return 1;
   }
 
+  /* Check for a crypto NAK */
+  if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) {
+    info->auth.mode = NTP_AUTH_SYMMETRIC;
+    info->auth.mac.start = parsed;
+    info->auth.mac.length = remainder;
+    info->auth.mac.key_id = 0;
+    return 1;
+  }
+
+  /* Parse the rest of the NTPv4 packet */
+
+  while (remainder > 0) {
+    /* Check if the remaining data is a MAC */
+    if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH)
+      break;
+
+    /* Check if this is a valid NTPv4 extension field and skip it */
+    if (!NEF_ParseField(packet, info->length, parsed,
+                        &ef_length, &ef_type, &ef_body, &ef_body_length)) {
+      DEBUG_LOG("Invalid format");
+      return 0;
+    }
+
+    assert(ef_length > 0 && ef_length % 4 == 0);
+
+    switch (ef_type) {
+      case NTP_EF_NTS_UNIQUE_IDENTIFIER:
+      case NTP_EF_NTS_COOKIE:
+      case NTP_EF_NTS_COOKIE_PLACEHOLDER:
+      case NTP_EF_NTS_AUTH_AND_EEF:
+        info->auth.mode = NTP_AUTH_NTS;
+        break;
+      case NTP_EF_EXP1:
+        if (ef_body_length == sizeof (NTP_ExtFieldExp1) &&
+            ntohl(((NTP_ExtFieldExp1 *)ef_body)->magic) == NTP_EF_EXP1_MAGIC)
+          info->ext_field_flags |= NTP_EF_FLAG_EXP1;
+        break;
+      default:
+        DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type);
+    }
+
+    info->ext_fields++;
+    parsed += ef_length;
+    remainder = info->length - parsed;
+  }
+
+  if (remainder == 0) {
+    /* No MAC */
+    return 1;
+  } else if (remainder >= NTP_MIN_MAC_LENGTH) {
+    info->auth.mode = NTP_AUTH_SYMMETRIC;
+    info->auth.mac.start = parsed;
+    info->auth.mac.length = remainder;
+    info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
+    return 1;
+  }
+
+  DEBUG_LOG("Invalid format");
   return 0;
 }
 
@@ -1438,34 +1579,113 @@
 /* ================================================== */
 
 static int
-receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
-               NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
+check_sync_loop(NCR_Instance inst, NTP_Packet *message, NTP_Local_Address *local_addr,
+                struct timespec *local_ts)
 {
+  double our_root_delay, our_root_dispersion;
+  int are_we_synchronised, our_stratum;
+  struct timespec our_ref_time;
+  NTP_Leap leap_status;
+  uint32_t our_ref_id;
+
+  /* Check if a client or peer can be synchronised to us */
+  if (!NIO_IsServerSocketOpen() || REF_GetMode() != REF_ModeNormal)
+    return 1;
+
+  /* Check if the source indicates that it is synchronised to our address
+     (assuming it uses the same address as the one from which we send requests
+     to the source) */
+  if (message->stratum > 1 &&
+      message->reference_id == htonl(UTI_IPToRefid(&local_addr->ip_addr)))
+    return 0;
+
+  /* Compare our reference data with the source to make sure it is not us
+     (e.g. due to a misconfiguration) */
+
+  REF_GetReferenceParams(local_ts, &are_we_synchronised, &leap_status, &our_stratum,
+                         &our_ref_id, &our_ref_time, &our_root_delay, &our_root_dispersion);
+
+  if (message->stratum == our_stratum &&
+      message->reference_id == htonl(our_ref_id) &&
+      message->root_delay == UTI_DoubleToNtp32(our_root_delay) &&
+      !UTI_IsZeroNtp64(&message->reference_ts)) {
+    NTP_int64 ntp_ref_time;
+
+    UTI_TimespecToNtp64(&our_ref_time, &ntp_ref_time, NULL);
+    if (UTI_CompareNtp64(&message->reference_ts, &ntp_ref_time) == 0) {
+      DEBUG_LOG("Source %s is me", UTI_IPToString(&inst->remote_addr.ip_addr));
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+process_sample(NCR_Instance inst, NTP_Sample *sample)
+{
+  double estimated_offset, error_in_estimate, filtered_sample_ago;
+  NTP_Sample filtered_sample;
+  int filtered_samples;
+
+  /* Accumulate the sample to the median filter if it is enabled.  When the
+     filter produces a result, check if it is not too old, i.e. the filter did
+     not miss too many samples due to missing responses or failing tests. */
+  if (inst->filter) {
+    SPF_AccumulateSample(inst->filter, sample);
+
+    filtered_samples = SPF_GetNumberOfSamples(inst->filter);
+
+    if (!SPF_GetFilteredSample(inst->filter, &filtered_sample))
+      return;
+
+    filtered_sample_ago = UTI_DiffTimespecsToDouble(&sample->time, &filtered_sample.time);
+
+    if (filtered_sample_ago > SOURCE_REACH_BITS / 2 * filtered_samples *
+                              UTI_Log2ToDouble(inst->local_poll)) {
+      DEBUG_LOG("filtered sample dropped ago=%f poll=%d", filtered_sample_ago,
+                inst->local_poll);
+      return;
+    }
+
+    sample = &filtered_sample;
+  }
+
+  /* Get the estimated offset predicted from previous samples.  The
+     convention here is that positive means local clock FAST of
+     reference, i.e. backwards to the way that 'offset' is defined. */
+  estimated_offset = SST_PredictOffset(SRC_GetSourcestats(inst->source), &sample->time);
+
+  error_in_estimate = fabs(-sample->offset - estimated_offset);
+
+  if (inst->mono_doffset != 0.0 && fabs(inst->mono_doffset) <= MAX_MONO_DOFFSET) {
+    DEBUG_LOG("Monotonic correction offset=%.9f", inst->mono_doffset);
+    SST_CorrectOffset(SRC_GetSourcestats(inst->source), inst->mono_doffset);
+  }
+  inst->mono_doffset = 0.0;
+
+  SRC_AccumulateSample(inst->source, sample);
+  SRC_SelectSource(inst->source);
+
+  adjust_poll(inst, get_poll_adj(inst, error_in_estimate,
+                                 sample->peer_dispersion + 0.5 * sample->peer_delay));
+}
+
+/* ================================================== */
+
+static int
+process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
+                 NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
+{
+  NTP_Sample sample;
   SST_Stats stats;
 
   int pkt_leap, pkt_version;
-  uint32_t pkt_refid, pkt_key_id;
+  uint32_t pkt_refid;
   double pkt_root_delay;
   double pkt_root_dispersion;
-  AuthenticationMode pkt_auth_mode;
-
-  /* The local time to which the (offset, delay, dispersion) triple will
-     be taken to relate.  For client/server operation this is practically
-     the same as either the transmit or receive time.  The difference comes
-     in symmetric active mode, when the receive may come minutes after the
-     transmit, and this time will be midway between the two */
-  struct timespec sample_time;
-
-  /* The estimated offset in seconds, a positive value indicates that the local
-     clock is SLOW of the remote source and a negative value indicates that the
-     local clock is FAST of the remote source */
-  double offset;
-
-  /* The estimated peer delay, dispersion and distance */
-  double delay, dispersion, distance;
-
-  /* The total root delay and dispersion */
-  double root_delay, root_dispersion;
 
   /* The skew and estimated frequency offset relative to the remote source */
   double skew, source_freq_lo, source_freq_hi;
@@ -1481,31 +1701,50 @@
   /* Kiss-o'-Death codes */
   int kod_rate;
 
-  /* The estimated offset predicted from previous samples.  The
-     convention here is that positive means local clock FAST of
-     reference, i.e. backwards to the way that 'offset' is defined. */
-  double estimated_offset;
-
-  /* The absolute difference between the offset estimate and
-     measurement in seconds */
-  double error_in_estimate;
+  /* Extension fields */
+  int parsed, ef_length, ef_type, ef_body_length;
+  void *ef_body;
+  NTP_ExtFieldExp1 *ef_exp1;
 
   NTP_Local_Timestamp local_receive, local_transmit;
   double remote_interval, local_interval, response_time;
-  double delay_time, precision;
+  double delay_time, precision, mono_doffset;
   int updated_timestamps;
 
   /* ==================== */
 
   stats = SRC_GetSourcestats(inst->source);
 
-  inst->report.total_rx_count++;
+  ef_exp1 = NULL;
+
+  /* Find requested non-authentication extension fields */
+  if (inst->ext_field_flags & info->ext_field_flags) {
+    for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
+      if (!NEF_ParseField(message, info->length, parsed,
+                          &ef_length, &ef_type, &ef_body, &ef_body_length))
+        break;
+
+      switch (ef_type) {
+        case NTP_EF_EXP1:
+          if (inst->ext_field_flags & NTP_EF_FLAG_EXP1 &&
+              ef_body_length == sizeof (*ef_exp1) &&
+              ntohl(((NTP_ExtFieldExp1 *)ef_body)->magic) == NTP_EF_EXP1_MAGIC)
+            ef_exp1 = ef_body;
+          break;
+      }
+    }
+  }
 
   pkt_leap = NTP_LVM_TO_LEAP(message->lvm);
   pkt_version = NTP_LVM_TO_VERSION(message->lvm);
   pkt_refid = ntohl(message->reference_id);
-  pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
-  pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
+  if (ef_exp1) {
+    pkt_root_delay = UTI_Ntp32f28ToDouble(ef_exp1->root_delay);
+    pkt_root_dispersion = UTI_Ntp32f28ToDouble(ef_exp1->root_dispersion);
+  } else {
+    pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
+    pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
+  }
 
   /* Check if the packet is valid per RFC 5905, section 8.
      The test values are 1 when passed and 0 when failed. */
@@ -1531,14 +1770,8 @@
   /* Test 4 would check for denied access.  It would always pass as this
      function is called only for known sources. */
 
-  /* Test 5 checks for authentication failure.  If we expect authenticated info
-     from this peer/server and the packet doesn't have it, the authentication
-     is bad, or it's authenticated with a different key than expected, it's got
-     to fail.  If we don't expect the packet to be authenticated, just ignore
-     the test. */
-  test5 = inst->auth_mode == AUTH_NONE ||
-          (check_packet_auth(message, length, &pkt_auth_mode, &pkt_key_id) &&
-           pkt_auth_mode == inst->auth_mode && pkt_key_id == inst->auth_key_id);
+  /* Test 5 checks for authentication failure */
+  test5 = NAU_CheckResponseAuth(inst->auth, message, info);
 
   /* Test 6 checks for unsynchronised server */
   test6 = pkt_leap != LEAP_Unsynchronised &&
@@ -1566,7 +1799,27 @@
     /* These are the timespec equivalents of the remote and local epochs */
     struct timespec remote_receive, remote_transmit, remote_request_receive;
     struct timespec local_average, remote_average, prev_remote_transmit;
-    double prev_remote_poll_interval;
+    double prev_remote_poll_interval, root_delay, root_dispersion;
+
+    /* If the remote monotonic timestamps are available and are from the same
+       epoch, calculate the change in the offset between the monotonic and
+       real-time clocks, i.e. separate the source's time corrections from
+       frequency corrections.  The offset is accumulated between measurements.
+       It will correct old measurements kept in sourcestats before accumulating
+       the new sample.  In the interleaved mode, cancel the correction out in
+       remote timestamps of the previous request and response, which were
+       captured before the source accumulated the new time corrections. */
+    if (ef_exp1 && inst->remote_mono_epoch == ntohl(ef_exp1->mono_epoch) &&
+        !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts) &&
+        !UTI_IsZeroNtp64(&inst->remote_ntp_monorx)) {
+      mono_doffset =
+          UTI_DiffNtp64ToDouble(&ef_exp1->mono_receive_ts, &inst->remote_ntp_monorx) -
+          UTI_DiffNtp64ToDouble(&message->receive_ts, &inst->remote_ntp_rx);
+      if (fabs(mono_doffset) > MAX_MONO_DOFFSET)
+        mono_doffset = 0.0;
+    } else {
+      mono_doffset = 0.0;
+    }
 
     /* Select remote and local timestamps for the new sample */
     if (interleaved_packet) {
@@ -1578,14 +1831,20 @@
             UTI_DiffTimespecsToDouble(&inst->local_tx.ts, &inst->local_rx.ts) >
           UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->prev_local_tx.ts)) {
         UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_receive);
+        UTI_AddDoubleToTimespec(&remote_receive, -mono_doffset, &remote_receive);
         remote_request_receive = remote_receive;
         local_transmit = inst->prev_local_tx;
+        root_delay = inst->remote_root_delay;
+        root_dispersion = inst->remote_root_dispersion;
       } else {
         UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive);
         UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_request_receive);
         local_transmit = inst->local_tx;
+        root_delay = MAX(pkt_root_delay, inst->remote_root_delay);
+        root_dispersion = MAX(pkt_root_dispersion, inst->remote_root_dispersion);
       }
       UTI_Ntp64ToTimespec(&message->transmit_ts, &remote_transmit);
+      UTI_AddDoubleToTimespec(&remote_transmit, -mono_doffset, &remote_transmit);
       UTI_Ntp64ToTimespec(&inst->remote_ntp_tx, &prev_remote_transmit);
       local_receive = inst->local_rx;
     } else {
@@ -1595,6 +1854,8 @@
       remote_request_receive = remote_receive;
       local_receive = *rx_ts;
       local_transmit = inst->local_tx;
+      root_delay = pkt_root_delay;
+      root_dispersion = pkt_root_dispersion;
     }
 
     /* Calculate intervals between remote and local timestamps */
@@ -1608,32 +1869,34 @@
     precision = LCL_GetSysPrecisionAsQuantum() + UTI_Log2ToDouble(message->precision);
 
     /* Calculate delay */
-    delay = fabs(local_interval - remote_interval);
-    if (delay < precision)
-      delay = precision;
+    sample.peer_delay = fabs(local_interval - remote_interval);
+    if (sample.peer_delay < precision)
+      sample.peer_delay = precision;
     
     /* Calculate offset.  Following the NTP definition, this is negative
        if we are fast of the remote source. */
-    offset = UTI_DiffTimespecsToDouble(&remote_average, &local_average);
+    sample.offset = UTI_DiffTimespecsToDouble(&remote_average, &local_average);
 
     /* Apply configured correction */
-    offset += inst->offset_correction;
+    sample.offset += inst->offset_correction;
 
     /* We treat the time of the sample as being midway through the local
        measurement period.  An analysis assuming constant relative
        frequency and zero network delay shows this is the only possible
        choice to estimate the frequency difference correctly for every
        sample pair. */
-    sample_time = local_average;
+    sample.time = local_average;
     
     SST_GetFrequencyRange(stats, &source_freq_lo, &source_freq_hi);
 
     /* Calculate skew */
     skew = (source_freq_hi - source_freq_lo) / 2.0;
     
-    /* and then calculate peer dispersion */
-    dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
-                 skew * fabs(local_interval);
+    /* and then calculate peer dispersion and the rest of the sample */
+    sample.peer_dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
+                             skew * fabs(local_interval);
+    sample.root_delay = root_delay + sample.peer_delay;
+    sample.root_dispersion = root_dispersion + sample.peer_dispersion;
     
     /* If the source is an active peer, this is the minimum assumed interval
        between previous two transmissions (if not constrained by minpoll) */
@@ -1647,10 +1910,11 @@
        processing time is sane, and in interleaved symmetric mode that the
        measured delay and intervals between remote timestamps don't indicate
        a missed response */
-    testA = delay - dispersion <= inst->max_delay && precision <= inst->max_delay &&
+    testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
+            precision <= inst->max_delay &&
             !(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
             !(inst->mode == MODE_ACTIVE && interleaved_packet &&
-              (delay > 0.5 * prev_remote_poll_interval ||
+              (sample.peer_delay > 0.5 * prev_remote_poll_interval ||
                UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
                (inst->remote_poll <= inst->prev_local_poll &&
                 UTI_DiffTimespecsToDouble(&remote_transmit, &prev_remote_transmit) >
@@ -1659,23 +1923,24 @@
     /* Test B requires in client mode that the ratio of the round trip delay
        to the minimum one currently in the stats data register is less than an
        administrator-defined value */
-    testB = check_delay_ratio(inst, stats, &sample_time, delay);
+    testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
 
     /* Test C requires that the ratio of the increase in delay from the minimum
        one in the stats data register to the standard deviation of the offsets
        in the register is less than an administrator-defined value or the
        difference between measured offset and predicted offset is larger than
        the increase in delay */
-    testC = check_delay_dev_ratio(inst, stats, &sample_time, offset, delay);
+    testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
 
-    /* Test D requires that the remote peer is not synchronised to us to
-       prevent a synchronisation loop */
-    testD = message->stratum <= 1 || REF_GetMode() != REF_ModeNormal ||
-            pkt_refid != UTI_IPToRefid(&local_addr->ip_addr);
+    /* Test D requires that the source is not synchronised to us and is not us
+       to prevent a synchronisation loop */
+    testD = check_sync_loop(inst, message, local_addr, &rx_ts->ts);
   } else {
     remote_interval = local_interval = response_time = 0.0;
-    offset = delay = dispersion = 0.0;
-    sample_time = rx_ts->ts;
+    sample.offset = sample.peer_delay = sample.peer_dispersion = 0.0;
+    sample.root_delay = sample.root_dispersion = 0.0;
+    sample.time = rx_ts->ts;
+    mono_doffset = 0.0;
     local_receive = *rx_ts;
     local_transmit = inst->local_tx;
     testA = testB = testC = testD = 0;
@@ -1685,10 +1950,6 @@
      the additional tests passed */
   good_packet = testA && testB && testC && testD;
 
-  root_delay = pkt_root_delay + delay;
-  root_dispersion = pkt_root_dispersion + dispersion;
-  distance = dispersion + 0.5 * delay;
-
   /* Update the NTP timestamps.  If it's a valid packet from a synchronised
      source, the timestamps may be used later when processing a packet in the
      interleaved mode.  Protect the timestamps against replay attacks in client
@@ -1710,6 +1971,19 @@
     inst->updated_init_timestamps = 0;
     updated_timestamps = 2;
 
+    /* If available, update the monotonic timestamp and accumulate the offset.
+       This needs to be done here to not lose changes in remote_ntp_rx in
+       symmetric mode when there are multiple responses per request. */
+    if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) {
+      inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch);
+      inst->remote_ntp_monorx = ef_exp1->mono_receive_ts;
+      inst->mono_doffset += mono_doffset;
+    } else {
+      inst->remote_mono_epoch = 0;
+      UTI_ZeroNtp64(&inst->remote_ntp_monorx);
+      inst->mono_doffset = 0.0;
+    }
+
     /* Don't use the same set of timestamps for the next sample */
     if (interleaved_packet)
       inst->prev_local_tx = inst->local_tx;
@@ -1746,19 +2020,21 @@
       (unsigned int)local_transmit.source >= sizeof (tss_chars))
     assert(0);
 
-  DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%f root_disp=%f refid=%"PRIx32" [%s]",
+  DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%.9f root_disp=%.9f refid=%"PRIx32" [%s]",
             message->lvm, message->stratum, message->poll, message->precision,
             pkt_root_delay, pkt_root_dispersion, pkt_refid,
-            message->stratum == NTP_INVALID_STRATUM ? UTI_RefidToString(pkt_refid) : "");
+            message->stratum == NTP_INVALID_STRATUM || message->stratum == 1 ?
+              UTI_RefidToString(pkt_refid) : "");
   DEBUG_LOG("reference=%s origin=%s receive=%s transmit=%s",
             UTI_Ntp64ToString(&message->reference_ts),
             UTI_Ntp64ToString(&message->originate_ts),
             UTI_Ntp64ToString(&message->receive_ts),
             UTI_Ntp64ToString(&message->transmit_ts));
   DEBUG_LOG("offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f",
-            offset, delay, dispersion, root_delay, root_dispersion);
-  DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
-            remote_interval, local_interval, response_time,
+            sample.offset, sample.peer_delay, sample.peer_dispersion,
+            sample.root_delay, sample.root_dispersion);
+  DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f mono_doffset=%.9f txs=%c rxs=%c",
+            remote_interval, local_interval, response_time, mono_doffset,
             tss_chars[local_transmit.source], tss_chars[local_receive.source]);
   DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d"
             " presend=%d valid=%d good=%d updated=%d",
@@ -1769,7 +2045,9 @@
   if (valid_packet) {
     inst->remote_poll = message->poll;
     inst->remote_stratum = message->stratum != NTP_INVALID_STRATUM ?
-                           message->stratum : NTP_MAX_STRATUM;
+                           MIN(message->stratum, NTP_MAX_STRATUM) : NTP_MAX_STRATUM;
+    inst->remote_root_delay = pkt_root_delay;
+    inst->remote_root_dispersion = pkt_root_dispersion;
 
     inst->prev_local_poll = inst->local_poll;
     inst->prev_tx_count = inst->tx_count;
@@ -1777,27 +2055,19 @@
 
     SRC_UpdateReachability(inst->source, synced_packet);
 
+    if (synced_packet) {
+      if (inst->copy && inst->remote_stratum > 0) {
+        /* Assume the reference ID and stratum of the server */
+        inst->remote_stratum--;
+        SRC_SetRefid(inst->source, ntohl(message->reference_id), &inst->remote_addr.ip_addr);
+      }
+
+      SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
+    }
+
     if (good_packet) {
-      /* Do this before we accumulate a new sample into the stats registers, obviously */
-      estimated_offset = SST_PredictOffset(stats, &sample_time);
-
-      SRC_AccumulateSample(inst->source,
-                           &sample_time,
-                           offset, delay, dispersion,
-                           root_delay, root_dispersion,
-                           MAX(message->stratum, inst->min_stratum),
-                           (NTP_Leap) pkt_leap);
-
-      SRC_SelectSource(inst->source);
-
-      /* Now examine the registers.  First though, if the prediction is
-         not even within +/- the peer distance of the peer, we are clearly
-         not tracking the peer at all well, so we back off the sampling
-         rate depending on just how bad the situation is. */
-      error_in_estimate = fabs(-offset - estimated_offset);
-
-      /* Now update the polling interval */
-      adjust_poll(inst, get_poll_adj(inst, error_in_estimate, distance));
+      /* Adjust the polling interval, accumulate the sample, etc. */
+      process_sample(inst, &sample);
 
       /* If we're in burst mode, check whether the burst is completed and
          revert to the previous mode */
@@ -1816,8 +2086,8 @@
           break;
       }
     } else {
-      /* Slowly increase the polling interval if we can't get good packet */
-      adjust_poll(inst, 0.1);
+      /* Slowly increase the polling interval if we can't get a good response */
+      adjust_poll(inst, testD ? 0.02 : 0.1);
     }
 
     /* If in client mode, no more packets are expected to be coming from the
@@ -1864,16 +2134,16 @@
     inst->report.root_dispersion = pkt_root_dispersion;
     inst->report.ref_id = pkt_refid;
     UTI_Ntp64ToTimespec(&message->reference_ts, &inst->report.ref_time);
-    inst->report.offset = offset;
-    inst->report.peer_delay = delay;
-    inst->report.peer_dispersion = dispersion;
+    inst->report.offset = sample.offset;
+    inst->report.peer_delay = sample.peer_delay;
+    inst->report.peer_dispersion = sample.peer_dispersion;
     inst->report.response_time = response_time;
     inst->report.jitter_asymmetry = SST_GetJitterAsymmetry(stats);
     inst->report.tests = ((((((((test1 << 1 | test2) << 1 | test3) << 1 |
                                test5) << 1 | test6) << 1 | test7) << 1 |
                             testA) << 1 | testB) << 1 | testC) << 1 | testD;
     inst->report.interleaved = interleaved_packet;
-    inst->report.authenticated = inst->auth_mode != AUTH_NONE;
+    inst->report.authenticated = NAU_IsAuthEnabled(inst->auth);
     inst->report.tx_tss_char = tss_chars[local_transmit.source];
     inst->report.rx_tss_char = tss_chars[local_receive.source];
 
@@ -1883,14 +2153,14 @@
   /* Do measurement logging */
   if (logfileid != -1 && (log_raw_measurements || synced_packet)) {
     LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d %1d%1d%1d %1d%1d%1d%d  %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e %08"PRIX32" %1d%1c %1c %1c",
-            UTI_TimeToLogForm(sample_time.tv_sec),
+            UTI_TimeToLogForm(sample.time.tv_sec),
             UTI_IPToString(&inst->remote_addr.ip_addr),
             leap_chars[pkt_leap],
             message->stratum,
             test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
             inst->local_poll, message->poll,
             inst->poll_score,
-            offset, delay, dispersion,
+            sample.offset, sample.peer_delay, sample.peer_dispersion,
             pkt_root_delay, pkt_root_dispersion, pkt_refid,
             NTP_LVM_TO_MODE(message->lvm), interleaved_packet ? 'I' : 'B',
             tss_chars[local_transmit.source],
@@ -1933,17 +2203,19 @@
 NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
                    NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
 {
-  int pkt_mode, proc_packet, proc_as_unknown;
+  int proc_packet, proc_as_unknown;
+  NTP_PacketInfo info;
 
-  if (!check_packet_format(message, length))
+  inst->report.total_rx_count++;
+
+  if (!parse_packet(message, length, &info))
     return 0;
 
-  pkt_mode = NTP_LVM_TO_MODE(message->lvm);
   proc_packet = 0;
   proc_as_unknown = 0;
 
   /* Now, depending on the mode we decide what to do */
-  switch (pkt_mode) {
+  switch (info.mode) {
     case MODE_ACTIVE:
       switch (inst->mode) {
         case MODE_ACTIVE:
@@ -2033,13 +2305,13 @@
       return 0;
     }
 
-    return receive_packet(inst, local_addr, rx_ts, message, length);
+    return process_response(inst, local_addr, rx_ts, message, &info);
   } else if (proc_as_unknown) {
     NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length);
     /* It's not a reply to our request, don't return success */
     return 0;
   } else {
-    DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%d", pkt_mode, inst->mode);
+    DEBUG_LOG("NTP packet discarded mode=%d our_mode=%u", (int)info.mode, inst->mode);
     return 0;
   }
 }
@@ -2052,12 +2324,12 @@
 NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
                      NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
 {
-  NTP_Mode pkt_mode, my_mode;
-  NTP_int64 *local_ntp_rx, *local_ntp_tx;
+  NTP_PacketInfo info;
+  NTP_Mode my_mode;
   NTP_Local_Timestamp local_tx, *tx_ts;
-  int pkt_version, valid_auth, log_index, interleaved, poll;
-  AuthenticationMode auth_mode;
-  uint32_t key_id;
+  NTP_int64 ntp_rx, *local_ntp_rx;
+  int log_index, interleaved, poll, version;
+  uint32_t kod;
 
   /* Ignore the packet if it wasn't received by server socket */
   if (!NIO_IsServerSocket(local_addr->sock_fd)) {
@@ -2065,20 +2337,16 @@
     return;
   }
 
-  if (!check_packet_format(message, length))
+  if (!parse_packet(message, length, &info))
     return;
 
   if (!ADF_IsAllowed(access_auth_table, &remote_addr->ip_addr)) {
-    DEBUG_LOG("NTP packet received from unauthorised host %s port %d",
-              UTI_IPToString(&remote_addr->ip_addr),
-              remote_addr->port);
+    DEBUG_LOG("NTP packet received from unauthorised host %s",
+              UTI_IPToString(&remote_addr->ip_addr));
     return;
   }
 
-  pkt_mode = NTP_LVM_TO_MODE(message->lvm);
-  pkt_version = NTP_LVM_TO_VERSION(message->lvm);
-
-  switch (pkt_mode) {
+  switch (info.mode) {
     case MODE_ACTIVE:
       /* We are symmetric passive, even though we don't ever lock to him */
       my_mode = MODE_PASSIVE;
@@ -2091,66 +2359,60 @@
       /* Check if it is an NTPv1 client request (NTPv1 packets have a reserved
          field instead of the mode field and the actual mode is determined from
          the port numbers).  Don't ever respond with a mode 0 packet! */
-      if (pkt_version == 1 && remote_addr->port != NTP_PORT) {
+      if (info.version == 1 && remote_addr->port != NTP_PORT) {
         my_mode = MODE_SERVER;
         break;
       }
       /* Fall through */
     default:
       /* Discard */
-      DEBUG_LOG("NTP packet discarded pkt_mode=%d", pkt_mode);
+      DEBUG_LOG("NTP packet discarded mode=%d", (int)info.mode);
       return;
   }
 
-  log_index = CLG_LogNTPAccess(&remote_addr->ip_addr, &rx_ts->ts);
+  kod = 0;
+  log_index = CLG_LogServiceAccess(CLG_NTP, &remote_addr->ip_addr, &rx_ts->ts);
 
   /* Don't reply to all requests if the rate is excessive */
-  if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) {
+  if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTP, log_index)) {
       DEBUG_LOG("NTP packet discarded to limit response rate");
       return;
   }
 
-  /* Check if the packet includes MAC that authenticates properly */
-  valid_auth = check_packet_auth(message, length, &auth_mode, &key_id);
+  /* Check authentication */
+  if (!NAU_CheckRequestAuth(message, &info, &kod)) {
+    DEBUG_LOG("NTP packet failed auth mode=%d kod=%"PRIx32, (int)info.auth.mode, kod);
 
-  /* If authentication failed, select whether and how we should respond */
-  if (!valid_auth) {
-    switch (auth_mode) {
-      case AUTH_NONE:
-        /* Reply with no MAC */
-        break;
-      case AUTH_MSSNTP:
-        /* Ignore the failure (MS-SNTP servers don't check client MAC) */
-        break;
-      default:
-        /* Discard packets in other modes */
-        DEBUG_LOG("NTP packet discarded auth_mode=%d", auth_mode);
-        return;
-    }
+    /* Don't respond unless a non-zero KoD was returned */
+    if (kod == 0)
+      return;
+  } else if (info.auth.mode != NTP_AUTH_NONE && info.auth.mode != NTP_AUTH_MSSNTP) {
+    CLG_LogAuthNtpRequest();
   }
 
-  local_ntp_rx = local_ntp_tx = NULL;
+  local_ntp_rx = NULL;
   tx_ts = NULL;
   interleaved = 0;
 
-  /* Check if the client is using the interleaved mode.  If it is, save the
-     new transmit timestamp and if the old transmit timestamp is valid, respond
-     in the interleaved mode.  This means the third reply to a new client is
-     the earliest one that can be interleaved.  We don't want to waste time
-     on clients that are not using the interleaved mode. */
-  if (log_index >= 0) {
-    CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
-    interleaved = !UTI_IsZeroNtp64(local_ntp_rx) &&
-                  !UTI_CompareNtp64(&message->originate_ts, local_ntp_rx) &&
-                  UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts);
+  /* Handle requests formed in the interleaved mode.  As an optimisation to
+     avoid saving all receive timestamps, require that the origin timestamp
+     has the lowest bit equal to 1, which indicates it was set to one of our
+     receive timestamps instead of transmit timestamps or zero.  Respond in the
+     interleaved mode if the receive timestamp is found and it has a non-zero
+     transmit timestamp (this is verified in transmit_packet()).  For a new
+     client starting with a zero origin timestamp, the third response is the
+     earliest one that can be interleaved. */
+  if (kod == 0 && log_index >= 0 && info.version == 4 &&
+      message->originate_ts.lo & htonl(1) &&
+      UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) != 0) {
+    ntp_rx = message->originate_ts;
+    local_ntp_rx = &ntp_rx;
+    UTI_ZeroTimespec(&local_tx.ts);
+    interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts);
 
-    if (interleaved) {
-      UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
-      tx_ts = &local_tx;
-    } else {
-      UTI_ZeroNtp64(local_ntp_tx);
-      local_ntp_tx = NULL;
-    }
+    tx_ts = &local_tx;
+    if (interleaved)
+      CLG_DisableNtpTimestamps(&ntp_rx);
   }
 
   /* Suggest the client to increase its polling interval if it indicates
@@ -2158,14 +2420,18 @@
   poll = CLG_GetNtpMinPoll();
   poll = MAX(poll, message->poll);
 
-  /* Send a reply */
-  transmit_packet(my_mode, interleaved, poll, pkt_version,
-                  auth_mode, key_id, &message->receive_ts, &message->transmit_ts,
-                  rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr);
+  /* Respond with the same version */
+  version = info.version;
 
-  /* Save the transmit timestamp */
-  if (tx_ts)
-    UTI_TimespecToNtp64(&tx_ts->ts, local_ntp_tx, NULL);
+  /* Send a reply */
+  if (!transmit_packet(my_mode, interleaved, poll, version, kod, info.ext_field_flags, NULL,
+                       &message->receive_ts, &message->transmit_ts,
+                       rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr,
+                       message, &info))
+    return;
+
+  if (local_ntp_rx)
+    CLG_SaveNtpTimestamps(local_ntp_rx, tx_ts ? &tx_ts->ts : NULL);
 }
 
 /* ================================================== */
@@ -2206,15 +2472,13 @@
 NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
                    NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
 {
-  NTP_Mode pkt_mode;
+  NTP_PacketInfo info;
 
-  if (!check_packet_format(message, length))
+  if (!parse_packet(message, length, &info))
     return;
 
-  pkt_mode = NTP_LVM_TO_MODE(message->lvm);
-
   /* Server and passive mode packets are responses to unknown sources */
-  if (pkt_mode != MODE_CLIENT && pkt_mode != MODE_ACTIVE) {
+  if (info.mode != MODE_CLIENT && info.mode != MODE_ACTIVE) {
     NCR_ProcessTxUnknown(&inst->remote_addr, local_addr, tx_ts, message, length);
     return;
   }
@@ -2229,28 +2493,32 @@
 NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
                      NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
 {
-  NTP_int64 *local_ntp_rx, *local_ntp_tx;
-  NTP_Local_Timestamp local_tx;
-  int log_index;
+  NTP_Local_Timestamp old_tx, new_tx;
+  NTP_int64 *local_ntp_rx;
+  NTP_PacketInfo info;
 
-  if (!check_packet_format(message, length))
+  if (!parse_packet(message, length, &info))
     return;
 
-  if (NTP_LVM_TO_MODE(message->lvm) == MODE_BROADCAST)
+  if (info.mode == MODE_BROADCAST)
     return;
 
-  log_index = CLG_GetClientIndex(&remote_addr->ip_addr);
-  if (log_index < 0)
-    return;
-
-  if (SMT_IsEnabled() && NTP_LVM_TO_MODE(message->lvm) == MODE_SERVER)
+  if (SMT_IsEnabled() && info.mode == MODE_SERVER)
     UTI_AddDoubleToTimespec(&tx_ts->ts, SMT_GetOffset(&tx_ts->ts), &tx_ts->ts);
 
-  CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
+  local_ntp_rx = &message->receive_ts;
+  new_tx = *tx_ts;
 
-  UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
-  update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
-  UTI_TimespecToNtp64(&local_tx.ts, local_ntp_tx, NULL);
+  if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts))
+    return;
+
+  /* Undo a clock adjustment between the RX and TX timestamps to minimise error
+     in the delay measured by the client */
+  CLG_UndoNtpTxTimestampSlew(local_ntp_rx, &new_tx.ts);
+
+  update_tx_timestamp(&old_tx, &new_tx, local_ntp_rx, NULL, message);
+
+  CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts);
 }
 
 /* ================================================== */
@@ -2270,53 +2538,81 @@
   if (!UTI_IsZeroTimespec(&inst->init_local_rx.ts))
     UTI_AdjustTimespec(&inst->init_local_rx.ts, when, &inst->init_local_rx.ts, &delta, dfreq,
                        doffset);
+
+  if (inst->filter)
+    SPF_SlewSamples(inst->filter, when, dfreq, doffset);
 }
 
 /* ================================================== */
 
-void
-NCR_TakeSourceOnline(NCR_Instance inst)
+static void
+set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity)
 {
-  switch (inst->opmode) {
-    case MD_ONLINE:
-      /* Nothing to do */
+  if (connectivity == SRC_MAYBE_ONLINE)
+    connectivity = NIO_IsServerConnectable(&inst->remote_addr) ? SRC_ONLINE : SRC_OFFLINE;
+
+  switch (connectivity) {
+    case SRC_ONLINE:
+      switch (inst->opmode) {
+        case MD_ONLINE:
+          /* Nothing to do */
+          break;
+        case MD_OFFLINE:
+          inst->opmode = MD_ONLINE;
+          NCR_ResetInstance(inst);
+          start_initial_timeout(inst);
+          if (inst->auto_iburst)
+            NCR_InitiateSampleBurst(inst, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES);
+          break;
+        case MD_BURST_WAS_ONLINE:
+          /* Will revert */
+          break;
+        case MD_BURST_WAS_OFFLINE:
+          inst->opmode = MD_BURST_WAS_ONLINE;
+          break;
+        default:
+          assert(0);
+      }
       break;
-    case MD_OFFLINE:
-      LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
-      inst->opmode = MD_ONLINE;
-      NCR_ResetInstance(inst);
-      start_initial_timeout(inst);
+    case SRC_OFFLINE:
+      switch (inst->opmode) {
+        case MD_ONLINE:
+          take_offline(inst);
+          break;
+        case MD_OFFLINE:
+          break;
+        case MD_BURST_WAS_ONLINE:
+          inst->opmode = MD_BURST_WAS_OFFLINE;
+          break;
+        case MD_BURST_WAS_OFFLINE:
+          break;
+        default:
+          assert(0);
+      }
       break;
-    case MD_BURST_WAS_ONLINE:
-      /* Will revert */
-      break;
-    case MD_BURST_WAS_OFFLINE:
-      inst->opmode = MD_BURST_WAS_ONLINE;
-      LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
-      break;
+    default:
+      assert(0);
   }
 }
 
 /* ================================================== */
 
 void
-NCR_TakeSourceOffline(NCR_Instance inst)
+NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity)
 {
-  switch (inst->opmode) {
-    case MD_ONLINE:
-      LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
-      take_offline(inst);
-      break;
-    case MD_OFFLINE:
-      break;
-    case MD_BURST_WAS_ONLINE:
-      inst->opmode = MD_BURST_WAS_OFFLINE;
-      LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
-      break;
-    case MD_BURST_WAS_OFFLINE:
-      break;
-  }
+  OperatingMode prev_opmode;
+  int was_online, is_online;
 
+  prev_opmode = inst->opmode;
+
+  set_connectivity(inst, connectivity);
+
+  /* Report an important change */
+  was_online = prev_opmode == MD_ONLINE || prev_opmode == MD_BURST_WAS_ONLINE;
+  is_online = inst->opmode == MD_ONLINE || inst->opmode == MD_BURST_WAS_ONLINE;
+  if (was_online != is_online)
+    LOG(LOGS_INFO, "Source %s %s",
+        UTI_IPToString(&inst->remote_addr.ip_addr), is_online ? "online" : "offline");
 }
 
 /* ================================================== */
@@ -2324,7 +2620,7 @@
 void
 NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
 {
-  if (new_minpoll < MIN_MINPOLL || new_minpoll > MAX_POLL)
+  if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL)
     return;
   inst->minpoll = new_minpoll;
   LOG(LOGS_INFO, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
@@ -2337,7 +2633,7 @@
 void
 NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
 {
-  if (new_maxpoll < MIN_MAXPOLL || new_maxpoll > MAX_POLL)
+  if (new_maxpoll < MIN_POLL || new_maxpoll > MAX_POLL)
     return;
   inst->maxpoll = new_maxpoll;
   LOG(LOGS_INFO, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);
@@ -2351,7 +2647,7 @@
 NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
 {
   inst->max_delay = CLAMP(0.0, new_max_delay, MAX_MAXDELAY);
-  LOG(LOGS_INFO, "Source %s new max delay %f",
+  LOG(LOGS_INFO, "Source %s new maxdelay %f",
       UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay);
 }
 
@@ -2361,7 +2657,7 @@
 NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
 {
   inst->max_delay_ratio = CLAMP(0.0, new_max_delay_ratio, MAX_MAXDELAYRATIO);
-  LOG(LOGS_INFO, "Source %s new max delay ratio %f",
+  LOG(LOGS_INFO, "Source %s new maxdelayratio %f",
       UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_ratio);
 }
 
@@ -2371,7 +2667,7 @@
 NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio)
 {
   inst->max_delay_dev_ratio = CLAMP(0.0, new_max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
-  LOG(LOGS_INFO, "Source %s new max delay dev ratio %f",
+  LOG(LOGS_INFO, "Source %s new maxdelaydevratio %f",
       UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_dev_ratio);
 }
 
@@ -2453,6 +2749,14 @@
 /* ================================================== */
 
 void
+NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report)
+{
+  NAU_GetReport(inst->auth, report);
+}
+
+/* ================================================== */
+
+void
 NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report)
 {
   *report = inst->report;
@@ -2489,11 +2793,13 @@
     if (server_sock_fd4 == INVALID_SOCK_FD &&
         ADF_IsAnyAllowed(access_auth_table, IPADDR_INET4)) {
       remote_addr.ip_addr.family = IPADDR_INET4;
+      remote_addr.port = 0;
       server_sock_fd4 = NIO_OpenServerSocket(&remote_addr);
     }
     if (server_sock_fd6 == INVALID_SOCK_FD &&
         ADF_IsAnyAllowed(access_auth_table, IPADDR_INET6)) {
       remote_addr.ip_addr.family = IPADDR_INET6;
+      remote_addr.port = 0;
       server_sock_fd6 = NIO_OpenServerSocket(&remote_addr);
     }
   } else {
@@ -2570,6 +2876,14 @@
 
 /* ================================================== */
 
+void
+NCR_DumpAuthData(NCR_Instance inst)
+{
+  NAU_DumpData(inst->auth);
+}
+
+/* ================================================== */
+
 static void
 broadcast_timeout(void *arg)
 {
@@ -2579,13 +2893,14 @@
   int poll;
 
   destination = ARR_GetElement(broadcasts, (long)arg);
-  poll = log(destination->interval) / log(2.0) + 0.5;
+  poll = round(log(destination->interval) / log(2.0));
 
   UTI_ZeroNtp64(&orig_ts);
   zero_local_timestamp(&recv_ts);
 
-  transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts,
-                  NULL, NULL, NULL, &destination->addr, &destination->local_addr);
+  transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, 0, destination->auth,
+                  &orig_ts, &orig_ts, &recv_ts, NULL, NULL, NULL,
+                  &destination->addr, &destination->local_addr, NULL, NULL);
 
   /* Requeue timeout.  We don't care if interval drifts gradually. */
   SCH_AddTimeoutInClass(destination->interval, get_separation(poll), SAMPLING_RANDOMNESS,
@@ -2595,17 +2910,17 @@
 /* ================================================== */
 
 void
-NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval)
+NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval)
 {
   BroadcastDestination *destination;
 
   destination = (BroadcastDestination *)ARR_GetNewElement(broadcasts);
 
-  destination->addr.ip_addr = *addr;
-  destination->addr.port = port;
+  destination->addr = *addr;
   destination->local_addr.ip_addr.family = IPADDR_UNSPEC;
   destination->local_addr.if_index = INVALID_IF_INDEX;
   destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr);
+  destination->auth = NAU_CreateNoneInstance();
   destination->interval = CLAMP(1, interval, 1 << MAX_POLL);
 
   SCH_AddTimeoutInClass(destination->interval, MAX_SAMPLING_SEPARATION, SAMPLING_RANDOMNESS,
diff --git a/chrony_3_3/ntp_core.h b/chrony/ntp_core.h
similarity index 88%
rename from chrony_3_3/ntp_core.h
rename to chrony/ntp_core.h
index f788d68..33f2bb8 100644
--- a/chrony_3_3/ntp_core.h
+++ b/chrony/ntp_core.h
@@ -59,7 +59,8 @@
 extern void NCR_Finalise(void);
 
 /* Get a new instance for a server or peer */
-extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params);
+extern NCR_Instance NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
+                                       SourceParameters *params, const char *name);
 
 /* Destroy an instance */
 extern void NCR_DestroyInstance(NCR_Instance instance);
@@ -74,7 +75,8 @@
 extern void NCR_ResetPoll(NCR_Instance instance);
 
 /* Change the remote address of an instance */
-extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
+extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr,
+                                    int ntp_only);
 
 /* This routine is called when a new packet arrives off the network,
    and it relates to a source we have an ongoing protocol exchange with */
@@ -99,12 +101,9 @@
 /* Slew receive and transmit times in instance records */
 extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
 
-/* Take a particular source online (i.e. start sampling it) */
-extern void NCR_TakeSourceOnline(NCR_Instance inst);
-
-/* Take a particular source offline (i.e. stop sampling it, without
-   marking it unreachable in the source selection stuff) */
-extern void NCR_TakeSourceOffline(NCR_Instance inst);
+/* Take a particular source online (i.e. start sampling it) or offline
+   (i.e. stop sampling it) */
+extern void NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity);
 
 extern void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll);
 
@@ -123,6 +122,7 @@
 extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
 
 extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
+extern void NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report);
 extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
 
 extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
@@ -137,6 +137,8 @@
 
 extern int NCR_IsSyncPeer(NCR_Instance instance);
 
-extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);
+extern void NCR_DumpAuthData(NCR_Instance inst);
+
+extern void NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval);
 
 #endif /* GOT_NTP_CORE_H */
diff --git a/chrony/ntp_ext.c b/chrony/ntp_ext.c
new file mode 100644
index 0000000..d2babb1
--- /dev/null
+++ b/chrony/ntp_ext.c
@@ -0,0 +1,192 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019-2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Functions for adding and parsing NTPv4 extension fields
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "ntp_ext.h"
+
+struct ExtFieldHeader {
+  uint16_t type;
+  uint16_t length;
+};
+
+/* ================================================== */
+
+static int
+format_field(unsigned char *buffer, int buffer_length, int start,
+             int type, int body_length, int *length, void **body)
+{
+  struct ExtFieldHeader *header;
+
+  if (buffer_length < 0 || start < 0 || buffer_length <= start ||
+      buffer_length - start < sizeof (*header) || start % 4 != 0)
+    return 0;
+
+  header = (struct ExtFieldHeader *)(buffer + start);
+
+  if (body_length < 0 || sizeof (*header) + body_length > 0xffff ||
+      start + sizeof (*header) + body_length > buffer_length || body_length % 4 != 0)
+    return 0;
+
+  header->type = htons(type);
+  header->length = htons(sizeof (*header) + body_length);
+  *length = sizeof (*header) + body_length;
+  *body = header + 1;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NEF_SetField(unsigned char *buffer, int buffer_length, int start,
+             int type, void *body, int body_length, int *length)
+{
+  void *ef_body;
+
+  if (!format_field(buffer, buffer_length, start, type, body_length, length, &ef_body))
+    return 0;
+
+  memcpy(ef_body, body, body_length);
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type, int body_length, void **body)
+{
+  int ef_length, length = info->length;
+
+  if (length < NTP_HEADER_LENGTH || length >= sizeof (*packet) || length % 4 != 0)
+    return 0;
+
+  /* Only NTPv4 packets can have extension fields */
+  if (info->version != 4)
+    return 0;
+
+  if (!format_field((unsigned char *)packet, sizeof (*packet), length,
+                    type, body_length, &ef_length, body))
+    return 0;
+
+  if (ef_length < NTP_MIN_EF_LENGTH)
+    return 0;
+
+  info->length += ef_length;
+  info->ext_fields++;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info,
+             int type, void *body, int body_length)
+{
+  void *ef_body;
+
+  if (!NEF_AddBlankField(packet, info, type, body_length, &ef_body))
+    return 0;
+
+  memcpy(ef_body, body, body_length);
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start,
+                     int *length, int *type, void **body, int *body_length)
+{
+  struct ExtFieldHeader *header;
+  int ef_length;
+
+  if (buffer_length < 0 || start < 0 || buffer_length <= start ||
+      buffer_length - start < sizeof (*header))
+    return 0;
+
+  header = (struct ExtFieldHeader *)(buffer + start);
+
+  assert(sizeof (*header) == 4);
+
+  ef_length = ntohs(header->length);
+
+  if (ef_length < (int)(sizeof (*header)) || start + ef_length > buffer_length ||
+      ef_length % 4 != 0)
+    return 0;
+
+  if (length)
+    *length = ef_length;
+  if (type)
+    *type = ntohs(header->type);
+  if (body)
+    *body = header + 1;
+  if (body_length)
+    *body_length = ef_length - sizeof (*header);
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NEF_ParseField(NTP_Packet *packet, int packet_length, int start,
+               int *length, int *type, void **body, int *body_length)
+{
+  int ef_length;
+
+  if (packet_length <= NTP_HEADER_LENGTH || packet_length > sizeof (*packet) ||
+      packet_length <= start || packet_length % 4 != 0 ||
+      start < NTP_HEADER_LENGTH || start % 4 != 0)
+    return 0;
+
+  /* Only NTPv4 packets have extension fields */
+  if (NTP_LVM_TO_VERSION(packet->lvm) != 4)
+    return 0;
+
+  /* Check if the remaining data is a MAC.  RFC 7822 specifies the maximum
+     length of a MAC in NTPv4 packets in order to enable deterministic
+     parsing. */
+  if (packet_length - start <= NTP_MAX_V4_MAC_LENGTH)
+    return 0;
+
+  if (!NEF_ParseSingleField((unsigned char *)packet, packet_length, start,
+                            &ef_length, type, body, body_length))
+    return 0;
+
+  if (ef_length < NTP_MIN_EF_LENGTH)
+    return 0;
+
+  if (length)
+    *length = ef_length;
+
+  return 1;
+}
diff --git a/chrony/ntp_ext.h b/chrony/ntp_ext.h
new file mode 100644
index 0000000..7405be8
--- /dev/null
+++ b/chrony/ntp_ext.h
@@ -0,0 +1,43 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for NTP extension fields
+  */
+
+#ifndef GOT_NTP_EXT_H
+#define GOT_NTP_EXT_H
+
+#include "ntp.h"
+
+extern int NEF_SetField(unsigned char *buffer, int buffer_length, int start,
+                        int type, void *body, int body_length, int *length);
+extern int NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type,
+                             int body_length, void **body);
+extern int NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info,
+                        int type, void *body, int body_length);
+extern int NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start,
+                                int *length, int *type, void **body, int *body_length);
+extern int NEF_ParseField(NTP_Packet *packet, int packet_length, int start,
+                          int *length, int *type, void **body, int *body_length);
+
+#endif
diff --git a/chrony/ntp_io.c b/chrony/ntp_io.c
new file mode 100644
index 0000000..86e3f26
--- /dev/null
+++ b/chrony/ntp_io.c
@@ -0,0 +1,610 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * Copyright (C) Timo Teras  2009
+ * Copyright (C) Miroslav Lichvar  2009, 2013-2016, 2018-2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  This file deals with the IO aspects of reading and writing NTP packets
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "memory.h"
+#include "ntp_io.h"
+#include "ntp_core.h"
+#include "ntp_sources.h"
+#include "ptp.h"
+#include "sched.h"
+#include "socket.h"
+#include "local.h"
+#include "logging.h"
+#include "conf.h"
+#include "privops.h"
+#include "util.h"
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+#include "ntp_io_linux.h"
+#endif
+
+#define INVALID_SOCK_FD -1
+
+/* The server/peer and client sockets for IPv4 and IPv6 */
+static int server_sock_fd4;
+static int server_sock_fd6;
+static int client_sock_fd4;
+static int client_sock_fd6;
+
+/* Reference counters for server sockets to keep them open only when needed */
+static int server_sock_ref4;
+static int server_sock_ref6;
+
+/* Flag indicating we create a new connected client socket for each
+   server instead of sharing client_sock_fd4 and client_sock_fd6 */
+static int separate_client_sockets;
+
+/* Flag indicating the server sockets are not created dynamically when needed,
+   either to have a socket for client requests when separate client sockets
+   are disabled and client port is equal to server port, or the server port is
+   disabled */
+static int permanent_server_sockets;
+
+/* Flag indicating the server IPv4 socket is bound to an address */
+static int bound_server_sock_fd4;
+
+/* PTP event port, or 0 if disabled */
+static int ptp_port;
+
+/* Shared server/client sockets for NTP-over-PTP */
+static int ptp_sock_fd4;
+static int ptp_sock_fd6;
+
+/* Buffer for transmitted NTP-over-PTP messages */
+static PTP_NtpMessage *ptp_message;
+
+/* Flag indicating that we have been initialised */
+static int initialised=0;
+
+/* ================================================== */
+
+/* Forward prototypes */
+static void read_from_socket(int sock_fd, int event, void *anything);
+
+/* ================================================== */
+
+static int
+open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
+{
+  int sock_fd, sock_flags, dscp, events = SCH_FILE_INPUT;
+  IPSockAddr local_addr;
+  char *iface;
+
+  if (!SCK_IsIpFamilyEnabled(family))
+    return INVALID_SOCK_FD;
+
+  if (!client_only) {
+    CNF_GetBindAddress(family, &local_addr.ip_addr);
+    iface = CNF_GetBindNtpInterface();
+  } else {
+    CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
+    iface = CNF_GetBindAcquisitionInterface();
+  }
+
+  local_addr.port = local_port;
+
+  sock_flags = SCK_FLAG_RX_DEST_ADDR | SCK_FLAG_PRIV_BIND;
+  if (!client_only)
+    sock_flags |= SCK_FLAG_BROADCAST;
+
+  sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, iface, sock_flags);
+  if (sock_fd < 0) {
+    if (!client_only)
+      LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
+    return INVALID_SOCK_FD;
+  }
+
+  dscp = CNF_GetNtpDscp();
+  if (dscp > 0 && dscp < 64) {
+#ifdef IP_TOS
+    if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
+      ;
+#endif
+  }
+
+  if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
+    bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
+
+  /* Enable kernel/HW timestamping of packets */
+#ifdef HAVE_LINUX_TIMESTAMPING
+  if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
+#endif
+    if (!SCK_EnableKernelRxTimestamping(sock_fd))
+      ;
+
+  /* Register handler for read and possibly exception events on the socket */
+  SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
+
+  return sock_fd;
+}
+
+/* ================================================== */
+
+static int
+open_separate_client_socket(IPSockAddr *remote_addr)
+{
+  return open_socket(remote_addr->ip_addr.family, 0, 1, remote_addr);
+}
+
+/* ================================================== */
+
+static void
+close_socket(int sock_fd)
+{
+  if (sock_fd == INVALID_SOCK_FD)
+    return;
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+  NIO_Linux_NotifySocketClosing(sock_fd);
+#endif
+  SCH_RemoveFileHandler(sock_fd);
+  SCK_CloseSocket(sock_fd);
+}
+
+/* ================================================== */
+
+void
+NIO_Initialise(void)
+{
+  int server_port, client_port;
+
+  assert(!initialised);
+  initialised = 1;
+
+#ifdef PRIVOPS_BINDSOCKET
+  SCK_SetPrivBind(PRV_BindSocket);
+#endif
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+  NIO_Linux_Initialise();
+#else
+  if (1) {
+    CNF_HwTsInterface *conf_iface;
+    if (CNF_GetHwTsInterface(0, &conf_iface))
+      LOG_FATAL("HW timestamping not supported");
+  }
+#endif
+
+  server_port = CNF_GetNTPPort();
+  client_port = CNF_GetAcquisitionPort();
+
+  /* Use separate connected sockets if client port is negative */
+  separate_client_sockets = client_port < 0;
+  if (client_port < 0)
+    client_port = 0;
+
+  permanent_server_sockets = !server_port || (!separate_client_sockets &&
+                                              client_port == server_port);
+
+  server_sock_fd4 = INVALID_SOCK_FD;
+  server_sock_fd6 = INVALID_SOCK_FD;
+  client_sock_fd4 = INVALID_SOCK_FD;
+  client_sock_fd6 = INVALID_SOCK_FD;
+  server_sock_ref4 = 0;
+  server_sock_ref6 = 0;
+
+  if (permanent_server_sockets && server_port) {
+    server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
+    server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
+  }
+
+  if (!separate_client_sockets) {
+    if (client_port != server_port || !server_port) {
+      client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
+      client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
+    } else {
+      client_sock_fd4 = server_sock_fd4;
+      client_sock_fd6 = server_sock_fd6;
+    }
+  }
+
+  if ((server_port && permanent_server_sockets &&
+       server_sock_fd4 == INVALID_SOCK_FD && server_sock_fd6 == INVALID_SOCK_FD) ||
+      (!separate_client_sockets &&
+       client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
+    LOG_FATAL("Could not open NTP sockets");
+  }
+
+  ptp_port = CNF_GetPtpPort();
+  ptp_sock_fd4 = INVALID_SOCK_FD;
+  ptp_sock_fd6 = INVALID_SOCK_FD;
+  ptp_message = NULL;
+
+  if (ptp_port > 0) {
+    ptp_sock_fd4 = open_socket(IPADDR_INET4, ptp_port, 0, NULL);
+    ptp_sock_fd6 = open_socket(IPADDR_INET6, ptp_port, 0, NULL);
+    ptp_message = MallocNew(PTP_NtpMessage);
+  }
+}
+
+/* ================================================== */
+
+void
+NIO_Finalise(void)
+{
+  if (server_sock_fd4 != client_sock_fd4)
+    close_socket(client_sock_fd4);
+  close_socket(server_sock_fd4);
+  server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
+
+  if (server_sock_fd6 != client_sock_fd6)
+    close_socket(client_sock_fd6);
+  close_socket(server_sock_fd6);
+  server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
+
+  close_socket(ptp_sock_fd4);
+  close_socket(ptp_sock_fd6);
+  ptp_sock_fd4 = ptp_sock_fd6 = INVALID_SOCK_FD;
+  Free(ptp_message);
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+  NIO_Linux_Finalise();
+#endif
+
+  initialised = 0;
+}
+
+/* ================================================== */
+
+int
+NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
+{
+  switch (remote_addr->ip_addr.family) {
+    case IPADDR_INET4:
+      if (ptp_port > 0 && remote_addr->port == ptp_port)
+        return ptp_sock_fd4;
+      if (separate_client_sockets)
+        return open_separate_client_socket(remote_addr);
+      return client_sock_fd4;
+    case IPADDR_INET6:
+      if (ptp_port > 0 && remote_addr->port == ptp_port)
+        return ptp_sock_fd6;
+      if (separate_client_sockets)
+        return open_separate_client_socket(remote_addr);
+      return client_sock_fd6;
+    default:
+      return INVALID_SOCK_FD;
+  }
+}
+
+/* ================================================== */
+
+int
+NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
+{
+  switch (remote_addr->ip_addr.family) {
+    case IPADDR_INET4:
+      if (ptp_port > 0 && remote_addr->port == ptp_port)
+        return ptp_sock_fd4;
+      if (permanent_server_sockets)
+        return server_sock_fd4;
+      if (server_sock_fd4 == INVALID_SOCK_FD)
+        server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNTPPort(), 0, NULL);
+      if (server_sock_fd4 != INVALID_SOCK_FD)
+        server_sock_ref4++;
+      return server_sock_fd4;
+    case IPADDR_INET6:
+      if (ptp_port > 0 && remote_addr->port == ptp_port)
+        return ptp_sock_fd6;
+      if (permanent_server_sockets)
+        return server_sock_fd6;
+      if (server_sock_fd6 == INVALID_SOCK_FD)
+        server_sock_fd6 = open_socket(IPADDR_INET6, CNF_GetNTPPort(), 0, NULL);
+      if (server_sock_fd6 != INVALID_SOCK_FD)
+        server_sock_ref6++;
+      return server_sock_fd6;
+    default:
+      return INVALID_SOCK_FD;
+  }
+}
+
+/* ================================================== */
+
+static int
+is_ptp_socket(int sock_fd)
+{
+  return ptp_port > 0 && sock_fd != INVALID_SOCK_FD &&
+    (sock_fd == ptp_sock_fd4 || sock_fd == ptp_sock_fd6);
+}
+
+/* ================================================== */
+
+void
+NIO_CloseClientSocket(int sock_fd)
+{
+  if (is_ptp_socket(sock_fd))
+    return;
+
+  if (separate_client_sockets)
+    close_socket(sock_fd);
+}
+
+/* ================================================== */
+
+void
+NIO_CloseServerSocket(int sock_fd)
+{
+  if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD || is_ptp_socket(sock_fd))
+    return;
+
+  if (sock_fd == server_sock_fd4) {
+    if (--server_sock_ref4 <= 0) {
+      close_socket(server_sock_fd4);
+      server_sock_fd4 = INVALID_SOCK_FD;
+    }
+  } else if (sock_fd == server_sock_fd6) {
+    if (--server_sock_ref6 <= 0) {
+      close_socket(server_sock_fd6);
+      server_sock_fd6 = INVALID_SOCK_FD;
+    }
+  } else {
+    assert(0);
+  }
+}
+
+/* ================================================== */
+
+int
+NIO_IsServerSocket(int sock_fd)
+{
+  return sock_fd != INVALID_SOCK_FD &&
+    (sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6 || is_ptp_socket(sock_fd));
+}
+
+/* ================================================== */
+
+int
+NIO_IsServerSocketOpen(void)
+{
+  return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD ||
+    ptp_sock_fd4 != INVALID_SOCK_FD || ptp_sock_fd6 != INVALID_SOCK_FD;
+}
+
+/* ================================================== */
+
+int
+NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
+{
+  int sock_fd;
+
+  sock_fd = open_separate_client_socket(remote_addr);
+  if (sock_fd == INVALID_SOCK_FD)
+    return 0;
+
+  close_socket(sock_fd);
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+process_message(SCK_Message *message, int sock_fd, int event)
+{
+  NTP_Local_Address local_addr;
+  NTP_Local_Timestamp local_ts;
+  struct timespec sched_ts;
+
+  SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
+  local_ts.source = NTP_TS_DAEMON;
+  sched_ts = local_ts.ts;
+
+  if (message->addr_type != SCK_ADDR_IP) {
+    DEBUG_LOG("Unexpected address type");
+    return;
+  }
+
+  local_addr.ip_addr = message->local_addr.ip;
+  local_addr.if_index = message->if_index;;
+  local_addr.sock_fd = sock_fd;
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+  if (NIO_Linux_ProcessMessage(message, &local_addr, &local_ts, event))
+    return;
+#else
+  if (!UTI_IsZeroTimespec(&message->timestamp.kernel)) {
+    LCL_CookTime(&message->timestamp.kernel, &local_ts.ts, &local_ts.err);
+    local_ts.source = NTP_TS_KERNEL;
+  }
+#endif
+
+  if (local_ts.source != NTP_TS_DAEMON)
+    DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
+              UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
+
+  if (!NIO_UnwrapMessage(message, sock_fd))
+    return;
+
+  /* Just ignore the packet if it's not of a recognized length */
+  if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
+    DEBUG_LOG("Unexpected length");
+    return;
+  }
+
+  NSR_ProcessRx(&message->remote_addr.ip, &local_addr, &local_ts, message->data, message->length);
+}
+
+/* ================================================== */
+
+static void
+read_from_socket(int sock_fd, int event, void *anything)
+{
+  SCK_Message *messages;
+  int i, received, flags = 0;
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+  if (NIO_Linux_ProcessEvent(sock_fd, event))
+    return;
+#endif
+
+  if (event == SCH_FILE_EXCEPTION) {
+#ifdef HAVE_LINUX_TIMESTAMPING
+    flags |= SCK_FLAG_MSG_ERRQUEUE;
+#else
+    assert(0);
+#endif
+  }
+
+  messages = SCK_ReceiveMessages(sock_fd, flags, &received);
+  if (!messages)
+    return;
+
+  for (i = 0; i < received; i++)
+    process_message(&messages[i], sock_fd, event);
+}
+
+/* ================================================== */
+
+int
+NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
+{
+  PTP_NtpMessage *msg;
+
+  if (!is_ptp_socket(sock_fd))
+    return 1;
+
+  if (message->length <= PTP_NTP_PREFIX_LENGTH) {
+    DEBUG_LOG("Unexpected length");
+    return 0;
+  }
+
+  msg = message->data;
+
+  if (msg->header.type != PTP_TYPE_DELAY_REQ || msg->header.version != PTP_VERSION ||
+      ntohs(msg->header.length) != message->length ||
+      msg->header.domain != PTP_DOMAIN_NTP ||
+      ntohs(msg->header.flags) != PTP_FLAG_UNICAST ||
+      ntohs(msg->tlv_header.type) != PTP_TLV_NTP ||
+      ntohs(msg->tlv_header.length) != message->length - PTP_NTP_PREFIX_LENGTH) {
+    DEBUG_LOG("Unexpected PTP message");
+    return 0;
+  }
+
+  message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH;
+  message->length -= PTP_NTP_PREFIX_LENGTH;
+
+  DEBUG_LOG("Unwrapped PTP->NTP len=%d", message->length);
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+wrap_message(SCK_Message *message, int sock_fd)
+{
+  assert(PTP_NTP_PREFIX_LENGTH == 48);
+
+  if (!is_ptp_socket(sock_fd))
+    return 1;
+
+  if (!ptp_message)
+    return 0;
+
+  if (message->length < NTP_HEADER_LENGTH ||
+      message->length + PTP_NTP_PREFIX_LENGTH > sizeof (*ptp_message)) {
+    DEBUG_LOG("Unexpected length");
+    return 0;
+  }
+
+  memset(ptp_message, 0, PTP_NTP_PREFIX_LENGTH);
+  ptp_message->header.type = PTP_TYPE_DELAY_REQ;
+  ptp_message->header.version = PTP_VERSION;
+  ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
+  ptp_message->header.domain = PTP_DOMAIN_NTP;
+  ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
+  ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
+  ptp_message->tlv_header.length = htons(message->length);
+  memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
+
+  message->data = ptp_message;
+  message->length += PTP_NTP_PREFIX_LENGTH;
+
+  DEBUG_LOG("Wrapped NTP->PTP len=%d", message->length - PTP_NTP_PREFIX_LENGTH);
+
+  return 1;
+}
+
+/* ================================================== */
+/* Send a packet to remote address from local address */
+
+int
+NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
+               NTP_Local_Address *local_addr, int length, int process_tx)
+{
+  SCK_Message message;
+
+  assert(initialised);
+
+  if (local_addr->sock_fd == INVALID_SOCK_FD) {
+    DEBUG_LOG("No socket to send to %s", UTI_IPSockAddrToString(remote_addr));
+    return 0;
+  }
+
+  SCK_InitMessage(&message, SCK_ADDR_IP);
+
+  message.data = packet;
+  message.length = length;
+
+  if (!wrap_message(&message, local_addr->sock_fd))
+    return 0;
+
+  /* Specify remote address if the socket is not connected */
+  if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
+    message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
+    message.remote_addr.ip.port = remote_addr->port;
+  }
+
+  message.local_addr.ip = local_addr->ip_addr;
+
+  /* Don't require responses to non-link-local addresses to use the same
+     interface */
+  message.if_index = SCK_IsLinkLocalIPAddress(&message.remote_addr.ip.ip_addr) ?
+                       local_addr->if_index : INVALID_IF_INDEX;
+
+#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
+  /* On FreeBSD a local IPv4 address cannot be specified on bound socket */
+  if (message.local_addr.ip.family == IPADDR_INET4 &&
+      (bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
+    message.local_addr.ip.family = IPADDR_UNSPEC;
+#endif
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+  if (process_tx)
+    NIO_Linux_RequestTxTimestamp(&message, local_addr->sock_fd);
+#endif
+
+  if (!SCK_SendMessage(local_addr->sock_fd, &message, 0))
+    return 0;
+
+  return 1;
+}
diff --git a/chrony_3_3/ntp_io.h b/chrony/ntp_io.h
similarity index 82%
rename from chrony_3_3/ntp_io.h
rename to chrony/ntp_io.h
index 1bdcf12..19ffeed 100644
--- a/chrony_3_3/ntp_io.h
+++ b/chrony/ntp_io.h
@@ -31,9 +31,10 @@
 
 #include "ntp.h"
 #include "addressing.h"
+#include "socket.h"
 
 /* Function to initialise the module. */
-extern void NIO_Initialise(int family);
+extern void NIO_Initialise(void);
 
 /* Function to finalise the module */
 extern void NIO_Finalise(void);
@@ -53,6 +54,15 @@
 /* Function to check if socket is a server socket */
 extern int NIO_IsServerSocket(int sock_fd);
 
+/* Function to check if a server socket is currently open */
+extern int NIO_IsServerSocketOpen(void);
+
+/* Function to check if client packets can be sent to a server */
+extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
+
+/* Function to unwrap an NTP message from non-native transport (e.g. PTP) */
+extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd);
+
 /* Function to transmit a packet */
 extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
                           NTP_Local_Address *local_addr, int length, int process_tx);
diff --git a/chrony_3_3/ntp_io_linux.c b/chrony/ntp_io_linux.c
similarity index 75%
rename from chrony_3_3/ntp_io_linux.c
rename to chrony/ntp_io_linux.c
index 22e1abb..4d099cc 100644
--- a/chrony_3_3/ntp_io_linux.c
+++ b/chrony/ntp_io_linux.c
@@ -2,7 +2,7 @@
   chronyd/chronyc - Programs for keeping computer clocks accurate.
 
  **********************************************************************
- * Copyright (C) Miroslav Lichvar  2016-2018
+ * Copyright (C) Miroslav Lichvar  2016-2019
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -24,17 +24,13 @@
   Functions for NTP I/O specific to Linux
   */
 
-/* Ugly, include our local versions from more recent kernel first */
-#include "linux-errqueue.h"
-#include "linux-net_tstamp.h"
-#define SCM_TIMESTAMPING_PKTINFO	58
-
 #include "config.h"
 
 #include "sysincl.h"
 
 #include <ifaddrs.h>
 #include <linux/ethtool.h>
+#include <linux/net_tstamp.h>
 #include <linux/sockios.h>
 #include <net/if.h>
 
@@ -48,17 +44,10 @@
 #include "ntp_io_linux.h"
 #include "ntp_sources.h"
 #include "sched.h"
+#include "socket.h"
 #include "sys_linux.h"
 #include "util.h"
 
-union sockaddr_in46 {
-  struct sockaddr_in in4;
-#ifdef FEAT_IPV6
-  struct sockaddr_in6 in6;
-#endif
-  struct sockaddr u;
-};
-
 struct Interface {
   char name[IF_NAMESIZE];
   int if_index;
@@ -84,7 +73,7 @@
 /* Minimum interval between PHC readings */
 #define MIN_PHC_POLL -6
 
-/* Maximum acceptable offset between HW and daemon/kernel timestamp */
+/* Maximum acceptable offset between SW/HW and daemon timestamp */
 #define MAX_TS_DELAY 1.0
 
 /* Array of Interfaces */
@@ -126,7 +115,7 @@
   struct ethtool_ts_info ts_info;
   struct hwtstamp_config ts_config;
   struct ifreq req;
-  int sock_fd, if_index, phc_fd, req_hwts_flags;
+  int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
   unsigned int i;
   struct Interface *iface;
 
@@ -136,7 +125,7 @@
       return 1;
   }
 
-  sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+  sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
   if (sock_fd < 0)
     return 0;
 
@@ -145,13 +134,13 @@
 
   if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
       sizeof (req.ifr_name)) {
-    close(sock_fd);
+    SCK_CloseSocket(sock_fd);
     return 0;
   }
 
   if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
     DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
-    close(sock_fd);
+    SCK_CloseSocket(sock_fd);
     return 0;
   }
 
@@ -162,7 +151,7 @@
 
   if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
     DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
-    close(sock_fd);
+    SCK_CloseSocket(sock_fd);
     return 0;
   }
 
@@ -170,47 +159,73 @@
                    SOF_TIMESTAMPING_RAW_HARDWARE;
   if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
     DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
-    close(sock_fd);
+    SCK_CloseSocket(sock_fd);
     return 0;
   }
 
-  ts_config.flags = 0;
-  ts_config.tx_type = HWTSTAMP_TX_ON;
+  if (ts_info.phc_index < 0) {
+    DEBUG_LOG("PHC missing on %s", req.ifr_name);
+    SCK_CloseSocket(sock_fd);
+    return 0;
+  }
 
   switch (conf_iface->rxfilter) {
     case CNF_HWTS_RXFILTER_ANY:
 #ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
       if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_NTP_ALL))
-        ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
+        rx_filter = HWTSTAMP_FILTER_NTP_ALL;
       else
 #endif
       if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))
-        ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
+        rx_filter = HWTSTAMP_FILTER_ALL;
       else
-        ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
+        rx_filter = HWTSTAMP_FILTER_NONE;
       break;
     case CNF_HWTS_RXFILTER_NONE:
-      ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
+      rx_filter = HWTSTAMP_FILTER_NONE;
       break;
 #ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
     case CNF_HWTS_RXFILTER_NTP:
-      ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
+      rx_filter = HWTSTAMP_FILTER_NTP_ALL;
       break;
 #endif
+    case CNF_HWTS_RXFILTER_PTP:
+      if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT))
+        rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+      else if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_EVENT))
+        rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+      else
+        rx_filter = HWTSTAMP_FILTER_NONE;
+      break;
     default:
-      ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
+      rx_filter = HWTSTAMP_FILTER_ALL;
       break;
   }
 
+  ts_config.flags = 0;
+  ts_config.tx_type = HWTSTAMP_TX_ON;
+  ts_config.rx_filter = rx_filter;
   req.ifr_data = (char *)&ts_config;
 
   if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
-    DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
-    close(sock_fd);
-    return 0;
+    LOG(errno == EPERM ? LOGS_ERR : LOGS_DEBUG,
+        "ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
+
+    /* Check the current timestamping configuration in case this interface
+       allows only reading of the configuration and it was already configured
+       as requested */
+    req.ifr_data = (char *)&ts_config;
+#ifdef SIOCGHWTSTAMP
+    if (ioctl(sock_fd, SIOCGHWTSTAMP, &req) ||
+        ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter)
+#endif
+    {
+      SCK_CloseSocket(sock_fd);
+      return 0;
+    }
   }
 
-  close(sock_fd);
+  SCK_CloseSocket(sock_fd);
 
   phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
   if (phc_fd < 0)
@@ -233,7 +248,8 @@
   iface->tx_comp = conf_iface->tx_comp;
   iface->rx_comp = conf_iface->rx_comp;
 
-  iface->clock = HCL_CreateInstance(UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
+  iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
+                                    UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
 
   LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
       ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -278,7 +294,7 @@
   struct ifreq req;
   int sock_fd, link_speed;
 
-  sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+  sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
   if (sock_fd < 0)
     return;
 
@@ -291,11 +307,11 @@
 
   if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
     DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
-    close(sock_fd);
+    SCK_CloseSocket(sock_fd);
     return;
   }
 
-  close(sock_fd);
+  SCK_CloseSocket(sock_fd);
 
   link_speed = ethtool_cmd_speed(&cmd);
 
@@ -313,17 +329,16 @@
 {
   int sock_fd;
 
-  sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+  sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
   if (sock_fd < 0)
     return 0;
 
-  if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
-    DEBUG_LOG("Could not enable timestamping option %x", option);
-    close(sock_fd);
+  if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, option)) {
+    SCK_CloseSocket(sock_fd);
     return 0;
   }
 
-  close(sock_fd);
+  SCK_CloseSocket(sock_fd);
   return 1;
 }
 #endif
@@ -335,19 +350,15 @@
 {
   int sock_fd, events = 0;
 
-  if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
-#ifdef FEAT_IPV6
-      && (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
-#endif
-     )
+  sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
+  if (sock_fd < 0)
     return INVALID_SOCK_FD;
 
   if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
-    close(sock_fd);
+    SCK_CloseSocket(sock_fd);
     return INVALID_SOCK_FD;
   }
 
-  UTI_FdSetCloexec(sock_fd);
   return sock_fd;
 }
 
@@ -417,7 +428,7 @@
   unsigned int i;
 
   if (dummy_rxts_socket != INVALID_SOCK_FD)
-    close(dummy_rxts_socket);
+    SCK_CloseSocket(dummy_rxts_socket);
 
   for (i = 0; i < ARR_GetSize(interfaces); i++) {
     iface = ARR_GetElement(interfaces, i);
@@ -447,14 +458,12 @@
   if (client_only || permanent_ts_options)
     flags |= ts_tx_flags;
 
-  if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
-    LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
+  if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, val)) {
     ts_flags = 0;
     return 0;
   }
 
-  if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
-    LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
+  if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, flags)) {
     ts_flags = 0;
     return 0;
   }
@@ -580,7 +589,11 @@
   if (rx_ntp_length && iface->link_speed) {
     if (!l2_length)
       l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
-                   iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
+                   iface->l2_udp6_ntp_start) + rx_ntp_length;
+
+    /* Include the frame check sequence (FCS) */
+    l2_length += 4;
+
     rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
 
     UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
@@ -607,6 +620,28 @@
 }
 
 /* ================================================== */
+
+static void
+process_sw_timestamp(struct timespec *sw_ts, NTP_Local_Timestamp *local_ts)
+{
+  double ts_delay, local_err;
+  struct timespec ts;
+
+  LCL_CookTime(sw_ts, &ts, &local_err);
+
+  ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
+
+  if (fabs(ts_delay) > MAX_TS_DELAY) {
+    DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
+    return;
+  }
+
+  local_ts->ts = ts;
+  local_ts->err = local_err;
+  local_ts->source = NTP_TS_KERNEL;
+}
+
+/* ================================================== */
 /* Extract UDP data from a layer 2 message.  Supported is Ethernet
    with optional VLAN tags. */
 
@@ -614,7 +649,6 @@
 extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
 {
   unsigned char *msg_start = msg;
-  union sockaddr_in46 addr;
 
   remote_addr->ip_addr.family = IPADDR_UNSPEC;
   remote_addr->port = 0;
@@ -637,19 +671,21 @@
   /* Parse destination address and port from IPv4/IPv6 and UDP headers */
   if (len >= 20 && msg[0] >> 4 == 4) {
     int ihl = (msg[0] & 0xf) * 4;
+    uint32_t addr;
 
     if (len < ihl + 8 || msg[9] != 17)
       return 0;
 
-    memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t));
-    addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2);
-    addr.in4.sin_family = AF_INET;
+    memcpy(&addr, msg + 16, sizeof (addr));
+    remote_addr->ip_addr.addr.in4 = ntohl(addr);
+    remote_addr->port = ntohs(*(uint16_t *)(msg + ihl + 2));
+    remote_addr->ip_addr.family = IPADDR_INET4;
     len -= ihl + 8, msg += ihl + 8;
 #ifdef FEAT_IPV6
   } else if (len >= 48 && msg[0] >> 4 == 6) {
     int eh_len, next_header = msg[6];
 
-    memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
+    memcpy(&remote_addr->ip_addr.addr.in6, msg + 24, sizeof (remote_addr->ip_addr.addr.in6));
     len -= 40, msg += 40;
 
     /* Skip IPv6 extension headers if present */
@@ -681,16 +717,14 @@
       len -= eh_len, msg += eh_len;
     }
 
-    addr.in6.sin6_port = *(uint16_t *)(msg + 2);
-    addr.in6.sin6_family = AF_INET6;
+    remote_addr->port = ntohs(*(uint16_t *)(msg + 2));
+    remote_addr->ip_addr.family = IPADDR_INET6;
     len -= 8, msg += 8;
 #endif
   } else {
     return 0;
   }
 
-  UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port);
-
   /* Move the message to fix alignment of its fields */
   if (len > 0)
     memmove(msg_start, msg, len);
@@ -701,72 +735,38 @@
 /* ================================================== */
 
 int
-NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
-                         NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
+NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
+                         NTP_Local_Timestamp *local_ts, int event)
 {
   struct Interface *iface;
-  struct cmsghdr *cmsg;
   int is_tx, ts_if_index, l2_length;
 
-  is_tx = hdr->msg_flags & MSG_ERRQUEUE;
+  is_tx = event == SCH_FILE_EXCEPTION;
   iface = NULL;
-  ts_if_index = local_addr->if_index;
-  l2_length = 0;
 
-  for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
-#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
-    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) {
-      struct scm_ts_pktinfo ts_pktinfo;
+  ts_if_index = message->timestamp.if_index;
+  if (ts_if_index == INVALID_IF_INDEX)
+    ts_if_index = message->if_index;
+  l2_length = message->timestamp.l2_length;
 
-      memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
-
-      ts_if_index = ts_pktinfo.if_index;
-      l2_length = ts_pktinfo.pkt_length;
-
-      DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length);
-    }
-#endif
-
-    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
-      struct scm_timestamping ts3;
-
-      memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
-
-      if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
-        iface = get_interface(ts_if_index);
-        if (iface) {
-          process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
-                               remote_addr->ip_addr.family, l2_length);
-        } else {
-          DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
-        }
-
-        /* If a HW transmit timestamp was received, resume processing
-           of non-error messages on this socket */
-        if (is_tx)
-          resume_socket(local_addr->sock_fd);
-      }
-
-      if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
-          (!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) {
-        LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
-        local_ts->source = NTP_TS_KERNEL;
-      }
+  if (!UTI_IsZeroTimespec(&message->timestamp.hw)) {
+    iface = get_interface(ts_if_index);
+    if (iface) {
+      process_hw_timestamp(iface, &message->timestamp.hw, local_ts, !is_tx ? message->length : 0,
+                           message->remote_addr.ip.ip_addr.family, l2_length);
+    } else {
+      DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
     }
 
-    if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
-        (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
-      struct sock_extended_err err;
+    /* If a HW transmit timestamp was received, resume processing
+       of non-error messages on this socket */
+    if (is_tx)
+      resume_socket(local_addr->sock_fd);
+  }
 
-      memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
-
-      if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
-          err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
-        DEBUG_LOG("Unknown extended error");
-        /* Drop the message */
-        return 1;
-      }
-    }
+  if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
+      (!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
+    process_sw_timestamp(&message->timestamp.kernel, local_ts);
   }
 
   /* If the kernel is slow with enabling RX timestamping, open a dummy
@@ -784,19 +784,19 @@
   /* The data from the error queue includes all layers up to UDP.  We have to
      extract the UDP data and also the destination address with port as there
      currently doesn't seem to be a better way to get them both. */
-  l2_length = length;
-  length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
+  l2_length = message->length;
+  message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length);
 
-  DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
-            l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
-            local_addr->sock_fd, local_addr->if_index, local_ts->source);
+  DEBUG_LOG("Extracted message for %s fd=%d len=%d",
+            UTI_IPSockAddrToString(&message->remote_addr.ip),
+            local_addr->sock_fd, message->length);
 
   /* Update assumed position of UDP data at layer 2 for next received packet */
-  if (iface && length) {
-    if (remote_addr->ip_addr.family == IPADDR_INET4)
-      iface->l2_udp4_ntp_start = l2_length - length;
-    else if (remote_addr->ip_addr.family == IPADDR_INET6)
-      iface->l2_udp6_ntp_start = l2_length - length;
+  if (iface && message->length) {
+    if (message->remote_addr.ip.ip_addr.family == IPADDR_INET4)
+      iface->l2_udp4_ntp_start = l2_length - message->length;
+    else if (message->remote_addr.ip.ip_addr.family == IPADDR_INET6)
+      iface->l2_udp6_ntp_start = l2_length - message->length;
   }
 
   /* Drop the message if it has no timestamp or its processing failed */
@@ -805,24 +805,24 @@
     return 1;
   }
 
-  if (length < NTP_NORMAL_PACKET_LENGTH)
+  if (!NIO_UnwrapMessage(message, local_addr->sock_fd))
     return 1;
 
-  NSR_ProcessTx(remote_addr, local_addr, local_ts,
-                (NTP_Packet *)hdr->msg_iov[0].iov_base, length);
+  if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet))
+    return 1;
+
+  NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
 
   return 1;
 }
 
 /* ================================================== */
 
-int
-NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
+void
+NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
 {
-  struct cmsghdr *cmsg;
-
   if (!ts_flags)
-    return cmsglen;
+    return;
 
   /* If a HW transmit timestamp is requested on a client socket, monitor
      events on the socket in order to avoid processing of a fast response
@@ -832,22 +832,9 @@
 
   /* Check if TX timestamping is disabled on this socket */
   if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
-    return cmsglen;
+    return;
 
-  /* Add control message that will enable TX timestamping for this message.
-     Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
-     control messages. */
-  cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen);
-  memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
-  cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
-
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SO_TIMESTAMPING;
-  cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags));
-
-  memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags));
-
-  return cmsglen;
+  message->timestamp.tx_flags = ts_tx_flags;
 }
 
 /* ================================================== */
diff --git a/chrony_3_3/ntp_io_linux.h b/chrony/ntp_io_linux.h
similarity index 86%
rename from chrony_3_3/ntp_io_linux.h
rename to chrony/ntp_io_linux.h
index ed37e6a..4d3af13 100644
--- a/chrony_3_3/ntp_io_linux.h
+++ b/chrony/ntp_io_linux.h
@@ -27,6 +27,8 @@
 #ifndef GOT_NTP_IO_LINUX_H
 #define GOT_NTP_IO_LINUX_H
 
+#include "socket.h"
+
 extern void NIO_Linux_Initialise(void);
 
 extern void NIO_Linux_Finalise(void);
@@ -35,10 +37,10 @@
 
 extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
 
-extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
-                                    NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
+extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
+                                    NTP_Local_Timestamp *local_ts, int event);
 
-extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
+extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
 
 extern void NIO_Linux_NotifySocketClosing(int sock_fd);
 
diff --git a/chrony_3_3/ntp_signd.c b/chrony/ntp_signd.c
similarity index 78%
rename from chrony_3_3/ntp_signd.c
rename to chrony/ntp_signd.c
index 6328b61..77b3249 100644
--- a/chrony_3_3/ntp_signd.c
+++ b/chrony/ntp_signd.c
@@ -34,6 +34,7 @@
 #include "ntp_io.h"
 #include "ntp_signd.h"
 #include "sched.h"
+#include "socket.h"
 #include "util.h"
 
 /* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
@@ -90,19 +91,11 @@
 static unsigned int queue_head;
 static unsigned int queue_tail;
 
-#define INVALID_SOCK_FD -1
+#define INVALID_SOCK_FD (-6)
 
 /* Unix domain socket connected to ntp_signd */
 static int sock_fd;
 
-#define MIN_AUTH_DELAY 1.0e-5
-#define MAX_AUTH_DELAY 1.0e-2
-
-/* Average time needed for signing one packet.  This is used to adjust the
-   transmit timestamp in NTP packets.  The timestamp won't be very accurate as
-   the delay is variable, but it should be good enough for MS-SNTP clients. */
-static double auth_delay;
-
 /* Flag indicating if the MS-SNTP authentication is enabled */
 static int enabled;
 
@@ -116,7 +109,7 @@
 close_socket(void)
 {
   SCH_RemoveFileHandler(sock_fd);
-  close(sock_fd);
+  SCK_CloseSocket(sock_fd);
   sock_fd = INVALID_SOCK_FD;
 
   /* Empty the queue */
@@ -128,35 +121,23 @@
 static int
 open_socket(void)
 {
-  struct sockaddr_un s;
+  char path[PATH_MAX];
 
-  if (sock_fd >= 0)
+  if (sock_fd != INVALID_SOCK_FD)
     return 1;
 
-  sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-  if (sock_fd < 0) {
-    DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
-    return 0;
-  }
-
-  UTI_FdSetCloexec(sock_fd);
-  SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
-
-  s.sun_family = AF_UNIX;
-  if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
-               CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
+  if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) {
     DEBUG_LOG("signd socket path too long");
-    close_socket();
     return 0;
   }
 
-  if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
-    DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
-    close_socket();
+  sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
+  if (sock_fd < 0) {
+    sock_fd = INVALID_SOCK_FD;
     return 0;
   }
 
-  DEBUG_LOG("Connected to signd");
+  SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
 
   return 1;
 }
@@ -194,10 +175,6 @@
   NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
                  ntohl(inst->response.length) + sizeof (inst->response.length) -
                  offsetof(SigndResponse, signed_packet), 0);
-
-  /* Update exponential moving average of the authentication delay */
-  delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
-  auth_delay += 0.1 * (delay - auth_delay);
 }
 
 /* ================================================== */
@@ -218,16 +195,14 @@
     if (!inst->sent)
       SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
 
-    s = send(sock_fd, (char *)&inst->request + inst->sent,
-             inst->request_length - inst->sent, 0);
+    s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent,
+                 inst->request_length - inst->sent, 0);
 
     if (s < 0) {
-      DEBUG_LOG("signd socket error: %s", strerror(errno));
       close_socket();
       return;
     }
 
-    DEBUG_LOG("Sent %d bytes to signd", s);
     inst->sent += s;
 
     /* Try again later if the request is not complete yet */
@@ -246,20 +221,14 @@
     }
 
     assert(inst->received < sizeof (inst->response));
-    s = recv(sock_fd, (char *)&inst->response + inst->received,
-             sizeof (inst->response) - inst->received, 0);
+    s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received,
+                    sizeof (inst->response) - inst->received, 0);
 
     if (s <= 0) {
-      if (s < 0)
-        DEBUG_LOG("signd socket error: %s", strerror(errno));
-      else
-        DEBUG_LOG("signd socket closed");
-
       close_socket();
       return;
     }
 
-    DEBUG_LOG("Received %d bytes from signd", s);
     inst->received += s;
 
     if (inst->received < sizeof (inst->response.length))
@@ -293,7 +262,6 @@
 NSD_Initialise()
 {
   sock_fd = INVALID_SOCK_FD;
-  auth_delay = MIN_AUTH_DELAY;
   enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
 
   if (!enabled)
@@ -320,15 +288,9 @@
 
 /* ================================================== */
 
-extern int NSD_GetAuthDelay(uint32_t key_id)
-{
-  return 1.0e9 * auth_delay;
-}
-
-/* ================================================== */
-
 int
-NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
+NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
+                      NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
 {
   SignInstance *inst;
 
@@ -342,7 +304,7 @@
     return 0;
   }
 
-  if (length != NTP_NORMAL_PACKET_LENGTH) {
+  if (info->length != NTP_HEADER_LENGTH) {
     DEBUG_LOG("Invalid packet length");
     return 0;
   }
@@ -355,7 +317,7 @@
   inst->local_addr = *local_addr;
   inst->sent = 0;
   inst->received = 0;
-  inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
+  inst->request_length = offsetof(SigndRequest, packet_to_sign) + info->length;
 
   /* The length field doesn't include itself */
   inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
@@ -365,7 +327,7 @@
   inst->request._pad = 0;
   inst->request.key_id = htonl(key_id);
 
-  memcpy(&inst->request.packet_to_sign, packet, length);
+  memcpy(&inst->request.packet_to_sign, packet, info->length);
 
   /* Enable output if there was no pending request */
   if (IS_QUEUE_EMPTY())
diff --git a/chrony_3_3/ntp_signd.h b/chrony/ntp_signd.h
similarity index 87%
rename from chrony_3_3/ntp_signd.h
rename to chrony/ntp_signd.h
index f45a5cb..d333c9a 100644
--- a/chrony_3_3/ntp_signd.h
+++ b/chrony/ntp_signd.h
@@ -35,10 +35,8 @@
 /* Finalisation function */
 extern void NSD_Finalise(void);
 
-/* Function to get an estimate of delay due to signing */
-extern int NSD_GetAuthDelay(uint32_t key_id);
-
 /* Function to sign an NTP packet and send it */
-extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
+extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
+                                 NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
 
 #endif
diff --git a/chrony/ntp_sources.c b/chrony/ntp_sources.c
new file mode 100644
index 0000000..3cbb2ae
--- /dev/null
+++ b/chrony/ntp_sources.c
@@ -0,0 +1,1430 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * Copyright (C) Miroslav Lichvar  2011-2012, 2014, 2016, 2020-2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Functions which manage the pool of NTP sources that we are currently
+  a client of or peering with.
+
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "array.h"
+#include "ntp_sources.h"
+#include "ntp_core.h"
+#include "ntp_io.h"
+#include "util.h"
+#include "logging.h"
+#include "local.h"
+#include "memory.h"
+#include "nameserv_async.h"
+#include "privops.h"
+#include "sched.h"
+
+/* ================================================== */
+
+/* Maximum number of sources */
+#define MAX_SOURCES 65536
+
+/* Record type private to this file, used to store information about
+   particular sources */
+typedef struct {
+  NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL
+                                      means this slot in table is in use
+                                      (an IPADDR_ID address means the address
+                                      is not resolved yet) */
+  NCR_Instance data;            /* Data for the protocol engine for this source */
+  char *name;                   /* Name of the source as it was specified
+                                   (may be an IP address) */
+  int pool_id;                  /* ID of the pool from which was this source
+                                   added or INVALID_POOL */
+  int tentative;                /* Flag indicating there was no valid response
+                                   received from the source yet */
+  uint32_t conf_id;             /* Configuration ID, which can be shared with
+                                   different sources in case of a pool */
+} SourceRecord;
+
+/* Hash table of SourceRecord, its size is a power of two and it's never
+   more than half full */
+static ARR_Instance records;
+
+/* Number of sources in the hash table */
+static int n_sources;
+
+/* Flag indicating new sources will be started automatically when added */
+static int auto_start_sources = 0;
+
+/* Flag indicating a record is currently being modified */
+static int record_lock;
+
+/* Last assigned address ID */
+static uint32_t last_address_id = 0;
+
+/* Last assigned configuration ID */
+static uint32_t last_conf_id = 0;
+
+/* Source scheduled for name resolving (first resolving or replacement) */
+struct UnresolvedSource {
+  /* Current address of the source (IPADDR_ID is used for a single source
+     with unknown address and IPADDR_UNSPEC for a pool of sources) */
+  NTP_Remote_Address address;
+  /* ID of the pool if not a single source */
+  int pool_id;
+  /* Name to be resolved */
+  char *name;
+  /* Flag indicating addresses should be used in a random order */
+  int random_order;
+  /* Next unresolved source in the list */
+  struct UnresolvedSource *next;
+};
+
+#define RESOLVE_INTERVAL_UNIT 7
+#define MIN_RESOLVE_INTERVAL 2
+#define MAX_RESOLVE_INTERVAL 9
+#define MIN_REPLACEMENT_INTERVAL 8
+
+static struct UnresolvedSource *unresolved_sources = NULL;
+static int resolving_interval = 0;
+static int resolving_restart = 0;
+static SCH_TimeoutID resolving_id;
+static struct UnresolvedSource *resolving_source = NULL;
+static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
+
+#define MAX_POOL_SOURCES 16
+#define INVALID_POOL (-1)
+
+/* Pool of sources with the same name */
+struct SourcePool {
+  /* Number of all sources from the pool */
+  int sources;
+  /* Number of sources with unresolved address */
+  int unresolved_sources;
+  /* Number of non-tentative sources */
+  int confirmed_sources;
+  /* Maximum number of confirmed sources */
+  int max_sources;
+};
+
+/* Array of SourcePool (indexed by their ID) */
+static ARR_Instance pools;
+
+/* Requested update of a source's address */
+struct AddressUpdate {
+  NTP_Remote_Address old_address;
+  NTP_Remote_Address new_address;
+};
+
+/* Update saved when record_lock is true */
+static struct AddressUpdate saved_address_update;
+
+/* ================================================== */
+/* Forward prototypes */
+
+static void resolve_sources(void);
+static void rehash_records(void);
+static void handle_saved_address_update(void);
+static void clean_source_record(SourceRecord *record);
+static void remove_pool_sources(int pool_id, int tentative, int unresolved);
+static void remove_unresolved_source(struct UnresolvedSource *us);
+
+static void
+slew_sources(struct timespec *raw,
+             struct timespec *cooked,
+             double dfreq,
+             double doffset,
+             LCL_ChangeType change_type,
+             void *anything);
+
+/* ================================================== */
+
+/* Flag indicating whether module is initialised */
+static int initialised = 0;
+
+/* ================================================== */
+
+static SourceRecord *
+get_record(unsigned index)
+{
+  return (SourceRecord *)ARR_GetElement(records, index);
+}
+
+/* ================================================== */
+
+static struct SourcePool *
+get_pool(unsigned index)
+{
+  return (struct SourcePool *)ARR_GetElement(pools, index);
+}
+
+/* ================================================== */
+
+void
+NSR_Initialise(void)
+{
+  n_sources = 0;
+  initialised = 1;
+
+  records = ARR_CreateInstance(sizeof (SourceRecord));
+  rehash_records();
+
+  pools = ARR_CreateInstance(sizeof (struct SourcePool));
+
+  LCL_AddParameterChangeHandler(slew_sources, NULL);
+}
+
+/* ================================================== */
+
+void
+NSR_Finalise(void)
+{
+  NSR_RemoveAllSources();
+
+  LCL_RemoveParameterChangeHandler(slew_sources, NULL);
+
+  ARR_DestroyInstance(records);
+  ARR_DestroyInstance(pools);
+
+  while (unresolved_sources)
+    remove_unresolved_source(unresolved_sources);
+
+  initialised = 0;
+}
+
+/* ================================================== */
+/* Find a slot matching an IP address.  It is assumed that there can
+   only ever be one record for a particular IP address. */
+
+static int
+find_slot(IPAddr *ip_addr, int *slot)
+{
+  SourceRecord *record;
+  uint32_t hash;
+  unsigned int i, size;
+
+  size = ARR_GetSize(records);
+
+  *slot = 0;
+  
+  switch (ip_addr->family) {
+    case IPADDR_INET4:
+    case IPADDR_INET6:
+    case IPADDR_ID:
+      break;
+    default:
+      return 0;
+  }
+
+  hash = UTI_IPToHash(ip_addr);
+
+  for (i = 0; i < size / 2; i++) {
+    /* Use quadratic probing */
+    *slot = (hash + (i + i * i) / 2) % size;
+    record = get_record(*slot);
+
+    if (!record->remote_addr)
+      break;
+
+    if (UTI_CompareIPs(&record->remote_addr->ip_addr, ip_addr, NULL) == 0)
+      return 1;
+  }
+
+  return 0;
+}
+
+/* ================================================== */
+/* Find a slot matching an IP address and port. The function returns:
+   0 => IP not matched, empty slot returned if a valid address was provided
+   1 => Only IP matched, port doesn't match
+   2 => Both IP and port matched. */
+
+static int
+find_slot2(NTP_Remote_Address *remote_addr, int *slot)
+{
+  if (!find_slot(&remote_addr->ip_addr, slot))
+    return 0;
+
+  return get_record(*slot)->remote_addr->port == remote_addr->port ? 2 : 1;
+}
+
+/* ================================================== */
+/* Check if hash table of given size is sufficient to contain sources */
+
+static int
+check_hashtable_size(unsigned int sources, unsigned int size)
+{
+  return sources * 2 <= size;
+}
+
+/* ================================================== */
+
+static void
+rehash_records(void)
+{
+  SourceRecord *temp_records;
+  unsigned int i, old_size, new_size;
+  int slot;
+
+  assert(!record_lock);
+
+  old_size = ARR_GetSize(records);
+
+  temp_records = MallocArray(SourceRecord, old_size);
+  memcpy(temp_records, ARR_GetElements(records), old_size * sizeof (SourceRecord));
+
+  /* The size of the hash table is always a power of two */
+  for (new_size = 1; !check_hashtable_size(n_sources, new_size); new_size *= 2)
+    ;
+
+  ARR_SetSize(records, new_size);
+
+  for (i = 0; i < new_size; i++)
+    get_record(i)->remote_addr = NULL;
+
+  for (i = 0; i < old_size; i++) {
+    if (!temp_records[i].remote_addr)
+      continue;
+
+    if (find_slot2(temp_records[i].remote_addr, &slot) != 0)
+      assert(0);
+
+    *get_record(slot) = temp_records[i];
+  }
+
+  Free(temp_records);
+}
+
+/* ================================================== */
+
+/* Procedure to add a new source */
+static NSR_Status
+add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
+           SourceParameters *params, int pool_id, uint32_t conf_id)
+{
+  SourceRecord *record;
+  int slot;
+
+  assert(initialised);
+
+  /* Find empty bin & check that we don't have the address already */
+  if (find_slot2(remote_addr, &slot) != 0) {
+    return NSR_AlreadyInUse;
+  } else if (!name && !UTI_IsIPReal(&remote_addr->ip_addr)) {
+    /* Name is required for non-real addresses */
+    return NSR_InvalidName;
+  } else if (n_sources >= MAX_SOURCES) {
+    return NSR_TooManySources;
+  } else {
+    if (remote_addr->ip_addr.family != IPADDR_INET4 &&
+        remote_addr->ip_addr.family != IPADDR_INET6 &&
+        remote_addr->ip_addr.family != IPADDR_ID) {
+      return NSR_InvalidAF;
+    } else {
+      n_sources++;
+
+      if (!check_hashtable_size(n_sources, ARR_GetSize(records))) {
+        rehash_records();
+        if (find_slot2(remote_addr, &slot) != 0)
+          assert(0);
+      }
+
+      assert(!record_lock);
+      record_lock = 1;
+
+      record = get_record(slot);
+      assert(!name || !UTI_IsStringIP(name));
+      record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
+      record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
+      record->remote_addr = NCR_GetRemoteAddress(record->data);
+      record->pool_id = pool_id;
+      record->tentative = 1;
+      record->conf_id = conf_id;
+
+      record_lock = 0;
+
+      if (record->pool_id != INVALID_POOL) {
+        get_pool(record->pool_id)->sources++;
+        if (!UTI_IsIPReal(&remote_addr->ip_addr))
+          get_pool(record->pool_id)->unresolved_sources++;
+      }
+
+      if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
+        NCR_StartInstance(record->data);
+
+      /* The new instance is allowed to change its address immediately */
+      handle_saved_address_update();
+
+      return NSR_Success;
+    }
+  }
+}
+
+/* ================================================== */
+
+static NSR_Status
+change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr,
+                      int replacement)
+{
+  int slot1, slot2, found;
+  SourceRecord *record;
+  LOG_Severity severity;
+  char *name;
+
+  found = find_slot2(old_addr, &slot1);
+  if (found != 2)
+    return NSR_NoSuchSource;
+
+  /* Make sure there is no other source using the new address (with the same
+     or different port), but allow a source to have its port changed */
+  found = find_slot2(new_addr, &slot2);
+  if (found == 2 || (found != 0 && slot1 != slot2))
+    return NSR_AlreadyInUse;
+
+  assert(!record_lock);
+  record_lock = 1;
+
+  record = get_record(slot1);
+  NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
+
+  if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
+      UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
+    assert(0);
+
+  if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
+    if (auto_start_sources)
+      NCR_StartInstance(record->data);
+    if (record->pool_id != INVALID_POOL)
+      get_pool(record->pool_id)->unresolved_sources--;
+  }
+
+  if (!record->tentative) {
+    record->tentative = 1;
+
+    if (record->pool_id != INVALID_POOL)
+      get_pool(record->pool_id)->confirmed_sources--;
+  }
+
+  record_lock = 0;
+
+  name = record->name;
+  severity = UTI_IsIPReal(&old_addr->ip_addr) ? LOGS_INFO : LOGS_DEBUG;
+
+  if (found == 0) {
+    /* The hash table must be rebuilt for the changed address */
+    rehash_records();
+
+    LOG(severity, "Source %s %s %s (%s)", UTI_IPToString(&old_addr->ip_addr),
+        replacement ? "replaced with" : "changed to",
+        UTI_IPToString(&new_addr->ip_addr), name);
+  } else {
+    LOG(severity, "Source %s (%s) changed port to %d",
+        UTI_IPToString(&new_addr->ip_addr), name, new_addr->port);
+  }
+
+  return NSR_Success;
+}
+
+/* ================================================== */
+
+static void
+handle_saved_address_update(void)
+{
+  if (!UTI_IsIPReal(&saved_address_update.old_address.ip_addr))
+    return;
+
+  if (change_source_address(&saved_address_update.old_address,
+                            &saved_address_update.new_address, 0) != NSR_Success)
+    /* This is expected to happen only if the old address is wrong */
+    LOG(LOGS_ERR, "Could not change %s to %s",
+        UTI_IPSockAddrToString(&saved_address_update.old_address),
+        UTI_IPSockAddrToString(&saved_address_update.new_address));
+
+  saved_address_update.old_address.ip_addr.family = IPADDR_UNSPEC;
+}
+
+/* ================================================== */
+
+static int
+replace_source_connectable(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
+{
+  if (!NIO_IsServerConnectable(new_addr)) {
+    DEBUG_LOG("%s not connectable", UTI_IPToString(&new_addr->ip_addr));
+    return 0;
+  }
+
+  if (change_source_address(old_addr, new_addr, 1) == NSR_AlreadyInUse)
+    return 0;
+
+  handle_saved_address_update();
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs)
+{
+  NTP_Remote_Address old_addr, new_addr;
+  SourceRecord *record;
+  unsigned short first = 0;
+  int i, j;
+
+  if (us->random_order)
+    UTI_GetRandomBytes(&first, sizeof (first));
+
+  for (i = 0; i < n_addrs; i++) {
+    new_addr.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
+
+    DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
+
+    if (us->pool_id != INVALID_POOL) {
+      /* In the pool resolving mode, try to replace a source from
+         the pool which does not have a real address yet */
+      for (j = 0; j < ARR_GetSize(records); j++) {
+        record = get_record(j);
+        if (!record->remote_addr || record->pool_id != us->pool_id ||
+            UTI_IsIPReal(&record->remote_addr->ip_addr))
+          continue;
+        old_addr = *record->remote_addr;
+        new_addr.port = old_addr.port;
+        if (replace_source_connectable(&old_addr, &new_addr))
+          ;
+        break;
+      }
+    } else {
+      new_addr.port = us->address.port;
+      if (replace_source_connectable(&us->address, &new_addr))
+        break;
+    }
+  }
+}
+
+/* ================================================== */
+
+static int
+is_resolved(struct UnresolvedSource *us)
+{
+  int slot;
+
+  if (us->pool_id != INVALID_POOL) {
+    return get_pool(us->pool_id)->unresolved_sources <= 0;
+  } else {
+    /* If the address is no longer present, it was removed or replaced
+       (i.e. resolved) */
+    return find_slot2(&us->address, &slot) == 0;
+  }
+}
+
+/* ================================================== */
+
+static void
+resolve_sources_timeout(void *arg)
+{
+  resolving_id = 0;
+  resolve_sources();
+}
+
+/* ================================================== */
+
+static void
+name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything)
+{
+  struct UnresolvedSource *us, *next;
+
+  us = (struct UnresolvedSource *)anything;
+
+  assert(us == resolving_source);
+  assert(resolving_id == 0);
+
+  DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs);
+
+  switch (status) {
+    case DNS_TryAgain:
+      break;
+    case DNS_Success:
+      process_resolved_name(us, ip_addrs, n_addrs);
+      break;
+    case DNS_Failure:
+      LOG(LOGS_WARN, "Invalid host %s", us->name);
+      break;
+    default:
+      assert(0);
+  }
+
+  next = us->next;
+
+  /* Don't repeat the resolving if it (permanently) failed, it was a
+     replacement of a real address, or all addresses are already resolved */
+  if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us))
+    remove_unresolved_source(us);
+
+  /* If a restart was requested and this was the last source in the list,
+     start with the first source again (if there still is one) */
+  if (!next && resolving_restart) {
+    next = unresolved_sources;
+    resolving_restart = 0;
+  }
+
+  resolving_source = next;
+
+  if (next) {
+    /* Continue with the next source in the list */
+    DEBUG_LOG("resolving %s", next->name);
+    DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
+  } else {
+    /* This was the last source in the list. If some sources couldn't
+       be resolved, try again in exponentially increasing interval. */
+    if (unresolved_sources) {
+      resolving_interval = CLAMP(MIN_RESOLVE_INTERVAL, resolving_interval + 1,
+                                 MAX_RESOLVE_INTERVAL);
+      resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT * (1 << resolving_interval),
+                                           resolve_sources_timeout, NULL);
+    } else {
+      resolving_interval = 0;
+    }
+
+    /* This round of resolving is done */
+    if (resolving_end_handler)
+      (resolving_end_handler)();
+  }
+}
+
+/* ================================================== */
+
+static void
+resolve_sources(void)
+{
+  struct UnresolvedSource *us, *next, *i;
+
+  assert(!resolving_source);
+
+  /* Remove sources that don't need to be resolved anymore */
+  for (i = unresolved_sources; i; i = next) {
+    next = i->next;
+    if (is_resolved(i))
+      remove_unresolved_source(i);
+  }
+
+  if (!unresolved_sources)
+    return;
+
+  PRV_ReloadDNS();
+
+  /* Start with the first source in the list, name_resolve_handler
+     will iterate over the rest */
+  us = unresolved_sources;
+
+  resolving_source = us;
+  DEBUG_LOG("resolving %s", us->name);
+  DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
+}
+
+/* ================================================== */
+
+static void
+append_unresolved_source(struct UnresolvedSource *us)
+{
+  struct UnresolvedSource **i;
+
+  for (i = &unresolved_sources; *i; i = &(*i)->next)
+    ;
+  *i = us;
+  us->next = NULL;
+}
+
+/* ================================================== */
+
+static void
+remove_unresolved_source(struct UnresolvedSource *us)
+{
+  struct UnresolvedSource **i;
+
+  for (i = &unresolved_sources; *i; i = &(*i)->next) {
+    if (*i == us) {
+      *i = us->next;
+      Free(us->name);
+      Free(us);
+      break;
+    }
+  }
+}
+
+/* ================================================== */
+
+static int get_unused_pool_id(void)
+{
+  struct UnresolvedSource *us;
+  int i;
+
+  for (i = 0; i < ARR_GetSize(pools); i++) {
+    if (get_pool(i)->sources > 0)
+      continue;
+
+    /* Make sure there is no name waiting to be resolved using this pool */
+    for (us = unresolved_sources; us; us = us->next) {
+      if (us->pool_id == i)
+        break;
+    }
+    if (us)
+      continue;
+
+    return i;
+  }
+
+  return INVALID_POOL;
+}
+
+/* ================================================== */
+
+NSR_Status
+NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
+              SourceParameters *params, uint32_t *conf_id)
+{
+  NSR_Status s;
+
+  s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
+  if (s != NSR_Success)
+    return s;
+
+  last_conf_id++;
+  if (conf_id)
+    *conf_id = last_conf_id;
+
+  return s;
+}
+
+/* ================================================== */
+
+NSR_Status
+NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
+                    SourceParameters *params, uint32_t *conf_id)
+{
+  struct UnresolvedSource *us;
+  struct SourcePool *sp;
+  NTP_Remote_Address remote_addr;
+  int i, new_sources, pool_id;
+
+  /* If the name is an IP address, add the source with the address directly */
+  if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
+    remote_addr.port = port;
+    return NSR_AddSource(&remote_addr, type, params, conf_id);
+  }
+
+  /* Make sure the name is at least printable and has no spaces */
+  for (i = 0; name[i] != '\0'; i++) {
+    if (!isgraph((unsigned char)name[i]))
+      return NSR_InvalidName;
+  }
+
+  us = MallocNew(struct UnresolvedSource);
+  us->name = Strdup(name);
+  us->random_order = 0;
+
+  remote_addr.ip_addr.family = IPADDR_ID;
+  remote_addr.ip_addr.addr.id = ++last_address_id;
+  remote_addr.port = port;
+
+  if (!pool) {
+    us->pool_id = INVALID_POOL;
+    us->address = remote_addr;
+    new_sources = 1;
+  } else {
+    pool_id = get_unused_pool_id();
+    if (pool_id != INVALID_POOL) {
+      sp = get_pool(pool_id);
+    } else {
+      sp = ARR_GetNewElement(pools);
+      pool_id = ARR_GetSize(pools) - 1;
+    }
+
+    sp->sources = 0;
+    sp->unresolved_sources = 0;
+    sp->confirmed_sources = 0;
+    sp->max_sources = CLAMP(1, params->max_sources, MAX_POOL_SOURCES);
+    us->pool_id = pool_id;
+    us->address.ip_addr.family = IPADDR_UNSPEC;
+    new_sources = MIN(2 * sp->max_sources, MAX_POOL_SOURCES);
+  }
+
+  append_unresolved_source(us);
+
+  last_conf_id++;
+  if (conf_id)
+    *conf_id = last_conf_id;
+
+  for (i = 0; i < new_sources; i++) {
+    if (i > 0)
+      remote_addr.ip_addr.addr.id = ++last_address_id;
+    if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success)
+      return NSR_TooManySources;
+  }
+
+  return NSR_UnresolvedName;
+}
+
+/* ================================================== */
+
+void
+NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
+{
+  resolving_end_handler = handler;
+}
+
+/* ================================================== */
+
+void
+NSR_ResolveSources(void)
+{
+  /* Try to resolve unresolved sources now */
+  if (unresolved_sources) {
+    /* Allow only one resolving to be running at a time */
+    if (!resolving_source) {
+      if (resolving_id != 0) {
+        SCH_RemoveTimeout(resolving_id);
+        resolving_id = 0;
+        resolving_interval--;
+      }
+      resolve_sources();
+    } else {
+      /* Try again as soon as the current resolving ends */
+      resolving_restart = 1;
+    }
+  } else {
+    /* No unresolved sources, we are done */
+    if (resolving_end_handler)
+      (resolving_end_handler)();
+  }
+}
+
+/* ================================================== */
+
+void NSR_StartSources(void)
+{
+  NTP_Remote_Address *addr;
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    addr = get_record(i)->remote_addr;
+    if (!addr || !UTI_IsIPReal(&addr->ip_addr))
+      continue;
+    NCR_StartInstance(get_record(i)->data);
+  }
+}
+
+/* ================================================== */
+
+void NSR_AutoStartSources(void)
+{
+  auto_start_sources = 1;
+}
+
+/* ================================================== */
+
+static void
+clean_source_record(SourceRecord *record)
+{
+  assert(record->remote_addr);
+
+  if (record->pool_id != INVALID_POOL) {
+    struct SourcePool *pool = get_pool(record->pool_id);
+
+    pool->sources--;
+    if (!UTI_IsIPReal(&record->remote_addr->ip_addr))
+      pool->unresolved_sources--;
+    if (!record->tentative)
+      pool->confirmed_sources--;
+    if (pool->max_sources > pool->sources)
+      pool->max_sources = pool->sources;
+  }
+
+  record->remote_addr = NULL;
+  NCR_DestroyInstance(record->data);
+  Free(record->name);
+
+  n_sources--;
+}
+
+/* ================================================== */
+
+/* Procedure to remove a source.  We don't bother whether the port
+   address is matched - we're only interested in removing a record for
+   the right IP address. */
+NSR_Status
+NSR_RemoveSource(IPAddr *address)
+{
+  int slot;
+
+  assert(initialised);
+
+  if (find_slot(address, &slot) == 0)
+    return NSR_NoSuchSource;
+
+  clean_source_record(get_record(slot));
+
+  /* Rehash the table to make sure there are no broken probe sequences.
+     This is costly, but it's not expected to happen frequently. */
+
+  rehash_records();
+
+  return NSR_Success;
+}
+
+/* ================================================== */
+
+void
+NSR_RemoveSourcesById(uint32_t conf_id)
+{
+  SourceRecord *record;
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (!record->remote_addr || record->conf_id != conf_id)
+      continue;
+    clean_source_record(record);
+  }
+
+  rehash_records();
+}
+
+/* ================================================== */
+
+void
+NSR_RemoveAllSources(void)
+{
+  SourceRecord *record;
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (!record->remote_addr)
+      continue;
+    clean_source_record(record);
+  }
+
+  rehash_records();
+}
+
+/* ================================================== */
+
+static void
+resolve_source_replacement(SourceRecord *record)
+{
+  struct UnresolvedSource *us;
+
+  DEBUG_LOG("trying to replace %s (%s)",
+            UTI_IPToString(&record->remote_addr->ip_addr), record->name);
+
+  us = MallocNew(struct UnresolvedSource);
+  us->name = Strdup(record->name);
+  /* If there never was a valid reply from this source (e.g. it was a bad
+     replacement), ignore the order of addresses from the resolver to not get
+     stuck to a pair of addresses if the order doesn't change, or a group of
+     IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
+  us->random_order = record->tentative;
+  us->pool_id = INVALID_POOL;
+  us->address = *record->remote_addr;
+
+  append_unresolved_source(us);
+  NSR_ResolveSources();
+}
+
+/* ================================================== */
+
+void
+NSR_HandleBadSource(IPAddr *address)
+{
+  static struct timespec last_replacement;
+  struct timespec now;
+  SourceRecord *record;
+  IPAddr ip_addr;
+  double diff;
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return;
+
+  record = get_record(slot);
+
+  /* Don't try to replace a source specified by an IP address unless the
+     address changed since the source was added (e.g. by NTS-KE) */
+  if (UTI_StringToIP(record->name, &ip_addr) &&
+      UTI_CompareIPs(&record->remote_addr->ip_addr, &ip_addr, NULL) == 0)
+    return;
+
+  /* Don't resolve names too frequently */
+  SCH_GetLastEventTime(NULL, NULL, &now);
+  diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
+  if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
+    DEBUG_LOG("replacement postponed");
+    return;
+  }
+  last_replacement = now;
+
+  resolve_source_replacement(record);
+}
+
+/* ================================================== */
+
+void
+NSR_RefreshAddresses(void)
+{
+  SourceRecord *record;
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (!record->remote_addr)
+      continue;
+
+    resolve_source_replacement(record);
+  }
+}
+
+/* ================================================== */
+
+NSR_Status
+NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
+{
+  int slot;
+
+  if (!UTI_IsIPReal(&old_addr->ip_addr) || !UTI_IsIPReal(&new_addr->ip_addr))
+    return NSR_InvalidAF;
+
+  if (UTI_CompareIPs(&old_addr->ip_addr, &new_addr->ip_addr, NULL) != 0 &&
+      find_slot(&new_addr->ip_addr, &slot))
+    return NSR_AlreadyInUse;
+
+  /* If a record is being modified (e.g. by change_source_address(), or the
+     source is just being created), postpone the change to avoid corruption */
+
+  if (!record_lock)
+    return change_source_address(old_addr, new_addr, 0);
+
+  if (UTI_IsIPReal(&saved_address_update.old_address.ip_addr))
+    return NSR_TooManySources;
+
+  saved_address_update.old_address = *old_addr;
+  saved_address_update.new_address = *new_addr;
+
+  return NSR_Success;
+}
+
+/* ================================================== */
+
+static void remove_pool_sources(int pool_id, int tentative, int unresolved)
+{
+  SourceRecord *record;
+  unsigned int i, removed;
+
+  for (i = removed = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+
+    if (!record->remote_addr || record->pool_id != pool_id)
+      continue;
+
+    if ((tentative && !record->tentative) ||
+        (unresolved && UTI_IsIPReal(&record->remote_addr->ip_addr)))
+      continue;
+
+    DEBUG_LOG("removing %ssource %s", tentative ? "tentative " : "",
+              UTI_IPToString(&record->remote_addr->ip_addr));
+
+    clean_source_record(record);
+    removed++;
+  }
+
+  if (removed)
+    rehash_records();
+}
+
+/* ================================================== */
+
+uint32_t
+NSR_GetLocalRefid(IPAddr *address)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  return NCR_GetLocalRefid(get_record(slot)->data);
+}
+
+/* ================================================== */
+
+char *
+NSR_GetName(IPAddr *address)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return NULL;
+
+  return get_record(slot)->name;
+}
+
+/* ================================================== */
+
+/* This routine is called by ntp_io when a new packet arrives off the network,
+   possibly with an authentication tail */
+void
+NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+              NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
+{
+  SourceRecord *record;
+  struct SourcePool *pool;
+  int slot;
+
+  assert(initialised);
+
+  /* Avoid unnecessary lookup if the packet cannot be a response from our
+     source.  Otherwise, it must match both IP address and port number. */
+  if (NTP_LVM_TO_MODE(message->lvm) != MODE_CLIENT &&
+      find_slot2(remote_addr, &slot) == 2) {
+    record = get_record(slot);
+
+    if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
+      return;
+
+    if (record->tentative) {
+      /* This was the first good reply from the source */
+      record->tentative = 0;
+
+      if (record->pool_id != INVALID_POOL) {
+        pool = get_pool(record->pool_id);
+        pool->confirmed_sources++;
+
+        DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->confirmed_sources);
+
+        /* If the number of sources from the pool reached the configured
+           maximum, remove the remaining tentative sources */
+        if (pool->confirmed_sources >= pool->max_sources)
+          remove_pool_sources(record->pool_id, 1, 0);
+      }
+    }
+  } else {
+    NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
+  }
+}
+
+/* ================================================== */
+
+void
+NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+              NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
+{
+  SourceRecord *record;
+  int slot;
+
+  /* Avoid unnecessary lookup if the packet cannot be a request to our
+     source.  Otherwise, it must match both IP address and port number. */
+  if (NTP_LVM_TO_MODE(message->lvm) != MODE_SERVER &&
+      find_slot2(remote_addr, &slot) == 2) {
+    record = get_record(slot);
+    NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
+  } else {
+    NCR_ProcessTxUnknown(remote_addr, local_addr, tx_ts, message, length);
+  }
+}
+
+/* ================================================== */
+
+static void
+slew_sources(struct timespec *raw,
+             struct timespec *cooked,
+             double dfreq,
+             double doffset,
+             LCL_ChangeType change_type,
+             void *anything)
+{
+  SourceRecord *record;
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (record->remote_addr) {
+      if (change_type == LCL_ChangeUnknownStep) {
+        NCR_ResetInstance(record->data);
+        NCR_ResetPoll(record->data);
+      } else {
+        NCR_SlewTimes(record->data, cooked, dfreq, doffset);
+      }
+    }
+  }
+}
+
+/* ================================================== */
+
+int
+NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
+{
+  SourceRecord *record, *syncpeer;
+  unsigned int i, any;
+
+  if (connectivity != SRC_OFFLINE)
+    NSR_ResolveSources();
+
+  any = 0;
+  syncpeer = NULL;
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (record->remote_addr) {
+      /* Ignore SRC_MAYBE_ONLINE connectivity change for unspecified unresolved
+         sources as they would always end up in the offline state */
+      if ((address->family == IPADDR_UNSPEC &&
+           (connectivity != SRC_MAYBE_ONLINE || UTI_IsIPReal(&record->remote_addr->ip_addr))) ||
+          !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
+        any = 1;
+        if (NCR_IsSyncPeer(record->data)) {
+          syncpeer = record;
+          continue;
+        }
+        NCR_SetConnectivity(record->data, connectivity);
+      }
+    }
+  }
+
+  /* Set the sync peer last to avoid unnecessary reference switching */
+  if (syncpeer)
+    NCR_SetConnectivity(syncpeer->data, connectivity);
+
+  return any;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
+                        IPAddr *mask, IPAddr *address)
+{
+  SourceRecord *record;
+  unsigned int i;
+  int any;
+
+  any = 0;
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (record->remote_addr) {
+      if (address->family == IPADDR_UNSPEC ||
+          !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
+        any = 1;
+        NCR_InitiateSampleBurst(record->data, n_good_samples, n_total_samples);
+      }
+    }
+  }
+
+  return any;
+
+}
+
+/* ================================================== */
+/* The ip address is assumed to be completed on input, that is how we
+   identify the source record. */
+
+void
+NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
+{
+  int slot;
+
+  if (find_slot(&report->ip_addr, &slot)) {
+    NCR_ReportSource(get_record(slot)->data, report, now);
+  } else {
+    report->poll = 0;
+    report->latest_meas_ago = 0;
+  }
+}
+
+/* ================================================== */
+
+int
+NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
+{
+  int slot;
+
+  if (!find_slot(address, &slot))
+    return 0;
+
+  NCR_GetAuthReport(get_record(slot)->data, report);
+  return 1;
+}
+
+/* ================================================== */
+/* The ip address is assumed to be completed on input, that is how we
+   identify the source record. */
+
+int
+NSR_GetNTPReport(RPT_NTPReport *report)
+{
+  int slot;
+
+  if (!find_slot(&report->remote_addr, &slot))
+    return 0;
+
+  NCR_GetNTPReport(get_record(slot)->data, report);
+  return 1;
+}
+
+/* ================================================== */
+
+void
+NSR_GetActivityReport(RPT_ActivityReport *report)
+{
+  SourceRecord *record;
+  unsigned int i;
+
+  report->online = 0;
+  report->offline = 0;
+  report->burst_online = 0;
+  report->burst_offline = 0;
+  report->unresolved = 0;
+
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (!record->remote_addr)
+      continue;
+
+    if (!UTI_IsIPReal(&record->remote_addr->ip_addr)) {
+      report->unresolved++;
+    } else {
+      NCR_IncrementActivityCounters(record->data, &report->online, &report->offline,
+                                    &report->burst_online, &report->burst_offline);
+    }
+  }
+}
+
+/* ================================================== */
+
+void
+NSR_DumpAuthData(void)
+{
+  SourceRecord *record;
+  int i;
+
+  for (i = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+    if (!record->remote_addr)
+      continue;
+    NCR_DumpAuthData(record->data);
+  }
+}
diff --git a/chrony_3_3/ntp_sources.h b/chrony/ntp_sources.h
similarity index 76%
rename from chrony_3_3/ntp_sources.h
rename to chrony/ntp_sources.h
index 23e9612..ba3b9c4 100644
--- a/chrony_3_3/ntp_sources.h
+++ b/chrony/ntp_sources.h
@@ -44,16 +44,21 @@
   NSR_NoSuchSource, /* Remove - attempt to remove a source that is not known */
   NSR_AlreadyInUse, /* AddSource - attempt to add a source that is already known */ 
   NSR_TooManySources, /* AddSource - too many sources already present */
-  NSR_InvalidAF /* AddSource - attempt to add a source with invalid address family */
+  NSR_InvalidAF, /* AddSource - attempt to add a source with invalid address family */
+  NSR_InvalidName, /* AddSourceByName - attempt to add a source with invalid name */
+  NSR_UnresolvedName, /* AddSourceByName - name will be resolved later */
 } NSR_Status;
 
 /* Procedure to add a new server or peer source. */
-extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params);
+extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
+                                SourceParameters *params, uint32_t *conf_id);
 
 /* Procedure to add a new server, peer source, or pool of servers specified by
    name instead of address.  The name is resolved in exponentially increasing
-   intervals until it succeeds or fails with a non-temporary error. */
-extern void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params);
+   intervals until it succeeds or fails with a non-temporary error.  If the
+   name is an address, it is equivalent to NSR_AddSource(). */
+extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
+                                      SourceParameters *params, uint32_t *conf_id);
 
 /* Function type for handlers to be called back when an attempt
  * (possibly unsuccessful) to resolve unresolved sources ends */
@@ -72,7 +77,10 @@
 extern void NSR_AutoStartSources(void);
 
 /* Procedure to remove a source */
-extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
+extern NSR_Status NSR_RemoveSource(IPAddr *address);
+
+/* Procedure to remove all sources matching a configuration ID */
+extern void NSR_RemoveSourcesById(uint32_t conf_id);
 
 /* Procedure to remove all sources */
 extern void NSR_RemoveAllSources(void);
@@ -83,9 +91,18 @@
 /* Procedure to resolve all names again */
 extern void NSR_RefreshAddresses(void);
 
+/* Procedure to update the address of a source.  The update may be
+   postponed. */
+extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr,
+                                             NTP_Remote_Address *new_addr);
+
 /* Procedure to get local reference ID corresponding to a source */
 extern uint32_t NSR_GetLocalRefid(IPAddr *address);
 
+/* Procedure to get the name of a source as it was specified (it may be
+   an IP address) */
+extern char *NSR_GetName(IPAddr *address);
+
 /* This routine is called by ntp_io when a new packet arrives off the network */
 extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
                           NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
@@ -102,14 +119,9 @@
 extern void NSR_Finalise(void);
 
 /* This routine is used to indicate that sources whose IP addresses
-   match a particular subnet should be set online again.  Returns a
-   flag indicating whether any hosts matched the address */
-extern int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address);
-
-/* This routine is used to indicate that sources whose IP addresses
-   match a particular subnet should be set offline.  Returns a flag
-   indicating whether any hosts matched the address */
-extern int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address);
+   match a particular subnet should be set online or offline.  It returns
+   a flag indicating whether any hosts matched the address. */
+extern int NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity);
 
 extern int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll);
 
@@ -129,8 +141,12 @@
 
 extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
 
+extern int NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report);
+
 extern int NSR_GetNTPReport(RPT_NTPReport *report);
 
 extern void NSR_GetActivityReport(RPT_ActivityReport *report);
 
+extern void NSR_DumpAuthData(void);
+
 #endif /* GOT_NTP_SOURCES_H */
diff --git a/chrony/nts_ke.h b/chrony/nts_ke.h
new file mode 100644
index 0000000..e7497af
--- /dev/null
+++ b/chrony/nts_ke.h
@@ -0,0 +1,81 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for the NTS Key Establishment protocol
+  */
+
+#ifndef GOT_NTS_KE_H
+#define GOT_NTS_KE_H
+
+#include "siv.h"
+
+#define NKE_PORT                        4460
+
+#define NKE_RECORD_CRITICAL_BIT         (1U << 15)
+#define NKE_RECORD_END_OF_MESSAGE       0
+#define NKE_RECORD_NEXT_PROTOCOL        1
+#define NKE_RECORD_ERROR                2
+#define NKE_RECORD_WARNING              3
+#define NKE_RECORD_AEAD_ALGORITHM       4
+#define NKE_RECORD_COOKIE               5
+#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
+#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
+
+#define NKE_NEXT_PROTOCOL_NTPV4         0
+
+#define NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD 0
+#define NKE_ERROR_BAD_REQUEST           1
+#define NKE_ERROR_INTERNAL_SERVER_ERROR 2
+
+#define NKE_ALPN_NAME                   "ntske/1"
+#define NKE_EXPORTER_LABEL              "EXPORTER-network-time-security"
+#define NKE_EXPORTER_CONTEXT_C2S        "\x0\x0\x0\xf\x0"
+#define NKE_EXPORTER_CONTEXT_S2C        "\x0\x0\x0\xf\x1"
+
+#define NKE_MAX_MESSAGE_LENGTH          16384
+#define NKE_MAX_RECORD_BODY_LENGTH      256
+#define NKE_MAX_COOKIE_LENGTH           256
+#define NKE_MAX_COOKIES                 8
+#define NKE_MAX_KEY_LENGTH SIV_MAX_KEY_LENGTH
+
+#define NKE_RETRY_FACTOR2_CONNECT       4
+#define NKE_RETRY_FACTOR2_TLS           10
+#define NKE_MAX_RETRY_INTERVAL2         19
+
+typedef struct {
+  int length;
+  unsigned char key[NKE_MAX_KEY_LENGTH];
+} NKE_Key;
+
+typedef struct {
+  SIV_Algorithm algorithm;
+  NKE_Key c2s;
+  NKE_Key s2c;
+} NKE_Context;
+
+typedef struct {
+  int length;
+  unsigned char cookie[NKE_MAX_COOKIE_LENGTH];
+} NKE_Cookie;
+
+#endif
diff --git a/chrony/nts_ke_client.c b/chrony/nts_ke_client.c
new file mode 100644
index 0000000..c22b0eb
--- /dev/null
+++ b/chrony/nts_ke_client.c
@@ -0,0 +1,442 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020-2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  NTS-KE client
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "nts_ke_client.h"
+
+#include "conf.h"
+#include "logging.h"
+#include "memory.h"
+#include "nameserv_async.h"
+#include "nts_ke_session.h"
+#include "siv.h"
+#include "socket.h"
+#include "util.h"
+
+#define CLIENT_TIMEOUT 16.0
+
+struct NKC_Instance_Record {
+  char *name;
+  IPSockAddr address;
+  NKSN_Credentials credentials;
+  NKSN_Instance session;
+  int destroying;
+  int got_response;
+  int resolving_name;
+
+  NKE_Context context;
+  NKE_Cookie cookies[NKE_MAX_COOKIES];
+  int num_cookies;
+  char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
+  IPSockAddr ntp_address;
+};
+
+/* ================================================== */
+
+static NKSN_Credentials default_credentials = NULL;
+static int default_credentials_refs = 0;
+
+/* ================================================== */
+
+static void
+name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg)
+{
+  NKC_Instance inst = arg;
+  int i;
+
+  inst->resolving_name = 0;
+
+  if (inst->destroying) {
+    Free(inst);
+    return;
+  }
+
+  if (status != DNS_Success || n_addrs < 1) {
+    LOG(LOGS_ERR, "Could not resolve NTP server %s from %s", inst->server_name, inst->name);
+    /* Force restart */
+    inst->got_response = 0;
+    return;
+  }
+
+  inst->ntp_address.ip_addr = ip_addrs[0];
+
+  /* Prefer an address in the same family as the NTS-KE server */
+  for (i = 0; i < n_addrs; i++) {
+    DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i]));
+    if (ip_addrs[i].family == inst->address.ip_addr.family) {
+      inst->ntp_address.ip_addr = ip_addrs[i];
+      break;
+    }
+  }
+}
+
+/* ================================================== */
+
+static int
+prepare_request(NKC_Instance inst)
+{
+  NKSN_Instance session = inst->session;
+  uint16_t datum;
+
+  NKSN_BeginMessage(session);
+
+  datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
+  if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
+    return 0;
+
+  datum = htons(AEAD_AES_SIV_CMAC_256);
+  if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
+    return 0;
+
+  if (!NKSN_EndMessage(session))
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+process_response(NKC_Instance inst)
+{
+  int next_protocol = -1, aead_algorithm = -1, error = 0;
+  int i, critical, type, length;
+  uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
+
+  assert(NKE_MAX_COOKIE_LENGTH <= NKE_MAX_RECORD_BODY_LENGTH);
+  assert(sizeof (data) % sizeof (uint16_t) == 0);
+  assert(sizeof (uint16_t) == 2);
+
+  inst->num_cookies = 0;
+  inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
+  inst->ntp_address.port = 0;
+  inst->server_name[0] = '\0';
+
+  while (!error) {
+    if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
+      break;
+
+    if (length > sizeof (data)) {
+      DEBUG_LOG("Record too long type=%d length=%d critical=%d", type, length, critical);
+      if (critical)
+        error = 1;
+      continue;
+    }
+
+    switch (type) {
+      case NKE_RECORD_NEXT_PROTOCOL:
+        if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
+          DEBUG_LOG("Unexpected NTS-KE next protocol");
+          error = 1;
+          break;
+        }
+        next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
+        break;
+      case NKE_RECORD_AEAD_ALGORITHM:
+        if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
+          DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
+          error = 1;
+          break;
+        }
+        aead_algorithm = AEAD_AES_SIV_CMAC_256;
+        inst->context.algorithm = aead_algorithm;
+        break;
+      case NKE_RECORD_ERROR:
+        if (length == 2)
+          DEBUG_LOG("NTS-KE error %d", ntohs(data[0]));
+        error = 1;
+        break;
+      case NKE_RECORD_WARNING:
+        if (length == 2)
+          DEBUG_LOG("NTS-KE warning %d", ntohs(data[0]));
+        error = 1;
+        break;
+      case NKE_RECORD_COOKIE:
+        DEBUG_LOG("Got cookie length=%d", length);
+
+        if (length < 1 || length > NKE_MAX_COOKIE_LENGTH || length % 4 != 0 ||
+            inst->num_cookies >= NKE_MAX_COOKIES) {
+          DEBUG_LOG("Unexpected length/cookie");
+          break;
+        }
+
+        assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
+        assert(NKE_MAX_COOKIES == sizeof (inst->cookies) /
+                                  sizeof (inst->cookies[inst->num_cookies]));
+        inst->cookies[inst->num_cookies].length = length;
+        memcpy(inst->cookies[inst->num_cookies].cookie, data, length);
+
+        inst->num_cookies++;
+        break;
+      case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
+        if (length < 1 || length >= sizeof (inst->server_name)) {
+          DEBUG_LOG("Invalid server name");
+          error = 1;
+          break;
+        }
+
+        memcpy(inst->server_name, data, length);
+        inst->server_name[length] = '\0';
+
+        /* Make sure the name is printable and has no spaces */
+        for (i = 0; i < length && isgraph((unsigned char)inst->server_name[i]); i++)
+          ;
+        if (i != length) {
+          DEBUG_LOG("Invalid server name");
+          error = 1;
+          break;
+        }
+
+        DEBUG_LOG("Negotiated server %s", inst->server_name);
+        break;
+      case NKE_RECORD_NTPV4_PORT_NEGOTIATION:
+        if (length != 2) {
+          DEBUG_LOG("Invalid port");
+          error = 1;
+          break;
+        }
+        inst->ntp_address.port = ntohs(data[0]);
+        DEBUG_LOG("Negotiated port %d", inst->ntp_address.port);
+        break;
+      default:
+        DEBUG_LOG("Unknown record type=%d length=%d critical=%d", type, length, critical);
+        if (critical)
+          error = 1;
+    }
+  }
+
+  DEBUG_LOG("NTS-KE response: error=%d next=%d aead=%d",
+            error, next_protocol, aead_algorithm);
+
+  if (error || inst->num_cookies == 0 ||
+      next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
+      aead_algorithm != AEAD_AES_SIV_CMAC_256)
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+handle_message(void *arg)
+{
+  NKC_Instance inst = arg;
+
+  if (!process_response(inst)) {
+    LOG(LOGS_ERR, "Received invalid NTS-KE response from %s", inst->name);
+    return 0;
+  }
+
+  if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
+                    &inst->context.c2s, &inst->context.s2c))
+    return 0;
+
+  if (inst->server_name[0] != '\0') {
+    if (inst->resolving_name)
+      return 0;
+    if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) {
+      int length = strlen(inst->server_name);
+
+      /* Add a trailing dot if not present to force the name to be
+         resolved as a fully qualified domain name */
+      if (length < 1 || length + 1 >= sizeof (inst->server_name))
+        return 0;
+      if (inst->server_name[length - 1] != '.') {
+        inst->server_name[length] = '.';
+        inst->server_name[length + 1] = '\0';
+      }
+
+      DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
+      inst->resolving_name = 1;
+    }
+  }
+
+  inst->got_response = 1;
+
+  return 1;
+}
+
+/* ================================================== */
+
+NKC_Instance
+NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set)
+{
+  const char **trusted_certs;
+  uint32_t *certs_ids;
+  NKC_Instance inst;
+  int n_certs;
+
+  inst = MallocNew(struct NKC_Instance_Record);
+
+  inst->address = *address;
+  inst->name = Strdup(name);
+  inst->session = NKSN_CreateInstance(0, inst->name, handle_message, inst);
+  inst->resolving_name = 0;
+  inst->destroying = 0;
+  inst->got_response = 0;
+
+  n_certs = CNF_GetNtsTrustedCertsPaths(&trusted_certs, &certs_ids);
+
+  /* Share the credentials among clients using the default set of trusted
+     certificates, which likely contains most certificates */
+  if (cert_set == 0) {
+    if (!default_credentials)
+      default_credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
+                                                             n_certs, cert_set);
+    inst->credentials = default_credentials;
+    if (default_credentials)
+      default_credentials_refs++;
+  } else {
+    inst->credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
+                                                         n_certs, cert_set);
+  }
+
+  return inst;
+}
+
+/* ================================================== */
+
+void
+NKC_DestroyInstance(NKC_Instance inst)
+{
+  NKSN_DestroyInstance(inst->session);
+
+  Free(inst->name);
+
+  if (inst->credentials) {
+    if (inst->credentials == default_credentials) {
+      default_credentials_refs--;
+      if (default_credentials_refs <= 0) {
+        NKSN_DestroyCertCredentials(default_credentials);
+        default_credentials = NULL;
+      }
+    } else {
+      NKSN_DestroyCertCredentials(inst->credentials);
+    }
+  }
+
+  /* If the asynchronous resolver is running, let the handler free
+     the instance later */
+  if (inst->resolving_name) {
+    inst->destroying = 1;
+    return;
+  }
+
+  Free(inst);
+}
+
+/* ================================================== */
+
+int
+NKC_Start(NKC_Instance inst)
+{
+  IPSockAddr local_addr;
+  char label[512], *iface;
+  int sock_fd;
+
+  assert(!NKC_IsActive(inst));
+
+  inst->got_response = 0;
+
+  if (!inst->credentials) {
+    DEBUG_LOG("Missing client credentials");
+    return 0;
+  }
+
+  /* Follow the bindacqaddress and bindacqdevice settings */
+  CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
+  local_addr.port = 0;
+  iface = CNF_GetBindAcquisitionInterface();
+
+  /* Make a label containing both the address and name of the server */
+  if (snprintf(label, sizeof (label), "%s (%s)",
+               UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label))
+    ;
+
+  sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, iface, 0);
+  if (sock_fd < 0) {
+    LOG(LOGS_ERR, "Could not connect to %s", label);
+    return 0;
+  }
+
+  /* Start an NTS-KE session */
+  if (!NKSN_StartSession(inst->session, sock_fd, label, inst->credentials, CLIENT_TIMEOUT)) {
+    SCK_CloseSocket(sock_fd);
+    return 0;
+  }
+
+  /* Send a request */
+  if (!prepare_request(inst)) {
+    DEBUG_LOG("Could not prepare NTS-KE request");
+    NKSN_StopSession(inst->session);
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NKC_IsActive(NKC_Instance inst)
+{
+  return !NKSN_IsStopped(inst->session) || inst->resolving_name;
+}
+
+/* ================================================== */
+
+int
+NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
+               NKE_Cookie *cookies, int *num_cookies, int max_cookies,
+               IPSockAddr *ntp_address)
+{
+  int i;
+
+  if (!inst->got_response || inst->resolving_name)
+    return 0;
+
+  *context = inst->context;
+
+  for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
+    cookies[i] = inst->cookies[i];
+  *num_cookies = i;
+
+  *ntp_address = inst->ntp_address;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NKC_GetRetryFactor(NKC_Instance inst)
+{
+  return NKSN_GetRetryFactor(inst->session);
+}
diff --git a/chrony/nts_ke_client.h b/chrony/nts_ke_client.h
new file mode 100644
index 0000000..a1bedb6
--- /dev/null
+++ b/chrony/nts_ke_client.h
@@ -0,0 +1,56 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for the NTS-KE client
+  */
+
+#ifndef GOT_NTS_KE_CLIENT_H
+#define GOT_NTS_KE_CLIENT_H
+
+#include "addressing.h"
+#include "nts_ke.h"
+
+typedef struct NKC_Instance_Record *NKC_Instance;
+
+/* Create a client NTS-KE instance */
+extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set);
+
+/* Destroy an instance */
+extern void NKC_DestroyInstance(NKC_Instance inst);
+
+/* Connect to the server, start an NTS-KE session, send an NTS-KE request, and
+   process the response (asynchronously) */
+extern int NKC_Start(NKC_Instance inst);
+
+/* Check if the client is still running */
+extern int NKC_IsActive(NKC_Instance inst);
+
+/* Get the NTS data if the session was successful */
+extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
+                          NKE_Cookie *cookies, int *num_cookies, int max_cookies,
+                          IPSockAddr *ntp_address);
+
+/* Get a factor to calculate retry interval (in log2 seconds) */
+extern int NKC_GetRetryFactor(NKC_Instance inst);
+
+#endif
diff --git a/chrony/nts_ke_server.c b/chrony/nts_ke_server.c
new file mode 100644
index 0000000..ece1b4c
--- /dev/null
+++ b/chrony/nts_ke_server.c
@@ -0,0 +1,967 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  NTS-KE server
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "nts_ke_server.h"
+
+#include "array.h"
+#include "conf.h"
+#include "clientlog.h"
+#include "local.h"
+#include "logging.h"
+#include "memory.h"
+#include "ntp_core.h"
+#include "nts_ke_session.h"
+#include "privops.h"
+#include "siv.h"
+#include "socket.h"
+#include "sched.h"
+#include "sys.h"
+#include "util.h"
+
+#define SERVER_TIMEOUT 2.0
+
+#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256
+#define SERVER_COOKIE_NONCE_LENGTH 16
+
+#define KEY_ID_INDEX_BITS 2
+#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
+#define FUTURE_KEYS 1
+
+#define DUMP_FILENAME "ntskeys"
+#define DUMP_IDENTIFIER "NKS0\n"
+
+#define INVALID_SOCK_FD (-7)
+
+typedef struct {
+  uint32_t key_id;
+  unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH];
+} ServerCookieHeader;
+
+typedef struct {
+  uint32_t id;
+  unsigned char key[SIV_MAX_KEY_LENGTH];
+  SIV_Instance siv;
+} ServerKey;
+
+typedef struct {
+  uint32_t key_id;
+  unsigned char key[SIV_MAX_KEY_LENGTH];
+  IPAddr client_addr;
+  uint16_t client_port;
+  uint16_t _pad;
+} HelperRequest;
+
+/* ================================================== */
+
+static ServerKey server_keys[MAX_SERVER_KEYS];
+static int current_server_key;
+static double last_server_key_ts;
+static int key_rotation_interval;
+
+static int server_sock_fd4;
+static int server_sock_fd6;
+
+static int helper_sock_fd;
+static int is_helper;
+
+static int initialised = 0;
+
+/* Array of NKSN instances */
+static ARR_Instance sessions;
+static NKSN_Credentials server_credentials;
+
+/* ================================================== */
+
+static int handle_message(void *arg);
+
+/* ================================================== */
+
+static int
+handle_client(int sock_fd, IPSockAddr *addr)
+{
+  NKSN_Instance inst, *instp;
+  int i;
+
+  /* Leave at least half of the descriptors which can handled by select()
+     to other use */
+  if (sock_fd > FD_SETSIZE / 2) {
+    DEBUG_LOG("Rejected connection from %s (%s)",
+              UTI_IPSockAddrToString(addr), "too many descriptors");
+    return 0;
+  }
+
+  /* Find an unused server slot or one with an already stopped session */
+  for (i = 0, inst = NULL; i < ARR_GetSize(sessions); i++) {
+    instp = ARR_GetElement(sessions, i);
+    if (!*instp) {
+      /* NULL handler arg will be replaced with the session instance */
+      inst = NKSN_CreateInstance(1, NULL, handle_message, NULL);
+      *instp = inst;
+      break;
+    } else if (NKSN_IsStopped(*instp)) {
+      inst = *instp;
+      break;
+    }
+  }
+
+  if (!inst) {
+    DEBUG_LOG("Rejected connection from %s (%s)",
+              UTI_IPSockAddrToString(addr), "too many connections");
+    return 0;
+  }
+
+  assert(server_credentials);
+
+  if (!NKSN_StartSession(inst, sock_fd, UTI_IPSockAddrToString(addr),
+                         server_credentials, SERVER_TIMEOUT))
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+handle_helper_request(int fd, int event, void *arg)
+{
+  SCK_Message *message;
+  HelperRequest *req;
+  IPSockAddr client_addr;
+  int sock_fd;
+
+  /* Receive the helper request with the NTS-KE session socket.
+     With multiple helpers EAGAIN errors are expected here. */
+  message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
+  if (!message)
+    return;
+
+  sock_fd = message->descriptor;
+  if (sock_fd < 0) {
+    /* Message with no descriptor is a shutdown command */
+    SCH_QuitProgram();
+    return;
+  }
+
+  if (!initialised) {
+    DEBUG_LOG("Uninitialised helper");
+    SCK_CloseSocket(sock_fd);
+    return;
+  }
+
+  if (message->length != sizeof (HelperRequest))
+    LOG_FATAL("Invalid helper request");
+
+  req = message->data;
+
+  /* Extract the current server key and client address from the request */
+  server_keys[current_server_key].id = ntohl(req->key_id);
+  assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key));
+  memcpy(server_keys[current_server_key].key, req->key,
+         sizeof (server_keys[current_server_key].key));
+  UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
+  client_addr.port = ntohs(req->client_port);
+
+  if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
+                  SIV_GetKeyLength(SERVER_COOKIE_SIV)))
+    LOG_FATAL("Could not set SIV key");
+
+  if (!handle_client(sock_fd, &client_addr)) {
+    SCK_CloseSocket(sock_fd);
+    return;
+  }
+
+  DEBUG_LOG("Accepted helper request fd=%d", sock_fd);
+}
+
+/* ================================================== */
+
+static void
+accept_connection(int listening_fd, int event, void *arg)
+{
+  SCK_Message message;
+  IPSockAddr addr;
+  int log_index, sock_fd;
+  struct timespec now;
+
+  sock_fd = SCK_AcceptConnection(listening_fd, &addr);
+  if (sock_fd < 0)
+    return;
+
+  if (!NCR_CheckAccessRestriction(&addr.ip_addr)) {
+    DEBUG_LOG("Rejected connection from %s (%s)",
+              UTI_IPSockAddrToString(&addr), "access denied");
+    SCK_CloseSocket(sock_fd);
+    return;
+  }
+
+  SCH_GetLastEventTime(&now, NULL, NULL);
+
+  log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now);
+  if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index)) {
+    DEBUG_LOG("Rejected connection from %s (%s)",
+              UTI_IPSockAddrToString(&addr), "rate limit");
+    SCK_CloseSocket(sock_fd);
+    return;
+  }
+
+  /* Pass the socket to a helper process if enabled.  Otherwise, handle the
+     client in the main process. */
+  if (helper_sock_fd != INVALID_SOCK_FD) {
+    HelperRequest req;
+
+    memset(&req, 0, sizeof (req));
+
+    /* Include the current server key and client address in the request */
+    req.key_id = htonl(server_keys[current_server_key].id);
+    assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
+    memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
+    UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
+    req.client_port = htons(addr.port);
+
+    SCK_InitMessage(&message, SCK_ADDR_UNSPEC);
+    message.data = &req;
+    message.length = sizeof (req);
+    message.descriptor = sock_fd;
+
+    errno = 0;
+    if (!SCK_SendMessage(helper_sock_fd, &message, SCK_FLAG_MSG_DESCRIPTOR)) {
+      /* If sending failed with EPIPE, it means all helpers closed their end of
+         the socket (e.g. due to a fatal error) */
+      if (errno == EPIPE)
+        LOG_FATAL("NTS-KE helpers failed");
+      SCK_CloseSocket(sock_fd);
+      return;
+    }
+
+    SCK_CloseSocket(sock_fd);
+  } else {
+    if (!handle_client(sock_fd, &addr)) {
+      SCK_CloseSocket(sock_fd);
+      return;
+    }
+  }
+
+  DEBUG_LOG("Accepted connection from %s fd=%d", UTI_IPSockAddrToString(&addr), sock_fd);
+}
+
+/* ================================================== */
+
+static int
+open_socket(int family)
+{
+  IPSockAddr local_addr;
+  int backlog, sock_fd;
+  char *iface;
+
+  if (!SCK_IsIpFamilyEnabled(family))
+    return INVALID_SOCK_FD;
+
+  CNF_GetBindAddress(family, &local_addr.ip_addr);
+  local_addr.port = CNF_GetNtsServerPort();
+  iface = CNF_GetBindNtpInterface();
+
+  sock_fd = SCK_OpenTcpSocket(NULL, &local_addr, iface, 0);
+  if (sock_fd < 0) {
+    LOG(LOGS_ERR, "Could not open NTS-KE socket on %s", UTI_IPSockAddrToString(&local_addr));
+    return INVALID_SOCK_FD;
+  }
+
+  /* Set the maximum number of waiting connections on the socket to the maximum
+     number of concurrent sessions */
+  backlog = MAX(CNF_GetNtsServerProcesses(), 1) * CNF_GetNtsServerConnections();
+
+  if (!SCK_ListenOnSocket(sock_fd, backlog)) {
+    SCK_CloseSocket(sock_fd);
+    return INVALID_SOCK_FD;
+  }
+
+  SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, accept_connection, NULL);
+
+  return sock_fd;
+}
+
+/* ================================================== */
+
+static void
+helper_signal(int x)
+{
+  SCH_QuitProgram();
+}
+
+/* ================================================== */
+
+static int
+prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
+{
+  NKE_Context context;
+  NKE_Cookie cookie;
+  char *ntp_server;
+  uint16_t datum;
+  int i;
+
+  DEBUG_LOG("NTS KE response: error=%d next=%d aead=%d", error, next_protocol, aead_algorithm);
+
+  NKSN_BeginMessage(session);
+
+  if (error >= 0) {
+    datum = htons(error);
+    if (!NKSN_AddRecord(session, 1, NKE_RECORD_ERROR, &datum, sizeof (datum)))
+      return 0;
+  } else if (next_protocol < 0) {
+    if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, NULL, 0))
+      return 0;
+  } else if (aead_algorithm < 0) {
+    datum = htons(next_protocol);
+    if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
+      return 0;
+    if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, NULL, 0))
+      return 0;
+  } else {
+    datum = htons(next_protocol);
+    if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
+      return 0;
+
+    datum = htons(aead_algorithm);
+    if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
+      return 0;
+
+    if (CNF_GetNTPPort() != NTP_PORT) {
+      datum = htons(CNF_GetNTPPort());
+      if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
+        return 0;
+    }
+
+    ntp_server = CNF_GetNtsNtpServer();
+    if (ntp_server) {
+      if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_SERVER_NEGOTIATION,
+                          ntp_server, strlen(ntp_server)))
+        return 0;
+    }
+
+    context.algorithm = aead_algorithm;
+
+    if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c))
+      return 0;
+
+    for (i = 0; i < NKE_MAX_COOKIES; i++) {
+      if (!NKS_GenerateCookie(&context, &cookie))
+        return 0;
+      if (!NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, cookie.cookie, cookie.length))
+        return 0;
+    }
+  }
+
+  if (!NKSN_EndMessage(session))
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+process_request(NKSN_Instance session)
+{
+  int next_protocol_records = 0, aead_algorithm_records = 0;
+  int next_protocol_values = 0, aead_algorithm_values = 0;
+  int next_protocol = -1, aead_algorithm = -1, error = -1;
+  int i, critical, type, length;
+  uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
+
+  assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
+  assert(sizeof (uint16_t) == 2);
+
+  while (error < 0) {
+    if (!NKSN_GetRecord(session, &critical, &type, &length, &data, sizeof (data)))
+      break;
+
+    switch (type) {
+      case NKE_RECORD_NEXT_PROTOCOL:
+        if (!critical || length < 2 || length % 2 != 0) {
+          error = NKE_ERROR_BAD_REQUEST;
+          break;
+        }
+
+        next_protocol_records++;
+
+        for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
+          next_protocol_values++;
+          if (ntohs(data[i]) == NKE_NEXT_PROTOCOL_NTPV4)
+            next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
+        }
+        break;
+      case NKE_RECORD_AEAD_ALGORITHM:
+        if (length < 2 || length % 2 != 0) {
+          error = NKE_ERROR_BAD_REQUEST;
+          break;
+        }
+
+        aead_algorithm_records++;
+
+        for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
+          aead_algorithm_values++;
+          if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
+            aead_algorithm = AEAD_AES_SIV_CMAC_256;
+        }
+        break;
+      case NKE_RECORD_ERROR:
+      case NKE_RECORD_WARNING:
+      case NKE_RECORD_COOKIE:
+        error = NKE_ERROR_BAD_REQUEST;
+        break;
+      default:
+        if (critical)
+          error = NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD;
+    }
+  }
+
+  if (error < 0) {
+    if (next_protocol_records != 1 || next_protocol_values < 1 ||
+        (next_protocol == NKE_NEXT_PROTOCOL_NTPV4 &&
+         (aead_algorithm_records != 1 || aead_algorithm_values < 1)))
+      error = NKE_ERROR_BAD_REQUEST;
+  }
+
+  if (!prepare_response(session, error, next_protocol, aead_algorithm))
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+handle_message(void *arg)
+{
+  NKSN_Instance session = arg;
+
+  return process_request(session);
+}
+
+/* ================================================== */
+
+static void
+generate_key(int index)
+{
+  int key_length;
+
+  if (index < 0 || index >= MAX_SERVER_KEYS)
+    assert(0);
+
+  key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
+  if (key_length > sizeof (server_keys[index].key))
+    assert(0);
+
+  UTI_GetRandomBytesUrandom(server_keys[index].key, key_length);
+
+  if (!server_keys[index].siv ||
+      !SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
+    LOG_FATAL("Could not set SIV key");
+
+  UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
+
+  /* Encode the index in the lowest bits of the ID */
+  server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
+  server_keys[index].id |= index;
+
+  DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
+
+  last_server_key_ts = SCH_GetLastEventMonoTime();
+}
+
+/* ================================================== */
+
+static void
+save_keys(void)
+{
+  char buf[SIV_MAX_KEY_LENGTH * 2 + 1], *dump_dir;
+  int i, index, key_length;
+  double last_key_age;
+  FILE *f;
+
+  /* Don't save the keys if rotation is disabled to enable an external
+     management of the keys (e.g. share them with another server) */
+  if (key_rotation_interval == 0)
+    return;
+
+  dump_dir = CNF_GetNtsDumpDir();
+  if (!dump_dir)
+    return;
+
+  f = UTI_OpenFile(dump_dir, DUMP_FILENAME, ".tmp", 'w', 0600);
+  if (!f)
+    return;
+
+  key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
+  last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
+
+  if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, SERVER_COOKIE_SIV, last_key_age) < 0)
+    goto error;
+
+  for (i = 0; i < MAX_SERVER_KEYS; i++) {
+    index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
+
+    if (key_length > sizeof (server_keys[index].key) ||
+        !UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
+        fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0)
+      goto error;
+  }
+
+  fclose(f);
+
+  /* Rename the temporary file, or remove it if that fails */
+  if (!UTI_RenameTempFile(dump_dir, DUMP_FILENAME, ".tmp", NULL)) {
+    if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, ".tmp"))
+      ;
+  }
+
+  return;
+
+error:
+  DEBUG_LOG("Could not %s server keys", "save");
+  fclose(f);
+
+  if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
+    ;
+}
+
+/* ================================================== */
+
+#define MAX_WORDS 2
+
+static int
+load_keys(void)
+{
+  char *dump_dir, line[1024], *words[MAX_WORDS];
+  unsigned char key[SIV_MAX_KEY_LENGTH];
+  int i, index, key_length, algorithm;
+  double key_age;
+  FILE *f;
+  uint32_t id;
+
+  dump_dir = CNF_GetNtsDumpDir();
+  if (!dump_dir)
+    return 0;
+
+  f = UTI_OpenFile(dump_dir, DUMP_FILENAME, NULL, 'r', 0);
+  if (!f)
+    return 0;
+
+  if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
+      !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
+        sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV ||
+        sscanf(words[1], "%lf", &key_age) != 1)
+    goto error;
+
+  key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
+  last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
+
+  for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
+    if (UTI_SplitString(line, words, MAX_WORDS) != 2 ||
+        sscanf(words[0], "%"PRIX32, &id) != 1)
+      goto error;
+
+    if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length)
+      goto error;
+
+    index = id % MAX_SERVER_KEYS;
+
+    server_keys[index].id = id;
+    assert(sizeof (server_keys[index].key) == sizeof (key));
+    memcpy(server_keys[index].key, key, key_length);
+
+    if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
+      LOG_FATAL("Could not set SIV key");
+
+    DEBUG_LOG("Loaded key %"PRIX32, id);
+
+    current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
+  }
+
+  fclose(f);
+
+  return 1;
+
+error:
+  DEBUG_LOG("Could not %s server keys", "load");
+  fclose(f);
+
+  return 0;
+}
+
+/* ================================================== */
+
+static void
+key_timeout(void *arg)
+{
+  current_server_key = (current_server_key + 1) % MAX_SERVER_KEYS;
+  generate_key((current_server_key + FUTURE_KEYS) % MAX_SERVER_KEYS);
+  save_keys();
+
+  SCH_AddTimeoutByDelay(key_rotation_interval, key_timeout, NULL);
+}
+
+/* ================================================== */
+
+static void
+run_helper(uid_t uid, gid_t gid, int scfilter_level)
+{
+  LOG_Severity log_severity;
+
+  /* Finish minimal initialisation and run using the scheduler loop
+     similarly to the main process */
+
+  DEBUG_LOG("Helper started");
+
+  /* Suppress a log message about disabled clock control */
+  log_severity = LOG_GetMinSeverity();
+  LOG_SetMinSeverity(LOGS_ERR);
+
+  SYS_Initialise(0);
+  LOG_SetMinSeverity(log_severity);
+
+  if (!geteuid() && (uid || gid))
+    SYS_DropRoot(uid, gid, SYS_NTSKE_HELPER);
+
+  NKS_Initialise();
+
+  UTI_SetQuitSignalsHandler(helper_signal, 1);
+  if (scfilter_level != 0)
+    SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER);
+
+  SCH_MainLoop();
+
+  DEBUG_LOG("Helper exiting");
+
+  NKS_Finalise();
+  SCK_Finalise();
+  SYS_Finalise();
+  SCH_Finalise();
+  LCL_Finalise();
+  PRV_Finalise();
+  CNF_Finalise();
+  LOG_Finalise();
+
+  UTI_ResetGetRandomFunctions();
+
+  exit(0);
+}
+
+/* ================================================== */
+
+void
+NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
+{
+  int i, processes, sock_fd1, sock_fd2;
+  const char **certs, **keys;
+  char prefix[16];
+  pid_t pid;
+
+  helper_sock_fd = INVALID_SOCK_FD;
+  is_helper = 0;
+
+  if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0)
+    return;
+
+  processes = CNF_GetNtsServerProcesses();
+  if (processes <= 0)
+    return;
+
+  /* Start helper processes to perform (computationally expensive) NTS-KE
+     sessions with clients on sockets forwarded from the main process */
+
+  sock_fd1 = SCK_OpenUnixSocketPair(0, &sock_fd2);
+  if (sock_fd1 < 0)
+    LOG_FATAL("Could not open socket pair");
+
+  for (i = 0; i < processes; i++) {
+    pid = fork();
+
+    if (pid < 0)
+      LOG_FATAL("fork() failed : %s", strerror(errno));
+
+    if (pid > 0)
+      continue;
+
+    is_helper = 1;
+
+    UTI_ResetGetRandomFunctions();
+
+    snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1);
+    LOG_SetDebugPrefix(prefix);
+    LOG_CloseParentFd();
+
+    SCK_CloseSocket(sock_fd1);
+    SCH_AddFileHandler(sock_fd2, SCH_FILE_INPUT, handle_helper_request, NULL);
+
+    run_helper(uid, gid, scfilter_level);
+  }
+
+  SCK_CloseSocket(sock_fd2);
+  helper_sock_fd = sock_fd1;
+}
+
+/* ================================================== */
+
+void
+NKS_Initialise(void)
+{
+  const char **certs, **keys;
+  int i, n_certs_keys;
+  double key_delay;
+
+  server_sock_fd4 = INVALID_SOCK_FD;
+  server_sock_fd6 = INVALID_SOCK_FD;
+
+  n_certs_keys = CNF_GetNtsServerCertAndKeyFiles(&certs, &keys);
+  if (n_certs_keys <= 0)
+    return;
+
+  if (helper_sock_fd == INVALID_SOCK_FD) {
+    server_credentials = NKSN_CreateServerCertCredentials(certs, keys, n_certs_keys);
+    if (!server_credentials)
+      return;
+  } else {
+    server_credentials = NULL;
+  }
+
+  sessions = ARR_CreateInstance(sizeof (NKSN_Instance));
+  for (i = 0; i < CNF_GetNtsServerConnections(); i++)
+    *(NKSN_Instance *)ARR_GetNewElement(sessions) = NULL;
+
+  /* Generate random keys, even if they will be replaced by reloaded keys,
+     or unused (in the helper) */
+  for (i = 0; i < MAX_SERVER_KEYS; i++) {
+    server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
+    generate_key(i);
+  }
+
+  current_server_key = MAX_SERVER_KEYS - 1;
+
+  if (!is_helper) {
+    server_sock_fd4 = open_socket(IPADDR_INET4);
+    server_sock_fd6 = open_socket(IPADDR_INET6);
+
+    key_rotation_interval = MAX(CNF_GetNtsRotate(), 0);
+
+    /* Reload saved keys, or save the new keys */
+    if (!load_keys())
+      save_keys();
+
+    if (key_rotation_interval > 0) {
+      key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
+      SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
+    }
+  }
+
+  initialised = 1;
+}
+
+/* ================================================== */
+
+void
+NKS_Finalise(void)
+{
+  int i;
+
+  if (!initialised)
+    return;
+
+  if (helper_sock_fd != INVALID_SOCK_FD) {
+    /* Send the helpers a request to exit */
+    for (i = 0; i < CNF_GetNtsServerProcesses(); i++) {
+      if (!SCK_Send(helper_sock_fd, "", 1, 0))
+        ;
+    }
+    SCK_CloseSocket(helper_sock_fd);
+  }
+  if (server_sock_fd4 != INVALID_SOCK_FD)
+    SCK_CloseSocket(server_sock_fd4);
+  if (server_sock_fd6 != INVALID_SOCK_FD)
+    SCK_CloseSocket(server_sock_fd6);
+
+  if (!is_helper)
+    save_keys();
+
+  for (i = 0; i < MAX_SERVER_KEYS; i++)
+    SIV_DestroyInstance(server_keys[i].siv);
+
+  for (i = 0; i < ARR_GetSize(sessions); i++) {
+    NKSN_Instance session = *(NKSN_Instance *)ARR_GetElement(sessions, i);
+    if (session)
+      NKSN_DestroyInstance(session);
+  }
+  ARR_DestroyInstance(sessions);
+
+  if (server_credentials)
+    NKSN_DestroyCertCredentials(server_credentials);
+}
+
+/* ================================================== */
+
+void
+NKS_DumpKeys(void)
+{
+  save_keys();
+}
+
+/* ================================================== */
+
+void
+NKS_ReloadKeys(void)
+{
+  /* Don't load the keys if they are expected to be generated by this server
+     instance (i.e. they are already loaded) to not delay the next rotation */
+  if (key_rotation_interval > 0)
+    return;
+
+  load_keys();
+}
+
+/* ================================================== */
+
+/* A server cookie consists of key ID, nonce, and encrypted C2S+S2C keys */
+
+int
+NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
+{
+  unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
+  int plaintext_length, tag_length;
+  ServerCookieHeader *header;
+  ServerKey *key;
+
+  if (!initialised) {
+    DEBUG_LOG("NTS server disabled");
+    return 0;
+  }
+
+  /* The algorithm is hardcoded for now */
+  if (context->algorithm != AEAD_AES_SIV_CMAC_256) {
+    DEBUG_LOG("Unexpected SIV algorithm");
+    return 0;
+  }
+
+  if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
+      context->s2c.length < 0 || context->s2c.length > NKE_MAX_KEY_LENGTH) {
+    DEBUG_LOG("Invalid key length");
+    return 0;
+  }
+
+  key = &server_keys[current_server_key];
+
+  header = (ServerCookieHeader *)cookie->cookie;
+
+  header->key_id = htonl(key->id);
+  UTI_GetRandomBytes(header->nonce, sizeof (header->nonce));
+
+  plaintext_length = context->c2s.length + context->s2c.length;
+  assert(plaintext_length <= sizeof (plaintext));
+  memcpy(plaintext, context->c2s.key, context->c2s.length);
+  memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
+
+  tag_length = SIV_GetTagLength(key->siv);
+  cookie->length = sizeof (*header) + plaintext_length + tag_length;
+  assert(cookie->length <= sizeof (cookie->cookie));
+  ciphertext = cookie->cookie + sizeof (*header);
+
+  if (!SIV_Encrypt(key->siv, header->nonce, sizeof (header->nonce),
+                   "", 0,
+                   plaintext, plaintext_length,
+                   ciphertext, plaintext_length + tag_length)) {
+    DEBUG_LOG("Could not encrypt cookie");
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
+{
+  unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
+  int ciphertext_length, plaintext_length, tag_length;
+  ServerCookieHeader *header;
+  ServerKey *key;
+  uint32_t key_id;
+
+  if (!initialised) {
+    DEBUG_LOG("NTS server disabled");
+    return 0;
+  }
+
+  if (cookie->length <= (int)sizeof (*header)) {
+    DEBUG_LOG("Invalid cookie length");
+    return 0;
+  }
+
+  header = (ServerCookieHeader *)cookie->cookie;
+  ciphertext = cookie->cookie + sizeof (*header);
+  ciphertext_length = cookie->length - sizeof (*header);
+
+  key_id = ntohl(header->key_id);
+  key = &server_keys[key_id % MAX_SERVER_KEYS];
+  if (key_id != key->id) {
+    DEBUG_LOG("Unknown key %"PRIX32, key_id);
+    return 0;
+  }
+
+  tag_length = SIV_GetTagLength(key->siv);
+  if (tag_length >= ciphertext_length) {
+    DEBUG_LOG("Invalid cookie length");
+    return 0;
+  }
+
+  plaintext_length = ciphertext_length - tag_length;
+  if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
+    DEBUG_LOG("Invalid cookie length");
+    return 0;
+  }
+
+  if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce),
+                   "", 0,
+                   ciphertext, ciphertext_length,
+                   plaintext, plaintext_length)) {
+    DEBUG_LOG("Could not decrypt cookie");
+    return 0;
+  }
+
+  context->algorithm = AEAD_AES_SIV_CMAC_256;
+
+  context->c2s.length = plaintext_length / 2;
+  context->s2c.length = plaintext_length / 2;
+  assert(context->c2s.length <= sizeof (context->c2s.key));
+
+  memcpy(context->c2s.key, plaintext, context->c2s.length);
+  memcpy(context->s2c.key, plaintext + context->c2s.length, context->s2c.length);
+
+  return 1;
+}
diff --git a/chrony/nts_ke_server.h b/chrony/nts_ke_server.h
new file mode 100644
index 0000000..4d8a92a
--- /dev/null
+++ b/chrony/nts_ke_server.h
@@ -0,0 +1,49 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for the NTS-KE server
+  */
+
+#ifndef GOT_NTS_KE_SERVER_H
+#define GOT_NTS_KE_SERVER_H
+
+#include "nts_ke.h"
+
+/* Init and fini functions */
+extern void NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level);
+extern void NKS_Initialise(void);
+extern void NKS_Finalise(void);
+
+/* Save the current server keys */
+extern void NKS_DumpKeys(void);
+
+/* Reload the keys */
+extern void NKS_ReloadKeys(void);
+
+/* Generate an NTS cookie with a given context */
+extern int NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie);
+
+/* Validate a cookie and decode the context */
+extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context);
+
+#endif
diff --git a/chrony/nts_ke_session.c b/chrony/nts_ke_session.c
new file mode 100644
index 0000000..96b08c1
--- /dev/null
+++ b/chrony/nts_ke_session.c
@@ -0,0 +1,920 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020-2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  NTS-KE session used by server and client
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "nts_ke_session.h"
+
+#include "conf.h"
+#include "local.h"
+#include "logging.h"
+#include "memory.h"
+#include "siv.h"
+#include "socket.h"
+#include "sched.h"
+#include "util.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#define INVALID_SOCK_FD (-8)
+
+struct RecordHeader {
+  uint16_t type;
+  uint16_t body_length;
+};
+
+struct Message {
+  int length;
+  int sent;
+  int parsed;
+  int complete;
+  unsigned char data[NKE_MAX_MESSAGE_LENGTH];
+};
+
+typedef enum {
+  KE_WAIT_CONNECT,
+  KE_HANDSHAKE,
+  KE_SEND,
+  KE_RECEIVE,
+  KE_SHUTDOWN,
+  KE_STOPPED,
+} KeState;
+
+struct NKSN_Instance_Record {
+  int server;
+  char *server_name;
+  NKSN_MessageHandler handler;
+  void *handler_arg;
+
+  KeState state;
+  int sock_fd;
+  char *label;
+  gnutls_session_t tls_session;
+  SCH_TimeoutID timeout_id;
+  int retry_factor;
+
+  struct Message message;
+  int new_message;
+};
+
+/* ================================================== */
+
+static gnutls_priority_t priority_cache;
+
+static int credentials_counter = 0;
+
+static int clock_updates = 0;
+
+/* ================================================== */
+
+static void
+reset_message(struct Message *message)
+{
+  message->length = 0;
+  message->sent = 0;
+  message->parsed = 0;
+  message->complete = 0;
+}
+
+/* ================================================== */
+
+static int
+add_record(struct Message *message, int critical, int type, const void *body, int body_length)
+{
+  struct RecordHeader header;
+
+  assert(message->length <= sizeof (message->data));
+
+  if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff ||
+      message->length + sizeof (header) + body_length > sizeof (message->data))
+    return 0;
+
+  header.type = htons(!!critical * NKE_RECORD_CRITICAL_BIT | type);
+  header.body_length = htons(body_length);
+
+  memcpy(&message->data[message->length], &header, sizeof (header));
+  message->length += sizeof (header);
+
+  if (body_length > 0) {
+    memcpy(&message->data[message->length], body, body_length);
+    message->length += body_length;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+reset_message_parsing(struct Message *message)
+{
+  message->parsed = 0;
+}
+
+/* ================================================== */
+
+static int
+get_record(struct Message *message, int *critical, int *type, int *body_length,
+           void *body, int buffer_length)
+{
+  struct RecordHeader header;
+  int blen, rlen;
+
+  if (message->length < message->parsed + sizeof (header) ||
+      buffer_length < 0)
+    return 0;
+
+  memcpy(&header, &message->data[message->parsed], sizeof (header));
+
+  blen = ntohs(header.body_length);
+  rlen = sizeof (header) + blen;
+  assert(blen >= 0 && rlen > 0);
+
+  if (message->length < message->parsed + rlen)
+    return 0;
+
+  if (critical)
+    *critical = !!(ntohs(header.type) & NKE_RECORD_CRITICAL_BIT);
+  if (type)
+    *type = ntohs(header.type) & ~NKE_RECORD_CRITICAL_BIT;
+  if (body)
+    memcpy(body, &message->data[message->parsed + sizeof (header)], MIN(buffer_length, blen));
+  if (body_length)
+    *body_length = blen;
+
+  message->parsed += rlen;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+check_message_format(struct Message *message, int eof)
+{
+  int critical = 0, type = -1, length = -1, ends = 0;
+
+  reset_message_parsing(message);
+  message->complete = 0;
+
+  while (get_record(message, &critical, &type, &length, NULL, 0)) {
+    if (type == NKE_RECORD_END_OF_MESSAGE) {
+      if (!critical || length != 0 || ends > 0)
+        return 0;
+      ends++;
+    }
+  }
+
+  /* If the message cannot be fully parsed, but more data may be coming,
+     consider the format to be ok */
+  if (message->length == 0 || message->parsed < message->length)
+    return !eof;
+
+  if (type != NKE_RECORD_END_OF_MESSAGE)
+    return !eof;
+
+  message->complete = 1;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static gnutls_session_t
+create_tls_session(int server_mode, int sock_fd, const char *server_name,
+                   gnutls_certificate_credentials_t credentials,
+                   gnutls_priority_t priority)
+{
+  unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
+  gnutls_session_t session;
+  gnutls_datum_t alpn;
+  unsigned int flags;
+  int r;
+
+  r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
+                  (server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
+  if (r < 0) {
+    LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
+    return NULL;
+  }
+
+  if (!server_mode) {
+    assert(server_name);
+
+    if (!UTI_IsStringIP(server_name)) {
+      r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
+      if (r < 0)
+        goto error;
+    }
+
+    flags = 0;
+
+    if (clock_updates < CNF_GetNoCertTimeCheck()) {
+      flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
+      DEBUG_LOG("Disabled time checks");
+    }
+
+    gnutls_session_set_verify_cert(session, server_name, flags);
+  }
+
+  r = gnutls_priority_set(session, priority);
+  if (r < 0)
+    goto error;
+
+  r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, credentials);
+  if (r < 0)
+    goto error;
+
+  memcpy(alpn_name, NKE_ALPN_NAME, sizeof (alpn_name));
+  alpn.data = alpn_name;
+  alpn.size = sizeof (alpn_name) - 1;
+
+  r = gnutls_alpn_set_protocols(session, &alpn, 1, 0);
+  if (r < 0)
+    goto error;
+
+  gnutls_transport_set_int(session, sock_fd);
+
+  return session;
+
+error:
+  LOG(LOGS_ERR, "Could not %s TLS session : %s", "set", gnutls_strerror(r));
+  gnutls_deinit(session);
+  return NULL;
+}
+
+/* ================================================== */
+
+static void
+stop_session(NKSN_Instance inst)
+{
+  if (inst->state == KE_STOPPED)
+    return;
+
+  inst->state = KE_STOPPED;
+
+  SCH_RemoveFileHandler(inst->sock_fd);
+  SCK_CloseSocket(inst->sock_fd);
+  inst->sock_fd = INVALID_SOCK_FD;
+
+  Free(inst->label);
+  inst->label = NULL;
+
+  gnutls_deinit(inst->tls_session);
+  inst->tls_session = NULL;
+
+  SCH_RemoveTimeout(inst->timeout_id);
+  inst->timeout_id = 0;
+}
+
+/* ================================================== */
+
+static void
+session_timeout(void *arg)
+{
+  NKSN_Instance inst = arg;
+
+  LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->label);
+
+  inst->timeout_id = 0;
+  stop_session(inst);
+}
+
+/* ================================================== */
+
+static int
+check_alpn(NKSN_Instance inst)
+{
+  gnutls_datum_t alpn;
+
+  if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
+      alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
+      memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+set_input_output(NKSN_Instance inst, int output)
+{
+  SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_INPUT, !output);
+  SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
+}
+
+/* ================================================== */
+
+static void
+change_state(NKSN_Instance inst, KeState state)
+{
+  int output;
+
+  switch (state) {
+    case KE_HANDSHAKE:
+      output = !inst->server;
+      break;
+    case KE_WAIT_CONNECT:
+    case KE_SEND:
+    case KE_SHUTDOWN:
+      output = 1;
+      break;
+    case KE_RECEIVE:
+      output = 0;
+      break;
+    default:
+      assert(0);
+  }
+
+  set_input_output(inst, output);
+
+  inst->state = state;
+}
+
+/* ================================================== */
+
+static int
+handle_event(NKSN_Instance inst, int event)
+{
+  struct Message *message = &inst->message;
+  int r;
+
+  DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
+
+  switch (inst->state) {
+    case KE_WAIT_CONNECT:
+      /* Check if connect() succeeded */
+      if (event != SCH_FILE_OUTPUT)
+        return 0;
+
+      /* Get the socket error */
+      if (!SCK_GetIntOption(inst->sock_fd, SOL_SOCKET, SO_ERROR, &r))
+        r = EINVAL;
+
+      if (r != 0) {
+        LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r));
+        stop_session(inst);
+        return 0;
+      }
+
+      DEBUG_LOG("Connected to %s", inst->label);
+
+      change_state(inst, KE_HANDSHAKE);
+      return 0;
+
+    case KE_HANDSHAKE:
+      r = gnutls_handshake(inst->tls_session);
+
+      if (r < 0) {
+        if (gnutls_error_is_fatal(r)) {
+          gnutls_datum_t cert_error;
+
+          /* Get a description of verification errors */
+          if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
+              gnutls_certificate_verification_status_print(
+                          gnutls_session_get_verify_cert_status(inst->tls_session),
+                          gnutls_certificate_type_get(inst->tls_session), &cert_error, 0) < 0)
+            cert_error.data = NULL;
+
+          LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
+              "TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
+              cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
+
+          if (cert_error.data)
+            gnutls_free(cert_error.data);
+
+          stop_session(inst);
+
+          /* Increase the retry interval if the handshake did not fail due
+             to the other end closing the connection */
+          if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
+            inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
+
+          return 0;
+        }
+
+        /* Disable output when the handshake is trying to receive data */
+        set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
+        return 0;
+      }
+
+      inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
+
+      if (DEBUG) {
+        char *description = gnutls_session_get_desc(inst->tls_session);
+        DEBUG_LOG("Handshake with %s completed %s",
+                  inst->label, description ? description : "");
+        gnutls_free(description);
+      }
+
+      if (!check_alpn(inst)) {
+        LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
+        stop_session(inst);
+        return 0;
+      }
+
+      /* Client will send a request to the server */
+      change_state(inst, inst->server ? KE_RECEIVE : KE_SEND);
+      return 0;
+
+    case KE_SEND:
+      assert(inst->new_message && message->complete);
+      assert(message->length <= sizeof (message->data) && message->length > message->sent);
+
+      r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
+                             message->length - message->sent);
+
+      if (r < 0) {
+        if (gnutls_error_is_fatal(r)) {
+          LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
+              "Could not send NTS-KE message to %s : %s", inst->label, gnutls_strerror(r));
+          stop_session(inst);
+        }
+        return 0;
+      }
+
+      DEBUG_LOG("Sent %d bytes to %s", r, inst->label);
+
+      message->sent += r;
+      if (message->sent < message->length)
+        return 0;
+
+      /* Client will receive a response */
+      change_state(inst, inst->server ? KE_SHUTDOWN : KE_RECEIVE);
+      reset_message(&inst->message);
+      inst->new_message = 0;
+      return 0;
+
+    case KE_RECEIVE:
+      do {
+        if (message->length >= sizeof (message->data)) {
+          DEBUG_LOG("Message is too long");
+          stop_session(inst);
+          return 0;
+        }
+
+        r = gnutls_record_recv(inst->tls_session, &message->data[message->length],
+                               sizeof (message->data) - message->length);
+
+        if (r < 0) {
+          /* Handle a renegotiation request on both client and server as
+             a protocol error */
+          if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) {
+            LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
+                "Could not receive NTS-KE message from %s : %s",
+                inst->label, gnutls_strerror(r));
+            stop_session(inst);
+          }
+          return 0;
+        }
+
+        DEBUG_LOG("Received %d bytes from %s", r, inst->label);
+
+        message->length += r;
+
+      } while (gnutls_record_check_pending(inst->tls_session) > 0);
+
+      if (!check_message_format(message, r == 0)) {
+        LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
+            "Received invalid NTS-KE message from %s", inst->label);
+        stop_session(inst);
+        return 0;
+      }
+
+      /* Wait for more data if the message is not complete yet */
+      if (!message->complete)
+        return 0;
+
+      /* Server will send a response to the client */
+      change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN);
+
+      /* Return success to process the received message */
+      return 1;
+
+    case KE_SHUTDOWN:
+      r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
+
+      if (r < 0) {
+        if (gnutls_error_is_fatal(r)) {
+          DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
+          stop_session(inst);
+          return 0;
+        }
+
+        /* Disable output when the TLS shutdown is trying to receive data */
+        set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
+        return 0;
+      }
+
+      SCK_ShutdownConnection(inst->sock_fd);
+      stop_session(inst);
+
+      DEBUG_LOG("Shutdown completed");
+      return 0;
+
+    default:
+      assert(0);
+      return 0;
+  }
+}
+
+/* ================================================== */
+
+static void
+read_write_socket(int fd, int event, void *arg)
+{
+  NKSN_Instance inst = arg;
+
+  if (!handle_event(inst, event))
+    return;
+
+  /* A valid message was received.  Call the handler to process the message,
+     and prepare a response if it is a server. */
+
+  reset_message_parsing(&inst->message);
+
+  if (!(inst->handler)(inst->handler_arg)) {
+    stop_session(inst);
+    return;
+  }
+}
+
+/* ================================================== */
+
+static time_t
+get_time(time_t *t)
+{
+  struct timespec now;
+
+  LCL_ReadCookedTime(&now, NULL);
+  if (t)
+    *t = now.tv_sec;
+
+  return now.tv_sec;
+}
+
+/* ================================================== */
+
+static void
+handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
+            double doffset, LCL_ChangeType change_type, void *anything)
+{
+  if (change_type != LCL_ChangeUnknownStep && clock_updates < INT_MAX)
+    clock_updates++;
+}
+
+/* ================================================== */
+
+static int gnutls_initialised = 0;
+
+static void
+init_gnutls(void)
+{
+  int r;
+
+  if (gnutls_initialised)
+    return;
+
+  r = gnutls_global_init();
+  if (r < 0)
+    LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
+
+  /* Prepare a priority cache for server and client NTS-KE sessions
+     (the NTS specification requires TLS1.3 or later) */
+  r = gnutls_priority_init2(&priority_cache,
+                            "-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
+                            NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
+  if (r < 0)
+    LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
+
+  /* Use our clock instead of the system clock in certificate verification */
+  gnutls_global_set_time_function(get_time);
+
+  gnutls_initialised = 1;
+  DEBUG_LOG("Initialised");
+
+  LCL_AddParameterChangeHandler(handle_step, NULL);
+}
+
+/* ================================================== */
+
+static void
+deinit_gnutls(void)
+{
+  if (!gnutls_initialised || credentials_counter > 0)
+    return;
+
+  LCL_RemoveParameterChangeHandler(handle_step, NULL);
+
+  gnutls_priority_deinit(priority_cache);
+  gnutls_global_deinit();
+  gnutls_initialised = 0;
+  DEBUG_LOG("Deinitialised");
+}
+
+/* ================================================== */
+
+static NKSN_Credentials
+create_credentials(const char **certs, const char **keys, int n_certs_keys,
+                   const char **trusted_certs, uint32_t *trusted_certs_ids,
+                   int n_trusted_certs, uint32_t trusted_cert_set)
+{
+  gnutls_certificate_credentials_t credentials = NULL;
+  int i, r;
+
+  init_gnutls();
+
+  r = gnutls_certificate_allocate_credentials(&credentials);
+  if (r < 0)
+    goto error;
+
+  if (certs && keys) {
+    if (trusted_certs || trusted_certs_ids)
+      assert(0);
+
+    for (i = 0; i < n_certs_keys; i++) {
+      r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
+                                               GNUTLS_X509_FMT_PEM);
+      if (r < 0)
+        goto error;
+    }
+  } else {
+    if (certs || keys || n_certs_keys > 0)
+      assert(0);
+
+    if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
+      r = gnutls_certificate_set_x509_system_trust(credentials);
+      if (r < 0)
+        goto error;
+    }
+
+    if (trusted_certs && trusted_certs_ids) {
+      for (i = 0; i < n_trusted_certs; i++) {
+        struct stat buf;
+
+        if (trusted_certs_ids[i] != trusted_cert_set)
+          continue;
+
+        if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
+          r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
+                                                    GNUTLS_X509_FMT_PEM);
+        else
+          r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
+                                                     GNUTLS_X509_FMT_PEM);
+        if (r < 0)
+          goto error;
+
+        DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
+      }
+    }
+  }
+
+  credentials_counter++;
+
+  return (NKSN_Credentials)credentials;
+
+error:
+  LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
+  if (credentials)
+    gnutls_certificate_free_credentials(credentials);
+  deinit_gnutls();
+  return NULL;
+}
+
+/* ================================================== */
+
+NKSN_Credentials
+NKSN_CreateServerCertCredentials(const char **certs, const char **keys, int n_certs_keys)
+{
+  return create_credentials(certs, keys, n_certs_keys, NULL, NULL, 0, 0);
+}
+
+/* ================================================== */
+
+NKSN_Credentials
+NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
+                                 int n_certs_ids, uint32_t trusted_cert_set)
+{
+  return create_credentials(NULL, NULL, 0, certs, ids, n_certs_ids, trusted_cert_set);
+}
+
+/* ================================================== */
+
+void
+NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
+{
+  gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
+  credentials_counter--;
+  deinit_gnutls();
+}
+
+/* ================================================== */
+
+NKSN_Instance
+NKSN_CreateInstance(int server_mode, const char *server_name,
+                    NKSN_MessageHandler handler, void *handler_arg)
+{
+  NKSN_Instance inst;
+
+  inst = MallocNew(struct NKSN_Instance_Record);
+
+  inst->server = server_mode;
+  inst->server_name = server_name ? Strdup(server_name) : NULL;
+  inst->handler = handler;
+  inst->handler_arg = handler_arg;
+  /* Replace a NULL argument with the session itself */
+  if (!inst->handler_arg)
+    inst->handler_arg = inst;
+
+  inst->state = KE_STOPPED;
+  inst->sock_fd = INVALID_SOCK_FD;
+  inst->label = NULL;
+  inst->tls_session = NULL;
+  inst->timeout_id = 0;
+  inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
+
+  return inst;
+}
+
+/* ================================================== */
+
+void
+NKSN_DestroyInstance(NKSN_Instance inst)
+{
+  stop_session(inst);
+
+  Free(inst->server_name);
+  Free(inst);
+}
+
+/* ================================================== */
+
+int
+NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
+                  NKSN_Credentials credentials, double timeout)
+{
+  assert(inst->state == KE_STOPPED);
+
+  inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
+                                         (gnutls_certificate_credentials_t)credentials,
+                                         priority_cache);
+  if (!inst->tls_session)
+    return 0;
+
+  inst->sock_fd = sock_fd;
+  SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, inst);
+
+  inst->label = Strdup(label);
+  inst->timeout_id = SCH_AddTimeoutByDelay(timeout, session_timeout, inst);
+  inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
+
+  reset_message(&inst->message);
+  inst->new_message = 0;
+
+  change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT);
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+NKSN_BeginMessage(NKSN_Instance inst)
+{
+  reset_message(&inst->message);
+  inst->new_message = 1;
+}
+
+/* ================================================== */
+
+int
+NKSN_AddRecord(NKSN_Instance inst, int critical, int type, const void *body, int body_length)
+{
+  assert(inst->new_message && !inst->message.complete);
+  assert(type != NKE_RECORD_END_OF_MESSAGE);
+
+  return add_record(&inst->message, critical, type, body, body_length);
+}
+
+/* ================================================== */
+
+int
+NKSN_EndMessage(NKSN_Instance inst)
+{
+  assert(!inst->message.complete);
+
+  /* Terminate the message */
+  if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0))
+    return 0;
+
+  inst->message.complete = 1;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
+               void *body, int buffer_length)
+{
+  int type2;
+
+  assert(inst->message.complete);
+
+  if (body_length)
+    *body_length = 0;
+
+  if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length))
+    return 0;
+
+  /* Hide the end-of-message record */
+  if (type2 == NKE_RECORD_END_OF_MESSAGE)
+    return 0;
+
+  if (type)
+    *type = type2;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
+{
+  int length = SIV_GetKeyLength(siv);
+
+  if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
+    DEBUG_LOG("Invalid algorithm");
+    return 0;
+  }
+
+  if (gnutls_prf_rfc5705(inst->tls_session,
+                         sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
+                         sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
+                         length, (char *)c2s->key) < 0 ||
+      gnutls_prf_rfc5705(inst->tls_session,
+                         sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
+                         sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
+                         length, (char *)s2c->key) < 0) {
+    DEBUG_LOG("Could not export key");
+    return 0;
+  }
+
+  c2s->length = length;
+  s2c->length = length;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NKSN_IsStopped(NKSN_Instance inst)
+{
+  return inst->state == KE_STOPPED;
+}
+
+/* ================================================== */
+
+void
+NKSN_StopSession(NKSN_Instance inst)
+{
+  stop_session(inst);
+}
+
+/* ================================================== */
+
+int
+NKSN_GetRetryFactor(NKSN_Instance inst)
+{
+  return inst->retry_factor;
+}
diff --git a/chrony/nts_ke_session.h b/chrony/nts_ke_session.h
new file mode 100644
index 0000000..2735e04
--- /dev/null
+++ b/chrony/nts_ke_session.h
@@ -0,0 +1,93 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for the NTS-KE session
+  */
+
+#ifndef GOT_NTS_KE_SESSION_H
+#define GOT_NTS_KE_SESSION_H
+
+#include "nts_ke.h"
+#include "siv.h"
+
+typedef struct NKSN_Credentials_Record *NKSN_Credentials;
+
+typedef struct NKSN_Instance_Record *NKSN_Instance;
+
+/* Handler for received NTS-KE messages.  A zero return code stops
+   the session. */
+typedef int (*NKSN_MessageHandler)(void *arg);
+
+/* Get server or client credentials using a server certificate and key,
+   or certificates of trusted CAs.  The credentials may be shared between
+   different clients or servers. */
+extern NKSN_Credentials NKSN_CreateServerCertCredentials(const char **certs, const char **keys,
+                                                         int n_certs_keys);
+extern NKSN_Credentials NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
+                                                         int n_certs_ids,
+                                                         uint32_t trusted_cert_set);
+
+/* Destroy the credentials */
+extern void NKSN_DestroyCertCredentials(NKSN_Credentials credentials);
+
+/* Create an instance */
+extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name,
+                                         NKSN_MessageHandler handler, void *handler_arg);
+
+/* Destroy an instance */
+extern void NKSN_DestroyInstance(NKSN_Instance inst);
+
+/* Start a new NTS-KE session */
+extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
+                             NKSN_Credentials credentials, double timeout);
+
+/* Begin an NTS-KE message.  A request should be made right after starting
+   the session and response should be made in the message handler. */
+extern void NKSN_BeginMessage(NKSN_Instance inst);
+
+/* Add a record to the message */
+extern int NKSN_AddRecord(NKSN_Instance inst, int critical, int type,
+                          const void *body, int body_length);
+
+/* Terminate the message */
+extern int NKSN_EndMessage(NKSN_Instance inst);
+
+/* Get the next record from the received message.  This function should be
+   called from the message handler. */
+extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
+                          void *body, int buffer_length);
+
+/* Export NTS keys for a specified algorithm */
+extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
+
+/* Check if the session has stopped */
+extern int NKSN_IsStopped(NKSN_Instance inst);
+
+/* Stop the session */
+extern void NKSN_StopSession(NKSN_Instance inst);
+
+/* Get a factor to calculate retry interval (in log2 seconds)
+   based on the session state or how it was terminated */
+extern int NKSN_GetRetryFactor(NKSN_Instance inst);
+
+#endif
diff --git a/chrony_3_3/sys_netbsd.h b/chrony/nts_ntp.h
similarity index 75%
copy from chrony_3_3/sys_netbsd.h
copy to chrony/nts_ntp.h
index 052f5b7..e39def2 100644
--- a/chrony_3_3/sys_netbsd.h
+++ b/chrony/nts_ntp.h
@@ -2,8 +2,7 @@
   chronyd/chronyc - Programs for keeping computer clocks accurate.
 
  **********************************************************************
- * Copyright (C) Richard P. Curnow  1997-2001
- * Copyright (C) J. Hannken-Illjes  2001
+ * Copyright (C) Miroslav Lichvar  2020
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -22,16 +21,16 @@
 
   =======================================================================
 
-  Header file for NetBSD driver
+  Header file for the NTS-NTP protocol
   */
 
-#ifndef GOT_SYS_NETBSD_H
-#define GOT_SYS_NETBSD_H
+#ifndef GOT_NTS_NTP_H
+#define GOT_NTS_NTP_H
 
-void SYS_NetBSD_Initialise(void);
+#define NTP_KOD_NTS_NAK                 0x4e54534e
 
-void SYS_NetBSD_Finalise(void);
-
-void SYS_NetBSD_DropRoot(uid_t uid, gid_t gid);
+#define NTS_MIN_UNIQ_ID_LENGTH          32
+#define NTS_MIN_UNPADDED_NONCE_LENGTH   16
+#define NTS_MAX_COOKIES                 8
 
 #endif
diff --git a/chrony/nts_ntp_auth.c b/chrony/nts_ntp_auth.c
new file mode 100644
index 0000000..ac0763e
--- /dev/null
+++ b/chrony/nts_ntp_auth.c
@@ -0,0 +1,183 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  NTS Authenticator and Encrypted Extension Fields extension field
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "nts_ntp_auth.h"
+
+#include "logging.h"
+#include "ntp_ext.h"
+#include "nts_ntp.h"
+#include "siv.h"
+#include "util.h"
+
+struct AuthHeader {
+  uint16_t nonce_length;
+  uint16_t ciphertext_length;
+};
+
+/* ================================================== */
+
+static int
+get_padding_length(int length)
+{
+  return length % 4U ? 4 - length % 4U : 0;
+}
+
+/* ================================================== */
+
+static int
+get_padded_length(int length)
+{
+  return length + get_padding_length(length);
+}
+
+/* ================================================== */
+
+int
+NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
+                   const unsigned char *nonce, int nonce_length,
+                   const unsigned char *plaintext, int plaintext_length,
+                   int min_ef_length)
+{
+  int auth_length, ciphertext_length, assoc_length;
+  int nonce_padding, ciphertext_padding, additional_padding;
+  unsigned char *ciphertext, *body;
+  struct AuthHeader *header;
+
+  assert(sizeof (*header) == 4);
+
+  if (nonce_length <= 0 || plaintext_length < 0) {
+    DEBUG_LOG("Invalid nonce/plaintext length");
+    return 0;
+  }
+
+  assoc_length = info->length;
+  ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
+  nonce_padding = get_padding_length(nonce_length);
+  ciphertext_padding = get_padding_length(ciphertext_length);
+  min_ef_length = get_padded_length(min_ef_length);
+
+  auth_length = sizeof (*header) + nonce_length + nonce_padding +
+                ciphertext_length + ciphertext_padding;
+  additional_padding = MAX(min_ef_length - auth_length - 4, 0);
+  additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
+                           additional_padding);
+  auth_length += additional_padding;
+
+  if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
+                         (void **)&header)) {
+    DEBUG_LOG("Could not add EF");
+    return 0;
+  }
+
+  header->nonce_length = htons(nonce_length);
+  header->ciphertext_length = htons(ciphertext_length);
+
+  body = (unsigned char *)(header + 1);
+  ciphertext = body + nonce_length + nonce_padding;
+
+  if ((unsigned char *)header + auth_length !=
+      ciphertext + ciphertext_length + ciphertext_padding + additional_padding)
+    assert(0);
+
+  memcpy(body, nonce, nonce_length);
+  memset(body + nonce_length, 0, nonce_padding);
+
+  if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
+                   plaintext, plaintext_length, ciphertext, ciphertext_length)) {
+    DEBUG_LOG("SIV encrypt failed");
+    info->length = assoc_length;
+    return 0;
+  }
+
+  memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding);
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
+                  unsigned char *plaintext, int buffer_length, int *plaintext_length)
+{
+  unsigned int siv_tag_length, nonce_length, ciphertext_length;
+  unsigned char *nonce, *ciphertext;
+  int ef_type, ef_body_length;
+  void *ef_body;
+  struct AuthHeader *header;
+
+  if (buffer_length < 0)
+    return 0;
+
+  if (!NEF_ParseField(packet, info->length, ef_start,
+                      NULL, &ef_type, &ef_body, &ef_body_length))
+    return 0;
+
+  if (ef_type != NTP_EF_NTS_AUTH_AND_EEF || ef_body_length < sizeof (*header))
+    return 0;
+
+  header = ef_body;
+
+  nonce_length = ntohs(header->nonce_length);
+  ciphertext_length = ntohs(header->ciphertext_length);
+
+  if (get_padded_length(nonce_length) +
+      get_padded_length(ciphertext_length) > ef_body_length)
+    return 0;
+
+  nonce = (unsigned char *)(header + 1);
+  ciphertext = nonce + get_padded_length(nonce_length);
+
+  siv_tag_length = SIV_GetTagLength(siv);
+
+  if (nonce_length < 1 ||
+      ciphertext_length < siv_tag_length ||
+      ciphertext_length - siv_tag_length > buffer_length) {
+    DEBUG_LOG("Unexpected nonce/ciphertext length");
+    return 0;
+  }
+
+  if (ef_body_length < sizeof (*header) +
+        NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
+    DEBUG_LOG("Missing padding");
+    return 0;
+  }
+
+  *plaintext_length = ciphertext_length - siv_tag_length;
+  assert(*plaintext_length >= 0);
+
+  if (!SIV_Decrypt(siv, nonce, nonce_length, packet, ef_start,
+                   ciphertext, ciphertext_length, plaintext, *plaintext_length)) {
+    DEBUG_LOG("SIV decrypt failed");
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/chrony/nts_ntp_auth.h b/chrony/nts_ntp_auth.h
new file mode 100644
index 0000000..856beb3
--- /dev/null
+++ b/chrony/nts_ntp_auth.h
@@ -0,0 +1,43 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header for NTS Authenticator and Encrypted Extension Fields
+  extension field
+  */
+
+#ifndef GOT_NTS_NTP_AUTH_H
+#define GOT_NTS_NTP_AUTH_H
+
+#include "ntp.h"
+#include "siv.h"
+
+extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
+                              const unsigned char *nonce, int nonce_length,
+                              const unsigned char *plaintext, int plaintext_length,
+                              int min_ef_length);
+
+extern int NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
+                             int ef_start, unsigned char *plaintext, int buffer_length,
+                             int *plaintext_length);
+
+#endif
diff --git a/chrony/nts_ntp_client.c b/chrony/nts_ntp_client.c
new file mode 100644
index 0000000..34412a6
--- /dev/null
+++ b/chrony/nts_ntp_client.c
@@ -0,0 +1,709 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Client NTS-NTP authentication
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "nts_ntp_client.h"
+
+#include "conf.h"
+#include "logging.h"
+#include "memory.h"
+#include "ntp.h"
+#include "ntp_ext.h"
+#include "ntp_sources.h"
+#include "nts_ke_client.h"
+#include "nts_ntp.h"
+#include "nts_ntp_auth.h"
+#include "sched.h"
+#include "siv.h"
+#include "util.h"
+
+/* Maximum length of all cookies to avoid IP fragmentation */
+#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
+
+/* Magic string of files containing keys and cookies */
+#define DUMP_IDENTIFIER "NNC0\n"
+
+struct NNC_Instance_Record {
+  /* Address of NTS-KE server */
+  IPSockAddr nts_address;
+  /* Hostname or IP address for certificate verification */
+  char *name;
+  /* ID of trusted certificates */
+  uint32_t cert_set;
+  /* Configured NTP port */
+  uint16_t default_ntp_port;
+  /* Address of NTP server (can be negotiated in NTS-KE) */
+  IPSockAddr ntp_address;
+
+  NKC_Instance nke;
+  SIV_Instance siv;
+
+  int nke_attempts;
+  double next_nke_attempt;
+  double last_nke_success;
+
+  NKE_Context context;
+  unsigned int context_id;
+  NKE_Cookie cookies[NTS_MAX_COOKIES];
+  int num_cookies;
+  int cookie_index;
+  int auth_ready;
+  int nak_response;
+  int ok_response;
+  unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
+  unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
+};
+
+/* ================================================== */
+
+static void save_cookies(NNC_Instance inst);
+static void load_cookies(NNC_Instance inst);
+
+/* ================================================== */
+
+static void
+reset_instance(NNC_Instance inst)
+{
+  if (inst->nke)
+    NKC_DestroyInstance(inst->nke);
+  inst->nke = NULL;
+  if (inst->siv)
+    SIV_DestroyInstance(inst->siv);
+  inst->siv = NULL;
+
+  inst->nke_attempts = 0;
+  inst->next_nke_attempt = 0.0;
+  inst->last_nke_success = 0.0;
+
+  memset(&inst->context, 0, sizeof (inst->context));
+  inst->context_id = 0;
+  memset(inst->cookies, 0, sizeof (inst->cookies));
+  inst->num_cookies = 0;
+  inst->cookie_index = 0;
+  inst->auth_ready = 0;
+  inst->nak_response = 0;
+  inst->ok_response = 1;
+  memset(inst->nonce, 0, sizeof (inst->nonce));
+  memset(inst->uniq_id, 0, sizeof (inst->uniq_id));
+}
+
+/* ================================================== */
+
+NNC_Instance
+NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, uint16_t ntp_port)
+{
+  NNC_Instance inst;
+
+  inst = MallocNew(struct NNC_Instance_Record);
+
+  inst->nts_address = *nts_address;
+  inst->name = Strdup(name);
+  inst->cert_set = cert_set;
+  inst->default_ntp_port = ntp_port;
+  inst->ntp_address.ip_addr = nts_address->ip_addr;
+  inst->ntp_address.port = ntp_port;
+  inst->siv = NULL;
+  inst->nke = NULL;
+
+  reset_instance(inst);
+
+  /* Try to reload saved keys and cookies */
+  load_cookies(inst);
+
+  return inst;
+}
+
+/* ================================================== */
+
+void
+NNC_DestroyInstance(NNC_Instance inst)
+{
+  save_cookies(inst);
+
+  reset_instance(inst);
+
+  Free(inst->name);
+  Free(inst);
+}
+
+/* ================================================== */
+
+static int
+check_cookies(NNC_Instance inst)
+{
+  /* Force a new NTS-KE session if a NAK was received without a valid response,
+     or the keys encrypting the cookies need to be refreshed */
+  if (inst->num_cookies > 0 &&
+      ((inst->nak_response && !inst->ok_response) ||
+       SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
+    inst->num_cookies = 0;
+    DEBUG_LOG("Dropped cookies");
+  }
+
+  return inst->num_cookies > 0;
+}
+
+/* ================================================== */
+
+static int
+set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
+{
+  NTP_Remote_Address old_address, new_address;
+
+  old_address = inst->ntp_address;
+  new_address = *negotiated_address;
+
+  if (new_address.ip_addr.family == IPADDR_UNSPEC)
+    new_address.ip_addr = inst->nts_address.ip_addr;
+  if (new_address.port == 0)
+    new_address.port = inst->default_ntp_port;
+
+  if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
+      old_address.port == new_address.port)
+    /* Nothing to do */
+    return 1;
+
+  if (NSR_UpdateSourceNtpAddress(&old_address, &new_address) != NSR_Success) {
+    LOG(LOGS_ERR, "Could not change %s to negotiated address %s",
+        UTI_IPToString(&old_address.ip_addr), UTI_IPToString(&new_address.ip_addr));
+    return 0;
+  }
+
+  inst->ntp_address = new_address;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+update_next_nke_attempt(NNC_Instance inst, double now)
+{
+  int factor, interval;
+
+  if (!inst->nke)
+    return;
+
+  factor = NKC_GetRetryFactor(inst->nke);
+  interval = MIN(factor + inst->nke_attempts - 1, NKE_MAX_RETRY_INTERVAL2);
+  inst->next_nke_attempt = now + UTI_Log2ToDouble(interval);
+}
+
+/* ================================================== */
+
+static int
+get_cookies(NNC_Instance inst)
+{
+  NTP_Remote_Address ntp_address;
+  double now;
+  int got_data;
+
+  assert(inst->num_cookies == 0);
+
+  now = SCH_GetLastEventMonoTime();
+
+  /* Create and start a new NTS-KE session if not already present */
+  if (!inst->nke) {
+    if (now < inst->next_nke_attempt) {
+      DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)",
+                inst->next_nke_attempt - now);
+      return 0;
+    }
+
+    inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
+
+    inst->nke_attempts++;
+    update_next_nke_attempt(inst, now);
+
+    if (!NKC_Start(inst->nke))
+      return 0;
+  }
+
+  update_next_nke_attempt(inst, now);
+
+  /* Wait until the session stops */
+  if (NKC_IsActive(inst->nke))
+    return 0;
+
+  assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
+
+  /* Get the new keys, cookies and NTP address if the session was successful */
+  got_data = NKC_GetNtsData(inst->nke, &inst->context,
+                            inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
+                            &ntp_address);
+
+  NKC_DestroyInstance(inst->nke);
+  inst->nke = NULL;
+
+  if (!got_data)
+    return 0;
+
+  if (inst->siv)
+    SIV_DestroyInstance(inst->siv);
+  inst->siv = NULL;
+
+  inst->context_id++;
+
+  /* Force a new session if the NTP address is used by another source, with
+     an expectation that it will eventually get a non-conflicting address */
+  if (!set_ntp_address(inst, &ntp_address)) {
+    inst->num_cookies = 0;
+    return 0;
+  }
+
+  inst->last_nke_success = now;
+  inst->cookie_index = 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NNC_PrepareForAuth(NNC_Instance inst)
+{
+  inst->auth_ready = 0;
+
+  /* Prepare data for the next request and invalidate any responses to the
+     previous request */
+  UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
+  UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
+
+  /* Get new cookies if there are not any, or they are no longer usable */
+  if (!check_cookies(inst)) {
+    if (!get_cookies(inst))
+      return 0;
+  }
+
+  inst->nak_response = 0;
+
+  if (!inst->siv)
+    inst->siv = SIV_CreateInstance(inst->context.algorithm);
+
+  if (!inst->siv ||
+      !SIV_SetKey(inst->siv, inst->context.c2s.key, inst->context.c2s.length)) {
+    DEBUG_LOG("Could not set SIV key");
+    return 0;
+  }
+
+  inst->auth_ready = 1;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
+                        NTP_PacketInfo *info)
+{
+  NKE_Cookie *cookie;
+  int i, req_cookies;
+  void *ef_body;
+
+  if (!inst->auth_ready)
+    return 0;
+
+  inst->auth_ready = 0;
+
+  if (inst->num_cookies <= 0 || !inst->siv)
+    return 0;
+
+  if (info->mode != MODE_CLIENT)
+    return 0;
+
+  cookie = &inst->cookies[inst->cookie_index];
+  inst->num_cookies--;
+  inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
+
+  req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies,
+                    MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
+
+  if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
+                    inst->uniq_id, sizeof (inst->uniq_id)))
+    return 0;
+
+  if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
+                    cookie->cookie, cookie->length))
+    return 0;
+
+  for (i = 0; i < req_cookies - 1; i++) {
+    if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
+                           cookie->length, &ef_body))
+      return 0;
+    memset(ef_body, 0, cookie->length);
+  }
+
+  if (!NNA_GenerateAuthEF(packet, info, inst->siv, inst->nonce, sizeof (inst->nonce),
+                          (const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
+    return 0;
+
+  inst->ok_response = 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+parse_encrypted_efs(NNC_Instance inst, unsigned char *plaintext, int length)
+{
+  int ef_length, parsed;
+
+  for (parsed = 0; parsed < length; parsed += ef_length) {
+    if (!NEF_ParseSingleField(plaintext, length, parsed, &ef_length, NULL, NULL, NULL)) {
+      DEBUG_LOG("Could not parse encrypted EF");
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
+{
+  int ef_type, ef_body_length, ef_length, parsed, index, acceptable, saved;
+  void *ef_body;
+
+  acceptable = saved = 0;
+
+  for (parsed = 0; parsed < length; parsed += ef_length) {
+    if (!NEF_ParseSingleField(plaintext, length, parsed,
+                              &ef_length, &ef_type, &ef_body, &ef_body_length))
+      return 0;
+
+    if (ef_type != NTP_EF_NTS_COOKIE)
+      continue;
+
+    if (ef_length < NTP_MIN_EF_LENGTH || ef_body_length > sizeof (inst->cookies[0].cookie)) {
+      DEBUG_LOG("Unexpected cookie length %d", ef_body_length);
+      continue;
+    }
+
+    acceptable++;
+
+    if (inst->num_cookies >= NTS_MAX_COOKIES)
+      continue;
+
+    index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
+    assert(index >= 0 && index < NTS_MAX_COOKIES);
+    assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
+
+    memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
+    inst->cookies[index].length = ef_body_length;
+    inst->num_cookies++;
+
+    saved++;
+  }
+
+  DEBUG_LOG("Extracted %d cookies (saved %d)", acceptable, saved);
+
+  return acceptable > 0;
+}
+
+/* ================================================== */
+
+int
+NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
+                      NTP_PacketInfo *info)
+{
+  int ef_type, ef_body_length, ef_length, parsed, plaintext_length;
+  int has_valid_uniq_id = 0, has_valid_auth = 0;
+  unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
+  void *ef_body;
+
+  if (info->ext_fields == 0 || info->mode != MODE_SERVER)
+    return 0;
+
+  /* Accept at most one response per request */
+  if (inst->ok_response || inst->auth_ready)
+    return 0;
+
+  if (!inst->siv ||
+      !SIV_SetKey(inst->siv, inst->context.s2c.key, inst->context.s2c.length)) {
+    DEBUG_LOG("Could not set SIV key");
+    return 0;
+  }
+
+  for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
+    if (!NEF_ParseField(packet, info->length, parsed,
+                        &ef_length, &ef_type, &ef_body, &ef_body_length))
+      /* This is not expected as the packet already passed parsing */
+      return 0;
+
+    switch (ef_type) {
+      case NTP_EF_NTS_UNIQUE_IDENTIFIER:
+        if (ef_body_length != sizeof (inst->uniq_id) ||
+            memcmp(ef_body, inst->uniq_id, sizeof (inst->uniq_id)) != 0) {
+          DEBUG_LOG("Invalid uniq id");
+          return 0;
+        }
+        has_valid_uniq_id = 1;
+        break;
+      case NTP_EF_NTS_COOKIE:
+        DEBUG_LOG("Unencrypted cookie");
+        break;
+      case NTP_EF_NTS_AUTH_AND_EEF:
+        if (parsed + ef_length != info->length) {
+          DEBUG_LOG("Auth not last EF");
+          return 0;
+        }
+
+        if (!NNA_DecryptAuthEF(packet, info, inst->siv, parsed,
+                               plaintext, sizeof (plaintext), &plaintext_length))
+          return 0;
+
+        if (!parse_encrypted_efs(inst, plaintext, plaintext_length))
+          return 0;
+
+        has_valid_auth = 1;
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (!has_valid_uniq_id || !has_valid_auth) {
+    if (has_valid_uniq_id && packet->stratum == NTP_INVALID_STRATUM &&
+        ntohl(packet->reference_id) == NTP_KOD_NTS_NAK) {
+      DEBUG_LOG("NTS NAK");
+      inst->nak_response = 1;
+      return 0;
+    }
+
+    DEBUG_LOG("Missing NTS EF");
+    return 0;
+  }
+
+  if (!extract_cookies(inst, plaintext, plaintext_length))
+    return 0;
+
+  inst->ok_response = 1;
+
+  /* At this point we know the client interoperates with the server.  Allow a
+     new NTS-KE session to be started as soon as the cookies run out. */
+  inst->nke_attempts = 0;
+  inst->next_nke_attempt = 0.0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
+{
+  save_cookies(inst);
+
+  inst->nts_address.ip_addr = *address;
+  inst->ntp_address.ip_addr = *address;
+
+  reset_instance(inst);
+
+  DEBUG_LOG("NTS reset");
+
+  load_cookies(inst);
+}
+
+/* ================================================== */
+
+static void
+save_cookies(NNC_Instance inst)
+{
+  char buf[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename;
+  struct timespec now;
+  double context_time;
+  FILE *f;
+  int i;
+
+  if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr))
+    return;
+
+  dump_dir = CNF_GetNtsDumpDir();
+  if (!dump_dir)
+    return;
+
+  filename = UTI_IPToString(&inst->nts_address.ip_addr);
+
+  f = UTI_OpenFile(dump_dir, filename, ".tmp", 'w', 0600);
+  if (!f)
+    return;
+
+  SCH_GetLastEventTime(&now, NULL, NULL);
+  context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
+  context_time += UTI_TimespecToDouble(&now);
+
+  if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ",
+              DUMP_IDENTIFIER, inst->name, context_time,
+              UTI_IPToString(&inst->ntp_address.ip_addr), inst->ntp_address.port,
+              inst->context_id, (int)inst->context.algorithm) < 0 ||
+      !UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
+      fprintf(f, "%s ", buf) < 0 ||
+      !UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
+      fprintf(f, "%s\n", buf) < 0)
+    goto error;
+
+  for (i = 0; i < inst->num_cookies; i++) {
+    if (!UTI_BytesToHex(inst->cookies[i].cookie, inst->cookies[i].length, buf, sizeof (buf)) ||
+        fprintf(f, "%s\n", buf) < 0)
+      goto error;
+  }
+
+  fclose(f);
+
+  if (!UTI_RenameTempFile(dump_dir, filename, ".tmp", ".nts"))
+    ;
+  return;
+
+error:
+  DEBUG_LOG("Could not %s cookies for %s", "save", filename);
+  fclose(f);
+
+  if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
+    ;
+}
+
+/* ================================================== */
+
+#define MAX_WORDS 4
+
+static void
+load_cookies(NNC_Instance inst)
+{
+  char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
+  unsigned int context_id;
+  int i, algorithm, port;
+  double context_time;
+  struct timespec now;
+  IPSockAddr ntp_addr;
+  FILE *f;
+
+  dump_dir = CNF_GetNtsDumpDir();
+  if (!dump_dir)
+    return;
+
+  filename = UTI_IPToString(&inst->nts_address.ip_addr);
+
+  f = UTI_OpenFile(dump_dir, filename, ".nts", 'r', 0);
+  if (!f)
+    return;
+
+  /* Don't load this file again */
+  if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
+    ;
+
+  if (inst->siv)
+    SIV_DestroyInstance(inst->siv);
+  inst->siv = NULL;
+
+  if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
+      !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
+        strcmp(words[0], inst->name) != 0 ||
+      !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
+        sscanf(words[0], "%lf", &context_time) != 1 ||
+      !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
+        !UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 ||
+      !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 4 ||
+        sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
+    goto error;
+
+  inst->context.algorithm = algorithm;
+  inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
+                                            sizeof (inst->context.s2c.key));
+  inst->context.c2s.length = UTI_HexToBytes(words[3], inst->context.c2s.key,
+                                            sizeof (inst->context.c2s.key));
+
+  if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
+      inst->context.c2s.length != inst->context.s2c.length)
+    goto error;
+
+  for (i = 0; i < NTS_MAX_COOKIES && fgets(line, sizeof (line), f); i++) {
+    if (UTI_SplitString(line, words, MAX_WORDS) != 1)
+      goto error;
+
+    inst->cookies[i].length = UTI_HexToBytes(words[0], inst->cookies[i].cookie,
+                                             sizeof (inst->cookies[i].cookie));
+    if (inst->cookies[i].length == 0)
+      goto error;
+  }
+
+  inst->num_cookies = i;
+
+  ntp_addr.port = port;
+  if (!set_ntp_address(inst, &ntp_addr))
+    goto error;
+
+  SCH_GetLastEventTime(&now, NULL, NULL);
+  context_time -= UTI_TimespecToDouble(&now);
+  if (context_time > 0)
+    context_time = 0;
+  inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
+  inst->context_id = context_id;
+
+  fclose(f);
+
+  DEBUG_LOG("Loaded %d cookies for %s", i, filename);
+  return;
+
+error:
+  DEBUG_LOG("Could not %s cookies for %s", "load", filename);
+  fclose(f);
+
+  memset(&inst->context, 0, sizeof (inst->context));
+  inst->num_cookies = 0;
+}
+
+/* ================================================== */
+
+void
+NNC_DumpData(NNC_Instance inst)
+{
+  save_cookies(inst);
+}
+
+/* ================================================== */
+
+void
+NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
+{
+  report->key_id = inst->context_id;
+  report->key_type = inst->context.algorithm;
+  report->key_length = 8 * inst->context.s2c.length;
+  report->ke_attempts = inst->nke_attempts;
+  if (report->key_length > 0)
+    report->last_ke_ago = SCH_GetLastEventMonoTime() - inst->last_nke_success;
+  else
+    report->last_ke_ago = -1;
+  report->cookies = inst->num_cookies;
+  report->cookie_length = inst->num_cookies > 0 ? inst->cookies[inst->cookie_index].length : 0;
+  report->nak = inst->nak_response;
+}
diff --git a/chrony/nts_ntp_client.h b/chrony/nts_ntp_client.h
new file mode 100644
index 0000000..2c314cc
--- /dev/null
+++ b/chrony/nts_ntp_client.h
@@ -0,0 +1,51 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for client NTS-NTP authentication
+  */
+
+#ifndef GOT_NTS_NTP_CLIENT_H
+#define GOT_NTS_NTP_CLIENT_H
+
+#include "addressing.h"
+#include "ntp.h"
+#include "reports.h"
+
+typedef struct NNC_Instance_Record *NNC_Instance;
+
+extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name,
+                                       uint32_t cert_set, uint16_t ntp_port);
+extern void NNC_DestroyInstance(NNC_Instance inst);
+extern int NNC_PrepareForAuth(NNC_Instance inst);
+extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
+                                   NTP_PacketInfo *info);
+extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
+                                 NTP_PacketInfo *info);
+
+extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
+
+extern void NNC_DumpData(NNC_Instance inst);
+
+extern void NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report);
+
+#endif
diff --git a/chrony/nts_ntp_server.c b/chrony/nts_ntp_server.c
new file mode 100644
index 0000000..9226c89
--- /dev/null
+++ b/chrony/nts_ntp_server.c
@@ -0,0 +1,283 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Server NTS-NTP authentication
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "nts_ntp_server.h"
+
+#include "conf.h"
+#include "logging.h"
+#include "memory.h"
+#include "ntp.h"
+#include "ntp_ext.h"
+#include "nts_ke_server.h"
+#include "nts_ntp.h"
+#include "nts_ntp_auth.h"
+#include "siv.h"
+#include "util.h"
+
+#define SERVER_SIV AEAD_AES_SIV_CMAC_256
+
+struct NtsServer {
+  SIV_Instance siv;
+  unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
+  NKE_Cookie cookies[NTS_MAX_COOKIES];
+  int num_cookies;
+  NTP_int64 req_tx;
+};
+
+/* The server instance handling all requests */
+struct NtsServer *server;
+
+/* ================================================== */
+
+void
+NNS_Initialise(void)
+{
+  const char **certs, **keys;
+
+  /* Create an NTS-NTP server instance only if NTS-KE server is enabled */
+  if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
+    server = NULL;
+    return;
+  }
+
+  server = Malloc(sizeof (struct NtsServer));
+  server->siv = SIV_CreateInstance(SERVER_SIV);
+  if (!server->siv)
+    LOG_FATAL("Could not initialise SIV cipher");
+}
+
+/* ================================================== */
+
+void
+NNS_Finalise(void)
+{
+  if (!server)
+    return;
+
+  SIV_DestroyInstance(server->siv);
+  Free(server);
+  server = NULL;
+}
+
+/* ================================================== */
+
+int
+NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
+{
+  int ef_type, ef_body_length, ef_length, has_uniq_id = 0, has_auth = 0, has_cookie = 0;
+  int i, plaintext_length, parsed, requested_cookies, cookie_length = -1, auth_start = 0;
+  unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
+  NKE_Context context;
+  NKE_Cookie cookie;
+  void *ef_body;
+
+  *kod = 0;
+
+  if (!server)
+    return 0;
+
+  server->num_cookies = 0;
+  server->req_tx = packet->transmit_ts;
+
+  if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
+    return 0;
+
+  requested_cookies = 0;
+
+  for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
+    if (!NEF_ParseField(packet, info->length, parsed,
+                        &ef_length, &ef_type, &ef_body, &ef_body_length))
+      /* This is not expected as the packet already passed NAU_ParsePacket() */
+      return 0;
+
+    switch (ef_type) {
+      case NTP_EF_NTS_UNIQUE_IDENTIFIER:
+        has_uniq_id = 1;
+        break;
+      case NTP_EF_NTS_COOKIE:
+        if (has_cookie || ef_body_length > sizeof (cookie.cookie)) {
+          DEBUG_LOG("Unexpected cookie/length");
+          return 0;
+        }
+        cookie.length = ef_body_length;
+        memcpy(cookie.cookie, ef_body, ef_body_length);
+        has_cookie = 1;
+        /* Fall through */
+      case NTP_EF_NTS_COOKIE_PLACEHOLDER:
+        requested_cookies++;
+
+        if (cookie_length >= 0 && cookie_length != ef_body_length) {
+          DEBUG_LOG("Invalid cookie/placeholder length");
+          return 0;
+        }
+        cookie_length = ef_body_length;
+        break;
+      case NTP_EF_NTS_AUTH_AND_EEF:
+        if (parsed + ef_length != info->length) {
+          DEBUG_LOG("Auth not last EF");
+          return 0;
+        }
+
+        auth_start = parsed;
+        has_auth = 1;
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (!has_uniq_id || !has_cookie || !has_auth) {
+    DEBUG_LOG("Missing an NTS EF");
+    return 0;
+  }
+
+  if (!NKS_DecodeCookie(&cookie, &context)) {
+    *kod = NTP_KOD_NTS_NAK;
+    return 0;
+  }
+
+  if (context.algorithm != SERVER_SIV) {
+    DEBUG_LOG("Unexpected SIV");
+    return 0;
+  }
+
+  if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) {
+    DEBUG_LOG("Could not set C2S key");
+    return 0;
+  }
+
+  if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
+                         plaintext, sizeof (plaintext), &plaintext_length)) {
+    *kod = NTP_KOD_NTS_NAK;
+    return 0;
+  }
+
+  for (parsed = 0; parsed < plaintext_length; parsed += ef_length) {
+    if (!NEF_ParseSingleField(plaintext, plaintext_length, parsed,
+                              &ef_length, &ef_type, &ef_body, &ef_body_length)) {
+      DEBUG_LOG("Could not parse encrypted EF");
+      return 0;
+    }
+
+    switch (ef_type) {
+      case NTP_EF_NTS_COOKIE_PLACEHOLDER:
+        if (cookie_length != ef_body_length) {
+          DEBUG_LOG("Invalid cookie/placeholder length");
+          return 0;
+        }
+        requested_cookies++;
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) {
+    DEBUG_LOG("Could not set S2C key");
+    return 0;
+  }
+
+  /* Prepare data for NNS_GenerateResponseAuth() to minimise the time spent
+     there (when the TX timestamp is already set) */
+
+  UTI_GetRandomBytes(server->nonce, sizeof (server->nonce));
+
+  assert(sizeof (server->cookies) / sizeof (server->cookies[0]) == NTS_MAX_COOKIES);
+  for (i = 0; i < NTS_MAX_COOKIES && i < requested_cookies; i++)
+    if (!NKS_GenerateCookie(&context, &server->cookies[i]))
+      return 0;
+
+  server->num_cookies = i;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
+                         NTP_Packet *response, NTP_PacketInfo *res_info,
+                         uint32_t kod)
+{
+  int i, ef_type, ef_body_length, ef_length, parsed;
+  void *ef_body;
+  unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
+  int plaintext_length;
+
+  if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER)
+    return 0;
+
+  /* Make sure this is a response to the request from the last call
+     of NNS_CheckRequestAuth() */
+  if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
+    assert(0);
+
+  for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
+    if (!NEF_ParseField(request, req_info->length, parsed,
+                        &ef_length, &ef_type, &ef_body, &ef_body_length))
+      /* This is not expected as the packet already passed parsing */
+      return 0;
+
+    switch (ef_type) {
+      case NTP_EF_NTS_UNIQUE_IDENTIFIER:
+        /* Copy the ID from the request */
+        if (!NEF_AddField(response, res_info, ef_type, ef_body, ef_body_length))
+          return 0;
+      default:
+        break;
+    }
+  }
+
+  /* NTS NAK response does not have any other fields */
+  if (kod)
+    return 1;
+
+  for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
+    if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
+                      NTP_EF_NTS_COOKIE, server->cookies[i].cookie,
+                      server->cookies[i].length, &ef_length))
+      return 0;
+
+    plaintext_length += ef_length;
+    assert(plaintext_length <= sizeof (plaintext));
+  }
+
+  server->num_cookies = 0;
+
+  /* Generate an authenticator field which will make the length
+     of the response equal to the length of the request */
+  if (!NNA_GenerateAuthEF(response, res_info, server->siv,
+                          server->nonce, sizeof (server->nonce),
+                          plaintext, plaintext_length,
+                          req_info->length - res_info->length))
+    return 0;
+
+  return 1;
+}
diff --git a/chrony_3_3/hash.h b/chrony/nts_ntp_server.h
similarity index 64%
copy from chrony_3_3/hash.h
copy to chrony/nts_ntp_server.h
index 185da66..fea28f2 100644
--- a/chrony_3_3/hash.h
+++ b/chrony/nts_ntp_server.h
@@ -2,7 +2,7 @@
   chronyd/chronyc - Programs for keeping computer clocks accurate.
 
  **********************************************************************
- * Copyright (C) Miroslav Lichvar  2012
+ * Copyright (C) Miroslav Lichvar  2020
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -21,23 +21,20 @@
 
   =======================================================================
 
-  Header file for crypto hashing.
-
+  Header file for server NTS-NTP authentication
   */
 
-#ifndef GOT_HASH_H
-#define GOT_HASH_H
+#ifndef GOT_NTS_NTP_SERVER_H
+#define GOT_NTS_NTP_SERVER_H
 
-/* length of hash values produced by SHA512 */
-#define MAX_HASH_LENGTH 64
+#include "ntp.h"
 
-extern int HSH_GetHashId(const char *name);
+extern void NNS_Initialise(void);
+extern void NNS_Finalise(void);
 
-extern unsigned int HSH_Hash(int id,
-    const unsigned char *in1, unsigned int in1_len,
-    const unsigned char *in2, unsigned int in2_len,
-    unsigned char *out, unsigned int out_len);
-
-extern void HSH_Finalise(void);
+extern int NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod);
+extern int NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
+                                    NTP_Packet *response, NTP_PacketInfo *res_info,
+                                    uint32_t kod);
 
 #endif
diff --git a/chrony_3_3/pktlength.c b/chrony/pktlength.c
similarity index 85%
rename from chrony_3_3/pktlength.c
rename to chrony/pktlength.c
index 14a43f7..642e477 100644
--- a/chrony_3_3/pktlength.c
+++ b/chrony/pktlength.c
@@ -87,7 +87,7 @@
   REQ_LENGTH_ENTRY(del_source, null),           /* DEL_SOURCE */
   REQ_LENGTH_ENTRY(null, null),                 /* WRITERTC */
   REQ_LENGTH_ENTRY(dfreq, null),                /* DFREQ */
-  REQ_LENGTH_ENTRY(doffset, null),              /* DOFFSET */
+  { 0, 0 },                                     /* DOFFSET - not supported */
   REQ_LENGTH_ENTRY(null, tracking),             /* TRACKING */
   REQ_LENGTH_ENTRY(sourcestats, sourcestats),   /* SOURCESTATS */
   REQ_LENGTH_ENTRY(null, rtc),                  /* RTCREPORT */
@@ -110,15 +110,25 @@
   REQ_LENGTH_ENTRY(smoothtime, null),           /* SMOOTHTIME */
   REQ_LENGTH_ENTRY(null, null),                 /* REFRESH */
   REQ_LENGTH_ENTRY(null, server_stats),         /* SERVER_STATS */
-  REQ_LENGTH_ENTRY(client_accesses_by_index,
-                   client_accesses_by_index),   /* CLIENT_ACCESSES_BY_INDEX2 */
+  { 0, 0 },                                     /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
   REQ_LENGTH_ENTRY(local, null),                /* LOCAL2 */
   REQ_LENGTH_ENTRY(ntp_data, ntp_data),         /* NTP_DATA */
   { 0, 0 },                                     /* ADD_SERVER2 */
   { 0, 0 },                                     /* ADD_PEER2 */
-  REQ_LENGTH_ENTRY(ntp_source, null),           /* ADD_SERVER3 */
-  REQ_LENGTH_ENTRY(ntp_source, null),           /* ADD_PEER3 */
+  { 0, 0 },                                     /* ADD_SERVER3 */
+  { 0, 0 },                                     /* ADD_PEER3 */
   REQ_LENGTH_ENTRY(null, null),                 /* SHUTDOWN */
+  REQ_LENGTH_ENTRY(null, null),                 /* ONOFFLINE */
+  REQ_LENGTH_ENTRY(ntp_source, null),           /* ADD_SOURCE */
+  REQ_LENGTH_ENTRY(ntp_source_name,
+                   ntp_source_name),            /* NTP_SOURCE_NAME */
+  REQ_LENGTH_ENTRY(null, null),                 /* RESET_SOURCES */
+  REQ_LENGTH_ENTRY(auth_data, auth_data),       /* AUTH_DATA */
+  REQ_LENGTH_ENTRY(client_accesses_by_index,
+                   client_accesses_by_index),   /* CLIENT_ACCESSES_BY_INDEX3 */
+  REQ_LENGTH_ENTRY(select_data, select_data),   /* SELECT_DATA */
+  REQ_LENGTH_ENTRY(null, null),                 /* RELOAD_SOURCES */
+  REQ_LENGTH_ENTRY(doffset, null),              /* DOFFSET2 */
 };
 
 static const uint16_t reply_lengths[] = {
@@ -136,11 +146,17 @@
   0,                                            /* MANUAL_LIST - not supported */
   RPY_LENGTH_ENTRY(activity),                   /* ACTIVITY */
   RPY_LENGTH_ENTRY(smoothing),                  /* SMOOTHING */
-  RPY_LENGTH_ENTRY(server_stats),               /* SERVER_STATS */
-  RPY_LENGTH_ENTRY(client_accesses_by_index),   /* CLIENT_ACCESSES_BY_INDEX2 */
+  0,                                            /* SERVER_STATS - not supported */
+  0,                                            /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
   RPY_LENGTH_ENTRY(ntp_data),                   /* NTP_DATA */
   RPY_LENGTH_ENTRY(manual_timestamp),           /* MANUAL_TIMESTAMP2 */
   RPY_LENGTH_ENTRY(manual_list),                /* MANUAL_LIST2 */
+  RPY_LENGTH_ENTRY(ntp_source_name),            /* NTP_SOURCE_NAME */
+  RPY_LENGTH_ENTRY(auth_data),                  /* AUTH_DATA */
+  RPY_LENGTH_ENTRY(client_accesses_by_index),   /* CLIENT_ACCESSES_BY_INDEX3 */
+  0,                                            /* SERVER_STATS2 - not supported */
+  RPY_LENGTH_ENTRY(select_data),                /* SELECT_DATA */
+  RPY_LENGTH_ENTRY(server_stats),               /* SERVER_STATS3 */
 };
 
 /* ================================================== */
diff --git a/chrony_3_3/pktlength.h b/chrony/pktlength.h
similarity index 100%
rename from chrony_3_3/pktlength.h
rename to chrony/pktlength.h
diff --git a/chrony_3_3/privops.c b/chrony/privops.c
similarity index 84%
rename from chrony_3_3/privops.c
rename to chrony/privops.c
index 359e163..3fb5cbd 100644
--- a/chrony_3_3/privops.c
+++ b/chrony/privops.c
@@ -33,6 +33,7 @@
 #include "nameserv.h"
 #include "logging.h"
 #include "privops.h"
+#include "socket.h"
 #include "util.h"
 
 #define OP_ADJUSTTIME     1024
@@ -158,7 +159,7 @@
 static int
 send_response(int fd, const PrvResponse *res)
 {
-  if (send(fd, res, sizeof (*res), 0) != sizeof (*res))
+  if (SCK_Send(fd, res, sizeof (*res), 0) != sizeof (*res))
     return 0;
 
   return 1;
@@ -170,37 +171,23 @@
 static int
 receive_from_daemon(int fd, PrvRequest *req)
 {
-  struct msghdr msg;
-  struct cmsghdr *cmsg;
-  struct iovec iov;
-  char cmsgbuf[256];
+  SCK_Message *message;
 
-  iov.iov_base = req;
-  iov.iov_len = sizeof (*req);
-
-  msg.msg_name = NULL;
-  msg.msg_namelen = 0;
-  msg.msg_iov = &iov;
-  msg.msg_iovlen = 1;
-  msg.msg_control = (void *)cmsgbuf;
-  msg.msg_controllen = sizeof (cmsgbuf);
-  msg.msg_flags = MSG_WAITALL;
-
-  /* read the data */
-  if (recvmsg(fd, &msg, 0) != sizeof (*req))
+  message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
+  if (!message || message->length != sizeof (*req))
     return 0;
 
+  memcpy(req, message->data, sizeof (*req));
+
   if (req->op == OP_BINDSOCKET) {
-    /* extract transferred descriptor */
-    req->data.bind_socket.sock = -1;
-    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
-        memcpy(&req->data.bind_socket.sock, CMSG_DATA(cmsg), sizeof (int));
-    }
+    req->data.bind_socket.sock = message->descriptor;
 
     /* return error if valid descriptor not found */
     if (req->data.bind_socket.sock < 0)
       return 0;
+  } else if (message->descriptor >= 0) {
+    SCK_CloseSocket(message->descriptor);
+    return 0;
   }
 
   return 1;
@@ -257,8 +244,7 @@
 static void
 do_bind_socket(ReqBindSocket *req, PrvResponse *res)
 {
-  unsigned short port;
-  IPAddr ip;
+  IPSockAddr ip_saddr;
   int sock_fd;
   struct sockaddr *sa;
   socklen_t sa_len;
@@ -267,10 +253,11 @@
   sa_len = req->sa_len;
   sock_fd = req->sock;
 
-  UTI_SockaddrToIPAndPort(sa, &ip, &port);
-  if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) {
-    close(sock_fd);
-    res_fatal(res, "Invalid port %d", port);
+  SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
+  if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
+      ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort()) {
+    SCK_CloseSocket(sock_fd);
+    res_fatal(res, "Invalid port %d", ip_saddr.port);
     return;
   }
 
@@ -279,7 +266,7 @@
     res->res_errno = errno;
 
   /* sock is still open on daemon side, but we're done with it in the helper */
-  close(sock_fd);
+  SCK_CloseSocket(sock_fd);
 }
 #endif
 
@@ -373,7 +360,7 @@
     send_response(fd, &res);
   }
 
-  close(fd);
+  SCK_CloseSocket(fd);
   exit(0);
 }
 
@@ -386,7 +373,7 @@
 {
   int resp_len;
 
-  resp_len = recv(helper_fd, res, sizeof (*res), 0);
+  resp_len = SCK_Receive(helper_fd, res, sizeof (*res), 0);
   if (resp_len < 0)
     LOG_FATAL("Could not read from helper : %s", strerror(errno));
   if (resp_len != sizeof (*res))
@@ -409,41 +396,22 @@
 static void
 send_request(PrvRequest *req)
 {
-  struct msghdr msg;
-  struct iovec iov;
-  char cmsgbuf[256];
+  SCK_Message message;
+  int flags;
 
-  iov.iov_base = req;
-  iov.iov_len = sizeof (*req);
+  SCK_InitMessage(&message, SCK_ADDR_UNSPEC);
 
-  msg.msg_name = NULL;
-  msg.msg_namelen = 0;
-  msg.msg_iov = &iov;
-  msg.msg_iovlen = 1;
-  msg.msg_control = NULL;
-  msg.msg_controllen = 0;
-  msg.msg_flags = 0;
+  message.data = req;
+  message.length = sizeof (*req);
+  flags = 0;
 
   if (req->op == OP_BINDSOCKET) {
     /* send file descriptor as a control message */
-    struct cmsghdr *cmsg;
-    int *ptr_send_fd;
-
-    msg.msg_control = cmsgbuf;
-    msg.msg_controllen = CMSG_SPACE(sizeof (int));
-
-    cmsg = CMSG_FIRSTHDR(&msg);
-    memset(cmsg, 0, CMSG_SPACE(sizeof (int)));
-
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    cmsg->cmsg_len = CMSG_LEN(sizeof (int));
-
-    ptr_send_fd = (int *)CMSG_DATA(cmsg);
-    *ptr_send_fd = req->data.bind_socket.sock;
+    message.descriptor = req->data.bind_socket.sock;
+    flags |= SCK_FLAG_MSG_DESCRIPTOR;
   }
 
-  if (sendmsg(helper_fd, &msg, 0) < 0) {
+  if (!SCK_SendMessage(helper_fd, &message, flags)) {
     /* don't try to send another request from exit() */
     helper_fd = -1;
     LOG_FATAL("Could not send to helper : %s", strerror(errno));
@@ -573,13 +541,13 @@
 int
 PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
 {
+  IPSockAddr ip_saddr;
   PrvRequest req;
   PrvResponse res;
-  IPAddr ip;
-  unsigned short port;
 
-  UTI_SockaddrToIPAndPort(address, &ip, &port);
-  if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort())
+  SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
+  if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
+      ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort())
     assert(0);
 
   if (!have_helper())
@@ -589,6 +557,7 @@
   req.op = OP_BINDSOCKET;
   req.data.bind_socket.sock = sock;
   req.data.bind_socket.sa_len = address_len;
+  assert(address_len <= sizeof (req.data.bind_socket.sa));
   memcpy(&req.data.bind_socket.sa.u, address, address_len);
 
   submit_request(&req, &res);
@@ -616,7 +585,6 @@
   req.op = OP_NAME2IPADDRESS;
   if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
                "%s", name) >= sizeof (req.data.name_to_ipaddress.name)) {
-    DEBUG_LOG("Name too long");
     return DNS_Failure;
   }
 
@@ -670,20 +638,14 @@
 PRV_StartHelper(void)
 {
   pid_t pid;
-  int fd, sock_pair[2];
+  int fd, sock_fd1, sock_fd2;
 
   if (have_helper())
     LOG_FATAL("Helper already running");
 
-  if (
-#ifdef SOCK_SEQPACKET
-      socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) &&
-#endif
-      socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair))
-    LOG_FATAL("socketpair() failed : %s", strerror(errno));
-
-  UTI_FdSetCloexec(sock_pair[0]);
-  UTI_FdSetCloexec(sock_pair[1]);
+  sock_fd1 = SCK_OpenUnixSocketPair(SCK_FLAG_BLOCK, &sock_fd2);
+  if (sock_fd1 < 0)
+    LOG_FATAL("Could not open socket pair");
 
   pid = fork();
   if (pid < 0)
@@ -691,23 +653,26 @@
 
   if (pid == 0) {
     /* child process */
-    close(sock_pair[0]);
+    SCK_CloseSocket(sock_fd1);
 
-    /* close other descriptors inherited from the parent process */
-    for (fd = 0; fd < 1024; fd++) {
-      if (fd != sock_pair[1])
+    /* close other descriptors inherited from the parent process, except
+       stdin, stdout, and stderr */
+    for (fd = STDERR_FILENO + 1; fd < 1024; fd++) {
+      if (fd != sock_fd2)
         close(fd);
     }
 
-    /* ignore signals, the process will exit on OP_QUIT request */
-    UTI_SetQuitSignalsHandler(SIG_IGN);
+    UTI_ResetGetRandomFunctions();
 
-    helper_main(sock_pair[1]);
+    /* ignore signals, the process will exit on OP_QUIT request */
+    UTI_SetQuitSignalsHandler(SIG_IGN, 1);
+
+    helper_main(sock_fd2);
 
   } else {
     /* parent process */
-    close(sock_pair[1]);
-    helper_fd = sock_pair[0];
+    SCK_CloseSocket(sock_fd2);
+    helper_fd = sock_fd1;
     helper_pid = pid;
 
     /* stop the helper even when not exiting cleanly from the main function */
diff --git a/chrony_3_3/privops.h b/chrony/privops.h
similarity index 100%
rename from chrony_3_3/privops.h
rename to chrony/privops.h
diff --git a/chrony/ptp.h b/chrony/ptp.h
new file mode 100644
index 0000000..7a93590
--- /dev/null
+++ b/chrony/ptp.h
@@ -0,0 +1,64 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2021
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ **********************************************************************
+
+  =======================================================================
+
+  This is the header file for the Precision Time Protocol (PTP).
+
+  */
+#ifndef GOT_PTP_H
+#define GOT_PTP_H
+
+#include "sysincl.h"
+
+#include "ntp.h"
+
+#define PTP_VERSION 2
+#define PTP_TYPE_DELAY_REQ 1
+#define PTP_DOMAIN_NTP 123
+#define PTP_FLAG_UNICAST (1 << (2 + 8))
+#define PTP_TLV_NTP 0x2023
+
+typedef struct {
+  uint8_t type;
+  uint8_t version;
+  uint16_t length;
+  uint8_t domain;
+  uint8_t min_sdoid;
+  uint16_t flags;
+  uint8_t rest[26];
+} PTP_Header;
+
+typedef struct {
+  uint16_t type;
+  uint16_t length;
+} PTP_TlvHeader;
+
+typedef struct {
+  PTP_Header header;
+  uint8_t origin_ts[10];
+  PTP_TlvHeader tlv_header;
+  NTP_Packet ntp_msg;
+} PTP_NtpMessage;
+
+#define PTP_NTP_PREFIX_LENGTH (int)offsetof(PTP_NtpMessage, ntp_msg)
+
+#endif
diff --git a/chrony/refclock.c b/chrony/refclock.c
new file mode 100644
index 0000000..968caa7
--- /dev/null
+++ b/chrony/refclock.c
@@ -0,0 +1,787 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2009-2011, 2013-2014, 2016-2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Routines implementing reference clocks.
+
+  */
+
+#include "config.h"
+
+#include "array.h"
+#include "refclock.h"
+#include "reference.h"
+#include "conf.h"
+#include "local.h"
+#include "memory.h"
+#include "util.h"
+#include "sources.h"
+#include "logging.h"
+#include "regress.h"
+#include "samplefilt.h"
+#include "sched.h"
+
+/* Maximum offset of locked reference as a fraction of the PPS interval */
+#define PPS_LOCK_LIMIT 0.4
+
+/* list of refclock drivers */
+extern RefclockDriver RCL_SHM_driver;
+extern RefclockDriver RCL_SOCK_driver;
+extern RefclockDriver RCL_PPS_driver;
+extern RefclockDriver RCL_PHC_driver;
+
+struct FilterSample {
+  double offset;
+  double dispersion;
+  struct timespec sample_time;
+};
+
+struct MedianFilter {
+  int length;
+  int index;
+  int used;
+  int last;
+  int avg_var_n;
+  double avg_var;
+  double max_var;
+  struct FilterSample *samples;
+  int *selected;
+  double *x_data;
+  double *y_data;
+  double *w_data;
+};
+
+struct RCL_Instance_Record {
+  RefclockDriver *driver;
+  void *data;
+  char *driver_parameter;
+  int driver_parameter_length;
+  int driver_poll;
+  int driver_polled;
+  int poll;
+  int leap_status;
+  int pps_forced;
+  int pps_rate;
+  int pps_active;
+  int max_lock_age;
+  int stratum;
+  int tai;
+  uint32_t ref_id;
+  uint32_t lock_ref;
+  double offset;
+  double delay;
+  double precision;
+  double pulse_width;
+  SPF_Instance filter;
+  SCH_TimeoutID timeout_id;
+  SRC_Instance source;
+};
+
+/* Array of pointers to RCL_Instance_Record */
+static ARR_Instance refclocks;
+
+static LOG_FileID logfileid;
+
+static int valid_sample_time(RCL_Instance instance, struct timespec *sample_time);
+static int pps_stratum(RCL_Instance instance, struct timespec *ts);
+static void poll_timeout(void *arg);
+static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
+             double doffset, LCL_ChangeType change_type, void *anything);
+static void add_dispersion(double dispersion, void *anything);
+static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
+
+static RCL_Instance
+get_refclock(unsigned int index)
+{
+  return *(RCL_Instance *)ARR_GetElement(refclocks, index);
+}
+
+void
+RCL_Initialise(void)
+{
+  refclocks = ARR_CreateInstance(sizeof (RCL_Instance));
+
+  CNF_AddRefclocks();
+
+  if (ARR_GetSize(refclocks) > 0) {
+    LCL_AddParameterChangeHandler(slew_samples, NULL);
+    LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
+  }
+
+  logfileid = CNF_GetLogRefclocks() ? LOG_FileOpen("refclocks",
+      "   Date (UTC) Time         Refid  DP L P  Raw offset   Cooked offset      Disp.")
+    : -1;
+}
+
+void
+RCL_Finalise(void)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(refclocks); i++) {
+    RCL_Instance inst = get_refclock(i);
+
+    if (inst->driver->fini)
+      inst->driver->fini(inst);
+
+    SPF_DestroyInstance(inst->filter);
+    Free(inst->driver_parameter);
+    SRC_DestroyInstance(inst->source);
+    Free(inst);
+  }
+
+  if (ARR_GetSize(refclocks) > 0) {
+    LCL_RemoveParameterChangeHandler(slew_samples, NULL);
+    LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL);
+  }
+
+  ARR_DestroyInstance(refclocks);
+}
+
+int
+RCL_AddRefclock(RefclockParameters *params)
+{
+  RCL_Instance inst;
+
+  inst = MallocNew(struct RCL_Instance_Record);
+  *(RCL_Instance *)ARR_GetNewElement(refclocks) = inst;
+
+  if (strcmp(params->driver_name, "SHM") == 0) {
+    inst->driver = &RCL_SHM_driver;
+  } else if (strcmp(params->driver_name, "SOCK") == 0) {
+    inst->driver = &RCL_SOCK_driver;
+  } else if (strcmp(params->driver_name, "PPS") == 0) {
+    inst->driver = &RCL_PPS_driver;
+  } else if (strcmp(params->driver_name, "PHC") == 0) {
+    inst->driver = &RCL_PHC_driver;
+  } else {
+    LOG_FATAL("unknown refclock driver %s", params->driver_name);
+  }
+
+  if (!inst->driver->init && !inst->driver->poll)
+    LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
+
+  if (params->tai && !CNF_GetLeapSecTimezone())
+    LOG_FATAL("refclock tai option requires leapsectz");
+
+  inst->data = NULL;
+  inst->driver_parameter = Strdup(params->driver_parameter);
+  inst->driver_parameter_length = 0;
+  inst->driver_poll = params->driver_poll;
+  inst->poll = params->poll;
+  inst->driver_polled = 0;
+  inst->leap_status = LEAP_Normal;
+  inst->pps_forced = params->pps_forced;
+  inst->pps_rate = params->pps_rate;
+  inst->pps_active = 0;
+  inst->max_lock_age = params->max_lock_age;
+  inst->stratum = params->stratum;
+  inst->tai = params->tai;
+  inst->lock_ref = params->lock_ref_id;
+  inst->offset = params->offset;
+  inst->delay = params->delay;
+  inst->precision = LCL_GetSysPrecisionAsQuantum();
+  inst->precision = MAX(inst->precision, params->precision);
+  inst->pulse_width = params->pulse_width;
+  inst->timeout_id = -1;
+  inst->source = NULL;
+
+  if (inst->driver_parameter) {
+    int i;
+
+    inst->driver_parameter_length = strlen(inst->driver_parameter);
+    for (i = 0; i < inst->driver_parameter_length; i++)
+      if (inst->driver_parameter[i] == ':')
+        inst->driver_parameter[i] = '\0';
+  }
+
+  if (inst->pps_rate < 1)
+    inst->pps_rate = 1;
+
+  if (params->ref_id)
+    inst->ref_id = params->ref_id;
+  else {
+    unsigned char ref[5] = { 0, 0, 0, 0, 0 };
+    unsigned int index = ARR_GetSize(refclocks) - 1;
+
+    snprintf((char *)ref, sizeof (ref), "%3.3s", params->driver_name);
+    ref[3] = index % 10 + '0';
+    if (index >= 10)
+      ref[2] = (index / 10) % 10 + '0';
+
+    inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
+  }
+
+  if (inst->driver->poll) {
+    int max_samples;
+
+    if (inst->driver_poll > inst->poll)
+      inst->driver_poll = inst->poll;
+
+    max_samples = 1 << (inst->poll - inst->driver_poll);
+    if (max_samples < params->filter_length) {
+      if (max_samples < 4) {
+        LOG(LOGS_WARN, "Setting filter length for %s to %d",
+            UTI_RefidToString(inst->ref_id), max_samples);
+      }
+      params->filter_length = max_samples;
+    }
+  }
+
+  if (inst->driver->init && !inst->driver->init(inst))
+    LOG_FATAL("refclock %s initialisation failed", params->driver_name);
+
+  /* Require the filter to have at least 4 samples to produce a filtered
+     sample, or be full for shorter lengths, and combine 60% of samples
+     closest to the median */
+  inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
+                                    params->max_dispersion, 0.6);
+
+  inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
+                                       NULL, params->min_samples, params->max_samples,
+                                       0.0, 0.0);
+
+  DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
+      params->driver_name, UTI_RefidToString(inst->ref_id),
+      inst->poll, inst->driver_poll, params->filter_length);
+
+  return 1;
+}
+
+void
+RCL_StartRefclocks(void)
+{
+  unsigned int i, j, n, lock_index;
+
+  n = ARR_GetSize(refclocks);
+
+  for (i = 0; i < n; i++) {
+    RCL_Instance inst = get_refclock(i);
+
+    SRC_SetActive(inst->source);
+    inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
+
+    /* Replace lock refid with the refclock's index, or -1 if not valid */
+
+    lock_index = -1;
+
+    if (inst->lock_ref != 0) {
+      for (j = 0; j < n; j++) {
+        RCL_Instance inst2 = get_refclock(j);
+
+        if (inst->lock_ref != inst2->ref_id)
+          continue;
+
+        if (inst->driver->poll && inst2->driver->poll &&
+            (double)inst->max_lock_age / inst->pps_rate < UTI_Log2ToDouble(inst2->driver_poll))
+          LOG(LOGS_WARN, "%s maxlockage too small for %s",
+              UTI_RefidToString(inst->ref_id), UTI_RefidToString(inst2->ref_id));
+
+        lock_index = j;
+        break;
+      }
+
+      if (lock_index == -1 || lock_index == i)
+        LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
+    }
+
+    inst->lock_ref = lock_index;
+  }
+}
+
+void
+RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
+{
+  unsigned int i;
+  uint32_t ref_id;
+
+  assert(report->ip_addr.family == IPADDR_INET4);
+  ref_id = report->ip_addr.addr.in4;
+
+  for (i = 0; i < ARR_GetSize(refclocks); i++) {
+    RCL_Instance inst = get_refclock(i);
+    if (inst->ref_id == ref_id) {
+      report->poll = inst->poll;
+      report->mode = RPT_LOCAL_REFERENCE;
+      break;
+    }
+  }
+}
+
+void
+RCL_SetDriverData(RCL_Instance instance, void *data)
+{
+  instance->data = data;
+}
+
+void *
+RCL_GetDriverData(RCL_Instance instance)
+{
+  return instance->data;
+}
+
+char *
+RCL_GetDriverParameter(RCL_Instance instance)
+{
+  return instance->driver_parameter;
+}
+
+static char *
+get_next_driver_option(RCL_Instance instance, char *option)
+{
+  if (option == NULL)
+    option = instance->driver_parameter;
+
+  option += strlen(option) + 1;
+
+  if (option >= instance->driver_parameter + instance->driver_parameter_length)
+    return NULL;
+
+  return option;
+}
+
+void
+RCL_CheckDriverOptions(RCL_Instance instance, const char **options)
+{
+  char *option;
+  int i, len;
+
+  for (option = get_next_driver_option(instance, NULL);
+       option;
+       option = get_next_driver_option(instance, option)) {
+    for (i = 0; options && options[i]; i++) {
+      len = strlen(options[i]);
+      if (!strncmp(options[i], option, len) &&
+          (option[len] == '=' || option[len] == '\0'))
+        break;
+    }
+
+    if (!options || !options[i])
+      LOG_FATAL("Invalid refclock driver option %s", option);
+  }
+}
+
+char *
+RCL_GetDriverOption(RCL_Instance instance, char *name)
+{
+  char *option;
+  int len;
+
+  len = strlen(name);
+
+  for (option = get_next_driver_option(instance, NULL);
+       option;
+       option = get_next_driver_option(instance, option)) {
+    if (!strncmp(name, option, len)) {
+      if (option[len] == '=')
+        return option + len + 1;
+      if (option[len] == '\0')
+        return option + len;
+    }
+  }
+
+  return NULL;
+}
+
+static int
+convert_tai_offset(struct timespec *sample_time, double *offset)
+{
+  struct timespec tai_ts, utc_ts;
+  int tai_offset;
+
+  /* Get approximate TAI-UTC offset for the reference time in TAI */
+  UTI_AddDoubleToTimespec(sample_time, *offset, &tai_ts);
+  tai_offset = REF_GetTaiOffset(&tai_ts);
+
+  /* Get TAI-UTC offset for the reference time in UTC +/- 1 second */
+  UTI_AddDoubleToTimespec(&tai_ts, -tai_offset, &utc_ts);
+  tai_offset = REF_GetTaiOffset(&utc_ts);
+
+  if (!tai_offset)
+    return 0;
+
+  *offset -= tai_offset;
+
+  return 1;
+}
+
+static int
+accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
+{
+  NTP_Sample sample;
+
+  sample.time = *sample_time;
+  sample.offset = offset;
+  sample.peer_delay = instance->delay;
+  sample.root_delay = instance->delay;
+  sample.peer_dispersion = dispersion;
+  sample.root_dispersion = dispersion;
+
+  return SPF_AccumulateSample(instance->filter, &sample);
+}
+
+int
+RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
+{
+  double correction, dispersion;
+  struct timespec cooked_time;
+
+  if (instance->pps_forced)
+    return RCL_AddPulse(instance, sample_time, -offset);
+
+  LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
+  UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
+  dispersion += instance->precision;
+
+  /* Make sure the timestamp and offset provided by the driver are sane */
+  if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
+      !valid_sample_time(instance, &cooked_time))
+    return 0;
+
+  switch (leap) {
+    case LEAP_Normal:
+    case LEAP_InsertSecond:
+    case LEAP_DeleteSecond:
+      instance->leap_status = leap;
+      break;
+    default:
+      DEBUG_LOG("refclock sample ignored bad leap %d", leap);
+      return 0;
+  }
+
+  if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
+    DEBUG_LOG("refclock sample ignored unknown TAI offset");
+    return 0;
+  }
+
+  if (!accumulate_sample(instance, &cooked_time,
+                         offset - correction + instance->offset, dispersion))
+    return 0;
+
+  instance->pps_active = 0;
+
+  log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
+
+  /* for logging purposes */
+  if (!instance->driver->poll)
+    instance->driver_polled++;
+
+  return 1;
+}
+
+int
+RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
+{
+  double correction, dispersion;
+  struct timespec cooked_time;
+
+  LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
+  UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
+  second += correction;
+
+  if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
+    return 0;
+
+  return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
+}
+
+static int
+check_pulse_edge(RCL_Instance instance, double offset, double distance)
+{
+  double max_error;
+
+  if (instance->pulse_width <= 0.0)
+    return 1;
+
+  max_error = 1.0 / instance->pps_rate - instance->pulse_width;
+  max_error = MIN(instance->pulse_width, max_error);
+  max_error *= 0.5;
+
+  if (fabs(offset) > max_error || distance > max_error) {
+      DEBUG_LOG("refclock pulse ignored offset=%.9f distance=%.9f max_error=%.9f",
+                offset, distance, max_error);
+      return 0;
+  }
+
+  return 1;
+}
+
+int
+RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
+                   double second, double dispersion, double raw_correction)
+{
+  double offset;
+  int rate;
+  NTP_Leap leap;
+
+  if (!UTI_IsTimeOffsetSane(cooked_time, second) ||
+      !valid_sample_time(instance, cooked_time))
+    return 0;
+
+  leap = LEAP_Normal;
+  dispersion += instance->precision;
+  rate = instance->pps_rate;
+
+  offset = -second + instance->offset;
+
+  /* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
+  offset -= (long)(offset * rate) / (double)rate;
+  if (offset < -0.5 / rate)
+    offset += 1.0 / rate;
+  else if (offset >= 0.5 / rate)
+    offset -= 1.0 / rate;
+
+  if (instance->lock_ref != -1) {
+    RCL_Instance lock_refclock;
+    NTP_Sample ref_sample;
+    double sample_diff, shift;
+
+    lock_refclock = get_refclock(instance->lock_ref);
+
+    if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
+      DEBUG_LOG("refclock pulse ignored no ref sample");
+      return 0;
+    }
+
+    ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
+
+    sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
+    if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
+      DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
+          sample_diff);
+      return 0;
+    }
+
+    /* Align the offset to the reference sample */
+    shift = round((ref_sample.offset - offset) * rate) / rate;
+
+    offset += shift;
+
+    if (fabs(ref_sample.offset - offset) +
+        ref_sample.root_dispersion + dispersion > PPS_LOCK_LIMIT / rate) {
+      DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
+                ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
+      return 0;
+    }
+
+    if (!check_pulse_edge(instance, ref_sample.offset - offset, 0.0))
+      return 0;
+
+    leap = lock_refclock->leap_status;
+
+    DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
+              offset, ref_sample.offset - offset, sample_diff);
+  } else {
+    struct timespec ref_time;
+    int is_synchronised, stratum;
+    double root_delay, root_dispersion, distance;
+    uint32_t ref_id;
+
+    /* Ignore the pulse if we are not well synchronized and the local
+       reference is not active */
+
+    REF_GetReferenceParams(cooked_time, &is_synchronised, &leap, &stratum,
+        &ref_id, &ref_time, &root_delay, &root_dispersion);
+    distance = fabs(root_delay) / 2 + root_dispersion;
+
+    if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) {
+      DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
+                offset, leap != LEAP_Unsynchronised, distance);
+      /* Drop also all stored samples */
+      SPF_DropSamples(instance->filter);
+      return 0;
+    }
+
+    if (!check_pulse_edge(instance, offset, distance))
+      return 0;
+  }
+
+  if (!accumulate_sample(instance, cooked_time, offset, dispersion))
+    return 0;
+
+  instance->leap_status = leap;
+  instance->pps_active = 1;
+
+  log_sample(instance, cooked_time, 0, 1, offset + raw_correction - instance->offset,
+             offset, dispersion);
+
+  /* for logging purposes */
+  if (!instance->driver->poll)
+    instance->driver_polled++;
+
+  return 1;
+}
+
+double
+RCL_GetPrecision(RCL_Instance instance)
+{
+  return instance->precision;
+}
+
+int
+RCL_GetDriverPoll(RCL_Instance instance)
+{
+  return instance->driver_poll;
+}
+
+static int
+valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
+{
+  struct timespec now;
+  double diff;
+
+  LCL_ReadCookedTime(&now, NULL);
+  diff = UTI_DiffTimespecsToDouble(&now, sample_time);
+
+  if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) {
+    DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
+              UTI_RefidToString(instance->ref_id),
+              UTI_TimespecToString(sample_time), diff);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+pps_stratum(RCL_Instance instance, struct timespec *ts)
+{
+  struct timespec ref_time;
+  int is_synchronised, stratum;
+  unsigned int i;
+  double root_delay, root_dispersion;
+  NTP_Leap leap;
+  uint32_t ref_id;
+  RCL_Instance refclock;
+
+  REF_GetReferenceParams(ts, &is_synchronised, &leap, &stratum,
+      &ref_id, &ref_time, &root_delay, &root_dispersion);
+
+  /* Don't change our stratum if the local reference is active
+     or this is the current source */
+  if (ref_id == instance->ref_id ||
+      (!is_synchronised && leap != LEAP_Unsynchronised))
+    return stratum - 1;
+
+  /* Or the current source is another PPS refclock */ 
+  for (i = 0; i < ARR_GetSize(refclocks); i++) {
+    refclock = get_refclock(i);
+    if (refclock->ref_id == ref_id &&
+        refclock->pps_active && refclock->lock_ref == -1)
+      return stratum - 1;
+  }
+
+  return 0;
+}
+
+static void
+poll_timeout(void *arg)
+{
+  NTP_Sample sample;
+  int poll, stratum;
+
+  RCL_Instance inst = (RCL_Instance)arg;
+
+  poll = inst->poll;
+
+  if (inst->driver->poll) {
+    poll = inst->driver_poll;
+    inst->driver->poll(inst);
+    inst->driver_polled++;
+  }
+  
+  if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
+    inst->driver_polled = 0;
+
+    if (SPF_GetFilteredSample(inst->filter, &sample)) {
+      /* Handle special case when PPS is used with the local reference */
+      if (inst->pps_active && inst->lock_ref == -1)
+        stratum = pps_stratum(inst, &sample.time);
+      else
+        stratum = inst->stratum;
+
+      SRC_UpdateReachability(inst->source, 1);
+      SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
+      SRC_AccumulateSample(inst->source, &sample);
+      SRC_SelectSource(inst->source);
+
+      log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
+    } else {
+      SRC_UpdateReachability(inst->source, 0);
+    }
+  }
+
+  inst->timeout_id = SCH_AddTimeoutByDelay(UTI_Log2ToDouble(poll), poll_timeout, arg);
+}
+
+static void
+slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
+             double doffset, LCL_ChangeType change_type, void *anything)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(refclocks); i++) {
+    if (change_type == LCL_ChangeUnknownStep)
+      SPF_DropSamples(get_refclock(i)->filter);
+    else
+      SPF_SlewSamples(get_refclock(i)->filter, cooked, dfreq, doffset);
+  }
+}
+
+static void
+add_dispersion(double dispersion, void *anything)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARR_GetSize(refclocks); i++)
+    SPF_AddDispersion(get_refclock(i)->filter, dispersion);
+}
+
+static void
+log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
+{
+  char sync_stats[4] = {'N', '+', '-', '?'};
+
+  if (logfileid == -1)
+    return;
+
+  if (!filtered) {
+    LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e",
+      UTI_TimeToLogForm(sample_time->tv_sec),
+      (int)sample_time->tv_nsec / 1000,
+      UTI_RefidToString(instance->ref_id),
+      instance->driver_polled,
+      sync_stats[instance->leap_status],
+      pulse,
+      raw_offset,
+      cooked_offset,
+      dispersion);
+  } else {
+    LOG_FileWrite(logfileid, "%s.%06d %-5s   - %1c -       -       %13.6e %10.3e",
+      UTI_TimeToLogForm(sample_time->tv_sec),
+      (int)sample_time->tv_nsec / 1000,
+      UTI_RefidToString(instance->ref_id),
+      sync_stats[instance->leap_status],
+      cooked_offset,
+      dispersion);
+  }
+}
diff --git a/chrony_3_3/refclock.h b/chrony/refclock.h
similarity index 96%
rename from chrony_3_3/refclock.h
rename to chrony/refclock.h
index 724f620..69a0152 100644
--- a/chrony_3_3/refclock.h
+++ b/chrony/refclock.h
@@ -72,6 +72,7 @@
 extern void RCL_SetDriverData(RCL_Instance instance, void *data);
 extern void *RCL_GetDriverData(RCL_Instance instance);
 extern char *RCL_GetDriverParameter(RCL_Instance instance);
+extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
 extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
 extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
 extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
diff --git a/chrony_3_3/refclock_phc.c b/chrony/refclock_phc.c
similarity index 94%
rename from chrony_3_3/refclock_phc.c
rename to chrony/refclock_phc.c
index 6aa5edd..e516b86 100644
--- a/chrony_3_3/refclock_phc.c
+++ b/chrony/refclock_phc.c
@@ -56,17 +56,18 @@
 
 static int phc_initialise(RCL_Instance instance)
 {
+  const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
   struct phc_instance *phc;
   int phc_fd, rising_edge;
   char *path, *s;
 
+  RCL_CheckDriverOptions(instance, options);
+
   path = RCL_GetDriverParameter(instance);
  
   phc_fd = SYS_Linux_OpenPHC(path, 0);
-  if (phc_fd < 0) {
+  if (phc_fd < 0)
     LOG_FATAL("Could not open PHC");
-    return 0;
-  }
 
   phc = MallocNew(struct phc_instance);
   phc->fd = phc_fd;
@@ -80,7 +81,7 @@
     s = RCL_GetDriverOption(instance, "channel");
     phc->channel = s ? atoi(s) : 0;
     rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
-    phc->clock = HCL_CreateInstance(UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
+    phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
 
     if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
                                          rising_edge, !rising_edge, 1))
diff --git a/chrony_3_3/refclock_pps.c b/chrony/refclock_pps.c
similarity index 80%
rename from chrony_3_3/refclock_pps.c
rename to chrony/refclock_pps.c
index 6f0565e..880c13f 100644
--- a/chrony_3_3/refclock_pps.c
+++ b/chrony/refclock_pps.c
@@ -48,59 +48,49 @@
 };
 
 static int pps_initialise(RCL_Instance instance) {
+  const char *options[] = {"clear", NULL};
   pps_handle_t handle;
   pps_params_t params;
   struct pps_instance *pps;
   int fd, edge_clear, mode;
   char *path;
 
+  RCL_CheckDriverOptions(instance, options);
+
   path = RCL_GetDriverParameter(instance);
   edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0;
 
   fd = open(path, O_RDWR);
-  if (fd < 0) {
-    LOG_FATAL("open() failed on %s", path);
-    return 0;
-  }
+  if (fd < 0)
+    LOG_FATAL("Could not open %s : %s", path, strerror(errno));
 
   UTI_FdSetCloexec(fd);
 
-  if (time_pps_create(fd, &handle) < 0) {
-    LOG_FATAL("time_pps_create() failed on %s", path);
-    return 0;
-  }
+  if (time_pps_create(fd, &handle) < 0)
+    LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
 
-  if (time_pps_getcap(handle, &mode) < 0) {
-    LOG_FATAL("time_pps_getcap() failed on %s", path);
-    return 0;
-  }
+  if (time_pps_getcap(handle, &mode) < 0)
+    LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
 
-  if (time_pps_getparams(handle, &params) < 0) {
-    LOG_FATAL("time_pps_getparams() failed on %s", path);
-    return 0;
-  }
+  if (time_pps_getparams(handle, &params) < 0)
+    LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
 
   if (!edge_clear) {
-    if (!(mode & PPS_CAPTUREASSERT)) {
+    if (!(mode & PPS_CAPTUREASSERT))
       LOG_FATAL("CAPTUREASSERT not supported on %s", path);
-      return 0;
-    }
+
     params.mode |= PPS_CAPTUREASSERT;
     params.mode &= ~PPS_CAPTURECLEAR;
   } else {
-    if (!(mode & PPS_CAPTURECLEAR)) {
+    if (!(mode & PPS_CAPTURECLEAR))
       LOG_FATAL("CAPTURECLEAR not supported on %s", path);
-      return 0;
-    }
+
     params.mode |= PPS_CAPTURECLEAR;
     params.mode &= ~PPS_CAPTUREASSERT;
   }
 
-  if (time_pps_setparams(handle, &params) < 0) {
-    LOG_FATAL("time_pps_setparams() failed on %s", path);
-    return 0;
-  }
-
+  if (time_pps_setparams(handle, &params) < 0)
+    LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
 
   pps = MallocNew(struct pps_instance);
   pps->handle = handle;
@@ -147,7 +137,7 @@
 
   if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
     DEBUG_LOG("PPS sample ignored seq=%lu ts=%s",
-              seq, UTI_TimespecToString(&ts));
+              (unsigned long)seq, UTI_TimespecToString(&ts));
     return 0;
   }
 
diff --git a/chrony_3_3/refclock_shm.c b/chrony/refclock_shm.c
similarity index 94%
rename from chrony_3_3/refclock_shm.c
rename to chrony/refclock_shm.c
index 7cced1d..ed68095 100644
--- a/chrony_3_3/refclock_shm.c
+++ b/chrony/refclock_shm.c
@@ -59,23 +59,26 @@
 };
 
 static int shm_initialise(RCL_Instance instance) {
+  const char *options[] = {"perm", NULL};
   int id, param, perm;
   char *s;
   struct shmTime *shm;
 
+  RCL_CheckDriverOptions(instance, options);
+
   param = atoi(RCL_GetDriverParameter(instance));
   s = RCL_GetDriverOption(instance, "perm");
   perm = s ? strtol(s, NULL, 8) & 0777 : 0600;
 
   id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
   if (id == -1) {
-    LOG_FATAL("shmget() failed");
+    LOG_FATAL("shmget() failed : %s", strerror(errno));
     return 0;
   }
    
   shm = (struct shmTime *)shmat(id, 0, 0);
   if ((long)shm == -1) {
-    LOG_FATAL("shmat() failed");
+    LOG_FATAL("shmat() failed : %s", strerror(errno));
     return 0;
   }
 
diff --git a/chrony_3_3/refclock_sock.c b/chrony/refclock_sock.c
similarity index 78%
rename from chrony_3_3/refclock_sock.c
rename to chrony/refclock_sock.c
index eb96147..86143cf 100644
--- a/chrony_3_3/refclock_sock.c
+++ b/chrony/refclock_sock.c
@@ -33,6 +33,7 @@
 #include "logging.h"
 #include "util.h"
 #include "sched.h"
+#include "socket.h"
 
 #define SOCK_MAGIC 0x534f434b
 
@@ -69,20 +70,19 @@
   s = recv(sockfd, &sample, sizeof (sample), 0);
 
   if (s < 0) {
-    LOG(LOGS_ERR, "Could not read SOCK sample : %s",
-        strerror(errno));
+    DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
     return;
   }
 
   if (s != sizeof (sample)) {
-    LOG(LOGS_WARN, "Unexpected length of SOCK sample : %d != %ld",
-        s, (long)sizeof (sample));
+    DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
+              s, (long)sizeof (sample));
     return;
   }
 
   if (sample.magic != SOCK_MAGIC) {
-    LOG(LOGS_WARN, "Unexpected magic number in SOCK sample : %x != %x",
-        sample.magic, SOCK_MAGIC);
+    DEBUG_LOG("Unexpected magic number in SOCK sample : %x != %x",
+              (unsigned int)sample.magic, (unsigned int)SOCK_MAGIC);
     return;
   }
 
@@ -98,31 +98,16 @@
 
 static int sock_initialise(RCL_Instance instance)
 {
-  struct sockaddr_un s;
   int sockfd;
   char *path;
 
+  RCL_CheckDriverOptions(instance, NULL);
+
   path = RCL_GetDriverParameter(instance);
  
-  s.sun_family = AF_UNIX;
-  if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
-    LOG_FATAL("path %s is too long", path);
-    return 0;
-  }
-
-  sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
-  if (sockfd < 0) {
-    LOG_FATAL("socket() failed");
-    return 0;
-  }
-
-  UTI_FdSetCloexec(sockfd);
-
-  unlink(path);
-  if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
-    LOG_FATAL("bind() failed");
-    return 0;
-  }
+  sockfd = SCK_OpenUnixDatagramSocket(NULL, path, 0);
+  if (sockfd < 0)
+    LOG_FATAL("Could not open socket %s", path);
 
   RCL_SetDriverData(instance, (void *)(long)sockfd);
   SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance);
@@ -135,7 +120,8 @@
 
   sockfd = (long)RCL_GetDriverData(instance);
   SCH_RemoveFileHandler(sockfd);
-  close(sockfd);
+  SCK_RemoveSocket(sockfd);
+  SCK_CloseSocket(sockfd);
 }
 
 RefclockDriver RCL_SOCK_driver = {
diff --git a/chrony_3_3/reference.c b/chrony/reference.c
similarity index 80%
rename from chrony_3_3/reference.c
rename to chrony/reference.c
index dff7ab9..9032323 100644
--- a/chrony_3_3/reference.c
+++ b/chrony/reference.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2009-2017
+ * Copyright (C) Miroslav Lichvar  2009-2018, 2020
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -42,11 +42,18 @@
 /* The minimum allowed skew */
 #define MIN_SKEW 1.0e-12
 
+/* The update interval of the reference in the local reference mode */
+#define LOCAL_REF_UPDATE_INTERVAL 64.0
+
+/* Interval between updates of the drift file */
+#define MAX_DRIFTFILE_AGE 3600.0
+
 static int are_we_synchronised;
 static int enable_local_stratum;
 static int local_stratum;
 static int local_orphan;
 static double local_distance;
+static struct timespec local_ref_time;
 static NTP_Leap our_leap_status;
 static int our_leap_sec;
 static int our_tai_offset;
@@ -58,6 +65,8 @@
 static double our_residual_freq;
 static double our_root_delay;
 static double our_root_dispersion;
+static double our_offset_sd;
+static double our_frequency_sd;
 
 static double max_update_skew;
 
@@ -103,6 +112,9 @@
 /* Leap second handling mode */
 static REF_LeapMode leap_mode;
 
+/* Time of UTC midnight of the upcoming or previous leap second */
+static time_t leap_when;
+
 /* Flag indicating the clock was recently corrected for leap second and it may
    not have correct time yet (missing 23:59:60 in the UTC time scale) */
 static int leap_in_progress;
@@ -134,8 +146,8 @@
 static int next_fb_drift;
 static SCH_TimeoutID fb_drift_timeout_id;
 
-/* Timestamp of last reference update */
-static struct timespec last_ref_update;
+/* Monotonic timestamp of the last reference update */
+static double last_ref_update;
 static double last_ref_update_interval;
 
 /* ================================================== */
@@ -160,9 +172,8 @@
     UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
 
   if (change_type == LCL_ChangeUnknownStep) {
-    UTI_ZeroTimespec(&last_ref_update);
-  } else if (last_ref_update.tv_sec) {
-    UTI_AdjustTimespec(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
+    last_ref_update = 0.0;
+    REF_SetUnsynchronised();
   }
 
   /* When the clock was stepped, check if that doesn't change our leap status
@@ -194,12 +205,14 @@
   our_frequency_ppm = 0.0;
   our_skew = 1.0; /* i.e. rather bad */
   our_residual_freq = 0.0;
+  our_frequency_sd = 0.0;
+  our_offset_sd = 0.0;
   drift_file_age = 0.0;
 
   /* Now see if we can get the drift file opened */
   drift_file = CNF_GetDriftFile();
   if (drift_file) {
-    in = fopen(drift_file, "r");
+    in = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0);
     if (in) {
       if (fscanf(in, "%lf%lf", &file_freq_ppm, &file_skew_ppm) == 2) {
         /* We have read valid data */
@@ -234,7 +247,9 @@
   correction_time_ratio = CNF_GetCorrectionTimeRatio();
 
   enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
+  UTI_ZeroTimespec(&local_ref_time);
 
+  leap_when = 0;
   leap_timeout_id = 0;
   leap_in_progress = 0;
   leap_mode = CNF_GetLeapSecMode();
@@ -269,7 +284,7 @@
   }
 
   UTI_ZeroTimespec(&our_ref_time);
-  UTI_ZeroTimespec(&last_ref_update);
+  last_ref_update = 0.0;
   last_ref_update_interval = 0.0;
 
   LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -289,6 +304,8 @@
     update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
   }
 
+  LCL_RemoveParameterChangeHandler(handle_slew, NULL);
+
   Free(fb_drifts);
 
   initialised = 0;
@@ -326,83 +343,25 @@
 }
 
 /* ================================================== */
-
-static double
-Sqr(double x)
-{
-  return x*x;
-}
-
-/* ================================================== */
-#if 0
-static double
-Cube(double x)
-{
-  return x*x*x;
-}
-#endif
-
-/* ================================================== */
 /* Update the drift coefficients to the file. */
 
 static void
 update_drift_file(double freq_ppm, double skew)
 {
-  struct stat buf;
-  char *temp_drift_file;
   FILE *out;
-  int r1, r2;
 
   /* Create a temporary file with a '.tmp' extension. */
-
-  temp_drift_file = (char*) Malloc(strlen(drift_file)+8);
-
-  if(!temp_drift_file) {
+  out = UTI_OpenFile(NULL, drift_file, ".tmp", 'w', 0644);
+  if (!out)
     return;
-  }
-
-  strcpy(temp_drift_file,drift_file);
-  strcat(temp_drift_file,".tmp");
-
-  out = fopen(temp_drift_file, "w");
-  if (!out) {
-    Free(temp_drift_file);
-    LOG(LOGS_WARN, "Could not open temporary driftfile %s.tmp for writing",
-        drift_file);
-    return;
-  }
 
   /* Write the frequency and skew parameters in ppm */
-  r1 = fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew);
-  r2 = fclose(out);
-  if (r1 < 0 || r2) {
-    Free(temp_drift_file);
-    LOG(LOGS_WARN, "Could not write to temporary driftfile %s.tmp",
-        drift_file);
-    return;
-  }
+  fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew);
+  fclose(out);
 
-  /* Clone the file attributes from the existing file if there is one. */
-
-  if (!stat(drift_file,&buf)) {
-    if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
-        chmod(temp_drift_file,buf.st_mode & 0777)) {
-      LOG(LOGS_WARN, "Could not change ownership or permissions of temporary driftfile %s.tmp",
-          drift_file);
-    }
-  }
-
-  /* Rename the temporary file to the correct location (see rename(2) for details). */
-
-  if (rename(temp_drift_file,drift_file)) {
-    unlink(temp_drift_file);
-    Free(temp_drift_file);
-    LOG(LOGS_WARN, "Could not replace old driftfile %s with new one %s.tmp",
-        drift_file,drift_file);
-    return;
-  }
-
-  Free(temp_drift_file);
+  /* Rename the temporary file to the correct location */
+  if (!UTI_RenameTempFile(NULL, drift_file, ".tmp", NULL))
+    ;
 }
 
 /* ================================================== */
@@ -468,16 +427,16 @@
 /* ================================================== */
 
 static void
-schedule_fb_drift(struct timespec *now)
+schedule_fb_drift(void)
 {
   int i, c, secs;
-  double unsynchronised;
-  struct timespec when;
+  double unsynchronised, now;
 
   if (fb_drift_timeout_id)
     return; /* already scheduled */
 
-  unsynchronised = UTI_DiffTimespecsToDouble(now, &last_ref_update);
+  now = SCH_GetLastEventMonoTime();
+  unsynchronised = now - last_ref_update;
 
   for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) {
     secs = 1 << i;
@@ -499,8 +458,7 @@
 
   if (i <= fb_drift_max) {
     next_fb_drift = i;
-    UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
-    fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
+    fb_drift_timeout_id = SCH_AddTimeoutByDelay(secs - unsynchronised, fb_drift_timeout, NULL);
     DEBUG_LOG("Fallback drift %d scheduled", i);
   }
 }
@@ -528,28 +486,35 @@
   double abs_offset;
   FILE *p;
   char buffer[BUFLEN], host[BUFLEN];
-  struct tm stm;
+  struct tm *tm;
 
   abs_offset = fabs(offset);
 
   if (abs_offset > log_change_threshold) {
-    LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
-        -offset);
+    LOG(LOGS_WARN, "System clock wrong by %.6f seconds", -offset);
   }
 
   if (do_mail_change &&
       (abs_offset > mail_change_threshold)) {
-    snprintf(buffer, sizeof(buffer), "%s %." S_MAX_USER_LEN "s", MAIL_PROGRAM, mail_change_user);
+    snprintf(buffer, sizeof (buffer), "%s -t", MAIL_PROGRAM);
     p = popen(buffer, "w");
     if (p) {
       if (gethostname(host, sizeof(host)) < 0) {
         strcpy(host, "<UNKNOWN>");
       }
+      host[sizeof (host) - 1] = '\0';
+
+      fprintf(p, "To: %s\n", mail_change_user);
       fprintf(p, "Subject: chronyd reports change to system clock on node [%s]\n", host);
       fputs("\n", p);
-      stm = *localtime(&now);
-      strftime(buffer, sizeof(buffer), "On %A, %d %B %Y\n  with the system clock reading %H:%M:%S (%Z)", &stm);
-      fputs(buffer, p);
+
+      tm = localtime(&now);
+      if (tm) {
+        strftime(buffer, sizeof (buffer),
+                 "On %A, %d %B %Y\n  with the system clock reading %H:%M:%S (%Z)", tm);
+        fputs(buffer, p);
+      }
+
       /* If offset < 0 the local clock is slow, so we are applying a
          positive change to it to bring it into line, hence the
          negation of 'offset' in the next statement (and earlier) */
@@ -592,8 +557,7 @@
     return 1;
   }
 
-  offset = fabs(offset);
-  if (offset > max_offset) {
+  if (fabs(offset) > max_offset) {
     LOG(LOGS_WARN, 
         "Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
         -offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
@@ -760,10 +724,12 @@
   if (!our_leap_sec)
     return;
 
+  leap_when = (now / (24 * 3600) + 1) * (24 * 3600);
+
   /* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC.  If the clock
      will be corrected by the system, timeout slightly sooner to be sure it
      will happen before the system correction. */
-  when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
+  when.tv_sec = leap_when;
   when.tv_nsec = 0;
   if (our_leap_sec < 0)
     when.tv_sec--;
@@ -807,7 +773,7 @@
   }
   
   if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
-      && !REF_IsLeapSecondClose()) {
+      && !REF_IsLeapSecondClose(NULL, 0.0)) {
     our_leap_sec = leap_sec;
     our_tai_offset = tai_offset;
 
@@ -847,6 +813,20 @@
 /* ================================================== */
 
 static void
+update_sync_status(struct timespec *now)
+{
+  double elapsed;
+
+  elapsed = fabs(UTI_DiffTimespecsToDouble(now, &our_ref_time));
+
+  LCL_SetSyncStatus(are_we_synchronised,
+                    our_offset_sd + elapsed * our_frequency_sd,
+                    our_root_delay / 2.0 + get_root_dispersion(now));
+}
+
+/* ================================================== */
+
+static void
 write_log(struct timespec *now, int combined_sources, double freq,
           double offset, double offset_sd, double uncorrected_offset,
           double orig_root_distance)
@@ -929,34 +909,69 @@
 
 /* ================================================== */
 
-void
-REF_SetReference(int stratum,
-                 NTP_Leap leap,
-                 int combined_sources,
-                 uint32_t ref_id,
-                 IPAddr *ref_ip,
-                 struct timespec *ref_time,
-                 double offset,
-                 double offset_sd,
-                 double frequency,
-                 double skew,
-                 double root_delay,
-                 double root_dispersion
-                 )
+static void
+get_clock_estimates(int manual,
+                    double measured_freq, double measured_skew,
+                    double *estimated_freq, double *estimated_skew,
+                    double *residual_freq)
 {
-  double previous_skew, new_skew;
-  double previous_freq, new_freq;
-  double old_weight, new_weight, sum_weight;
-  double delta_freq1, delta_freq2;
-  double skew1, skew2;
-  double our_offset;
-  double our_frequency;
-  double abs_freq_ppm;
-  double update_interval;
-  double elapsed, correction_rate, orig_root_distance;
+  double gain, expected_freq, expected_skew, extra_skew;
+
+  /* We assume that the local clock is running according to our previously
+     determined value */
+  expected_freq = 0.0;
+  expected_skew = our_skew;
+
+  /* Set new frequency based on weighted average of the expected and measured
+     skew.  Disable updates that are based on totally unreliable frequency
+     information unless it is a manual reference. */
+  if (manual) {
+    gain = 1.0;
+  } else if (fabs(measured_skew) > max_update_skew) {
+    DEBUG_LOG("Skew %f too large to track", measured_skew);
+    gain = 0.0;
+  } else {
+    gain = 3.0 * SQUARE(expected_skew) /
+           (3.0 * SQUARE(expected_skew) + SQUARE(measured_skew));
+  }
+
+  gain = CLAMP(0.0, gain, 1.0);
+
+  *estimated_freq = expected_freq + gain * (measured_freq - expected_freq);
+  *residual_freq = measured_freq - *estimated_freq;
+
+  extra_skew = sqrt(SQUARE(expected_freq - *estimated_freq) * (1.0 - gain) +
+                    SQUARE(measured_freq - *estimated_freq) * gain);
+
+  *estimated_skew = expected_skew + gain * (measured_skew - expected_skew) + extra_skew;
+}
+
+/* ================================================== */
+
+static void
+fuzz_ref_time(struct timespec *ts)
+{
+  uint32_t rnd;
+
+  /* Add a random value from interval [-1.0, 0.0] */
+  UTI_GetRandomBytes(&rnd, sizeof (rnd));
+  UTI_AddDoubleToTimespec(ts, -(double)rnd / (uint32_t)-1, ts);
+}
+
+/* ================================================== */
+
+void
+REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
+                 uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
+                 double offset, double offset_sd,
+                 double frequency, double frequency_sd, double skew,
+                 double root_delay, double root_dispersion)
+{
   double uncorrected_offset, accumulate_offset, step_offset;
+  double residual_frequency, local_abs_frequency;
+  double elapsed, mono_now, update_interval, correction_rate, orig_root_distance;
   struct timespec now, raw_now;
-  NTP_int64 ref_fuzz;
+  int manual;
 
   assert(initialised);
 
@@ -966,23 +981,32 @@
     return;
   }
 
-  /* Guard against dividing by zero and NaN */
-  if (!(skew > MIN_SKEW))
-    skew = MIN_SKEW;
+  manual = leap == LEAP_Unsynchronised;
 
+  mono_now = SCH_GetLastEventMonoTime();
   LCL_ReadRawTime(&raw_now);
   LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
   UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
 
   elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
-  our_offset = offset + elapsed * frequency;
+  offset += elapsed * frequency;
 
-  if (!is_offset_ok(our_offset))
+  if (last_ref_update != 0.0) {
+    update_interval = mono_now - last_ref_update;
+  } else {
+    update_interval = 0.0;
+  }
+
+  /* Get new estimates of the frequency and skew including the new data */
+  get_clock_estimates(manual, frequency, skew,
+                      &frequency, &skew, &residual_frequency);
+
+  if (!is_offset_ok(offset))
     return;
 
   orig_root_distance = our_root_delay / 2.0 + get_root_dispersion(&now);
 
-  are_we_synchronised = leap != LEAP_Unsynchronised ? 1 : 0;
+  are_we_synchronised = leap != LEAP_Unsynchronised;
   our_stratum = stratum + 1;
   our_ref_id = ref_id;
   if (ref_ip)
@@ -990,17 +1014,15 @@
   else
     our_ref_ip.family = IPADDR_UNSPEC;
   our_ref_time = *ref_time;
+  our_skew = skew;
+  our_residual_freq = residual_frequency;
   our_root_delay = root_delay;
   our_root_dispersion = root_dispersion;
-
-  if (last_ref_update.tv_sec) {
-    update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
-    if (update_interval < 0.0)
-      update_interval = 0.0;
-  } else {
-    update_interval = 0.0;
-  }
-  last_ref_update = now;
+  our_frequency_sd = frequency_sd;
+  our_offset_sd = offset_sd;
+  last_ref_update = mono_now;
+  last_ref_update_interval = update_interval;
+  last_offset = offset;
 
   /* We want to correct the offset quickly, but we also want to keep the
      frequency error caused by the correction itself low.
@@ -1018,108 +1040,60 @@
   correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
 
   /* Check if the clock should be stepped */
-  if (is_step_limit_reached(our_offset, uncorrected_offset)) {
+  if (is_step_limit_reached(offset, uncorrected_offset)) {
     /* Cancel the uncorrected offset and correct the total offset by step */
     accumulate_offset = uncorrected_offset;
-    step_offset = our_offset - uncorrected_offset;
+    step_offset = offset - uncorrected_offset;
   } else {
-    accumulate_offset = our_offset;
+    accumulate_offset = offset;
     step_offset = 0.0;
   }
 
-  /* Eliminate updates that are based on totally unreliable frequency
-     information. Ignore this limit with manual reference. */
-
-  if (fabs(skew) < max_update_skew || leap == LEAP_Unsynchronised) {
-
-    previous_skew = our_skew;
-    new_skew = skew;
-
-    previous_freq = 0.0; /* We assume that the local clock is running
-                          according to our previously determined
-                          value; note that this is a delta frequency
-                          --- absolute frequencies are only known in
-                          the local module. */
-    new_freq = frequency;
-
-    /* Set new frequency based on weighted average of old and new skew. With
-       manual reference the old frequency has no weight. */
-
-    old_weight = leap != LEAP_Unsynchronised ? 1.0 / Sqr(previous_skew) : 0.0;
-    new_weight = 3.0 / Sqr(new_skew);
-
-    sum_weight = old_weight + new_weight;
-
-    our_frequency = (previous_freq * old_weight + new_freq * new_weight) / sum_weight;
-
-    delta_freq1 = previous_freq - our_frequency;
-    delta_freq2 = new_freq - our_frequency;
-
-    skew1 = sqrt((Sqr(delta_freq1) * old_weight + Sqr(delta_freq2) * new_weight) / sum_weight);
-    skew2 = (previous_skew * old_weight + new_skew * new_weight) / sum_weight;
-    our_skew = skew1 + skew2;
-
-    our_residual_freq = new_freq - our_frequency;
-
-    LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
+  /* Adjust the clock */
+  LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
     
-  } else {
-    DEBUG_LOG("Skew %f too large to track, offset=%f", skew, accumulate_offset);
-
-    LCL_AccumulateOffset(accumulate_offset, correction_rate);
-
-    our_residual_freq = frequency;
-  }
-
-  update_leap_status(leap, raw_now.tv_sec, 0);
-  maybe_log_offset(our_offset, raw_now.tv_sec);
+  maybe_log_offset(offset, raw_now.tv_sec);
 
   if (step_offset != 0.0) {
     if (LCL_ApplyStepOffset(step_offset))
       LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
   }
 
-  LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
+  update_leap_status(leap, raw_now.tv_sec, 0);
+  update_sync_status(&now);
 
   /* Add a random error of up to one second to the reference time to make it
      less useful when disclosed to NTP and cmdmon clients for estimating
      receive timestamps in the interleaved symmetric NTP mode */
-  UTI_GetNtp64Fuzz(&ref_fuzz, 0);
-  UTI_TimespecToNtp64(&our_ref_time, &ref_fuzz, &ref_fuzz);
-  UTI_Ntp64ToTimespec(&ref_fuzz, &our_ref_time);
-  if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
-    our_ref_time.tv_sec--;
+  fuzz_ref_time(&our_ref_time);
 
-  abs_freq_ppm = LCL_ReadAbsoluteFrequency();
+  local_abs_frequency = LCL_ReadAbsoluteFrequency();
 
-  write_log(&now, combined_sources, abs_freq_ppm, our_offset, offset_sd,
-            uncorrected_offset, orig_root_distance);
+  write_log(&now, combined_sources, local_abs_frequency,
+            offset, offset_sd, uncorrected_offset, orig_root_distance);
 
   if (drift_file) {
     /* Update drift file at most once per hour */
     drift_file_age += update_interval;
-    if (drift_file_age < 0.0 || drift_file_age > 3600.0) {
-      update_drift_file(abs_freq_ppm, our_skew);
+    if (drift_file_age >= MAX_DRIFTFILE_AGE) {
+      update_drift_file(local_abs_frequency, our_skew);
       drift_file_age = 0.0;
     }
   }
 
   /* Update fallback drifts */
   if (fb_drifts && are_we_synchronised) {
-    update_fb_drifts(abs_freq_ppm, update_interval);
-    schedule_fb_drift(&now);
+    update_fb_drifts(local_abs_frequency, update_interval);
+    schedule_fb_drift();
   }
 
-  last_ref_update_interval = update_interval;
-  last_offset = our_offset;
-
   /* Update the moving average of squares of offset, quickly on start */
   if (avg2_moving) {
-    avg2_offset += 0.1 * (our_offset * our_offset - avg2_offset);
+    avg2_offset += 0.1 * (SQUARE(offset) - avg2_offset);
   } else {
-    if (avg2_offset > 0.0 && avg2_offset < our_offset * our_offset)
+    if (avg2_offset > 0.0 && avg2_offset < SQUARE(offset))
       avg2_moving = 1;
-    avg2_offset = our_offset * our_offset;
+    avg2_offset = SQUARE(offset);
   }
 }
 
@@ -1138,7 +1112,7 @@
      only supposed to be used with the local source option, really.
      Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */
   REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL,
-                   ref_time, offset, 0.0, frequency, skew, 0.0, 0.0);
+                   ref_time, offset, 0.0, frequency, skew, skew, 0.0, 0.0);
 }
 
 /* ================================================== */
@@ -1163,7 +1137,7 @@
   UTI_AddDoubleToTimespec(&now_raw, uncorrected_offset, &now);
 
   if (fb_drifts) {
-    schedule_fb_drift(&now);
+    schedule_fb_drift();
   }
 
   update_leap_status(LEAP_Unsynchronised, 0, 0);
@@ -1181,6 +1155,25 @@
 /* ================================================== */
 
 void
+REF_UpdateLeapStatus(NTP_Leap leap)
+{
+  struct timespec raw_now, now;
+
+  /* Wait for a full reference update if not already synchronised */
+  if (!are_we_synchronised)
+    return;
+
+  SCH_GetLastEventTime(&now, NULL, &raw_now);
+
+  update_leap_status(leap, raw_now.tv_sec, 0);
+
+  /* Update also the synchronisation status */
+  update_sync_status(&now);
+}
+
+/* ================================================== */
+
+void
 REF_GetReferenceParams
 (
  struct timespec *local_time,
@@ -1193,7 +1186,7 @@
  double *root_dispersion
 )
 {
-  double dispersion;
+  double dispersion, delta;
 
   assert(initialised);
 
@@ -1225,13 +1218,17 @@
 
     *stratum = local_stratum;
     *ref_id = NTP_REFID_LOCAL;
-    /* Make the reference time be now less a second - this will
-       scarcely affect the client, but will ensure that the transmit
-       timestamp cannot come before this (which would cause test 7 to
-       fail in the client's read routine) if the local system clock's
-       read routine is broken in any way. */
-    *ref_time = *local_time;
-    --ref_time->tv_sec;
+
+    /* Keep the reference timestamp up to date.  Adjust the timestamp to make
+       sure that the transmit timestamp cannot come before this (which might
+       fail a test of an NTP client). */
+    delta = UTI_DiffTimespecsToDouble(local_time, &local_ref_time);
+    if (delta > LOCAL_REF_UPDATE_INTERVAL || delta < 1.0) {
+      UTI_AddDoubleToTimespec(local_time, -1.0, &local_ref_time);
+      fuzz_ref_time(&local_ref_time);
+    }
+
+    *ref_time = local_ref_time;
 
     /* Not much else we can do for leap second bits - maybe need to
        have a way for the administrator to feed leap bits in */
@@ -1333,22 +1330,25 @@
 
 #define LEAP_SECOND_CLOSE 5
 
-int REF_IsLeapSecondClose(void)
+static int
+is_leap_close(time_t t)
+{
+  return leap_when != 0 &&
+         t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
+}
+
+/* ================================================== */
+
+int REF_IsLeapSecondClose(struct timespec *ts, double offset)
 {
   struct timespec now, now_raw;
-  time_t t;
-
-  if (!our_leap_sec)
-    return 0;
 
   SCH_GetLastEventTime(&now, NULL, &now_raw);
 
-  t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
-  if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
+  if (is_leap_close(now.tv_sec) || is_leap_close(now_raw.tv_sec))
     return 1;
 
-  t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec;
-  if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
+  if (ts && (is_leap_close(ts->tv_sec) || is_leap_close(ts->tv_sec + offset)))
     return 1;
 
   return 0;
diff --git a/chrony_3_3/reference.h b/chrony/reference.h
similarity index 93%
rename from chrony_3_3/reference.h
rename to chrony/reference.h
index 6ee4953..09400d4 100644
--- a/chrony_3_3/reference.h
+++ b/chrony/reference.h
@@ -144,6 +144,7 @@
  double offset,
  double offset_sd,
  double frequency,
+ double frequency_sd,
  double skew,
  double root_delay,
  double root_dispersion
@@ -161,6 +162,9 @@
 extern void
 REF_SetUnsynchronised(void);
 
+/* Announce a leap second before the full reference update */
+extern void REF_UpdateLeapStatus(NTP_Leap leap);
+
 /* Return the current stratum of this host or 16 if the host is not
    synchronised */
 extern int REF_GetOurStratum(void);
@@ -180,9 +184,9 @@
 extern void REF_EnableLocal(int stratum, double distance, int orphan);
 extern void REF_DisableLocal(void);
 
-/* Check if current raw or cooked time is close to a leap second
-   and is better to discard any measurements */
-extern int REF_IsLeapSecondClose(void);
+/* Check if either of the current raw and cooked time, and optionally a
+   provided timestamp with an offset, is close to a leap second */
+extern int REF_IsLeapSecondClose(struct timespec *ts, double offset);
 
 /* Return TAI-UTC offset corresponding to a time in UTC if available */
 extern int REF_GetTaiOffset(struct timespec *ts);
diff --git a/chrony_3_3/regress.c b/chrony/regress.c
similarity index 100%
rename from chrony_3_3/regress.c
rename to chrony/regress.c
diff --git a/chrony_3_3/regress.h b/chrony/regress.h
similarity index 100%
rename from chrony_3_3/regress.h
rename to chrony/regress.h
diff --git a/chrony_3_3/reports.h b/chrony/reports.h
similarity index 80%
rename from chrony_3_3/reports.h
rename to chrony/reports.h
index 6a24670..c403c23 100644
--- a/chrony_3_3/reports.h
+++ b/chrony/reports.h
@@ -36,8 +36,14 @@
   int stratum;
   int poll;
   enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode;
-  enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_CANDIDATE, RPT_OUTLIER} state;
-  int sel_options;
+  enum {
+    RPT_NONSELECTABLE,
+    RPT_FALSETICKER,
+    RPT_JITTERY,
+    RPT_SELECTABLE,
+    RPT_UNSELECTED,
+    RPT_SELECTED,
+  } state;
 
   int reachability;
   unsigned long latest_meas_ago; /* seconds */
@@ -78,8 +84,8 @@
 
 typedef struct {
   struct timespec ref_time;
-  unsigned short n_samples;
-  unsigned short n_runs;
+  unsigned long n_samples;
+  unsigned long n_runs;
   unsigned long span_seconds;
   double rtc_seconds_fast;
   double rtc_gain_rate_ppm;
@@ -88,22 +94,32 @@
 typedef struct {
   IPAddr ip_addr;
   uint32_t ntp_hits;
+  uint32_t nke_hits;
   uint32_t cmd_hits;
   uint16_t ntp_drops;
+  uint16_t nke_drops;
   uint16_t cmd_drops;
   int8_t ntp_interval;
+  int8_t nke_interval;
   int8_t cmd_interval;
   int8_t ntp_timeout_interval;
   uint32_t last_ntp_hit_ago;
+  uint32_t last_nke_hit_ago;
   uint32_t last_cmd_hit_ago;
 } RPT_ClientAccessByIndex_Report;
 
 typedef struct {
   uint32_t ntp_hits;
+  uint32_t nke_hits;
   uint32_t cmd_hits;
   uint32_t ntp_drops;
+  uint32_t nke_drops;
   uint32_t cmd_drops;
   uint32_t log_drops;
+  uint32_t ntp_auth_hits;
+  uint32_t ntp_interleaved_hits;
+  uint32_t ntp_timestamps;
+  uint32_t ntp_span_seconds;
 } RPT_ServerStatsReport;
 
 typedef struct {
@@ -160,4 +176,30 @@
   uint32_t total_valid_count;
 } RPT_NTPReport;
 
+typedef struct {
+  NTP_AuthMode mode;
+  uint32_t key_id;
+  int key_type;
+  int key_length;
+  int ke_attempts;
+  uint32_t last_ke_ago;
+  int cookies;
+  int cookie_length;
+  int nak;
+} RPT_AuthReport;
+
+typedef struct {
+  uint32_t ref_id;
+  IPAddr ip_addr;
+  char state_char;
+  int authentication;
+  NTP_Leap leap;
+  int conf_options;
+  int eff_options;
+  uint32_t last_sample_ago;
+  double score;
+  double lo_limit;
+  double hi_limit;
+} RPT_SelectReport;
+
 #endif /* GOT_REPORTS_H */
diff --git a/chrony_3_3/rtc.c b/chrony/rtc.c
similarity index 97%
rename from chrony_3_3/rtc.c
rename to chrony/rtc.c
index 33d04bf..d946541 100644
--- a/chrony_3_3/rtc.c
+++ b/chrony/rtc.c
@@ -148,6 +148,8 @@
     if (driver.init) {
       if ((driver.init)()) {
         driver_initialised = 1;
+      } else {
+        LOG(LOGS_ERR, "RTC driver could not be initialised");
       }
     } else {
       LOG(LOGS_ERR, "RTC not supported on this operating system");
@@ -160,7 +162,7 @@
 void
 RTC_Finalise(void)
 {
-  if (driver.fini) {
+  if (driver_initialised) {
     (driver.fini)();
   }
 }
diff --git a/chrony_3_3/rtc.h b/chrony/rtc.h
similarity index 100%
rename from chrony_3_3/rtc.h
rename to chrony/rtc.h
diff --git a/chrony_3_3/rtc_linux.c b/chrony/rtc_linux.c
similarity index 88%
rename from chrony_3_3/rtc_linux.c
rename to chrony/rtc_linux.c
index 89855c2..58b625b 100644
--- a/chrony_3_3/rtc_linux.c
+++ b/chrony/rtc_linux.c
@@ -64,7 +64,7 @@
 
 /* ================================================== */
 
-static int fd = -1;
+static int fd;
 
 #define LOWEST_MEASUREMENT_PERIOD 15
 #define HIGHEST_MEASUREMENT_PERIOD 480
@@ -82,16 +82,12 @@
 #define MAX_SAMPLES 64
 
 /* Real time clock samples.  We store the seconds count as originally
-   measured, together with a 'trim' that compensates these values for
-   any steps made to the RTC to bring it back into line
-   occasionally.  The trim is in seconds. */
+   measured. */
 static time_t *rtc_sec = NULL;
-static double *rtc_trim = NULL;
 
 /* Reference time, against which delta times on the RTC scale are measured */
 static time_t rtc_ref;
 
-
 /* System clock samples associated with the above samples. */
 static struct timespec *system_times = NULL;
 
@@ -145,7 +141,7 @@
 /* ================================================== */
 
 /* Flag to remember whether to assume the RTC is running on UTC */
-static int rtc_on_utc = 1;
+static int rtc_on_utc;
 
 /* ================================================== */
 
@@ -168,7 +164,6 @@
   n_to_save = n_samples - new_first;
 
   memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
-  memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
   memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
 
   n_samples = n_to_save;
@@ -188,21 +183,16 @@
   }
 
   /* Discard all samples if the RTC was stepped back (not our trim) */
-  if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) {
+  if (n_samples > 0 && rtc_sec[n_samples - 1] >= rtc) {
     DEBUG_LOG("RTC samples discarded");
     n_samples = 0;
   }
 
   /* Always use most recent sample as reference */
-  /* use sample only if n_sample is not negative*/
-  if(n_samples >=0)
-  {
   rtc_ref = rtc;
   rtc_sec[n_samples] = rtc;
-  rtc_trim[n_samples] = 0.0;
   system_times[n_samples] = *sys;
   ++n_samples_since_regression;
-  }
   ++n_samples;
 }
 
@@ -227,7 +217,7 @@
   if (n_samples > 0) {
 
     for (i=0; i<n_samples; i++) {
-      rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref);
+      rtc_rel[i] = (double)(rtc_sec[i] - rtc_ref);
       offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
                     (1.0e-9 * system_times[i].tv_nsec) +
                     rtc_rel[i]);
@@ -352,7 +342,7 @@
 
 static time_t
 t_from_rtc(struct tm *stm) {
-  struct tm temp1, temp2;
+  struct tm temp1, temp2, *tm;
   long diff;
   time_t t1, t2;
 
@@ -360,12 +350,14 @@
   temp1.tm_isdst = 0;
   
   t1 = mktime(&temp1);
-  if (rtc_on_utc) {
-    temp2 = *gmtime(&t1);
-  } else {
-    temp2 = *localtime(&t1);
+
+  tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
+  if (!tm) {
+    DEBUG_LOG("gmtime()/localtime() failed");
+    return -1;
   }
-  
+
+  temp2 = *tm;
   temp2.tm_isdst = 0;
   t2 = mktime(&temp2);
   diff = t2 - t1;
@@ -388,12 +380,9 @@
   if (!hwclock_file || !hwclock_file[0])
     return;
 
-  in = fopen(hwclock_file, "r");
-  if (!in) {
-    LOG(LOGS_WARN, "Could not open %s : %s",
-        hwclock_file, strerror(errno));
+  in = UTI_OpenFile(NULL, hwclock_file, NULL, 'r', 0);
+  if (!in)
     return;
-  }
 
   /* Read third line from the file. */
   for (i = 0; i < 3; i++) {
@@ -435,6 +424,7 @@
 static void
 read_coefs_from_file(void)
 {
+  double ref_time;
   FILE *in;
 
   if (!tried_to_load_coefs) {
@@ -443,12 +433,14 @@
 
     tried_to_load_coefs = 1;
 
-    if (coefs_file_name && (in = fopen(coefs_file_name, "r"))) {
-      if (fscanf(in, "%d%ld%lf%lf",
+    if (coefs_file_name &&
+        (in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) {
+      if (fscanf(in, "%d%lf%lf%lf",
                  &valid_coefs_from_file,
-                 &file_ref_time,
+                 &ref_time,
                  &file_ref_offset,
                  &file_rate_ppm) == 4) {
+        file_ref_time = ref_time;
       } else {
         LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
       }
@@ -464,67 +456,40 @@
 static int
 write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
 {
-  struct stat buf;
-  char *temp_coefs_file_name;
   FILE *out;
-  int r1, r2;
 
   /* Create a temporary file with a '.tmp' extension. */
-
-  temp_coefs_file_name = (char*) Malloc(strlen(coefs_file_name)+8);
-
-  if(!temp_coefs_file_name) {
+  out = UTI_OpenFile(NULL, coefs_file_name, ".tmp", 'w', 0644);
+  if (!out)
     return RTC_ST_BADFILE;
-  }
-
-  strcpy(temp_coefs_file_name,coefs_file_name);
-  strcat(temp_coefs_file_name,".tmp");
-
-  out = fopen(temp_coefs_file_name, "w");
-  if (!out) {
-    Free(temp_coefs_file_name);
-    LOG(LOGS_WARN, "Could not open temporary RTC file %s.tmp for writing",
-        coefs_file_name);
-    return RTC_ST_BADFILE;
-  }
 
   /* Gain rate is written out in ppm */
-  r1 = fprintf(out, "%1d %ld %.6f %.3f\n",
-               valid, ref_time, offset, 1.0e6 * rate);
-  r2 = fclose(out);
-  if (r1 < 0 || r2) {
-    Free(temp_coefs_file_name);
-    LOG(LOGS_WARN, "Could not write to temporary RTC file %s.tmp",
-        coefs_file_name);
+  fprintf(out, "%1d %.0f %.6f %.3f\n", valid, (double)ref_time, offset, 1.0e6 * rate);
+  fclose(out);
+
+  /* Rename the temporary file to the correct location */
+  if (!UTI_RenameTempFile(NULL, coefs_file_name, ".tmp", NULL))
     return RTC_ST_BADFILE;
-  }
-
-  /* Clone the file attributes from the existing file if there is one. */
-
-  if (!stat(coefs_file_name,&buf)) {
-    if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) ||
-        chmod(temp_coefs_file_name,buf.st_mode & 0777)) {
-      LOG(LOGS_WARN,
-          "Could not change ownership or permissions of temporary RTC file %s.tmp",
-          coefs_file_name);
-    }
-  }
-
-  /* Rename the temporary file to the correct location (see rename(2) for details). */
-
-  if (rename(temp_coefs_file_name,coefs_file_name)) {
-    unlink(temp_coefs_file_name);
-    Free(temp_coefs_file_name);
-    LOG(LOGS_WARN, "Could not replace old RTC file %s.tmp with new one %s",
-        coefs_file_name, coefs_file_name);
-    return RTC_ST_BADFILE;
-  }
-
-  Free(temp_coefs_file_name);
 
   return RTC_ST_OK;
 }
 
+/* ================================================== */
+
+static int
+switch_interrupts(int on_off)
+{
+  if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
+    LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
+        on_off ? "enable" : "disable", strerror(errno));
+    return 0;
+  }
+
+  if (on_off)
+    skip_interrupts = 1;
+
+  return 1;
+}
 
 /* ================================================== */
 /* file_name is the name of the file where we save the RTC params
@@ -534,8 +499,24 @@
 int
 RTC_Linux_Initialise(void)
 {
+  /* Try to open the device */
+  fd = open(CNF_GetRtcDevice(), O_RDWR);
+  if (fd < 0) {
+    LOG(LOGS_ERR, "Could not open RTC device %s : %s",
+        CNF_GetRtcDevice(), strerror(errno));
+    return 0;
+  }
+
+  /* Make sure the RTC supports interrupts */
+  if (!switch_interrupts(1) || !switch_interrupts(0)) {
+    close(fd);
+    return 0;
+  }
+
+  /* Close on exec */
+  UTI_FdSetCloexec(fd);
+
   rtc_sec = MallocArray(time_t, MAX_SAMPLES);
-  rtc_trim = MallocArray(double, MAX_SAMPLES);
   system_times = MallocArray(struct timespec, MAX_SAMPLES);
 
   /* Setup details depending on configuration options */
@@ -544,18 +525,6 @@
   /* In case it didn't get done by pre-init */
   coefs_file_name = CNF_GetRtcFile();
 
-  /* Try to open device */
-
-  fd = open (CNF_GetRtcDevice(), O_RDWR);
-  if (fd < 0) {
-    LOG(LOGS_ERR, "Could not open RTC device %s : %s",
-        CNF_GetRtcDevice(), strerror(errno));
-    return 0;
-  }
-
-  /* Close on exec */
-  UTI_FdSetCloexec(fd);
-
   n_samples = 0;
   n_samples_since_regression = 0;
   n_runs = 0;
@@ -588,43 +557,24 @@
   /* Remove input file handler */
   if (fd >= 0) {
     SCH_RemoveFileHandler(fd);
+    switch_interrupts(0);
     close(fd);
 
     /* Save the RTC data */
     (void) RTC_Linux_WriteParameters();
 
   }
+
+  if (rtc_sec)
+    LCL_RemoveParameterChangeHandler(slew_samples, NULL);
+
   Free(rtc_sec);
-  Free(rtc_trim);
   Free(system_times);
 }
 
 /* ================================================== */
 
 static void
-switch_interrupts(int onoff)
-{
-  int status;
-
-  if (onoff) {
-    status = ioctl(fd, RTC_UIE_ON, 0);
-    if (status < 0) {
-      LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "enable", strerror(errno));
-      return;
-    }
-    skip_interrupts = 1;
-  } else {
-    status = ioctl(fd, RTC_UIE_OFF, 0);
-    if (status < 0) {
-      LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "disable", strerror(errno));
-      return;
-    }
-  }
-}    
-
-/* ================================================== */
-
-static void
 measurement_timeout(void *any)
 {
   timeout_id = 0;
@@ -679,11 +629,7 @@
   run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
 
   n_samples_since_regression = 0;
-
-  /* Set sample number to -1 so the next sample is not used, as it will not yet be corrected for System Trim*/
-
-  n_samples = -1;
-
+  n_samples = 0;
 
   read_coefs_from_file();
 
@@ -1068,8 +1014,7 @@
   report->n_samples = n_samples;
   report->n_runs = n_runs;
   if (n_samples > 1) {
-    report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) +
-                            (long)(rtc_trim[n_samples-1] - rtc_trim[0]));
+    report->span_seconds = rtc_sec[n_samples - 1] - rtc_sec[0];
   } else {
     report->span_seconds = 0;
   }
diff --git a/chrony_3_3/rtc_linux.h b/chrony/rtc_linux.h
similarity index 100%
rename from chrony_3_3/rtc_linux.h
rename to chrony/rtc_linux.h
diff --git a/chrony/samplefilt.c b/chrony/samplefilt.c
new file mode 100644
index 0000000..fe32b5c
--- /dev/null
+++ b/chrony/samplefilt.c
@@ -0,0 +1,451 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2009-2011, 2014, 2016, 2018
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Routines implementing a median sample filter.
+
+  */
+
+#include "config.h"
+
+#include "local.h"
+#include "logging.h"
+#include "memory.h"
+#include "regress.h"
+#include "samplefilt.h"
+#include "util.h"
+
+#define MIN_SAMPLES 1
+#define MAX_SAMPLES 256
+
+struct SPF_Instance_Record {
+  int min_samples;
+  int max_samples;
+  int index;
+  int used;
+  int last;
+  int avg_var_n;
+  double avg_var;
+  double max_var;
+  double combine_ratio;
+  NTP_Sample *samples;
+  int *selected;
+  double *x_data;
+  double *y_data;
+  double *w_data;
+};
+
+/* ================================================== */
+
+SPF_Instance
+SPF_CreateInstance(int min_samples, int max_samples, double max_dispersion, double combine_ratio)
+{
+  SPF_Instance filter;
+
+  filter = MallocNew(struct SPF_Instance_Record);
+
+  min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
+  max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
+  max_samples = MAX(min_samples, max_samples);
+  combine_ratio = CLAMP(0.0, combine_ratio, 1.0);
+
+  filter->min_samples = min_samples;
+  filter->max_samples = max_samples;
+  filter->index = -1;
+  filter->used = 0;
+  filter->last = -1;
+  /* Set the first estimate to the system precision */
+  filter->avg_var_n = 0;
+  filter->avg_var = SQUARE(LCL_GetSysPrecisionAsQuantum());
+  filter->max_var = SQUARE(max_dispersion);
+  filter->combine_ratio = combine_ratio;
+  filter->samples = MallocArray(NTP_Sample, filter->max_samples);
+  filter->selected = MallocArray(int, filter->max_samples);
+  filter->x_data = MallocArray(double, filter->max_samples);
+  filter->y_data = MallocArray(double, filter->max_samples);
+  filter->w_data = MallocArray(double, filter->max_samples);
+
+  return filter;
+}
+
+/* ================================================== */
+
+void
+SPF_DestroyInstance(SPF_Instance filter)
+{
+  Free(filter->samples);
+  Free(filter->selected);
+  Free(filter->x_data);
+  Free(filter->y_data);
+  Free(filter->w_data);
+  Free(filter);
+}
+
+/* ================================================== */
+
+/* Check that samples times are strictly increasing */
+
+static int
+check_sample(SPF_Instance filter, NTP_Sample *sample)
+{
+  if (filter->used <= 0)
+    return 1;
+
+  if (UTI_CompareTimespecs(&filter->samples[filter->last].time, &sample->time) >= 0) {
+    DEBUG_LOG("filter non-increasing sample time %s", UTI_TimespecToString(&sample->time));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample)
+{
+  if (!check_sample(filter, sample))
+      return 0;
+
+  filter->index++;
+  filter->index %= filter->max_samples;
+  filter->last = filter->index;
+  if (filter->used < filter->max_samples)
+    filter->used++;
+
+  filter->samples[filter->index] = *sample;
+
+  DEBUG_LOG("filter sample %d t=%s offset=%.9f peer_disp=%.9f",
+            filter->index, UTI_TimespecToString(&sample->time),
+            sample->offset, sample->peer_dispersion);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample)
+{
+  if (filter->last < 0)
+    return 0;
+
+  *sample = filter->samples[filter->last];
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_GetNumberOfSamples(SPF_Instance filter)
+{
+  return filter->used;
+}
+
+/* ================================================== */
+
+double
+SPF_GetAvgSampleDispersion(SPF_Instance filter)
+{
+  return sqrt(filter->avg_var);
+}
+
+/* ================================================== */
+
+void
+SPF_DropSamples(SPF_Instance filter)
+{
+  filter->index = -1;
+  filter->used = 0;
+}
+
+/* ================================================== */
+
+static const NTP_Sample *tmp_sort_samples;
+
+static int
+compare_samples(const void *a, const void *b)
+{
+  const NTP_Sample *s1, *s2;
+
+  s1 = &tmp_sort_samples[*(int *)a];
+  s2 = &tmp_sort_samples[*(int *)b];
+
+  if (s1->offset < s2->offset)
+    return -1;
+  else if (s1->offset > s2->offset)
+    return 1;
+  return 0;
+}
+
+/* ================================================== */
+
+static int
+select_samples(SPF_Instance filter)
+{
+  int i, j, k, o, from, to, *selected;
+  double min_dispersion;
+
+  if (filter->used < filter->min_samples)
+    return 0;
+
+  selected = filter->selected;
+
+  /* With 4 or more samples, select those that have peer dispersion smaller
+     than 1.5x of the minimum dispersion */
+  if (filter->used > 4) {
+    for (i = 1, min_dispersion = filter->samples[0].peer_dispersion; i < filter->used; i++) {
+      if (min_dispersion > filter->samples[i].peer_dispersion)
+        min_dispersion = filter->samples[i].peer_dispersion;
+    }
+
+    for (i = j = 0; i < filter->used; i++) {
+      if (filter->samples[i].peer_dispersion <= 1.5 * min_dispersion)
+        selected[j++] = i;
+    }
+  } else {
+    j = 0;
+  }
+
+  if (j < 4) {
+    /* Select all samples */
+
+    for (j = 0; j < filter->used; j++)
+      selected[j] = j;
+  }
+
+  /* And sort their indices by offset */
+  tmp_sort_samples = filter->samples;
+  qsort(selected, j, sizeof (int), compare_samples);
+
+  /* Select samples closest to the median */
+  if (j > 2) {
+    from = j * (1.0 - filter->combine_ratio) / 2.0;
+    from = CLAMP(1, from, (j - 1) / 2);
+  } else {
+    from = 0;
+  }
+
+  to = j - from;
+
+  /* Mark unused samples and sort the rest by their time */
+
+  o = filter->used - filter->index - 1;
+
+  for (i = 0; i < from; i++)
+    selected[i] = -1;
+  for (; i < to; i++)
+    selected[i] = (selected[i] + o) % filter->used;
+  for (; i < filter->used; i++)
+    selected[i] = -1;
+
+  for (i = from; i < to; i++) {
+    j = selected[i];
+    selected[i] = -1;
+    while (j != -1 && selected[j] != j) {
+      k = selected[j];
+      selected[j] = j;
+      j = k;
+    }
+  }
+
+  for (i = j = 0; i < filter->used; i++) {
+    if (selected[i] != -1)
+      selected[j++] = (selected[i] + filter->used - o) % filter->used;
+  }
+
+  assert(j > 0 && j <= filter->max_samples);
+
+  return j;
+}
+
+/* ================================================== */
+
+static int
+combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
+{
+  double mean_peer_dispersion, mean_root_dispersion, mean_peer_delay, mean_root_delay;
+  double mean_x, mean_y, disp, var, prev_avg_var;
+  NTP_Sample *sample, *last_sample;
+  int i, dof;
+
+  last_sample = &filter->samples[filter->selected[n - 1]];
+
+  /* Prepare data */
+  for (i = 0; i < n; i++) {
+    sample = &filter->samples[filter->selected[i]];
+
+    filter->x_data[i] = UTI_DiffTimespecsToDouble(&sample->time, &last_sample->time);
+    filter->y_data[i] = sample->offset;
+    filter->w_data[i] = sample->peer_dispersion;
+  }
+
+  /* Calculate mean offset and interval since the last sample */
+  for (i = 0, mean_x = mean_y = 0.0; i < n; i++) {
+    mean_x += filter->x_data[i];
+    mean_y += filter->y_data[i];
+  }
+  mean_x /= n;
+  mean_y /= n;
+
+  if (n >= 4) {
+    double b0, b1, s2, sb0, sb1;
+
+    /* Set y axis to the mean sample time */
+    for (i = 0; i < n; i++)
+      filter->x_data[i] -= mean_x;
+
+    /* Make a linear fit and use the estimated standard deviation of the
+       intercept as dispersion */
+    RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
+                           &b0, &b1, &s2, &sb0, &sb1);
+    var = s2;
+    disp = sb0;
+    dof = n - 2;
+  } else if (n >= 2) {
+    for (i = 0, disp = 0.0; i < n; i++)
+      disp += (filter->y_data[i] - mean_y) * (filter->y_data[i] - mean_y);
+    var = disp / (n - 1);
+    disp = sqrt(var);
+    dof = n - 1;
+  } else {
+    var = filter->avg_var;
+    disp = sqrt(var);
+    dof = 1;
+  }
+
+  /* Avoid working with zero dispersion */
+  if (var < 1e-20) {
+    var = 1e-20;
+    disp = sqrt(var);
+  }
+
+  /* Drop the sample if the variance is larger than the maximum */
+  if (filter->max_var > 0.0 && var > filter->max_var) {
+    DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
+              sqrt(var), sqrt(filter->max_var));
+    return 0;
+  }
+
+  prev_avg_var = filter->avg_var;
+
+  /* Update the exponential moving average of the variance */
+  if (filter->avg_var_n > 50) {
+    filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
+  } else {
+    filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
+      (dof + filter->avg_var_n);
+    if (filter->avg_var_n == 0)
+      prev_avg_var = filter->avg_var;
+    filter->avg_var_n += dof;
+  }
+
+  /* Use the long-term average of variance instead of the estimated value
+     unless it is significantly smaller in order to reduce the noise in
+     sourcestats weights */
+  if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
+    disp = sqrt(filter->avg_var) * disp / sqrt(var);
+
+  mean_peer_dispersion = mean_root_dispersion = mean_peer_delay = mean_root_delay = 0.0;
+
+  for (i = 0; i < n; i++) {
+    sample = &filter->samples[filter->selected[i]];
+
+    mean_peer_dispersion += sample->peer_dispersion;
+    mean_root_dispersion += sample->root_dispersion;
+    mean_peer_delay += sample->peer_delay;
+    mean_root_delay += sample->root_delay;
+  }
+
+  mean_peer_dispersion /= n;
+  mean_root_dispersion /= n;
+  mean_peer_delay /= n;
+  mean_root_delay /= n;
+
+  UTI_AddDoubleToTimespec(&last_sample->time, mean_x, &result->time);
+  result->offset = mean_y;
+  result->peer_dispersion = MAX(disp, mean_peer_dispersion);
+  result->root_dispersion = MAX(disp, mean_root_dispersion);
+  result->peer_delay = mean_peer_delay;
+  result->root_delay = mean_root_delay;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
+{
+  int n;
+
+  n = select_samples(filter);
+
+  if (n < 1)
+    return 0;
+
+  if (!combine_selected_samples(filter, n, sample))
+    return 0;
+
+  SPF_DropSamples(filter);
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double doffset)
+{
+  int i, first, last;
+  double delta_time;
+
+  if (filter->last < 0)
+    return;
+
+  /* Always slew the last sample as it may be returned even if no new
+     samples were accumulated */
+  if (filter->used > 0) {
+    first = 0;
+    last = filter->used - 1;
+  } else {
+    first = last = filter->last;
+  }
+
+  for (i = first; i <= last; i++) {
+    UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
+                       &delta_time, dfreq, doffset);
+    filter->samples[i].offset -= delta_time;
+  }
+}
+
+/* ================================================== */
+
+void
+SPF_AddDispersion(SPF_Instance filter, double dispersion)
+{
+  int i;
+
+  for (i = 0; i < filter->used; i++) {
+    filter->samples[i].peer_dispersion += dispersion;
+    filter->samples[i].root_dispersion += dispersion;
+  }
+}
diff --git a/chrony/samplefilt.h b/chrony/samplefilt.h
new file mode 100644
index 0000000..3b4350a
--- /dev/null
+++ b/chrony/samplefilt.h
@@ -0,0 +1,49 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2018
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for sample filter.
+
+  */
+
+#ifndef GOT_SAMPLEFILT_H
+#define GOT_SAMPLEFILT_H
+
+#include "ntp.h"
+
+typedef struct SPF_Instance_Record *SPF_Instance;
+
+extern SPF_Instance SPF_CreateInstance(int min_samples, int max_samples,
+                                       double max_dispersion, double combine_ratio);
+extern void SPF_DestroyInstance(SPF_Instance filter);
+
+extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
+extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
+extern int SPF_GetNumberOfSamples(SPF_Instance filter);
+extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
+extern void SPF_DropSamples(SPF_Instance filter);
+extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
+extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
+                            double dfreq, double doffset);
+extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
+
+#endif
diff --git a/chrony_3_3/sched.c b/chrony/sched.c
similarity index 89%
rename from chrony_3_3/sched.c
rename to chrony/sched.c
index 45ef11c..1f5ebe4 100644
--- a/chrony_3_3/sched.c
+++ b/chrony/sched.c
@@ -65,6 +65,12 @@
 static struct timespec last_select_ts, last_select_ts_raw;
 static double last_select_ts_err;
 
+#define TS_MONO_PRECISION_NS 10000000U
+
+/* Monotonic low-precision timestamp measuring interval since the start */
+static double last_select_ts_mono;
+static uint32_t last_select_ts_mono_ns;
+
 /* ================================================== */
 
 /* Variables to handler the timer queue */
@@ -105,7 +111,8 @@
 
 /* ================================================== */
 
-static int need_to_exit;
+/* Flag terminating the main loop, which can be set from a signal handler */
+static volatile int need_to_exit;
 
 /* ================================================== */
 
@@ -136,6 +143,8 @@
 
   LCL_ReadRawTime(&last_select_ts_raw);
   last_select_ts = last_select_ts_raw;
+  last_select_ts_mono = 0.0;
+  last_select_ts_mono_ns = 0;
 
   initialised = 1;
 }
@@ -147,6 +156,8 @@
 SCH_Finalise(void) {
   ARR_DestroyInstance(file_handlers);
 
+  LCL_RemoveParameterChangeHandler(handle_slew, NULL);
+
   initialised = 0;
 }
 
@@ -247,6 +258,14 @@
 
 /* ================================================== */
 
+double
+SCH_GetLastEventMonoTime(void)
+{
+  return last_select_ts_mono;
+}
+
+/* ================================================== */
+
 #define TQE_ALLOC_QUANTUM 32
 
 static TimerQueueEntry *
@@ -480,12 +499,15 @@
 
 static void
 dispatch_timeouts(struct timespec *now) {
+  unsigned long n_done, n_entries_on_start;
   TimerQueueEntry *ptr;
   SCH_TimeoutHandler handler;
   SCH_ArbitraryArgument arg;
-  int n_done = 0, n_entries_on_start = n_timer_queue_entries;
 
-  while (1) {
+  n_entries_on_start = n_timer_queue_entries;
+  n_done = 0;
+
+  do {
     LCL_ReadRawTime(now);
 
     if (!(n_timer_queue_entries > 0 &&
@@ -508,16 +530,21 @@
     /* Increment count of timeouts handled */
     ++n_done;
 
-    /* If more timeouts were handled than there were in the timer queue on
-       start and there are now, assume some code is scheduling timeouts with
-       negative delays and abort.  Make the actual limit higher in case the
-       machine is temporarily overloaded and dispatching the handlers takes
-       more time than was delay of a scheduled timeout. */
-    if (n_done > n_timer_queue_entries * 4 &&
-        n_done > n_entries_on_start * 4) {
+    /* If the number of dispatched timeouts is significantly larger than the
+       length of the queue on start and now, assume there is a bug causing
+       an infinite loop by constantly adding a timeout with a zero or negative
+       delay.  Check the actual rate of timeouts to avoid false positives in
+       case the execution slowed down so much (e.g. due to memory thrashing)
+       that it repeatedly takes more time to handle the timeout than is its
+       delay.  This is a safety mechanism intended to stop a full-speed flood
+       of NTP requests due to a bug in the NTP polling. */
+
+    if (n_done > 20 &&
+        n_done > 4 * MAX(n_timer_queue_entries, n_entries_on_start) &&
+        fabs(UTI_DiffTimespecsToDouble(now, &last_select_ts_raw)) / n_done < 0.01)
       LOG_FATAL("Possible infinite loop in scheduling");
-    }
-  }
+
+  } while (!need_to_exit);
 }
 
 /* ================================================== */
@@ -534,7 +561,8 @@
     if (except_fds && FD_ISSET(fd, except_fds)) {
       /* This descriptor has an exception, dispatch its handler */
       ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
-      (ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
+      if (ptr->handler)
+        (ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
       nfd--;
 
       /* Don't try to read from it now */
@@ -547,14 +575,16 @@
     if (read_fds && FD_ISSET(fd, read_fds)) {
       /* This descriptor can be read from, dispatch its handler */
       ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
-      (ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
+      if (ptr->handler)
+        (ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
       nfd--;
     }
 
     if (write_fds && FD_ISSET(fd, write_fds)) {
       /* This descriptor can be written to, dispatch its handler */
       ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
-      (ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
+      if (ptr->handler)
+        (ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
       nfd--;
     }
   }
@@ -703,6 +733,31 @@
 
 /* ================================================== */
 
+static void
+update_monotonic_time(struct timespec *now, struct timespec *before)
+{
+  struct timespec diff;
+
+  /* Avoid frequent floating-point operations and handle small
+     increments to a large value */
+
+  UTI_DiffTimespecs(&diff, now, before);
+  if (diff.tv_sec == 0) {
+    last_select_ts_mono_ns += diff.tv_nsec;
+  } else {
+    last_select_ts_mono += fabs(UTI_TimespecToDouble(&diff) +
+                                last_select_ts_mono_ns / 1.0e9);
+    last_select_ts_mono_ns = 0;
+  }
+
+  if (last_select_ts_mono_ns > TS_MONO_PRECISION_NS) {
+    last_select_ts_mono += last_select_ts_mono_ns / 1.0e9;
+    last_select_ts_mono_ns = 0;
+  }
+}
+
+/* ================================================== */
+
 void
 SCH_MainLoop(void)
 {
@@ -753,6 +808,8 @@
     LCL_ReadRawTime(&now);
     LCL_CookTime(&now, &cooked, &err);
 
+    update_monotonic_time(&now, &last_select_ts_raw);
+
     /* Check if the time didn't jump unexpectedly */
     if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) {
       /* Cook the time again after handling the step */
diff --git a/chrony_3_3/sched.h b/chrony/sched.h
similarity index 96%
rename from chrony_3_3/sched.h
rename to chrony/sched.h
index 5ff53c7..f1f4eb9 100644
--- a/chrony_3_3/sched.h
+++ b/chrony/sched.h
@@ -65,6 +65,9 @@
 /* Get the time stamp taken after a file descriptor became ready or a timeout expired */
 extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);
 
+/* Get a low-precision monotonic timestamp (starting at 0.0) */
+extern double SCH_GetLastEventMonoTime(void);
+
 /* This queues a timeout to elapse at a given (raw) local time */
 extern SCH_TimeoutID SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg);
 
diff --git a/chrony/siv.h b/chrony/siv.h
new file mode 100644
index 0000000..e303d34
--- /dev/null
+++ b/chrony/siv.h
@@ -0,0 +1,70 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Header file for Synthetic Initialization Vector (SIV) ciphers.
+
+  */
+
+#ifndef GOT_SIV_H
+#define GOT_SIV_H
+
+/* Maximum key length of all supported SIVs */
+#define SIV_MAX_KEY_LENGTH 32
+
+/* Maximum difference between lengths of ciphertext and plaintext */
+#define SIV_MAX_TAG_LENGTH 16
+
+/* Identifiers of SIV algorithms following the IANA AEAD registry */
+typedef enum {
+  AEAD_AES_SIV_CMAC_256 = 15,
+  AEAD_AES_SIV_CMAC_384 = 16,
+  AEAD_AES_SIV_CMAC_512 = 17,
+  AEAD_AES_128_GCM_SIV = 30,
+  AEAD_AES_256_GCM_SIV = 31,
+} SIV_Algorithm;
+
+typedef struct SIV_Instance_Record *SIV_Instance;
+
+extern SIV_Instance SIV_CreateInstance(SIV_Algorithm algorithm);
+
+extern void SIV_DestroyInstance(SIV_Instance instance);
+
+extern int SIV_GetKeyLength(SIV_Algorithm algorithm);
+
+extern int SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length);
+
+extern int SIV_GetTagLength(SIV_Instance instance);
+
+extern int SIV_Encrypt(SIV_Instance instance,
+                       const unsigned char *nonce, int nonce_length,
+                       const void *assoc, int assoc_length,
+                       const void *plaintext, int plaintext_length,
+                       unsigned char *ciphertext, int ciphertext_length);
+
+extern int SIV_Decrypt(SIV_Instance instance,
+                       const unsigned char *nonce, int nonce_length,
+                       const void *assoc, int assoc_length,
+                       const unsigned char *ciphertext, int ciphertext_length,
+                       void *plaintext, int plaintext_length);
+
+#endif
diff --git a/chrony/siv_gnutls.c b/chrony/siv_gnutls.c
new file mode 100644
index 0000000..437f715
--- /dev/null
+++ b/chrony/siv_gnutls.c
@@ -0,0 +1,259 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ **********************************************************************
+
+  =======================================================================
+
+  SIV ciphers using the GnuTLS library
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include <gnutls/crypto.h>
+
+#include "logging.h"
+#include "memory.h"
+#include "siv.h"
+
+struct SIV_Instance_Record {
+  gnutls_cipher_algorithm_t algorithm;
+  gnutls_aead_cipher_hd_t cipher;
+};
+
+/* ================================================== */
+
+static int instance_counter = 0;
+static int gnutls_initialised = 0;
+
+/* ================================================== */
+
+static void
+init_gnutls(void)
+{
+  int r;
+
+  if (gnutls_initialised)
+    return;
+
+  r = gnutls_global_init();
+  if (r < 0)
+    LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
+
+  DEBUG_LOG("Initialised");
+  gnutls_initialised = 1;
+}
+
+/* ================================================== */
+
+static void
+deinit_gnutls(void)
+{
+  assert(gnutls_initialised);
+  gnutls_global_deinit();
+  gnutls_initialised = 0;
+  DEBUG_LOG("Deinitialised");
+}
+
+/* ================================================== */
+
+static gnutls_cipher_algorithm_t
+get_cipher_algorithm(SIV_Algorithm algorithm)
+{
+  switch (algorithm) {
+    case AEAD_AES_SIV_CMAC_256:
+      return GNUTLS_CIPHER_AES_128_SIV;
+    default:
+      return 0;
+  }
+}
+
+/* ================================================== */
+
+SIV_Instance
+SIV_CreateInstance(SIV_Algorithm algorithm)
+{
+  gnutls_cipher_algorithm_t calgo;
+  SIV_Instance instance;
+
+  calgo = get_cipher_algorithm(algorithm);
+  if (calgo == 0)
+    return NULL;
+
+  if (instance_counter == 0)
+    init_gnutls();
+
+  /* Check if the cipher is actually supported */
+  if (gnutls_cipher_get_tag_size(calgo) == 0) {
+    if (instance_counter == 0)
+      deinit_gnutls();
+    return NULL;
+  }
+
+  instance = MallocNew(struct SIV_Instance_Record);
+  instance->algorithm = calgo;
+  instance->cipher = NULL;
+
+  instance_counter++;
+
+  return instance;
+}
+
+/* ================================================== */
+
+void
+SIV_DestroyInstance(SIV_Instance instance)
+{
+  if (instance->cipher)
+    gnutls_aead_cipher_deinit(instance->cipher);
+  Free(instance);
+
+  instance_counter--;
+  if (instance_counter == 0)
+    deinit_gnutls();
+}
+
+/* ================================================== */
+
+int
+SIV_GetKeyLength(SIV_Algorithm algorithm)
+{
+  gnutls_cipher_algorithm_t calgo = get_cipher_algorithm(algorithm);
+  int len;
+
+  if (calgo == 0)
+    return 0;
+
+  len = gnutls_cipher_get_key_size(calgo);
+
+  if (len < 1 || len > SIV_MAX_KEY_LENGTH)
+    LOG_FATAL("Invalid key length");
+
+  return len;
+}
+
+/* ================================================== */
+
+int
+SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
+{
+  gnutls_aead_cipher_hd_t cipher;
+  gnutls_datum_t datum;
+  int r;
+
+  if (length <= 0 || length != gnutls_cipher_get_key_size(instance->algorithm))
+    return 0;
+
+  datum.data = (unsigned char *)key;
+  datum.size = length;
+
+  /* Initialise a new cipher with the provided key (gnutls does not seem to
+     have a function to change the key directly) */
+  r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
+  if (r < 0) {
+    DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
+    return 0;
+  }
+
+  /* Replace the previous cipher */
+  if (instance->cipher)
+    gnutls_aead_cipher_deinit(instance->cipher);
+  instance->cipher = cipher;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SIV_GetTagLength(SIV_Instance instance)
+{
+  int len;
+
+  len = gnutls_cipher_get_tag_size(instance->algorithm);
+
+  if (len < 1 || len > SIV_MAX_TAG_LENGTH)
+    LOG_FATAL("Invalid tag length");
+
+  return len;
+}
+
+/* ================================================== */
+
+int
+SIV_Encrypt(SIV_Instance instance,
+            const unsigned char *nonce, int nonce_length,
+            const void *assoc, int assoc_length,
+            const void *plaintext, int plaintext_length,
+            unsigned char *ciphertext, int ciphertext_length)
+{
+  size_t clen = ciphertext_length;
+
+  if (!instance->cipher)
+    return 0;
+
+  if (nonce_length < 1 || assoc_length < 0 ||
+      plaintext_length < 0 || ciphertext_length < 0)
+    return 0;
+
+  assert(assoc && plaintext);
+
+  if (gnutls_aead_cipher_encrypt(instance->cipher,
+                                 nonce, nonce_length, assoc, assoc_length, 0,
+                                 plaintext, plaintext_length, ciphertext, &clen) < 0)
+    return 0;
+
+  if (clen != ciphertext_length)
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SIV_Decrypt(SIV_Instance instance,
+            const unsigned char *nonce, int nonce_length,
+            const void *assoc, int assoc_length,
+            const unsigned char *ciphertext, int ciphertext_length,
+            void *plaintext, int plaintext_length)
+{
+  size_t plen = plaintext_length;
+
+  if (!instance->cipher)
+    return 0;
+
+  if (nonce_length < 1 || assoc_length < 0 ||
+      plaintext_length < 0 || ciphertext_length < 0)
+    return 0;
+
+  assert(assoc && plaintext);
+
+  if (gnutls_aead_cipher_decrypt(instance->cipher,
+                                 nonce, nonce_length, assoc, assoc_length, 0,
+                                 ciphertext, ciphertext_length, plaintext, &plen) < 0)
+    return 0;
+
+  if (plen != plaintext_length)
+    return 0;
+
+  return 1;
+}
diff --git a/chrony/siv_nettle.c b/chrony/siv_nettle.c
new file mode 100644
index 0000000..d8f8b23
--- /dev/null
+++ b/chrony/siv_nettle.c
@@ -0,0 +1,156 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  SIV ciphers using the Nettle library
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#ifdef HAVE_NETTLE_SIV_CMAC
+#include <nettle/siv-cmac.h>
+#else
+#include "siv_nettle_int.c"
+#endif
+
+#include "memory.h"
+#include "siv.h"
+
+struct SIV_Instance_Record {
+  struct siv_cmac_aes128_ctx siv;
+  int key_set;
+};
+
+/* ================================================== */
+
+SIV_Instance
+SIV_CreateInstance(SIV_Algorithm algorithm)
+{
+  SIV_Instance instance;
+
+  if (algorithm != AEAD_AES_SIV_CMAC_256)
+    return NULL;
+
+  instance = MallocNew(struct SIV_Instance_Record);
+  instance->key_set = 0;
+
+  return instance;
+}
+
+/* ================================================== */
+
+void
+SIV_DestroyInstance(SIV_Instance instance)
+{
+  Free(instance);
+}
+
+/* ================================================== */
+
+int
+SIV_GetKeyLength(SIV_Algorithm algorithm)
+{
+  assert(32 <= SIV_MAX_KEY_LENGTH);
+
+  if (algorithm == AEAD_AES_SIV_CMAC_256)
+    return 32;
+  return 0;
+}
+
+/* ================================================== */
+
+int
+SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
+{
+  if (length != 32)
+    return 0;
+
+  siv_cmac_aes128_set_key(&instance->siv, key);
+
+  instance->key_set = 1;
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SIV_GetTagLength(SIV_Instance instance)
+{
+  assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH);
+
+  return SIV_DIGEST_SIZE;
+}
+
+/* ================================================== */
+
+int
+SIV_Encrypt(SIV_Instance instance,
+            const unsigned char *nonce, int nonce_length,
+            const void *assoc, int assoc_length,
+            const void *plaintext, int plaintext_length,
+            unsigned char *ciphertext, int ciphertext_length)
+{
+  if (!instance->key_set)
+    return 0;
+
+  if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
+      plaintext_length < 0 || plaintext_length > ciphertext_length ||
+      plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
+    return 0;
+
+  assert(assoc && plaintext);
+
+  siv_cmac_aes128_encrypt_message(&instance->siv, nonce_length, nonce,
+                                  assoc_length, assoc,
+                                  ciphertext_length, ciphertext, plaintext);
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SIV_Decrypt(SIV_Instance instance,
+            const unsigned char *nonce, int nonce_length,
+            const void *assoc, int assoc_length,
+            const unsigned char *ciphertext, int ciphertext_length,
+            void *plaintext, int plaintext_length)
+{
+  if (!instance->key_set)
+    return 0;
+
+  if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
+      plaintext_length < 0 || plaintext_length > ciphertext_length ||
+      plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
+    return 0;
+
+  assert(assoc && plaintext);
+
+  if (!siv_cmac_aes128_decrypt_message(&instance->siv, nonce_length, nonce,
+                                       assoc_length, assoc,
+                                       plaintext_length, plaintext, ciphertext))
+    return 0;
+
+  return 1;
+}
diff --git a/chrony/siv_nettle_int.c b/chrony/siv_nettle_int.c
new file mode 100644
index 0000000..714eff6
--- /dev/null
+++ b/chrony/siv_nettle_int.c
@@ -0,0 +1,452 @@
+/* This is a single-file implementation of AES-SIV-CMAC-256 based on
+   a patch for GNU Nettle by Nikos Mavrogiannopoulos */
+
+/*
+   AES-CMAC-128 (rfc 4493)
+   Copyright (C) Stefan Metzmacher 2012
+   Copyright (C) Jeremy Allison 2012
+   Copyright (C) Michael Adam 2012
+   Copyright (C) 2017, Red Hat Inc.
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle 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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+/* siv-aes128.c, siv-cmac.c, siv.h
+
+   AES-SIV, RFC5297
+   SIV-CMAC, RFC5297
+
+   Copyright (C) 2017 Nikos Mavrogiannopoulos
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle 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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+/* cmac.h, siv-cmac.h, cmac-aes128.c
+
+   CMAC mode, as specified in RFC4493
+   SIV-CMAC mode, as specified in RFC5297
+   CMAC using AES128 as the underlying cipher.
+
+   Copyright (C) 2017 Red Hat, Inc.
+
+   Contributed by Nikos Mavrogiannopoulos
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle 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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+# include "config.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "nettle/aes.h"
+#include "nettle/ctr.h"
+#include "nettle/macros.h"
+#include "nettle/memxor.h"
+#include "nettle/memops.h"
+
+#include "nettle/nettle-types.h"
+
+/* For SIV, the block size of the block cipher shall be 128 bits. */
+#define SIV_BLOCK_SIZE  16
+#define SIV_DIGEST_SIZE 16
+#define SIV_MIN_NONCE_SIZE 1
+
+/*
+ * SIV mode requires the aad and plaintext when building the IV, which
+ * prevents streaming processing and it incompatible with the AEAD API.
+ */
+
+/* AES_SIV_CMAC_256 */
+struct siv_cmac_aes128_ctx {
+    struct aes128_ctx         cipher;
+    uint8_t s2vk[AES128_KEY_SIZE];
+};
+
+struct cmac128_ctx
+{
+  /* Key */
+  union nettle_block16 K1;
+  union nettle_block16 K2;
+
+  /* MAC state */
+  union nettle_block16 X;
+
+  /* Block buffer */
+  union nettle_block16 block;
+  size_t index;
+};
+
+/* shift one and XOR with 0x87. */
+static void
+_cmac128_block_mulx(union nettle_block16 *dst,
+	   const union nettle_block16 *src)
+{
+  uint64_t b1 = READ_UINT64(src->b);
+  uint64_t b2 = READ_UINT64(src->b+8);
+
+  b1 = (b1 << 1) | (b2 >> 63);
+  b2 <<= 1;
+
+  if (src->b[0] & 0x80)
+    b2 ^= 0x87;
+
+  WRITE_UINT64(dst->b, b1);
+  WRITE_UINT64(dst->b+8, b2);
+}
+
+static void
+cmac128_set_key(struct cmac128_ctx *ctx, const void *cipher,
+		nettle_cipher_func *encrypt)
+{
+  static const uint8_t const_zero[] = {
+    0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00
+  };
+  union nettle_block16 *L = &ctx->block;
+  memset(ctx, 0, sizeof(*ctx));
+
+  /* step 1 - generate subkeys k1 and k2 */
+  encrypt(cipher, 16, L->b, const_zero);
+
+  _cmac128_block_mulx(&ctx->K1, L);
+  _cmac128_block_mulx(&ctx->K2, &ctx->K1);
+}
+
+#define MIN(x,y) ((x)<(y)?(x):(y))
+
+static void
+cmac128_update(struct cmac128_ctx *ctx, const void *cipher,
+	       nettle_cipher_func *encrypt,
+	       size_t msg_len, const uint8_t *msg)
+{
+  union nettle_block16 Y;
+  /*
+   * check if we expand the block
+   */
+  if (ctx->index < 16)
+    {
+      size_t len = MIN(16 - ctx->index, msg_len);
+      memcpy(&ctx->block.b[ctx->index], msg, len);
+      msg += len;
+      msg_len -= len;
+      ctx->index += len;
+    }
+
+  if (msg_len == 0) {
+    /* if it is still the last block, we are done */
+    return;
+  }
+
+  /*
+   * now checksum everything but the last block
+   */
+  memxor3(Y.b, ctx->X.b, ctx->block.b, 16);
+  encrypt(cipher, 16, ctx->X.b, Y.b);
+
+  while (msg_len > 16)
+    {
+      memxor3(Y.b, ctx->X.b, msg, 16);
+      encrypt(cipher, 16, ctx->X.b, Y.b);
+      msg += 16;
+      msg_len -= 16;
+    }
+
+  /*
+   * copy the last block, it will be processed in
+   * cmac128_digest().
+   */
+  memcpy(ctx->block.b, msg, msg_len);
+  ctx->index = msg_len;
+}
+
+static void
+cmac128_digest(struct cmac128_ctx *ctx, const void *cipher,
+	       nettle_cipher_func *encrypt,
+	       unsigned length,
+	       uint8_t *dst)
+{
+  union nettle_block16 Y;
+
+  memset(ctx->block.b+ctx->index, 0, sizeof(ctx->block.b)-ctx->index);
+
+  /* re-use ctx->block for memxor output */
+  if (ctx->index < 16)
+    {
+      ctx->block.b[ctx->index] = 0x80;
+      memxor(ctx->block.b, ctx->K2.b, 16);
+    }
+  else
+    {
+      memxor(ctx->block.b, ctx->K1.b, 16);
+    }
+
+  memxor3(Y.b, ctx->block.b, ctx->X.b, 16);
+
+  assert(length <= 16);
+  if (length == 16)
+    {
+      encrypt(cipher, 16, dst, Y.b);
+    }
+  else
+    {
+      encrypt(cipher, 16, ctx->block.b, Y.b);
+      memcpy(dst, ctx->block.b, length);
+    }
+
+  /* reset state for re-use */
+  memset(&ctx->X, 0, sizeof(ctx->X));
+  ctx->index = 0;
+}
+
+
+#define CMAC128_CTX(type) \
+  { struct cmac128_ctx ctx; type cipher; }
+
+/* NOTE: Avoid using NULL, as we don't include anything defining it. */
+#define CMAC128_SET_KEY(self, set_key, encrypt, cmac_key)	\
+  do {								\
+    (set_key)(&(self)->cipher, (cmac_key));			\
+    if (0) (encrypt)(&(self)->cipher, ~(size_t) 0,		\
+		     (uint8_t *) 0, (const uint8_t *) 0);	\
+    cmac128_set_key(&(self)->ctx, &(self)->cipher,		\
+		(nettle_cipher_func *) (encrypt));		\
+  } while (0)
+
+#define CMAC128_UPDATE(self, encrypt, length, src)		\
+  cmac128_update(&(self)->ctx, &(self)->cipher,			\
+	      (nettle_cipher_func *)encrypt, (length), (src))
+
+#define CMAC128_DIGEST(self, encrypt, length, digest)		\
+  (0 ? (encrypt)(&(self)->cipher, ~(size_t) 0,			\
+		 (uint8_t *) 0, (const uint8_t *) 0)		\
+     : cmac128_digest(&(self)->ctx, &(self)->cipher,		\
+		  (nettle_cipher_func *) (encrypt),		\
+		  (length), (digest)))
+
+struct cmac_aes128_ctx CMAC128_CTX(struct aes128_ctx);
+
+static void
+cmac_aes128_set_key(struct cmac_aes128_ctx *ctx, const uint8_t *key)
+{
+  CMAC128_SET_KEY(ctx, aes128_set_encrypt_key, aes128_encrypt, key);
+}
+
+static void
+cmac_aes128_update (struct cmac_aes128_ctx *ctx,
+		   size_t length, const uint8_t *data)
+{
+  CMAC128_UPDATE (ctx, aes128_encrypt, length, data);
+}
+
+static void
+cmac_aes128_digest(struct cmac_aes128_ctx *ctx,
+		  size_t length, uint8_t *digest)
+{
+  CMAC128_DIGEST(ctx, aes128_encrypt, length, digest);
+}
+
+static const uint8_t const_one[] = {
+	0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x01
+};
+
+static const uint8_t const_zero[] = {
+	0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00
+};
+
+static
+void _siv_s2v(nettle_set_key_func *cmac_set_key,
+	      nettle_hash_update_func *cmac_update,
+	      nettle_hash_digest_func *cmac_digest,
+	      size_t cmac_ctx_size,
+	      const uint8_t *s2vk, size_t alength, const uint8_t *adata,
+              size_t nlength, const uint8_t *nonce,
+              size_t plength, const uint8_t *pdata,
+              uint8_t *v)
+{
+  uint8_t ctx[sizeof(struct cmac128_ctx)+sizeof(struct aes_ctx)];
+  union nettle_block16 D, S, T;
+
+  assert(cmac_ctx_size <= sizeof (ctx));
+
+  cmac_set_key(ctx, s2vk);
+
+  if (nlength == 0 && alength == 0) {
+    cmac_update(ctx, 16, const_one);
+    cmac_digest(ctx, 16, v);
+    return;
+  }
+
+  cmac_update(ctx, 16, const_zero);
+  cmac_digest(ctx, 16, D.b);
+
+  if (1) {
+    _cmac128_block_mulx(&D, &D);
+    cmac_update(ctx, alength, adata);
+    cmac_digest(ctx, 16, S.b);
+
+    memxor(D.b, S.b, 16);
+  }
+
+  if (nlength > 0) {
+    _cmac128_block_mulx(&D, &D);
+    cmac_update(ctx, nlength, nonce);
+    cmac_digest(ctx, 16, S.b);
+
+    memxor(D.b, S.b, 16);
+  }
+
+  /* Sn */
+  if (plength >= 16) {
+    cmac_update(ctx, plength-16, pdata);
+
+    pdata += plength-16;
+
+    memxor3(T.b, pdata, D.b, 16);
+  } else {
+    union nettle_block16 pad;
+
+    _cmac128_block_mulx(&T, &D);
+    memcpy(pad.b, pdata, plength);
+    pad.b[plength] = 0x80;
+    if (plength+1 < 16)
+      memset(&pad.b[plength+1], 0, 16-plength-1);
+
+    memxor(T.b, pad.b, 16);
+  }
+
+  cmac_update(ctx, 16, T.b);
+  cmac_digest(ctx, 16, v);
+}
+
+static void
+siv_cmac_aes128_set_key(struct siv_cmac_aes128_ctx *ctx, const uint8_t *key)
+{
+  memcpy(ctx->s2vk, key, 16);
+  aes128_set_encrypt_key(&ctx->cipher, key+16);
+}
+
+static void
+siv_cmac_aes128_encrypt_message(struct siv_cmac_aes128_ctx *ctx,
+				size_t nlength, const uint8_t *nonce,
+				size_t alength, const uint8_t *adata,
+				size_t clength, uint8_t *dst, const uint8_t *src)
+{
+  union nettle_block16 siv;
+  size_t slength;
+
+  assert (clength >= SIV_DIGEST_SIZE);
+  slength = clength - SIV_DIGEST_SIZE;
+
+  /* create CTR nonce */
+  _siv_s2v((nettle_set_key_func*)cmac_aes128_set_key,
+	   (nettle_hash_update_func*)cmac_aes128_update,
+	   (nettle_hash_digest_func*)cmac_aes128_digest,
+	   sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata,
+	   nlength, nonce, slength, src, siv.b);
+  memcpy(dst, siv.b, SIV_DIGEST_SIZE);
+  siv.b[8] &= ~0x80;
+  siv.b[12] &= ~0x80;
+
+  ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE,
+            siv.b, slength, dst+SIV_DIGEST_SIZE, src);
+}
+
+static int
+siv_cmac_aes128_decrypt_message(struct siv_cmac_aes128_ctx *ctx,
+				size_t nlength, const uint8_t *nonce,
+				size_t alength, const uint8_t *adata,
+				size_t mlength, uint8_t *dst, const uint8_t *src)
+{
+  union nettle_block16 siv;
+  union nettle_block16 ctr;
+
+  memcpy(ctr.b, src, SIV_DIGEST_SIZE);
+  ctr.b[8] &= ~0x80;
+  ctr.b[12] &= ~0x80;
+
+  ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE,
+            ctr.b, mlength, dst, src+SIV_DIGEST_SIZE);
+
+  /* create CTR nonce */
+  _siv_s2v((nettle_set_key_func*)cmac_aes128_set_key,
+	   (nettle_hash_update_func*)cmac_aes128_update,
+	   (nettle_hash_digest_func*)cmac_aes128_digest,
+	   sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata,
+	   nlength, nonce, mlength, dst, siv.b);
+
+  return memeql_sec(siv.b, src, SIV_DIGEST_SIZE);
+}
+
diff --git a/chrony_3_3/smooth.c b/chrony/smooth.c
similarity index 98%
rename from chrony_3_3/smooth.c
rename to chrony/smooth.c
index a21dcd8..4c350e9 100644
--- a/chrony_3_3/smooth.c
+++ b/chrony/smooth.c
@@ -144,7 +144,7 @@
      is equal to the offset that should be smoothed out */
 
   s1 = smooth_offset / max_wander;
-  s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
+  s2 = SQUARE(smooth_freq) / (2.0 * SQUARE(max_wander));
   
   /* Calculate the lengths of the 1st and 3rd stage assuming there is no
      frequency limit.  The direction of the 1st stage is selected so that
@@ -272,6 +272,10 @@
 
 void SMT_Finalise(void)
 {
+  if (!enabled)
+    return;
+
+  LCL_RemoveParameterChangeHandler(handle_slew, NULL);
 }
 
 int SMT_IsEnabled(void)
diff --git a/chrony_3_3/smooth.h b/chrony/smooth.h
similarity index 100%
rename from chrony_3_3/smooth.h
rename to chrony/smooth.h
diff --git a/chrony/socket.c b/chrony/socket.c
new file mode 100644
index 0000000..914f758
--- /dev/null
+++ b/chrony/socket.c
@@ -0,0 +1,1633 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * Copyright (C) Timo Teras  2009
+ * Copyright (C) Miroslav Lichvar  2009, 2013-2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  This file implements socket operations.
+
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+/* Ugly, include our local versions from more recent kernel first */
+#include "array.h"
+#include "linux-errqueue.h"
+#include "linux-net_tstamp.h"
+
+#include "logging.h"
+#include "privops.h"
+#include "ptp.h"
+#include "socket.h"
+#include "util.h"
+
+#define INVALID_SOCK_FD (-4)
+#define CMSG_BUF_SIZE 256
+
+union sockaddr_all {
+  struct sockaddr_in in4;
+#ifdef FEAT_IPV6
+  struct sockaddr_in6 in6;
+#endif
+  struct sockaddr_un un;
+  struct sockaddr sa;
+};
+
+struct Message {
+  union sockaddr_all name;
+  struct iovec iov;
+  /* Buffer of sufficient length for all expected messages */
+  struct {
+    /* Extra space for Ethernet, IPv4/IPv6, and UDP headers in
+       timestamped messages received from the Linux error queue */
+    uint8_t l234_headers[64];
+    union {
+      NTP_Packet ntp_msg;
+      PTP_NtpMessage ptp_msg;
+      CMD_Request cmd_request;
+      CMD_Reply cmd_reply;
+    } msg;
+  } msg_buf;
+  /* Aligned buffer for control messages */
+  struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)];
+};
+
+#ifdef HAVE_RECVMMSG
+#define MAX_RECV_MESSAGES 16
+#define MessageHeader mmsghdr
+#else
+/* Compatible with mmsghdr */
+struct MessageHeader {
+  struct msghdr msg_hdr;
+  unsigned int msg_len;
+};
+
+#define MAX_RECV_MESSAGES 1
+#endif
+
+static int initialised;
+
+/* Flags indicating in which IP families sockets can be requested */
+static int ip4_enabled;
+static int ip6_enabled;
+
+/* Flags supported by socket() */
+static int supported_socket_flags;
+
+/* Arrays of Message, MessageHeader, and SCK_Message */
+static ARR_Instance recv_messages;
+static ARR_Instance recv_headers;
+static ARR_Instance recv_sck_messages;
+
+static unsigned int received_messages;
+
+static int (*priv_bind_function)(int sock_fd, struct sockaddr *address,
+                                 socklen_t address_len);
+
+/* ================================================== */
+
+static void
+prepare_buffers(unsigned int n)
+{
+  struct MessageHeader *hdr;
+  struct Message *msg;
+  unsigned int i;
+
+  for (i = 0; i < n; i++) {
+    msg = ARR_GetElement(recv_messages, i);
+    hdr = ARR_GetElement(recv_headers, i);
+
+    msg->iov.iov_base = &msg->msg_buf;
+    msg->iov.iov_len = sizeof (msg->msg_buf);
+    hdr->msg_hdr.msg_name = &msg->name;
+    hdr->msg_hdr.msg_namelen = sizeof (msg->name);
+    hdr->msg_hdr.msg_iov = &msg->iov;
+    hdr->msg_hdr.msg_iovlen = 1;
+    hdr->msg_hdr.msg_control = msg->cmsg_buf;
+    hdr->msg_hdr.msg_controllen = sizeof (msg->cmsg_buf);
+    hdr->msg_hdr.msg_flags = 0;
+    hdr->msg_len = 0;
+  }
+}
+
+/* ================================================== */
+
+static const char *
+domain_to_string(int domain)
+{
+  switch (domain) {
+    case AF_INET:
+      return "IPv4";
+#ifdef AF_INET6
+    case AF_INET6:
+      return "IPv6";
+#endif
+    case AF_UNIX:
+      return "Unix";
+    case AF_UNSPEC:
+      return "UNSPEC";
+    default:
+      return "?";
+  }
+}
+
+/* ================================================== */
+
+#if defined(SOCK_CLOEXEC) || defined(SOCK_NONBLOCK)
+static int
+check_socket_flag(int sock_flag, int fd_flag, int fs_flag)
+{
+  int sock_fd, fd_flags, fs_flags;
+
+  sock_fd = socket(AF_INET, SOCK_DGRAM | sock_flag, 0);
+  if (sock_fd < 0)
+    return 0;
+
+  fd_flags = fcntl(sock_fd, F_GETFD);
+  fs_flags = fcntl(sock_fd, F_GETFL);
+
+  close(sock_fd);
+
+  if (fd_flags == -1 || (fd_flags & fd_flag) != fd_flag ||
+      fs_flags == -1 || (fs_flags & fs_flag) != fs_flag)
+    return 0;
+
+  return 1;
+}
+#endif
+
+/* ================================================== */
+
+static int
+set_socket_nonblock(int sock_fd)
+{
+  if (fcntl(sock_fd, F_SETFL, O_NONBLOCK) < 0) {
+    DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+get_open_flags(int flags)
+{
+  int r = supported_socket_flags;
+
+#ifdef SOCK_NONBLOCK
+  if (flags & SCK_FLAG_BLOCK)
+    r &= ~SOCK_NONBLOCK;
+#endif
+
+  return r;
+}
+
+/* ================================================== */
+
+static int
+set_socket_flags(int sock_fd, int flags)
+{
+  /* Close the socket automatically on exec */
+  if (
+#ifdef SOCK_CLOEXEC
+      (supported_socket_flags & SOCK_CLOEXEC) == 0 &&
+#endif
+      !UTI_FdSetCloexec(sock_fd))
+    return 0;
+
+  /* Enable non-blocking mode */
+  if ((flags & SCK_FLAG_BLOCK) == 0 &&
+#ifdef SOCK_NONBLOCK
+      (supported_socket_flags & SOCK_NONBLOCK) == 0 &&
+#endif
+      !set_socket_nonblock(sock_fd))
+    return 0;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+open_socket(int domain, int type, int flags)
+{
+  int sock_fd;
+
+  sock_fd = socket(domain, type | get_open_flags(flags), 0);
+
+  if (sock_fd < 0) {
+    DEBUG_LOG("Could not open %s socket : %s",
+              domain_to_string(domain), strerror(errno));
+    return INVALID_SOCK_FD;
+  }
+
+  if (!set_socket_flags(sock_fd, flags)) {
+    close(sock_fd);
+    return INVALID_SOCK_FD;
+  }
+
+  return sock_fd;
+}
+
+/* ================================================== */
+
+static int
+open_socket_pair(int domain, int type, int flags, int *other_fd)
+{
+  int sock_fds[2];
+
+  if (socketpair(domain, type | get_open_flags(flags), 0, sock_fds) < 0) {
+    DEBUG_LOG("Could not open %s socket : %s",
+              domain_to_string(domain), strerror(errno));
+    return INVALID_SOCK_FD;
+  }
+
+  if (!set_socket_flags(sock_fds[0], flags) || !set_socket_flags(sock_fds[1], flags)) {
+    close(sock_fds[0]);
+    close(sock_fds[1]);
+    return INVALID_SOCK_FD;
+  }
+
+  *other_fd = sock_fds[1];
+
+  return sock_fds[0];
+}
+
+/* ================================================== */
+
+static int
+set_socket_options(int sock_fd, int flags)
+{
+  /* Make the socket capable of sending broadcast packets if requested */
+  if (flags & SCK_FLAG_BROADCAST && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_BROADCAST, 1))
+    ;
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+set_ip_options(int sock_fd, int family, int flags)
+{
+#if defined(FEAT_IPV6) && defined(IPV6_V6ONLY)
+  /* Receive only IPv6 packets on an IPv6 socket */
+  if (family == IPADDR_INET6 && !SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, 1))
+    return 0;
+#endif
+
+  /* Provide destination address of received packets if requested */
+  if (flags & SCK_FLAG_RX_DEST_ADDR) {
+    if (family == IPADDR_INET4) {
+#ifdef HAVE_IN_PKTINFO
+      if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_PKTINFO, 1))
+        ;
+#elif defined(IP_RECVDSTADDR)
+      if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, 1))
+        ;
+#endif
+    }
+#ifdef FEAT_IPV6
+    else if (family == IPADDR_INET6) {
+#ifdef HAVE_IN6_PKTINFO
+#ifdef IPV6_RECVPKTINFO
+      if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1))
+        ;
+#else
+      if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, 1))
+        ;
+#endif
+#endif
+    }
+#endif
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+is_any_address(IPAddr *addr)
+{
+  IPAddr any_addr;
+
+  SCK_GetAnyLocalIPAddress(addr->family, &any_addr);
+
+  return UTI_CompareIPs(&any_addr, addr, NULL) == 0;
+}
+
+/* ================================================== */
+
+static int
+bind_device(int sock_fd, const char *iface)
+{
+#ifdef SO_BINDTODEVICE
+  if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) < 0) {
+    DEBUG_LOG("Could not bind socket to %s : %s", iface, strerror(errno));
+    return 0;
+  }
+  return 1;
+#else
+  DEBUG_LOG("Could not bind socket to %s : %s", iface, "Not supported");
+  return 0;
+#endif
+}
+
+/* ================================================== */
+
+static int
+bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
+{
+  union sockaddr_all saddr;
+  socklen_t saddr_len;
+  int s;
+
+  /* Make the socket capable of re-using an old address if binding to a specific port */
+  if (addr->port > 0 && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1))
+    ;
+
+#if defined(LINUX) && defined(SO_REUSEPORT)
+  /* Allow multiple instances to bind to the same port in order to enable load
+     balancing.  Don't enable this option on non-Linux systems as it has
+     a slightly different meaning there (with some important implications). */
+  if (addr->port > 0 && !SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_REUSEPORT, 1))
+    ;
+#endif
+
+#ifdef IP_FREEBIND
+  /* Allow binding to an address that doesn't exist yet */
+  if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_FREEBIND, 1))
+    ;
+#endif
+
+  saddr_len = SCK_IPSockAddrToSockaddr(addr, (struct sockaddr *)&saddr, sizeof (saddr));
+  if (saddr_len == 0)
+    return 0;
+
+  if (flags & SCK_FLAG_PRIV_BIND && priv_bind_function)
+    s = priv_bind_function(sock_fd, &saddr.sa, saddr_len);
+  else
+    s = bind(sock_fd, &saddr.sa, saddr_len);
+
+  if (s < 0) {
+    DEBUG_LOG("Could not bind socket to %s : %s",
+              UTI_IPSockAddrToString(addr), strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+connect_ip_address(int sock_fd, IPSockAddr *addr)
+{
+  union sockaddr_all saddr;
+  socklen_t saddr_len;
+
+  saddr_len = SCK_IPSockAddrToSockaddr(addr, (struct sockaddr *)&saddr, sizeof (saddr));
+  if (saddr_len == 0)
+    return 0;
+
+  if (connect(sock_fd, &saddr.sa, saddr_len) < 0 && errno != EINPROGRESS) {
+    DEBUG_LOG("Could not connect socket to %s : %s",
+              UTI_IPSockAddrToString(addr), strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface,
+               int type, int flags)
+{
+  int domain, family, sock_fd;
+
+  if (local_addr)
+    family = local_addr->ip_addr.family;
+  else if (remote_addr)
+    family = remote_addr->ip_addr.family;
+  else
+    family = IPADDR_INET4;
+
+  switch (family) {
+    case IPADDR_INET4:
+      if (!ip4_enabled)
+        return INVALID_SOCK_FD;
+      domain = AF_INET;
+      break;
+#ifdef FEAT_IPV6
+    case IPADDR_INET6:
+      if (!ip6_enabled)
+        return INVALID_SOCK_FD;
+      domain = AF_INET6;
+      break;
+#endif
+    default:
+      DEBUG_LOG("Unspecified family");
+      return INVALID_SOCK_FD;
+  }
+
+  sock_fd = open_socket(domain, type, flags);
+  if (sock_fd < 0)
+    return INVALID_SOCK_FD;
+
+  if (!set_socket_options(sock_fd, flags))
+    goto error;
+
+  if (!set_ip_options(sock_fd, family, flags))
+    goto error;
+
+  if (iface && !bind_device(sock_fd, iface))
+    goto error;
+
+  /* Bind the socket if a non-any local address/port was specified */
+  if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC &&
+      (local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) &&
+      !bind_ip_address(sock_fd, local_addr, flags))
+    goto error;
+
+  /* Connect the socket if a remote address was specified */
+  if (remote_addr && remote_addr->ip_addr.family != IPADDR_UNSPEC &&
+      !connect_ip_address(sock_fd, remote_addr))
+    goto error;
+
+  if (remote_addr || local_addr)
+    DEBUG_LOG("Opened %s%s socket fd=%d%s%s%s%s",
+              type == SOCK_DGRAM ? "UDP" : type == SOCK_STREAM ? "TCP" : "?",
+              family == IPADDR_INET4 ? "v4" : "v6",
+              sock_fd,
+              remote_addr ? " remote=" : "",
+              remote_addr ? UTI_IPSockAddrToString(remote_addr) : "",
+              local_addr ? " local=" : "",
+              local_addr ? UTI_IPSockAddrToString(local_addr) : "");
+
+  return sock_fd;
+
+error:
+  SCK_CloseSocket(sock_fd);
+  return INVALID_SOCK_FD;
+}
+
+/* ================================================== */
+
+static int
+bind_unix_address(int sock_fd, const char *addr, int flags)
+{
+  union sockaddr_all saddr;
+
+  memset(&saddr, 0, sizeof (saddr));
+
+  if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
+      sizeof (saddr.un.sun_path)) {
+    DEBUG_LOG("Unix socket path %s too long", addr);
+    return 0;
+  }
+  saddr.un.sun_family = AF_UNIX;
+
+  if (unlink(addr) < 0)
+    DEBUG_LOG("Could not remove %s : %s", addr, strerror(errno));
+
+  /* PRV_BindSocket() doesn't support Unix sockets yet */
+  if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
+    DEBUG_LOG("Could not bind Unix socket to %s : %s", addr, strerror(errno));
+    return 0;
+  }
+
+  /* Allow access to everyone with access to the directory if requested */
+  if (flags & SCK_FLAG_ALL_PERMISSIONS && chmod(addr, 0666) < 0) {
+    DEBUG_LOG("Could not change permissions of %s : %s", addr, strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+connect_unix_address(int sock_fd, const char *addr)
+{
+  union sockaddr_all saddr;
+
+  memset(&saddr, 0, sizeof (saddr));
+
+  if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
+      sizeof (saddr.un.sun_path)) {
+    DEBUG_LOG("Unix socket path %s too long", addr);
+    return 0;
+  }
+  saddr.un.sun_family = AF_UNIX;
+
+  if (connect(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
+    DEBUG_LOG("Could not connect Unix socket to %s : %s", addr, strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static int
+open_unix_socket(const char *remote_addr, const char *local_addr, int type, int flags)
+{
+  int sock_fd;
+
+  sock_fd = open_socket(AF_UNIX, type, flags);
+  if (sock_fd < 0)
+    return INVALID_SOCK_FD;
+
+  if (!set_socket_options(sock_fd, flags))
+    goto error;
+
+  /* Bind the socket if a local address was specified */
+  if (local_addr && !bind_unix_address(sock_fd, local_addr, flags))
+    goto error;
+
+  /* Connect the socket if a remote address was specified */
+  if (remote_addr && !connect_unix_address(sock_fd, remote_addr))
+    goto error;
+
+  DEBUG_LOG("Opened Unix socket fd=%d%s%s%s%s",
+            sock_fd,
+            remote_addr ? " remote=" : "", remote_addr ? remote_addr : "",
+            local_addr ? " local=" : "", local_addr ? local_addr : "");
+
+  return sock_fd;
+
+error:
+  SCK_RemoveSocket(sock_fd);
+  SCK_CloseSocket(sock_fd);
+  return INVALID_SOCK_FD;
+}
+
+/* ================================================== */
+
+static int
+open_unix_socket_pair(int type, int flags, int *other_fd)
+{
+  int sock_fd;
+
+  sock_fd = open_socket_pair(AF_UNIX, type, flags, other_fd);
+  if (sock_fd < 0)
+    return INVALID_SOCK_FD;
+
+  DEBUG_LOG("Opened Unix socket pair fd1=%d fd2=%d", sock_fd, *other_fd);
+
+  return sock_fd;
+}
+
+/* ================================================== */
+
+static int
+get_recv_flags(int flags)
+{
+  int recv_flags = 0;
+
+  if (flags & SCK_FLAG_MSG_ERRQUEUE) {
+#ifdef MSG_ERRQUEUE
+    recv_flags |= MSG_ERRQUEUE;
+#else
+    assert(0);
+#endif
+  }
+
+  return recv_flags;
+}
+
+/* ================================================== */
+
+static void
+handle_recv_error(int sock_fd, int flags)
+{
+#ifdef MSG_ERRQUEUE
+  /* If reading from the error queue failed, the select() exception should
+     be for a socket error.  Clear the error to avoid a busy loop. */
+  if (flags & SCK_FLAG_MSG_ERRQUEUE) {
+    int error = 0;
+
+    if (SCK_GetIntOption(sock_fd, SOL_SOCKET, SO_ERROR, &error))
+      errno = error;
+  }
+#endif
+
+  DEBUG_LOG("Could not receive message fd=%d : %s", sock_fd, strerror(errno));
+}
+
+/* ================================================== */
+
+static void
+log_message(int sock_fd, int direction, SCK_Message *message, const char *prefix,
+            const char *error)
+{
+  const char *local_addr, *remote_addr;
+  char if_index[20], tss[10], tsif[20], tslen[20];
+
+  if (DEBUG <= 0 || log_min_severity > LOGS_DEBUG)
+    return;
+
+  remote_addr = NULL;
+  local_addr = NULL;
+  if_index[0] = '\0';
+  tss[0] = '\0';
+  tsif[0] = '\0';
+  tslen[0] = '\0';
+
+  switch (message->addr_type) {
+    case SCK_ADDR_IP:
+      if (message->remote_addr.ip.ip_addr.family != IPADDR_UNSPEC)
+        remote_addr = UTI_IPSockAddrToString(&message->remote_addr.ip);
+      if (message->local_addr.ip.family != IPADDR_UNSPEC)
+        local_addr = UTI_IPToString(&message->local_addr.ip);
+      break;
+    case SCK_ADDR_UNIX:
+      remote_addr = message->remote_addr.path;
+      break;
+    default:
+      break;
+  }
+
+  if (message->if_index != INVALID_IF_INDEX)
+    snprintf(if_index, sizeof (if_index), " if=%d", message->if_index);
+
+  if (direction > 0) {
+    if (!UTI_IsZeroTimespec(&message->timestamp.kernel) ||
+        !UTI_IsZeroTimespec(&message->timestamp.hw))
+      snprintf(tss, sizeof (tss), " tss=%s%s",
+               !UTI_IsZeroTimespec(&message->timestamp.kernel) ? "K" : "",
+               !UTI_IsZeroTimespec(&message->timestamp.hw) ? "H" : "");
+
+    if (message->timestamp.if_index != INVALID_IF_INDEX)
+      snprintf(tsif, sizeof (tsif), " tsif=%d", message->timestamp.if_index);
+
+    if (message->timestamp.l2_length != 0)
+      snprintf(tslen, sizeof (tslen), " tslen=%d", message->timestamp.l2_length);
+  }
+
+  DEBUG_LOG("%s message%s%s%s%s fd=%d len=%d%s%s%s%s%s%s",
+            prefix,
+            remote_addr ? (direction > 0 ? " from " : " to ") : "",
+            remote_addr ? remote_addr : "",
+            local_addr ? (direction > 0 ? " to " : " from ") : "",
+            local_addr ? local_addr : "",
+            sock_fd, message->length, if_index,
+            tss, tsif, tslen,
+            error ? " : " : "", error ? error : "");
+}
+
+/* ================================================== */
+
+static void
+init_message_addresses(SCK_Message *message, SCK_AddressType addr_type)
+{
+  message->addr_type = addr_type;
+
+  switch (addr_type) {
+    case SCK_ADDR_UNSPEC:
+      break;
+    case SCK_ADDR_IP:
+      message->remote_addr.ip.ip_addr.family = IPADDR_UNSPEC;
+      message->remote_addr.ip.port = 0;
+      message->local_addr.ip.family = IPADDR_UNSPEC;
+      break;
+    case SCK_ADDR_UNIX:
+      message->remote_addr.path = NULL;
+      break;
+    default:
+      assert(0);
+  }
+}
+
+/* ================================================== */
+
+static void
+init_message_nonaddress(SCK_Message *message)
+{
+  message->data = NULL;
+  message->length = 0;
+  message->if_index = INVALID_IF_INDEX;
+
+  UTI_ZeroTimespec(&message->timestamp.kernel);
+  UTI_ZeroTimespec(&message->timestamp.hw);
+  message->timestamp.if_index = INVALID_IF_INDEX;
+  message->timestamp.l2_length = 0;
+  message->timestamp.tx_flags = 0;
+
+  message->descriptor = INVALID_SOCK_FD;
+}
+
+/* ================================================== */
+
+static int
+match_cmsg(struct cmsghdr *cmsg, int level, int type, size_t length)
+{
+  if (cmsg->cmsg_type == type && cmsg->cmsg_level == level &&
+      (length == 0 || cmsg->cmsg_len == CMSG_LEN(length)))
+    return 1;
+  return 0;
+}
+
+/* ================================================== */
+
+static int
+process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
+               SCK_Message *message)
+{
+  struct cmsghdr *cmsg;
+  int r = 1;
+
+  if (msg->msg_namelen <= sizeof (union sockaddr_all) &&
+      msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
+    switch (((struct sockaddr *)msg->msg_name)->sa_family) {
+      case AF_INET:
+#ifdef FEAT_IPV6
+      case AF_INET6:
+#endif
+        init_message_addresses(message, SCK_ADDR_IP);
+        SCK_SockaddrToIPSockAddr(msg->msg_name, msg->msg_namelen, &message->remote_addr.ip);
+        break;
+      case AF_UNIX:
+        init_message_addresses(message, SCK_ADDR_UNIX);
+        message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path;
+        break;
+      default:
+        init_message_addresses(message, SCK_ADDR_UNSPEC);
+        DEBUG_LOG("Unexpected address");
+        r = 0;
+        break;
+    }
+  } else {
+    init_message_addresses(message, SCK_ADDR_UNSPEC);
+
+    if (msg->msg_namelen > sizeof (union sockaddr_all)) {
+      DEBUG_LOG("Truncated source address");
+      r = 0;
+    }
+  }
+
+  init_message_nonaddress(message);
+
+  if (msg->msg_iovlen == 1) {
+    message->data = msg->msg_iov[0].iov_base;
+    message->length = msg_length;
+  } else {
+    DEBUG_LOG("Unexpected iovlen");
+    r = 0;
+  }
+
+  if (msg->msg_flags & MSG_TRUNC) {
+    log_message(sock_fd, 1, message, "Truncated", NULL);
+    r = 0;
+  }
+
+  if (msg->msg_flags & MSG_CTRUNC) {
+    log_message(sock_fd, 1, message, "Truncated cmsg in", NULL);
+    r = 0;
+  }
+
+  for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+    if (0) {
+    }
+#ifdef HAVE_IN_PKTINFO
+    else if (match_cmsg(cmsg, IPPROTO_IP, IP_PKTINFO, sizeof (struct in_pktinfo))) {
+      struct in_pktinfo ipi;
+
+      if (message->addr_type != SCK_ADDR_IP)
+        init_message_addresses(message, SCK_ADDR_IP);
+
+      memcpy(&ipi, CMSG_DATA(cmsg), sizeof (ipi));
+      message->local_addr.ip.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
+      message->local_addr.ip.family = IPADDR_INET4;
+      message->if_index = ipi.ipi_ifindex;
+    }
+#elif defined(IP_RECVDSTADDR)
+    else if (match_cmsg(cmsg, IPPROTO_IP, IP_RECVDSTADDR, sizeof (struct in_addr))) {
+      struct in_addr addr;
+
+      if (message->addr_type != SCK_ADDR_IP)
+        init_message_addresses(message, SCK_ADDR_IP);
+
+      memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr));
+      message->local_addr.ip.addr.in4 = ntohl(addr.s_addr);
+      message->local_addr.ip.family = IPADDR_INET4;
+    }
+#endif
+#ifdef HAVE_IN6_PKTINFO
+    else if (match_cmsg(cmsg, IPPROTO_IPV6, IPV6_PKTINFO, sizeof (struct in6_pktinfo))) {
+      struct in6_pktinfo ipi;
+
+      if (message->addr_type != SCK_ADDR_IP)
+        init_message_addresses(message, SCK_ADDR_IP);
+
+      memcpy(&ipi, CMSG_DATA(cmsg), sizeof (ipi));
+      memcpy(&message->local_addr.ip.addr.in6, &ipi.ipi6_addr.s6_addr,
+             sizeof (message->local_addr.ip.addr.in6));
+      message->local_addr.ip.family = IPADDR_INET6;
+      message->if_index = ipi.ipi6_ifindex;
+    }
+#endif
+#ifdef SCM_TIMESTAMP
+    else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMP, sizeof (struct timeval))) {
+      struct timeval tv;
+
+      memcpy(&tv, CMSG_DATA(cmsg), sizeof (tv));
+      UTI_TimevalToTimespec(&tv, &message->timestamp.kernel);
+    }
+#endif
+#ifdef SCM_TIMESTAMPNS
+    else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof (message->timestamp.kernel))) {
+      memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel));
+    }
+#endif
+#ifdef HAVE_LINUX_TIMESTAMPING
+#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
+    else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING_PKTINFO,
+                        sizeof (struct scm_ts_pktinfo))) {
+      struct scm_ts_pktinfo ts_pktinfo;
+
+      memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
+      message->timestamp.if_index = ts_pktinfo.if_index;
+      message->timestamp.l2_length = ts_pktinfo.pkt_length;
+    }
+#endif
+    else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING,
+                        sizeof (struct scm_timestamping))) {
+      struct scm_timestamping ts3;
+
+      memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
+      message->timestamp.kernel = ts3.ts[0];
+      message->timestamp.hw = ts3.ts[2];
+    }
+    else if ((match_cmsg(cmsg, SOL_IP, IP_RECVERR, 0) ||
+              match_cmsg(cmsg, SOL_IPV6, IPV6_RECVERR, 0)) &&
+             cmsg->cmsg_len >= CMSG_LEN(sizeof (struct sock_extended_err))) {
+      struct sock_extended_err err;
+
+      memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
+
+      if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
+          err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
+        log_message(sock_fd, 1, message, "Unexpected extended error in", NULL);
+        r = 0;
+      }
+    }
+#endif
+    else if (match_cmsg(cmsg, SOL_SOCKET, SCM_RIGHTS, 0)) {
+      if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) {
+        int i, fd;
+
+        DEBUG_LOG("Unexpected SCM_RIGHTS");
+        for (i = 0; CMSG_LEN((i + 1) * sizeof (int)) <= cmsg->cmsg_len; i++) {
+          memcpy(&fd, (char *)CMSG_DATA(cmsg) + i * sizeof (int), sizeof (fd));
+          close(fd);
+        }
+        r = 0;
+      } else {
+        memcpy(&message->descriptor, CMSG_DATA(cmsg), sizeof (message->descriptor));
+      }
+    }
+    else {
+      DEBUG_LOG("Unexpected control message level=%d type=%d len=%d",
+                cmsg->cmsg_level, cmsg->cmsg_type, (int)cmsg->cmsg_len);
+    }
+  }
+
+  if (!r && message->descriptor != INVALID_SOCK_FD)
+    close(message->descriptor);
+
+  return r;
+}
+
+/* ================================================== */
+
+static SCK_Message *
+receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
+{
+  struct MessageHeader *hdr;
+  SCK_Message *messages;
+  unsigned int i, n, n_ok;
+  int ret, recv_flags = 0;
+
+  assert(initialised);
+
+  *num_messages = 0;
+
+  if (max_messages < 1)
+    return NULL;
+
+  /* Prepare used buffers for new messages */
+  prepare_buffers(received_messages);
+  received_messages = 0;
+
+  messages = ARR_GetElements(recv_sck_messages);
+
+  hdr = ARR_GetElements(recv_headers);
+  n = ARR_GetSize(recv_headers);
+  n = MIN(n, max_messages);
+
+  if (n < 1 || n > MAX_RECV_MESSAGES ||
+      n > ARR_GetSize(recv_messages) || n > ARR_GetSize(recv_sck_messages))
+    assert(0);
+
+  recv_flags = get_recv_flags(flags);
+
+#ifdef HAVE_RECVMMSG
+  ret = recvmmsg(sock_fd, hdr, n, recv_flags, NULL);
+  if (ret >= 0)
+    n = ret;
+#else
+  n = 1;
+  ret = recvmsg(sock_fd, &hdr[0].msg_hdr, recv_flags);
+  if (ret >= 0)
+    hdr[0].msg_len = ret;
+#endif
+
+  if (ret < 0) {
+    handle_recv_error(sock_fd, flags);
+    return NULL;
+  }
+
+  received_messages = n;
+
+  for (i = n_ok = 0; i < n; i++) {
+    hdr = ARR_GetElement(recv_headers, i);
+    if (!process_header(&hdr->msg_hdr, hdr->msg_len, sock_fd, flags, &messages[n_ok]))
+      continue;
+
+    log_message(sock_fd, 1, &messages[n_ok],
+                flags & SCK_FLAG_MSG_ERRQUEUE ? "Received error" : "Received", NULL);
+
+    n_ok++;
+  }
+
+  *num_messages = n_ok;
+
+  return n_ok > 0 ? messages : NULL;
+}
+
+/* ================================================== */
+
+static void *
+add_control_message(struct msghdr *msg, int level, int type, size_t length, size_t buf_length)
+{
+  struct cmsghdr *cmsg;
+  size_t cmsg_space;
+
+  /* Avoid using CMSG_NXTHDR as the one in glibc does not support adding
+     control messages: https://sourceware.org/bugzilla/show_bug.cgi?id=13500 */
+
+  cmsg = msg->msg_control;
+  cmsg_space = CMSG_SPACE(length);
+
+  if (!cmsg || length > buf_length || msg->msg_controllen + cmsg_space > buf_length) {
+    DEBUG_LOG("Could not add control message level=%d type=%d", level, type);
+    return NULL;
+  }
+
+  cmsg = (struct cmsghdr *)((char *)cmsg + msg->msg_controllen);
+
+  memset(cmsg, 0, cmsg_space);
+
+  cmsg->cmsg_level = level;
+  cmsg->cmsg_type = type;
+  cmsg->cmsg_len = CMSG_LEN(length);
+
+  msg->msg_controllen += cmsg_space;
+
+  return CMSG_DATA(cmsg);
+}
+
+/* ================================================== */
+
+static int
+send_message(int sock_fd, SCK_Message *message, int flags)
+{
+  struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)];
+  union sockaddr_all saddr;
+  socklen_t saddr_len;
+  struct msghdr msg;
+  struct iovec iov;
+
+  switch (message->addr_type) {
+    case SCK_ADDR_UNSPEC:
+      saddr_len = 0;
+      break;
+    case SCK_ADDR_IP:
+      saddr_len = SCK_IPSockAddrToSockaddr(&message->remote_addr.ip,
+                                           (struct sockaddr *)&saddr, sizeof (saddr));
+      break;
+    case SCK_ADDR_UNIX:
+      memset(&saddr, 0, sizeof (saddr));
+      if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s",
+                   message->remote_addr.path) >= sizeof (saddr.un.sun_path)) {
+        DEBUG_LOG("Unix socket path %s too long", message->remote_addr.path);
+        return 0;
+      }
+      saddr.un.sun_family = AF_UNIX;
+      saddr_len = sizeof (saddr.un);
+      break;
+    default:
+      assert(0);
+  }
+
+  if (saddr_len) {
+    msg.msg_name = &saddr.un;
+    msg.msg_namelen = saddr_len;
+  } else {
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+  }
+
+  if (message->length < 0) {
+    DEBUG_LOG("Invalid length %d", message->length);
+    return 0;
+  }
+
+  iov.iov_base = message->data;
+  iov.iov_len = message->length;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsg_buf;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  if (message->addr_type == SCK_ADDR_IP) {
+    if (message->local_addr.ip.family == IPADDR_INET4) {
+#ifdef HAVE_IN_PKTINFO
+      struct in_pktinfo *ipi;
+
+      ipi = add_control_message(&msg, IPPROTO_IP, IP_PKTINFO, sizeof (*ipi),
+                                sizeof (cmsg_buf));
+      if (!ipi)
+        return 0;
+
+      ipi->ipi_spec_dst.s_addr = htonl(message->local_addr.ip.addr.in4);
+      if (message->if_index != INVALID_IF_INDEX)
+        ipi->ipi_ifindex = message->if_index;
+
+#elif defined(IP_SENDSRCADDR)
+      struct in_addr *addr;
+
+      addr = add_control_message(&msg, IPPROTO_IP, IP_SENDSRCADDR, sizeof (*addr),
+                                 sizeof (cmsg_buf));
+      if (!addr)
+        return 0;
+
+      addr->s_addr = htonl(message->local_addr.ip.addr.in4);
+#endif
+    }
+
+#ifdef HAVE_IN6_PKTINFO
+    if (message->local_addr.ip.family == IPADDR_INET6) {
+      struct in6_pktinfo *ipi;
+
+      ipi = add_control_message(&msg, IPPROTO_IPV6, IPV6_PKTINFO, sizeof (*ipi),
+                                sizeof (cmsg_buf));
+      if (!ipi)
+        return 0;
+
+      memcpy(&ipi->ipi6_addr.s6_addr, &message->local_addr.ip.addr.in6,
+             sizeof(ipi->ipi6_addr.s6_addr));
+      if (message->if_index != INVALID_IF_INDEX)
+        ipi->ipi6_ifindex = message->if_index;
+    }
+#endif
+  }
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+  if (message->timestamp.tx_flags) {
+    int *ts_tx_flags;
+
+    /* Set timestamping flags for this message */
+
+    ts_tx_flags = add_control_message(&msg, SOL_SOCKET, SO_TIMESTAMPING,
+                                      sizeof (*ts_tx_flags), sizeof (cmsg_buf));
+    if (!ts_tx_flags)
+      return 0;
+
+    *ts_tx_flags = message->timestamp.tx_flags;
+  }
+#endif
+
+  if (flags & SCK_FLAG_MSG_DESCRIPTOR) {
+    int *fd;
+
+    fd = add_control_message(&msg, SOL_SOCKET, SCM_RIGHTS, sizeof (*fd), sizeof (cmsg_buf));
+    if (!fd)
+      return 0;
+
+    *fd = message->descriptor;
+  }
+
+  /* This is apparently required on some systems */
+  if (msg.msg_controllen == 0)
+    msg.msg_control = NULL;
+
+  if (sendmsg(sock_fd, &msg, 0) < 0) {
+    log_message(sock_fd, -1, message, "Could not send", strerror(errno));
+    return 0;
+  }
+
+  log_message(sock_fd, -1, message, "Sent", NULL);
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+SCK_Initialise(int family)
+{
+  ip4_enabled = family == IPADDR_INET4 || family == IPADDR_UNSPEC;
+#ifdef FEAT_IPV6
+  ip6_enabled = family == IPADDR_INET6 || family == IPADDR_UNSPEC;
+#else
+  ip6_enabled = 0;
+#endif
+
+  recv_messages = ARR_CreateInstance(sizeof (struct Message));
+  ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
+  recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
+  ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
+  recv_sck_messages = ARR_CreateInstance(sizeof (SCK_Message));
+  ARR_SetSize(recv_sck_messages, MAX_RECV_MESSAGES);
+
+  received_messages = MAX_RECV_MESSAGES;
+
+  priv_bind_function = NULL;
+
+  supported_socket_flags = 0;
+#ifdef SOCK_CLOEXEC
+  if (check_socket_flag(SOCK_CLOEXEC, FD_CLOEXEC, 0))
+    supported_socket_flags |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+  if (check_socket_flag(SOCK_NONBLOCK, 0, O_NONBLOCK))
+    supported_socket_flags |= SOCK_NONBLOCK;
+#endif
+
+  initialised = 1;
+}
+
+/* ================================================== */
+
+void
+SCK_Finalise(void)
+{
+  ARR_DestroyInstance(recv_sck_messages);
+  ARR_DestroyInstance(recv_headers);
+  ARR_DestroyInstance(recv_messages);
+
+  initialised = 0;
+}
+
+/* ================================================== */
+
+int
+SCK_IsIpFamilyEnabled(int family)
+{
+  switch (family) {
+    case IPADDR_INET4:
+      return ip4_enabled;
+    case IPADDR_INET6:
+      return ip6_enabled;
+    default:
+      return 0;
+  }
+}
+
+/* ================================================== */
+
+void
+SCK_GetAnyLocalIPAddress(int family, IPAddr *local_addr)
+{
+  local_addr->family = family;
+
+  switch (family) {
+    case IPADDR_INET4:
+      local_addr->addr.in4 = INADDR_ANY;
+      break;
+    case IPADDR_INET6:
+#ifdef FEAT_IPV6
+      memcpy(&local_addr->addr.in6, &in6addr_any, sizeof (local_addr->addr.in6));
+#else
+      memset(&local_addr->addr.in6, 0, sizeof (local_addr->addr.in6));
+#endif
+      break;
+  }
+}
+
+/* ================================================== */
+
+void
+SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr)
+{
+  local_addr->family = family;
+
+  switch (family) {
+    case IPADDR_INET4:
+      local_addr->addr.in4 = INADDR_LOOPBACK;
+      break;
+    case IPADDR_INET6:
+#ifdef FEAT_IPV6
+      memcpy(&local_addr->addr.in6, &in6addr_loopback, sizeof (local_addr->addr.in6));
+#else
+      memset(&local_addr->addr.in6, 0, sizeof (local_addr->addr.in6));
+      local_addr->addr.in6[15] = 1;
+#endif
+      break;
+  }
+}
+
+/* ================================================== */
+
+int
+SCK_IsLinkLocalIPAddress(IPAddr *addr)
+{
+  switch (addr->family) {
+    case IPADDR_INET4:
+      /* 169.254.0.0/16 */
+      return (addr->addr.in4 & 0xffff0000) == 0xa9fe0000;
+    case IPADDR_INET6:
+      /* fe80::/10 */
+      return addr->addr.in6[0] == 0xfe && (addr->addr.in6[1] & 0xc0) == 0x80;
+    default:
+      return 0;
+  }
+}
+
+/* ================================================== */
+
+void
+SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
+                                socklen_t address_len))
+{
+  priv_bind_function = function;
+}
+
+/* ================================================== */
+
+int
+SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
+{
+  return open_ip_socket(remote_addr, local_addr, iface, SOCK_DGRAM, flags);
+}
+
+/* ================================================== */
+
+int
+SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
+{
+  return open_ip_socket(remote_addr, local_addr, iface, SOCK_STREAM, flags);
+}
+
+/* ================================================== */
+
+int
+SCK_OpenUnixDatagramSocket(const char *remote_addr, const char *local_addr, int flags)
+{
+  return open_unix_socket(remote_addr, local_addr, SOCK_DGRAM, flags);
+}
+
+/* ================================================== */
+
+int
+SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_addr, int flags)
+{
+  return open_unix_socket(remote_addr, local_addr, SOCK_STREAM, flags);
+}
+
+/* ================================================== */
+
+int
+SCK_OpenUnixSocketPair(int flags, int *other_fd)
+{
+  int sock_fd;
+
+  /* Prefer SEQPACKET sockets over DGRAM in order to receive a zero-length
+     message (end of file) when the other end is unexpectedly closed */
+  if (
+#ifdef SOCK_SEQPACKET
+      (sock_fd = open_unix_socket_pair(SOCK_SEQPACKET, flags, other_fd)) < 0 &&
+#endif
+      (sock_fd = open_unix_socket_pair(SOCK_DGRAM, flags, other_fd)) < 0)
+    return INVALID_SOCK_FD;
+
+  return sock_fd;
+}
+
+/* ================================================== */
+
+int SCK_SetIntOption(int sock_fd, int level, int name, int value) {
+  if (setsockopt(sock_fd, level, name, &value, sizeof (value)) < 0) {
+    DEBUG_LOG("setsockopt() failed fd=%d level=%d name=%d value=%d : %s",
+              sock_fd, level, name, value, strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SCK_GetIntOption(int sock_fd, int level, int name, int *value)
+{
+  socklen_t len = sizeof (*value);
+
+  if (getsockopt(sock_fd, level, name, value, &len) < 0) {
+    DEBUG_LOG("getsockopt() failed fd=%d level=%d name=%d : %s",
+              sock_fd, level, name, strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SCK_EnableKernelRxTimestamping(int sock_fd)
+{
+#ifdef SO_TIMESTAMPNS
+  if (SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, 1))
+    return 1;
+#endif
+#ifdef SO_TIMESTAMP
+  if (SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMP, 1))
+    return 1;
+#endif
+
+  return 0;
+}
+
+/* ================================================== */
+
+int
+SCK_ListenOnSocket(int sock_fd, int backlog)
+{
+  if (listen(sock_fd, backlog) < 0) {
+    DEBUG_LOG("listen() failed : %s", strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SCK_AcceptConnection(int sock_fd, IPSockAddr *remote_addr)
+{
+  union sockaddr_all saddr;
+  socklen_t saddr_len = sizeof (saddr);
+  int conn_fd;
+
+  conn_fd = accept(sock_fd, &saddr.sa, &saddr_len);
+  if (conn_fd < 0) {
+    DEBUG_LOG("accept() failed : %s", strerror(errno));
+    return INVALID_SOCK_FD;
+  }
+
+  if (!UTI_FdSetCloexec(conn_fd) || !set_socket_nonblock(conn_fd)) {
+    close(conn_fd);
+    return INVALID_SOCK_FD;
+  }
+
+  SCK_SockaddrToIPSockAddr(&saddr.sa, saddr_len, remote_addr);
+
+  return conn_fd;
+}
+
+/* ================================================== */
+
+int
+SCK_ShutdownConnection(int sock_fd)
+{
+  if (shutdown(sock_fd, SHUT_RDWR) < 0) {
+    DEBUG_LOG("shutdown() failed : %s", strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+int
+SCK_Receive(int sock_fd, void *buffer, int length, int flags)
+{
+  int r;
+
+  if (length < 0) {
+    DEBUG_LOG("Invalid length %d", length);
+    return -1;
+  }
+
+  r = recv(sock_fd, buffer, length, get_recv_flags(flags));
+
+  if (r < 0) {
+    handle_recv_error(sock_fd, flags);
+    return r;
+  }
+
+  DEBUG_LOG("Received data fd=%d len=%d", sock_fd, r);
+
+  return r;
+}
+
+/* ================================================== */
+
+int
+SCK_Send(int sock_fd, const void *buffer, int length, int flags)
+{
+  int r;
+
+  assert(flags == 0);
+
+  if (length < 0) {
+    DEBUG_LOG("Invalid length %d", length);
+    return -1;
+  }
+
+  r = send(sock_fd, buffer, length, 0);
+
+  if (r < 0) {
+    DEBUG_LOG("Could not send data fd=%d len=%d : %s", sock_fd, length, strerror(errno));
+    return r;
+  }
+
+  DEBUG_LOG("Sent data fd=%d len=%d", sock_fd, r);
+
+  return r;
+}
+
+/* ================================================== */
+
+SCK_Message *
+SCK_ReceiveMessage(int sock_fd, int flags)
+{
+  int num_messages;
+
+  return receive_messages(sock_fd, flags, 1, &num_messages);
+}
+
+/* ================================================== */
+
+SCK_Message *
+SCK_ReceiveMessages(int sock_fd, int flags, int *num_messages)
+{
+  return receive_messages(sock_fd, flags, MAX_RECV_MESSAGES, num_messages);
+}
+
+/* ================================================== */
+
+void
+SCK_InitMessage(SCK_Message *message, SCK_AddressType addr_type)
+{
+  init_message_addresses(message, addr_type);
+  init_message_nonaddress(message);
+}
+
+/* ================================================== */
+
+int
+SCK_SendMessage(int sock_fd, SCK_Message *message, int flags)
+{
+  return send_message(sock_fd, message, flags);
+}
+
+/* ================================================== */
+
+int
+SCK_RemoveSocket(int sock_fd)
+{
+  union sockaddr_all saddr;
+  socklen_t saddr_len;
+
+  saddr_len = sizeof (saddr);
+
+  if (getsockname(sock_fd, &saddr.sa, &saddr_len) < 0) {
+    DEBUG_LOG("getsockname() failed : %s", strerror(errno));
+    return 0;
+  }
+
+  if (saddr_len > sizeof (saddr) || saddr_len <= sizeof (saddr.sa.sa_family) ||
+      saddr.sa.sa_family != AF_UNIX)
+    return 0;
+
+  if (unlink(saddr.un.sun_path) < 0) {
+    DEBUG_LOG("Could not remove %s : %s", saddr.un.sun_path, strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+void
+SCK_CloseSocket(int sock_fd)
+{
+  close(sock_fd);
+}
+
+/* ================================================== */
+
+void
+SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa)
+{
+  ip_sa->ip_addr.family = IPADDR_UNSPEC;
+  ip_sa->port = 0;
+
+  switch (sa->sa_family) {
+    case AF_INET:
+      if (sa_length < (int)sizeof (struct sockaddr_in))
+        return;
+      ip_sa->ip_addr.family = IPADDR_INET4;
+      ip_sa->ip_addr.addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
+      ip_sa->port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+      break;
+#ifdef FEAT_IPV6
+    case AF_INET6:
+      if (sa_length < (int)sizeof (struct sockaddr_in6))
+        return;
+      ip_sa->ip_addr.family = IPADDR_INET6;
+      memcpy(&ip_sa->ip_addr.addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
+             sizeof (ip_sa->ip_addr.addr.in6));
+      ip_sa->port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
+      break;
+#endif
+    default:
+      break;
+  }
+}
+
+/* ================================================== */
+
+int
+SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length)
+{
+  switch (ip_sa->ip_addr.family) {
+    case IPADDR_INET4:
+      if (sa_length < (int)sizeof (struct sockaddr_in))
+        return 0;
+      memset(sa, 0, sizeof (struct sockaddr_in));
+      sa->sa_family = AF_INET;
+      ((struct sockaddr_in *)sa)->sin_addr.s_addr = htonl(ip_sa->ip_addr.addr.in4);
+      ((struct sockaddr_in *)sa)->sin_port = htons(ip_sa->port);
+#ifdef SIN6_LEN
+      ((struct sockaddr_in *)sa)->sin_len = sizeof (struct sockaddr_in);
+#endif
+      return sizeof (struct sockaddr_in);
+#ifdef FEAT_IPV6
+    case IPADDR_INET6:
+      if (sa_length < (int)sizeof (struct sockaddr_in6))
+        return 0;
+      memset(sa, 0, sizeof (struct sockaddr_in6));
+      sa->sa_family = AF_INET6;
+      memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, ip_sa->ip_addr.addr.in6,
+             sizeof (((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr));
+      ((struct sockaddr_in6 *)sa)->sin6_port = htons(ip_sa->port);
+#ifdef SIN6_LEN
+      ((struct sockaddr_in6 *)sa)->sin6_len = sizeof (struct sockaddr_in6);
+#endif
+      return sizeof (struct sockaddr_in6);
+#endif
+    default:
+      if (sa_length < (int)sizeof (struct sockaddr))
+        return 0;
+      memset(sa, 0, sizeof (struct sockaddr));
+      sa->sa_family = AF_UNSPEC;
+      return 0;
+  }
+}
diff --git a/chrony/socket.h b/chrony/socket.h
new file mode 100644
index 0000000..cdbae2d
--- /dev/null
+++ b/chrony/socket.h
@@ -0,0 +1,147 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  This is the header file for socket operations.
+
+  */
+
+#ifndef GOT_SOCKET_H
+#define GOT_SOCKET_H
+
+#include "addressing.h"
+
+/* Flags for opening sockets */
+#define SCK_FLAG_BLOCK 1
+#define SCK_FLAG_BROADCAST 2
+#define SCK_FLAG_RX_DEST_ADDR 4
+#define SCK_FLAG_ALL_PERMISSIONS 8
+#define SCK_FLAG_PRIV_BIND 16
+
+/* Flags for receiving and sending messages */
+#define SCK_FLAG_MSG_ERRQUEUE 1
+#define SCK_FLAG_MSG_DESCRIPTOR 2
+
+typedef enum {
+  SCK_ADDR_UNSPEC = 0,
+  SCK_ADDR_IP,
+  SCK_ADDR_UNIX
+} SCK_AddressType;
+
+typedef struct {
+  void *data;
+  int length;
+  SCK_AddressType addr_type;
+  int if_index;
+
+  union {
+    IPSockAddr ip;
+    const char *path;
+  } remote_addr;
+
+  union {
+    IPAddr ip;
+  } local_addr;
+
+  struct {
+    struct timespec kernel;
+    struct timespec hw;
+    int if_index;
+    int l2_length;
+    int tx_flags;
+  } timestamp;
+
+  int descriptor;
+} SCK_Message;
+
+/* Initialisation function (the specified IP family is enabled,
+   or all if IPADDR_UNSPEC) */
+extern void SCK_Initialise(int family);
+
+/* Finalisation function */
+extern void SCK_Finalise(void);
+
+/* Check if support for the IP family is enabled */
+extern int SCK_IsIpFamilyEnabled(int family);
+
+/* Get the 0.0.0.0/::0 or 127.0.0.1/::1 address */
+extern void SCK_GetAnyLocalIPAddress(int family, IPAddr *local_addr);
+extern void SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr);
+
+/* Check if an IP address is a link-local address */
+extern int SCK_IsLinkLocalIPAddress(IPAddr *addr);
+
+/* Specify a bind()-like function for binding sockets to privileged ports when
+   running in a restricted process (e.g. after dropping root privileges) */
+extern void SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
+                                            socklen_t address_len));
+
+/* Open a socket (addresses and iface may be NULL) */
+extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr,
+                             const char *iface, int flags);
+extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr,
+                             const char *iface, int flags);
+extern int SCK_OpenUnixDatagramSocket(const char *remote_addr, const char *local_addr,
+                                      int flags);
+extern int SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_addr,
+                                    int flags);
+extern int SCK_OpenUnixSocketPair(int flags, int *other_fd);
+
+/* Set and get a socket option of int size */
+extern int SCK_SetIntOption(int sock_fd, int level, int name, int value);
+extern int SCK_GetIntOption(int sock_fd, int level, int name, int *value);
+
+/* Enable RX timestamping socket option */
+extern int SCK_EnableKernelRxTimestamping(int sock_fd);
+
+/* Operate on a stream socket - listen()/accept()/shutdown() wrappers */
+extern int SCK_ListenOnSocket(int sock_fd, int backlog);
+extern int SCK_AcceptConnection(int sock_fd, IPSockAddr *remote_addr);
+extern int SCK_ShutdownConnection(int sock_fd);
+
+/* Receive and send data on connected sockets - recv()/send() wrappers */
+extern int SCK_Receive(int sock_fd, void *buffer, int length, int flags);
+extern int SCK_Send(int sock_fd, const void *buffer, int length, int flags);
+
+/* Receive a single message or multiple messages.  The functions return
+   a pointer to static buffers, or NULL on error.  The buffers are valid until
+   another call of the functions and can be reused for sending messages. */
+extern SCK_Message *SCK_ReceiveMessage(int sock_fd, int flags);
+extern SCK_Message *SCK_ReceiveMessages(int sock_fd, int flags, int *num_messages);
+
+/* Initialise a new message (e.g. before sending) */
+extern void SCK_InitMessage(SCK_Message *message, SCK_AddressType addr_type);
+
+/* Send a message */
+extern int SCK_SendMessage(int sock_fd, SCK_Message *message, int flags);
+
+/* Remove bound Unix socket */
+extern int SCK_RemoveSocket(int sock_fd);
+
+/* Close the socket */
+extern void SCK_CloseSocket(int sock_fd);
+
+/* Convert between IPSockAddr and sockaddr_in/in6 */
+extern void SCK_SockaddrToIPSockAddr(struct sockaddr *sa, int sa_length, IPSockAddr *ip_sa);
+extern int SCK_IPSockAddrToSockaddr(IPSockAddr *ip_sa, struct sockaddr *sa, int sa_length);
+
+#endif
diff --git a/chrony_3_3/sources.c b/chrony/sources.c
similarity index 67%
rename from chrony_3_3/sources.c
rename to chrony/sources.c
index 8aaa4a6..c439a74 100644
--- a/chrony_3_3/sources.c
+++ b/chrony/sources.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2011-2016
+ * Copyright (C) Miroslav Lichvar  2011-2016, 2018, 2020-2021
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -54,7 +54,6 @@
 /* ================================================== */
 /* Structure used to hold info for selecting between sources */
 struct SelectInfo {
-  int stratum;
   int select_ok;
   double std_dev;
   double root_distance;
@@ -91,7 +90,6 @@
    source */
 struct SRC_Instance_Record {
   SST_Stats stats;
-  NTP_Leap leap_status;         /* Leap status */
   int index;                    /* Index back into the array of source */
   uint32_t ref_id;              /* The reference ID of this source
                                    (i.e. from its IP address, NOT the
@@ -119,13 +117,28 @@
   /* Type of the source */
   SRC_Type type;
 
-  /* Options used when selecting sources */ 
+  /* Flag indicating that the source is authenticated */
+  int authenticated;
+
+  /* Configured selection options */
+  int conf_sel_options;
+
+  /* Effective selection options */
   int sel_options;
 
   /* Score against currently selected source */
   double sel_score;
 
   struct SelectInfo sel_info;
+
+  /* Current stratum */
+  int stratum;
+
+  /* Current leap status */
+  NTP_Leap leap;
+
+  /* Flag indicating the source has a leap second vote */
+  int leap_vote;
 };
 
 /* ================================================== */
@@ -164,16 +177,17 @@
 static double stratum_weight;
 static double combine_limit;
 
+/* Identifier of the dump file */
+#define DUMP_IDENTIFIER "SRC0\n"
+
 /* ================================================== */
 /* Forward prototype */
 
-static void
-slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
-             double doffset, LCL_ChangeType change_type, void *anything);
-static void
-add_dispersion(double dispersion, void *anything);
-static char *
-source_to_string(SRC_Instance inst);
+static void update_sel_options(void);
+static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
+                         double doffset, LCL_ChangeType change_type, void *anything);
+static void add_dispersion(double dispersion, void *anything);
+static char *source_to_string(SRC_Instance inst);
 
 /* ================================================== */
 /* Initialisation function */
@@ -213,9 +227,9 @@
 /* Function to create a new instance.  This would be called by one of
    the individual source-type instance creation routines. */
 
-SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options,
-                                   IPAddr *addr, int min_samples, int max_samples,
-                                   double min_delay, double asymmetry)
+SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
+                                   int sel_options, IPAddr *addr, int min_samples,
+                                   int max_samples, double min_delay, double asymmetry)
 {
   SRC_Instance result;
 
@@ -248,13 +262,18 @@
 
   result->index = n_sources;
   result->type = type;
+  result->authenticated = authenticated;
+  result->conf_sel_options = sel_options;
   result->sel_options = sel_options;
+  result->active = 0;
 
   SRC_SetRefid(result, ref_id, addr);
   SRC_ResetInstance(result);
 
   n_sources++;
 
+  update_sel_options();
+
   return result;
 }
 
@@ -279,6 +298,8 @@
   --n_sources;
   Free(instance);
 
+  update_sel_options();
+
   /* If this was the previous reference source, we have to reselect! */
   if (selected_source_index == dead_index)
     SRC_ReselectSource();
@@ -291,14 +312,17 @@
 void
 SRC_ResetInstance(SRC_Instance instance)
 {
-  instance->leap_status = LEAP_Normal;
-  instance->active = 0;
   instance->updates = 0;
   instance->reachability = 0;
   instance->reachability_size = 0;
   instance->distant = 0;
   instance->status = SRC_BAD_STATS;
   instance->sel_score = 1.0;
+  instance->stratum = 0;
+  instance->leap = LEAP_Unsynchronised;
+  instance->leap_vote = 0;
+
+  memset(&instance->sel_info, 0, sizeof (instance->sel_info));
 
   SST_ResetInstance(instance->stats);
 }
@@ -324,45 +348,74 @@
 
 /* ================================================== */
 
+static NTP_Leap
+get_leap_status(void)
+{
+  int i, leap_votes, leap_ins, leap_del;
+
+  /* Accept a leap second if more than half of the sources with a vote agree */
+
+  for (i = leap_ins = leap_del = leap_votes = 0; i < n_sources; i++) {
+    if (!sources[i]->leap_vote)
+      continue;
+
+    leap_votes++;
+    if (sources[i]->leap == LEAP_InsertSecond)
+      leap_ins++;
+    else if (sources[i]->leap == LEAP_DeleteSecond)
+      leap_del++;
+  }
+
+  if (leap_ins > leap_votes / 2)
+    return LEAP_InsertSecond;
+  else if (leap_del > leap_votes / 2)
+    return LEAP_DeleteSecond;
+  else
+    return LEAP_Normal;
+}
+
+/* ================================================== */
+
+void
+SRC_UpdateStatus(SRC_Instance inst, int stratum, NTP_Leap leap)
+{
+  inst->stratum = stratum;
+
+  if (REF_IsLeapSecondClose(NULL, 0.0))
+    return;
+
+  inst->leap = leap;
+
+  if (inst->leap_vote)
+    REF_UpdateLeapStatus(get_leap_status());
+}
+
+/* ================================================== */
+
 /* This function is called by one of the source drivers when it has
    a new sample that is to be accumulated.
 
    This function causes the frequency estimation to be re-run for the
    designated source, and the clock selection procedure to be re-run
    afterwards.
-
-   Parameters are described in sources.h
-
    */
 
-void SRC_AccumulateSample
-(SRC_Instance inst, 
- struct timespec *sample_time,
- double offset, 
- double peer_delay,
- double peer_dispersion,
- double root_delay, 
- double root_dispersion, 
- int stratum,
- NTP_Leap leap_status)
+void
+SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
 {
 
   assert(initialised);
 
-  inst->leap_status = leap_status;
+  DEBUG_LOG("src=%s ts=%s offset=%e delay=%e disp=%e",
+            source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
+            sample->root_delay, sample->root_dispersion);
 
-  DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
-            source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
-            root_delay, root_dispersion, stratum);
-
-  if (REF_IsLeapSecondClose()) {
+  if (REF_IsLeapSecondClose(&sample->time, sample->offset)) {
     LOG(LOGS_INFO, "Dropping sample around leap second");
     return;
   }
 
-  /* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
-     IS FLIPPED */
-  SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
+  SST_AccumulateSample(inst->stats, sample);
   SST_DoNewRegression(inst->stats);
 }
 
@@ -394,7 +447,7 @@
       if (!sources[i]->active)
         continue;
 
-      /* Don't expect more updates than from an offline iburst NTP source */
+      /* Don't expect more updates than the initial burst of an NTP source */
       if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1)
         continue;
 
@@ -450,7 +503,76 @@
 /* ================================================== */
 
 static void
-log_selection_message(char *format, char *arg)
+update_sel_options(void)
+{
+  int options, auth_ntp_options, unauth_ntp_options, refclk_options;
+  int i, auth_ntp_sources, unauth_ntp_sources;
+
+  auth_ntp_sources = unauth_ntp_sources = 0;
+
+  for (i = 0; i < n_sources; i++) {
+    if (sources[i]->conf_sel_options & SRC_SELECT_NOSELECT)
+      continue;
+    if (sources[i]->type != SRC_NTP)
+      continue;
+    if (sources[i]->authenticated)
+      auth_ntp_sources++;
+    else
+      unauth_ntp_sources++;
+  }
+
+  auth_ntp_options = unauth_ntp_options = refclk_options = 0;
+
+  /* Determine which selection options need to be added to authenticated NTP
+     sources, unauthenticated NTP sources, and refclocks, to follow the
+     configured selection mode */
+  switch (CNF_GetAuthSelectMode()) {
+    case SRC_AUTHSELECT_IGNORE:
+      break;
+    case SRC_AUTHSELECT_MIX:
+      if (auth_ntp_sources > 0 && unauth_ntp_sources > 0)
+        auth_ntp_options = refclk_options = SRC_SELECT_REQUIRE | SRC_SELECT_TRUST;
+      break;
+    case SRC_AUTHSELECT_PREFER:
+      if (auth_ntp_sources > 0)
+        unauth_ntp_options = SRC_SELECT_NOSELECT;
+      break;
+    case SRC_AUTHSELECT_REQUIRE:
+      unauth_ntp_options = SRC_SELECT_NOSELECT;
+      break;
+    default:
+      assert(0);
+  }
+
+  for (i = 0; i < n_sources; i++) {
+    options = sources[i]->conf_sel_options;
+
+    if (options & SRC_SELECT_NOSELECT)
+      continue;
+
+    switch (sources[i]->type) {
+      case SRC_NTP:
+        options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options;
+        break;
+      case SRC_REFCLOCK:
+        options |= refclk_options;
+        break;
+      default:
+        assert(0);
+    }
+
+    if (sources[i]->sel_options != options) {
+      DEBUG_LOG("changing %s from %x to %x", source_to_string(sources[i]),
+                (unsigned int)sources[i]->sel_options, (unsigned int)options);
+      sources[i]->sel_options = options;
+    }
+  }
+}
+
+/* ================================================== */
+
+static void
+log_selection_message(const char *format, const char *arg)
 {
   if (REF_GetMode() != REF_ModeNormal)
     return;
@@ -459,6 +581,24 @@
 
 /* ================================================== */
 
+static void
+log_selection_source(const char *format, SRC_Instance inst)
+{
+  char buf[320], *name, *ntp_name;
+
+  name = source_to_string(inst);
+  ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
+
+  if (ntp_name && strcmp(name, ntp_name) != 0)
+    snprintf(buf, sizeof (buf), "%s (%s)", name, ntp_name);
+  else
+    snprintf(buf, sizeof (buf), "%s", name);
+
+  log_selection_message(format, buf);
+}
+
+/* ================================================== */
+
 static int
 compare_sort_elements(const void *a, const void *b)
 {
@@ -497,6 +637,20 @@
 /* ================================================== */
 
 static void
+mark_source(SRC_Instance inst, SRC_Status status)
+{
+  inst->status = status;
+
+  DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
+            source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options,
+            (unsigned int)inst->reachability, inst->reachability_size, inst->updates,
+            inst->distant, (int)inst->leap, inst->leap_vote,
+            inst->sel_info.lo_limit, inst->sel_info.hi_limit);
+}
+
+/* ================================================== */
+
+static void
 mark_ok_sources(SRC_Status status)
 {
   int i;
@@ -504,7 +658,7 @@
   for (i = 0; i < n_sources; i++) {
     if (sources[i]->status != SRC_OK)
       continue;
-    sources[i]->status = status;
+    mark_source(sources[i], status);
   }
 }
 
@@ -512,20 +666,21 @@
 
 static int
 combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
-                double *offset_sd, double *frequency, double *skew)
+                double *offset_sd, double *frequency, double *frequency_sd, double *skew)
 {
   struct timespec src_ref_time;
-  double src_offset, src_offset_sd, src_frequency, src_skew;
+  double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
   double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
   double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
-  double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew;
+  double frequency_weight, sum_frequency_weight, sum_frequency;
+  double inv_sum2_frequency_sd, inv_sum2_skew;
   int i, index, combined;
 
   if (n_sel_sources == 1)
     return 1;
 
   sum_offset_weight = sum_offset = sum2_offset_sd = 0.0;
-  sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0;
+  sum_frequency_weight = sum_frequency = inv_sum2_frequency_sd = inv_sum2_skew = 0.0;
 
   sel_src_distance = sources[selected_source_index]->sel_info.root_distance;
   if (sources[selected_source_index]->type == SRC_NTP)
@@ -535,7 +690,7 @@
     index = sel_sources[i];
     SST_GetTrackingData(sources[index]->stats, &src_ref_time,
                         &src_offset, &src_offset_sd,
-                        &src_frequency, &src_skew,
+                        &src_frequency, &src_frequency_sd, &src_skew,
                         &src_root_delay, &src_root_dispersion);
 
     /* Don't include this source if its distance is longer than the distance of
@@ -554,29 +709,32 @@
     }
 
     if (sources[index]->distant) {
-      sources[index]->status = SRC_DISTANT;
+      mark_source(sources[index], SRC_DISTANT);
       continue;
     }
 
     if (sources[index]->status == SRC_OK)
-      sources[index]->status = SRC_UNSELECTED;
+      mark_source(sources[index], SRC_UNSELECTED);
 
     elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
     src_offset += elapsed * src_frequency;
+    src_offset_sd += elapsed * src_frequency_sd;
     offset_weight = 1.0 / sources[index]->sel_info.root_distance;
-    frequency_weight = 1.0 / src_skew;
+    frequency_weight = 1.0 / SQUARE(src_frequency_sd);
 
-    DEBUG_LOG("combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
-        index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
+    DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
+              index, offset_weight, src_offset, src_offset_sd,
+              frequency_weight, src_frequency, src_frequency_sd, src_skew);
 
     sum_offset_weight += offset_weight;
     sum_offset += offset_weight * src_offset;
-    sum2_offset_sd += offset_weight * (src_offset_sd * src_offset_sd +
-        (src_offset - *offset) * (src_offset - *offset));
+    sum2_offset_sd += offset_weight * (SQUARE(src_offset_sd) +
+                                       SQUARE(src_offset - *offset));
 
     sum_frequency_weight += frequency_weight;
     sum_frequency += frequency_weight * src_frequency;
-    inv_sum2_skew += 1.0 / (src_skew * src_skew);
+    inv_sum2_frequency_sd += 1.0 / SQUARE(src_frequency_sd);
+    inv_sum2_skew += 1.0 / SQUARE(src_skew);
 
     combined++;
   }
@@ -585,10 +743,11 @@
   *offset = sum_offset / sum_offset_weight;
   *offset_sd = sqrt(sum2_offset_sd / sum_offset_weight);
   *frequency = sum_frequency / sum_frequency_weight;
+  *frequency_sd = 1.0 / sqrt(inv_sum2_frequency_sd);
   *skew = 1.0 / sqrt(inv_sum2_skew);
 
-  DEBUG_LOG("combined result offset=%e sd=%e freq=%e skew=%e",
-      *offset, *offset_sd, *frequency, *skew);
+  DEBUG_LOG("combined result offset=%e osd=%e freq=%e fsd=%e skew=%e",
+            *offset, *offset_sd, *frequency, *frequency_sd, *skew);
 
   return combined;
 }
@@ -602,14 +761,15 @@
 {
   struct SelectInfo *si;
   struct timespec now, ref_time;
-  int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources;
-  int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
-  int depth, best_depth, trust_depth, best_trust_depth;
+  int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
+  int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
+  int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources;
   int combined, stratum, min_stratum, max_score_index;
-  int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
-  double src_offset, src_offset_sd, src_frequency, src_skew;
+  int orphan_stratum, orphan_source;
+  double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
   double src_root_delay, src_root_dispersion;
   double best_lo, best_hi, distance, sel_src_distance, max_score;
+  double best_trust_lo, best_trust_hi;
   double first_sample_ago, max_reach_sample_ago;
   NTP_Leap leap_status;
 
@@ -631,15 +791,19 @@
   /* Step 1 - build intervals about each source */
 
   n_endpoints = 0;
-  n_sel_sources = 0;
+  n_sel_sources = n_sel_trust_sources = 0;
   n_badstats_sources = 0;
   sel_req_source = 0;
   max_sel_reach = max_badstat_reach = 0;
+  max_sel_reach_size = 0;
   max_reach_sample_ago = 0.0;
 
   for (i = 0; i < n_sources; i++) {
     assert(sources[i]->status != SRC_OK);
 
+    /* Don't allow the source to vote on leap seconds unless it's selectable */
+    sources[i]->leap_vote = 0;
+
     /* If some sources are specified with the require option, at least one
        of them will have to be selectable in order to update the clock */
     if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
@@ -647,19 +811,19 @@
 
     /* Ignore sources which were added with the noselect option */
     if (sources[i]->sel_options & SRC_SELECT_NOSELECT) {
-      sources[i]->status = SRC_UNSELECTABLE;
+      mark_source(sources[i], SRC_UNSELECTABLE);
       continue;
     }
 
     si = &sources[i]->sel_info;
-    SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
+    SST_GetSelectionData(sources[i]->stats, &now,
                          &si->lo_limit, &si->hi_limit, &si->root_distance,
                          &si->std_dev, &first_sample_ago,
                          &si->last_sample_ago, &si->select_ok);
 
     if (!si->select_ok) {
       ++n_badstats_sources;
-      sources[i]->status = SRC_BAD_STATS;
+      mark_source(sources[i], SRC_BAD_STATS);
       if (max_badstat_reach < sources[i]->reachability)
         max_badstat_reach = sources[i]->reachability;
       continue;
@@ -675,15 +839,16 @@
       si->hi_limit += extra_disp;
     }
 
-    /* Require the root distance to be below the allowed maximum */
-    if (si->root_distance > max_distance) {
-      sources[i]->status = SRC_BAD_DISTANCE;
+    /* Require the root distance to be below the allowed maximum and the
+       endpoints to be in the right order (i.e. a non-negative distance) */
+    if (!(si->root_distance <= max_distance && si->lo_limit <= si->hi_limit)) {
+      mark_source(sources[i], SRC_BAD_DISTANCE);
       continue;
     }
 
     /* And the same applies for the estimated standard deviation */
     if (si->std_dev > max_jitter) {
-      sources[i]->status = SRC_JITTERY;
+      mark_source(sources[i], SRC_JITTERY);
       continue;
     }
 
@@ -694,6 +859,9 @@
 
     if (max_sel_reach < sources[i]->reachability)
       max_sel_reach = sources[i]->reachability;
+
+    if (max_sel_reach_size < sources[i]->reachability_size)
+      max_sel_reach_size = sources[i]->reachability_size;
   }
 
   orphan_stratum = REF_GetOrphanStratum();
@@ -709,7 +877,7 @@
        can still be selected if its newest sample is not older than the oldest
        sample from reachable sources. */
     if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) {
-      sources[i]->status = SRC_STALE;
+      mark_source(sources[i], SRC_STALE);
       continue;
     }
 
@@ -730,10 +898,10 @@
        source can settle down to a state where only one server is serving its
        local unsychronised time and others are synchronised to it. */
 
-    if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) {
-      sources[i]->status = SRC_ORPHAN;
+    if (sources[i]->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) {
+      mark_source(sources[i], SRC_ORPHAN);
 
-      if (si->stratum == orphan_stratum && sources[i]->reachability &&
+      if (sources[i]->stratum == orphan_stratum && sources[i]->reachability &&
           (orphan_source == INVALID_SOURCE ||
            sources[i]->ref_id < sources[orphan_source]->ref_id))
         orphan_source = i;
@@ -761,6 +929,9 @@
     if (sources[i]->status != SRC_OK)
       continue;
 
+    if (sources[i]->sel_options & SRC_SELECT_TRUST)
+      n_sel_trust_sources++;
+
     si = &sources[i]->sel_info;
 
     j1 = n_endpoints;
@@ -777,18 +948,17 @@
     n_endpoints += 2;
   }
 
-  DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f",
-            n_badstats_sources, n_sel_sources, max_badstat_reach,
-            max_sel_reach, max_reach_sample_ago);
+  DEBUG_LOG("badstat=%d sel=%d badstat_reach=%o sel_reach=%o size=%d max_reach_ago=%f",
+            n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach,
+            (unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago);
 
   /* Wait for the next call if we have no source selected and there is
      a source with bad stats (has less than 3 samples) with reachability
      equal to shifted maximum reachability of sources with valid stats.
      This delays selecting source on start with servers using the same
      polling interval until they all have valid stats. */
-  if (n_badstats_sources && n_sel_sources &&
-      selected_source_index == INVALID_SOURCE &&
-      max_sel_reach >> 1 == max_badstat_reach) {
+  if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE &&
+      max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
     mark_ok_sources(SRC_WAITS_STATS);
     return;
   }
@@ -834,7 +1004,7 @@
 
   trust_depth = best_trust_depth = 0;
   depth = best_depth = 0;
-  best_lo = best_hi = 0.0;
+  best_lo = best_hi = best_trust_lo = best_trust_hi = 0.0;
 
   for (i = 0; i < n_endpoints; i++) {
     switch (sort_list[i].tag) {
@@ -844,14 +1014,20 @@
           trust_depth++;
         if (trust_depth > best_trust_depth ||
             (trust_depth == best_trust_depth && depth > best_depth)) {
-          best_trust_depth = trust_depth;
+          if (trust_depth > best_trust_depth) {
+            best_trust_depth = trust_depth;
+            best_trust_lo = sort_list[i].offset;
+          }
           best_depth = depth;
           best_lo = sort_list[i].offset;
         }
         break;
       case HIGH:
-        if (trust_depth == best_trust_depth && depth == best_depth)
-          best_hi = sort_list[i].offset;
+        if (trust_depth == best_trust_depth) {
+          if (depth == best_depth)
+            best_hi = sort_list[i].offset;
+          best_trust_hi = sort_list[i].offset;
+        }
         if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST)
           trust_depth--;
         depth--;
@@ -859,11 +1035,16 @@
       default:
         assert(0);
     }
+    assert(trust_depth <= depth);
+    assert(trust_depth >= 0);
   }
 
-  if (best_depth <= n_sel_sources / 2 && !best_trust_depth) {
-    /* Could not even get half the reachable sources to agree and there
-       are no trusted sources - clearly we can't synchronise */
+  assert(depth == 0 && trust_depth == 0);
+  assert(2 * n_sel_sources == n_endpoints);
+
+  if ((best_trust_depth == 0 && best_depth <= n_sel_sources / 2) ||
+      (best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
+    /* Could not even get half the reachable (trusted) sources to agree */
 
     if (selected_source_index != INVALID_SOURCE) {
       log_selection_message("Can't synchronise: no majority", NULL);
@@ -890,24 +1071,28 @@
       continue;
 
     /* Check if source's interval contains the best interval, or is wholly
-       contained within it.  If there are any trusted sources the first
-       condition is applied only to them to not allow non-trusted sources to
-       move the final offset outside the interval. */
-    if (((!best_trust_depth || sources[i]->sel_options & SRC_SELECT_TRUST) &&
-         sources[i]->sel_info.lo_limit <= best_lo &&
+       contained within it.  If there are any trusted sources, other sources
+       are required to be wholly contained within the best interval of the
+       trusted sources to not allow non-trusted sources to move the final
+       offset outside the trusted interval. */
+    if ((sources[i]->sel_info.lo_limit <= best_lo &&
          sources[i]->sel_info.hi_limit >= best_hi) ||
         (sources[i]->sel_info.lo_limit >= best_lo &&
          sources[i]->sel_info.hi_limit <= best_hi)) {
 
+      if (!(best_trust_depth == 0 || (sources[i]->sel_options & SRC_SELECT_TRUST) ||
+            (sources[i]->sel_info.lo_limit >= best_trust_lo &&
+             sources[i]->sel_info.hi_limit <= best_trust_hi))) {
+        mark_source(sources[i], SRC_UNTRUSTED);
+        continue;
+      }
+
       sel_sources[n_sel_sources++] = i;
 
       if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
         sel_req_source = 0;
-    } else if (sources[i]->sel_info.lo_limit <= best_lo &&
-               sources[i]->sel_info.hi_limit >= best_hi) {
-      sources[i]->status = SRC_UNTRUSTED;
     } else {
-      sources[i]->status = SRC_FALSETICKER;
+      mark_source(sources[i], SRC_FALSETICKER);
     }
   }
 
@@ -922,26 +1107,15 @@
     return;
   }
 
-  /* Accept leap second status if more than half of selectable (and trusted
-     if there are any) sources agree */
-  for (i = leap_ins = leap_del = leap_votes = 0; i < n_sel_sources; i++) {
+  /* Enable the selectable sources (and trusted if there are any) to
+     vote on leap seconds */
+  for (i = 0; i < n_sel_sources; i++) {
     index = sel_sources[i];
     if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
       continue;
-    leap_votes++;
-    if (sources[index]->leap_status == LEAP_InsertSecond)
-      leap_ins++;
-    else if (sources[index]->leap_status == LEAP_DeleteSecond)
-      leap_del++;
+    sources[index]->leap_vote = 1;
   }
 
-  if (leap_ins > leap_votes / 2)
-    leap_status = LEAP_InsertSecond;
-  else if (leap_del > leap_votes / 2)
-    leap_status = LEAP_DeleteSecond;
-  else
-    leap_status = LEAP_Normal;
-
   /* If there are any sources with prefer option, reduce the list again
      only to the preferred sources */
   for (i = 0; i < n_sel_sources; i++) {
@@ -951,7 +1125,7 @@
   if (i < n_sel_sources) {
     for (i = j = 0; i < n_sel_sources; i++) {
       if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER))
-        sources[sel_sources[i]]->status = SRC_NONPREFERRED;
+        mark_source(sources[sel_sources[i]], SRC_NONPREFERRED);
       else
         sel_sources[j++] = sel_sources[i];
     }
@@ -965,10 +1139,10 @@
   /* Find minimum stratum */
 
   index = sel_sources[0];
-  min_stratum = sources[index]->sel_info.stratum;
+  min_stratum = sources[index]->stratum;
   for (i = 1; i < n_sel_sources; i++) {
     index = sel_sources[i];
-    stratum = sources[index]->sel_info.stratum;
+    stratum = sources[index]->stratum;
     if (stratum < min_stratum)
       min_stratum = stratum;
   }
@@ -981,7 +1155,7 @@
 
   if (selected_source_index != INVALID_SOURCE)
     sel_src_distance = sources[selected_source_index]->sel_info.root_distance +
-      (sources[selected_source_index]->sel_info.stratum - min_stratum) * stratum_weight;
+      (sources[selected_source_index]->stratum - min_stratum) * stratum_weight;
 
   for (i = 0; i < n_sources; i++) {
     /* Reset score for non-selectable sources */
@@ -993,7 +1167,7 @@
     }
 
     distance = sources[i]->sel_info.root_distance +
-      (sources[i]->sel_info.stratum - min_stratum) * stratum_weight;
+      (sources[i]->stratum - min_stratum) * stratum_weight;
     if (sources[i]->type == SRC_NTP)
       distance += reselect_distance;
 
@@ -1015,10 +1189,8 @@
       sources[i]->sel_score = 1.0 / distance;
     }
 
-    DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f",
-              sources[i]->sel_score, sources[i]->ref_id,
-              updated_inst ? updated_inst->ref_id : 0,
-              sources[i]->status, distance);
+    DEBUG_LOG("%s score=%f dist=%f",
+              source_to_string(sources[i]), sources[i]->sel_score, distance);
 
     if (max_score < sources[i]->sel_score) {
       max_score = sources[i]->sel_score;
@@ -1039,13 +1211,11 @@
     if (sources[max_score_index]->updates == 0) {
       selected_source_index = INVALID_SOURCE;
       mark_ok_sources(SRC_WAITS_UPDATE);
-      DEBUG_LOG("best source has no updates");
       return;
     }
 
     selected_source_index = max_score_index;
-    log_selection_message("Selected source %s",
-                          source_to_string(sources[selected_source_index]));
+    log_selection_source("Selected source %s", sources[selected_source_index]);
 
     /* New source has been selected, reset all scores */
     for (i = 0; i < n_sources; i++) {
@@ -1054,7 +1224,7 @@
     }
   }
 
-  sources[selected_source_index]->status = SRC_SELECTED;
+  mark_source(sources[selected_source_index], SRC_SELECTED);
 
   /* Don't update reference when the selected source has no new samples */
 
@@ -1064,8 +1234,7 @@
     for (i = 0; i < n_sel_sources; i++) {
       index = sel_sources[i];
       if (sources[index]->status == SRC_OK)
-        sources[index]->status = sources[index]->distant ?
-                                 SRC_DISTANT : SRC_UNSELECTED;
+        mark_source(sources[index], sources[index]->distant ? SRC_DISTANT : SRC_UNSELECTED);
     }
     return;
   }
@@ -1073,23 +1242,25 @@
   for (i = 0; i < n_sources; i++)
     sources[i]->updates = 0;
 
+  leap_status = get_leap_status();
+
   /* Now just use the statistics of the selected source combined with
      the other selectable sources for trimming the local clock */
 
   SST_GetTrackingData(sources[selected_source_index]->stats, &ref_time,
                       &src_offset, &src_offset_sd,
-                      &src_frequency, &src_skew,
+                      &src_frequency, &src_frequency_sd, &src_skew,
                       &src_root_delay, &src_root_dispersion);
 
-  combined = combine_sources(n_sel_sources, &ref_time, &src_offset,
-                             &src_offset_sd, &src_frequency, &src_skew);
+  combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd,
+                             &src_frequency, &src_frequency_sd, &src_skew);
 
-  REF_SetReference(sources[selected_source_index]->sel_info.stratum,
+  REF_SetReference(sources[selected_source_index]->stratum,
                    leap_status, combined,
                    sources[selected_source_index]->ref_id,
                    sources[selected_source_index]->ip_addr,
                    &ref_time, src_offset, src_offset_sd,
-                   src_frequency, src_skew,
+                   src_frequency, src_frequency_sd, src_skew,
                    src_root_delay, src_root_dispersion);
 }
 
@@ -1136,7 +1307,7 @@
   }
 
   if (change_type == LCL_ChangeUnknownStep) {
-    /* After resetting no source is selectable, set reference unsynchronised */
+    /* Update selection status */
     SRC_SelectSource(NULL);
   }
 }
@@ -1157,35 +1328,60 @@
 
 /* ================================================== */
 
-static
-FILE *open_dumpfile(SRC_Instance inst, const char *mode)
+static int
+get_dumpfile(SRC_Instance inst, char *filename, size_t len)
 {
+  /* Use the IP address, or reference ID with reference clocks */
+  switch (inst->type) {
+    case SRC_NTP:
+      if (!UTI_IsIPReal(inst->ip_addr) ||
+          snprintf(filename, len, "%s", source_to_string(inst)) >= len)
+        return 0;
+      break;
+    case SRC_REFCLOCK:
+      if (snprintf(filename, len, "refid:%08"PRIx32, inst->ref_id) >= len)
+        return 0;
+      break;
+    default:
+      assert(0);
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+static void
+save_source(SRC_Instance inst)
+{
+  char filename[64], *dumpdir, *ntp_name;
   FILE *f;
-  char filename[1024], *dumpdir;
 
   dumpdir = CNF_GetDumpDir();
-  if (dumpdir[0] == '\0') {
-    LOG(LOGS_WARN, "dumpdir not specified");
-    return NULL;
+  if (!dumpdir)
+    return;
+
+  if (!get_dumpfile(inst, filename, sizeof (filename)))
+    return;
+
+  f = UTI_OpenFile(dumpdir, filename, ".dat", 'w', 0644);
+  if (!f)
+    return;
+
+  ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : ".";
+
+  if (fprintf(f, "%s%s\n%d %o %d %d %d\n",
+              DUMP_IDENTIFIER, ntp_name, inst->authenticated,
+              (unsigned int)inst->reachability, inst->reachability_size,
+              inst->stratum, (int)inst->leap) < 0 ||
+      !SST_SaveToFile(inst->stats, f)) {
+    fclose(f);
+    if (!UTI_RemoveFile(dumpdir, filename, ".dat"))
+      ;
+    return;
   }
 
-  /* Include IP address in the name for NTP sources, or reference ID in hex */
-  if ((inst->type == SRC_NTP &&
-       snprintf(filename, sizeof (filename), "%s/%s.dat", dumpdir,
-                source_to_string(inst)) >= sizeof (filename)) ||
-      (inst->type != SRC_NTP &&
-       snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat",
-                dumpdir, inst->ref_id) >= sizeof (filename))) {
-    LOG(LOGS_WARN, "dumpdir too long");
-    return NULL;
-  }
-
-  f = fopen(filename, mode);
-  if (!f && mode[0] != 'r')
-    LOG(LOGS_WARN, "Could not open dump file for %s",
-        source_to_string(inst));
-
-  return f;
+  fclose(f);
 }
 
 /* ================================================== */
@@ -1194,16 +1390,60 @@
 void
 SRC_DumpSources(void)
 {
-  FILE *out;
   int i;
 
-  for (i = 0; i < n_sources; i++) {
-    out = open_dumpfile(sources[i], "w");
-    if (!out)
-      continue;
-    SST_SaveToFile(sources[i]->stats, out);
-    fclose(out);
+  for (i = 0; i < n_sources; i++)
+    save_source(sources[i]);
+}
+
+/* ================================================== */
+
+#define MAX_WORDS 1
+
+static void
+load_source(SRC_Instance inst)
+{
+  char filename[64], line[256], *dumpdir, *ntp_name, *words[MAX_WORDS];
+  int auth, leap, reach_size, stratum;
+  unsigned int reach;
+  FILE *f;
+
+  dumpdir = CNF_GetDumpDir();
+  if (!dumpdir)
+    return;
+
+  if (!get_dumpfile(inst, filename, sizeof (filename)))
+    return;
+
+  f = UTI_OpenFile(dumpdir, filename, ".dat", 'r', 0);
+  if (!f)
+    return;
+
+  ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
+
+  if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
+      !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
+        (inst->type == SRC_NTP && (!ntp_name || strcmp(words[0], ntp_name) != 0)) ||
+      !fgets(line, sizeof (line), f) ||
+        sscanf(words[0], "%d %o %d %d %d",
+               &auth, &reach, &reach_size, &stratum, &leap) != 5 ||
+        (!auth && inst->authenticated) ||
+        stratum < 0 || stratum >= NTP_MAX_STRATUM ||
+        leap < LEAP_Normal || leap >= LEAP_Unsynchronised ||
+      !SST_LoadFromFile(inst->stats, f)) {
+    LOG(LOGS_WARN, "Could not load dump file for %s", source_to_string(inst));
+    fclose(f);
+    return;
   }
+
+  inst->reachability = reach & ((1U << SOURCE_REACH_BITS) - 1);
+  inst->reachability_size = CLAMP(0, reach_size, SOURCE_REACH_BITS);
+  inst->stratum = stratum;
+  inst->leap = leap;
+
+  LOG(LOGS_INFO, "Loaded dump file for %s", source_to_string(inst));
+
+  fclose(f);
 }
 
 /* ================================================== */
@@ -1211,21 +1451,17 @@
 void
 SRC_ReloadSources(void)
 {
-  FILE *in;
   int i;
 
   for (i = 0; i < n_sources; i++) {
-    in = open_dumpfile(sources[i], "r");
-    if (!in)
-      continue;
-    if (!SST_LoadFromFile(sources[i]->stats, in))
-      LOG(LOGS_WARN, "Could not load dump file for %s",
-          source_to_string(sources[i]));
-    else
-      LOG(LOGS_INFO, "Loaded dump file for %s",
-          source_to_string(sources[i]));
-    fclose(in);
+    load_source(sources[i]);
+
+    /* Allow an immediate update of the reference */
+    sources[i]->updates++;
   }
+
+  /* Select sources and set the reference */
+  SRC_SelectSource(NULL);
 }
 
 /* ================================================== */
@@ -1233,13 +1469,13 @@
 void
 SRC_RemoveDumpFiles(void)
 {
-  char pattern[1024], name[64], *dumpdir, *s;
+  char pattern[PATH_MAX], name[64], *dumpdir, *s;
   IPAddr ip_addr;
   glob_t gl;
   size_t i;
 
   dumpdir = CNF_GetDumpDir();
-  if (dumpdir[0] == '\0' ||
+  if (!dumpdir ||
       snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern))
     return;
 
@@ -1260,8 +1496,8 @@
     if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
       continue;
 
-    DEBUG_LOG("Removing %s", gl.gl_pathv[i]);
-    unlink(gl.gl_pathv[i]);
+    if (!UTI_RemoveFile(NULL, gl.gl_pathv[i], NULL))
+      ;
   }
 
   globfree(&gl);
@@ -1269,6 +1505,17 @@
 
 /* ================================================== */
 
+void
+SRC_ResetSources(void)
+{
+  int i;
+
+  for (i = 0; i < n_sources; i++)
+    SRC_ResetInstance(sources[i]);
+}
+
+/* ================================================== */
+
 int
 SRC_IsSyncPeer(SRC_Instance inst)
 {
@@ -1329,6 +1576,8 @@
       report->ip_addr.family = IPADDR_INET4;
     }
 
+    report->stratum = src->stratum;
+
     switch (src->status) {
       case SRC_FALSETICKER:
         report->state = RPT_FALSETICKER;
@@ -1336,26 +1585,24 @@
       case SRC_JITTERY:
         report->state = RPT_JITTERY;
         break;
-      case SRC_UNTRUSTED:
       case SRC_WAITS_SOURCES:
       case SRC_NONPREFERRED:
       case SRC_WAITS_UPDATE:
       case SRC_DISTANT:
       case SRC_OUTLIER:
-        report->state = RPT_OUTLIER;
+        report->state = RPT_SELECTABLE;
         break;
       case SRC_UNSELECTED:
-        report->state = RPT_CANDIDATE;
+        report->state = RPT_UNSELECTED;
         break;
       case SRC_SELECTED:
-        report->state = RPT_SYNC;
+        report->state = RPT_SELECTED;
         break;
       default:
-        report->state = RPT_UNREACH;
+        report->state = RPT_NONSELECTABLE;
         break;
     }
 
-    report->sel_options = src->sel_options;
     report->reachability = src->reachability;
 
     /* Call stats module to fill out estimates */
@@ -1389,6 +1636,79 @@
 
 /* ================================================== */
 
+static char
+get_status_char(SRC_Status status)
+{
+  switch (status) {
+    case SRC_UNSELECTABLE:
+      return 'N';
+    case SRC_BAD_STATS:
+      return 'M';
+    case SRC_BAD_DISTANCE:
+      return 'd';
+    case SRC_JITTERY:
+      return '~';
+    case SRC_WAITS_STATS:
+      return 'w';
+    case SRC_STALE:
+      return 'S';
+    case SRC_ORPHAN:
+      return 'O';
+    case SRC_UNTRUSTED:
+      return 'T';
+    case SRC_FALSETICKER:
+      return 'x';
+    case SRC_WAITS_SOURCES:
+      return 'W';
+    case SRC_NONPREFERRED:
+      return 'P';
+    case SRC_WAITS_UPDATE:
+      return 'U';
+    case SRC_DISTANT:
+      return 'D';
+    case SRC_OUTLIER:
+      return 'L';
+    case SRC_UNSELECTED:
+      return '+';
+    case SRC_SELECTED:
+      return '*';
+    default:
+      return '?';
+  }
+}
+
+/* ================================================== */
+
+int
+SRC_GetSelectReport(int index, RPT_SelectReport *report)
+{
+  SRC_Instance inst;
+
+  if (index >= n_sources || index < 0)
+    return 0;
+
+  inst = sources[index];
+
+  report->ref_id = inst->ref_id;
+  if (inst->ip_addr)
+    report->ip_addr = *inst->ip_addr;
+  else
+    report->ip_addr.family = IPADDR_UNSPEC;
+  report->state_char = get_status_char(inst->status);
+  report->authentication = inst->authenticated;
+  report->leap = inst->leap;
+  report->conf_options = inst->conf_sel_options;
+  report->eff_options = inst->sel_options;
+  report->last_sample_ago = inst->sel_info.last_sample_ago;
+  report->score = inst->sel_score;
+  report->lo_limit = inst->sel_info.lo_limit;
+  report->hi_limit = inst->sel_info.hi_limit;
+
+  return 1;
+}
+
+/* ================================================== */
+
 SRC_Type
 SRC_GetType(int index)
 {
diff --git a/chrony_3_3/sources.h b/chrony/sources.h
similarity index 73%
rename from chrony_3_3/sources.h
rename to chrony/sources.h
index 60d28fd..6c799bb 100644
--- a/chrony_3_3/sources.h
+++ b/chrony/sources.h
@@ -51,6 +51,14 @@
 /* Finalisation function */
 extern void SRC_Finalise(void);
 
+/* Modes for selecting NTP sources based on their authentication status */
+typedef enum {
+  SRC_AUTHSELECT_IGNORE,
+  SRC_AUTHSELECT_MIX,
+  SRC_AUTHSELECT_PREFER,
+  SRC_AUTHSELECT_REQUIRE,
+} SRC_AuthSelectMode;
+
 typedef enum {
   SRC_NTP,                      /* NTP client/peer */
   SRC_REFCLOCK                  /* Rerefence clock */
@@ -59,9 +67,9 @@
 /* Function to create a new instance.  This would be called by one of
    the individual source-type instance creation routines. */
 
-extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options,
-                                          IPAddr *addr, int min_samples, int max_samples,
-                                          double min_delay, double asymmetry);
+extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
+                                          int sel_options, IPAddr *addr, int min_samples,
+                                          int max_samples, double min_delay, double asymmetry);
 
 /* Function to get rid of a source when it is being unconfigured.
    This may cause the current reference source to be reselected, if this
@@ -79,35 +87,11 @@
 /* Function to get access to the sourcestats instance */
 extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
 
-/* This function is called by one of the source drivers when it has
-   a new sample that is to be accumulated.
+/* Function to update the stratum and leap status of the source */
+extern void SRC_UpdateStatus(SRC_Instance instance, int stratum, NTP_Leap leap);
 
-   This function causes the frequency estimation to be re-run for the
-   designated source, and the clock selection procedure to be re-run
-   afterwards.
-
-   sample_time is the local time at which the sample is to be
-   considered to have been made, in terms of doing a regression fit of
-   offset against local time.
-
-   offset is the offset at the time, in seconds.  Positive indicates
-   that the local clock is SLOW relative to the source, negative
-   indicates that the local clock is FAST relative to it.
-
-   root_delay and root_dispersion are in seconds, and are as per
-   RFC 5905.  root_dispersion only includes the peer's root dispersion
-   + local sampling precision + skew dispersion accrued during the
-   measurement.  It is the job of the source statistics algorithms +
-   track.c to add on the extra dispersion due to the residual standard
-   deviation of the offsets from this source after regression, to form
-   the root_dispersion field in the packets transmitted to clients or
-   peers.
-
-   stratum is the stratum of the source that supplied the sample.
-
-   */
-
-extern void SRC_AccumulateSample(SRC_Instance instance, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
+/* Function to accumulate a new sample from the source */
+extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample);
 
 /* This routine sets the source as receiving reachability updates */
 extern void SRC_SetActive(SRC_Instance inst);
@@ -140,13 +124,16 @@
 extern void SRC_ReloadSources(void);
 extern void SRC_RemoveDumpFiles(void);
 
+extern void SRC_ResetSources(void);
+
 extern int SRC_IsSyncPeer(SRC_Instance inst);
 extern int SRC_IsReachable(SRC_Instance inst);
 extern int SRC_ReadNumberOfSources(void);
 extern int SRC_ActiveSources(void);
-extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
 
+extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
 extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
+extern int SRC_GetSelectReport(int index, RPT_SelectReport *report);
 
 extern SRC_Type SRC_GetType(int index);
 
diff --git a/chrony_3_3/sourcestats.c b/chrony/sourcestats.c
similarity index 84%
rename from chrony_3_3/sourcestats.c
rename to chrony/sourcestats.c
index ff4c652..900dc60 100644
--- a/chrony_3_3/sourcestats.c
+++ b/chrony/sourcestats.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2011-2014, 2016-2017
+ * Copyright (C) Miroslav Lichvar  2011-2014, 2016-2018, 2021
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -54,6 +54,9 @@
 /* The minimum standard deviation */
 #define MIN_STDDEV 1.0e-9
 
+/* The worst case bound on an unknown standard deviation of the offset */
+#define WORST_CASE_STDDEV_BOUND 4.0
+
 /* The asymmetry of network jitter when all jitter is in one direction */
 #define MAX_ASYMMETRY 0.5
 
@@ -132,6 +135,7 @@
      source per unit local time.  (Positive => local clock fast,
      negative => local clock slow) */
   double estimated_frequency;
+  double estimated_frequency_sd;
 
   /* This is the assumed worst case bounds on the estimated frequency.
      We assume that the true frequency lies within +/- half this much
@@ -173,11 +177,6 @@
   /* This array contains the root dispersions of each sample at the
      time of the measurements */
   double root_dispersions[MAX_SAMPLES];
-
-  /* This array contains the strata that were associated with the sources
-     at the times the samples were generated */
-  int strata[MAX_SAMPLES];
-
 };
 
 /* ================================================== */
@@ -244,11 +243,12 @@
   inst->best_single_sample = 0;
   inst->min_delay_sample = 0;
   inst->estimated_frequency = 0;
-  inst->skew = 2000.0e-6;
+  inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
+  inst->skew = WORST_CASE_FREQ_BOUND;
   inst->estimated_offset = 0.0;
-  inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
+  inst->estimated_offset_sd = WORST_CASE_STDDEV_BOUND;
   UTI_ZeroTimespec(&inst->offset_time);
-  inst->std_dev = 4.0;
+  inst->std_dev = WORST_CASE_STDDEV_BOUND;
   inst->nruns = 0;
   inst->asymmetry_run = 0;
   inst->asymmetry = 0.0;
@@ -287,11 +287,7 @@
 /* ================================================== */
 
 void
-SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
-                     double offset,
-                     double peer_delay, double peer_dispersion,
-                     double root_delay, double root_dispersion,
-                     int stratum)
+SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
 {
   int n, m;
 
@@ -303,7 +299,7 @@
 
   /* Make sure it's newer than the last sample */
   if (inst->n_samples &&
-      UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
+      UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], &sample->time) >= 0) {
     LOG(LOGS_WARN, "Out of order sample detected, discarding history for %s",
         inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
     SST_ResetInstance(inst);
@@ -313,14 +309,15 @@
     (MAX_SAMPLES * REGRESS_RUNS_RATIO);
   m = n % MAX_SAMPLES;
 
-  inst->sample_times[n] = *sample_time;
-  inst->offsets[n] = offset;
-  inst->orig_offsets[m] = offset;
-  inst->peer_delays[n] = peer_delay;
-  inst->peer_dispersions[m] = peer_dispersion;
-  inst->root_delays[m] = root_delay;
-  inst->root_dispersions[m] = root_dispersion;
-  inst->strata[m] = stratum;
+  /* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
+     IS FLIPPED */
+  inst->sample_times[n] = sample->time;
+  inst->offsets[n] = -sample->offset;
+  inst->orig_offsets[m] = -sample->offset;
+  inst->peer_delays[n] = sample->peer_delay;
+  inst->peer_dispersions[m] = sample->peer_dispersion;
+  inst->root_delays[m] = sample->root_delay;
+  inst->root_dispersions[m] = sample->root_dispersion;
  
   if (inst->peer_delays[n] < inst->fixed_min_delay)
     inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n];
@@ -550,7 +547,7 @@
       sd_weight = 1.0;
       if (peer_distances[i] > min_distance)
         sd_weight += (peer_distances[i] - min_distance) / sd;
-      weights[i] = sd_weight * sd_weight;
+      weights[i] = SQUARE(sd_weight);
     }
   }
 
@@ -570,6 +567,7 @@
     old_freq = inst->estimated_frequency;
   
     inst->estimated_frequency = est_slope;
+    inst->estimated_frequency_sd = CLAMP(MIN_SKEW, est_slope_sd, MAX_SKEW);
     inst->skew = est_slope_sd * RGR_GetTCoef(degrees_of_freedom);
     inst->estimated_offset = est_intercept;
     inst->offset_time = inst->sample_times[inst->last_sample];
@@ -599,8 +597,20 @@
     times_back_start = inst->runs_samples + best_start;
     prune_register(inst, best_start);
   } else {
-    inst->estimated_frequency = 0.0;
+    inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
     inst->skew = WORST_CASE_FREQ_BOUND;
+    inst->estimated_offset_sd = WORST_CASE_STDDEV_BOUND;
+    inst->std_dev = WORST_CASE_STDDEV_BOUND;
+    inst->nruns = 0;
+
+    if (inst->n_samples > 0) {
+      inst->estimated_offset = inst->offsets[inst->last_sample];
+      inst->offset_time = inst->sample_times[inst->last_sample];
+    } else {
+      inst->estimated_offset = 0.0;
+      UTI_ZeroTimespec(&inst->offset_time);
+    }
+
     times_back_start = 0;
   }
 
@@ -636,7 +646,6 @@
 
 void
 SST_GetSelectionData(SST_Stats inst, struct timespec *now,
-                     int *stratum,
                      double *offset_lo_limit,
                      double *offset_hi_limit,
                      double *root_distance,
@@ -656,7 +665,6 @@
   i = get_runsbuf_index(inst, inst->best_single_sample);
   j = get_buf_index(inst, inst->best_single_sample);
 
-  *stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
   *std_dev = inst->std_dev;
 
   sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
@@ -688,6 +696,13 @@
 
   *select_ok = inst->regression_ok;
 
+  /* If maxsamples is too small to have a successful regression, enable the
+     selection as a special case for a fast update/print-once reference mode */
+  if (!*select_ok && inst->n_samples < 3 && inst->n_samples == inst->max_samples) {
+    *std_dev = CNF_GetMaxJitter();
+    *select_ok = 1;
+  }
+
   DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
             inst->n_samples, offset, *root_distance, *std_dev,
             *first_sample_ago, *last_sample_ago, *select_ok);
@@ -698,7 +713,7 @@
 void
 SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
                     double *average_offset, double *offset_sd,
-                    double *frequency, double *skew,
+                    double *frequency, double *frequency_sd, double *skew,
                     double *root_delay, double *root_dispersion)
 {
   int i, j;
@@ -713,16 +728,16 @@
   *average_offset = inst->estimated_offset;
   *offset_sd = inst->estimated_offset_sd;
   *frequency = inst->estimated_frequency;
+  *frequency_sd = inst->estimated_frequency_sd;
   *skew = inst->skew;
   *root_delay = inst->root_delays[j];
 
   elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
-  *root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
+  *root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample + *offset_sd;
 
-  DEBUG_LOG("n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
-            inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew,
-            *average_offset, *offset_sd, *root_dispersion);
-
+  DEBUG_LOG("n=%d off=%f offsd=%f freq=%e freqsd=%e skew=%e delay=%f disp=%f",
+            inst->n_samples, *average_offset, *offset_sd,
+            *frequency, *frequency_sd, *skew, *root_delay, *root_dispersion);
 }
 
 /* ================================================== */
@@ -764,6 +779,22 @@
 
 /* ================================================== */
 
+void
+SST_CorrectOffset(SST_Stats inst, double doffset)
+{
+  int i;
+
+  if (!inst->n_samples)
+    return;
+
+  for (i = -inst->runs_samples; i < inst->n_samples; i++)
+    inst->offsets[get_runsbuf_index(inst, i)] += doffset;
+
+  inst->estimated_offset += doffset;
+}
+
+/* ================================================== */
+
 void 
 SST_AddDispersion(SST_Stats inst, double dispersion)
 {
@@ -837,38 +868,30 @@
 /* This is used to save the register to a file, so that we can reload
    it after restarting the daemon */
 
-void
+int
 SST_SaveToFile(SST_Stats inst, FILE *out)
 {
   int m, i, j;
 
-  fprintf(out, "%d\n", inst->n_samples);
+  if (inst->n_samples < 1)
+    return 0;
+
+  if (fprintf(out, "%d %d\n", inst->n_samples, inst->asymmetry_run) < 0)
+    return 0;
 
   for(m = 0; m < inst->n_samples; m++) {
     i = get_runsbuf_index(inst, m);
     j = get_buf_index(inst, m);
 
-    fprintf(out,
-#ifdef HAVE_LONG_TIME_T
-            "%08"PRIx64" %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
-            (uint64_t)inst->sample_times[i].tv_sec,
-#else
-            "%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
-            (unsigned long)inst->sample_times[i].tv_sec,
-#endif
-            (unsigned long)inst->sample_times[i].tv_nsec / 1000,
-            inst->offsets[i],
-            inst->orig_offsets[j],
-            inst->peer_delays[i],
-            inst->peer_dispersions[j],
-            inst->root_delays[j],
-            inst->root_dispersions[j],
-            1.0, /* used to be inst->weights[i] */
-            inst->strata[j]);
-
+    if (fprintf(out, "%s %.6e %.6e %.6e %.6e %.6e %.6e\n",
+                UTI_TimespecToString(&inst->sample_times[i]),
+                inst->offsets[i], inst->orig_offsets[j],
+                inst->peer_delays[i], inst->peer_dispersions[j],
+                inst->root_delays[j], inst->root_dispersions[j]) < 0)
+      return 0;
   }
 
-  fprintf(out, "%d\n", inst->asymmetry_run);
+  return 1;
 }
 
 /* ================================================== */
@@ -877,65 +900,46 @@
 int
 SST_LoadFromFile(SST_Stats inst, FILE *in)
 {
-#ifdef HAVE_LONG_TIME_T
-  uint64_t sec;
-#else
-  unsigned long sec;
-#endif
-  unsigned long usec;
-  int i;
-  char line[1024];
-  double weight;
+  int i, n_samples, arun;
+  struct timespec now;
+  double sample_time;
+  char line[256];
+
+  if (!fgets(line, sizeof (line), in) ||
+      sscanf(line, "%d %d", &n_samples, &arun) != 2 ||
+      n_samples < 1 || n_samples > MAX_SAMPLES)
+    return 0;
 
   SST_ResetInstance(inst);
 
-  if (fgets(line, sizeof(line), in) &&
-      sscanf(line, "%d", &inst->n_samples) == 1 &&
-      inst->n_samples >= 0 && inst->n_samples <= MAX_SAMPLES) {
+  LCL_ReadCookedTime(&now, NULL);
 
-    for (i=0; i<inst->n_samples; i++) {
-      if (!fgets(line, sizeof(line), in) ||
-          (sscanf(line,
-#ifdef HAVE_LONG_TIME_T
-                  "%"SCNx64"%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
-#else
-                  "%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
-#endif
-                  &(sec), &(usec),
-                  &(inst->offsets[i]),
-                  &(inst->orig_offsets[i]),
-                  &(inst->peer_delays[i]),
-                  &(inst->peer_dispersions[i]),
-                  &(inst->root_delays[i]),
-                  &(inst->root_dispersions[i]),
-                  &weight, /* not used anymore */
-                  &(inst->strata[i])) != 10)) {
+  for (i = 0; i < n_samples; i++) {
+    if (!fgets(line, sizeof (line), in) ||
+        sscanf(line, "%lf %lf %lf %lf %lf %lf %lf",
+               &sample_time, &inst->offsets[i], &inst->orig_offsets[i],
+               &inst->peer_delays[i], &inst->peer_dispersions[i],
+               &inst->root_delays[i], &inst->root_dispersions[i]) != 7)
+      return 0;
 
-        /* This is the branch taken if the read FAILED */
+    if (!UTI_IsTimeOffsetSane(&now, sample_time - UTI_TimespecToDouble(&now)))
+      return 0;
 
-        inst->n_samples = 0; /* Load abandoned if any sign of corruption */
-        return 0;
-      } else {
+    /* Some resolution is lost in the double format, but that's ok */
+    UTI_DoubleToTimespec(sample_time, &inst->sample_times[i]);
 
-        /* This is the branch taken if the read is SUCCESSFUL */
-        inst->sample_times[i].tv_sec = sec;
-        inst->sample_times[i].tv_nsec = 1000 * usec;
-        UTI_NormaliseTimespec(&inst->sample_times[i]);
-      }
-    }
-
-    /* This field was not saved in older versions */
-    if (!fgets(line, sizeof(line), in) || sscanf(line, "%d\n", &inst->asymmetry_run) != 1)
-      inst->asymmetry_run = 0;
-  } else {
-    inst->n_samples = 0; /* Load abandoned if any sign of corruption */
-    return 0;
+    /* Make sure the samples are sane and they are in order */
+    if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
+        !(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 &&
+          fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) ||
+        (i > 0 && UTI_CompareTimespecs(&inst->sample_times[i],
+                                       &inst->sample_times[i - 1]) <= 0))
+      return 0;
   }
 
-  if (!inst->n_samples)
-    return 1;
-
+  inst->n_samples = n_samples;
   inst->last_sample = inst->n_samples - 1;
+  inst->asymmetry_run = CLAMP(-MAX_ASYMMETRY_RUN, arun, MAX_ASYMMETRY_RUN);
 
   find_min_delay_sample(inst);
   SST_DoNewRegression(inst);
@@ -957,7 +961,6 @@
     report->orig_latest_meas = inst->orig_offsets[j];
     report->latest_meas = inst->offsets[i];
     report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
-    report->stratum = inst->strata[j];
 
     /* Align the sample time to reduce the leak of the receive timestamp */
     last_sample_time = inst->sample_times[i];
@@ -987,31 +990,24 @@
 {
   double dspan;
   double elapsed, sample_elapsed;
-  int li, lj, bi, bj;
+  int bi, bj;
 
   report->n_samples = inst->n_samples;
   report->n_runs = inst->nruns;
 
-  if (inst->n_samples > 1) {
-    li = get_runsbuf_index(inst, inst->n_samples - 1);
-    lj = get_buf_index(inst, inst->n_samples - 1);
-    dspan = UTI_DiffTimespecsToDouble(&inst->sample_times[li],
-        &inst->sample_times[get_runsbuf_index(inst, 0)]);
-    report->span_seconds = (unsigned long) (dspan + 0.5);
+  if (inst->n_samples > 0) {
+    bi = get_runsbuf_index(inst, inst->best_single_sample);
+    bj = get_buf_index(inst, inst->best_single_sample);
 
-    if (inst->n_samples > 3) {
-      elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
-      bi = get_runsbuf_index(inst, inst->best_single_sample);
-      bj = get_buf_index(inst, inst->best_single_sample);
-      sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[bi]);
-      report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency;
-      report->est_offset_err = (inst->estimated_offset_sd +
-                 sample_elapsed * inst->skew +
-                 (0.5*inst->root_delays[bj] + inst->root_dispersions[bj]));
-    } else {
-      report->est_offset = inst->offsets[li];
-      report->est_offset_err = 0.5*inst->root_delays[lj] + inst->root_dispersions[lj];
-    }
+    dspan = UTI_DiffTimespecsToDouble(&inst->sample_times[inst->last_sample],
+                                      &inst->sample_times[get_runsbuf_index(inst, 0)]);
+    elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
+    sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[bi]);
+
+    report->span_seconds = round(dspan);
+    report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency;
+    report->est_offset_err = inst->estimated_offset_sd + sample_elapsed * inst->skew +
+                             (0.5 * inst->root_delays[bj] + inst->root_dispersions[bj]);
   } else {
     report->span_seconds = 0;
     report->est_offset = 0;
diff --git a/chrony_3_3/sourcestats.h b/chrony/sourcestats.h
similarity index 87%
rename from chrony_3_3/sourcestats.h
rename to chrony/sourcestats.h
index f0a5ca9..7a551e6 100644
--- a/chrony_3_3/sourcestats.h
+++ b/chrony/sourcestats.h
@@ -51,19 +51,8 @@
 /* This function changes the reference ID and IP address */
 extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
 
-/* This function accumulates a single sample into the statistics handler
-
-   sample_time is the epoch at which the sample is to be considered to
-   have been made.
-
-   offset is the offset of the local clock relative to the source in
-   seconds.  Positive indicates that the local clock if FAST (contrary
-   to the NTP parts of the software)
-
-   stratum is the stratum of the source from which the sample came.
-  */
-
-extern void SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
+/* This function accumulates a single sample into the statistics handler */
+extern void SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample);
 
 /* This function runs the linear regression operation on the data.  It
    finds the set of most recent samples that give the tightest
@@ -80,7 +69,6 @@
 /* Get data needed for selection */
 extern void
 SST_GetSelectionData(SST_Stats inst, struct timespec *now,
-                     int *stratum,
                      double *offset_lo_limit,
                      double *offset_hi_limit,
                      double *root_distance,
@@ -93,7 +81,7 @@
 extern void
 SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
                     double *average_offset, double *offset_sd,
-                    double *frequency, double *skew,
+                    double *frequency, double *frequency_sd, double *skew,
                     double *root_delay, double *root_dispersion);
 
 /* This routine is called when the local machine clock parameters are
@@ -114,6 +102,10 @@
 
 extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset);
 
+/* This routine corrects already accumulated samples to improve the
+   frequency estimate when a new sample is accumulated */
+extern void SST_CorrectOffset(SST_Stats inst, double doffset);
+
 /* This routine is called when an indeterminate offset is introduced
    into the local time. */
 extern void SST_AddDispersion(SST_Stats inst, double dispersion);
@@ -131,7 +123,7 @@
                                 double *last_sample_ago, double *predicted_offset,
                                 double *min_delay, double *skew, double *std_dev);
 
-extern void SST_SaveToFile(SST_Stats inst, FILE *out);
+extern int SST_SaveToFile(SST_Stats inst, FILE *out);
 
 extern int SST_LoadFromFile(SST_Stats inst, FILE *in);
 
diff --git a/chrony_3_3/srcparams.h b/chrony/srcparams.h
similarity index 88%
rename from chrony_3_3/srcparams.h
rename to chrony/srcparams.h
index 7a73bad..f811146 100644
--- a/chrony_3_3/srcparams.h
+++ b/chrony/srcparams.h
@@ -29,10 +29,16 @@
 
 #include "sources.h"
 
+typedef enum {
+  SRC_OFFLINE,
+  SRC_ONLINE,
+  SRC_MAYBE_ONLINE,
+} SRC_Connectivity;
+
 typedef struct {
   int minpoll;
   int maxpoll;
-  int online;
+  SRC_Connectivity connectivity;
   int auto_offline;
   int presend_minpoll;
   int burst;
@@ -43,9 +49,15 @@
   int max_sources;
   int min_samples;
   int max_samples;
+  int filter_length;
   int interleaved;
   int sel_options;
+  int nts;
+  int nts_port;
+  int copy;
+  int ext_fields;
   uint32_t authkey;
+  uint32_t cert_set;
   double max_delay;
   double max_delay_ratio;
   double max_delay_dev_ratio;
@@ -67,6 +79,8 @@
 #define SRC_DEFAULT_MINSAMPLES (-1)
 #define SRC_DEFAULT_MAXSAMPLES (-1)
 #define SRC_DEFAULT_ASYMMETRY 1.0
+#define SRC_DEFAULT_NTSPORT 4460
+#define SRC_DEFAULT_CERTSET 0
 #define INACTIVE_AUTHKEY 0
 
 /* Flags for source selection */
diff --git a/chrony_3_3/stubs.c b/chrony/stubs.c
similarity index 68%
rename from chrony_3_3/stubs.c
rename to chrony/stubs.c
index eac81c0..cb7b9a6 100644
--- a/chrony_3_3/stubs.c
+++ b/chrony/stubs.c
@@ -28,6 +28,7 @@
 #include "config.h"
 
 #include "clientlog.h"
+#include "cmac.h"
 #include "cmdmon.h"
 #include "keys.h"
 #include "logging.h"
@@ -39,12 +40,16 @@
 #include "ntp_io.h"
 #include "ntp_sources.h"
 #include "ntp_signd.h"
+#include "nts_ke_client.h"
+#include "nts_ke_server.h"
+#include "nts_ntp_client.h"
+#include "nts_ntp_server.h"
 #include "privops.h"
 #include "refclock.h"
 #include "sched.h"
 #include "util.h"
 
-#ifndef FEAT_ASYNCDNS
+#if defined(FEAT_NTP) && !defined(FEAT_ASYNCDNS)
 
 /* This is a blocking implementation used when asynchronous resolving is not available */
 
@@ -107,7 +112,7 @@
 #ifndef FEAT_CMDMON
 
 void
-CAM_Initialise(int family)
+CAM_Initialise(void)
 {
 }
 
@@ -142,7 +147,7 @@
 #ifndef FEAT_NTP
 
 void
-NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval)
+NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval)
 {
 }
 
@@ -169,7 +174,7 @@
 }
 
 void
-NIO_Initialise(int family)
+NIO_Initialise(void)
 {
 }
 
@@ -189,23 +194,31 @@
 }
 
 NSR_Status
-NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
+NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
+              SourceParameters *params, uint32_t *conf_id)
 {
   return NSR_TooManySources;
 }
 
-void
-NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params)
+NSR_Status
+NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
+                    SourceParameters *params, uint32_t *conf_id)
 {
+  return NSR_TooManySources;
 }
 
 NSR_Status
-NSR_RemoveSource(NTP_Remote_Address *remote_addr)
+NSR_RemoveSource(IPAddr *address)
 {
   return NSR_NoSuchSource;
 }
 
 void
+NSR_RemoveSourcesById(uint32_t conf_id)
+{
+}
+
+void
 NSR_RemoveAllSources(void)
 {
 }
@@ -220,6 +233,12 @@
 {
 }
 
+char *
+NSR_GetName(IPAddr *address)
+{
+  return NULL;
+}
+
 void
 NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
 {
@@ -254,13 +273,7 @@
 }
 
 int
-NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
-{
-  return 0;
-}
-
-int
-NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
+NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
 {
   return 0;
 }
@@ -314,6 +327,12 @@
 }
   
 int
+NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
+{
+  return 0;
+}
+
+int
 NSR_GetNTPReport(RPT_NTPReport *report)
 {
   return 0;
@@ -325,6 +344,11 @@
   memset(report, 0, sizeof (*report));
 }
 
+void
+NSR_DumpAuthData(void)
+{
+}
+
 #ifndef FEAT_CMDMON
 
 void
@@ -404,15 +428,140 @@
 }
 
 int
-NSD_GetAuthDelay(uint32_t key_id)
-{
-  return 0;
-}
-
-int
-NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
+NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
+                      NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
 {
   return 0;
 }
 
 #endif /* !FEAT_SIGND */
+
+#ifndef HAVE_CMAC
+
+int
+CMC_GetKeyLength(CMC_Algorithm algorithm)
+{
+  return 0;
+}
+
+CMC_Instance
+CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
+{
+  return NULL;
+}
+
+int
+CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
+{
+  return 0;
+}
+
+void
+CMC_DestroyInstance(CMC_Instance inst)
+{
+}
+
+#endif /* !HAVE_CMAC */
+
+#ifndef FEAT_NTS
+
+void
+NNS_Initialise(void)
+{
+}
+
+void
+NNS_Finalise(void)
+{
+}
+
+int
+NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
+{
+  *kod = 0;
+  return 0;
+}
+
+int
+NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
+                         NTP_Packet *response, NTP_PacketInfo *res_info,
+                         uint32_t kod)
+{
+  return 0;
+}
+
+NNC_Instance
+NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set,
+                   uint16_t ntp_port)
+{
+  return NULL;
+}
+
+void
+NNC_DestroyInstance(NNC_Instance inst)
+{
+}
+
+int
+NNC_PrepareForAuth(NNC_Instance inst)
+{
+  return 1;
+}
+
+int
+NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
+{
+  static int logged = 0;
+
+  LOG(logged ? LOGS_DEBUG : LOGS_WARN, "Missing NTS support");
+  logged = 1;
+  return 0;
+}
+
+int
+NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
+{
+  return 0;
+}
+
+void
+NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
+{
+}
+
+void
+NNC_DumpData(NNC_Instance inst)
+{
+}
+
+void
+NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
+{
+}
+
+void
+NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
+{
+}
+
+void
+NKS_Initialise(void)
+{
+}
+
+void
+NKS_Finalise(void)
+{
+}
+
+void
+NKS_DumpKeys(void)
+{
+}
+
+void
+NKS_ReloadKeys(void)
+{
+}
+
+#endif /* !FEAT_NTS */
diff --git a/chrony_3_3/sys.c b/chrony/sys.c
similarity index 84%
rename from chrony_3_3/sys.c
rename to chrony/sys.c
index 4d68b37..1a1a432 100644
--- a/chrony_3_3/sys.c
+++ b/chrony/sys.c
@@ -35,10 +35,13 @@
 
 #if defined(LINUX)
 #include "sys_linux.h"
+#include "sys_posix.h"
 #elif defined(SOLARIS)
 #include "sys_solaris.h"
+#include "sys_posix.h"
 #elif defined(NETBSD) || defined(FREEBSD)
 #include "sys_netbsd.h"
+#include "sys_posix.h"
 #elif defined(MACOSX)
 #include "sys_macosx.h"
 #endif
@@ -94,16 +97,16 @@
 
 /* ================================================== */
 
-void SYS_DropRoot(uid_t uid, gid_t gid)
+void SYS_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context)
 {
 #if defined(LINUX) && defined (FEAT_PRIVDROP)
-  SYS_Linux_DropRoot(uid, gid, !null_driver);
+  SYS_Linux_DropRoot(uid, gid, context, !null_driver);
 #elif defined(SOLARIS) && defined(FEAT_PRIVDROP)
-  SYS_Solaris_DropRoot(uid, gid);
+  SYS_Solaris_DropRoot(uid, gid, context);
 #elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP)
-  SYS_NetBSD_DropRoot(uid, gid);
+  SYS_NetBSD_DropRoot(uid, gid, context, !null_driver);
 #elif defined(MACOSX) && defined(FEAT_PRIVDROP)
-  SYS_MacOSX_DropRoot(uid, gid);
+  SYS_MacOSX_DropRoot(uid, gid, context);
 #else
   LOG_FATAL("dropping root privileges not supported");
 #endif
@@ -111,10 +114,10 @@
 
 /* ================================================== */
 
-void SYS_EnableSystemCallFilter(int level)
+void SYS_EnableSystemCallFilter(int level, SYS_ProcessContext context)
 {
 #if defined(LINUX) && defined(FEAT_SCFILTER)
-  SYS_Linux_EnableSystemCallFilter(level);
+  SYS_Linux_EnableSystemCallFilter(level, context);
 #else
   LOG_FATAL("system call filter not supported");
 #endif
@@ -124,10 +127,10 @@
 
 void SYS_SetScheduler(int SchedPriority)
 {
-#if defined(LINUX) && defined(HAVE_SCHED_SETSCHEDULER)
-  SYS_Linux_SetScheduler(SchedPriority);
-#elif defined(MACOSX)
+#if defined(MACOSX)
   SYS_MacOSX_SetScheduler(SchedPriority);
+#elif defined(HAVE_PTHREAD_SETSCHEDPARAM)
+  SYS_Posix_SetScheduler(SchedPriority);
 #else
   LOG_FATAL("scheduler priority setting not supported");
 #endif
@@ -137,8 +140,8 @@
 
 void SYS_LockMemory(void)
 {
-#if defined(LINUX) && defined(HAVE_MLOCKALL)
-  SYS_Linux_MemLockAll(1);
+#if defined(HAVE_MLOCKALL)
+  SYS_Posix_MemLockAll();
 #else
   LOG_FATAL("memory locking not supported");
 #endif
diff --git a/chrony_3_3/sys.h b/chrony/sys.h
similarity index 83%
rename from chrony_3_3/sys.h
rename to chrony/sys.h
index cb726f2..9272daf 100644
--- a/chrony_3_3/sys.h
+++ b/chrony/sys.h
@@ -35,12 +35,17 @@
 /* Called at the end of the run to do final clean-up */
 extern void SYS_Finalise(void);
 
-/* Drop root privileges to the specified user and group */
-extern void SYS_DropRoot(uid_t uid, gid_t gid);
+typedef enum {
+  SYS_MAIN_PROCESS,
+  SYS_NTSKE_HELPER,
+} SYS_ProcessContext;
+
+/* Switch to the specified user and group in given context */
+extern void SYS_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context);
 
 /* Enable a system call filter to allow only system calls
    which chronyd normally needs after initialization */
-extern void SYS_EnableSystemCallFilter(int level);
+extern void SYS_EnableSystemCallFilter(int level, SYS_ProcessContext context);
 
 extern void SYS_SetScheduler(int SchedPriority);
 extern void SYS_LockMemory(void);
diff --git a/chrony_3_3/sys_generic.c b/chrony/sys_generic.c
similarity index 99%
rename from chrony_3_3/sys_generic.c
rename to chrony/sys_generic.c
index 9c785d2..6e29ee2 100644
--- a/chrony_3_3/sys_generic.c
+++ b/chrony/sys_generic.c
@@ -417,6 +417,8 @@
 
   LCL_ReadRawTime(&now);
   stop_fastslew(&now);
+
+  LCL_RemoveParameterChangeHandler(handle_step, NULL);
 }
 
 /* ================================================== */
diff --git a/chrony_3_3/sys_generic.h b/chrony/sys_generic.h
similarity index 100%
rename from chrony_3_3/sys_generic.h
rename to chrony/sys_generic.h
diff --git a/chrony_3_3/sys_linux.c b/chrony/sys_linux.c
similarity index 62%
rename from chrony_3_3/sys_linux.c
rename to chrony/sys_linux.c
index f4b532d..9cab2ef 100644
--- a/chrony_3_3/sys_linux.c
+++ b/chrony/sys_linux.c
@@ -4,7 +4,7 @@
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
  * Copyright (C) John G. Hasler  2009
- * Copyright (C) Miroslav Lichvar  2009-2012, 2014-2017
+ * Copyright (C) Miroslav Lichvar  2009-2012, 2014-2018
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -33,20 +33,6 @@
 
 #include <sys/utsname.h>
 
-#if defined(HAVE_SCHED_SETSCHEDULER)
-#  include <sched.h>
-#endif
-
-#if defined(HAVE_MLOCKALL)
-#  include <sys/mman.h>
-#include <sys/resource.h>
-#endif
-
-#ifdef FEAT_PRIVDROP
-#include <sys/prctl.h>
-#include <sys/capability.h>
-#endif
-
 #if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
 #include <linux/ptp_clock.h>
 #endif
@@ -66,6 +52,11 @@
 #endif
 #endif
 
+#ifdef FEAT_PRIVDROP
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
 #include "sys_linux.h"
 #include "sys_timex.h"
 #include "conf.h"
@@ -107,21 +98,6 @@
 static int tick_update_hz;
 
 /* ================================================== */
-
-inline static long
-our_round(double x)
-{
-  long y;
-
-  if (x > 0.0)
-    y = x + 0.5;
-  else
-    y = x - 0.5;
-
-  return y;
-}
-
-/* ================================================== */
 /* Positive means currently fast of true time, i.e. jump backwards */
 
 static int
@@ -158,7 +134,7 @@
   double required_freq;
   int required_delta_tick;
 
-  required_delta_tick = our_round(freq_ppm / dhz);
+  required_delta_tick = round(freq_ppm / dhz);
 
   /* Older kernels (pre-2.6.18) don't apply the frequency offset exactly as
      set by adjtimex() and a scaling constant (that depends on the internal
@@ -309,9 +285,9 @@
   nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
   max_tick_bias = nominal_tick / 10;
 
-  /* We can't reliably detect the internal kernel HZ, it may not even be fixed
-     (CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
-  tick_update_hz = 100;
+  /* In modern kernels the frequency of the clock is updated immediately in the
+     adjtimex() system call.  Assume a maximum delay of 10 microseconds. */
+  tick_update_hz = 100000;
 
   get_kernel_version(&major, &minor, &patch);
   DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
@@ -322,9 +298,15 @@
 
   if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
       kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
-    /* Tickless kernels before 2.6.33 accumulated ticks only in
-       half-second intervals */
+    /* In tickless kernels before 2.6.33 the frequency is updated in
+       a half-second interval */
     tick_update_hz = 2;
+  } else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
+    /* In kernels before 4.19 the frequency is updated only on internal ticks
+       (CONFIG_HZ).  As their rate cannot be reliably detected from the user
+       space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
+       assume the lowest commonly used constant rate */
+    tick_update_hz = 100;
   }
 
   /* ADJ_SETOFFSET support */
@@ -334,8 +316,8 @@
     have_setoffset = 1;
   }
 
-  DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d",
-      hz, nominal_tick, max_tick_bias);
+  DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
+            hz, nominal_tick, max_tick_bias, tick_update_hz);
 }
 
 /* ================================================== */
@@ -385,7 +367,7 @@
 static void
 report_time_adjust_blockers(void)
 {
-#ifdef FEAT_PRIVDROP
+#if defined(FEAT_PRIVDROP) && defined(CAP_IS_SUPPORTED)
   if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
     return;
   LOG(LOGS_WARN, "CAP_SYS_TIME not present");
@@ -429,7 +411,7 @@
 
 #ifdef FEAT_PRIVDROP
 void
-SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
+SYS_Linux_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control)
 {
   char cap_text[256];
   cap_t cap;
@@ -440,13 +422,23 @@
   
   UTI_DropRoot(uid, gid);
 
-  /* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened
-     and keep CAP_SYS_TIME only if the clock control is enabled */
-  if (snprintf(cap_text, sizeof (cap_text), "%s %s",
-               CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "",
+  /* Keep CAP_NET_BIND_SERVICE if the NTP server sockets may need to be bound
+     to a privileged port.
+     Keep CAP_NET_RAW if an NTP socket may need to be bound to a device on
+     kernels before 5.7.
+     Keep CAP_SYS_TIME if the clock control is enabled. */
+  if (snprintf(cap_text, sizeof (cap_text), "%s %s %s",
+               (CNF_GetNTPPort() > 0 && CNF_GetNTPPort() < 1024) ?
+                 "cap_net_bind_service=ep" : "",
+               (CNF_GetBindNtpInterface() || CNF_GetBindAcquisitionInterface()) &&
+                 !SYS_Linux_CheckKernelVersion(5, 7) ? "cap_net_raw=ep" : "",
                clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
     assert(0);
 
+  /* Helpers don't need any capabilities */
+  if (context != SYS_MAIN_PROCESS)
+    cap_text[0] = '\0';
+
   if ((cap = cap_from_text(cap_text)) == NULL) {
     LOG_FATAL("cap_from_text() failed");
   }
@@ -477,37 +469,154 @@
 /* ================================================== */
 
 void
-SYS_Linux_EnableSystemCallFilter(int level)
+SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
 {
-  const int syscalls[] = {
+  const int allowed[] = {
     /* Clock */
-    SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday),
-    SCMP_SYS(settimeofday), SCMP_SYS(time),
+    SCMP_SYS(adjtimex),
+    SCMP_SYS(clock_adjtime),
+#ifdef __NR_clock_adjtime64
+    SCMP_SYS(clock_adjtime64),
+#endif
+    SCMP_SYS(clock_gettime),
+#ifdef __NR_clock_gettime64
+    SCMP_SYS(clock_gettime64),
+#endif
+    SCMP_SYS(gettimeofday),
+    SCMP_SYS(settimeofday),
+    SCMP_SYS(time),
+
     /* Process */
-    SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid),
-    SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn),
-    SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn),
+    SCMP_SYS(clone),
+#ifdef __NR_clone3
+    SCMP_SYS(clone3),
+#endif
+    SCMP_SYS(exit),
+    SCMP_SYS(exit_group),
+    SCMP_SYS(getpid),
+    SCMP_SYS(getrlimit),
+    SCMP_SYS(getuid),
+    SCMP_SYS(getuid32),
+    SCMP_SYS(rt_sigaction),
+    SCMP_SYS(rt_sigreturn),
+    SCMP_SYS(rt_sigprocmask),
+    SCMP_SYS(set_tid_address),
+    SCMP_SYS(sigreturn),
     SCMP_SYS(wait4),
+    SCMP_SYS(waitpid),
+
     /* Memory */
-    SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2),
-    SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
+    SCMP_SYS(brk),
+    SCMP_SYS(madvise),
+    SCMP_SYS(mmap),
+    SCMP_SYS(mmap2),
+    SCMP_SYS(mprotect),
+    SCMP_SYS(mremap),
+    SCMP_SYS(munmap),
+    SCMP_SYS(shmdt),
+
     /* Filesystem */
-    SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32),
-    SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64),
-    SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
-    SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
+    SCMP_SYS(_llseek),
+    SCMP_SYS(access),
+    SCMP_SYS(chmod),
+    SCMP_SYS(chown),
+    SCMP_SYS(chown32),
+    SCMP_SYS(faccessat),
+    SCMP_SYS(fchmodat),
+    SCMP_SYS(fchownat),
+    SCMP_SYS(fstat),
+    SCMP_SYS(fstat64),
+    SCMP_SYS(fstatat64),
+    SCMP_SYS(getdents),
+    SCMP_SYS(getdents64),
+    SCMP_SYS(lseek),
+    SCMP_SYS(lstat),
+    SCMP_SYS(lstat64),
+    SCMP_SYS(newfstatat),
+    SCMP_SYS(readlink),
+    SCMP_SYS(readlinkat),
+    SCMP_SYS(rename),
+    SCMP_SYS(renameat),
+#ifdef __NR_renameat2
+    SCMP_SYS(renameat2),
+#endif
+    SCMP_SYS(stat),
+    SCMP_SYS(stat64),
+    SCMP_SYS(statfs),
+    SCMP_SYS(statfs64),
+#ifdef __NR_statx
+    SCMP_SYS(statx),
+#endif
+    SCMP_SYS(unlink),
+    SCMP_SYS(unlinkat),
+
     /* Socket */
-    SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(getsockopt),
-    SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
-    SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
+    SCMP_SYS(accept),
+    SCMP_SYS(bind),
+    SCMP_SYS(connect),
+    SCMP_SYS(getsockname),
+    SCMP_SYS(getsockopt),
+    SCMP_SYS(recv),
+    SCMP_SYS(recvfrom),
+    SCMP_SYS(recvmmsg),
+#ifdef __NR_recvmmsg_time64
+    SCMP_SYS(recvmmsg_time64),
+#endif
+    SCMP_SYS(recvmsg),
+    SCMP_SYS(send),
+    SCMP_SYS(sendmmsg),
+    SCMP_SYS(sendmsg),
+    SCMP_SYS(sendto),
+    SCMP_SYS(shutdown),
     /* TODO: check socketcall arguments */
     SCMP_SYS(socketcall),
+
     /* General I/O */
-    SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe),
-    SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
-    SCMP_SYS(set_robust_list), SCMP_SYS(write),
+    SCMP_SYS(_newselect),
+    SCMP_SYS(close),
+    SCMP_SYS(open),
+    SCMP_SYS(openat),
+    SCMP_SYS(pipe),
+    SCMP_SYS(pipe2),
+    SCMP_SYS(poll),
+    SCMP_SYS(ppoll),
+#ifdef __NR_ppoll_time64
+    SCMP_SYS(ppoll_time64),
+#endif
+    SCMP_SYS(pread64),
+    SCMP_SYS(pselect6),
+#ifdef __NR_pselect6_time64
+    SCMP_SYS(pselect6_time64),
+#endif
+    SCMP_SYS(read),
+    SCMP_SYS(futex),
+#ifdef __NR_futex_time64
+    SCMP_SYS(futex_time64),
+#endif
+    SCMP_SYS(select),
+    SCMP_SYS(set_robust_list),
+    SCMP_SYS(write),
+
     /* Miscellaneous */
-    SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname),
+    SCMP_SYS(getrandom),
+    SCMP_SYS(sysinfo),
+    SCMP_SYS(uname),
+  };
+
+  const int denied_any[] = {
+    SCMP_SYS(execve),
+#ifdef __NR_execveat
+    SCMP_SYS(execveat),
+#endif
+    SCMP_SYS(fork),
+    SCMP_SYS(ptrace),
+    SCMP_SYS(vfork),
+  };
+
+  const int denied_ntske[] = {
+    SCMP_SYS(ioctl),
+    SCMP_SYS(setsockopt),
+    SCMP_SYS(socket),
   };
 
   const int socket_domains[] = {
@@ -518,18 +627,24 @@
   };
 
   const static int socket_options[][2] = {
-    { SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND },
+    { SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
 #ifdef FEAT_IPV6
     { SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
 #endif
+#ifdef SO_BINDTODEVICE
+    { SOL_SOCKET, SO_BINDTODEVICE },
+#endif
     { SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
+#ifdef SO_REUSEPORT
+    { SOL_SOCKET, SO_REUSEPORT },
+#endif
     { SOL_SOCKET, SO_TIMESTAMP }, { SOL_SOCKET, SO_TIMESTAMPNS },
 #ifdef HAVE_LINUX_TIMESTAMPING
     { SOL_SOCKET, SO_SELECT_ERR_QUEUE }, { SOL_SOCKET, SO_TIMESTAMPING },
 #endif
   };
 
-  const static int fcntls[] = { F_GETFD, F_SETFD };
+  const static int fcntls[] = { F_GETFD, F_SETFD, F_GETFL, F_SETFL };
 
   const static unsigned long ioctls[] = {
     FIONREAD, TCGETS,
@@ -538,6 +653,9 @@
 #ifdef PTP_PIN_SETFUNC
     PTP_PIN_SETFUNC,
 #endif
+#ifdef PTP_SYS_OFFSET_EXTENDED
+    PTP_SYS_OFFSET_EXTENDED,
+#endif
 #ifdef PTP_SYS_OFFSET_PRECISE
     PTP_SYS_OFFSET_PRECISE,
 #endif
@@ -553,64 +671,102 @@
 #endif
   };
 
+  unsigned int default_action, deny_action;
   scmp_filter_ctx *ctx;
   int i;
 
-  /* Check if the chronyd configuration is supported */
-  check_seccomp_applicability();
+  /* Sign of the level determines the deny action (kill or SIGSYS).
+     At level 1, selected syscalls are allowed, others are denied.
+     At level 2, selected syscalls are denied, others are allowed. */
 
-  /* Start the helper process, which will run without any seccomp filter.  It
-     will be used for getaddrinfo(), for which it's difficult to maintain a
-     list of required system calls (with glibc it depends on what NSS modules
-     are installed and enabled on the system). */
-  PRV_StartHelper();
+  deny_action = level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP;
+  if (level < 0)
+    level = -level;
 
-  ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
+  switch (level) {
+    case 1:
+      default_action = deny_action;
+      break;
+    case 2:
+      default_action = SCMP_ACT_ALLOW;
+      break;
+    default:
+      LOG_FATAL("Unsupported filter level");
+  }
+
+  if (context == SYS_MAIN_PROCESS) {
+    /* Check if the chronyd configuration is supported */
+    check_seccomp_applicability();
+
+    /* At level 1, start a helper process which will not have a seccomp filter.
+       It will be used for getaddrinfo(), for which it is difficult to maintain
+       a list of required system calls (with glibc it depends on what NSS
+       modules are installed and enabled on the system). */
+    if (default_action != SCMP_ACT_ALLOW)
+      PRV_StartHelper();
+  }
+
+  ctx = seccomp_init(default_action);
   if (ctx == NULL)
       LOG_FATAL("Failed to initialize seccomp");
 
-  /* Add system calls that are always allowed */
-  for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
-    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0)
-      goto add_failed;
+  if (default_action != SCMP_ACT_ALLOW) {
+    for (i = 0; i < sizeof (allowed) / sizeof (*allowed); i++) {
+      if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, allowed[i], 0) < 0)
+        goto add_failed;
+    }
+  } else {
+    for (i = 0; i < sizeof (denied_any) / sizeof (*denied_any); i++) {
+      if (seccomp_rule_add(ctx, deny_action, denied_any[i], 0) < 0)
+        goto add_failed;
+    }
+
+    if (context == SYS_NTSKE_HELPER) {
+      for (i = 0; i < sizeof (denied_ntske) / sizeof (*denied_ntske); i++) {
+        if (seccomp_rule_add(ctx, deny_action, denied_ntske[i], 0) < 0)
+          goto add_failed;
+      }
+    }
   }
 
-  /* Allow sockets to be created only in selected domains */
-  for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) {
-    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
-                         SCMP_A0(SCMP_CMP_EQ, socket_domains[i])) < 0)
-      goto add_failed;
-  }
+  if (default_action != SCMP_ACT_ALLOW && context == SYS_MAIN_PROCESS) {
+    /* Allow opening sockets in selected domains */
+    for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) {
+      if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
+                           SCMP_A0(SCMP_CMP_EQ, socket_domains[i])) < 0)
+        goto add_failed;
+    }
 
-  /* Allow setting only selected sockets options */
-  for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) {
-    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 3,
-                         SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]),
-                         SCMP_A2(SCMP_CMP_EQ, socket_options[i][1]),
-                         SCMP_A4(SCMP_CMP_LE, sizeof (int))) < 0)
-      goto add_failed;
-  }
+    /* Allow selected socket options */
+    for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) {
+      if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 2,
+                           SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]),
+                           SCMP_A2(SCMP_CMP_EQ, socket_options[i][1])))
+        goto add_failed;
+    }
 
-  /* Allow only selected fcntl calls */
-  for (i = 0; i < sizeof (fcntls) / sizeof (*fcntls); i++) {
-    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 1,
-                         SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0 ||
-        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
-                         SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0)
-      goto add_failed;
-  }
+    /* Allow selected fcntl calls */
+    for (i = 0; i < sizeof (fcntls) / sizeof (*fcntls); i++) {
+      if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 1,
+                           SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0 ||
+          seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
+                           SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0)
+        goto add_failed;
+    }
 
-  /* Allow only selected ioctls */
-  for (i = 0; i < sizeof (ioctls) / sizeof (*ioctls); i++) {
-    if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
-                         SCMP_A1(SCMP_CMP_EQ, ioctls[i])) < 0)
-      goto add_failed;
+    /* Allow selected ioctls */
+    for (i = 0; i < sizeof (ioctls) / sizeof (*ioctls); i++) {
+      if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
+                           SCMP_A1(SCMP_CMP_EQ, ioctls[i])) < 0)
+        goto add_failed;
+    }
   }
 
   if (seccomp_load(ctx) < 0)
     LOG_FATAL("Failed to load seccomp rules");
 
-  LOG(LOGS_INFO, "Loaded seccomp filter");
+  LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG,
+      "Loaded seccomp filter (level %d)", level);
   seccomp_release(ctx);
   return;
 
@@ -621,63 +777,6 @@
 
 /* ================================================== */
 
-#if defined(HAVE_SCHED_SETSCHEDULER)
-  /* Install SCHED_FIFO real-time scheduler with specified priority */
-void SYS_Linux_SetScheduler(int SchedPriority)
-{
-  int pmax, pmin;
-  struct sched_param sched;
-
-  if (SchedPriority < 1 || SchedPriority > 99) {
-    LOG_FATAL("Bad scheduler priority: %d", SchedPriority);
-  } else {
-    sched.sched_priority = SchedPriority;
-    pmax = sched_get_priority_max(SCHED_FIFO);
-    pmin = sched_get_priority_min(SCHED_FIFO);
-    if ( SchedPriority > pmax ) {
-      sched.sched_priority = pmax;
-    }
-    else if ( SchedPriority < pmin ) {
-      sched.sched_priority = pmin;
-    }
-    if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) {
-      LOG(LOGS_ERR, "sched_setscheduler() failed");
-    }
-    else {
-      DEBUG_LOG("Enabled SCHED_FIFO with priority %d",
-          sched.sched_priority);
-    }
-  }
-}
-#endif /* HAVE_SCHED_SETSCHEDULER */
-
-#if defined(HAVE_MLOCKALL)
-/* Lock the process into RAM so that it will never be swapped out */ 
-void SYS_Linux_MemLockAll(int LockAll)
-{
-  struct rlimit rlim;
-  if (LockAll == 1 ) {
-    /* Make sure that we will be able to lock all the memory we need */
-    /* even after dropping privileges.  This does not actually reaerve any memory */
-    rlim.rlim_max = RLIM_INFINITY;
-    rlim.rlim_cur = RLIM_INFINITY;
-    if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
-      LOG(LOGS_ERR, "setrlimit() failed: not locking into RAM");
-    }
-    else {
-      if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
-	LOG(LOGS_ERR, "mlockall() failed");
-      }
-      else {
-	DEBUG_LOG("Successfully locked into RAM");
-      }
-    }
-  }
-}
-#endif /* HAVE_MLOCKALL */
-
-/* ================================================== */
-
 int
 SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
 {
@@ -695,35 +794,17 @@
 #define PHC_READINGS 10
 
 static int
-get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
-               struct timespec *sys_ts, double *err)
+process_phc_readings(struct timespec ts[][3], int n, double precision,
+                     struct timespec *phc_ts, struct timespec *sys_ts, double *err)
 {
-  struct ptp_sys_offset sys_off;
-  struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
-  double min_delay = 0.0, delays[PHC_READINGS], phc_sum, sys_sum, sys_prec;
-  int i, n;
+  double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
+  int i, combined;
 
-  /* Silence valgrind */
-  memset(&sys_off, 0, sizeof (sys_off));
-
-  sys_off.n_samples = PHC_READINGS;
-
-  if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
-    DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
+  if (n > PTP_MAX_SAMPLES)
     return 0;
-  }
 
-  for (i = 0; i < PHC_READINGS; i++) {
-    ts1.tv_sec = sys_off.ts[i * 2].sec;
-    ts1.tv_nsec = sys_off.ts[i * 2].nsec;
-    ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
-    ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
-    ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
-    ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
-
-    sys_tss[i] = ts1;
-    phc_tss[i] = ts2;
-    delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
+  for (i = 0; i < n; i++) {
+    delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
 
     if (delays[i] < 0.0) {
       /* Step in the middle of a PHC reading? */
@@ -738,23 +819,92 @@
   sys_prec = LCL_GetSysPrecisionAsQuantum();
 
   /* Combine best readings */
-  for (i = n = 0, phc_sum = sys_sum = 0.0; i < PHC_READINGS; i++) {
+  for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
     if (delays[i] > min_delay + MAX(sys_prec, precision))
       continue;
 
-    phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
-    sys_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
-    n++;
+    phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
+    sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
+    combined++;
   }
 
-  assert(n);
+  assert(combined);
 
-  UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
-  UTI_AddDoubleToTimespec(&sys_tss[0], sys_sum / n, sys_ts);
+  UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
+  UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
   *err = MAX(min_delay / 2.0, precision);
 
   return 1;
 }
+
+/* ================================================== */
+
+static int
+get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
+               struct timespec *sys_ts, double *err)
+{
+  struct timespec ts[PHC_READINGS][3];
+  struct ptp_sys_offset sys_off;
+  int i;
+
+  /* Silence valgrind */
+  memset(&sys_off, 0, sizeof (sys_off));
+
+  sys_off.n_samples = PHC_READINGS;
+
+  if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
+    DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
+    return 0;
+  }
+
+  for (i = 0; i < PHC_READINGS; i++) {
+    ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
+    ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
+    ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
+    ts[i][1].tv_nsec = sys_off.ts[i * 2 + 1].nsec;
+    ts[i][2].tv_sec = sys_off.ts[i * 2 + 2].sec;
+    ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
+  }
+
+  return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
+}
+
+/* ================================================== */
+
+static int
+get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
+                        struct timespec *sys_ts, double *err)
+{
+#ifdef PTP_SYS_OFFSET_EXTENDED
+  struct timespec ts[PHC_READINGS][3];
+  struct ptp_sys_offset_extended sys_off;
+  int i;
+
+  /* Silence valgrind */
+  memset(&sys_off, 0, sizeof (sys_off));
+
+  sys_off.n_samples = PHC_READINGS;
+
+  if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
+    DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
+    return 0;
+  }
+
+  for (i = 0; i < PHC_READINGS; i++) {
+    ts[i][0].tv_sec = sys_off.ts[i][0].sec;
+    ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
+    ts[i][1].tv_sec = sys_off.ts[i][1].sec;
+    ts[i][1].tv_nsec = sys_off.ts[i][1].nsec;
+    ts[i][2].tv_sec = sys_off.ts[i][2].sec;
+    ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
+  }
+
+  return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
+#else
+  return 0;
+#endif
+}
+
 /* ================================================== */
 
 static int
@@ -828,6 +978,10 @@
       get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
     *reading_mode = 2;
     return 1;
+  } else if ((*reading_mode == 3 || !*reading_mode) &&
+      get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
+    *reading_mode = 3;
+    return 1;
   } else if ((*reading_mode == 1 || !*reading_mode) &&
       get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
     *reading_mode = 1;
diff --git a/chrony_3_3/sys_linux.h b/chrony/sys_linux.h
similarity index 88%
rename from chrony_3_3/sys_linux.h
rename to chrony/sys_linux.h
index 799ae9a..b09ec31 100644
--- a/chrony_3_3/sys_linux.h
+++ b/chrony/sys_linux.h
@@ -27,17 +27,15 @@
 #ifndef GOT_SYS_LINUX_H
 #define GOT_SYS_LINUX_H
 
+#include "sys.h"
+
 extern void SYS_Linux_Initialise(void);
 
 extern void SYS_Linux_Finalise(void);
 
-extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control);
+extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control);
 
-extern void SYS_Linux_EnableSystemCallFilter(int level);
-
-extern void SYS_Linux_MemLockAll(int LockAll);
-
-extern void SYS_Linux_SetScheduler(int SchedPriority);
+extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context);
 
 extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
 
diff --git a/chrony_3_3/sys_macosx.c b/chrony/sys_macosx.c
similarity index 92%
rename from chrony_3_3/sys_macosx.c
rename to chrony/sys_macosx.c
index 00ce302..e3a38ed 100644
--- a/chrony_3_3/sys_macosx.c
+++ b/chrony/sys_macosx.c
@@ -4,7 +4,7 @@
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2001
  * Copyright (C) J. Hannken-Illjes  2001
- * Copyright (C) Bryan Christianson  2015, 2017
+ * Copyright (C) Bryan Christianson  2015, 2017, 2020
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -46,13 +46,12 @@
 #include "privops.h"
 #include "util.h"
 
-#ifdef HAVE_MACOS_SYS_TIMEX
 #include <dlfcn.h>
+
+#ifdef HAVE_MACOS_SYS_TIMEX
 #include "sys_netbsd.h"
-#include "sys_timex.h"
 
 static int have_ntp_adjtime = 0;
-static int have_bad_adjtime = 0;
 #endif
 
 /* ================================================== */
@@ -416,9 +415,10 @@
 /* ================================================== */
 
 #ifdef FEAT_PRIVDROP
-void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid)
+void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context)
 {
-  PRV_StartHelper();
+  if (context == SYS_MAIN_PROCESS)
+    PRV_StartHelper();
 
   UTI_DropRoot(uid, gid);
 }
@@ -453,27 +453,34 @@
 
 /* ================================================== */
 
-#ifdef HAVE_MACOS_SYS_TIMEX
-/*
-    Test adjtime() to see if Apple have fixed the signed/unsigned bug
-*/
-static int
-test_adjtime()
+#if HAVE_CLOCK_GETTIME
+int
+clock_gettime(clockid_t clock_id, struct timespec *ts)
 {
-  struct timeval tv1 = {-1, 0};
-  struct timeval tv2 = {0, 0};
-  struct timeval tv;
+  /* Check that the system clock_gettime symbol is actually present before
+     attempting to call it. The symbol is available in macOS 10.12
+     and later. */
 
-  if (PRV_AdjustTime(&tv1, &tv) != 0) {
-    return 0;
+  static int init = 0;
+  static int (*sys_clock_gettime)(clockid_t, struct timespec *) = NULL;
+  int ret = 0;
+
+  if (!init) {
+    sys_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime");
+    init = 1;
   }
-  if (PRV_AdjustTime(&tv2, &tv) != 0) {
-    return 0;
+
+  if (sys_clock_gettime != NULL) {
+    ret = sys_clock_gettime(clock_id, ts);
+  } else {
+    struct timeval tv;
+
+    if (gettimeofday(&tv, NULL) < 0)
+      LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
+
+    UTI_TimevalToTimespec(&tv, ts);
   }
-  if (tv.tv_sec < -1 || tv.tv_sec > 1) {
-    return 0;
-  }
-  return 1;
+  return ret;
 }
 #endif
 
@@ -485,13 +492,7 @@
 #ifdef HAVE_MACOS_SYS_TIMEX
   have_ntp_adjtime = (dlsym(RTLD_NEXT, "ntp_adjtime") != NULL);
   if (have_ntp_adjtime) {
-    have_bad_adjtime = !test_adjtime();
-    if (have_bad_adjtime) {
-      LOG(LOGS_WARN, "adjtime() is buggy - using timex driver");
-      SYS_Timex_Initialise();
-    } else {
-      SYS_NetBSD_Initialise();
-    }
+    SYS_NetBSD_Initialise();
     return;
   }
 #endif
@@ -505,11 +506,7 @@
 {
 #ifdef HAVE_MACOS_SYS_TIMEX
   if (have_ntp_adjtime) {
-    if (have_bad_adjtime) {
-      SYS_Timex_Finalise();
-    } else {
-      SYS_NetBSD_Finalise();
-    }
+    SYS_NetBSD_Finalise();
     return;
   }
 #endif
diff --git a/chrony_3_3/sys_macosx.h b/chrony/sys_macosx.h
similarity index 92%
rename from chrony_3_3/sys_macosx.h
rename to chrony/sys_macosx.h
index 5555616..09f0beb 100644
--- a/chrony_3_3/sys_macosx.h
+++ b/chrony/sys_macosx.h
@@ -30,8 +30,10 @@
 #ifndef GOT_SYS_MACOSX_H
 #define GOT_SYS_MACOSX_H
 
+#include "sys.h"
+
 void SYS_MacOSX_SetScheduler(int SchedPriority);
-void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid);
+void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context);
 void SYS_MacOSX_Initialise(void);
 void SYS_MacOSX_Finalise(void);
 
diff --git a/chrony_3_3/sys_netbsd.c b/chrony/sys_netbsd.c
similarity index 95%
rename from chrony_3_3/sys_netbsd.c
rename to chrony/sys_netbsd.c
index 840d6a5..e1b99bb 100644
--- a/chrony_3_3/sys_netbsd.c
+++ b/chrony/sys_netbsd.c
@@ -131,7 +131,7 @@
 
 #ifdef FEAT_PRIVDROP
 void
-SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
+SYS_NetBSD_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control)
 {
 #ifdef NETBSD
   int fd;
@@ -139,11 +139,15 @@
 
   /* On NetBSD the helper is used only for socket binding, but on FreeBSD
      it's used also for setting and adjusting the system clock */
-  PRV_StartHelper();
+  if (context == SYS_MAIN_PROCESS)
+    PRV_StartHelper();
 
   UTI_DropRoot(uid, gid);
 
 #ifdef NETBSD
+  if (!clock_control)
+    return;
+
   /* Check if we have write access to /dev/clockctl */
   fd = open("/dev/clockctl", O_WRONLY);
   if (fd < 0)
diff --git a/chrony_3_3/sys_netbsd.h b/chrony/sys_netbsd.h
similarity index 91%
rename from chrony_3_3/sys_netbsd.h
rename to chrony/sys_netbsd.h
index 052f5b7..7a05e6c 100644
--- a/chrony_3_3/sys_netbsd.h
+++ b/chrony/sys_netbsd.h
@@ -28,10 +28,12 @@
 #ifndef GOT_SYS_NETBSD_H
 #define GOT_SYS_NETBSD_H
 
+#include "sys.h"
+
 void SYS_NetBSD_Initialise(void);
 
 void SYS_NetBSD_Finalise(void);
 
-void SYS_NetBSD_DropRoot(uid_t uid, gid_t gid);
+void SYS_NetBSD_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context, int clock_control);
 
 #endif
diff --git a/chrony_3_3/sys_null.c b/chrony/sys_null.c
similarity index 100%
rename from chrony_3_3/sys_null.c
rename to chrony/sys_null.c
diff --git a/chrony_3_3/sys_null.h b/chrony/sys_null.h
similarity index 100%
rename from chrony_3_3/sys_null.h
rename to chrony/sys_null.h
diff --git a/chrony/sys_posix.c b/chrony/sys_posix.c
new file mode 100644
index 0000000..356e86a
--- /dev/null
+++ b/chrony/sys_posix.c
@@ -0,0 +1,109 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * Copyright (C) John G. Hasler  2009
+ * Copyright (C) Miroslav Lichvar  2009-2012, 2014-2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ **********************************************************************
+
+  =======================================================================
+
+  This module is for POSIX compliant operating systems.
+
+  */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include <sys/utsname.h>
+
+#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#if defined(HAVE_MLOCKALL)
+#include <sys/mman.h>
+#endif
+#if defined(HAVE_SETRLIMIT_MEMLOCK)
+#include <sys/resource.h>
+#endif
+
+#include "sys_posix.h"
+#include "conf.h"
+#include "local.h"
+#include "logging.h"
+#include "util.h"
+
+/* ================================================== */
+
+#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
+/* Install SCHED_FIFO real-time scheduler with specified priority */
+void
+SYS_Posix_SetScheduler(int priority)
+{
+  struct sched_param sched;
+  int pmax, pmin;
+
+  if (priority < 1 || priority > 99)
+    LOG_FATAL("Bad scheduler priority: %d", priority);
+
+  sched.sched_priority = priority;
+  pmax = sched_get_priority_max(SCHED_FIFO);
+  pmin = sched_get_priority_min(SCHED_FIFO);
+  if (priority > pmax) {
+    sched.sched_priority = pmax;
+  } else if (priority < pmin) {
+    sched.sched_priority = pmin;
+  }
+
+  if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched) < 0) {
+    LOG(LOGS_ERR, "pthread_setschedparam() failed");
+  } else {
+    DEBUG_LOG("Enabled SCHED_FIFO with priority %d", sched.sched_priority);
+  }
+}
+#endif /* HAVE_PTHREAD_SETSCHEDPARAM  */
+
+/* ================================================== */
+
+#if defined(HAVE_MLOCKALL)
+/* Lock the process into RAM so that it will never be swapped out */
+void
+SYS_Posix_MemLockAll(void)
+{
+#if defined(HAVE_SETRLIMIT_MEMLOCK)
+  struct rlimit rlim;
+
+  /* Ensure we can reserve as much as we need */
+  rlim.rlim_max = RLIM_INFINITY;
+  rlim.rlim_cur = RLIM_INFINITY;
+  if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
+    LOG(LOGS_ERR, "setrlimit() failed");
+    return;
+  }
+#endif
+
+  if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
+    LOG(LOGS_ERR, "mlockall() failed");
+  } else {
+    DEBUG_LOG("Successfully locked into RAM");
+  }
+}
+#endif /* HAVE_MLOCKALL */
diff --git a/chrony_3_3/sys_macosx.h b/chrony/sys_posix.h
similarity index 71%
copy from chrony_3_3/sys_macosx.h
copy to chrony/sys_posix.h
index 5555616..bb34b80 100644
--- a/chrony_3_3/sys_macosx.h
+++ b/chrony/sys_posix.h
@@ -2,9 +2,9 @@
   chronyd/chronyc - Programs for keeping computer clocks accurate.
 
  **********************************************************************
- * Copyright (C) Richard P. Curnow  1997-2001
- * Copyright (C) J. Hannken-Illjes  2001
- * Copyright (C) Bryan Christianson 2015
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * Copyright (C) John G. Hasler  2009
+ * Copyright (C) Miroslav Lichvar  2009-2012, 2014-2018
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -23,16 +23,14 @@
 
   =======================================================================
 
-  Header file for macOS driver
-
+  The header file for shared Posix functionality
   */
 
-#ifndef GOT_SYS_MACOSX_H
-#define GOT_SYS_MACOSX_H
+#ifndef GOT_SYS_POSIX_H
+#define GOT_SYS_POSIX_H
 
-void SYS_MacOSX_SetScheduler(int SchedPriority);
-void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid);
-void SYS_MacOSX_Initialise(void);
-void SYS_MacOSX_Finalise(void);
+extern void SYS_Posix_MemLockAll(void);
 
-#endif
+extern void SYS_Posix_SetScheduler(int priority);
+
+#endif  /* GOT_SYS_POSIX_H */
diff --git a/chrony_3_3/sys_solaris.c b/chrony/sys_solaris.c
similarity index 61%
rename from chrony_3_3/sys_solaris.c
rename to chrony/sys_solaris.c
index 21197b9..3210ef0 100644
--- a/chrony_3_3/sys_solaris.c
+++ b/chrony/sys_solaris.c
@@ -21,23 +21,54 @@
 
   =======================================================================
 
-  Driver file for Solaris operating system
+  Driver file for illumos operating system (previously Solaris)
   */
 
 #include "config.h"
 
 #include "sysincl.h"
 
+#include "logging.h"
 #include "privops.h"
 #include "sys_solaris.h"
 #include "sys_timex.h"
 #include "util.h"
 
+#include <kvm.h>
+#include <nlist.h>
+
+/* ================================================== */
+
+static void
+set_dosynctodr(int on_off)
+{
+  struct nlist nl[] = { {"dosynctodr"}, {NULL} };
+  kvm_t *kt;
+
+  kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
+  if (!kt)
+    LOG_FATAL("Could not open kvm");
+
+  if (kvm_nlist(kt, nl) < 0 || !nl[0].n_value)
+    LOG_FATAL("Could not get dosynctodr address");
+
+  if (kvm_kwrite(kt, nl[0].n_value, &on_off, sizeof (on_off)) < 0)
+    LOG_FATAL("Could not write to dosynctodr");
+
+  kvm_close(kt);
+}
+
 /* ================================================== */
 
 void
 SYS_Solaris_Initialise(void)
 {
+  /* The kernel keeps the system clock and hardware clock synchronised to each
+     other.  The dosynctodr variable needs to be set to zero to prevent the
+     the system clock from following the hardware clock when the system clock
+     is not adjusted by adjtime() or ntp_adjtime(modes=MOD_OFFSET). */
+  set_dosynctodr(0);
+
   /* The kernel allows the frequency to be set in the full range off int32_t */
   SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
                                     0.0, 0.0, NULL, NULL);
@@ -55,9 +86,10 @@
 
 #ifdef FEAT_PRIVDROP
 void
-SYS_Solaris_DropRoot(uid_t uid, gid_t gid)
+SYS_Solaris_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context)
 {
-  PRV_StartHelper();
+  if (context == SYS_MAIN_PROCESS)
+    PRV_StartHelper();
   UTI_DropRoot(uid, gid);
 }
 #endif
diff --git a/chrony_3_3/sys_solaris.h b/chrony/sys_solaris.h
similarity index 92%
rename from chrony_3_3/sys_solaris.h
rename to chrony/sys_solaris.h
index 46015ba..5979232 100644
--- a/chrony_3_3/sys_solaris.h
+++ b/chrony/sys_solaris.h
@@ -27,10 +27,12 @@
 #ifndef GOT_SYS_SOLARIS_H
 #define GOT_SYS_SOLARIS_H
 
+#include "sys.h"
+
 void SYS_Solaris_Initialise(void);
 
 void SYS_Solaris_Finalise(void);
 
-void SYS_Solaris_DropRoot(uid_t uid, gid_t gid);
+void SYS_Solaris_DropRoot(uid_t uid, gid_t gid, SYS_ProcessContext context);
 
 #endif
diff --git a/chrony_3_3/sys_timex.c b/chrony/sys_timex.c
similarity index 93%
rename from chrony_3_3/sys_timex.c
rename to chrony/sys_timex.c
index e54ad24..0ee6c8e 100644
--- a/chrony_3_3/sys_timex.c
+++ b/chrony/sys_timex.c
@@ -69,6 +69,18 @@
 /* ================================================== */
 
 static double
+convert_timex_frequency(const struct timex *txc)
+{
+  double freq_ppm;
+
+  freq_ppm = txc->freq / FREQ_SCALE;
+
+  return -freq_ppm;
+}
+
+/* ================================================== */
+
+static double
 read_frequency(void)
 {
   struct timex txc;
@@ -77,7 +89,7 @@
 
   SYS_Timex_Adjust(&txc, 0);
 
-  return txc.freq / -FREQ_SCALE;
+  return convert_timex_frequency(&txc);
 }
 
 /* ================================================== */
@@ -92,7 +104,7 @@
 
   SYS_Timex_Adjust(&txc, 0);
 
-  return txc.freq / -FREQ_SCALE;
+  return convert_timex_frequency(&txc);
 }
 
 /* ================================================== */
@@ -256,10 +268,8 @@
   state = NTP_ADJTIME(txc);
 
   if (state < 0) {
-    if (!ignore_error)
-      LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
-    else
-      DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
+    LOG(ignore_error ? LOGS_DEBUG : LOGS_FATAL,
+        NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
   }
 
   return state;
diff --git a/chrony_3_3/sys_timex.h b/chrony/sys_timex.h
similarity index 100%
rename from chrony_3_3/sys_timex.h
rename to chrony/sys_timex.h
diff --git a/chrony_3_3/sysincl.h b/chrony/sysincl.h
similarity index 84%
rename from chrony_3_3/sysincl.h
rename to chrony/sysincl.h
index a9e4da0..e26b236 100644
--- a/chrony_3_3/sysincl.h
+++ b/chrony/sysincl.h
@@ -21,14 +21,14 @@
 
   =======================================================================
 
-  This file includes all system header files that the software
-  requires.  This allows us to isolate system dependencies to this file
-  alone.
+  This file includes most system header files that the software
+  requires to better isolate system dependencies.
   */
 
 #ifndef GOT_SYSINCL_H
 #define GOT_SYSINCL_H
 
+#include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
@@ -36,11 +36,11 @@
 #include <float.h>
 #include <glob.h>
 #include <grp.h>
+#include <inttypes.h>
+#include <limits.h>
 #include <math.h>
-#include <netdb.h>
 #include <netinet/in.h>
 #include <pwd.h>
-#include <resolv.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stddef.h>
@@ -55,7 +55,6 @@
 #include <sys/un.h>
 #include <sys/shm.h>
 #include <sys/wait.h>
-#include <syslog.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -63,19 +62,6 @@
 #include <sys/timex.h>
 #endif
 
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#elif HAVE_STDINT_H
-#include <stdint.h>
-#else
-/* Tough */
-#endif
-
-#ifdef FEAT_IPV6
-/* For inet_ntop() */
-#include <arpa/inet.h>
-#endif
-
 #ifdef HAVE_GETRANDOM
 #include <sys/random.h>
 #endif
diff --git a/chrony_3_3/tempcomp.c b/chrony/tempcomp.c
similarity index 96%
rename from chrony_3_3/tempcomp.c
rename to chrony/tempcomp.c
index f57e5cc..a468e33 100644
--- a/chrony_3_3/tempcomp.c
+++ b/chrony/tempcomp.c
@@ -84,7 +84,7 @@
   FILE *f;
   double temp, comp;
 
-  f = fopen(filename, "r");
+  f = UTI_OpenFile(NULL, filename, NULL, 'r', 0);
 
   if (f && fscanf(f, "%lf", &temp) == 1) {
     comp = get_tempcomp(temp);
@@ -122,11 +122,7 @@
   char line[256];
   struct Point *p;
 
-  f = fopen(filename, "r");
-  if (!f) {
-    LOG_FATAL("Could not open tempcomp point file %s", filename);
-    return;
-  }
+  f = UTI_OpenFile(NULL, filename, NULL, 'R', 0);
 
   points = ARR_CreateInstance(sizeof (struct Point));
 
diff --git a/chrony_3_3/tempcomp.h b/chrony/tempcomp.h
similarity index 100%
rename from chrony_3_3/tempcomp.h
rename to chrony/tempcomp.h
diff --git a/chrony_3_3/test/compilation/001-features b/chrony/test/compilation/001-features
similarity index 63%
rename from chrony_3_3/test/compilation/001-features
rename to chrony/test/compilation/001-features
index d61c07f..9bd340f 100755
--- a/chrony_3_3/test/compilation/001-features
+++ b/chrony/test/compilation/001-features
@@ -4,6 +4,8 @@
 
 cd ../..
 
+export CFLAGS="-O2 -Werror -Wpointer-arith -Wformat-signedness -Wno-unknown-warning-option -D_FORTIFY_SOURCE=2"
+
 for opts in \
 	"--enable-debug" \
 	"--enable-ntp-signd" \
@@ -15,14 +17,20 @@
 	"--disable-rtc" \
 	"--disable-sechash" \
 	"--disable-cmdmon" \
+	"--disable-cmdmon --enable-scfilter" \
 	"--disable-ntp" \
+	"--disable-ntp --enable-scfilter" \
+	"--disable-nts" \
 	"--disable-refclock" \
 	"--disable-timestamping" \
 	"--disable-timestamping --disable-ntp" \
 	"--disable-cmdmon --disable-ntp" \
+	"--disable-cmdmon --disable-ntp --enable-scfilter" \
 	"--disable-cmdmon --disable-refclock" \
 	"--disable-cmdmon --disable-ntp --disable-refclock"
 do
-	./configure $opts
+	./configure $opts || exit 1
+	make clean
 	make "$@" || exit 1
+	make -C test/unit check || exit 1
 done
diff --git a/chrony_3_3/test/compilation/002-scanbuild b/chrony/test/compilation/002-scanbuild
similarity index 64%
rename from chrony_3_3/test/compilation/002-scanbuild
rename to chrony/test/compilation/002-scanbuild
index da87407..35a3faf 100755
--- a/chrony_3_3/test/compilation/002-scanbuild
+++ b/chrony/test/compilation/002-scanbuild
@@ -8,7 +8,8 @@
 	"--host-system=FreeBSD" \
 	"--without-nettle" \
 	"--without-nettle --without-nss" \
-	"--without-nettle --without-nss --without-tomcrypt"
+	"--without-nettle --without-nss --without-tomcrypt" \
+	"--without-nettle --without-nss --without-tomcrypt --without-gnutls"
 do
 	./configure $opts
 	scan-build make "$@" || exit 1
diff --git a/chrony/test/compilation/003-sanitizers b/chrony/test/compilation/003-sanitizers
new file mode 100755
index 0000000..2cf5258
--- /dev/null
+++ b/chrony/test/compilation/003-sanitizers
@@ -0,0 +1,101 @@
+#!/usr/bin/env bash
+# Run the unit and simulation tests with different compiler sanitizers
+# and under valgrind
+
+cd ../..
+
+if [ "$(uname -sm)" != "Linux x86_64" ]; then
+  echo Test supported on Linux x86_64 only
+  exit 1
+fi
+
+[ -f /etc/os-release ] && . /etc/os-release
+
+if [ "$ID" = "fedora" ]; then
+  echo Checking test dependencies:
+  rpm -q {gcc,clang}.x86_64 {valgrind,libgcc,clang-libs}.{x86_64,i686} || exit 1
+  rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt,gnutls}-devel.{x86_64,i686} || exit 1
+  echo
+fi
+
+touch Makefile
+
+for extra_config_opts in \
+  "--all-privops" \
+  "--disable-ipv6" \
+  "--disable-scfilter" \
+  "--without-gnutls" \
+  "--without-nettle" \
+  "--without-nettle --without-nss" \
+  "--without-nettle --without-nss --without-tomcrypt" \
+  "--without-nettle --without-nss --without-tomcrypt --without-gnutls"; \
+do
+  for arch_opts in "-m32" ""; do
+    pushd test/simulation/clknetsim || exit 1
+    make clean > /dev/null 2>&1
+    CFLAGS="$arch_opts -DCLKNETSIM_DISABLE_SYSCALL" make "$@" || exit 1
+    echo
+
+    popd
+
+    for CC in gcc clang; do
+      export CC
+
+      for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
+        export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
+
+        # clang msan doesn't work on i686 and otherwise requires patches
+        echo $CFLAGS | grep -q 'sanitize=memory' && continue
+
+        [ -n "$TEST_NO_M32_CLANG" -a "$arch_opts" = "-m32" -a "$CC" = "clang" ] && continue
+
+        [ -n "$TEST_GCC_STATIC_ASAN" -a "$CC" = "gcc" ] &&
+                echo $CFLAGS | grep -q 'sanitize=address' && CFLAGS="$CFLAGS -static-libasan"
+
+        config_opts="--with-user=chrony --with-ntp-era=1000000000 --enable-debug --enable-scfilter --enable-ntp-signd $extra_config_opts"
+
+        echo -----------------------------------------------------------------------------
+        echo CC=\"$CC\" CFLAGS=\"$CFLAGS\" ./configure $config_opts
+
+        make distclean > /dev/null 2>&1
+
+        ./configure $config_opts || exit 1
+
+        if echo "$config_opts" | grep -q all-privops; then
+          for op in ADJUSTTIME ADJUSTTIMEX SETTIME BINDSOCKET; do
+            echo "#define PRIVOPS_$op 1" >> config.h
+          done
+        fi
+
+        make "$@" || exit 1
+
+        [ -n "$TEST_BUILD_ONLY" ] && continue
+
+        echo
+        pushd test/unit || exit 1
+        make "$@" || exit 1
+        if [ "$san_options" = "" ]; then
+          make check TEST_WRAPPER="valgrind --error-exitcode=1" || exit 1
+        else
+          make check || exit 1
+        fi
+        popd
+
+        [ -n "$TEST_UNIT_ONLY" ] && continue
+
+        echo
+        pushd test/simulation || exit 1
+        export CLKNETSIM_RANDOM_SEED=101
+        if [ "$arch_opts" = "" -a "$san_options" = "" ]; then
+          CLKNETSIM_CLIENT_WRAPPER=valgrind ./run -i 1 || exit 1
+        elif [ "$CC" = "gcc" ] && ! echo $CFLAGS | grep -q "-static-libasan"; then
+          libasan=$(ldd ../../chronyd | grep -o '/.*lib.*/libasan.so.[0-9]')
+          CLKNETSIM_PRELOAD=$libasan ./run -i 1 || exit 1
+        else
+          ./run -i 1 || exit 1
+        fi
+        popd
+      done
+    done
+  done
+done
diff --git a/chrony_3_3/test/kernel/Makefile b/chrony/test/kernel/Makefile
similarity index 100%
rename from chrony_3_3/test/kernel/Makefile
rename to chrony/test/kernel/Makefile
diff --git a/chrony_3_3/test/kernel/adjtime.c b/chrony/test/kernel/adjtime.c
similarity index 100%
rename from chrony_3_3/test/kernel/adjtime.c
rename to chrony/test/kernel/adjtime.c
diff --git a/chrony_3_3/test/kernel/ntpadjtime.c b/chrony/test/kernel/ntpadjtime.c
similarity index 91%
rename from chrony_3_3/test/kernel/ntpadjtime.c
rename to chrony/test/kernel/ntpadjtime.c
index d6be154..4af96b4 100644
--- a/chrony_3_3/test/kernel/ntpadjtime.c
+++ b/chrony/test/kernel/ntpadjtime.c
@@ -52,15 +52,15 @@
 
   printf("freq range:\n");
 
-  for (i = 0; i <= 1000; i += 50) {
+  for (i = -1000; i <= 1000; i += 50) {
     t.modes = MOD_FREQUENCY;
-    t.freq = i << 16;
+    t.freq = i * (1 << 16);
     printf("%4d ppm => ", i);
     if (try_ntpadjtime(&t) < 0)
       continue;
 
     printf("%4ld ppm : ", t.freq / (1 << 16));
-    printf("%s\n", t.freq == i << 16 ? "ok" : "fail");
+    printf("%s\n", t.freq == i * (1 << 16) ? "ok" : "fail");
   }
 }
 
diff --git a/chrony_3_3/test/simulation/001-defaults b/chrony/test/simulation/001-defaults
similarity index 91%
rename from chrony_3_3/test/simulation/001-defaults
rename to chrony/test/simulation/001-defaults
index 541cdad..b39d95c 100755
--- a/chrony_3_3/test/simulation/001-defaults
+++ b/chrony/test/simulation/001-defaults
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/002-largenetwork b/chrony/test/simulation/002-largenetwork
similarity index 93%
rename from chrony_3_3/test/simulation/002-largenetwork
rename to chrony/test/simulation/002-largenetwork
index fd41106..a9e0ad8 100755
--- a/chrony_3_3/test/simulation/002-largenetwork
+++ b/chrony/test/simulation/002-largenetwork
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/003-largefreqoffset b/chrony/test/simulation/003-largefreqoffset
similarity index 95%
rename from chrony_3_3/test/simulation/003-largefreqoffset
rename to chrony/test/simulation/003-largefreqoffset
index 7210662..9381b1a 100755
--- a/chrony_3_3/test/simulation/003-largefreqoffset
+++ b/chrony/test/simulation/003-largefreqoffset
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/004-largetimeoffset b/chrony/test/simulation/004-largetimeoffset
similarity index 93%
rename from chrony_3_3/test/simulation/004-largetimeoffset
rename to chrony/test/simulation/004-largetimeoffset
index 5d8e2b6..4aebdd3 100755
--- a/chrony_3_3/test/simulation/004-largetimeoffset
+++ b/chrony/test/simulation/004-largetimeoffset
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/005-externalstep b/chrony/test/simulation/005-externalstep
similarity index 97%
rename from chrony_3_3/test/simulation/005-externalstep
rename to chrony/test/simulation/005-externalstep
index 709c8a8..e6fff26 100755
--- a/chrony_3_3/test/simulation/005-externalstep
+++ b/chrony/test/simulation/005-externalstep
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/006-largejitter b/chrony/test/simulation/006-largejitter
similarity index 93%
rename from chrony_3_3/test/simulation/006-largejitter
rename to chrony/test/simulation/006-largejitter
index f70e63c..36ae5e2 100755
--- a/chrony_3_3/test/simulation/006-largejitter
+++ b/chrony/test/simulation/006-largejitter
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/007-largewander b/chrony/test/simulation/007-largewander
similarity index 93%
rename from chrony_3_3/test/simulation/007-largewander
rename to chrony/test/simulation/007-largewander
index 5572cbc..af0c599 100755
--- a/chrony_3_3/test/simulation/007-largewander
+++ b/chrony/test/simulation/007-largewander
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony/test/simulation/008-ntpera b/chrony/test/simulation/008-ntpera
new file mode 100755
index 0000000..2a4f332
--- /dev/null
+++ b/chrony/test/simulation/008-ntpera
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "NTP eras"
+
+if check_config_h 'HAVE_LONG_TIME_T 1'; then
+	ntp_start=$(awk "BEGIN {print $(grep NTP_ERA_SPLIT ../../config.h | tr -dc '0-9*+-')}")
+else
+	ntp_start="-2147483648"
+fi
+
+# Set the starting test date to 500 seconds before the second NTP era.
+# This should work with 32-bit time_t and also with 64-bit time_t if the
+# configured NTP interval covers the test interval.
+export CLKNETSIM_START_DATE=$(date -d 'Feb  7 06:19:56 UTC 2036' +'%s')
+
+if awk "BEGIN {exit !($ntp_start <= $CLKNETSIM_START_DATE && \
+		$CLKNETSIM_START_DATE + $limit < $ntp_start + 2^32)}"; then
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_packet_interval || test_fail
+	check_sync || test_fail
+fi
+
+# The following tests need 64-bit time_t and ntp_start not before 1970
+check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
+echo "$ntp_start" | grep -q '-' && test_skip
+
+for time_offset in -1e-1 1e-1; do
+	for start_offset in 0 "2^32 - $limit"; do
+		export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
+		run_test || test_fail
+		check_chronyd_exit || test_fail
+		check_source_selection || test_fail
+		check_packet_interval || test_fail
+		check_sync || test_fail
+	done
+
+	for start_offset in -$limit "2^32"; do
+		export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
+		run_test || test_fail
+		check_chronyd_exit || test_fail
+		check_source_selection || test_fail
+		check_packet_interval || test_fail
+		check_sync && test_fail
+	done
+done
+
+test_pass
diff --git a/chrony_3_3/test/simulation/009-sourceselection b/chrony/test/simulation/009-sourceselection
similarity index 97%
rename from chrony_3_3/test/simulation/009-sourceselection
rename to chrony/test/simulation/009-sourceselection
index 7e60931..547c376 100755
--- a/chrony_3_3/test/simulation/009-sourceselection
+++ b/chrony/test/simulation/009-sourceselection
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/010-multrecv b/chrony/test/simulation/010-multrecv
similarity index 92%
rename from chrony_3_3/test/simulation/010-multrecv
rename to chrony/test/simulation/010-multrecv
index 8adfab6..36e7476 100755
--- a/chrony_3_3/test/simulation/010-multrecv
+++ b/chrony/test/simulation/010-multrecv
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/011-asymjitter b/chrony/test/simulation/011-asymjitter
similarity index 93%
rename from chrony_3_3/test/simulation/011-asymjitter
rename to chrony/test/simulation/011-asymjitter
index 18e6ec1..9fb5567 100755
--- a/chrony_3_3/test/simulation/011-asymjitter
+++ b/chrony/test/simulation/011-asymjitter
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/001-defaults b/chrony/test/simulation/012-daemonts
similarity index 67%
copy from chrony_3_3/test/simulation/001-defaults
copy to chrony/test/simulation/012-daemonts
index 541cdad..a1b90e3 100755
--- a/chrony_3_3/test/simulation/001-defaults
+++ b/chrony/test/simulation/012-daemonts
@@ -1,8 +1,10 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
-test_start "default test settings"
+test_start "daemon timestamping"
+
+export CLKNETSIM_TIMESTAMPING=0
 
 run_test || test_fail
 check_chronyd_exit || test_fail
diff --git a/chrony_3_3/test/simulation/001-defaults b/chrony/test/simulation/013-nameserv
similarity index 75%
copy from chrony_3_3/test/simulation/001-defaults
copy to chrony/test/simulation/013-nameserv
index 541cdad..941026b 100755
--- a/chrony_3_3/test/simulation/001-defaults
+++ b/chrony/test/simulation/013-nameserv
@@ -1,8 +1,10 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
-test_start "default test settings"
+test_start "name resolving"
+
+dns=1
 
 run_test || test_fail
 check_chronyd_exit || test_fail
diff --git a/chrony_3_3/test/simulation/101-poll b/chrony/test/simulation/101-poll
similarity index 73%
rename from chrony_3_3/test/simulation/101-poll
rename to chrony/test/simulation/101-poll
index f350777..1856a27 100755
--- a/chrony_3_3/test/simulation/101-poll
+++ b/chrony/test/simulation/101-poll
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 test_start "minpoll/maxpoll options"
@@ -12,11 +12,13 @@
 freq_rms_limit=5e-6
 client_conf="makestep 1e-2 1"
 
-for poll in $(seq 2 14); do
+for poll in $(seq 1 14); do
 	client_server_options="minpoll $poll maxpoll $poll"
 	limit=$[2**$poll * 10]
 	min_sync_time=$[2**$poll * 2]
 	max_sync_time=$[2**$poll * 21 / 10 + 1]
+	client_max_min_out_interval=$(awk "BEGIN {print 2^$poll * 1.1}")
+	client_min_mean_out_interval=$(awk "BEGIN {print 2^$poll * 0.99}")
 
 	run_test || test_fail
 	check_chronyd_exit || test_fail
diff --git a/chrony_3_3/test/simulation/102-iburst b/chrony/test/simulation/102-iburst
similarity index 94%
rename from chrony_3_3/test/simulation/102-iburst
rename to chrony/test/simulation/102-iburst
index bd82530..9936572 100755
--- a/chrony_3_3/test/simulation/102-iburst
+++ b/chrony/test/simulation/102-iburst
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 test_start "iburst option"
diff --git a/chrony/test/simulation/103-initstepslew b/chrony/test/simulation/103-initstepslew
new file mode 100755
index 0000000..fe47b68
--- /dev/null
+++ b/chrony/test/simulation/103-initstepslew
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "initstepslew directive"
+
+freq_offset=0.0
+wander=0.0
+time_rms_limit=1e-3
+limit=100
+
+client_conf="initstepslew 5 192.168.123.1"
+client_server_conf="#"
+
+min_sync_time=6
+max_sync_time=35
+
+for time_offset in -2.0 -0.2 0.2 2.0; do
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_packet_interval || test_fail
+	check_sync || test_fail
+	check_log_messages "00:00:0.Z System's initial.*slew" 1 1 || test_fail
+done
+
+min_sync_time=5
+max_sync_time=5
+
+for time_offset in -1e8 -1e2 1e2 1e8; do
+	run_test || test_fail
+	check_packet_interval || test_fail
+	check_sync || test_fail
+	check_log_messages "System's initial.*step" 1 1 || test_fail
+done
+
+time_offset=3
+limit=500
+servers=2
+falsetickers=1
+client_conf="initstepslew 5 192.168.123.1 192.168.123.2"
+client_server_conf="server 192.168.123.2"
+
+min_sync_time=360
+max_sync_time=450
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_log_messages "00:03:2.Z No suitable source for initstepslew" 1 1 || test_fail
+
+client_conf="initstepslew 5 192.168.123.1 192.168.123.2"
+
+min_sync_time=1
+max_sync_time=500
+server_conf="deny all"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync && test_fail
+check_log_messages "00:00:1.Z No suitable source for initstepslew" 1 1 || test_fail
+
+test_pass
diff --git a/chrony_3_3/test/simulation/104-driftfile b/chrony/test/simulation/104-driftfile
similarity index 95%
rename from chrony_3_3/test/simulation/104-driftfile
rename to chrony/test/simulation/104-driftfile
index 703dad7..93d4363 100755
--- a/chrony_3_3/test/simulation/104-driftfile
+++ b/chrony/test/simulation/104-driftfile
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 test_start "driftfile directive"
diff --git a/chrony_3_3/test/simulation/105-ntpauth b/chrony/test/simulation/105-ntpauth
similarity index 73%
rename from chrony_3_3/test/simulation/105-ntpauth
rename to chrony/test/simulation/105-ntpauth
index 4c77f10..1f228f5 100755
--- a/chrony_3_3/test/simulation/105-ntpauth
+++ b/chrony/test/simulation/105-ntpauth
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
@@ -23,16 +23,20 @@
 
 keys=4
 
-if grep -q 'FEAT_SECHASH 1' ../../config.h; then
-	hashes="MD5 SHA1 SHA256 SHA384 SHA512"
-else
-	hashes="MD5"
-fi
+types="MD5"
+check_config_h 'FEAT_SECHASH 1' && types="$types SHA1 SHA256 SHA384 SHA512"
+check_config_h 'HAVE_CMAC 1' && types="$types AES128 AES256"
 
-for hash in $hashes; do
+for type in $types; do
 	keys=$[$keys + 1]
-	key=$(echo $keys $hash HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | \
-		head -c $[$RANDOM % 64 * 2 + 2]))
+	case $type in
+		AES128)	length=16;;
+		AES256)	length=32;;
+		*)	length=$[$RANDOM % 32 + 1];;
+	esac
+
+	key=$(echo $keys $type HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | \
+		head -c $[$length * 2]))
 	echo "$key" >> tmp/server.keys
 	echo "$key" >> tmp/client.keys
 done
@@ -70,13 +74,18 @@
 peers=2
 max_sync_time=500
 base_delay="$default_base_delay (* -1 (equal 0.1 from 3) (equal 0.1 to 1))"
+
+for versions in "3 3" "3 4" "4 3" "4 4"; do
+	for key in 1 $keys; do
+		client_lpeer_options="version ${versions% *} key $key"
+		client_rpeer_options="version ${versions#* } key $key"
+		run_test || test_fail
+		check_chronyd_exit || test_fail
+		check_sync || test_fail
+	done
+done
+
 client_lpeer_options="key 1"
-client_rpeer_options="key 1"
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_sync || test_fail
-
 client_rpeer_options="key 2"
 
 run_test || test_fail
diff --git a/chrony/test/simulation/106-refclock b/chrony/test/simulation/106-refclock
new file mode 100755
index 0000000..c8ccb72
--- /dev/null
+++ b/chrony/test/simulation/106-refclock
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "SHM refclock"
+
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+check_config_h 'FEAT_PHC 1' || test_skip
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+servers=0
+limit=1000
+refclock_jitter=$jitter
+min_sync_time=45
+max_sync_time=70
+chronyc_start=70
+chronyc_conf="tracking"
+
+for refclock in "SHM 0" "PHC /dev/ptp0"; do
+	client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
+logdir tmp
+log refclocks"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+	check_chronyc_output "^Reference ID.*47505300 \(GPS\)
+Stratum.*: 4
+.*
+Root delay      : 0.001000000 seconds
+.*
+Update interval : 16\.. seconds
+.*$" || test_fail
+
+	check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
+	check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
+	rm -f tmp/refclocks.log
+done
+
+if check_config_h 'FEAT_PPS 1'; then
+	refclock_offset=0.35
+	refclock_jitter=0.05
+
+	client_conf="
+refclock SHM 0 refid NMEA noselect
+refclock PPS /dev/pps0 lock NMEA
+logdir tmp
+log refclocks"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+	check_chronyc_output "^Reference ID.*50505331 \(PPS1\)
+Stratum.*: 1
+.*
+Root delay      : 0\.000000001 seconds
+.*$" || test_fail
+
+	check_file_messages "20.* PPS1.*[0-9] N " 620 740 refclocks.log || test_fail
+	check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
+	rm -f tmp/refclocks.log
+
+	client_conf="
+refclock SHM 0 noselect
+refclock PPS /dev/pps0
+local
+logdir tmp
+log refclocks"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+	check_chronyc_output "^Reference ID.*50505331 \(PPS1\)
+Stratum.*: 10
+.*
+Root delay      : 0\.000000001 seconds
+.*$" || test_fail
+
+	check_file_messages "20.* PPS1.*[0-9] N " 997 1001 refclocks.log || test_fail
+	check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
+	rm -f tmp/refclocks.log
+
+	min_sync_time=100
+	max_sync_time=220
+	chronyc_start=220
+	client_conf="
+refclock SHM 0 refid NMEA offset 0.35 delay 0.1
+refclock PPS /dev/pps0
+logdir tmp
+log refclocks"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+	check_chronyc_output "^Reference ID.*50505331 \(PPS1\)
+Stratum.*: 1
+.*
+Root delay      : 0\.000000001 seconds
+.*$" || test_fail
+
+	check_file_messages "20.* PPS1.*[0-9] N " 800 940  refclocks.log || test_fail
+	check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail
+	rm -f tmp/refclocks.log
+fi
+
+test_pass
diff --git a/chrony_3_3/test/simulation/107-allowdeny b/chrony/test/simulation/107-allowdeny
similarity index 92%
rename from chrony_3_3/test/simulation/107-allowdeny
rename to chrony/test/simulation/107-allowdeny
index b11db32..4665337 100755
--- a/chrony_3_3/test/simulation/107-allowdeny
+++ b/chrony/test/simulation/107-allowdeny
@@ -1,9 +1,11 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
 test_start "allow/deny directives"
 
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
 limit=500
 
 # Note that start_client in clknetsim.bash always adds allow to the config
diff --git a/chrony/test/simulation/108-peer b/chrony/test/simulation/108-peer
new file mode 100755
index 0000000..906de17
--- /dev/null
+++ b/chrony/test/simulation/108-peer
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "NTP peers"
+
+# Allow and drop packets to the server in 1000 second intervals, so only one
+# client has access to it and the other is forced to switch to the peer.
+base_delay=$(cat <<-EOF | tr -d '\n'
+  (+ 1e-4
+     (* -1
+        (equal 0.1 from 2)
+        (equal 0.1 to 1)
+        (equal 0.1 (min (% time 2000) 1000) 1000))
+     (* -1
+        (equal 0.1 from 3)
+        (equal 0.1 to 1)
+	(equal 0.1 (max (% time 2000) 1000) 1000)))
+EOF
+)
+
+clients=2
+peers=2
+max_sync_time=1000
+client_server_options="minpoll 6 maxpoll 6"
+client_peer_options="minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
+client_peer_options=""
+
+while read lminpoll lmaxpoll rminpoll rmaxpoll max_sync_time; do
+	client_lpeer_options="minpoll $lminpoll maxpoll $lmaxpoll"
+	client_rpeer_options="minpoll $rminpoll maxpoll $rmaxpoll"
+	limit=$[$max_sync_time * 10]
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_sync || test_fail
+done <<-EOF
+	3 6 3 6 400
+	3 3 6 6 450
+	6 6 3 3 450
+	3 6 6 6 450
+	6 6 3 6 450
+	-2 -2 2 2 220
+	2 2 -2 -2 220
+EOF
+
+test_pass
diff --git a/chrony_3_3/test/simulation/109-makestep b/chrony/test/simulation/109-makestep
similarity index 97%
rename from chrony_3_3/test/simulation/109-makestep
rename to chrony/test/simulation/109-makestep
index f984f72..78d8d59 100755
--- a/chrony_3_3/test/simulation/109-makestep
+++ b/chrony/test/simulation/109-makestep
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 test_start "makestep directive"
diff --git a/chrony/test/simulation/110-chronyc b/chrony/test/simulation/110-chronyc
new file mode 100755
index 0000000..6693d4d
--- /dev/null
+++ b/chrony/test/simulation/110-chronyc
@@ -0,0 +1,495 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "chronyc"
+
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+refclock_jitter=$jitter
+client_server_conf="
+server node1.net1.clk
+server 192.168.123.2"
+client_conf="
+refclock SHM 0 noselect
+smoothtime 400 0.001 leaponly"
+cmdmon_unix=0
+
+chronyc_conf="activity
+tracking
+sourcename 192.168.123.1
+sourcename 192.168.123.2
+sources
+sourcestats
+manual list
+smoothing
+waitsync
+rtcdata"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+2 sources online
+0 sources offline
+0 sources doing burst \(return to online\)
+0 sources doing burst \(return to offline\)
+0 sources with unknown address
+Reference ID    : C0A87B01 \(192\.168\.123\.1\)
+Stratum         : 2
+Ref time \(UTC\)  : Fri Jan 01 00:1.:.. 2010
+System time     : 0\.0000..... seconds (slow|fast) of NTP time
+Last offset     : [+-]0\.000...... seconds
+RMS offset      : 0\.000...... seconds
+Frequency       : (99|100)\.... ppm fast
+Residual freq   : [+-][0-9]\.... ppm
+Skew            : [0-9]\.... ppm
+Root delay      : 0\.000...... seconds
+Root dispersion : 0\.000...... seconds
+Update interval : [0-9]+\.. seconds
+Leap status     : Normal
+node1\.net1\.clk
+192\.168\.123\.2
+MS Name/IP address         Stratum Poll Reach LastRx Last sample               
+===============================================================================
+#\? SHM0                          0   4   377    [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
+\^\* 192\.168\.123\.1                 1   [67]   377    [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
+\^\? 192\.168\.123\.2                 0   [0-9]+     0     -     \+0ns\[   \+0ns\] \+/-    0ns
+Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
+==============================================================================
+SHM0                       [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\....  [0-9 +-]+[un]s [0-9 ]+[un]s
+192\.168\.123\.1              [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\....  [0-9 +-]+[un]s [0-9 ]+[un]s
+192\.168\.123\.2               0   0     0     \+0\.000   2000\.000     \+0ns  4000ms
+210 n_samples = 0
+#    Date     Time\(UTC\)    Slewed   Original   Residual
+=======================================================
+Active         : Yes \(leap second only\)
+Offset         : \+0\.000000000 seconds
+Frequency      : \+0\.000000 ppm
+Wander         : \+0\.000000 ppm per second
+Last update    : [0-9]+\.. seconds ago
+Remaining time : 0\.0 seconds
+try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
+513 RTC driver not running$" \
+|| test_fail
+
+chronyc_conf="tracking"
+dns=1
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^Reference ID    : C0A87B01 \(node1\.net1\.clk\)" \
+	|| test_fail
+
+chronyc_options="-c"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
+	|| test_fail
+
+chronyc_options=""
+server_strata=0
+chronyc_start=0.5
+client_server_conf=""
+client_conf=""
+server_conf="server 192.168.123.1"
+limit=1
+
+for chronyc_conf in \
+	"accheck 1.2.3.4" \
+	"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
+	"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
+	"add server node1.net1.clk" \
+	"allow 1.2.3.4" \
+	"allow 1.2" \
+	"allow 3.4.5" \
+	"allow 6.7.8/22" \
+	"allow 6.7.8.9/22" \
+	"allow 0/0" \
+	"allow" \
+	"allow all 10/24" \
+	"authdata" \
+	"burst 5/10" \
+	"burst 3/5 255.255.255.0/1.2.3.0" \
+	"burst 1/2 1.2.3.0/24" \
+	"clients" \
+	"clients -k" \
+	"clients -p 100" \
+	"clients -r" \
+	"cmdaccheck 1.2.3.4" \
+	"cmdallow 1.2.3.4" \
+	"cmdallow all 1.2.3.0/24" \
+	"cmddeny 1.2.3.4" \
+	"cmddeny all 1.2.3.0/24" \
+	"cyclelogs" \
+	"delete 10.0.0.0" \
+	"delete ID#0000000001" \
+	"deny 1.2.3.4" \
+	"deny all 1.2.3.0/24" \
+	"dfreq 1.0e-3" \
+	"doffset -1.0" \
+	"dump" \
+	"local stratum 5 distance 1.0 orphan" \
+	"local off" \
+	"makestep 10.0 3" \
+	"makestep" \
+	"manual delete 0" \
+	"manual off" \
+	"manual on" \
+	"manual reset" \
+	"maxdelay 1.2.3.4 1e-2" \
+	"maxdelaydevratio 1.2.3.4 5.0" \
+	"maxdelayratio 1.2.3.4 3.0" \
+	"maxpoll 1.2.3.4 5" \
+	"maxupdateskew 1.2.3.4 10.0" \
+	"minpoll 1.2.3.4 3" \
+	"minstratum 1.2.3.4 1" \
+	"minstratum ID#0000000001 1" \
+	"ntpdata 1.2.3.4" \
+	"offline" \
+	"offline 255.255.255.0/1.2.3.0" \
+	"offline 1.2.3.0/24" \
+	"online" \
+	"online 1.2.3.0/24" \
+	"onoffline" \
+	"polltarget 1.2.3.4 10" \
+	"refresh" \
+	"rekey" \
+	"reload sources" \
+	"reselect" \
+	"reselectdist 1e-3" \
+	"reset sources" \
+	"selectdata" \
+	"settime 16:30" \
+	"settime 16:30:05" \
+	"settime Nov 21, 2015 16:30:05" \
+	"serverstats" \
+	"shutdown" \
+	"smoothtime reset" \
+	"smoothtime activate" \
+	"trimrtc" \
+	"writertc"
+do
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_chronyc_output "501 Not authorised$" || test_fail
+done
+
+cmdmon_unix=1
+
+chronyc_conf="
+authdata
+clients -k -p 2
+clients -r
+clients
+ntpdata
+selectdata
+serverstats"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+node1\.net1\.clk                 -     0    0    0    -    0    0    0    0
+Hostname                      NTP   Drop Int IntL Last  NTS-KE   Drop Int  Last
+===============================================================================
+Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
+===============================================================================
+node1\.net1\.clk                  1      0   -   -     0       0      0   -     -
+Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
+===============================================================================
+node1\.net1\.clk                  0      0   -   -     0       0      0   -     -
+
+Remote address  : 192\.168\.123\.1 \(C0A87B01\)
+Remote port     : 123
+Local address   : 192\.168\.123\.1 \(C0A87B01\)
+Leap status     : Normal
+Version         : 4
+Mode            : Server
+Stratum         : 1
+Poll interval   : 6 \(64 seconds\)
+Precision       : -23 \(0\.000000119 seconds\)
+Root delay      : 0\.000000 seconds
+Root dispersion : 0\.000000 seconds
+Reference ID    : 7F7F0101 \(\)
+Reference time  : Thu Dec 31 23:59:5[89] 2009
+Offset          : [-+]0\.000...... seconds
+Peer delay      : 0\.00....... seconds
+Peer dispersion : 0\.00000.... seconds
+Response time   : 0\.000000... seconds
+Jitter asymmetry: \+0\.00
+NTP tests       : 111 111 1110
+Interleaved     : No
+Authenticated   : No
+TX timestamping : Kernel
+RX timestamping : Kernel
+Total TX        : 1
+Total RX        : 1
+Total valid RX  : 1
+S Name/IP Address        Auth COpts EOpts Last Score     Interval  Leap
+=======================================================================
+M node1\.net1\.clk            N ----- -----    0   1\.0    \+0ns    \+0ns  N
+NTP packets received       : 1
+NTP packets dropped        : 0
+Command packets received   : 12
+Command packets dropped    : 0
+Client log records dropped : 0
+NTS-KE connections accepted: 0
+NTS-KE connections dropped : 0
+Authenticated NTP packets  : 0
+Interleaved NTP packets    : 0
+NTP timestamps held        : 0
+NTP timestamp span         : 0$" || test_fail
+
+chronyc_conf="
+deny all
+cmdallow all
+allow 1.2.3.4
+allow 1.2.3.0/28
+deny 1.2.3.0/27
+allow 1.2.4.5
+deny all 1.2.4.0/27
+cmddeny 5.6.7.8
+cmdallow all 5.6.7.0/28
+accheck 1.2.3.4
+accheck 1.2.3.5
+accheck 1.2.4.5
+cmdaccheck 5.6.7.8"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+208 Access allowed
+209 Access denied
+209 Access denied
+208 Access allowed$" || test_fail
+
+if check_config_h 'FEAT_IPV6 1'; then
+	chronyc_conf="
+	deny all
+	cmdallow all
+	allow 2001:db8::1
+	allow 2001:db8::/64
+	deny 2001:db8::/63
+	allow 2001:db8:1::1
+	deny all 2001:db8:1::/63
+	cmddeny 2001:db9::1
+	cmdallow all 2001:db9::/64
+	accheck 2001:db8::1
+	accheck 2001:db8::2
+	accheck 2001:db8:1::1
+	cmdaccheck 2001:db9::1"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+
+	check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+208 Access allowed
+209 Access denied
+209 Access denied
+208 Access allowed$" || test_fail
+fi
+
+chronyc_conf="
+delete 192.168.123.1
+add server node1.net1.clk minpoll 6 maxpoll 10 iburst
+offline 192.168.123.1
+burst 1/1 192.168.123.1
+online 192.168.123.1
+maxdelay 192.168.123.1 1e-2
+maxdelaydevratio 192.168.123.1 5.0
+maxdelayratio 192.168.123.1 3.0
+maxpoll 192.168.123.1 5
+maxupdateskew 192.168.123.1 10.0
+minpoll 192.168.123.1 3
+minstratum 192.168.123.1 1
+polltarget 192.168.123.1 10
+delete 192.168.123.1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK$" || test_fail
+
+chronyc_conf="
+cyclelogs
+dump
+dfreq 1.0e-3
+doffset -0.01
+local stratum 5 distance 1.0 orphan
+local off
+makestep 10.0 3
+makestep
+manual on
+settime now
+manual delete 0
+manual reset
+manual off
+onoffline
+refresh
+rekey
+reload sources
+reselect
+reselectdist 1e-3
+reset sources
+shutdown"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+Clock was .\... seconds fast.  Frequency change = 0.00ppm, new frequency = 0.00ppm
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK$" || test_fail
+
+server_conf="
+server 192.168.123.1
+noclientlog"
+
+commands=(
+	"add server nosuchnode.net1.clk" "^Invalid host/IP address$"
+	"allow nosuchnode.net1.clk" "^Could not read address$"
+	"allow 192.168.123.0/2 4" "^Could not read address$"
+	"allow 192.168.123.0/2e" "^Could not read address$"
+	"allow 192.168.12e" "^Could not read address$"
+	"allow 192.168123" "^Could not read address$"
+	"allow 192.168.123.2/33" "^507 Bad subnet$"
+	"clients" "Hostname.*519 Client logging is not active in the daemon$"
+	"delete 192.168.123.2" "^503 No such source$"
+	"minpoll 192.168.123.2 5" "^503 No such source$"
+	"ntpdata 192.168.123.2" "^503 No such source$"
+	"settime now" "^505 Facility not enabled in daemon$"
+	"smoothing" "^505 Facility not enabled in daemon$"
+	"smoothtime activate" "^505 Facility not enabled in daemon$"
+	"smoothtime reset" "^505 Facility not enabled in daemon$"
+	"sourcename 192.168.123.2" "^503 No such source$"
+	"trimrtc" "^513 RTC driver not running$"
+	"writertc" "^513 RTC driver not running$"
+)
+
+for i in $(seq 0 $[${#commands[*]} / 2]); do
+	chronyc_conf=${commands[$[i * 2]]}
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_chronyc_output "${commands[$[i * 2 + 1]]}" || test_fail
+done
+
+cmdmon_unix=0
+server_conf="server 192.168.123.1"
+
+chronyc_conf="dns -n
+dns +n
+dns -4
+dns -6
+dns -46
+timeout 200
+retries 1
+keygen
+keygen 10 MD5 128
+keygen 11 MD5 40
+help
+quit
+nosuchcommand"
+
+run_test || test_fail
+
+check_chronyc_output "^1 (MD5|SHA1) HEX:........................................
+10 MD5 HEX:................................
+11 MD5 HEX:....................
+System clock:.*this help
+ *$" || test_fail
+
+chronyc_conf="keygen 10 NOSUCHTYPE 128
+help"
+run_test || test_fail
+check_chronyc_output "^Unknown hash function or cipher NOSUCHTYPE\$" || test_fail
+
+if check_config_h 'FEAT_SECHASH 1'; then
+	for hash in MD5 SHA1 SHA256 SHA384 SHA512; do
+		chronyc_conf="keygen 5 $hash"
+		run_test || test_fail
+		check_chronyc_output "^5 $hash HEX:........................................\$" || test_fail
+	done
+fi
+
+if check_config_h 'HAVE_CMAC 1'; then
+	chronyc_conf="keygen 6 AES128
+keygen 7 AES256"
+	run_test || test_fail
+	check_chronyc_output "^6 AES128 HEX:................................
+7 AES256 HEX:................................................................\$" || test_fail
+fi
+
+# Pass every fourth request
+base_delay=$(cat <<-EOF | tr -d '\n'
+  (+ 1e-4
+     (* -1
+        (equal 0.1 from 2)
+        (equal 0.1 (min (% (sum 1) 4) 1) 1)))
+EOF
+)
+limit=15
+
+chronyc_conf="sources"
+run_test || test_fail
+check_chronyc_output "^506 Cannot talk to daemon$" || test_fail
+
+chronyc_conf="retries 3
+sources"
+run_test || test_fail
+check_chronyc_output "^MS.*0ns$" || test_fail
+
+test_pass
diff --git a/chrony_3_3/test/simulation/111-knownclient b/chrony/test/simulation/111-knownclient
similarity index 93%
rename from chrony_3_3/test/simulation/111-knownclient
rename to chrony/test/simulation/111-knownclient
index 3d3fd87..92bad54 100755
--- a/chrony_3_3/test/simulation/111-knownclient
+++ b/chrony/test/simulation/111-knownclient
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/112-port b/chrony/test/simulation/112-port
similarity index 94%
rename from chrony_3_3/test/simulation/112-port
rename to chrony/test/simulation/112-port
index e983757..2f10eed 100755
--- a/chrony_3_3/test/simulation/112-port
+++ b/chrony/test/simulation/112-port
@@ -1,9 +1,11 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
 test_start "port and acquisitionport directives"
 
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
 run_test || test_fail
 check_chronyd_exit || test_fail
 check_source_selection || test_fail
diff --git a/chrony_3_3/test/simulation/113-leapsecond b/chrony/test/simulation/113-leapsecond
similarity index 94%
rename from chrony_3_3/test/simulation/113-leapsecond
rename to chrony/test/simulation/113-leapsecond
index 5b9758f..394440b 100755
--- a/chrony_3_3/test/simulation/113-leapsecond
+++ b/chrony/test/simulation/113-leapsecond
@@ -1,8 +1,11 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
+
 test_start "leap second"
 
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+
 export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
 
 leap=$[2 * 24 * 3600]
diff --git a/chrony_3_3/test/simulation/114-presend b/chrony/test/simulation/114-presend
similarity index 95%
rename from chrony_3_3/test/simulation/114-presend
rename to chrony/test/simulation/114-presend
index 3113253..4fd89f8 100755
--- a/chrony_3_3/test/simulation/114-presend
+++ b/chrony/test/simulation/114-presend
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 test_start "presend option"
diff --git a/chrony/test/simulation/115-cmdmontime b/chrony/test/simulation/115-cmdmontime
new file mode 100755
index 0000000..525062d
--- /dev/null
+++ b/chrony/test/simulation/115-cmdmontime
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "cmdmon timestamps"
+
+# The following tests need 64-bit time_t
+check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+limit=2
+client_server_options="noselect"
+client_conf="local stratum 1"
+chronyc_start="1.5"
+chronyc_conf="tracking"
+
+for year in `seq 1850 100 2300`; do
+	export CLKNETSIM_START_DATE=$(date -d "Jan 01 00:00:05 $year UTC" +'%s')
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_chronyc_output "^.*Ref time \(UTC\).*Jan 01 00:00:0. $year.*$" || test_fail
+done
+
+test_pass
diff --git a/chrony_3_3/test/simulation/116-minsources b/chrony/test/simulation/116-minsources
similarity index 95%
rename from chrony_3_3/test/simulation/116-minsources
rename to chrony/test/simulation/116-minsources
index 392f360..f576423 100755
--- a/chrony_3_3/test/simulation/116-minsources
+++ b/chrony/test/simulation/116-minsources
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/117-fallbackdrift b/chrony/test/simulation/117-fallbackdrift
similarity index 95%
rename from chrony_3_3/test/simulation/117-fallbackdrift
rename to chrony/test/simulation/117-fallbackdrift
index 22270c9..21f6963 100755
--- a/chrony_3_3/test/simulation/117-fallbackdrift
+++ b/chrony/test/simulation/117-fallbackdrift
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 test_start "fallback drift"
diff --git a/chrony_3_3/test/simulation/118-maxdelay b/chrony/test/simulation/118-maxdelay
similarity index 96%
rename from chrony_3_3/test/simulation/118-maxdelay
rename to chrony/test/simulation/118-maxdelay
index 22b9a50..bb8b47c 100755
--- a/chrony_3_3/test/simulation/118-maxdelay
+++ b/chrony/test/simulation/118-maxdelay
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 test_start "maxdelay options"
diff --git a/chrony_3_3/test/simulation/119-smoothtime b/chrony/test/simulation/119-smoothtime
similarity index 96%
rename from chrony_3_3/test/simulation/119-smoothtime
rename to chrony/test/simulation/119-smoothtime
index 6b4ae39..7f5114c 100755
--- a/chrony_3_3/test/simulation/119-smoothtime
+++ b/chrony/test/simulation/119-smoothtime
@@ -1,8 +1,11 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
+
 test_start "smoothtime option"
 
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+
 server_strata=2
 server_conf="smoothtime 400 0.001"
 server_server_options="minpoll 8"
diff --git a/chrony_3_3/test/simulation/120-selectoptions b/chrony/test/simulation/120-selectoptions
similarity index 67%
rename from chrony_3_3/test/simulation/120-selectoptions
rename to chrony/test/simulation/120-selectoptions
index 7e10293..611815e 100755
--- a/chrony_3_3/test/simulation/120-selectoptions
+++ b/chrony/test/simulation/120-selectoptions
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
@@ -16,7 +16,6 @@
 run_test || test_fail
 check_chronyd_exit || test_fail
 check_source_selection || test_fail
-check_packet_interval || test_fail
 check_sync || test_fail
 
 client_server_conf="
@@ -27,7 +26,6 @@
 run_test || test_fail
 check_chronyd_exit || test_fail
 check_source_selection || test_fail
-check_packet_interval || test_fail
 # This check is expected to fail
 check_sync && test_fail
 
@@ -36,10 +34,9 @@
 run_test || test_fail
 check_chronyd_exit || test_fail
 check_source_selection || test_fail
-check_packet_interval || test_fail
 check_sync || test_fail
 
-base_delay=$default_base_delay
+base_delay=1e-3
 falsetickers=1
 
 client_server_conf="
@@ -65,4 +62,28 @@
 check_source_selection && test_fail
 check_sync && test_fail
 
+cat > tmp/keys <<-EOF
+1 MD5 HEX:1B81CBF88D4A73F2E8CE59647F6E5C1719B6CAF5
+EOF
+
+server_conf="keyfile tmp/keys"
+client_server_conf="
+server 192.168.123.1 key 1
+server 192.168.123.2
+server 192.168.123.3"
+
+for authselectmode in require prefer mix ignore; do
+	client_conf="keyfile tmp/keys
+		     authselectmode $authselectmode"
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_packet_interval || test_fail
+	if [ $authselectmode = ignore ]; then
+		check_sync || test_fail
+	else
+		check_sync && test_fail
+	fi
+done
+
 test_pass
diff --git a/chrony_3_3/test/simulation/121-orphan b/chrony/test/simulation/121-orphan
similarity index 76%
rename from chrony_3_3/test/simulation/121-orphan
rename to chrony/test/simulation/121-orphan
index ed92153..7579997 100755
--- a/chrony_3_3/test/simulation/121-orphan
+++ b/chrony/test/simulation/121-orphan
@@ -1,16 +1,19 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
 test_start "orphan option"
 
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
 server_strata=3
 server_conf="local stratum 5 orphan
 server 192.168.123.1
 server 192.168.123.2
 server 192.168.123.3"
-max_sync_time=500
-chronyc_start=300
+max_sync_time=900
+client_start=140
+chronyc_start=700
 chronyc_conf="tracking"
 time_rms_limit=5e-4
 
diff --git a/chrony/test/simulation/122-xleave b/chrony/test/simulation/122-xleave
new file mode 100755
index 0000000..bc4c3a7
--- /dev/null
+++ b/chrony/test/simulation/122-xleave
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "interleaved mode"
+
+client_server_options="xleave"
+client_conf="
+logdir tmp
+log rawmeasurements"
+
+server_conf="noclientlog"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+server_conf=""
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail
+check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+clients=2
+peers=2
+max_sync_time=500
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 2) (equal 0.1 to 1)))"
+
+client_lpeer_options="xleave minpoll 5 maxpoll 5"
+client_rpeer_options="minpoll 5 maxpoll 5"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+# These checks are expected to fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+rm -f tmp/measurements.log
+
+for rpoll in 4 5 6; do
+	client_rpeer_options="xleave minpoll $rpoll maxpoll $rpoll"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+
+	if [ $rpoll -le 5 ]; then
+		check_file_messages "111 111 1111.* 1B [DKH] [DKH]\$" 0 0 measurements.log || test_fail
+		check_file_messages "111 111 1111.* 1I [DKH] [DKH]\$" 200 310 measurements.log || test_fail
+	else
+		check_file_messages "111 111 1111.* 1B [DKH] [DKH]\$" 125 135 measurements.log || test_fail
+		check_file_messages "111 111 1111.* 1I [DKH] [DKH]\$" 20 30 measurements.log || test_fail
+	fi
+	rm -f tmp/measurements.log
+done
+
+if check_config_h 'FEAT_CMDMON 1'; then
+	# test client timestamp selection and server timestamp correction
+	base_delay="(+ 1.25e-6 (* -1 (equal 0.1 from 5)))"
+	jitter=1e-9
+	wander=1e-12
+	client_lpeer_options="xleave minpoll 5 maxpoll 5 noselect"
+	client_rpeer_options="xleave minpoll 5 maxpoll 5 noselect"
+	chronyc_conf="doffset -0.1"
+	chronyc_start=7200
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync && test_fail
+
+	check_file_messages "\.2   N  2 111 111 ....   5  5 .\... ..\....e-..  2\....e-06" \
+		290 310 measurements.log || test_fail
+	check_file_messages "\.2   N  2 111 111 ....   5  5 .\... ..\....e-..  .\....e-0[0123]" \
+		0 0 measurements.log || test_fail
+	rm -f tmp/measurements.log
+fi
+
+test_pass
diff --git a/chrony_3_3/test/simulation/123-mindelay b/chrony/test/simulation/123-mindelay
similarity index 96%
rename from chrony_3_3/test/simulation/123-mindelay
rename to chrony/test/simulation/123-mindelay
index cde214a..89a9f33 100755
--- a/chrony_3_3/test/simulation/123-mindelay
+++ b/chrony/test/simulation/123-mindelay
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/124-tai b/chrony/test/simulation/124-tai
similarity index 92%
rename from chrony_3_3/test/simulation/124-tai
rename to chrony/test/simulation/124-tai
index b5be030..97064f7 100755
--- a/chrony_3_3/test/simulation/124-tai
+++ b/chrony/test/simulation/124-tai
@@ -1,8 +1,11 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
+
 test_start "tai option"
 
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+
 export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 31 2008 23:50:00' +'%s')
 
 leap=$[10 * 60]
diff --git a/chrony_3_3/test/simulation/125-packetloss b/chrony/test/simulation/125-packetloss
similarity index 96%
rename from chrony_3_3/test/simulation/125-packetloss
rename to chrony/test/simulation/125-packetloss
index 95604da..505e4fa 100755
--- a/chrony_3_3/test/simulation/125-packetloss
+++ b/chrony/test/simulation/125-packetloss
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/126-burst b/chrony/test/simulation/126-burst
similarity index 97%
rename from chrony_3_3/test/simulation/126-burst
rename to chrony/test/simulation/126-burst
index d63f290..1cb6f9c 100755
--- a/chrony_3_3/test/simulation/126-burst
+++ b/chrony/test/simulation/126-burst
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony/test/simulation/127-filter b/chrony/test/simulation/127-filter
new file mode 100755
index 0000000..db70d13
--- /dev/null
+++ b/chrony/test/simulation/127-filter
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "filter option"
+
+client_server_options="minpoll 4 maxpoll 4 filter 15"
+min_sync_time=710
+max_sync_time=720
+client_max_min_out_interval=16.1
+client_min_mean_out_interval=15.9
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/128-nocontrol b/chrony/test/simulation/128-nocontrol
new file mode 100755
index 0000000..3f0d18d
--- /dev/null
+++ b/chrony/test/simulation/128-nocontrol
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "-x option"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+wander=0.0
+time_offset=0.0
+freq_offset=0.0
+time_max_limit=1e-6
+freq_max_limit=1e-9
+min_sync_time=0
+max_sync_time=0
+client_chronyd_options="-x"
+chronyc_start=300
+chronyc_conf="tracking"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_chronyc_output "^.*Stratum *: 2.*$" || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/129-reload b/chrony/test/simulation/129-reload
new file mode 100755
index 0000000..56bc3da
--- /dev/null
+++ b/chrony/test/simulation/129-reload
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "-r option"
+
+wander=0.0
+limit=100
+min_sync_time=100
+max_sync_time=104
+client_chronyd_options="-r"
+client_conf="dumpdir tmp
+maxupdateskew 10000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_log_messages "Loaded dump file" 0 0 || test_fail
+check_file_messages "." 6 6 192.168.123.1.dat || test_fail
+
+client_start=$limit
+limit=1000
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_log_messages "Loaded dump file" 1 1 || test_fail
+check_file_messages "." 10 30 192.168.123.1.dat || test_fail
+
+rm -f tmp/*.dat
+
+client_start=0
+limit=200
+jitter=1e-6
+client_conf="dumpdir tmp
+maxupdateskew 1e-6
+maxslewrate 1e-6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_log_messages "Loaded dump file" 0 0 || test_fail
+check_file_messages "." 8 8 192.168.123.1.dat || test_fail
+cp tmp/192.168.123.1.dat tmp/backup.dat
+
+client_start=$limit
+limit=1000
+min_sync_time=201
+max_sync_time=203
+client_server_options="offline"
+client_conf="dumpdir tmp"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_log_messages "Loaded dump file" 1 1 || test_fail
+check_file_messages "." 8 8 192.168.123.1.dat || test_fail
+
+cp -f tmp/backup.dat tmp/192.168.123.1.dat
+
+client_server_options="key 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync && test_fail
+
+check_log_messages "Could not load dump file" 1 1 || test_fail
+check_log_messages "Loaded dump file" 0 0 || test_fail
+
+client_server_options=""
+
+if check_config_h 'FEAT_REFCLOCK 1'; then
+	refclock_jitter=1e-6
+	servers=0
+	client_start=0
+	limit=40
+	min_sync_time=56
+	max_sync_time=58
+	client_chronyd_options="-r"
+	client_conf="dumpdir tmp
+	refclock SHM 0"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+
+	check_log_messages "Loaded dump file" 0 0 || test_fail
+	check_file_messages "." 6 6 refid:53484d30.dat || test_fail
+
+	client_start=$limit
+	limit=300
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+
+	check_log_messages "Loaded dump file" 1 1 || test_fail
+	check_file_messages "." 6 23 refid:53484d30.dat || test_fail
+
+	rm -f tmp/*.dat
+fi
+
+test_pass
diff --git a/chrony/test/simulation/130-quit b/chrony/test/simulation/130-quit
new file mode 100755
index 0000000..da2b8cf
--- /dev/null
+++ b/chrony/test/simulation/130-quit
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "-q/-Q option"
+
+wander=0.0
+freq_offset=0.0
+min_sync_time=5
+max_sync_time=10
+client_chronyd_options="-q"
+client_server_options="iburst"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+min_sync_time=1
+max_sync_time=1
+client_server_options="iburst maxsamples 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+
+client_chronyd_options="-Q"
+run_test || test_fail
+check_sync && test_fail
+
+test_pass
diff --git a/chrony/test/simulation/131-maxchange b/chrony/test/simulation/131-maxchange
new file mode 100755
index 0000000..59cc0c1
--- /dev/null
+++ b/chrony/test/simulation/131-maxchange
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "maxchange directive"
+
+time_offset=2
+max_sync_time=5000
+client_conf="maxchange 0.1 1 3"
+client_step="(* $step (equal 0.1 (sum 1.0) 300))"
+
+run_test || test_fail
+check_chronyd_exit && test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync && test_fail
+check_log_messages "seconds exceeds.*ignored" 3 3 || test_fail
+check_log_messages "seconds exceeds.*exiting" 1 1 || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/132-logchange b/chrony/test/simulation/132-logchange
new file mode 100755
index 0000000..59ddf7c
--- /dev/null
+++ b/chrony/test/simulation/132-logchange
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "logchange directive"
+
+time_offset=2
+min_sync_time=590
+max_sync_time=700
+client_server_options="maxsamples 6"
+client_conf="logchange 0.1"
+client_step="(* $step (equal 0.1 (sum 1.0) 300))"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_log_messages "clock wrong by" 4 8 || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/133-hwtimestamp b/chrony/test/simulation/133-hwtimestamp
new file mode 100755
index 0000000..2c92685
--- /dev/null
+++ b/chrony/test/simulation/133-hwtimestamp
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "hwtimestamp directive"
+
+check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
+
+export CLKNETSIM_TIMESTAMPING=2
+
+refclock_jitter=1e-8
+refclock_offset=10.0
+min_sync_time=4
+max_sync_time=20
+limit=200
+server_conf="hwtimestamp eth0"
+client_server_options="minpoll 0 maxpoll 0 minsamples 32 xleave"
+client_chronyd_options="-d"
+
+for client_conf in "hwtimestamp eth0" "hwtimestamp eth0
+acquisitionport 123"; do
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+
+	if check_config_h 'FEAT_DEBUG 1'; then
+		check_log_messages "HW clock samples" 190 200 || test_fail
+		check_log_messages "HW clock reset" 0 0 || test_fail
+		check_log_messages "Received message.*tss=KH" 195 200 || test_fail
+		check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
+		check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
+		check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
+		check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
+		check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
+	fi
+done
+
+test_pass
diff --git a/chrony/test/simulation/134-log b/chrony/test/simulation/134-log
new file mode 100755
index 0000000..c7a3433
--- /dev/null
+++ b/chrony/test/simulation/134-log
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "log directive"
+
+check_config_h 'FEAT_PHC 1' || test_skip
+
+refclock_jitter=$jitter
+client_server_options="maxpoll 6"
+client_conf="refclock PHC /dev/ptp0 dpoll 4 poll 6 noselect
+logbanner 10
+logdir tmp
+log tracking rawmeasurements measurements statistics rtc refclocks tempcomp
+tempcomp tmp/tempcomp 64 0.0 0.0 0.0 0.0"
+
+echo 0.0 > tmp/tempcomp
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "=============" 31 33 \
+	tracking.log measurements.log tempcomp.log || test_fail
+check_file_messages "20.*192\.168\.123\.1" 150 160 \
+	tracking.log measurements.log statistics.log || test_fail
+check_file_messages "20.*PHC0" 150 160 statistics.log || test_fail
+check_file_messages "20.*PHC0" 750 800 refclocks.log || test_fail
+check_file_messages "20.* 0\.0000" 150 160 tempcomp.log || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/135-ratelimit b/chrony/test/simulation/135-ratelimit
new file mode 100755
index 0000000..86c435d
--- /dev/null
+++ b/chrony/test/simulation/135-ratelimit
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "ratelimit directive"
+
+server_conf="ratelimit interval 6 burst 2 leak 4"
+client_server_options="minpoll 3 maxpoll 3"
+min_sync_time=16
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "	2	1	" 1200 1300 log.packets || test_fail
+check_file_messages "	1	2	" 180 220 log.packets || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/136-broadcast b/chrony/test/simulation/136-broadcast
new file mode 100755
index 0000000..1488c53
--- /dev/null
+++ b/chrony/test/simulation/136-broadcast
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "broadcast directive"
+
+server_conf="broadcast 64 192.168.123.255"
+client_server_options="offline"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval && test_fail
+
+check_file_messages "	1	2	" 150 160 log.packets || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/137-pool b/chrony/test/simulation/137-pool
new file mode 100755
index 0000000..de8d77d
--- /dev/null
+++ b/chrony/test/simulation/137-pool
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "pool directive"
+
+limit=500
+client_conf="logdir tmp
+log measurements"
+
+servers=3
+client_server_conf="pool nodes-1-2-3.net1.clk"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.1" 5 10 measurements.log || test_fail
+check_file_messages "20.*192.168.123.2" 5 10 measurements.log || test_fail
+check_file_messages "20.*192.168.123.3" 5 10 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+servers=6
+client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.*" 30 35 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+servers=6
+client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk maxsources 2 minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.*" 15 17 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+test_pass
diff --git a/chrony/test/simulation/138-syncloop b/chrony/test/simulation/138-syncloop
new file mode 100755
index 0000000..2d3999e
--- /dev/null
+++ b/chrony/test/simulation/138-syncloop
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "loop prevention"
+
+mkdir tmp/logdir1 tmp/logdir2
+
+server_conf="
+server 192.168.123.1
+server 192.168.123.2
+logdir tmp/logdir1
+log measurements"
+client_server_conf="
+server 192.168.123.1
+server 192.168.123.2
+logdir tmp/logdir2
+log measurements
+allow"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1110" 30 200 logdir1/measurements.log || test_fail
+check_file_messages "20.*123\.2.* 111 111 1110" 30 200 logdir1/measurements.log || test_fail
+check_file_messages "20.*123\...* 111 111 1111" 0 0 logdir1/measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 111 1111" 30 200 logdir2/measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 111 1110" 0 0 logdir2/measurements.log || test_fail
+check_file_messages "20.*123\.2.* 111 111 1110" 30 200 logdir2/measurements.log || test_fail
+check_file_messages "20.*123\.2.* 111 111 1111" 0 0 logdir1/measurements.log || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/139-nts b/chrony/test/simulation/139-nts
new file mode 100755
index 0000000..6a2112d
--- /dev/null
+++ b/chrony/test/simulation/139-nts
@@ -0,0 +1,312 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "NTP authentication with NTS"
+
+check_config_h 'FEAT_NTS 1' || test_skip
+certtool --help &> /dev/null || test_skip
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan  1 00:00:00 UTC 2010' +'%s')
+
+for i in 1 2; do
+	cat > tmp/cert$i.cfg <<-EOF
+	cn = "node$i.net1.clk"
+	dns_name = "node$i.net1.clk"
+	ip_address = "192.168.123.$i"
+	serial = 001
+	activation_date = "2010-01-01 00:00:00 UTC"
+	expiration_date = "2010-01-02 00:00:00 UTC"
+	signing_key
+	encryption_key
+	EOF
+
+	certtool --generate-privkey --key-type=ed25519 --outfile tmp/server$i.key &> \
+		tmp/log.certtool$i
+	certtool --generate-self-signed --load-privkey tmp/server$i.key \
+		--template tmp/cert$i.cfg --outfile tmp/server$i.crt &>> tmp/log.certtool$i
+done
+
+max_sync_time=400
+dns=1
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsprocesses 0
+ntsrotate 66
+ntsdumpdir tmp
+"
+client_server_options="minpoll 6 maxpoll 6 nts"
+client_conf="
+nosystemcert
+ntstrustedcerts /dev/null
+ntstrustedcerts tmp/server1.crt
+ntstrustedcerts /dev/null
+logdir tmp
+log rawmeasurements"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1111" 75 80 measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 001 0000" 37 39 measurements.log || test_fail
+check_file_messages "	2	1	.*	4460	" 260 300 log.packets || test_fail
+check_file_messages "." 6 6 ntskeys || test_fail
+rm -f tmp/measurements.log
+
+client_conf+="
+ntsrefresh 120
+ntsdumpdir tmp"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1111" 99 103 measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail
+check_file_messages "	2	1	.*	4460	" 350 390 log.packets || test_fail
+check_file_messages "." 6 6 ntskeys || test_fail
+check_file_messages "." 12 13 192.168.123.1.nts || test_fail
+rm -f tmp/measurements.log
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan  1 00:00:00 UTC 2010 + 40000 sec' +'%s')
+
+server_conf+="
+ntsrotate 100000"
+client_conf+="
+ntsrefresh 39500"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1111" 150 160 measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail
+check_file_messages "	2	1	.*	4460	" 6 10 log.packets || test_fail
+check_file_messages "^9\.......e+03	2	1	.*	4460	" 6 10 log.packets || test_fail
+check_file_messages "." 6 6 ntskeys || test_fail
+check_file_messages "." 12 13 192.168.123.1.nts || test_fail
+rm -f tmp/measurements.log
+
+client_conf="
+nosystemcert"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages "	2	1	.*	123	" 0 0 log.packets || test_fail
+check_file_messages "	2	1	.*	4460	" 10 20 log.packets || test_fail
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan  2 00:00:01 UTC 2010' +'%s')
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages "	2	1	.*	123	" 0 0 log.packets || test_fail
+check_file_messages "	2	1	.*	4460	" 10 20 log.packets || test_fail
+check_log_messages "expired certificate" 4 4 || test_fail
+
+client_conf+="
+nocerttimecheck 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan  1 00:00:00 UTC 2010' +'%s')
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntsrefresh 500"
+
+for dns in 1 0; do
+	server_conf="
+	ntsserverkey tmp/server1.key
+	ntsservercert tmp/server1.crt
+	ntsprocesses 0
+	ntsrotate 0
+	ntsdumpdir tmp"
+
+	if [ $dns != 0 ]; then
+		server_conf+="
+		ntsntpserver node2.net1.clk"
+		client_server_conf="server node1.net1.clk $client_server_options"
+	else
+		server_conf+="
+		ntsntpserver 192.168.123.2"
+		client_server_conf="server 192.168.123.1 $client_server_options"
+	fi
+
+	servers=1
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection && test_fail
+	check_sync && test_fail
+
+	check_file_messages "	2	1	.*	4460	" 50 100 log.packets || test_fail
+	check_file_messages "	2	2	.*	4460	" 0 0 log.packets || test_fail
+	check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 6 8 || test_fail
+	check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 6 8 || test_fail
+
+	servers=2
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+
+	check_file_messages "	3	1	.*	4460	" 100 150 log.packets || test_fail
+	check_file_messages "	3	2	.*	4460	" 0 0 log.packets || test_fail
+	check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 1 1 || test_fail
+	check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 0 0 || test_fail
+
+	server_conf+="
+	ntsratelimit interval 12 burst 1 leak 4"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection && test_fail
+
+	check_file_messages "	3	1	.*	4460	1	0	2" 25 50 log.packets || test_fail
+	check_file_messages "	3	2	.*	4460	" 0 0 log.packets || test_fail
+	check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 2 6 || test_fail
+	check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 1 6 || test_fail
+done
+
+servers=2
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsprocesses 0
+ntsrotate 0
+ntsntpserver node2.net1.clk
+port 11123
+ntsdumpdir tmp"
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntsdumpdir tmp"
+client_server_conf="server 192.168.123.1 $client_server_options"
+
+rm -f tmp/*.nts
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_log_messages "Could not change" 0 0 || test_fail
+check_file_messages "	3	1	.*	4460	1	0	2" 1 1 log.packets || test_fail
+check_file_messages "	3	2	.*	4460	" 0 0 log.packets || test_fail
+
+for dns in 1 0; do
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+
+	check_log_messages "Could not change" 0 0 || test_fail
+	check_file_messages "	3	1	.*	4460	1	0	2" 0 0 log.packets || test_fail
+	check_file_messages "	3	2	.*	4460	" 0 0 log.packets || test_fail
+done
+
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsprocesses 0
+ntsrotate 0
+ntsdumpdir tmp"
+
+head -n 8 tmp/192.168.123.1.nts > tmp/192.168.123.1.nts_
+mv tmp/192.168.123.1.nts_ tmp/192.168.123.1.nts
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_log_messages "Could not change" 0 0 || test_fail
+check_file_messages "	3	1	.*	4460	1	0	2" 1 1 log.packets || test_fail
+check_file_messages "	3	2	.*	4460	" 0 0 log.packets || test_fail
+check_file_messages "	3	1	.*	11123	" 0 0 log.packets || test_fail
+check_file_messages "	3	2	.*	123	" 0 0 log.packets || test_fail
+check_file_messages "	3	2	.*	11123	" 3 3 log.packets || test_fail
+
+dns=1
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsserverkey tmp/server2.key
+ntsservercert tmp/server2.crt
+ntsprocesses 0"
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntstrustedcerts tmp/server2.crt
+minsources 2"
+client_server_conf=""
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntstrustedcerts 1 tmp/server1.crt
+ntstrustedcerts 2 tmp/server2.crt
+ntstrustedcerts 3 tmp/server2.crt"
+client_server_conf="
+server node1.net1.clk $client_server_options certset 0
+server node2.net1.clk $client_server_options certset 2"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "	3	1	.*	123	" 100 200 log.packets || test_fail
+check_file_messages "	3	2	.*	123	" 100 200 log.packets || test_fail
+
+client_server_conf="
+server node1.net1.clk $client_server_options certset 2
+server node2.net1.clk $client_server_options"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages "	3	1	.*	123	" 0 0 log.packets || test_fail
+check_file_messages "	3	2	.*	123	" 0 0 log.packets || test_fail
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/nosuch.crt
+ntstrustedcerts 2 tmp/nosuch.crt"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages "	3	1	.*	123	" 0 0 log.packets || test_fail
+check_file_messages "	3	2	.*	123	" 0 0 log.packets || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/140-noclientlog b/chrony/test/simulation/140-noclientlog
new file mode 100755
index 0000000..502398f
--- /dev/null
+++ b/chrony/test/simulation/140-noclientlog
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "noclientlog option"
+
+server_conf="noclientlog"
+client_server_options="xleave"
+client_conf="
+logdir tmp
+log rawmeasurements"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "111 111 1111.* 4B " 30 200 measurements.log || test_fail
+check_file_messages "111 111 1111.* 4I " 0 0 measurements.log || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/141-copy b/chrony/test/simulation/141-copy
new file mode 100755
index 0000000..80e56bc
--- /dev/null
+++ b/chrony/test/simulation/141-copy
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "copy option"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+client_server_options="copy"
+chronyc_conf="tracking"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+check_chronyc_output "^Reference ID *: 7F7F0101 \(192\.168\.123\.1\)
+Stratum *: 1" || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/142-ptpport b/chrony/test/simulation/142-ptpport
new file mode 100755
index 0000000..060932c
--- /dev/null
+++ b/chrony/test/simulation/142-ptpport
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "PTP port"
+
+# Block communication between 3 and 1
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
+
+cat > tmp/peer.keys <<-EOF
+1 MD5 1234567890
+EOF
+
+clients=2
+peers=2
+max_sync_time=420
+
+server_conf="
+ptpport 319"
+client_conf="
+ptpport 319
+authselectmode ignore
+keyfile tmp/peer.keys"
+client_server_options="minpoll 6 maxpoll 6 port 319"
+client_peer_options="minpoll 6 maxpoll 6 port 319 key 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "	2	1	.*	319	319	1	96	" 150 160 \
+	log.packets || test_fail
+check_file_messages "	1	2	.*	319	319	1	96	" 150 160 \
+	log.packets || test_fail
+check_file_messages "	2	3	.*	319	319	1	116	" 150 160 \
+	log.packets || test_fail
+check_file_messages "	3	2	.*	319	319	1	116	" 150 160 \
+	log.packets || test_fail
+
+test_pass
diff --git a/chrony/test/simulation/143-manual b/chrony/test/simulation/143-manual
new file mode 100755
index 0000000..618cee6
--- /dev/null
+++ b/chrony/test/simulation/143-manual
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+export TZ=UTC
+
+test_start "manual input"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+limit=$[12 * 3600]
+client_server_conf=" "
+client_conf="manual"
+chronyc_conf="timeout 4000000
+settime 1:00:00
+settime 2:00:00
+settime 3:00:00
+settime 4:00:00
+manual delete 2
+settime 6:00:00
+manual list
+settime 8:00:00
+manual reset
+settime 10:00:00
+manual list"
+chronyc_start=1800
+base_delay=1800
+jitter=1e-6
+
+time_max_limit=4e-3
+freq_max_limit=4e-3
+time_rms_limit=2e-3
+freq_rms_limit=2e-5
+min_sync_time=7204
+max_sync_time=7206
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+
+check_chronyc_output "^200 OK
+Clock was 0\.4. seconds fast\.  Frequency change = 0\.00ppm, new frequency = 0\.00ppm
+200 OK
+Clock was 0\.3. seconds fast\.  Frequency change = (99|100)\...ppm, new frequency = (99|100)\...ppm
+200 OK
+Clock was \-?0\.0. seconds fast\.  Frequency change = \-?0\.[01].ppm, new frequency = (99|100)\...ppm
+200 OK
+Clock was \-?0\.0. seconds fast\.  Frequency change = \-?0\.[01].ppm, new frequency = (99|100)\...ppm
+200 OK
+200 OK
+Clock was \-?0\.0. seconds fast\.  Frequency change = \-?0\.[012].ppm, new frequency = (99|100)\...ppm
+210 n_samples = 4
+#    Date     Time\(UTC\)    Slewed   Original   Residual
+=======================================================
+ 0 2010-01-01 (00:59:59|01:00:00)      [- ]0\.00       0\.46      [- ]0\.00
+ 1 2010-01-01 (01:59:59|02:00:00)      [- ]0\.00       0\.36      [- ]0\.00
+ 2 2010-01-01 (03:59:59|04:00:00)      [- ]0\.00      [- ]0\.00      [- ]0\.00
+ 3 2010-01-01 (05:59:59|06:00:00)      [- ]0\.00      [- ]0\.00      [- ]0\.00
+200 OK
+Clock was \-?0\.0. seconds fast\.  Frequency change = \-?0\.[012].ppm, new frequency = (99|100)\...ppm
+200 OK
+200 OK
+Clock was \-?0\.0. seconds fast\.  Frequency change = \-?0\.00ppm, new frequency = (99|100)\...ppm
+210 n_samples = 1
+#    Date     Time\(UTC\)    Slewed   Original   Residual
+=======================================================
+ 0 2010-01-01 (09:59:59|10:00:00)      [- ]0\.00      [- ]0\.00      [- ]0\.00$" \
+	|| test_fail
+
+test_pass
diff --git a/chrony/test/simulation/144-exp1 b/chrony/test/simulation/144-exp1
new file mode 100755
index 0000000..4a2042d
--- /dev/null
+++ b/chrony/test/simulation/144-exp1
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "experimental extension field"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+primary_time_offset=0.1
+server_strata=4
+min_sync_time=2000
+max_sync_time=2300
+chronyc_conf="doffset 0.1"
+chronyc_options="-h /clknetsim/unix/1:1"
+chronyc_start=2000
+
+for options in "extfield F323" "xleave extfield F323"; do
+	client_server_options="minpoll 6 maxpoll 6 $options"
+	server_server_options="$client_server_options"
+
+	run_test || test_fail
+	check_chronyd_exit || test_fail
+	check_source_selection || test_fail
+	check_sync || test_fail
+done
+
+server_server_options=""
+server_strata=1
+clients=4
+peers=4
+max_sync_time=2400
+# chain of peers and one enabled chronyc
+base_delay=$(cat <<-EOF | tr -d '\n'
+  (+ 1e-4 -1
+     (equal 0.1 from (+ to 1))
+     (equal 0.1 from (+ to -1))
+     (equal 0.1 from 6)
+     (equal 0.1 to 6))
+EOF
+)
+
+for lpoll in 5 6 7; do
+	for options in "minsamples 16 extfield F323" "minsamples 16 xleave extfield F323"; do
+		client_lpeer_options="minpoll $lpoll maxpoll $lpoll $options"
+		client_rpeer_options="minpoll 6 maxpoll 6 $options"
+		client_server_options="$client_rpeer_options"
+
+		run_test || test_fail
+		check_chronyd_exit || test_fail
+		check_source_selection || test_fail
+		check_sync || test_fail
+	done
+done
+
+test_pass
diff --git a/chrony_3_3/test/simulation/201-freqaccumulation b/chrony/test/simulation/201-freqaccumulation
similarity index 96%
rename from chrony_3_3/test/simulation/201-freqaccumulation
rename to chrony/test/simulation/201-freqaccumulation
index 7a9c22c..6f14246 100755
--- a/chrony_3_3/test/simulation/201-freqaccumulation
+++ b/chrony/test/simulation/201-freqaccumulation
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/202-prefer b/chrony/test/simulation/202-prefer
similarity index 94%
rename from chrony_3_3/test/simulation/202-prefer
rename to chrony/test/simulation/202-prefer
index ae24848..207c800 100755
--- a/chrony_3_3/test/simulation/202-prefer
+++ b/chrony/test/simulation/202-prefer
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 . ./test.common
 
diff --git a/chrony_3_3/test/simulation/README b/chrony/test/simulation/README
similarity index 100%
rename from chrony_3_3/test/simulation/README
rename to chrony/test/simulation/README
diff --git a/chrony_3_3/test/simulation/run b/chrony/test/simulation/run
similarity index 98%
rename from chrony_3_3/test/simulation/run
rename to chrony/test/simulation/run
index 463e8dd..0954438 100755
--- a/chrony_3_3/test/simulation/run
+++ b/chrony/test/simulation/run
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 print_help() {
 	echo "$1 [-a] [-i ITERATIONS] [-m MAXFAILS] [-s SEED] [TEST]..."
diff --git a/chrony_3_3/test/simulation/test.common b/chrony/test/simulation/test.common
similarity index 78%
rename from chrony_3_3/test/simulation/test.common
rename to chrony/test/simulation/test.common
index 0cd0fce..70bbde1 100644
--- a/chrony_3_3/test/simulation/test.common
+++ b/chrony/test/simulation/test.common
@@ -27,6 +27,7 @@
 # Default test testings
 
 default_limit=10000
+default_primary_time_offset=0.0
 default_time_offset=1e-1
 default_freq_offset=1e-4
 default_base_delay=1e-4
@@ -62,7 +63,9 @@
 default_server_conf=""
 default_client_conf=""
 default_chronyc_conf=""
-default_chronyd_options=""
+default_server_chronyd_options=""
+default_client_chronyd_options=""
+default_chronyc_options=""
 
 default_time_max_limit=1e-3
 default_freq_max_limit=5e-4
@@ -74,16 +77,20 @@
 default_client_min_mean_out_interval=0.0
 default_client_max_min_out_interval=inf
 
+default_cmdmon_unix=1
+default_dns=0
+
 # Initialize test settings from their defaults
-for defopt in $(declare | grep '^default_'); do
-	defoptname=${defopt%%=*}
+for defoptname in ${!default_*}; do
 	optname=${defoptname#default_}
-	eval "[ -z \"\${$optname:+a}\" ] && $optname=\"\$$defoptname\""
+	[ -z "${!optname}" ] && declare "$optname"="${!defoptname}"
 done
 
 test_start() {
-	rm -f tmp/*
+	rm -rf tmp/*
 	echo "Testing $@:"
+
+	check_config_h 'FEAT_NTP 1' || test_skip
 }
 
 test_pass() {
@@ -178,6 +185,16 @@
 	echo $[$servers * $server_strata + $clients]
 }
 
+get_node_name() {
+	local index=$1
+
+	if [ $dns -ne 0 ]; then
+		echo "node$index.net1.clk"
+	else
+		echo "192.168.123.$index"
+	fi
+}
+
 get_chronyd_conf() {
 	local i stratum=$1 peer=$2
 
@@ -186,31 +203,38 @@
 		echo "$server_conf"
 	elif [ $stratum -le $server_strata ]; then
 		for i in $(seq 1 $servers); do
-			echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $server_server_options"
+			echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $server_server_options"
 		done
 		for i in $(seq 1 $peers); do
 			[ $i -eq $peer -o $i -gt $servers ] && continue
-			echo -n "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $server_peer_options "
+			echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $server_peer_options "
 			[ $i -lt $peer ] && echo "$server_lpeer_options" || echo "$server_rpeer_options"
 		done
 		echo "$server_conf"
 	else
+		echo "deny"
 		if [ -n "$client_server_conf" ]; then
 			echo "$client_server_conf"
 		else
 			for i in $(seq 1 $servers); do
-				echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $client_server_options"
+				echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $client_server_options"
 			done
 		fi
 		for i in $(seq 1 $peers); do
 			[ $i -eq $peer -o $i -gt $clients ] && continue
-			echo -n "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $client_peer_options "
+			echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $client_peer_options "
 			[ $i -lt $peer ] && echo "$client_lpeer_options" || echo "$client_rpeer_options"
 		done
 		echo "$client_conf"
 	fi
 }
 
+# Check if chrony was built with specified option in config.h
+check_config_h() {
+	local pattern=$1
+	grep -q "^#define $pattern" ../../config.h
+}
+
 # Check if the clock was well synchronized
 check_sync() {
 	local i sync_time max_time_error max_freq_error ret=0
@@ -253,8 +277,9 @@
 	for i in $(seq 1 $(get_chronyd_nodes)); do
 		test_message 3 0 "node $i:"
 
-		tail -n 1 tmp/log.$i | grep -q 'chronyd exiting' && \
+		grep -q 'chronyd exiting' tmp/log.$i && \
 			! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
+			! grep -q 'Assertion.*failed' tmp/log.$i && \
 			test_ok || test_bad
 		[ $? -eq 0 ] || ret=1
 	done
@@ -337,6 +362,43 @@
 	return $ret
 }
 
+# Check the number of messages matching a pattern in the client logs
+check_log_messages() {
+	local i count ret=0 pattern=$1 min=$2 max=$3
+
+	test_message 2 1 "checking number of messages \"$pattern\":"
+
+	for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
+		count=$(grep "$pattern" tmp/log.$i | wc -l)
+		test_message 3 0 "node $i: $count"
+
+		[ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
+			test_ok || test_bad
+		[ $? -eq 0 ] || ret=1
+	done
+
+	return $ret
+}
+
+# Check the number of messages matching a pattern in a specified file
+check_file_messages() {
+	local i count ret=0 pattern=$1 min=$2 max=$3
+	shift 3
+
+	test_message 2 1 "checking number of messages \"$pattern\":"
+
+	for i; do
+		count=$(grep "$pattern" tmp/$i | wc -l)
+		test_message 3 0 "$i: $count"
+
+		[ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
+			test_ok || test_bad
+		[ $? -eq 0 ] || ret=1
+	done
+
+	return $ret
+}
+
 # Check if only NTP port (123) was used
 check_packet_port() {
 	local i ret=0 port=123
@@ -346,9 +408,9 @@
 	for i in $(seq 1 $(get_chronyd_nodes)); do
 		test_message 3 0 "node $i:"
 
-		grep -E -q "	$port	[0-9]+\$" tmp/log.packets && \
+		grep -E -q "^([0-9e.+-]+	){5}$port	" tmp/log.packets && \
 			! grep -E "^[0-9e.+-]+	$i	" tmp/log.packets | \
-			grep -E -q -v "	$port	[0-9]+\$" && \
+			grep -E -q -v "^([0-9e.+-]+	){5}$port	" && \
 			test_ok || test_bad
 		[ $? -eq 0 ] || ret=1
 	done
@@ -358,14 +420,13 @@
 
 # Print test settings which differ from default value
 print_nondefaults() {
-	local defopt defoptname optname
+	local defoptname optname
 
 	test_message 2 1 "non-default settings:"
-	declare | grep '^default_*' | while read defopt; do
-		defoptname=${defopt%%=*}
+	for defoptname in ${!default_*}; do
 		optname=${defoptname#default_}
-		eval "[ \"\$$optname\" = \"\$$defoptname\" ]" || \
-			test_message 3 1 $(eval "echo $optname=\$$optname")
+		[ "${!defoptname}" = "${!optname}" ] || \
+			test_message 3 1 $optname=${!optname}
 	done
 }
 
@@ -375,6 +436,7 @@
 	test_message 2 0 "running simulation:"
 
 	start_server $nodes \
+		-n 2 \
 		-o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \
 		-R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \
 		-r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \
@@ -382,7 +444,7 @@
 }
 
 run_test() {
-	local i j n stratum node nodes step start freq offset conf
+	local i j n stratum node nodes step start freq offset conf options
 
 	test_message 1 1 "network with $servers*$server_strata servers and $clients clients:"
 	print_nondefaults
@@ -390,10 +452,11 @@
 	nodes=$(get_chronyd_nodes)
 	[ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients]
 
+	export CLKNETSIM_UNIX_SUBNET=$[$cmdmon_unix != 0 ? 2 : 0]
+
 	for i in $(seq 1 $nodes); do
 		echo "node${i}_shift_pll = $shift_pll"
 		for j in $(seq 1 $nodes); do
-			[ $i -eq $j ] && continue
 			echo "node${i}_delay${j} = $(get_delay_expr up)"
 			echo "node${j}_delay${i} = $(get_delay_expr down)"
 		done
@@ -410,17 +473,21 @@
 				step=$server_step
 				start=$server_start
 				freq=""
-				[ $i -le $falsetickers ] && offset=$i.0 || offset=0.0
+				[ $i -le $falsetickers ] &&
+					offset=$i.0 || offset=$primary_time_offset
+				options=$server_chronyd_options
 			elif [ $stratum -le $server_strata ]; then
 				step=$server_step
 				start=$server_start
 				freq=$(get_wander_expr)
 				offset=0.0
+				options=$server_chronyd_options
 			else
 				step=$client_step
 				start=$client_start
 				freq=$(get_wander_expr)
 				offset=$time_offset
+				options=$client_chronyd_options
 			fi
 
 			conf=$(get_chronyd_conf $stratum $i $n)
@@ -431,7 +498,7 @@
 				echo "node${node}_refclock = $(get_refclock_expr)" >> tmp/conf
 			echo "node${node}_offset = $offset" >> tmp/conf
 			echo "node${node}_start = $start" >> tmp/conf
-			start_client $node chronyd "$conf" "" "$chronyd_options" && \
+			start_client $node chronyd "$conf" "" "$options" && \
 				test_ok || test_error
 
 			[ $? -ne 0 ] && return 1
@@ -442,9 +509,15 @@
 	for i in $(seq 1 $[$nodes - $node + 1]); do
 		test_message 2 0 "starting node $node:"
 
+		options=$([ $dns -eq 0 ] && printf "%s" "-n")
+		if [ $cmdmon_unix -ne 0 ]; then
+			options+=" -h /clknetsim/unix/$[$node - $clients]:1"
+		else
+			options+=" -h $(get_node_name $[$node - $clients])"
+		fi
+
 		echo "node${node}_start = $chronyc_start" >> tmp/conf
-		start_client $node chronyc "$chronyc_conf" "" \
-			"-n -h 192.168.123.$[$node - $clients]" && \
+		start_client $node chronyc "$chronyc_conf" "" "$options $chronyc_options" && \
 			test_ok || test_error
 
 		[ $? -ne 0 ] && return 1
diff --git a/chrony/test/system/001-minimal b/chrony/test/system/001-minimal
new file mode 100755
index 0000000..107fa3f
--- /dev/null
+++ b/chrony/test/system/001-minimal
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "minimal configuration"
+
+minimal_config=1
+
+start_chronyd || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+
+test_pass
diff --git a/chrony/test/system/002-extended b/chrony/test/system/002-extended
new file mode 100755
index 0000000..7a6734f
--- /dev/null
+++ b/chrony/test/system/002-extended
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "extended configuration"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+test_pass
diff --git a/chrony/test/system/003-memlock b/chrony/test/system/003-memlock
new file mode 100755
index 0000000..e4ab1bf
--- /dev/null
+++ b/chrony/test/system/003-memlock
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "memory locking"
+
+extra_chronyd_options="-m"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+test_pass
diff --git a/chrony/test/system/004-priority b/chrony/test/system/004-priority
new file mode 100755
index 0000000..bf8a04b
--- /dev/null
+++ b/chrony/test/system/004-priority
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "process priority"
+
+extra_chronyd_options="-P 1"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+test_pass
diff --git a/chrony/test/system/006-privdrop b/chrony/test/system/006-privdrop
new file mode 100755
index 0000000..6d7b0c9
--- /dev/null
+++ b/chrony/test/system/006-privdrop
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+check_chronyd_features PRIVDROP || test_skip "PRIVDROP support disabled"
+
+user="nobody"
+
+test_start "dropping of root privileges"
+
+minimal_config=1
+
+start_chronyd || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+
+test_pass
diff --git a/chrony/test/system/007-cmdmon b/chrony/test/system/007-cmdmon
new file mode 100755
index 0000000..bbcc12f
--- /dev/null
+++ b/chrony/test/system/007-cmdmon
@@ -0,0 +1,180 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "chronyc commands"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+
+has_ipv6=$(check_chronyd_features IPV6 && ping6 -c 1 ::1 > /dev/null 2>&1 && echo 1 || echo 0)
+
+for command in \
+	"allow 1.2.3.4" \
+	"deny 1.2.3.4" \
+	"cmddeny" \
+	"cmdallow" \
+	"cmddeny 1.2.3.4" \
+	"cmdallow 1.2.3.4" \
+	"add server 127.123.1.1" \
+	"delete 127.123.1.1" \
+	"burst 1/1" \
+	"cyclelogs" \
+	"dfreq 1.0e-3" \
+	"doffset -0.1" \
+	"dump" \
+	"offline" \
+	"local off" \
+	"local" \
+	"online" \
+	"onoffline" \
+	"maxdelay $server 1e-1" \
+	"maxdelaydevratio $server 5.0" \
+	"maxdelayratio $server 3.0" \
+	"maxpoll $server 12" \
+	"maxupdateskew $server 10.0" \
+	"minpoll $server 10" \
+	"minstratum $server 1" \
+	"polltarget $server 10" \
+	"refresh" \
+	"rekey" \
+	"reload sources" \
+	"reselect" \
+	"reselectdist 1e-3" \
+	"reset sources" \
+	"smoothtime reset" \
+	"smoothtime activate" \
+; do
+	run_chronyc "$command" || test_fail
+	check_chronyc_output "^200 OK$" || test_fail
+done
+
+run_chronyc "accheck $server" || test_fail
+check_chronyc_output "^208 Access allowed$" || test_fail
+run_chronyc "accheck 1.2.3.4" || test_fail
+check_chronyc_output "^209 Access denied$" || test_fail
+
+run_chronyc "cmdaccheck 1.2.3.4" || test_fail
+check_chronyc_output "^208 Access allowed$" || test_fail
+
+run_chronyc "authdata" || test_fail
+check_chronyc_output "^Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+127\.0\.0\.1                      -     0    0    0    -    0    0    0    0$" \
+	|| test_chronyc
+
+run_chronyc "clients" || test_fail
+check_chronyc_output "^Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
+===============================================================================
+127\.0\.0\.1               [0-9 ]+    0 [-0-9 ]+   -  [ 0-9]+       0      0   -     -$" \
+	|| test_fail
+
+run_chronyc "ntpdata $server" || test_fail
+check_chronyc_output "^Remote address  : 127\.0\.0\.1 \(7F000001\)
+Remote port     : [0-9]+
+Local address   : 127\.0\.0\.1 \(7F000001\)
+Leap status     : Normal
+Version         : 4
+Mode            : Server
+Stratum         : 10
+Poll interval   : (-6|[0-9]+) \([0-9]+ seconds\)
+Precision       : [0-9 +-]+ \(0\.[0-9]+ seconds\)
+Root delay      : 0\.000000 seconds
+Root dispersion : 0\.000000 seconds
+Reference ID    : 7F7F0101 \(\)
+Reference time  : [A-Za-z0-9: ]+
+Offset          : [+-]0\.......... seconds
+Peer delay      : 0\.......... seconds
+Peer dispersion : 0\.......... seconds
+Response time   : 0\.......... seconds
+Jitter asymmetry: \+0\.00
+NTP tests       : 111 111 1110
+Interleaved     : No
+Authenticated   : No
+TX timestamping : (Daemon|Kernel)
+RX timestamping : (Daemon|Kernel)
+Total TX        : [0-9]+
+Total RX        : [0-9]+
+Total valid RX  : [0-9]+$" || test_fail
+
+run_chronyc "selectdata" || test_fail
+check_chronyc_output "^S Name/IP Address        Auth COpts EOpts Last Score     Interval  Leap
+=======================================================================
+M 127\.0\.0\.1                 N ----- -----    0   1\.0    \+0ns    \+0ns  \?$" || test_fail
+
+run_chronyc "serverstats" || test_fail
+check_chronyc_output "^NTP packets received       : [0-9]+
+NTP packets dropped        : 0
+Command packets received   : [0-9]+
+Command packets dropped    : 0
+Client log records dropped : 0
+NTS-KE connections accepted: 0
+NTS-KE connections dropped : 0
+Authenticated NTP packets  : 0
+Interleaved NTP packets    : 0
+NTP timestamps held        : 0
+NTP timestamp span         : 0$"|| test_fail
+
+run_chronyc "manual on" || test_fail
+check_chronyc_output "^200 OK$" || test_fail
+
+run_chronyc "settime now" || test_fail
+check_chronyc_output "^200 OK
+Clock was.*$" || test_fail
+
+run_chronyc "manual delete 0" || test_fail
+check_chronyc_output "^200 OK$" || test_fail
+
+run_chronyc "settime now" || test_fail
+check_chronyc_output "^200 OK
+Clock was.*$" || test_fail
+
+run_chronyc "manual list" || test_fail
+check_chronyc_output "^210 n_samples = 1
+#    Date     Time\(UTC\)    Slewed   Original   Residual
+=======================================================
+ 0.*$" || test_fail
+
+run_chronyc "manual reset" || test_fail
+check_chronyc_output "^200 OK$" || test_fail
+
+run_chronyc "manual off" || test_fail
+check_chronyc_output "^200 OK$" || test_fail
+
+run_chronyc "shutdown" || test_fail
+check_chronyc_output "^200 OK$" || test_fail
+
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+start_chronyd || test_fail
+
+run_chronyc "makestep" && test_fail
+check_chronyc_output "500 Failure" || test_fail
+
+run_chronyc "trimrtc" && test_fail
+check_chronyc_output "513 RTC driver not running" || test_fail
+
+run_chronyc "writertc" && test_fail
+check_chronyc_output "513 RTC driver not running" || test_fail
+
+chronyc_host=127.0.0.1
+
+run_chronyc "tracking" || test_fail
+check_chronyc_output "^Reference ID" || test_fail
+
+run_chronyc "makestep" && test_fail
+check_chronyc_output "^501 Not authorised$" || test_fail
+
+if [ "$has_ipv6" = "1" ]; then
+	chronyc_host=::1
+
+	run_chronyc "tracking" || test_fail
+	check_chronyc_output "^Reference ID" || test_fail
+
+	run_chronyc "makestep" && test_fail
+	check_chronyc_output "^501 Not authorised$" || test_fail
+fi
+
+stop_chronyd || test_fail
+
+test_pass
diff --git a/chrony/test/system/008-confload b/chrony/test/system/008-confload
new file mode 100755
index 0000000..a7fc1d5
--- /dev/null
+++ b/chrony/test/system/008-confload
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "loading of configuration"
+
+minimal_config=1
+extra_chronyd_directives="
+include $TEST_DIR/conf1.d/conf.1
+confdir $TEST_DIR/conf1.d
+confdir $TEST_DIR/conf2.d $TEST_DIR/conf3.d $TEST_DIR/conf4.d
+sourcedir $TEST_DIR/conf5.d
+include $TEST_DIR/conf1.d/conf.2"
+
+mkdir $TEST_DIR/conf{1,2,3,4,5}.d
+
+echo "server 127.123.1.1" > $TEST_DIR/conf1.d/conf.1
+echo "server 127.123.1.2" > $TEST_DIR/conf1.d/conf.2
+echo "server 127.123.1.3" > $TEST_DIR/conf1.d/3.conf
+echo "server 127.123.1.4" > $TEST_DIR/conf1.d/4.conf
+echo "server 127.123.2.2" > $TEST_DIR/conf2.d/2.conf
+echo "server 127.123.2.3" > $TEST_DIR/conf2.d/3.conf
+echo "server 127.123.3.1" > $TEST_DIR/conf3.d/1.conf
+echo "server 127.123.3.2" > $TEST_DIR/conf3.d/2.conf
+echo "server 127.123.3.3" > $TEST_DIR/conf3.d/3.conf
+echo "server 127.123.4.1" > $TEST_DIR/conf4.d/1.conf
+echo "server 127.123.4.2" > $TEST_DIR/conf4.d/2.conf
+echo "server 127.123.4.3" > $TEST_DIR/conf4.d/3.conf
+echo "server 127.123.4.4" > $TEST_DIR/conf4.d/4.conf
+echo "server 127.123.5.1" > $TEST_DIR/conf5.d/1.sources
+echo "server 127.123.5.2" > $TEST_DIR/conf5.d/2.sources
+echo "server 127.123.5.3" > $TEST_DIR/conf5.d/3.sources
+
+start_chronyd || test_fail
+
+run_chronyc "sources" || test_fail
+check_chronyc_output "^[^=]*
+=*
+.. 127\.123\.1\.1 [^^]*
+.. 127\.123\.1\.3 [^^]*
+.. 127\.123\.1\.4 [^^]*
+.. 127\.123\.3\.1 [^^]*
+.. 127\.123\.2\.2 [^^]*
+.. 127\.123\.2\.3 [^^]*
+.. 127\.123\.4\.4 [^^]*
+.. 127\.123\.1\.2 [^^]*
+.. 127\.123\.5\.1 [^^]*
+.. 127\.123\.5\.2 [^^]*
+.. 127\.123\.5\.3 [^^]*$" || test_fail
+
+rm $TEST_DIR/conf5.d/1.sources
+echo "server 127.123.5.2 minpoll 7" > $TEST_DIR/conf5.d/2.sources
+echo > $TEST_DIR/conf5.d/3.sources
+echo "server 127.123.5.4" > $TEST_DIR/conf5.d/4.sources
+
+run_chronyc "reload sources" || test_fail
+
+run_chronyc "sources" || test_fail
+check_chronyc_output "^[^=]*
+=*
+.. 127\.123\.1\.1 [^^]*
+.. 127\.123\.1\.3 [^^]*
+.. 127\.123\.1\.4 [^^]*
+.. 127\.123\.3\.1 [^^]*
+.. 127\.123\.2\.2 [^^]*
+.. 127\.123\.2\.3 [^^]*
+.. 127\.123\.4\.4 [^^]*
+.. 127\.123\.1\.2 *[05]   6 [^^]*
+.. 127\.123\.5\.2 *[05]   7 [^^]*
+.. 127\.123\.5\.4 [^^]*$" || test_fail
+
+stop_chronyd || test_fail
+
+test_pass
diff --git a/chrony/test/system/009-binddevice b/chrony/test/system/009-binddevice
new file mode 100755
index 0000000..fc64ae2
--- /dev/null
+++ b/chrony/test/system/009-binddevice
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+[ "$(uname -s)" = "Linux" ] || test_skip "non-Linux system"
+
+test_start "binddevice directives"
+
+extra_chronyd_directives="
+binddevice lo
+bindacqdevice lo
+bindcmddevice lo"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+
+run_chronyc "ntpdata $server" || test_fail
+check_chronyc_output "^Remote address" || test_fail
+
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+test_pass
diff --git a/chrony/test/system/010-nts b/chrony/test/system/010-nts
new file mode 100755
index 0000000..a0b366e
--- /dev/null
+++ b/chrony/test/system/010-nts
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+check_chronyd_features NTS || test_skip "NTS support disabled"
+certtool --help &> /dev/null || test_skip "certtool missing"
+
+test_start "NTS authentication"
+
+cat > $TEST_DIR/cert.cfg <<EOF
+cn = "chrony-nts-test"
+dns_name = "chrony-nts-test"
+ip_address = "$server"
+serial = 001
+activation_date = "$[$(date '+%Y') - 1]-01-01 00:00:00 UTC"
+expiration_date = "$[$(date '+%Y') + 2]-01-01 00:00:00 UTC"
+signing_key
+encryption_key
+EOF
+
+certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \
+	&> $TEST_DIR/certtool.log
+certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \
+	--template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log
+chown $user $TEST_DIR/server.*
+
+ntpport=$(get_free_port)
+ntsport=$(get_free_port)
+
+server_options="port $ntpport nts ntsport $ntsport"
+extra_chronyd_directives="
+port $ntpport
+ntsport $ntsport
+ntsserverkey $TEST_DIR/server.key
+ntsservercert $TEST_DIR/server.crt
+ntstrustedcerts $TEST_DIR/server.crt
+ntsdumpdir $TEST_LIBDIR
+ntsprocesses 3"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+
+run_chronyc "authdata" || test_fail
+check_chronyc_output "^Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+127\.0\.0\.1                    NTS     1   15  256    [0-9]    0    0    [78]  100$" || test_fail
+
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+server_options="port $ntpport nts ntsport $((ntsport + 1))"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+
+run_chronyc "authdata" || test_fail
+check_chronyc_output "^Name/IP address             Mode KeyID Type KLen Last Atmp  NAK Cook CLen
+=========================================================================
+127\.0\.0\.1                    NTS     1   15  256    [0-9]    0    0    [78]  100$" || test_fail
+
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+test_pass
diff --git a/chrony/test/system/099-scfilter b/chrony/test/system/099-scfilter
new file mode 100755
index 0000000..6b098ac
--- /dev/null
+++ b/chrony/test/system/099-scfilter
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
+
+test_start "system call filter in non-destructive tests"
+
+for level in "-1" "1" "-2" "2"; do
+	test_message 1 1 "level $level:"
+	for test in 0[0-8][0-9]-*[^_]; do
+		test_message 2 0 "$test"
+		TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null
+		result=$?
+
+		if [ $result != 0 ] && [ $result != 9 ] ; then
+			test_bad
+			test_fail
+		fi
+		test_ok
+	done
+done
+
+test_pass
diff --git a/chrony/test/system/100-clockupdate b/chrony/test/system/100-clockupdate
new file mode 100755
index 0000000..191e461
--- /dev/null
+++ b/chrony/test/system/100-clockupdate
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "update of system clock"
+
+clock_control=1
+minimal_config=1
+
+start_chronyd || test_fail
+run_chronyc "dfreq 1e-3" || test_fail
+check_chronyc_output "200 OK" || test_fail
+
+before=$(date '+%s')
+run_chronyc "doffset -1.0" || test_fail
+check_chronyc_output "200 OK" || test_fail
+run_chronyc "makestep" || test_fail
+check_chronyc_output "200 OK" || test_fail
+after=$(date '+%s')
+
+test_message 1 0 "checking system clock"
+[ "$before" -lt "$after" ] && test_ok || test_bad || test_fail
+
+run_chronyc "doffset 1.0" || test_fail
+run_chronyc "makestep" || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_message_count "System clock was stepped by" 2 2 || test_fail
+
+test_pass
diff --git a/chrony/test/system/101-rtc b/chrony/test/system/101-rtc
new file mode 100755
index 0000000..68bce68
--- /dev/null
+++ b/chrony/test/system/101-rtc
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+check_chronyd_features RTC || test_skip "RTC support disabled"
+[ -c "/dev/rtc" ] || test_skip "missing /dev/rtc"
+
+test_start "real-time clock"
+
+minimal_config=1
+extra_chronyd_options="-s"
+extra_chronyd_directives="rtcfile $TEST_DIR/rtcfile"
+echo "1 $(date +%s) 0.0 0.0" > "$TEST_DIR/rtcfile"
+
+start_chronyd || test_fail
+stop_chronyd || test_fail
+check_chronyd_message_count "\(clock off from RTC\|RTC time before last\|Could not \(enable\|disable\) RTC interrupt\)" 1 1 || test_fail
+
+test_pass
diff --git a/chrony/test/system/102-hwtimestamp b/chrony/test/system/102-hwtimestamp
new file mode 100755
index 0000000..6b86d20
--- /dev/null
+++ b/chrony/test/system/102-hwtimestamp
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+[ "$(uname -s)" = "Linux" ] || test_skip "non-Linux system"
+
+hwts_iface=""
+for iface_path in /sys/class/net/*; do
+	iface=$(basename "$iface_path")
+	if ethtool -T "$iface" 2> /dev/null | grep -q '	all\($\| \)'; then
+		hwts_iface="$iface"
+		break
+	fi
+done
+
+[ -n "$hwts_iface" ] || test_skip "no HW timestamping interface found"
+
+test_start "hardware timestamping"
+
+minimal_config=1
+extra_chronyd_directives="hwtimestamp $hwts_iface"
+
+start_chronyd || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_message_count "Enabled HW timestamping on $hwts_iface" 1 1 || test_fail
+
+test_pass
diff --git a/chrony/test/system/103-refclock b/chrony/test/system/103-refclock
new file mode 100755
index 0000000..e5b74e0
--- /dev/null
+++ b/chrony/test/system/103-refclock
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+check_chronyd_features REFCLOCK || test_skip "refclock support disabled"
+
+test_start "reference clocks"
+
+extra_chronyd_directives="
+refclock SOCK $TEST_DIR/refclock.sock
+refclock SHM 100"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+test_pass
diff --git a/chrony/test/system/104-systemdirs b/chrony/test/system/104-systemdirs
new file mode 100755
index 0000000..15508dc
--- /dev/null
+++ b/chrony/test/system/104-systemdirs
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+TEST_LIBDIR=${CHRONY_LIBDIR:-/var/lib/chrony}
+TEST_LOGDIR=${CHRONY_LOGDIR:-/var/log/chrony}
+TEST_RUNDIR=${CHRONY_RUNDIR:-/var/run/chrony}
+
+. ./test.common
+
+user=$(ls -ld "$TEST_RUNDIR" 2> /dev/null | awk '{print $3}')
+
+test_start "system directories"
+
+start_chronyd || test_fail
+wait_for_sync || test_fail
+stop_chronyd || test_fail
+check_chronyd_messages || test_fail
+check_chronyd_files || test_fail
+
+test_pass
diff --git a/chrony/test/system/199-scfilter b/chrony/test/system/199-scfilter
new file mode 100755
index 0000000..29b7cc3
--- /dev/null
+++ b/chrony/test/system/199-scfilter
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
+
+test_start "system call filter in destructive tests"
+
+for level in "-1" "1" "-2" "2"; do
+	test_message 1 1 "level $level:"
+	for test in 1[0-8][0-9]-*[^_]; do
+		test_message 2 0 "$test"
+		TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null
+		result=$?
+
+		if [ $result != 0 ] && [ $result != 9 ] ; then
+			test_bad
+			test_fail
+		fi
+		test_ok
+	done
+done
+
+test_pass
diff --git a/chrony/test/system/run b/chrony/test/system/run
new file mode 100755
index 0000000..5516ed4
--- /dev/null
+++ b/chrony/test/system/run
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+
+print_help() {
+	echo "$1 [-a] [-d] [TEST]..."
+}
+
+run_test() {
+	local result name=$1
+
+	if [ $destructive -ne 1 ] && [[ "$name" == 1[0-9][0-9]-* ]]; then
+		echo "SKIP (destructive test)"
+		return 9
+	fi
+
+	./$name
+	result=$?
+
+	if [ $result -ne 0 -a $result -ne 9 ]; then
+		if [ $abort_on_fail -ne 0 ]; then
+			exit 1
+		fi
+	fi
+
+	return $result
+}
+
+passed=() failed=() skipped=()
+
+abort_on_fail=0
+destructive=0
+
+while getopts ":ad" opt; do
+	case $opt in
+		a) abort_on_fail=1;;
+		d) destructive=1;;
+		*) print_help "$0"; exit 3;;
+	esac
+done
+
+shift $[$OPTIND - 1]
+
+[ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_])
+
+for test in "${tests[@]}"; do
+	printf "%s   " "$test"
+	run_test $test
+	result=$?
+	echo
+
+	case $result in
+		0) passed=(${passed[@]} $test);;
+		9) skipped=(${skipped[@]} $test);;
+		*) failed=(${failed[@]} $test);;
+	esac
+done
+
+echo
+echo "SUMMARY:"
+echo "  TOTAL  $[${#passed[@]} + ${#failed[@]} + ${#skipped[@]}]"
+echo "  PASSED ${#passed[@]}"
+echo "  FAILED ${#failed[@]}    (${failed[@]})"
+echo "  SKIPPED ${#skipped[@]}   (${skipped[@]})"
+
+[ ${#failed[@]} -eq 0 ]
diff --git a/chrony/test/system/test.common b/chrony/test/system/test.common
new file mode 100644
index 0000000..7005c9e
--- /dev/null
+++ b/chrony/test/system/test.common
@@ -0,0 +1,373 @@
+# Copyright (C) Miroslav Lichvar  2009
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+# 
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+export LC_ALL=C
+export PATH=${CHRONY_PATH:-../..}:$PATH
+
+TEST_DIR=${TEST_DIR:-$(pwd)/tmp}
+TEST_LIBDIR=${TEST_LIBDIR:-$TEST_DIR}
+TEST_LOGDIR=${TEST_LOGDIR:-$TEST_DIR}
+TEST_RUNDIR=${TEST_RUNDIR:-$TEST_DIR}
+TEST_SCFILTER=${TEST_SCFILTER:-0}
+
+test_start() {
+	check_chronyd_features NTP CMDMON || test_skip "NTP/CMDMON support disabled"
+
+	[ "${#TEST_DIR}" -ge 5 ] || test_skip "invalid TEST_DIR"
+
+	rm -rf "$TEST_DIR"
+	mkdir -p "$TEST_DIR" && chmod 700 "$TEST_DIR" || test_skip "could not create $TEST_DIR"
+
+	[ -d "$TEST_LIBDIR" ] || test_skip "missing $TEST_LIBDIR"
+	[ -d "$TEST_LOGDIR" ] || test_skip "missing $TEST_LOGDIR"
+	[ -d "$TEST_RUNDIR" ] || test_skip "missing $TEST_RUNDIR"
+
+	rm -f "$TEST_LIBDIR"/* "$TEST_LOGDIR"/* "$TEST_RUNDIR"/*
+
+	if [ "$user" != "root" ]; then
+		id -u "$user" > /dev/null 2> /dev/null || test_skip "missing user $user"
+		chown "$user:$(id -g "$user")" "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
+		su "$user" -s /bin/sh -c "touch $TEST_DIR/test" 2> /dev/null || \
+			test_skip "$user cannot access $TEST_DIR"
+		rm "$TEST_DIR/test"
+	fi
+
+	echo "Testing $*:"
+}
+
+test_pass() {
+	echo "PASS"
+	exit 0
+}
+
+test_fail() {
+	echo "FAIL"
+	exit 1
+}
+
+test_skip() {
+	local msg=$1
+
+	[ -n "$msg" ] && echo "SKIP ($msg)" || echo "SKIP"
+	exit 9
+}
+
+test_ok() {
+	pad_line
+	echo -e "\tOK"
+	return 0
+}
+
+test_bad() {
+	pad_line
+	echo -e "\tBAD"
+	return 1
+}
+
+test_error() {
+	pad_line
+	echo -e "\tERROR"
+	return 1
+}
+
+chronyd=$(command -v chronyd)
+chronyc=$(command -v chronyc)
+
+[ $EUID -eq 0 ] || test_skip "not root"
+
+[ -x "$chronyd" ] || test_skip "chronyd not found"
+[ -x "$chronyc" ] || test_skip "chronyc not found"
+
+if netstat -aln > /dev/null 2> /dev/null; then
+	port_list_command="netstat -aln"
+elif ss -atun > /dev/null 2> /dev/null; then
+	port_list_command="ss -atun"
+else
+	test_skip "missing netstat or ss"
+fi
+
+# Default test testings
+default_minimal_config=0
+default_extra_chronyd_directives=""
+default_extra_chronyd_options=""
+default_clock_control=0
+default_server=127.0.0.1
+default_server_name=127.0.0.1
+default_server_options=""
+default_user=root
+
+# Initialize test settings from their defaults
+for defoptname in ${!default_*}; do
+	optname=${defoptname#default_}
+	[ -z "${!optname}" ] && declare "$optname"="${!defoptname}"
+done
+
+msg_length=0
+pad_line() {
+	local line_length=56
+	[ $msg_length -lt $line_length ] && \
+		printf "%$((line_length - msg_length))s" ""
+	msg_length=0
+}
+
+# Print aligned message
+test_message() {
+	local level=$1 eol=$2
+	shift 2
+	local msg="$*"
+
+	while [ "$level" -gt 0 ]; do
+		echo -n "  "
+		level=$((level - 1))
+		msg_length=$((msg_length + 2))
+	done
+	echo -n "$msg"
+
+	msg_length=$((msg_length + ${#msg}))
+	if [ "$eol" -ne 0 ]; then
+		echo
+		msg_length=0
+	fi
+}
+
+# Check if chronyd has specified features
+check_chronyd_features() {
+	local feature features
+
+	features=$($chronyd -v | sed 's/.*(\(.*\)).*/\1/')
+
+	for feature; do
+		echo "$features" | grep -q "+$feature" || return 1
+	done
+}
+
+# Print test settings which differ from default value
+print_nondefaults() {
+	local defoptname optname
+
+	test_message 1 1 "non-default settings:"
+	for defoptname in ${!default_*}; do
+		optname=${defoptname#default_}
+		[ "${!defoptname}" = "${!optname}" ] || \
+			test_message 2 1 "$optname"=${!optname}
+	done
+}
+
+get_conffile() {
+	echo "$TEST_DIR/chronyd.conf"
+}
+
+get_pidfile() {
+	echo "$TEST_RUNDIR/chronyd.pid"
+}
+
+get_logfile() {
+	echo "$TEST_LOGDIR/chronyd.log"
+}
+
+get_cmdsocket() {
+	echo "$TEST_RUNDIR/chronyd.sock"
+}
+
+# Find a free port in the 10000-20000 range (their use is racy)
+get_free_port() {
+	local port
+
+	while true; do
+		port=$((RANDOM % 10000 + 10000))
+		$port_list_command | grep -q '^\(tcp\|udp\).*[:.]'"$port " && continue
+		break
+	done
+
+	echo $port
+}
+
+generate_chrony_conf() {
+	local ntpport cmdport
+
+	ntpport=$(get_free_port)
+	cmdport=$(get_free_port)
+
+	echo "0.0 10000" > "$TEST_LIBDIR/driftfile"
+	echo "1 MD5 abcdefghijklmnopq" > "$TEST_DIR/keys"
+	chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR/keys"
+	echo "0.0" > "$TEST_DIR/tempcomp"
+
+	(
+		echo "pidfile $(get_pidfile)"
+		echo "bindcmdaddress $(get_cmdsocket)"
+		echo "port $ntpport"
+		echo "cmdport $cmdport"
+
+		echo "$extra_chronyd_directives"
+
+		[ "$minimal_config" -ne 0 ] && exit 0
+
+		echo "allow"
+		echo "cmdallow"
+		echo "local"
+
+		echo "server $server_name port $ntpport minpoll -6 maxpoll -6 $server_options"
+
+		[ "$server" = "127.0.0.1" ] && echo "bindacqaddress $server"
+		echo "bindaddress 127.0.0.1"
+		echo "bindcmdaddress 127.0.0.1"
+		echo "dumpdir $TEST_RUNDIR"
+		echo "logdir $TEST_LOGDIR"
+		echo "log tempcomp rawmeasurements refclocks statistics tracking rtc"
+		echo "logbanner 0"
+		echo "smoothtime 100.0 0.001"
+		echo "leapsectz right/UTC"
+		echo "dscp 46"
+
+		echo "include /dev/null"
+		echo "keyfile $TEST_DIR/keys"
+		echo "driftfile $TEST_LIBDIR/driftfile"
+		echo "tempcomp $TEST_DIR/tempcomp 0.1 0 0 0 0"
+
+	) > "$(get_conffile)"
+}
+
+get_chronyd_options() {
+	[ "$clock_control" -eq 0 ] && echo "-x"
+	echo "-l $(get_logfile)"
+	echo "-f $(get_conffile)"
+	echo "-u $user"
+	echo "-F $TEST_SCFILTER"
+	echo "$extra_chronyd_options"
+}
+
+# Start a chronyd instance
+start_chronyd() {
+	local pid pidfile=$(get_pidfile)
+
+	print_nondefaults
+	test_message 1 0 "starting chronyd"
+
+	generate_chrony_conf
+
+	trap stop_chronyd EXIT
+
+	rm -f "$TEST_LOGDIR"/*.log
+
+	$CHRONYD_WRAPPER "$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1
+
+	[ $? -eq 0 ] && [ -f "$pidfile" ] && ps -p "$(cat "$pidfile")" > /dev/null && test_ok || test_error
+}
+
+wait_for_sync() {
+	local prev_length
+
+	test_message 1 0 "waiting for synchronization"
+	prev_length=$msg_length
+
+	for i in $(seq 1 10); do
+		run_chronyc "ntpdata $server" > /dev/null 2>&1 || break
+		if check_chronyc_output "Total RX +: [1-9]" > /dev/null 2>&1; then
+			msg_length=$prev_length
+			test_ok
+			return
+		fi
+		sleep 1
+	done
+
+	msg_length=$prev_length
+	test_error
+}
+
+# Stop the chronyd instance
+stop_chronyd() {
+	local pid pidfile
+
+	pidfile=$(get_pidfile)
+	[ -f "$pidfile" ] || return 0
+
+	pid=$(cat "$pidfile")
+
+	test_message 1 0 "stopping chronyd"
+
+	if ! kill "$pid" 2> /dev/null; then
+		test_error
+		return
+	fi
+
+	# Wait for the process to terminate (we cannot use "wait")
+	while ps -p "$pid" > /dev/null; do
+		sleep 0.1
+	done
+
+	test_ok
+}
+
+# Check chronyd log for expected and unexpected messages
+check_chronyd_messages() {
+	local logfile=$(get_logfile)
+
+	test_message 1 0 "checking chronyd messages"
+
+	grep -q 'chronyd exiting' "$logfile" && \
+		([ "$clock_control" -eq 0 ] || ! grep -q 'Disabled control of system clock' "$logfile") && \
+		([ "$clock_control" -ne 0 ] || grep -q 'Disabled control of system clock' "$logfile") && \
+		([ "$minimal_config" -ne 0 ] || grep -q 'Frequency .* read from' "$logfile") && \
+		grep -q 'chronyd exiting' "$logfile" && \
+		! grep -q 'Could not' "$logfile" && \
+		! grep -q 'Disabled command socket' "$logfile" && \
+		test_ok || test_bad
+}
+
+# Check the number of messages matching a pattern in a specified file
+check_chronyd_message_count() {
+	local count pattern=$1 min=$2 max=$3 logfile=$(get_logfile)
+
+	test_message 1 0 "checking message \"$pattern\""
+
+	count=$(grep "$pattern" "$(get_logfile)" | wc -l)
+
+	[ "$min" -le "$count" ] && [ "$count" -le "$max" ] && test_ok || test_bad
+}
+
+# Check the logs and dump file for measurements and a clock update
+check_chronyd_files() {
+	test_message 1 0 "checking chronyd files"
+
+	grep -q " $server .* 111 111 1110 " "$TEST_LOGDIR/measurements.log" && \
+		[ -f "$TEST_LOGDIR/tempcomp.log" ] && [ "$(wc -l < "$TEST_LOGDIR/tempcomp.log")" -ge 2 ] && \
+		test_ok || test_bad
+}
+
+# Run a chronyc command
+run_chronyc() {
+	local host=$chronyc_host options="-n -m"
+
+	test_message 1 0 "running chronyc $([ -n "$host" ] && echo "@$host ")$*"
+
+	if [ -z "$host" ]; then
+		host="$(get_cmdsocket)"
+	else
+		options="$options -p $(grep cmdport "$(get_conffile)" | awk '{print $2}')"
+	fi
+
+	$CHRONYC_WRAPPER "$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \
+		test_ok || test_error
+}
+
+# Compare chronyc output with specified pattern
+check_chronyc_output() {
+	local pattern=$1
+
+	test_message 1 0 "checking chronyc output"
+
+	[[ "$(cat "$TEST_DIR/chronyc.out")" =~ $pattern ]] && test_ok || test_bad
+}
diff --git a/chrony_3_3/test/unit/Makefile b/chrony/test/unit/Makefile
similarity index 64%
rename from chrony_3_3/test/unit/Makefile
rename to chrony/test/unit/Makefile
index 75f7861..64a1cd8 100644
--- a/chrony_3_3/test/unit/Makefile
+++ b/chrony/test/unit/Makefile
@@ -4,20 +4,23 @@
 CC = gcc
 CFLAGS = -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread
 CPPFLAGS = -I$(CHRONY_SRCDIR) 
-LDFLAGS =  -pie -Wl,-z,relro,-z,now -lm 
+LDFLAGS =  -pie -Wl,-z,relro,-z,now  -lm  
 
 SHARED_OBJS = test.o
 
 TEST_OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
 TESTS := $(patsubst %.o,%.test,$(filter-out $(SHARED_OBJS),$(TEST_OBJS)))
 
-FILTER_OBJS = %/main.o %/client.o %/getdate.o
-CHRONY_OBJS := $(filter-out $(FILTER_OBJS),$(wildcard $(CHRONY_SRCDIR)/*.o))
+CHRONYD_OBJS := $(patsubst %.o,$(CHRONY_SRCDIR)/%.o,$(filter-out main.o,\
+		  $(filter %.o,$(shell $(MAKE) -f $(CHRONY_SRCDIR)/Makefile \
+					print-chronyd-objects NODEPS=1))))
 
 all: $(TESTS)
 
-%.test: %.o $(SHARED_OBJS)
-	$(CC) $(CFLAGS) -o $@ $^ $(CHRONY_OBJS:%/$*.o=) $(LDFLAGS)
+$(CHRONYD_OBJS): ;
+
+%.test: %.o $(SHARED_OBJS) $(CHRONYD_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(filter-out $(CHRONY_SRCDIR)/$<,$^) $(LDFLAGS)
 
 %.o: %.c
 	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
@@ -30,7 +33,7 @@
 	exit $$ret
 
 clean:
-	rm -f *.o $(TESTS)
+	rm -f *.o *.gcda *.gcno core.* $(TESTS)
 	rm -rf .deps
 
 distclean: clean
diff --git a/chrony_3_3/test/unit/Makefile.in b/chrony/test/unit/Makefile.in
similarity index 65%
rename from chrony_3_3/test/unit/Makefile.in
rename to chrony/test/unit/Makefile.in
index 554663b..9979840 100644
--- a/chrony_3_3/test/unit/Makefile.in
+++ b/chrony/test/unit/Makefile.in
@@ -11,13 +11,16 @@
 TEST_OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
 TESTS := $(patsubst %.o,%.test,$(filter-out $(SHARED_OBJS),$(TEST_OBJS)))
 
-FILTER_OBJS = %/main.o %/client.o %/getdate.o
-CHRONY_OBJS := $(filter-out $(FILTER_OBJS),$(wildcard $(CHRONY_SRCDIR)/*.o))
+CHRONYD_OBJS := $(patsubst %.o,$(CHRONY_SRCDIR)/%.o,$(filter-out main.o,\
+		  $(filter %.o,$(shell $(MAKE) -f $(CHRONY_SRCDIR)/Makefile \
+					print-chronyd-objects NODEPS=1))))
 
 all: $(TESTS)
 
-%.test: %.o $(SHARED_OBJS)
-	$(CC) $(CFLAGS) -o $@ $^ $(CHRONY_OBJS:%/$*.o=) $(LDFLAGS)
+$(CHRONYD_OBJS): ;
+
+%.test: %.o $(SHARED_OBJS) $(CHRONYD_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(filter-out $(CHRONY_SRCDIR)/$<,$^) $(LDFLAGS)
 
 %.o: %.c
 	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
@@ -30,7 +33,7 @@
 	exit $$ret
 
 clean:
-	rm -f *.o $(TESTS)
+	rm -f *.o *.gcda *.gcno core.* $(TESTS)
 	rm -rf .deps
 
 distclean: clean
diff --git a/chrony_3_3/test/unit/addrfilt.c b/chrony/test/unit/addrfilt.c
similarity index 100%
rename from chrony_3_3/test/unit/addrfilt.c
rename to chrony/test/unit/addrfilt.c
diff --git a/chrony/test/unit/clientlog.c b/chrony/test/unit/clientlog.c
new file mode 100644
index 0000000..f59e130
--- /dev/null
+++ b/chrony/test/unit/clientlog.c
@@ -0,0 +1,292 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2016, 2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#if defined(FEAT_NTP) || defined(FEAT_CMDMON)
+
+#include <clientlog.c>
+
+static uint64_t
+get_random64(void)
+{
+  return ((uint64_t)random() << 40) ^ ((uint64_t)random() << 20) ^ random();
+}
+
+void
+test_unit(void)
+{
+  uint64_t ts64, prev_first_ts64, prev_last_ts64, max_step;
+  uint32_t index2, prev_first, prev_size;
+  struct timespec ts, ts2;
+  int i, j, k, index, shift;
+  CLG_Service s;
+  NTP_int64 ntp_ts;
+  IPAddr ip;
+  char conf[][100] = {
+    "clientloglimit 20000",
+    "ratelimit interval 3 burst 4 leak 3",
+    "cmdratelimit interval 3 burst 4 leak 3",
+    "ntsratelimit interval 6 burst 8 leak 3",
+  };
+
+  CNF_Initialise(0, 0);
+  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
+    CNF_ParseLine(NULL, i + 1, conf[i]);
+
+  LCL_Initialise();
+  CLG_Initialise();
+
+  TEST_CHECK(ARR_GetSize(records) == 16);
+
+  for (i = 0; i < 500; i++) {
+    DEBUG_LOG("iteration %d", i);
+
+    ts.tv_sec = (time_t)random() & 0x0fffffff;
+    ts.tv_nsec = 0;
+
+    for (j = 0; j < 1000; j++) {
+      TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
+      DEBUG_LOG("address %s", UTI_IPToString(&ip));
+
+      s = random() % MAX_SERVICES;
+      index = CLG_LogServiceAccess(s, &ip, &ts);
+      TEST_CHECK(index >= 0);
+      CLG_LimitServiceRate(s, index);
+
+      UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts);
+    }
+  }
+
+  DEBUG_LOG("records %u", ARR_GetSize(records));
+  TEST_CHECK(ARR_GetSize(records) == 128);
+
+  s = CLG_NTP;
+
+  for (i = j = 0; i < 10000; i++) {
+    ts.tv_sec += 1;
+    index = CLG_LogServiceAccess(s, &ip, &ts);
+    TEST_CHECK(index >= 0);
+    if (!CLG_LimitServiceRate(s, index))
+      j++;
+  }
+
+  DEBUG_LOG("requests %d responses %d", i, j);
+  TEST_CHECK(j * 4 < i && j * 6 > i);
+
+  TEST_CHECK(!ntp_ts_map.timestamps);
+
+  UTI_ZeroNtp64(&ntp_ts);
+  CLG_SaveNtpTimestamps(&ntp_ts, NULL);
+  TEST_CHECK(ntp_ts_map.timestamps);
+  TEST_CHECK(ntp_ts_map.first == 0);
+  TEST_CHECK(ntp_ts_map.size == 0);
+  TEST_CHECK(ntp_ts_map.max_size == 128);
+  TEST_CHECK(ARR_GetSize(ntp_ts_map.timestamps) == ntp_ts_map.max_size);
+
+  TEST_CHECK(ntp_ts_map.max_size > NTPTS_INSERT_LIMIT);
+
+  for (i = 0; i < 200; i++) {
+    DEBUG_LOG("iteration %d", i);
+
+    max_step = (1ULL << (i % 50));
+    ts64 = 0ULL - 100 * max_step;
+
+    if (i > 150)
+      ntp_ts_map.max_size = 1U << (i % 8);
+    assert(ntp_ts_map.max_size <= 128);
+    ntp_ts_map.first = i % ntp_ts_map.max_size;
+    ntp_ts_map.size = 0;
+    ntp_ts_map.cached_rx_ts = 0ULL;
+    ntp_ts_map.slew_epoch = i * 400;
+
+    for (j = 0; j < 500; j++) {
+      do {
+        ts64 += get_random64() % max_step + 1;
+      } while (ts64 == 0ULL);
+
+      int64_to_ntp64(ts64, &ntp_ts);
+
+      if (random() % 10) {
+        UTI_Ntp64ToTimespec(&ntp_ts, &ts);
+        UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
+      } else {
+        UTI_ZeroTimespec(&ts);
+      }
+
+      CLG_SaveNtpTimestamps(&ntp_ts,
+                            UTI_IsZeroTimespec(&ts) ? (random() % 2 ? &ts : NULL) : &ts);
+
+      if (j < ntp_ts_map.max_size) {
+        TEST_CHECK(ntp_ts_map.size == j + 1);
+        TEST_CHECK(ntp_ts_map.first == i % ntp_ts_map.max_size);
+      } else {
+        TEST_CHECK(ntp_ts_map.size == ntp_ts_map.max_size);
+        TEST_CHECK(ntp_ts_map.first == (i + j + ntp_ts_map.size + 1) % ntp_ts_map.max_size);
+      }
+      TEST_CHECK(ntp_ts_map.cached_index == ntp_ts_map.size - 1);
+      TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->slew_epoch == ntp_ts_map.slew_epoch);
+      TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
+      TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
+
+      for (k = random() % 4; k > 0; k--) {
+        index2 = random() % ntp_ts_map.size;
+        int64_to_ntp64(get_ntp_tss(index2)->rx_ts, &ntp_ts);
+        if (random() % 2)
+          TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+
+        UTI_Ntp64ToTimespec(&ntp_ts, &ts);
+        UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
+
+        ts2 = ts;
+        CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
+        if ((get_ntp_tss(index2)->slew_epoch + 1) % (1U << 16) != ntp_ts_map.slew_epoch) {
+          TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
+        } else {
+          TEST_CHECK(fabs(UTI_DiffTimespecsToDouble(&ts, &ts2) - ntp_ts_map.slew_offset) <
+                     1.0e-9);
+        }
+
+        CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+
+        TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
+        TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
+
+        if (random() % 2) {
+          uint16_t prev_epoch = ntp_ts_map.slew_epoch;
+          handle_slew(NULL, NULL, 0.0, TST_GetRandomDouble(-1.0e-5, 1.0e-5),
+                      LCL_ChangeAdjust, NULL);
+          TEST_CHECK((prev_epoch + 1) % (1U << 16) == ntp_ts_map.slew_epoch);
+        }
+
+        if (ntp_ts_map.size > 1) {
+          index = random() % (ntp_ts_map.size - 1);
+          if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) {
+            int64_to_ntp64(get_ntp_tss(index)->rx_ts + 1, &ntp_ts);
+            TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+            int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts);
+            TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+            CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+            CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
+          }
+        }
+
+        if (random() % 2) {
+          int64_to_ntp64(get_ntp_tss(0)->rx_ts - 1, &ntp_ts);
+          TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+          int64_to_ntp64(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts + 1, &ntp_ts);
+          TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+          CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+          CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
+        }
+      }
+    }
+
+    for (j = 0; j < 500; j++) {
+      shift = (i % 3) * 26;
+
+      if (i % 7 == 0) {
+        while (ntp_ts_map.size < ntp_ts_map.max_size) {
+          ts64 += get_random64() >> (shift + 8);
+          int64_to_ntp64(ts64, &ntp_ts);
+          CLG_SaveNtpTimestamps(&ntp_ts, NULL);
+          if (ntp_ts_map.cached_index + NTPTS_INSERT_LIMIT < ntp_ts_map.size)
+            ts64 = get_ntp_tss(ntp_ts_map.size - 1)->rx_ts;
+        }
+      }
+      do {
+        if (ntp_ts_map.size > 1 && random() % 2) {
+          k = random() % (ntp_ts_map.size - 1);
+          ts64 = get_ntp_tss(k)->rx_ts +
+                 (get_ntp_tss(k + 1)->rx_ts - get_ntp_tss(k)->rx_ts) / 2;
+        } else {
+          ts64 = get_random64() >> shift;
+        }
+      } while (ts64 == 0ULL);
+
+      int64_to_ntp64(ts64, &ntp_ts);
+
+      prev_first = ntp_ts_map.first;
+      prev_size = ntp_ts_map.size;
+      prev_first_ts64 = get_ntp_tss(0)->rx_ts;
+      prev_last_ts64 = get_ntp_tss(prev_size - 1)->rx_ts;
+      CLG_SaveNtpTimestamps(&ntp_ts, NULL);
+
+      TEST_CHECK(find_ntp_rx_ts(ts64, &index2));
+
+      if (ntp_ts_map.size > 1) {
+        TEST_CHECK(ntp_ts_map.size > 0 && ntp_ts_map.size <= ntp_ts_map.max_size);
+        if (get_ntp_tss(index2)->flags & NTPTS_DISABLED)
+          continue;
+
+        TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - ts64 <= NTPTS_FUTURE_LIMIT);
+
+        if ((int64_t)(prev_last_ts64 - ts64) <= NTPTS_FUTURE_LIMIT) {
+          TEST_CHECK(prev_size + 1 >= ntp_ts_map.size);
+          if (index2 + NTPTS_INSERT_LIMIT + 1 >= ntp_ts_map.size &&
+              !(index2 == 0 && NTPTS_INSERT_LIMIT < ntp_ts_map.max_size &&
+                ((NTPTS_INSERT_LIMIT == prev_size && (int64_t)(ts64 - prev_first_ts64) > 0) ||
+                 (NTPTS_INSERT_LIMIT + 1 == prev_size && (int64_t)(ts64 - prev_first_ts64) < 0))))
+            TEST_CHECK((prev_first + prev_size + 1) % ntp_ts_map.max_size ==
+                       (ntp_ts_map.first + ntp_ts_map.size) % ntp_ts_map.max_size);
+          else
+            TEST_CHECK(prev_first + prev_size == ntp_ts_map.first + ntp_ts_map.size);
+        }
+
+        TEST_CHECK((int64_t)(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
+                             get_ntp_tss(0)->rx_ts) > 0);
+        for (k = 0; k + 1 < ntp_ts_map.size; k++)
+          TEST_CHECK((int64_t)(get_ntp_tss(k + 1)->rx_ts - get_ntp_tss(k)->rx_ts) > 0);
+      }
+
+      if (random() % 10 == 0) {
+        CLG_DisableNtpTimestamps(&ntp_ts);
+        TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+      }
+
+      for (k = random() % 10; k > 0; k--) {
+        ts64 = get_random64() >> shift;
+        int64_to_ntp64(ts64, &ntp_ts);
+        CLG_GetNtpTxTimestamp(&ntp_ts, &ts);
+      }
+    }
+
+    if (random() % 2) {
+      handle_slew(NULL, NULL, 0.0, TST_GetRandomDouble(-1.0e9, 1.0e9),
+                  LCL_ChangeUnknownStep, NULL);
+      TEST_CHECK(ntp_ts_map.size == 0);
+      TEST_CHECK(ntp_ts_map.cached_rx_ts == 0ULL);
+      TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+      CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+    }
+  }
+
+  CLG_Finalise();
+  LCL_Finalise();
+  CNF_Finalise();
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/cmac.c b/chrony/test/unit/cmac.c
new file mode 100644
index 0000000..be4ffbb
--- /dev/null
+++ b/chrony/test/unit/cmac.c
@@ -0,0 +1,109 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include <sysincl.h>
+#include <cmac.h>
+#include <logging.h>
+#include <util.h>
+#include "test.h"
+
+#define MAX_KEY_LENGTH 64
+#define MAX_HASH_LENGTH 64
+
+struct cmac_test {
+  const char *name;
+  const unsigned char key[MAX_KEY_LENGTH];
+  int key_length;
+  const unsigned char hash[MAX_HASH_LENGTH];
+  int hash_length;
+};
+
+void
+test_unit(void)
+{
+  unsigned char data[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+  unsigned char hash[MAX_HASH_LENGTH];
+  struct cmac_test tests[] = {
+    { "AES128", "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16,
+                "\xaf\x3c\xfe\xc2\x66\x71\x08\x04\xd4\xaf\x5b\x16\x2b\x11\xf4\x85", 16 },
+    { "AES256", "\x14\x02\x8e\x7d\x17\x3c\x2f\x4e\x17\x0f\x37\x96\xc3\x2c\xc5\x99"
+                "\x18\xdd\x55\x23\xb7\xd7\x9b\xc5\x76\x36\x88\x3f\xc5\x82\xb5\x83", 32,
+                "\xfe\xf7\x94\x96\x14\x04\x11\x0b\x87\xe4\xd4\x3f\x81\xb3\xb2\x2d", 16 },
+    { "", "", 0, "", 0 }
+  };
+
+  CMC_Algorithm algorithm;
+  CMC_Instance inst;
+  int i, j, length;
+
+#ifndef HAVE_CMAC
+  TEST_REQUIRE(0);
+#endif
+
+  TEST_CHECK(CMC_INVALID == 0);
+
+  for (i = 0; tests[i].name[0] != '\0'; i++) {
+    algorithm = UTI_CmacNameToAlgorithm(tests[i].name);
+    TEST_CHECK(algorithm != 0);
+    TEST_CHECK(CMC_GetKeyLength(algorithm) == tests[i].key_length);
+
+    DEBUG_LOG("testing %s", tests[i].name);
+
+    for (j = -1; j <= 128; j++) {
+      if (j == tests[i].key_length)
+        continue;
+      TEST_CHECK(!CMC_CreateInstance(algorithm, tests[i].key, j));
+    }
+
+    inst = CMC_CreateInstance(algorithm, tests[i].key, tests[i].key_length);
+    TEST_CHECK(inst);
+
+    TEST_CHECK(!CMC_CreateInstance(0, tests[i].key, tests[i].key_length));
+
+    TEST_CHECK(CMC_Hash(inst, data, -1, hash, sizeof (hash)) == 0);
+    TEST_CHECK(CMC_Hash(inst, data, sizeof (data) - 1, hash, -1) == 0);
+
+    for (j = 0; j <= sizeof (hash); j++) {
+      memset(hash, 0, sizeof (hash));
+      length = CMC_Hash(inst, data, sizeof (data) - 1, hash, j);
+
+#if 0
+      for (int k = 0; k < length; k++)
+        printf("\\x%02x", hash[k]);
+      printf("\n");
+#endif
+
+      if (j >= tests[i].hash_length)
+        TEST_CHECK(length == tests[i].hash_length);
+      else
+        TEST_CHECK(length == j);
+
+      TEST_CHECK(!memcmp(hash, tests[i].hash, length));
+    }
+
+    for (j = 0; j < sizeof (data); j++) {
+      length = CMC_Hash(inst, data, j, hash, sizeof (hash));
+      TEST_CHECK(length == tests[i].hash_length);
+    }
+
+    CMC_DestroyInstance(inst);
+  }
+}
diff --git a/chrony_3_3/test/unit/hash.c b/chrony/test/unit/hash.c
similarity index 79%
rename from chrony_3_3/test/unit/hash.c
rename to chrony/test/unit/hash.c
index 2275473..2f9ffef 100644
--- a/chrony_3_3/test/unit/hash.c
+++ b/chrony/test/unit/hash.c
@@ -22,12 +22,13 @@
 #include <sysincl.h>
 #include <hash.h>
 #include <logging.h>
+#include <util.h>
 #include "test.h"
 
 struct hash_test {
   const char *name;
   const unsigned char out[MAX_HASH_LENGTH];
-  unsigned int length;
+  int length;
 };
 
 void
@@ -37,6 +38,7 @@
   unsigned char data2[] = "12345678910";
   unsigned char out[MAX_HASH_LENGTH];
   struct hash_test tests[] = {
+    { "MD5-NC",    "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16 },
     { "MD5",       "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16 },
     { "SHA1",      "\xd8\x85\xb3\x86\xce\xea\x93\xeb\x92\xcd\x7b\x94\xb9\x8d\xc2\x8e"
                    "\x3e\x31\x13\xdd", 20},
@@ -60,14 +62,6 @@
                    "\x39\xfc\xcb\xc1\x29\xe1\x23\x7d\x8b\x56\x54\xe3\x08\x9d\xf9\x74"
                    "\x78\x69\x2e\x3c\x7e\x51\x1e\x9d\xab\x09\xbe\xe7\x6b\x1a\xa1\x22"
                    "\x93\xb1\x2b\x82\x9d\x1e\xcf\xa8\x99\xc5\xec\x7b\x1d\x89\x07\x2b", 64 },
-    { "RMD128",    "\x6f\xd7\x1f\x37\x47\x0f\xbd\x42\x57\xc8\xbb\xee\xba\x65\xf9\x35", 16 },
-    { "RMD160",    "\x7a\x88\xec\xc7\x09\xc5\x65\x34\x11\x24\xe3\xf9\xf7\xa5\xbf\xc6"
-                   "\x01\xe2\xc9\x32", 20},
-    { "RMD256",    "\x59\xdf\xd4\xcb\xc9\xbe\x7c\x27\x08\xa7\x23\xf7\xb3\x0c\xf0\x0d"
-                   "\xa0\xcf\x5b\x18\x16\x51\x56\x6d\xda\x7b\x87\x24\x9d\x83\x35\xe1", 32 },
-    { "RMD320",    "\x68\x98\x10\xf4\xb6\x79\xb6\x15\xf1\x48\x2d\x73\xd0\x23\x84\x01"
-                   "\xbf\xaa\x67\xcf\x1e\x35\x5c\xbf\xe9\xb8\xaf\xe1\xee\x0d\xf0\x6b"
-                   "\xe2\x3a\x9a\x3a\xa7\x56\xad\x70", 40},
     { "TIGER",     "\x1c\xcd\x68\x74\xca\xd6\xd5\x17\xba\x3e\x82\xaf\xbd\x70\xdc\x66"
                    "\x99\xaa\xae\x16\x72\x59\xd1\x64", 24},
     { "WHIRLPOOL", "\xe3\xcd\xe6\xbf\xe1\x8c\xe4\x4d\xc8\xb4\xa5\x7c\x36\x8d\xc8\x8a"
@@ -77,27 +71,41 @@
     { "", "", 0 }
   };
 
-  unsigned int length;
-  int i, j, hash_id;
+  HSH_Algorithm algorithm;
+  int i, j, hash_id, length;
+
+  TEST_CHECK(HSH_INVALID == 0);
 
   for (i = 0; tests[i].name[0] != '\0'; i++) {
-    hash_id = HSH_GetHashId(tests[i].name);
+    algorithm = UTI_HashNameToAlgorithm(tests[i].name);
+    if (strcmp(tests[i].name, "MD5-NC") == 0) {
+      TEST_CHECK(algorithm == 0);
+      algorithm = HSH_MD5_NONCRYPTO;
+    } else {
+      TEST_CHECK(algorithm != 0);
+    }
+    hash_id = HSH_GetHashId(algorithm);
     if (hash_id < 0) {
-      TEST_CHECK(strcmp(tests[i].name, "MD5"));
+      TEST_CHECK(algorithm != HSH_MD5_NONCRYPTO);
+      TEST_CHECK(algorithm != HSH_MD5);
 #ifdef FEAT_SECHASH
-      TEST_CHECK(strcmp(tests[i].name, "SHA1"));
-      TEST_CHECK(strcmp(tests[i].name, "SHA256"));
-      TEST_CHECK(strcmp(tests[i].name, "SHA384"));
-      TEST_CHECK(strcmp(tests[i].name, "SHA512"));
+      TEST_CHECK(algorithm != HSH_SHA1);
+      TEST_CHECK(algorithm != HSH_SHA256);
+      TEST_CHECK(algorithm != HSH_SHA384);
+      TEST_CHECK(algorithm != HSH_SHA512);
 #endif
       continue;
     }
 
     DEBUG_LOG("testing %s", tests[i].name);
 
+    TEST_CHECK(HSH_Hash(hash_id, data1, -1, NULL, 0, out, sizeof (out)) == 0);
+    TEST_CHECK(HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, -1, out, sizeof (out)) == 0);
+    TEST_CHECK(HSH_Hash(hash_id, data1, sizeof (data1) - 1, NULL, 0, out, -1) == 0);
+
     for (j = 0; j <= sizeof (out); j++) {
-      TEST_CHECK(HSH_GetHashId(tests[i].name) == hash_id);
-      TEST_CHECK(HSH_GetHashId("nosuchhash") < 0);
+      TEST_CHECK(HSH_GetHashId(algorithm) == hash_id);
+      TEST_CHECK(HSH_GetHashId(0) < 0);
 
       memset(out, 0, sizeof (out));
       length = HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, sizeof (data2) - 1,
@@ -106,7 +114,7 @@
       if (j >= tests[i].length)
         TEST_CHECK(length == tests[i].length);
       else
-        TEST_CHECK(length == 0 || length == j || length == tests[i].length);
+        TEST_CHECK(length == j);
 
       TEST_CHECK(!memcmp(out, tests[i].out, length));
     }
diff --git a/chrony/test/unit/hwclock.c b/chrony/test/unit/hwclock.c
new file mode 100644
index 0000000..6462c5c
--- /dev/null
+++ b/chrony/test/unit/hwclock.c
@@ -0,0 +1,84 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2016-2018
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <hwclock.c>
+#include "test.h"
+
+void
+test_unit(void)
+{
+  struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
+  HCL_Instance clock;
+  double freq, jitter, interval, dj, sum;
+  int i, j, k, count;
+
+  LCL_Initialise();
+
+  for (i = 1; i <= 8; i++) {
+    clock = HCL_CreateInstance(random() % (1 << i), 1 << i, 1.0);
+
+    for (j = 0, count = 0, sum = 0.0; j < 100; j++) {
+      UTI_ZeroTimespec(&start_hw_ts);
+      UTI_ZeroTimespec(&start_local_ts);
+      UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts);
+      UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts);
+
+      DEBUG_LOG("iteration %d", j);
+
+      freq = TST_GetRandomDouble(0.9, 1.1);
+      jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
+      interval = TST_GetRandomDouble(0.1, 10.0);
+
+      clock->n_samples = 0;
+      clock->valid_coefs = 0;
+
+      for (k = 0; k < 100; k++) {
+        UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq, &hw_ts);
+        UTI_AddDoubleToTimespec(&start_local_ts, k * interval, &local_ts);
+        if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
+          dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
+          DEBUG_LOG("delta/jitter %f", dj);
+          if (clock->n_samples >= clock->max_samples / 2)
+            sum += dj, count++;
+          TEST_CHECK(clock->n_samples < 4 || dj <= 4.0);
+          TEST_CHECK(clock->n_samples < 8 || dj <= 3.0);
+        }
+
+        UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
+
+        if (HCL_NeedsNewSample(clock, &local_ts))
+          HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
+
+        TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
+
+        if (!clock->valid_coefs)
+          continue;
+
+        TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
+      }
+    }
+
+    TEST_CHECK(sum / count < 2.4 / sqrt(clock->max_samples));
+
+    HCL_DestroyInstance(clock);
+  }
+
+  LCL_Finalise();
+}
diff --git a/chrony_3_3/test/unit/keys.c b/chrony/test/unit/keys.c
similarity index 80%
rename from chrony_3_3/test/unit/keys.c
rename to chrony/test/unit/keys.c
index ac995fa..aa5e649 100644
--- a/chrony_3_3/test/unit/keys.c
+++ b/chrony/test/unit/keys.c
@@ -18,48 +18,64 @@
  **********************************************************************
  */
 
-#include <keys.c>
+#include <config.h>
 #include "test.h"
 
+#if defined(FEAT_NTP) || defined(FEAT_CMDMON)
+
+#include <keys.c>
+
 #define KEYS 100
 #define KEYFILE "keys.test-keys"
 
 static
 uint32_t write_random_key(FILE *f)
 {
-  const char *hash_name;
+  const char *type, *prefix;
   char key[128];
   uint32_t id;
   int i, length;
 
   length = random() % sizeof (key) + 1;
   length = MAX(length, 4);
+  prefix = random() % 2 ? "HEX:" : "";
+
+  switch (random() % 8) {
+#ifdef FEAT_SECHASH
+    case 0:
+      type = "SHA1";
+      break;
+    case 1:
+      type = "SHA256";
+      break;
+    case 2:
+      type = "SHA384";
+      break;
+    case 3:
+      type = "SHA512";
+      break;
+#endif
+#ifdef HAVE_CMAC
+    case 4:
+      type = "AES128";
+      length = prefix[0] == '\0' ? 8 : 16;
+      break;
+    case 5:
+      type = "AES256";
+      length = prefix[0] == '\0' ? 16 : 32;
+      break;
+#endif
+    case 6:
+      type = "MD5";
+      break;
+    default:
+      type = "";
+  }
+
   UTI_GetRandomBytes(&id, sizeof (id));
   UTI_GetRandomBytes(key, length);
 
-  switch (random() % 6) {
-#ifdef FEAT_SECHASH
-    case 0:
-      hash_name = "SHA1";
-      break;
-    case 1:
-      hash_name = "SHA256";
-      break;
-    case 2:
-      hash_name = "SHA384";
-      break;
-    case 3:
-      hash_name = "SHA512";
-      break;
-#endif
-    case 4:
-      hash_name = "MD5";
-      break;
-    default:
-      hash_name = "";
-  }
-
-  fprintf(f, "%u %s %s", id, hash_name, random() % 2 ? "HEX:" : "");
+  fprintf(f, "%u %s %s", id, type, prefix);
   for (i = 0; i < length; i++)
     fprintf(f, "%02hhX", key[i]);
   fprintf(f, "\n");
@@ -83,7 +99,7 @@
 void
 test_unit(void)
 {
-  int i, j, data_len, auth_len;
+  int i, j, data_len, auth_len, type, bits;
   uint32_t keys[KEYS], key;
   unsigned char data[100], auth[MAX_HASH_LENGTH];
   char conf[][100] = {
@@ -109,7 +125,6 @@
 
     for (j = 0; j < KEYS; j++) {
       TEST_CHECK(KEY_KeyKnown(keys[j]));
-      TEST_CHECK(KEY_GetAuthDelay(keys[j]) >= 0);
       TEST_CHECK(KEY_GetAuthLength(keys[j]) >= 16);
 
       data_len = random() % (sizeof (data) + 1);
@@ -128,12 +143,16 @@
 
       auth[auth_len - 1]++;
       TEST_CHECK(!KEY_CheckAuth(keys[j], data, data_len, auth, auth_len, auth_len));
+
+      TEST_CHECK(KEY_GetKeyInfo(keys[j], &type, &bits));
+      TEST_CHECK(type > 0 && bits > 0);
     }
 
     for (j = 0; j < 1000; j++) {
       UTI_GetRandomBytes(&key, sizeof (key));
       if (KEY_KeyKnown(key))
         continue;
+      TEST_CHECK(!KEY_GetKeyInfo(key, &type, &bits));
       TEST_CHECK(!KEY_GenerateAuth(key, data, data_len, auth, sizeof (auth)));
       TEST_CHECK(!KEY_CheckAuth(key, data, data_len, auth, auth_len, auth_len));
     }
@@ -145,3 +164,10 @@
   CNF_Finalise();
   HSH_Finalise();
 }
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/ntp_auth.c b/chrony/test/unit/ntp_auth.c
new file mode 100644
index 0000000..5f2a9bc
--- /dev/null
+++ b/chrony/test/unit/ntp_auth.c
@@ -0,0 +1,289 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ **********************************************************************
+ */
+
+#include <config.h>
+#include <sysincl.h>
+#include <conf.h>
+#include <keys.h>
+#include <local.h>
+#include <ntp_ext.h>
+#include <ntp_signd.h>
+#include <nts_ntp_server.h>
+#include <socket.h>
+#include "test.h"
+
+#ifdef FEAT_NTP
+
+#include <ntp_auth.c>
+
+static void
+prepare_packet(NTP_AuthMode auth_mode, NTP_Packet *packet, NTP_PacketInfo *info, int req)
+{
+  unsigned char buf[64];
+  int i, version;
+  NTP_Mode mode;
+
+  switch (auth_mode) {
+    case NTP_AUTH_MSSNTP:
+    case NTP_AUTH_MSSNTP_EXT:
+      version = 3;
+      mode = random() % 2 ? (req ? MODE_CLIENT : MODE_SERVER) :
+                            (req ? MODE_ACTIVE : MODE_PASSIVE);
+      break;
+    case NTP_AUTH_NTS:
+      version = 4;
+      mode = req ? MODE_CLIENT : MODE_SERVER;
+      break;
+    default:
+      version = 3 + random() % 2;
+      mode = random() % 2 ? (req ? MODE_CLIENT : MODE_SERVER) :
+                            (req ? MODE_ACTIVE : MODE_PASSIVE);
+      break;
+  }
+
+  memset(packet, 0, sizeof (*packet));
+  memset(info, 0, sizeof (*info));
+  packet->lvm = NTP_LVM(LEAP_Normal, version, mode);
+  info->length = NTP_HEADER_LENGTH;
+  info->version = version;
+  info->mode = mode;
+
+  if (version == 4) {
+    memset(buf, 0, sizeof (buf));
+    for (i = random() % 5; i > 0; i--)
+      TEST_CHECK(NEF_AddField(packet, info, 0, buf, sizeof (buf)));
+  }
+}
+
+static void
+add_dummy_auth(NTP_AuthMode auth_mode, uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info)
+{
+  unsigned char buf[64];
+  int len, fill;
+
+  info->auth.mode = auth_mode;
+
+  switch (auth_mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+    case NTP_AUTH_MSSNTP:
+    case NTP_AUTH_MSSNTP_EXT:
+      switch (auth_mode) {
+        case NTP_AUTH_SYMMETRIC:
+          len = 16 + random() % 2 * 4;
+          fill = 1 + random() % 255;
+          break;
+        case NTP_AUTH_MSSNTP:
+          len = 16;
+          fill = 0;
+          break;
+        case NTP_AUTH_MSSNTP_EXT:
+          len = 68;
+          fill = 0;
+          break;
+        default:
+          assert(0);
+      }
+
+      assert(info->length + 4 + len <= sizeof (*packet));
+
+      *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
+      info->auth.mac.key_id = key_id;
+      info->length += 4;
+
+      memset((unsigned char *)packet + info->length, fill, len);
+      info->length += len;
+      break;
+    case NTP_AUTH_NTS:
+      memset(buf, 0, sizeof (buf));
+      TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, buf, sizeof (buf)));
+      break;
+    default:
+      assert(0);
+  }
+}
+
+void
+test_unit(void)
+{
+  int i, j, can_auth_req, can_auth_res;
+  NTP_PacketInfo req_info, res_info;
+  NTP_Packet req, res;
+  NAU_Instance inst;
+  RPT_AuthReport report;
+  NTP_AuthMode mode;
+  IPSockAddr nts_addr;
+  uint32_t key_id, kod;
+  char conf[][100] = {
+    "keyfile ntp_core.keys"
+  };
+
+  CNF_Initialise(0, 0);
+  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
+    CNF_ParseLine(NULL, i + 1, conf[i]);
+
+  LCL_Initialise();
+  KEY_Initialise();
+  NSD_Initialise();
+  NNS_Initialise();
+
+  SCK_GetAnyLocalIPAddress(IPADDR_INET4, &nts_addr.ip_addr);
+  nts_addr.port = 0;
+
+  for (i = 0; i < 1000; i++) {
+    key_id = INACTIVE_AUTHKEY;
+
+    switch (i % 5) {
+      case 0:
+        inst = NAU_CreateNoneInstance();
+        TEST_CHECK(!NAU_IsAuthEnabled(inst));
+        TEST_CHECK(NAU_GetSuggestedNtpVersion(inst) == 4);
+        mode = NTP_AUTH_NONE;
+        can_auth_req = 1;
+        can_auth_res = 1;
+        break;
+      case 1:
+        key_id = random() % 7 + 2;
+        inst = NAU_CreateSymmetricInstance(key_id);
+        TEST_CHECK(NAU_IsAuthEnabled(inst));
+        TEST_CHECK(NAU_GetSuggestedNtpVersion(inst) ==
+                   (KEY_KeyKnown(inst->key_id) && KEY_GetAuthLength(inst->key_id) > 20 ? 3 : 4));
+        mode = NTP_AUTH_SYMMETRIC;
+        can_auth_req = KEY_KeyKnown(key_id);
+        can_auth_res = can_auth_req;
+        break;
+      case 2:
+        inst = NAU_CreateNtsInstance(&nts_addr, "test", 0, 0);
+        TEST_CHECK(NAU_IsAuthEnabled(inst));
+        TEST_CHECK(NAU_GetSuggestedNtpVersion(inst) == 4);
+        mode = NTP_AUTH_NTS;
+        can_auth_req = 0;
+        can_auth_res = 0;
+        break;
+      case 3:
+        key_id = 1 + random() % 100;
+        inst = NULL;
+        mode = NTP_AUTH_MSSNTP;
+        can_auth_req = 1;
+        can_auth_res = 0;
+        break;
+      case 4:
+        key_id = 1 + random() % 100;
+        inst = NULL;
+        mode = NTP_AUTH_MSSNTP_EXT;
+        can_auth_req = 0;
+        can_auth_res = 0;
+        break;
+      default:
+        assert(0);
+    }
+
+    DEBUG_LOG("iteration %d auth=%d key_id=%d", i, (int)mode, (int)key_id);
+
+    prepare_packet(mode, &req, &req_info, 1);
+
+    if (inst) {
+      TEST_CHECK(inst->mode == mode);
+      TEST_CHECK(inst->key_id == key_id);
+
+      NAU_DumpData(inst);
+      NAU_GetReport(inst, &report);
+      if (random() % 2)
+        NAU_ChangeAddress(inst, &nts_addr.ip_addr);
+
+      if (inst->mode == NTP_AUTH_NTS) {
+        for (j = random() % 5; j > 0; j--)
+#ifdef FEAT_NTS
+          TEST_CHECK(!NAU_PrepareRequestAuth(inst));
+#else
+          TEST_CHECK(NAU_PrepareRequestAuth(inst));
+#endif
+        TEST_CHECK(!NAU_GenerateRequestAuth(inst, &req, &req_info));
+      } else if (can_auth_req) {
+        TEST_CHECK(NAU_PrepareRequestAuth(inst));
+        TEST_CHECK(NAU_GenerateRequestAuth(inst, &req, &req_info));
+      } else {
+        TEST_CHECK(NAU_PrepareRequestAuth(inst));
+        TEST_CHECK(!NAU_GenerateRequestAuth(inst, &req, &req_info));
+      }
+    }
+
+    if (!inst || !can_auth_req)
+      add_dummy_auth(mode, key_id, &req, &req_info);
+
+    assert(req_info.auth.mode == mode);
+    assert(req_info.auth.mac.key_id == key_id);
+
+    kod = 1;
+    TEST_CHECK(NAU_CheckRequestAuth(&req, &req_info, &kod) == can_auth_req);
+    TEST_CHECK(kod == 0);
+
+    if (inst) {
+      for (j = NTP_AUTH_NONE; j <= NTP_AUTH_NTS; j++) {
+        if (j == mode && j == NTP_AUTH_NONE)
+          continue;
+
+        prepare_packet(j, &res, &res_info, 0);
+        add_dummy_auth(j, key_id ? key_id : 1, &res, &res_info);
+
+        TEST_CHECK(res_info.auth.mode == j);
+        TEST_CHECK(!NAU_CheckResponseAuth(inst, &res, &res_info));
+      }
+    }
+
+    prepare_packet(mode, &res, &res_info, 0);
+    TEST_CHECK(NAU_GenerateResponseAuth(&req, &req_info, &res, &res_info, NULL, NULL, kod) ==
+               can_auth_res);
+    if (!can_auth_res)
+      add_dummy_auth(mode, key_id, &res, &res_info);
+
+    assert(res_info.auth.mode == mode);
+    assert(res_info.auth.mac.key_id == key_id);
+
+    if (inst) {
+      if (mode == NTP_AUTH_SYMMETRIC) {
+        res_info.auth.mac.key_id ^= 1;
+        TEST_CHECK(!NAU_CheckResponseAuth(inst, &res, &res_info));
+        res_info.auth.mac.key_id ^= 1;
+      }
+
+      TEST_CHECK(NAU_CheckResponseAuth(inst, &res, &res_info) == can_auth_res);
+
+      NAU_GetReport(inst, &report);
+      NAU_DestroyInstance(inst);
+    }
+  }
+
+  NNS_Finalise();
+  NSD_Finalise();
+  KEY_Finalise();
+  LCL_Finalise();
+  CNF_Finalise();
+  HSH_Finalise();
+}
+
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/ntp_core.c b/chrony/test/unit/ntp_core.c
new file mode 100644
index 0000000..74145b9
--- /dev/null
+++ b/chrony/test/unit/ntp_core.c
@@ -0,0 +1,615 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2017-2018
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include <sysincl.h>
+#include <cmdparse.h>
+#include <conf.h>
+#include <keys.h>
+#include <ntp_ext.h>
+#include <ntp_io.h>
+#include <sched.h>
+#include <local.h>
+#include "test.h"
+
+#ifdef FEAT_NTP
+
+static struct timespec current_time;
+static NTP_Packet req_buffer, res_buffer;
+static int req_length, res_length;
+
+#define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0)
+#define NIO_CloseServerSocket(fd) assert(fd == 100)
+#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
+#define NIO_CloseClientSocket(fd) assert(fd == 101)
+#define NIO_IsServerSocket(fd) (fd == 100)
+#define NIO_IsServerSocketOpen() 1
+#define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1)
+#define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1))
+#define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \
+  add_timeout_in_class(delay, separation, randomness, class, handler, arg)
+#define SCH_RemoveTimeout(id) assert(!id || id == 102)
+#define LCL_ReadRawTime(ts) (*ts = current_time)
+#define LCL_ReadCookedTime(ts, err) do {double *p = err; *ts = current_time; if (p) *p = 0.0;} while (0)
+#define LCL_GetSysPrecisionAsLog() (random() % 10 - 30)
+#define SRC_UpdateReachability(inst, reach)
+#define SRC_ResetReachability(inst)
+
+static SCH_TimeoutID
+add_timeout_in_class(double min_delay, double separation, double randomness,
+                     SCH_TimeoutClass class, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
+{
+  return 102;
+}
+
+#include <ntp_core.c>
+
+static void
+advance_time(double x)
+{
+  UTI_AddDoubleToTimespec(&current_time, x, &current_time);
+}
+
+static uint32_t
+get_random_key_id(void)
+{
+  uint32_t id;
+
+  do {
+    id = random() % 8 + 2;
+  } while (!KEY_KeyKnown(id));
+
+  return id;
+}
+
+static void
+send_request(NCR_Instance inst)
+{
+  NTP_Local_Address local_addr;
+  NTP_Local_Timestamp local_ts;
+  uint32_t prev_tx_count;
+
+  prev_tx_count = inst->report.total_tx_count;
+
+  transmit_timeout(inst);
+  TEST_CHECK(!inst->valid_rx);
+  TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count);
+
+  advance_time(1e-5);
+
+  if (random() % 2) {
+    local_addr.ip_addr.family = IPADDR_UNSPEC;
+    local_addr.if_index = INVALID_IF_INDEX;
+    local_addr.sock_fd = 101;
+    local_ts.ts = current_time;
+    local_ts.err = 0.0;
+    local_ts.source = NTP_TS_KERNEL;
+
+    NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer, req_length);
+  }
+}
+
+static void
+process_request(NTP_Remote_Address *remote_addr)
+{
+  NTP_Local_Address local_addr;
+  NTP_Local_Timestamp local_ts;
+
+  local_addr.ip_addr.family = IPADDR_UNSPEC;
+  local_addr.if_index = INVALID_IF_INDEX;
+  local_addr.sock_fd = 100;
+  local_ts.ts = current_time;
+  local_ts.err = 0.0;
+  local_ts.source = NTP_TS_KERNEL;
+
+  res_length = 0;
+  NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts,
+                       &req_buffer, req_length);
+  res_length = req_length;
+  res_buffer = req_buffer;
+
+  advance_time(1e-5);
+
+  if (random() % 2) {
+    local_ts.ts = current_time;
+    NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts,
+                         &res_buffer, res_length);
+  }
+}
+
+static void
+send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth)
+{
+  NTP_Packet *req, *res;
+  uint32_t key_id = 0;
+  int i, auth_len = 0, ef_len, efs;
+
+  req = &req_buffer;
+  res = &res_buffer;
+
+  TEST_CHECK(req_length >= NTP_HEADER_LENGTH);
+
+  res->lvm = NTP_LVM(LEAP_Normal, NTP_LVM_TO_VERSION(req->lvm),
+                     NTP_LVM_TO_MODE(req->lvm) == MODE_CLIENT ? MODE_SERVER : MODE_ACTIVE);
+  res->stratum = 1;
+  res->poll = req->poll;
+  res->precision = -20;
+  res->root_delay = UTI_DoubleToNtp32(0.1);
+  res->root_dispersion = UTI_DoubleToNtp32(0.1);
+  res->reference_id = 0;
+  UTI_ZeroNtp64(&res->reference_ts);
+  res->originate_ts = interleaved ? req->receive_ts : req->transmit_ts;
+
+  advance_time(TST_GetRandomDouble(1e-4, 1e-2));
+  UTI_TimespecToNtp64(&current_time, &res->receive_ts, NULL);
+  advance_time(TST_GetRandomDouble(-1e-4, 1e-3));
+  UTI_TimespecToNtp64(&current_time, &res->transmit_ts, NULL);
+  advance_time(TST_GetRandomDouble(1e-4, 1e-2));
+
+  if (!valid_ts) {
+    switch (random() % (allow_update ? 4 : 5)) {
+      case 0:
+        res->originate_ts.hi = random();
+        break;
+      case 1:
+        res->originate_ts.lo = random();
+        break;
+      case 2:
+        UTI_ZeroNtp64(&res->originate_ts);
+        break;
+      case 3:
+        UTI_ZeroNtp64(&res->receive_ts);
+        break;
+      case 4:
+        UTI_ZeroNtp64(&res->transmit_ts);
+        break;
+      default:
+        assert(0);
+    }
+  }
+
+  res_length = NTP_HEADER_LENGTH;
+
+  if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2) {
+    unsigned char buf[128];
+
+    memset(buf, 0, sizeof (buf));
+    efs = random() % 5;
+
+    for (i = 0; i < efs; i++) {
+      ef_len = (i + 1 == efs ? NTP_MAX_V4_MAC_LENGTH + 4 : NTP_MIN_EF_LENGTH) +
+               4 * (random() % 10);
+      TEST_CHECK(NEF_SetField((unsigned char *)res, sizeof (*res), res_length, 0,
+                              buf, ef_len - 4, &ef_len));
+      res_length += ef_len;
+    }
+  }
+
+  if (authenticated) {
+    key_id = ntohl(*(uint32_t *)req->extensions);
+    if (key_id == 0)
+      key_id = get_random_key_id();
+    auth_len = KEY_GetAuthLength(key_id);
+    assert(auth_len);
+    if (NTP_LVM_TO_VERSION(res->lvm) == 4)
+      auth_len = MIN(auth_len, NTP_MAX_V4_MAC_LENGTH - 4);
+
+    if (KEY_GenerateAuth(key_id, res, res_length,
+                         (unsigned char *)res + res_length + 4, auth_len) != auth_len)
+      assert(0);
+    res_length += 4 + auth_len;
+  }
+
+  if (!valid_auth && authenticated) {
+    assert(auth_len);
+
+    switch (random() % 5) {
+      case 0:
+        key_id++;
+        break;
+      case 1:
+        key_id ^= 1;
+        if (KEY_GenerateAuth(key_id, res, res_length - auth_len - 4,
+                             (unsigned char *)res + res_length - auth_len, auth_len) != auth_len)
+          assert(0);
+        break;
+      case 2:
+        ((unsigned char *)res)[res_length - auth_len + random() % auth_len]++;
+        break;
+      case 3:
+        res_length -= 4 + auth_len;
+        auth_len = 4 * (random() % (auth_len / 4));
+        res_length += 4 + auth_len;
+        break;
+      case 4:
+        if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2 &&
+            KEY_GetAuthLength(key_id) > NTP_MAX_V4_MAC_LENGTH - 4) {
+          res_length -= 4 + auth_len;
+          auth_len += 4 + 4 * (random() %
+                               ((KEY_GetAuthLength(key_id) - NTP_MAX_V4_MAC_LENGTH - 4) / 4));
+          if (KEY_GenerateAuth(key_id, res, res_length,
+                               (unsigned char *)res + res_length + 4, auth_len) != auth_len)
+              assert(0);
+          res_length += 4 + auth_len;
+        } else {
+          memset((unsigned char *)res + res_length, 0, 4);
+          auth_len += 4;
+          res_length += 4;
+        }
+        break;
+      default:
+        assert(0);
+    }
+  }
+
+  assert(res_length <= sizeof (*res));
+  assert(res_length >= NTP_HEADER_LENGTH + auth_len);
+
+  if (authenticated)
+    *(uint32_t *)((unsigned char *)res + res_length - auth_len - 4) = htonl(key_id);
+}
+
+static void
+proc_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init)
+{
+  NTP_Local_Address local_addr;
+  NTP_Local_Timestamp local_ts;
+  NTP_Packet *res;
+  uint32_t prev_rx_count, prev_valid_count;
+  struct timespec prev_rx_ts, prev_init_rx_ts;
+  int ret;
+
+  res = &res_buffer;
+
+  local_addr.ip_addr.family = IPADDR_UNSPEC;
+  local_addr.if_index = INVALID_IF_INDEX;
+  local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) != MODE_SERVER ? 100 : 101;
+  local_ts.ts = current_time;
+  local_ts.err = 0.0;
+  local_ts.source = NTP_TS_KERNEL;
+
+  prev_rx_count = inst->report.total_rx_count;
+  prev_valid_count = inst->report.total_valid_count;
+  prev_rx_ts = inst->local_rx.ts;
+  prev_init_rx_ts = inst->init_local_rx.ts;
+
+  ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
+
+  if (good > 0)
+    TEST_CHECK(ret);
+  else if (!good)
+    TEST_CHECK(!ret);
+
+  TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count);
+
+  if (valid)
+    TEST_CHECK(prev_valid_count + 1 == inst->report.total_valid_count);
+  else
+    TEST_CHECK(prev_valid_count == inst->report.total_valid_count);
+
+  if (updated_sync)
+    TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
+  else
+    TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
+
+  if (updated_init > 0)
+    TEST_CHECK(UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
+  else if (!updated_init)
+    TEST_CHECK(!UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
+
+  if (valid) {
+    TEST_CHECK(UTI_IsZeroTimespec(&inst->init_local_rx.ts));
+    TEST_CHECK(UTI_IsZeroNtp64(&inst->init_remote_ntp_tx));
+  }
+}
+
+static void
+process_replay(NCR_Instance inst, NTP_Packet *packet_queue,
+               int queue_length, int updated_init)
+{
+  do {
+    res_buffer = packet_queue[random() % queue_length];
+  } while (!UTI_CompareNtp64(&res_buffer.transmit_ts, &inst->remote_ntp_tx));
+  proc_response(inst, 0, 0, 0, updated_init);
+  advance_time(1e-6);
+}
+
+static void
+add_dummy_auth(NTP_AuthMode auth_mode, uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info)
+{
+  unsigned char buf[64];
+  int len, fill;
+
+  info->auth.mode = auth_mode;
+
+  switch (auth_mode) {
+    case NTP_AUTH_NONE:
+      break;
+    case NTP_AUTH_SYMMETRIC:
+    case NTP_AUTH_MSSNTP:
+    case NTP_AUTH_MSSNTP_EXT:
+      switch (auth_mode) {
+        case NTP_AUTH_SYMMETRIC:
+          len = 16 + random() % 2 * 4;
+          fill = 1 + random() % 255;
+          break;
+        case NTP_AUTH_MSSNTP:
+          len = 16;
+          fill = 0;
+          break;
+        case NTP_AUTH_MSSNTP_EXT:
+          len = 68;
+          fill = 0;
+          break;
+        default:
+          assert(0);
+      }
+
+      assert(info->length + 4 + len <= sizeof (*packet));
+
+      *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
+      info->auth.mac.key_id = key_id;
+      info->length += 4;
+
+      memset((unsigned char *)packet + info->length, fill, len);
+      info->length += len;
+      break;
+    case NTP_AUTH_NTS:
+      memset(buf, 0, sizeof (buf));
+      TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, buf, sizeof (buf)));
+      break;
+    default:
+      assert(0);
+  }
+}
+
+#define PACKET_QUEUE_LENGTH 10
+
+void
+test_unit(void)
+{
+  char source_line[] = "127.0.0.1 maxdelaydevratio 1e6";
+  char conf[][100] = {
+    "allow",
+    "port 0",
+    "local",
+    "keyfile ntp_core.keys"
+  };
+  int i, j, k, interleaved, authenticated, valid, updated, has_updated;
+  CPS_NTP_Source source;
+  NTP_Remote_Address remote_addr;
+  NCR_Instance inst1, inst2;
+  NTP_Packet packet_queue[PACKET_QUEUE_LENGTH], packet;
+  NTP_PacketInfo info;
+
+  CNF_Initialise(0, 0);
+  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
+    CNF_ParseLine(NULL, i + 1, conf[i]);
+
+  LCL_Initialise();
+  TST_RegisterDummyDrivers();
+  SCH_Initialise();
+  SRC_Initialise();
+  NIO_Initialise();
+  NCR_Initialise();
+  REF_Initialise();
+  KEY_Initialise();
+
+  CNF_SetupAccessRestrictions();
+
+  CPS_ParseNTPSourceAdd(source_line, &source);
+
+  for (i = 0; i < 1000; i++) {
+    source.params.interleaved = random() % 2;
+    source.params.authkey = random() % 2 ? get_random_key_id() : INACTIVE_AUTHKEY;
+    source.params.version = random() % 4 + 1;
+
+    UTI_ZeroTimespec(&current_time);
+    advance_time(TST_GetRandomDouble(1.0, 1e9));
+
+    TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
+    remote_addr.port = 123;
+
+    inst1 = NCR_CreateInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER,
+                               &source.params, NULL);
+    NCR_StartInstance(inst1);
+    has_updated = 0;
+
+    for (j = 0; j < 50; j++) {
+      DEBUG_LOG("client/peer test iteration %d/%d", i, j);
+
+      interleaved = random() % 2 && (inst1->mode != MODE_CLIENT ||
+                                     inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
+      authenticated = random() % 2;
+      valid = (!interleaved || (source.params.interleaved && has_updated)) &&
+              ((source.params.authkey == INACTIVE_AUTHKEY) == !authenticated);
+      updated = (valid || inst1->mode == MODE_ACTIVE) &&
+                ((source.params.authkey == INACTIVE_AUTHKEY) == !authenticated);
+      has_updated = has_updated || updated;
+      if (inst1->mode == MODE_CLIENT)
+        updated = 0;
+
+      DEBUG_LOG("authkey=%d version=%d interleaved=%d authenticated=%d valid=%d updated=%d has_updated=%d",
+                (int)source.params.authkey, source.params.version,
+                interleaved, authenticated, valid, updated, has_updated);
+
+      send_request(inst1);
+
+      send_response(interleaved, authenticated, 1, 0, 1);
+      DEBUG_LOG("response 1");
+      proc_response(inst1, 0, 0, 0, updated);
+
+      if (source.params.authkey) {
+        send_response(interleaved, authenticated, 1, 1, 0);
+        DEBUG_LOG("response 2");
+        proc_response(inst1, 0, 0, 0, 0);
+      }
+
+      send_response(interleaved, authenticated, 1, 1, 1);
+      DEBUG_LOG("response 3");
+      proc_response(inst1, -1, valid, valid, updated);
+      DEBUG_LOG("response 4");
+      proc_response(inst1, 0, 0, 0, 0);
+
+      advance_time(-1.0);
+
+      send_response(interleaved, authenticated, 1, 1, 1);
+      DEBUG_LOG("response 5");
+      proc_response(inst1, 0, 0, 0, updated && valid);
+
+      advance_time(1.0);
+
+      send_response(interleaved, authenticated, 1, 1, 1);
+      DEBUG_LOG("response 6");
+      proc_response(inst1, 0, 0, valid && updated, updated);
+    }
+
+    NCR_DestroyInstance(inst1);
+
+    inst1 = NCR_CreateInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER,
+                               &source.params, NULL);
+    NCR_StartInstance(inst1);
+
+    for (j = 0; j < 20; j++) {
+      DEBUG_LOG("server test iteration %d/%d", i, j);
+
+      send_request(inst1);
+      process_request(&remote_addr);
+      proc_response(inst1, 1, 1, 1, 0);
+      advance_time(1 << inst1->local_poll);
+    }
+
+    NCR_DestroyInstance(inst1);
+
+    inst1 = NCR_CreateInstance(&remote_addr, NTP_PEER, &source.params, NULL);
+    NCR_StartInstance(inst1);
+    inst2 = NCR_CreateInstance(&remote_addr, NTP_PEER, &source.params, NULL);
+    NCR_StartInstance(inst2);
+
+    res_length = req_length = 0;
+
+    for (j = 0; j < 20; j++) {
+      DEBUG_LOG("peer replay test iteration %d/%d", i, j);
+
+      send_request(inst1);
+      res_buffer = req_buffer;
+      assert(!res_length || res_length == req_length);
+      res_length = req_length;
+
+      TEST_CHECK(inst1->valid_timestamps == (j > 0));
+
+      DEBUG_LOG("response 1->2");
+      proc_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1);
+
+      packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer;
+
+      for (k = 0; k < j % 4 + 1; k++) {
+        DEBUG_LOG("replay ?->1 %d", k);
+        process_replay(inst1, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), k ? -1 : 1);
+        DEBUG_LOG("replay ?->2 %d", k);
+        process_replay(inst2, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), -1);
+      }
+
+      advance_time(1 << (source.params.minpoll - 1));
+
+      send_request(inst2);
+      res_buffer = req_buffer;
+      assert(res_length == req_length);
+
+      TEST_CHECK(inst2->valid_timestamps == (j > 0));
+
+      DEBUG_LOG("response 2->1");
+      proc_response(inst1, 1, 1, 1, 1);
+
+      packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer;
+
+      for (k = 0; k < j % 4 + 1; k++) {
+        DEBUG_LOG("replay ?->1 %d", k);
+        process_replay(inst1, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), k ? -1 : 1);
+        DEBUG_LOG("replay ?->2 %d", k);
+        process_replay(inst2, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), -1);
+      }
+
+      advance_time(1 << (source.params.minpoll - 1));
+    }
+
+    NCR_DestroyInstance(inst1);
+    NCR_DestroyInstance(inst2);
+  }
+
+  memset(&packet, 0, sizeof (packet));
+  packet.lvm = NTP_LVM(LEAP_Normal, NTP_VERSION, MODE_CLIENT);
+
+  TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
+  TEST_CHECK(info.auth.mode == NTP_AUTH_NONE);
+
+  TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
+  add_dummy_auth(NTP_AUTH_SYMMETRIC, 100, &packet, &info);
+  memset(&info.auth, 0, sizeof (info.auth));
+  TEST_CHECK(parse_packet(&packet, info.length, &info));
+  TEST_CHECK(info.auth.mode == NTP_AUTH_SYMMETRIC);
+  TEST_CHECK(info.auth.mac.start == NTP_HEADER_LENGTH);
+  TEST_CHECK(info.auth.mac.length == info.length - NTP_HEADER_LENGTH);
+  TEST_CHECK(info.auth.mac.key_id == 100);
+
+  TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
+  add_dummy_auth(NTP_AUTH_NTS, 0, &packet, &info);
+  memset(&info.auth, 0, sizeof (info.auth));
+  TEST_CHECK(parse_packet(&packet, info.length, &info));
+  TEST_CHECK(info.auth.mode == NTP_AUTH_NTS);
+
+  packet.lvm = NTP_LVM(LEAP_Normal, 3, MODE_CLIENT);
+
+  TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
+  add_dummy_auth(NTP_AUTH_MSSNTP, 200, &packet, &info);
+  memset(&info.auth, 0, sizeof (info.auth));
+  TEST_CHECK(parse_packet(&packet, info.length, &info));
+  TEST_CHECK(info.auth.mode == NTP_AUTH_MSSNTP);
+  TEST_CHECK(info.auth.mac.start == NTP_HEADER_LENGTH);
+  TEST_CHECK(info.auth.mac.length == 20);
+  TEST_CHECK(info.auth.mac.key_id == 200);
+
+  TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
+  add_dummy_auth(NTP_AUTH_MSSNTP_EXT, 300, &packet, &info);
+  memset(&info.auth, 0, sizeof (info.auth));
+  TEST_CHECK(parse_packet(&packet, info.length, &info));
+  TEST_CHECK(info.auth.mode == NTP_AUTH_MSSNTP_EXT);
+  TEST_CHECK(info.auth.mac.start == NTP_HEADER_LENGTH);
+  TEST_CHECK(info.auth.mac.length == 72);
+  TEST_CHECK(info.auth.mac.key_id == 300);
+
+  KEY_Finalise();
+  REF_Finalise();
+  NCR_Finalise();
+  NIO_Finalise();
+  SRC_Finalise();
+  SCH_Finalise();
+  LCL_Finalise();
+  CNF_Finalise();
+  HSH_Finalise();
+}
+
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/ntp_core.keys b/chrony/test/unit/ntp_core.keys
new file mode 100644
index 0000000..a3e4f6c
--- /dev/null
+++ b/chrony/test/unit/ntp_core.keys
@@ -0,0 +1,8 @@
+2 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
+3 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
+4 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A
+5 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A
+6 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F
+7 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F
+8 AES128 HEX:5D5E8A31D4B459A66D445259E147CFB5
+9 AES128 HEX:5D5E8A31D4B459A66D445259E147CFB5
diff --git a/chrony/test/unit/ntp_ext.c b/chrony/test/unit/ntp_ext.c
new file mode 100644
index 0000000..c37e702
--- /dev/null
+++ b/chrony/test/unit/ntp_ext.c
@@ -0,0 +1,167 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTP
+
+#include <util.h>
+#include <logging.h>
+
+#include <ntp_ext.c>
+
+void
+test_unit(void)
+{
+  unsigned char *buffer, body[NTP_MAX_EXTENSIONS_LENGTH];
+  void *bodyp;
+  NTP_PacketInfo info;
+  NTP_Packet packet;
+  int i, j, start, length, type, type2, body_length, body_length2;
+
+  assert(sizeof (uint16_t) == 2);
+  assert(sizeof (body) == sizeof (packet.extensions));
+
+  buffer = (unsigned char *)packet.extensions;
+
+  for (i = 0; i < 10000; i++) {
+    body_length = random() % (sizeof (body) - 4 + 1) / 4 * 4;
+    start = random() % (sizeof (packet.extensions) - body_length - 4 + 1) / 4 * 4;
+    type = random() % 0x10000;
+
+    DEBUG_LOG("body_length=%d start=%d type=%d", body_length, start, type);
+    assert(body_length + start <= sizeof (packet.extensions));
+
+    UTI_GetRandomBytes(body, body_length);
+
+    TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start,
+                             type, body, body_length + 4, &length));
+    TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start + 4,
+                             type, body, body_length, &length));
+    TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start,
+                             type, body, body_length - 1, &length));
+    TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start,
+                             type, body, body_length - 2, &length));
+    TEST_CHECK(!NEF_SetField(buffer, body_length + start + 4, start,
+                             type, body, body_length - 3, &length));
+    TEST_CHECK(!NEF_SetField(buffer, body_length + start + 3, start,
+                             type, body, body_length, &length));
+    TEST_CHECK(!NEF_SetField(buffer, body_length + start + 5, start + 1,
+                             type, body, body_length, &length));
+
+    TEST_CHECK(NEF_SetField(buffer, body_length + start + 4, start,
+                            type, body, body_length, &length));
+    TEST_CHECK(length == body_length + 4);
+    TEST_CHECK(((uint16_t *)buffer)[start / 2] == htons(type));
+    TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] == htons(length));
+    TEST_CHECK(memcmp(buffer + start + 4, body, body_length) == 0);
+
+    memset(&packet, 0, sizeof (packet));
+    packet.lvm = NTP_LVM(0, 4, MODE_CLIENT);
+    memset(&info, 0, sizeof (info));
+
+    info.version = 3;
+    info.length = NTP_HEADER_LENGTH;
+    TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp));
+
+    info.version = 4;
+    info.length = NTP_HEADER_LENGTH - 4;
+    TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp));
+
+    info.length = sizeof (packet) - body_length;
+    TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp));
+
+    info.length = NTP_HEADER_LENGTH + start;
+
+    if (body_length < 12) {
+      TEST_CHECK(!NEF_AddBlankField(&packet, &info, type, body_length, &bodyp));
+      continue;
+    }
+
+    TEST_CHECK(NEF_AddBlankField(&packet, &info, type, body_length, &bodyp));
+    TEST_CHECK(info.length == NTP_HEADER_LENGTH + start + body_length + 4);
+    TEST_CHECK(((uint16_t *)buffer)[start / 2] == htons(type));
+    TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] == htons(length));
+    TEST_CHECK(bodyp == buffer + start + 4);
+    TEST_CHECK(info.ext_fields == 1);
+
+    memset(buffer, 0, sizeof (packet.extensions));
+    info.length = NTP_HEADER_LENGTH + start;
+    info.ext_fields = 0;
+
+    TEST_CHECK(NEF_AddField(&packet, &info, type, body, body_length));
+    TEST_CHECK(info.length == NTP_HEADER_LENGTH + start + body_length + 4);
+    TEST_CHECK(((uint16_t *)buffer)[start / 2] == htons(type));
+    TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] == htons(length));
+    TEST_CHECK(memcmp(buffer + start + 4, body, body_length) == 0);
+    TEST_CHECK(info.ext_fields == 1);
+
+    for (j = 1; j <= 4; j++) {
+      TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] = htons(length + j));
+      TEST_CHECK(!NEF_ParseSingleField(buffer, start + body_length + 4, start,
+                                       &length, &type2, &bodyp, &body_length2));
+    }
+
+    TEST_CHECK(((uint16_t *)buffer)[start / 2 + 1] = htons(length));
+
+    TEST_CHECK(NEF_ParseSingleField(buffer, sizeof (packet.extensions), start,
+                                    &length, &type2, &bodyp, &body_length2));
+    TEST_CHECK(length == body_length + 4);
+    TEST_CHECK(type2 == type);
+    TEST_CHECK(bodyp == buffer + start + 4);
+    TEST_CHECK(body_length2 == body_length);
+
+    TEST_CHECK(!NEF_ParseField(&packet, sizeof (packet) + 4,
+                               NTP_HEADER_LENGTH + start,
+                               &length, &type2, &bodyp, &body_length2));
+
+    if (body_length < 24) {
+      TEST_CHECK(!NEF_ParseField(&packet, NTP_HEADER_LENGTH + start + length, 
+                                 NTP_HEADER_LENGTH + start,
+                                 &length, &type2, &bodyp, &body_length2));
+      if (sizeof (packet.extensions) - start <= 24) {
+        TEST_CHECK(!NEF_ParseField(&packet, sizeof (packet), NTP_HEADER_LENGTH + start,
+                                   &length, &type2, &bodyp, &body_length2));
+        continue;
+      } else {
+        TEST_CHECK(NEF_ParseField(&packet, sizeof (packet), NTP_HEADER_LENGTH + start,
+                                  &length, &type2, &bodyp, &body_length2));
+      }
+    } else {
+      TEST_CHECK(NEF_ParseField(&packet, NTP_HEADER_LENGTH + start + length, 
+                                NTP_HEADER_LENGTH + start,
+                                &length, &type2, &bodyp, &body_length2));
+    }
+    TEST_CHECK(length == body_length + 4);
+    TEST_CHECK(type2 == type);
+    TEST_CHECK(bodyp == buffer + start + 4);
+    TEST_CHECK(body_length2 == body_length);
+
+  }
+}
+
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/ntp_sources.c b/chrony/test/unit/ntp_sources.c
new file mode 100644
index 0000000..ea3910f
--- /dev/null
+++ b/chrony/test/unit/ntp_sources.c
@@ -0,0 +1,364 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2016, 2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTP
+
+#include <conf.h>
+#include <cmdparse.h>
+#include <nameserv_async.h>
+#include <ntp_core.h>
+#include <ntp_io.h>
+
+static char *requested_name = NULL;
+static DNS_NameResolveHandler resolve_handler = NULL;
+static void *resolve_handler_arg = NULL;
+
+#define DNS_Name2IPAddressAsync(name, handler, arg) \
+  requested_name = (name), \
+  resolve_handler = (handler), \
+  resolve_handler_arg = (arg)
+#define NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only) \
+  change_remote_address(inst, remote_addr, ntp_only)
+#define NCR_ProcessRxKnown(remote_addr, local_addr, ts, msg, len) (random() % 2)
+#define NIO_IsServerConnectable(addr) (random() % 2)
+
+static void change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr,
+                                  int ntp_only);
+
+#include <ntp_sources.c>
+
+#undef NCR_ChangeRemoteAddress
+
+static void
+resolve_random_address(DNS_Status status, int rand_bits)
+{
+  IPAddr ip_addrs[DNS_MAX_ADDRESSES];
+  int i, n_addrs;
+
+  TEST_CHECK(requested_name);
+  requested_name = NULL;
+
+  if (status == DNS_Success) {
+    n_addrs = random() % DNS_MAX_ADDRESSES + 1;
+    for (i = 0; i < n_addrs; i++)
+      TST_GetRandomAddress(&ip_addrs[i], IPADDR_UNSPEC, rand_bits);
+  } else {
+    n_addrs = 0;
+  }
+
+  (resolve_handler)(status, n_addrs, ip_addrs, resolve_handler_arg);
+}
+
+static int
+update_random_address(NTP_Remote_Address *addr, int rand_bits)
+{
+  NTP_Remote_Address new_addr;
+  NSR_Status status;
+
+  TST_GetRandomAddress(&new_addr.ip_addr, IPADDR_UNSPEC, rand_bits);
+  new_addr.port = random() % 1024;
+
+  status = NSR_UpdateSourceNtpAddress(addr, &new_addr);
+  if (status == NSR_InvalidAF) {
+    TEST_CHECK(!UTI_IsIPReal(&addr->ip_addr));
+  } else {
+    TEST_CHECK(status == NSR_Success || status == NSR_AlreadyInUse);
+  }
+
+  return status == NSR_Success;
+}
+
+static void
+change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
+{
+  int update = !ntp_only && random() % 4 == 0, update_pos = random() % 2, r = 0;
+
+  TEST_CHECK(record_lock);
+
+  if (update && update_pos == 0)
+    r = update_random_address(remote_addr, 4);
+
+  NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only);
+
+  if (update && update_pos == 1)
+    r = update_random_address(remote_addr, 4);
+
+  if (r)
+    TEST_CHECK(UTI_IsIPReal(&saved_address_update.old_address.ip_addr));
+}
+
+void
+test_unit(void)
+{
+  char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64];
+  int i, j, k, slot, found, pool, prev_n;
+  uint32_t hash = 0, conf_id;
+  NTP_Remote_Address addrs[256], addr;
+  NTP_Local_Address local_addr;
+  NTP_Local_Timestamp local_ts;
+  struct UnresolvedSource *us;
+  RPT_ActivityReport report;
+  CPS_NTP_Source source;
+  NSR_Status status;
+  NTP_Packet msg;
+
+  CNF_Initialise(0, 0);
+  CNF_ParseLine(NULL, 1, conf);
+
+  PRV_Initialise();
+  LCL_Initialise();
+  TST_RegisterDummyDrivers();
+  SCH_Initialise();
+  SRC_Initialise();
+  NIO_Initialise();
+  NCR_Initialise();
+  REF_Initialise();
+  NSR_Initialise();
+
+  CPS_ParseNTPSourceAdd(source_line, &source);
+
+  TEST_CHECK(n_sources == 0);
+
+  for (i = 0; i < 6; i++) {
+    TEST_CHECK(ARR_GetSize(records) == 1);
+
+    DEBUG_LOG("collision mod %u", 1U << i);
+
+    for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
+      while (1) {
+        do {
+          TST_GetRandomAddress(&addrs[j].ip_addr, IPADDR_UNSPEC, -1);
+        } while (UTI_IPToHash(&addrs[j].ip_addr) % (1U << i) != hash % (1U << i));
+
+        for (k = 0; k < j; k++)
+          if (UTI_CompareIPs(&addrs[k].ip_addr, &addrs[j].ip_addr, NULL) == 0)
+            break;
+        if (k == j)
+          break;
+      }
+
+      addrs[j].port = random() % 1024;
+
+      if (!j)
+        hash = UTI_IPToHash(&addrs[j].ip_addr);
+
+      DEBUG_LOG("adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr),
+                UTI_IPToHash(&addrs[j].ip_addr) % (1U << i));
+
+      status = NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER,
+                             &source.params, NULL);
+      TEST_CHECK(status == NSR_Success);
+      TEST_CHECK(n_sources == j + 1);
+
+      TEST_CHECK(strcmp(NSR_GetName(&addrs[j].ip_addr), UTI_IPToString(&addrs[j].ip_addr)) == 0);
+
+      for (k = 0; k <= j; k++) {
+        addr = addrs[k];
+        found = find_slot2(&addr, &slot);
+        TEST_CHECK(found == 2);
+        TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
+                                   &addr.ip_addr, NULL));
+        addr.port++;
+        found = find_slot2(&addr, &slot);
+        TEST_CHECK(found == 1);
+        TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
+                                   &addr.ip_addr, NULL));
+      }
+
+      status = NSR_AddSource(&addrs[j], NTP_SERVER, &source.params, &conf_id);
+      TEST_CHECK(status == NSR_AlreadyInUse);
+    }
+
+    for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
+      DEBUG_LOG("removing source %s", UTI_IPToString(&addrs[j].ip_addr));
+      NSR_RemoveSource(&addrs[j].ip_addr);
+
+      for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) {
+        found = find_slot2(&addrs[k], &slot);
+        TEST_CHECK(found == (k <= j ? 0 : 2));
+      }
+    }
+  }
+
+  TEST_CHECK(n_sources == 0);
+
+  status = NSR_AddSourceByName("a b", 0, 0, 0, &source.params, &conf_id);
+  TEST_CHECK(status == NSR_InvalidName);
+
+  local_addr.ip_addr.family = IPADDR_INET4;
+  local_addr.ip_addr.addr.in4 = 0;
+  local_addr.if_index = -1;
+  local_addr.sock_fd = 0;
+  memset(&local_ts, 0, sizeof (local_ts));
+
+  for (i = 0; i < 500; i++) {
+    for (j = 0; j < 20; j++) {
+      snprintf(name, sizeof (name), "ntp%d.example.net", (int)(random() % 10));
+      pool = random() % 2;
+      prev_n = n_sources;
+
+      DEBUG_LOG("%d/%d adding source %s pool=%d", i, j, name, pool);
+      status = NSR_AddSourceByName(name, 0, pool, random() % 2 ? NTP_SERVER : NTP_PEER,
+                                   &source.params, &conf_id);
+      TEST_CHECK(status == NSR_UnresolvedName);
+
+      TEST_CHECK(n_sources == prev_n + (pool ? source.params.max_sources * 2 : 1));
+      TEST_CHECK(unresolved_sources);
+
+      for (us = unresolved_sources; us->next; us = us->next)
+        ;
+      TEST_CHECK(strcmp(us->name, name) == 0);
+      if (pool) {
+        TEST_CHECK(us->address.ip_addr.family == IPADDR_UNSPEC && us->pool_id >= 0);
+      } else {
+        TEST_CHECK(strcmp(NSR_GetName(&us->address.ip_addr), name) == 0);
+        TEST_CHECK(find_slot2(&us->address, &slot) == 2);
+      }
+
+      if (random() % 2) {
+        if (!resolving_id || random() % 2) {
+          NSR_ResolveSources();
+        } else {
+          SCH_RemoveTimeout(resolving_id);
+          resolve_sources_timeout(NULL);
+          TEST_CHECK(resolving_id == 0);
+          TEST_CHECK(requested_name);
+        }
+
+        TEST_CHECK(!!unresolved_sources == (resolving_id != 0) || requested_name);
+      }
+
+      while (requested_name && random() % 2) {
+        TEST_CHECK(resolving_source);
+        TEST_CHECK(strcmp(requested_name, resolving_source->name) == 0);
+        TEST_CHECK(!record_lock);
+
+        switch (random() % 3) {
+          case 0:
+            resolve_random_address(DNS_Success, 4);
+            break;
+          case 1:
+            resolve_random_address(DNS_TryAgain, 0);
+            break;
+          case 2:
+            resolve_random_address(DNS_Failure, 0);
+            break;
+        }
+      }
+
+      while (random() % 8 > 0) {
+        slot = random() % ARR_GetSize(records);
+        if (!get_record(slot)->remote_addr)
+          continue;
+
+        switch (random() % 5) {
+          case 0:
+            msg.lvm = NTP_LVM(0, NTP_VERSION, random() % 2 ? MODE_CLIENT : MODE_SERVER);
+            NSR_ProcessTx(get_record(slot)->remote_addr, &local_addr,
+                          &local_ts, &msg, 0);
+            break;
+          case 1:
+            msg.lvm = NTP_LVM(0, NTP_VERSION, random() % 2 ? MODE_CLIENT : MODE_SERVER);
+            NSR_ProcessRx(get_record(slot)->remote_addr, &local_addr,
+                          &local_ts, &msg, 0);
+            break;
+          case 2:
+            NSR_HandleBadSource(&get_record(slot)->remote_addr->ip_addr);
+            break;
+          case 3:
+            NSR_SetConnectivity(NULL, &get_record(slot)->remote_addr->ip_addr, SRC_OFFLINE);
+            break;
+          case 4:
+            update_random_address(get_record(slot)->remote_addr, 4);
+            TEST_CHECK(!UTI_IsIPReal(&saved_address_update.old_address.ip_addr));
+            break;
+        }
+
+        TEST_CHECK(!record_lock);
+      }
+
+      NSR_GetActivityReport(&report);
+      TEST_CHECK(report.online == 0);
+      TEST_CHECK(report.offline >= 0);
+      TEST_CHECK(report.burst_online == 0);
+      TEST_CHECK(report.burst_offline == 0);
+      TEST_CHECK(report.unresolved >= 0);
+
+      if (random() % 4 == 0) {
+        NSR_RemoveSourcesById(conf_id);
+        TEST_CHECK(n_sources <= prev_n);
+      } else if (random() % 8 == 0) {
+        NSR_RefreshAddresses();
+        TEST_CHECK(unresolved_sources);
+      }
+    }
+
+    NSR_RemoveAllSources();
+    TEST_CHECK(n_sources == 0);
+
+    for (j = 0; j < ARR_GetSize(pools); j++) {
+      TEST_CHECK(get_pool(j)->sources == 0);
+      TEST_CHECK(get_pool(j)->unresolved_sources == 0);
+      TEST_CHECK(get_pool(j)->confirmed_sources == 0);
+      TEST_CHECK(get_pool(j)->max_sources == 0);
+    }
+
+    while (requested_name) {
+      TEST_CHECK(resolving_source);
+      resolve_random_address(random() % 2 ? DNS_Success : DNS_TryAgain, 4);
+    }
+
+    if (unresolved_sources && resolving_id == 0)
+      NSR_ResolveSources();
+
+    TEST_CHECK(!!unresolved_sources == (resolving_id != 0));
+
+    if (resolving_id) {
+      SCH_RemoveTimeout(resolving_id);
+      resolve_sources_timeout(NULL);
+    }
+
+    TEST_CHECK(resolving_id == 0);
+    TEST_CHECK(!requested_name);
+    TEST_CHECK(!unresolved_sources);
+  }
+
+  NSR_Finalise();
+  REF_Finalise();
+  NCR_Finalise();
+  NIO_Finalise();
+  SRC_Finalise();
+  SCH_Finalise();
+  LCL_Finalise();
+  PRV_Finalise();
+  CNF_Finalise();
+  HSH_Finalise();
+}
+
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/nts_ke.crt b/chrony/test/unit/nts_ke.crt
new file mode 100644
index 0000000..8165287
--- /dev/null
+++ b/chrony/test/unit/nts_ke.crt
@@ -0,0 +1,8 @@
+-----BEGIN CERTIFICATE-----
+MIIBDjCBwaADAgECAgEBMAUGAytlcDAPMQ0wCwYDVQQDEwR0ZXN0MCAXDTcwMDEw
+MTAwMDAwMFoYDzIxMDAwMTAyMDAwMDAwWjAPMQ0wCwYDVQQDEwR0ZXN0MCowBQYD
+K2VwAyEA3oh/FZeOxRYvJVLfCDEwGI6Oe23gTgLHx8a87tvwgfyjQDA+MAwGA1Ud
+EwEB/wQCMAAwDwYDVR0PAQH/BAUDAweAADAdBgNVHQ4EFgQUYwAqF9q3jxUk68m1
+cuz8DueOHeMwBQYDK2VwA0EAne0dCRXb0dW8bn2v3RhVTqeTJWXfl74x8MTQMTM7
+/uGTqoYOA0YJffypd+p27pvx2BEoNQWRYM6pqBg55KbwDw==
+-----END CERTIFICATE-----
diff --git a/chrony/test/unit/nts_ke.key b/chrony/test/unit/nts_ke.key
new file mode 100644
index 0000000..6414c64
--- /dev/null
+++ b/chrony/test/unit/nts_ke.key
@@ -0,0 +1,25 @@
+Public Key Info:
+	Public Key Algorithm: EdDSA (Ed25519)
+	Key Security Level: High (256 bits)
+
+curve:	Ed25519
+private key:
+	01:d9:75:42:7c:52:cc:29:9e:90:01:f3:da:26:f6:d7
+	ad:af:a5:2a:82:36:1d:86:c6:57:a7:b4:99:9b:6c:6d
+	
+
+x:
+	de:88:7f:15:97:8e:c5:16:2f:25:52:df:08:31:30:18
+	8e:8e:7b:6d:e0:4e:02:c7:c7:c6:bc:ee:db:f0:81:fc
+	
+
+
+Public Key PIN:
+	pin-sha256:C4LBJP2cRxvLcZ6pjowcOEQhcW3ZPMVTpLgRGsBDeMw=
+Public Key ID:
+	sha256:0b82c124fd9c471bcb719ea98e8c1c384421716dd93cc553a4b8111ac04378cc
+	sha1:63002a17dab78f1524ebc9b572ecfc0ee78e1de3
+
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIAHZdUJ8UswpnpAB89om9tetr6UqgjYdhsZXp7SZm2xt
+-----END PRIVATE KEY-----
diff --git a/chrony/test/unit/nts_ke_client.c b/chrony/test/unit/nts_ke_client.c
new file mode 100644
index 0000000..72690bf
--- /dev/null
+++ b/chrony/test/unit/nts_ke_client.c
@@ -0,0 +1,144 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTS
+
+#include <nts_ke_client.c>
+#include <local.h>
+
+static void
+prepare_response(NKSN_Instance session, int valid)
+{
+  uint16_t data[16];
+  int i, index, length;
+
+  if (valid)
+    index = -1;
+  else
+    index = random() % 10;
+  DEBUG_LOG("index=%d", index);
+
+  NKSN_BeginMessage(session);
+
+  memset(data, 0, sizeof (data));
+  length = 2;
+  assert(sizeof (data[0]) == 2);
+
+  if (index == 0) {
+    data[0] = htons(random() % 100);
+    TEST_CHECK(NKSN_AddRecord(session, 1, random() % 2 ? NKE_RECORD_ERROR : NKE_RECORD_WARNING,
+                              data, length));
+  } else if (index == 1) {
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_ERROR + 1000, data, length));
+  }
+
+  if (index != 2) {
+    if (index == 3)
+      data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4 + random() % 10 + 1);
+    else
+      data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
+    if (index == 4)
+      length = 3 + random() % 10;
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
+  }
+
+  if (index != 5) {
+    if (index == 6)
+      data[0] = htons(AEAD_AES_SIV_CMAC_256 + random() % 10 + 1);
+    else
+      data[0] = htons(AEAD_AES_SIV_CMAC_256);
+    if (index == 7)
+      length = 3 + random() % 10;
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));
+  }
+
+  if (random() % 2) {
+    const char server[] = "127.0.0.1";
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_SERVER_NEGOTIATION,
+                              server, sizeof (server) - 1));
+  }
+
+  if (random() % 2) {
+    data[0] = htons(123);
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length));
+  }
+
+  if (random() % 2) {
+    length = random() % (sizeof (data) + 1);
+    TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
+  }
+
+  if (index != 8) {
+    for (i = 0; i < NKE_MAX_COOKIES; i++) {
+      length = (random() % sizeof (data) + 1) / 4 * 4;
+      if (index == 9)
+        length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1);
+      TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length));
+    }
+  }
+
+  TEST_CHECK(NKSN_EndMessage(session));
+}
+
+void
+test_unit(void)
+{
+  NKC_Instance inst;
+  IPSockAddr addr;
+  int i, r, valid;
+
+  char conf[][100] = {
+    "nosystemcert",
+  };
+
+  CNF_Initialise(0, 0);
+  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
+    CNF_ParseLine(NULL, i + 1, conf[i]);
+
+  LCL_Initialise();
+
+  SCK_GetLoopbackIPAddress(AF_INET, &addr.ip_addr);
+  addr.port = 0;
+
+  inst = NKC_CreateInstance(&addr, "test", 0);
+  TEST_CHECK(inst);
+
+  for (i = 0; i < 10000; i++) {
+    valid = random() % 2;
+    prepare_response(inst->session, valid);
+    r = process_response(inst);
+    TEST_CHECK(r == valid);
+  }
+
+  NKC_DestroyInstance(inst);
+
+  LCL_Finalise();
+  CNF_Finalise();
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/nts_ke_server.c b/chrony/test/unit/nts_ke_server.c
new file mode 100644
index 0000000..f4f03a1
--- /dev/null
+++ b/chrony/test/unit/nts_ke_server.c
@@ -0,0 +1,230 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTS
+
+#include <local.h>
+#include <nts_ke_session.h>
+#include <util.h>
+
+#define NKSN_GetKeys get_keys
+
+static int
+get_keys(NKSN_Instance session, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
+{
+  c2s->length = SIV_GetKeyLength(siv);
+  UTI_GetRandomBytes(c2s->key, c2s->length);
+  s2c->length = SIV_GetKeyLength(siv);
+  UTI_GetRandomBytes(s2c->key, s2c->length);
+  return 1;
+}
+
+#include <nts_ke_server.c>
+
+static void
+prepare_request(NKSN_Instance session, int valid)
+{
+  uint16_t data[16];
+  int index, length;
+
+  if (valid)
+    index = -1;
+  else
+    index = random() % 9;
+  DEBUG_LOG("index=%d", index);
+
+  NKSN_BeginMessage(session);
+
+  memset(data, 0, sizeof (data));
+  length = 2;
+  assert(sizeof (data[0]) == 2);
+
+  if (index != 0) {
+    memset(data, NKE_NEXT_PROTOCOL_NTPV4 + 1, sizeof (data));
+    data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
+    if (index == 1)
+      length = 0;
+    else if (index == 2)
+      length = 3 + random() % 15 * 2;
+    else
+      length = 2 + random() % 16 * 2;
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
+  }
+
+  if (index == 3)
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
+
+  if (index != 4) {
+    data[0] = htons(AEAD_AES_SIV_CMAC_256);
+    if (index == 5)
+      length = 0;
+    else if (index == 6)
+      length = 3 + random() % 15 * 2;
+    else
+      length = 2 + random() % 16 * 2;
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));
+  }
+
+  if (index == 7)
+    TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));
+
+  if (index == 8) {
+    length = random() % (sizeof (data) + 1);
+    TEST_CHECK(NKSN_AddRecord(session, 1, 1000 + random() % 1000, data, length));
+  }
+
+  if (random() % 2) {
+    const char server[] = "127.0.0.1";
+    TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_SERVER_NEGOTIATION,
+                              server, sizeof (server) - 1));
+  }
+
+  if (random() % 2) {
+    data[0] = htons(123);
+    TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length));
+  }
+
+  if (random() % 2) {
+    length = random() % (sizeof (data) + 1);
+    TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
+  }
+
+  TEST_CHECK(NKSN_EndMessage(session));
+}
+
+static void
+process_response(NKSN_Instance session, int valid)
+{
+  int records, errors, critical, type, length;
+
+  for (records = errors = 0; ; records++) {
+    if (!NKSN_GetRecord(session, &critical, &type, &length, NULL, 0))
+      break;
+    if (type == NKE_RECORD_ERROR)
+      errors++;
+  }
+
+  if (valid) {
+    TEST_CHECK(records >= 2);
+  } else {
+    TEST_CHECK(records == 1);
+    TEST_CHECK(errors == 1);
+  }
+}
+
+void
+test_unit(void)
+{
+  NKSN_Instance session;
+  NKE_Context context, context2;
+  NKE_Cookie cookie;
+  int i, valid, l;
+  uint32_t sum, sum2;
+
+  char conf[][100] = {
+    "ntsdumpdir .",
+    "ntsport 0",
+    "ntsprocesses 0",
+    "ntsserverkey nts_ke.key",
+    "ntsservercert nts_ke.crt",
+  };
+
+  CNF_Initialise(0, 0);
+  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
+    CNF_ParseLine(NULL, i + 1, conf[i]);
+
+  LCL_Initialise();
+  TST_RegisterDummyDrivers();
+  SCH_Initialise();
+
+  unlink("ntskeys");
+  NKS_PreInitialise(0, 0, 0);
+  NKS_Initialise();
+
+  session = NKSN_CreateInstance(1, NULL, handle_message, NULL);
+
+  for (i = 0; i < 10000; i++) {
+    valid = random() % 2;
+    prepare_request(session, valid);
+    TEST_CHECK(process_request(session));
+    process_response(session, valid);
+  }
+
+
+  for (i = 0; i < 10000; i++) {
+    context.algorithm = AEAD_AES_SIV_CMAC_256;
+    get_keys(session, context.algorithm, &context.c2s, &context.s2c);
+    memset(&cookie, 0, sizeof (cookie));
+    TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
+    TEST_CHECK(NKS_DecodeCookie(&cookie, &context2));
+    TEST_CHECK(context.algorithm == context2.algorithm);
+    TEST_CHECK(context.c2s.length == context2.c2s.length);
+    TEST_CHECK(context.s2c.length == context2.s2c.length);
+    TEST_CHECK(memcmp(context.c2s.key, context2.c2s.key, context.c2s.length) == 0);
+    TEST_CHECK(memcmp(context.s2c.key, context2.s2c.key, context.s2c.length) == 0);
+
+    if (random() % 4) {
+      cookie.cookie[random() % (cookie.length)]++;
+    } else if (random() % 4) {
+      generate_key(current_server_key);
+    } else {
+      l = cookie.length;
+      while (l == cookie.length)
+        cookie.length = random() % (sizeof (cookie.cookie) + 1);
+    }
+    TEST_CHECK(!NKS_DecodeCookie(&cookie, &context2));
+  }
+
+  unlink("ntskeys");
+  save_keys();
+
+  for (i = 0, sum = 0; i < MAX_SERVER_KEYS; i++) {
+    sum += server_keys[i].id + server_keys[i].key[0];
+    generate_key(i);
+  }
+
+  load_keys();
+  TEST_CHECK(unlink("ntskeys") == 0);
+
+  for (i = 0, sum2 = 0; i < MAX_SERVER_KEYS; i++) {
+    sum2 += server_keys[i].id + server_keys[i].key[0];
+  }
+
+  TEST_CHECK(sum == sum2);
+
+  NKSN_DestroyInstance(session);
+
+  NKS_Finalise();
+  TEST_CHECK(unlink("ntskeys") == 0);
+
+  SCH_Finalise();
+  LCL_Finalise();
+  CNF_Finalise();
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/nts_ke_session.c b/chrony/test/unit/nts_ke_session.c
new file mode 100644
index 0000000..d0e72c7
--- /dev/null
+++ b/chrony/test/unit/nts_ke_session.c
@@ -0,0 +1,224 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTS
+
+#include <nts_ke_session.c>
+
+#include <local.h>
+#include <socket.h>
+#include <sched.h>
+
+static NKSN_Instance client, server;
+static unsigned char record[NKE_MAX_MESSAGE_LENGTH];
+static int record_length, critical, type_start, records;
+static int request_received;
+static int response_received;
+
+static void
+send_message(NKSN_Instance inst)
+{
+  int i;
+
+  record_length = random() % (NKE_MAX_MESSAGE_LENGTH - 4 + 1);
+  for (i = 0; i < record_length; i++)
+    record[i] = random() % 256;
+  critical = random() % 2;
+  type_start = random() % 30000 + 1;
+  assert(sizeof (struct RecordHeader) == 4);
+  records = random() % ((NKE_MAX_MESSAGE_LENGTH - 4) / (4 + record_length) + 1);
+
+  DEBUG_LOG("critical=%d type_start=%d records=%d*%d",
+            critical, type_start, records, record_length);
+
+  NKSN_BeginMessage(inst);
+
+  TEST_CHECK(check_message_format(&inst->message, 0));
+  TEST_CHECK(!check_message_format(&inst->message, 1));
+
+  TEST_CHECK(!NKSN_AddRecord(inst, 0, 1, record, NKE_MAX_MESSAGE_LENGTH - 4 + 1));
+
+  TEST_CHECK(check_message_format(&inst->message, 0));
+  TEST_CHECK(!check_message_format(&inst->message, 1));
+
+  for (i = 0; i < records; i++) {
+    TEST_CHECK(NKSN_AddRecord(inst, critical, type_start + i, record, record_length));
+    TEST_CHECK(!NKSN_AddRecord(inst, 0, 1, &record,
+                               NKE_MAX_MESSAGE_LENGTH - inst->message.length - 4 + 1));
+
+    TEST_CHECK(check_message_format(&inst->message, 0));
+    TEST_CHECK(!check_message_format(&inst->message, 1));
+  }
+
+  TEST_CHECK(NKSN_EndMessage(inst));
+
+  TEST_CHECK(check_message_format(&inst->message, 0));
+  TEST_CHECK(check_message_format(&inst->message, 1));
+}
+
+static void
+verify_message(NKSN_Instance inst)
+{
+  unsigned char buffer[NKE_MAX_MESSAGE_LENGTH];
+  int i, c, t, length, buffer_length, msg_length, prev_parsed;
+  NKE_Key c2s, s2c;
+
+  for (i = 0; i < records; i++) {
+    memset(buffer, 0, sizeof (buffer));
+    buffer_length = random() % (record_length + 1);
+    assert(buffer_length <= sizeof (buffer));
+
+    prev_parsed = inst->message.parsed;
+    msg_length = inst->message.length;
+
+    TEST_CHECK(NKSN_GetRecord(inst, &c, &t, &length, buffer, buffer_length));
+    TEST_CHECK(c == critical);
+    TEST_CHECK(t == type_start + i);
+    TEST_CHECK(length == record_length);
+    TEST_CHECK(memcmp(record, buffer, buffer_length) == 0);
+    if (buffer_length < record_length)
+      TEST_CHECK(buffer[buffer_length] == 0);
+
+    inst->message.length = inst->message.parsed - 1;
+    inst->message.parsed = prev_parsed;
+    TEST_CHECK(!get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length));
+    TEST_CHECK(inst->message.parsed == prev_parsed);
+    inst->message.length = msg_length;
+    if (msg_length < 0x8000) {
+      inst->message.data[prev_parsed + 2] ^= 0x80;
+      TEST_CHECK(!get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length));
+      TEST_CHECK(inst->message.parsed == prev_parsed);
+      inst->message.data[prev_parsed + 2] ^= 0x80;
+    }
+    TEST_CHECK(get_record(&inst->message, NULL, NULL, NULL, buffer, buffer_length));
+    TEST_CHECK(inst->message.parsed > prev_parsed);
+  }
+
+  TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer)));
+
+  TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, &c2s, &s2c));
+  TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
+  TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
+}
+
+static int
+handle_request(void *arg)
+{
+  NKSN_Instance server = arg;
+
+  verify_message(server);
+
+  request_received = 1;
+
+  send_message(server);
+
+  return 1;
+}
+
+static int
+handle_response(void *arg)
+{
+  NKSN_Instance client = arg;
+
+  response_received = 1;
+
+  verify_message(client);
+
+  return 1;
+}
+
+static void
+check_finished(void *arg)
+{
+  DEBUG_LOG("checking for stopped sessions");
+  if (!NKSN_IsStopped(server) || !NKSN_IsStopped(client)) {
+    SCH_AddTimeoutByDelay(0.001, check_finished, NULL);
+    return;
+  }
+
+  SCH_QuitProgram();
+}
+
+void
+test_unit(void)
+{
+  NKSN_Credentials client_cred, server_cred;
+  const char *cert, *key;
+  int sock_fds[2], i;
+  uint32_t cert_id;
+
+  LCL_Initialise();
+  TST_RegisterDummyDrivers();
+
+  cert = "nts_ke.crt";
+  key = "nts_ke.key";
+  cert_id = 0;
+
+  for (i = 0; i < 50; i++) {
+    SCH_Initialise();
+
+    server = NKSN_CreateInstance(1, NULL, handle_request, NULL);
+    client = NKSN_CreateInstance(0, "test", handle_response, NULL);
+
+    server_cred = NKSN_CreateServerCertCredentials(&cert, &key, 1);
+    client_cred = NKSN_CreateClientCertCredentials(&cert, &cert_id, 1, 0);
+
+    TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds) == 0);
+    TEST_CHECK(fcntl(sock_fds[0], F_SETFL, O_NONBLOCK) == 0);
+    TEST_CHECK(fcntl(sock_fds[1], F_SETFL, O_NONBLOCK) == 0);
+
+    TEST_CHECK(NKSN_StartSession(server, sock_fds[0], "client", server_cred, 4.0));
+    TEST_CHECK(NKSN_StartSession(client, sock_fds[1], "server", client_cred, 4.0));
+
+    send_message(client);
+
+    request_received = response_received = 0;
+
+    check_finished(NULL);
+
+    SCH_MainLoop();
+
+    TEST_CHECK(NKSN_IsStopped(server));
+    TEST_CHECK(NKSN_IsStopped(client));
+
+    TEST_CHECK(request_received);
+    TEST_CHECK(response_received);
+
+    NKSN_DestroyInstance(server);
+    NKSN_DestroyInstance(client);
+
+    NKSN_DestroyCertCredentials(server_cred);
+    NKSN_DestroyCertCredentials(client_cred);
+
+    SCH_Finalise();
+  }
+
+  LCL_Finalise();
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/nts_ntp_auth.c b/chrony/test/unit/nts_ntp_auth.c
new file mode 100644
index 0000000..307b93b
--- /dev/null
+++ b/chrony/test/unit/nts_ntp_auth.c
@@ -0,0 +1,112 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTS
+
+#include <nts_ntp_auth.c>
+
+#include "ntp_ext.h"
+#include "siv.h"
+
+void
+test_unit(void)
+{
+  unsigned char key[SIV_MAX_KEY_LENGTH], nonce[256], plaintext[256], plaintext2[256];
+  NTP_PacketInfo info;
+  NTP_Packet packet;
+  SIV_Instance siv;
+  int i, j, r, packet_length, nonce_length, key_length;
+  int plaintext_length, plaintext2_length, min_ef_length;
+
+  siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
+  TEST_CHECK(siv);
+
+  for (i = 0; i < 10000; i++) {
+    key_length = SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256);
+    for (j = 0; j < key_length; j++)
+      key[j] = random() % 256;
+    TEST_CHECK(SIV_SetKey(siv, key, key_length));
+
+    nonce_length = random() % sizeof (nonce) + 1;
+    for (j = 0; j < nonce_length; j++)
+      nonce[j] = random() % 256;
+
+    plaintext_length = random() % (sizeof (plaintext) + 1);
+    for (j = 0; j < plaintext_length; j++)
+      plaintext[j] = random() % 256;
+
+    packet_length = NTP_HEADER_LENGTH + random() % 100 * 4;
+    min_ef_length = random() % (sizeof (packet) - packet_length);
+
+    memset(&packet, 0, sizeof (packet));
+    packet.lvm = NTP_LVM(0, 4, 0);
+    memset(&info, 0, sizeof (info));
+    info.version = 4;
+    info.length = packet_length;
+
+    DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d",
+              packet_length, nonce_length, plaintext_length, min_ef_length);
+
+    r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
+                           -1, 0);
+    TEST_CHECK(!r);
+    r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext,
+                           plaintext_length, 0);
+    TEST_CHECK(!r);
+    r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
+                           plaintext_length, sizeof (packet) - info.length + 1);
+    TEST_CHECK(!r);
+
+    r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
+                           plaintext_length, min_ef_length);
+    TEST_CHECK(r);
+    TEST_CHECK(info.length - packet_length >= min_ef_length);
+
+    r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
+                          -1, &plaintext2_length);
+    TEST_CHECK(!r);
+
+    r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
+                          sizeof (plaintext2), &plaintext2_length);
+    TEST_CHECK(r);
+    TEST_CHECK(plaintext_length == plaintext2_length);
+    TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0);
+
+    j = random() % (packet_length + plaintext_length +
+                    nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4;
+    ((unsigned char *)&packet)[j]++;
+    r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
+                          sizeof (plaintext2), &plaintext2_length);
+    TEST_CHECK(!r);
+    ((unsigned char *)&packet)[j]--;
+  }
+
+  SIV_DestroyInstance(siv);
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/nts_ntp_client.c b/chrony/test/unit/nts_ntp_client.c
new file mode 100644
index 0000000..c14fd41
--- /dev/null
+++ b/chrony/test/unit/nts_ntp_client.c
@@ -0,0 +1,284 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTS
+
+#include "socket.h"
+#include "ntp.h"
+#include "nts_ke_client.h"
+
+#define NKC_CreateInstance(address, name, cert_set) Malloc(1)
+#define NKC_DestroyInstance(inst) Free(inst)
+#define NKC_Start(inst) (random() % 2)
+#define NKC_IsActive(inst) (random() % 2)
+#define NKC_GetRetryFactor(inst) (1)
+
+static int get_nts_data(NKC_Instance inst, NKE_Context *context,
+                        NKE_Cookie *cookies, int *num_cookies, int max_cookies,
+                        IPSockAddr *ntp_address);
+#define NKC_GetNtsData get_nts_data
+
+#include <nts_ntp_client.c>
+
+static int
+get_nts_data(NKC_Instance inst, NKE_Context *context,
+             NKE_Cookie *cookies, int *num_cookies, int max_cookies,
+             IPSockAddr *ntp_address)
+{
+  int i;
+
+  if (random() % 2)
+    return 0;
+
+  context->algorithm = AEAD_AES_SIV_CMAC_256;
+
+  context->c2s.length = SIV_GetKeyLength(context->algorithm);
+  UTI_GetRandomBytes(context->c2s.key, context->c2s.length);
+  context->s2c.length = SIV_GetKeyLength(context->algorithm);
+  UTI_GetRandomBytes(context->s2c.key, context->s2c.length);
+
+  *num_cookies = random() % max_cookies + 1;
+  for (i = 0; i < *num_cookies; i++) {
+    cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);
+    if (random() % 4 != 0)
+      cookies[i].length = cookies[i].length / 4 * 4;
+    memset(cookies[i].cookie, random(), cookies[i].length);
+  }
+
+  ntp_address->ip_addr.family = IPADDR_UNSPEC;
+  ntp_address->port = 0;
+
+  return 1;
+}
+
+static int
+get_request(NNC_Instance inst)
+{
+  unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH], uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
+  NTP_PacketInfo info;
+  NTP_Packet packet;
+  int expected_length, req_cookies;
+
+  memset(&packet, 0, sizeof (packet));
+  memset(&info, 0, sizeof (info));
+  info.version = 4;
+  info.mode = MODE_CLIENT;
+  info.length = random() % (sizeof (packet) + 1);
+  if (random() % 4 != 0)
+    info.length = info.length / 4 * 4;
+
+  if (inst->num_cookies > 0 && random() % 2) {
+    inst->num_cookies = 0;
+
+    TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info));
+  }
+
+  while (!NNC_PrepareForAuth(inst)) {
+    inst->next_nke_attempt = SCH_GetLastEventMonoTime() + random() % 10 - 7;
+  }
+
+  TEST_CHECK(inst->num_cookies > 0);
+  TEST_CHECK(inst->siv);
+
+  memcpy(nonce, inst->nonce, sizeof (nonce));
+  memcpy(uniq_id, inst->uniq_id, sizeof (uniq_id));
+  TEST_CHECK(NNC_PrepareForAuth(inst));
+  TEST_CHECK(memcmp(nonce, inst->nonce, sizeof (nonce)) != 0);
+  TEST_CHECK(memcmp(uniq_id, inst->uniq_id, sizeof (uniq_id)) != 0);
+
+  req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies + 1,
+                    MAX_TOTAL_COOKIE_LENGTH /
+                      (inst->cookies[inst->cookie_index].length + 4));
+  expected_length = info.length + 4 + sizeof (inst->uniq_id) +
+                    req_cookies * (4 + inst->cookies[inst->cookie_index].length) +
+                    4 + 4 + sizeof (inst->nonce) + SIV_GetTagLength(inst->siv);
+  DEBUG_LOG("length=%d cookie_length=%d expected_length=%d",
+            info.length, inst->cookies[inst->cookie_index].length, expected_length);
+
+  if (info.length % 4 == 0 && info.length >= NTP_HEADER_LENGTH &&
+      inst->cookies[inst->cookie_index].length % 4 == 0 &&
+      inst->cookies[inst->cookie_index].length >= (NTP_MIN_EF_LENGTH - 4) &&
+      expected_length <= sizeof (packet)) {
+    TEST_CHECK(NNC_GenerateRequestAuth(inst, &packet, &info));
+    TEST_CHECK(info.length == expected_length);
+    return 1;
+  } else {
+    TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info));
+    return 0;
+  }
+}
+
+static void
+prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
+{
+  unsigned char cookie[508], plaintext[528], nonce[448];
+  int nonce_length, ef_length, cookie_length, plaintext_length, min_auth_length;
+  int i, index, auth_start;
+  SIV_Instance siv;
+
+  memset(packet, 0, sizeof (*packet));
+  packet->lvm = NTP_LVM(0, 4, MODE_SERVER);
+  memset(info, 0, sizeof (*info));
+  info->version = 4;
+  info->mode = MODE_SERVER;
+  info->length = NTP_HEADER_LENGTH;
+
+  if (valid)
+    index = -1;
+  else
+    index = random() % (nak ? 2 : 8);
+
+  DEBUG_LOG("index=%d nak=%d", index, nak);
+
+  if (index != 0)
+    TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER, inst->uniq_id,
+                            sizeof (inst->uniq_id)));
+  if (index == 1)
+    ((unsigned char *)packet)[NTP_HEADER_LENGTH + 4]++;
+
+  if (nak) {
+    packet->stratum = NTP_INVALID_STRATUM;
+    packet->reference_id = htonl(NTP_KOD_NTS_NAK);
+    return;
+  }
+
+  nonce_length = random() % (sizeof (nonce)) + 1;
+
+  do {
+    cookie_length = random() % (sizeof (cookie) + 1);
+  } while (cookie_length % 4 != 0 ||
+           ((index != 2) == (cookie_length < NTP_MIN_EF_LENGTH - 4 ||
+                             cookie_length > NKE_MAX_COOKIE_LENGTH)));
+
+  min_auth_length = random() % (512 + 1);
+
+  DEBUG_LOG("nonce_length=%d cookie_length=%d min_auth_length=%d",
+            nonce_length, cookie_length, min_auth_length);
+
+  UTI_GetRandomBytes(nonce, nonce_length);
+  UTI_GetRandomBytes(cookie, cookie_length);
+
+  if (cookie_length >= 12 && cookie_length <= 32 && random() % 2 == 0)
+    TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_COOKIE, cookie, cookie_length));
+
+  plaintext_length = 0;
+  if (index != 3) {
+    for (i = random() % ((sizeof (plaintext) - 16) / (cookie_length + 4)); i >= 0; i--) {
+      TEST_CHECK(NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
+                              NTP_EF_NTS_COOKIE, cookie,
+                              i == 0 ? cookie_length : random() % (cookie_length + 1) / 4 * 4,
+                              &ef_length));
+      plaintext_length += ef_length;
+    }
+  }
+  auth_start = info->length;
+  if (index != 4) {
+    if (index == 5) {
+      assert(plaintext_length + 16 <= sizeof (plaintext));
+      memset(plaintext + plaintext_length, 0, 16);
+      plaintext_length += 16;
+    }
+    siv = SIV_CreateInstance(inst->context.algorithm);
+    TEST_CHECK(siv);
+    TEST_CHECK(SIV_SetKey(siv, inst->context.s2c.key, inst->context.s2c.length));
+    TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv,
+                                  nonce, nonce_length, plaintext, plaintext_length,
+                                  min_auth_length));
+    SIV_DestroyInstance(siv);
+  }
+  if (index == 6)
+    ((unsigned char *)packet)[auth_start + 8]++;
+  if (index == 7)
+    TEST_CHECK(NEF_AddField(packet, info, 0x7000, inst->uniq_id, sizeof (inst->uniq_id)));
+}
+
+void
+test_unit(void)
+{
+  NNC_Instance inst;
+  NTP_PacketInfo info;
+  NTP_Packet packet;
+  IPSockAddr addr;
+  IPAddr ip_addr;
+  int i, j, prev_num_cookies, valid;
+
+  TEST_CHECK(SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0);
+
+  SCK_GetLoopbackIPAddress(AF_INET, &addr.ip_addr);
+  addr.port = 0;
+
+  inst = NNC_CreateInstance(&addr, "test", 0, 0);
+  TEST_CHECK(inst);
+
+  for (i = 0; i < 100000; i++) {
+    if (!get_request(inst))
+      continue;
+
+    valid = random() % 2;
+
+    TEST_CHECK(!inst->nak_response);
+    TEST_CHECK(!inst->ok_response);
+
+    if (random() % 2) {
+      prepare_response(inst, &packet, &info, 0, 1);
+      TEST_CHECK(!NNC_CheckResponseAuth(inst, &packet, &info));
+      TEST_CHECK(!inst->nak_response);
+      TEST_CHECK(!inst->ok_response);
+      for (j = random() % 3; j > 0; j--) {
+        prepare_response(inst, &packet, &info, 1, 1);
+        TEST_CHECK(!NNC_CheckResponseAuth(inst, &packet, &info));
+        TEST_CHECK(inst->nak_response);
+        TEST_CHECK(!inst->ok_response);
+      }
+    }
+
+    prev_num_cookies = inst->num_cookies;
+    prepare_response(inst, &packet, &info, valid, 0);
+
+    if (valid) {
+      TEST_CHECK(NNC_CheckResponseAuth(inst, &packet, &info));
+      TEST_CHECK(inst->num_cookies >= MIN(NTS_MAX_COOKIES, prev_num_cookies + 1));
+      TEST_CHECK(inst->ok_response);
+    }
+
+    prev_num_cookies = inst->num_cookies;
+    TEST_CHECK(!NNC_CheckResponseAuth(inst, &packet, &info));
+    TEST_CHECK(inst->num_cookies == prev_num_cookies);
+    TEST_CHECK(inst->ok_response == valid);
+
+    if (random() % 10 == 0) {
+      TST_GetRandomAddress(&ip_addr, IPADDR_INET4, 32);
+      NNC_ChangeAddress(inst, &ip_addr);
+      TEST_CHECK(UTI_CompareIPs(&inst->nts_address.ip_addr, &ip_addr, NULL) == 0);
+    }
+  }
+
+  NNC_DestroyInstance(inst);
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony/test/unit/nts_ntp_server.c b/chrony/test/unit/nts_ntp_server.c
new file mode 100644
index 0000000..40938d6
--- /dev/null
+++ b/chrony/test/unit/nts_ntp_server.c
@@ -0,0 +1,176 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2020
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include "test.h"
+
+#ifdef FEAT_NTS
+
+#include <local.h>
+#include <sched.h>
+
+#include <nts_ntp_server.c>
+
+static void
+prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
+{
+  unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH], nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
+  SIV_Instance siv;
+  NKE_Context context;
+  NKE_Cookie cookie;
+  int i, index, cookie_start, auth_start;
+
+  context.algorithm = SERVER_SIV;
+  context.c2s.length = SIV_GetKeyLength(context.algorithm);
+  UTI_GetRandomBytes(&context.c2s.key, context.c2s.length);
+  context.s2c.length = SIV_GetKeyLength(context.algorithm);
+  UTI_GetRandomBytes(&context.s2c.key, context.s2c.length);
+
+  TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
+
+  UTI_GetRandomBytes(uniq_id, sizeof (uniq_id));
+  UTI_GetRandomBytes(nonce, sizeof (nonce));
+
+  memset(packet, 0, sizeof (*packet));
+  packet->lvm = NTP_LVM(0, 4, MODE_CLIENT);
+  memset(info, 0, sizeof (*info));
+  info->version = 4;
+  info->mode = MODE_CLIENT;
+  info->length = NTP_HEADER_LENGTH;
+
+  if (valid)
+    index = -1;
+  else
+    index = random() % 3;
+
+  DEBUG_LOG("valid=%d nak=%d index=%d", valid, nak, index);
+
+  if (index != 0)
+    TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
+                            uniq_id, sizeof (uniq_id)));
+
+  cookie_start = info->length;
+
+  if (index != 1)
+    TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
+                            cookie.cookie, cookie.length));
+
+  for (i = random() % 4; i > 0; i--)
+    TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
+                            cookie.cookie, cookie.length));
+
+  auth_start = info->length;
+
+  if (index != 2) {
+    siv = SIV_CreateInstance(context.algorithm);
+    TEST_CHECK(SIV_SetKey(siv, context.c2s.key, context.c2s.length));
+    TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, nonce, sizeof (nonce),
+                                  (const unsigned char *)"", 0, 0));
+    SIV_DestroyInstance(siv);
+  }
+
+  if (nak)
+    ((unsigned char *)packet)[(index == 2 ? cookie_start :
+                               (index == 1 ? auth_start :
+                                (random() % 2 ? cookie_start : auth_start))) +
+                              4 + random() % 16]++;
+}
+
+static void
+init_response(NTP_Packet *packet, NTP_PacketInfo *info)
+{
+  memset(packet, 0, sizeof (*packet));
+  packet->lvm = NTP_LVM(0, 4, MODE_SERVER);
+  memset(info, 0, sizeof (*info));
+  info->version = 4;
+  info->mode = MODE_SERVER;
+  info->length = NTP_HEADER_LENGTH;
+}
+
+void
+test_unit(void)
+{
+  NTP_PacketInfo req_info, res_info;
+  NTP_Packet request, response;
+  int i, valid, nak;
+  uint32_t kod;
+
+  char conf[][100] = {
+    "ntsport 0",
+    "ntsprocesses 0",
+    "ntsserverkey nts_ke.key",
+    "ntsservercert nts_ke.crt",
+  };
+
+  CNF_Initialise(0, 0);
+  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
+    CNF_ParseLine(NULL, i + 1, conf[i]);
+
+  LCL_Initialise();
+  TST_RegisterDummyDrivers();
+  SCH_Initialise();
+  NKS_PreInitialise(0, 0, 0);
+  NKS_Initialise();
+  NNS_Initialise();
+
+  for (i = 0; i < 50000; i++) {
+    valid = random() % 2;
+    nak = random() % 2;
+    prepare_request(&request, &req_info, valid, nak);
+
+    TEST_CHECK(NNS_CheckRequestAuth(&request, &req_info, &kod) == (valid && !nak));
+
+    if (valid && !nak) {
+      TEST_CHECK(kod == 0);
+      TEST_CHECK(server->num_cookies > 0);
+
+      init_response(&response, &res_info);
+      TEST_CHECK(NNS_GenerateResponseAuth(&request, &req_info, &response, &res_info, kod));
+
+      TEST_CHECK(res_info.ext_fields == 2);
+      TEST_CHECK(server->num_cookies == 0);
+    } else if (valid && nak) {
+      TEST_CHECK(kod == NTP_KOD_NTS_NAK);
+      TEST_CHECK(server->num_cookies == 0);
+
+      init_response(&response, &res_info);
+      TEST_CHECK(NNS_GenerateResponseAuth(&request, &req_info, &response, &res_info, kod));
+
+      TEST_CHECK(res_info.ext_fields == 1);
+      TEST_CHECK(server->num_cookies == 0);
+    } else {
+      TEST_CHECK(kod == 0);
+      TEST_CHECK(server->num_cookies == 0);
+    }
+  }
+
+  NNS_Finalise();
+  NKS_Finalise();
+  SCH_Finalise();
+  LCL_Finalise();
+  CNF_Finalise();
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony_3_3/test/unit/regress.c b/chrony/test/unit/regress.c
similarity index 100%
rename from chrony_3_3/test/unit/regress.c
rename to chrony/test/unit/regress.c
diff --git a/chrony/test/unit/samplefilt.c b/chrony/test/unit/samplefilt.c
new file mode 100644
index 0000000..513ff2c
--- /dev/null
+++ b/chrony/test/unit/samplefilt.c
@@ -0,0 +1,116 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2018
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <local.h>
+#include "test.h"
+
+#define LCL_GetSysPrecisionAsQuantum() (1.0e-6)
+
+#include <samplefilt.c>
+
+void
+test_unit(void)
+{
+  NTP_Sample sample_in, sample_out;
+  SPF_Instance filter;
+  int i, j, k, sum_count, min_samples, max_samples;
+  double mean, combine_ratio, sum_err;
+
+  LCL_Initialise();
+
+  memset(&sample_in, 0, sizeof (sample_in));
+  memset(&sample_out, 0, sizeof (sample_out));
+
+  for (i = 0; i <= 100; i++) {
+    max_samples = random() % 20 + 1;
+    min_samples = random() % (max_samples) + 1;
+    combine_ratio = TST_GetRandomDouble(0.0, 1.0);
+
+    filter = SPF_CreateInstance(min_samples, max_samples, 2.0, combine_ratio);
+
+    for (j = 0, sum_count = 0, sum_err = 0.0; j < 100; j++) {
+      DEBUG_LOG("iteration %d/%d", i, j);
+
+      mean = TST_GetRandomDouble(-1.0e3, 1.0e3);
+      UTI_ZeroTimespec(&sample_in.time);
+
+      for (k = 0; k < 100; k++) {
+        UTI_AddDoubleToTimespec(&sample_in.time, TST_GetRandomDouble(1.0e-1, 1.0e2),
+                                &sample_in.time);
+        sample_in.offset = mean + TST_GetRandomDouble(-1.0, 1.0);
+        sample_in.peer_dispersion = TST_GetRandomDouble(1.0e-4, 2.0e-4);
+        sample_in.root_dispersion = TST_GetRandomDouble(1.0e-3, 2.0e-3);
+        sample_in.peer_delay = TST_GetRandomDouble(1.0e-2, 2.0e-2);
+        sample_in.root_delay = TST_GetRandomDouble(1.0e-1, 2.0e-1);
+
+        TEST_CHECK(SPF_AccumulateSample(filter, &sample_in));
+        TEST_CHECK(!SPF_AccumulateSample(filter, &sample_in));
+
+        TEST_CHECK(SPF_GetNumberOfSamples(filter) == MIN(k + 1, max_samples));
+
+        SPF_GetLastSample(filter, &sample_out);
+        TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in)));
+
+        SPF_SlewSamples(filter, &sample_in.time, 0.0, 0.0);
+        SPF_AddDispersion(filter, 0.0);
+
+        if (k + 1 < min_samples)
+          TEST_CHECK(!SPF_GetFilteredSample(filter, &sample_out));
+
+        TEST_CHECK(SPF_GetNumberOfSamples(filter) == MIN(k + 1, max_samples));
+      }
+
+      if (random() % 10) {
+        TEST_CHECK(SPF_GetFilteredSample(filter, &sample_out));
+
+        TEST_CHECK(SPF_GetAvgSampleDispersion(filter) <= 2.0);
+
+        sum_err += sample_out.offset - mean;
+        sum_count++;
+
+        TEST_CHECK(UTI_CompareTimespecs(&sample_out.time, &sample_in.time) <= 0 &&
+                   sample_out.time.tv_sec >= 0);
+        TEST_CHECK(fabs(sample_out.offset - mean) <= 1.0);
+        TEST_CHECK(sample_out.peer_dispersion >= 1.0e-4 &&
+                   (sample_out.peer_dispersion <= 2.0e-4 || filter->max_samples > 1));
+        TEST_CHECK(sample_out.root_dispersion >= 1.0e-3 &&
+                   (sample_out.root_dispersion <= 2.0e-3 || filter->max_samples > 1));
+        TEST_CHECK(sample_out.peer_delay >= 1.0e-2 &&
+                   sample_out.peer_delay <= 2.0e-2);
+        TEST_CHECK(sample_out.root_delay >= 1.0e-1 &&
+                   sample_out.root_delay <= 2.0e-1);
+
+        if (max_samples == 1)
+          TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in)));
+
+      } else {
+        SPF_DropSamples(filter);
+      }
+
+      TEST_CHECK(SPF_GetNumberOfSamples(filter) == 0);
+    }
+
+    TEST_CHECK(fabs(sum_err / sum_count) < 0.3);
+
+    SPF_DestroyInstance(filter);
+  }
+
+  LCL_Finalise();
+}
diff --git a/chrony/test/unit/siv.c b/chrony/test/unit/siv.c
new file mode 100644
index 0000000..76846c8
--- /dev/null
+++ b/chrony/test/unit/siv.c
@@ -0,0 +1,321 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2019
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <config.h>
+#include <sysincl.h>
+#include <logging.h>
+#include <siv.h>
+#include "test.h"
+
+#ifdef HAVE_SIV
+
+struct siv_test {
+  SIV_Algorithm algorithm;
+  const unsigned char key[64];
+  int key_length;
+  const unsigned char nonce[128];
+  int nonce_length;
+  const unsigned char assoc[128];
+  int assoc_length;
+  const unsigned char plaintext[128];
+  int plaintext_length;
+  const unsigned char ciphertext[128];
+  int ciphertext_length;
+};
+
+void
+test_unit(void)
+{
+  struct siv_test tests[] = {
+    { AEAD_AES_SIV_CMAC_256,
+      "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde"
+      "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32,
+      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16,
+      "", 0,
+      "", 0,
+      "\x22\x3e\xb5\x94\xe0\xe0\x25\x4b\x00\x25\x8e\x21\x9a\x1c\xa4\x21", 16
+    },
+    { AEAD_AES_SIV_CMAC_256,
+      "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde"
+      "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32,
+      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16,
+      "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16,
+      "", 0,
+      "\xd7\x20\x19\x89\xc6\xdb\xc6\xd6\x61\xfc\x62\xbc\x86\x5e\xee\xef", 16
+    },
+    { AEAD_AES_SIV_CMAC_256,
+      "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde"
+      "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32,
+      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16,
+      "", 0,
+      "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16,
+      "\xb6\xc1\x60\xe9\xc2\xfd\x2a\xe8\xde\xc5\x36\x8b\x2a\x33\xed\xe1"
+      "\x14\xff\xb3\x97\x34\x5c\xcb\xe4\x4a\xa4\xde\xac\xd9\x36\x90\x46", 32
+    },
+    { AEAD_AES_SIV_CMAC_256,
+      "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde"
+      "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32,
+      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e", 15,
+      "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c", 15,
+      "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4", 15,
+      "\x03\x8c\x41\x51\xba\x7a\x8f\x77\x6e\x56\x31\x99\x42\x0b\xc7\x03"
+      "\xe7\x6c\x67\xc9\xda\xb7\x0d\x5b\x44\x06\x26\x5a\xd0\xd2\x3b", 31
+    },
+    { AEAD_AES_SIV_CMAC_256,
+      "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde"
+      "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32,
+      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 16,
+      "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16,
+      "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7", 16,
+      "\x5c\x05\x23\x65\xf4\x57\x0a\xa0\xfb\x38\x3e\xce\x9b\x75\x85\xeb"
+      "\x68\x85\x19\x36\x0c\x7c\x48\x11\x40\xcb\x9b\x57\x9a\x0e\x65\x32", 32
+    },
+    { AEAD_AES_SIV_CMAC_256,
+      "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde"
+      "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32,
+      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+      "\xd5", 17,
+      "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b"
+      "\xa0", 17,
+      "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7"
+      "\x08", 17,
+      "\xaf\x58\x4b\xe7\x82\x1e\x96\x19\x29\x91\x25\xe0\xdd\x80\x3b\x49"
+      "\xa5\x11\xcd\xb6\x08\xf3\x76\xa0\xb6\xfa\x15\x82\xf3\x95\xe1\xeb"
+      "\xbd", 33
+    },
+    { AEAD_AES_SIV_CMAC_256,
+      "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde"
+      "\xef\x01\x23\x45\x67\x89\xab\xcd\xde\xf0\x12\x34\x56\x78\x9a\xbc", 32,
+      "\xb0\x5a\x1b\xc7\x56\xe7\xb6\x2c\xb4\x85\xe5\x56\xa5\x28\xc0\x6c"
+      "\x2f\x3b\x0b\x9d\x1a\x0c\xdf\x69\x47\xe0\xcc\xc0\x87\xaa\x5c\x09"
+      "\x98\x48\x8d\x6a\x8e\x1e\x05\xd7\x8b\x68\x74\x83\xb5\x1d\xf1\x2c", 48,
+      "\xe5\x8b\xd2\x6a\x30\xc5\xc5\x61\xcc\xbd\x7c\x27\xbf\xfe\xf9\x06"
+      "\x00\x5b\xd7\xfc\x11\x0b\xcf\x16\x61\xef\xac\x05\xa7\xaf\xec\x27"
+      "\x41\xc8\x5e\x9e\x0d\xf9\x2f\xaf\x20\x79\x17\xe5\x17\x91\x2a\x27"
+      "\x34\x1c\xbc\xaf\xeb\xef\x7f\x52\xe7\x1e\x4c\x2a\xca\xbd\x2b\xbe"
+      "\x34\xd6\xfb\x69\xd3\x3e\x49\x59\x60\xb4\x26\xc9\xb8\xce\xba", 79,
+      "\x6c\xe7\xcf\x7e\xab\x7b\xa0\xe1\xa7\x22\xcb\x88\xde\x5e\x42\xd2"
+      "\xec\x79\xe0\xa2\xcf\x5f\x0f\x6f\x6b\x89\x57\xcd\xae\x17\xd4\xc2"
+      "\xf3\x1b\xa2\xa8\x13\x78\x23\x2f\x83\xa8\xd4\x0c\xc0\xd2\xf3\x99"
+      "\xae\x81\xa1\xca\x5b\x5f\x45\xa6\x6f\x0c\x8a\xf3\xd4\x67\x40\x81"
+      "\x26\xe2\x01\x86\xe8\x5a\xd5\xf8\x58\x80\x9f\x56\xaa\x76\x96\xbf"
+      "\x31", 81,
+      "\x9a\x06\x33\xe0\xee\x00\x6a\x9b\xc8\x20\xd5\xe2\xc2\xed\xb5\x75"
+      "\xfa\x9e\x42\x2a\x31\x6b\xda\xca\xaa\x7d\x31\x8b\x84\x7a\xb8\xd7"
+      "\x8a\x81\x25\x64\xed\x41\x9b\xa9\x77\x10\xbd\x05\x0c\x4e\xc5\x31"
+      "\x0c\xa2\x86\xec\x8a\x94\xc8\x24\x23\x3c\x13\xee\xa5\x51\xc9\xdf"
+      "\x48\xc9\x55\xc5\x2f\x40\x73\x3f\x98\xbb\x8d\x69\x78\x46\x64\x17"
+      "\x8d\x49\x2f\x14\x62\xa4\x7c\x2a\x57\x38\x87\xce\xc6\x72\xd3\x5c"
+      "\xa1", 97
+    },
+    { 0, "", 0 }
+  };
+
+  unsigned char plaintext[sizeof (((struct siv_test *)NULL)->plaintext)];
+  unsigned char ciphertext[sizeof (((struct siv_test *)NULL)->ciphertext)];
+  SIV_Instance siv;
+  int i, j, r;
+
+  TEST_CHECK(SIV_CreateInstance(0) == NULL);
+
+  for (i = 0; tests[i].algorithm != 0; i++) {
+    DEBUG_LOG("testing %d (%d)", (int)tests[i].algorithm, i);
+
+    assert(tests[i].key_length <= sizeof (tests[i].key));
+    assert(tests[i].nonce_length <= sizeof (tests[i].nonce));
+    assert(tests[i].assoc_length <= sizeof (tests[i].assoc));
+    assert(tests[i].plaintext_length <= sizeof (tests[i].plaintext));
+    assert(tests[i].ciphertext_length <= sizeof (tests[i].ciphertext));
+
+    siv = SIV_CreateInstance(tests[i].algorithm);
+    TEST_CHECK(siv != NULL);
+
+    TEST_CHECK(SIV_GetKeyLength(tests[i].algorithm) == tests[i].key_length);
+
+    r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                    tests[i].assoc, tests[i].assoc_length,
+                    tests[i].plaintext, tests[i].plaintext_length,
+                    ciphertext, tests[i].ciphertext_length);
+    TEST_CHECK(!r);
+    r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                    tests[i].assoc, tests[i].assoc_length,
+                    tests[i].ciphertext, tests[i].ciphertext_length,
+                    plaintext, tests[i].plaintext_length);
+    TEST_CHECK(!r);
+
+    for (j = -1; j < 1024; j++) {
+      r = SIV_SetKey(siv, tests[i].key, j);
+      TEST_CHECK(r == (j == tests[i].key_length));
+    }
+
+    TEST_CHECK(SIV_GetTagLength(siv) == tests[i].ciphertext_length - tests[i].plaintext_length);
+
+    r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                    tests[i].assoc, tests[i].assoc_length,
+                    tests[i].plaintext, tests[i].plaintext_length,
+                    ciphertext, tests[i].ciphertext_length);
+    TEST_CHECK(r);
+
+#if 0
+    for (j = 0; j < tests[i].ciphertext_length; j++) {
+      printf("\\x%02x", ciphertext[j]);
+      if (j % 16 == 15)
+        printf("\n");
+    }
+    printf("\n");
+#endif
+    TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) == 0);
+
+    for (j = -1; j < tests[i].nonce_length; j++) {
+      r = SIV_Encrypt(siv, tests[i].nonce, j,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].plaintext, tests[i].plaintext_length,
+                      ciphertext, tests[i].ciphertext_length);
+      if (j > 0) {
+        TEST_CHECK(r);
+        TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) != 0);
+      } else {
+        TEST_CHECK(!r);
+      }
+    }
+
+    for (j = -1; j < tests[i].assoc_length; j++) {
+      r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, j,
+                      tests[i].plaintext, tests[i].plaintext_length,
+                      ciphertext, tests[i].ciphertext_length);
+      if (j >= 0) {
+        TEST_CHECK(r);
+        TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) != 0);
+      } else {
+        TEST_CHECK(!r);
+      }
+    }
+
+    for (j = -1; j < tests[i].plaintext_length; j++) {
+      r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].plaintext, j,
+                      ciphertext, j + SIV_GetTagLength(siv));
+      if (j >= 0) {
+        TEST_CHECK(r);
+        TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, j + SIV_GetTagLength(siv)) != 0);
+      } else {
+        TEST_CHECK(!r);
+      }
+    }
+
+    for (j = -1; j < 2 * tests[i].plaintext_length; j++) {
+      if (j == tests[i].plaintext_length)
+        continue;
+      r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].plaintext, j,
+                      ciphertext, tests[i].ciphertext_length);
+      TEST_CHECK(!r);
+    }
+
+    for (j = -1; j < 2 * tests[i].ciphertext_length; j++) {
+      if (j == tests[i].ciphertext_length)
+        continue;
+      r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].plaintext, tests[i].plaintext_length,
+                      ciphertext, j);
+      TEST_CHECK(!r);
+    }
+
+    r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                    tests[i].assoc, tests[i].assoc_length,
+                    tests[i].ciphertext, tests[i].ciphertext_length,
+                    plaintext, tests[i].plaintext_length);
+    TEST_CHECK(r);
+    TEST_CHECK(memcmp(plaintext, tests[i].plaintext, tests[i].plaintext_length) == 0);
+
+    for (j = -1; j < tests[i].nonce_length; j++) {
+      r = SIV_Decrypt(siv, tests[i].nonce, j,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].ciphertext, tests[i].ciphertext_length,
+                      plaintext, tests[i].plaintext_length);
+      TEST_CHECK(!r);
+    }
+
+    for (j = -1; j < tests[i].assoc_length; j++) {
+      r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, j,
+                      tests[i].ciphertext, tests[i].ciphertext_length,
+                      plaintext, tests[i].plaintext_length);
+      TEST_CHECK(!r);
+    }
+
+    for (j = -1; j < 2 * tests[i].ciphertext_length; j++) {
+      if (j == tests[i].ciphertext_length)
+        continue;
+
+      r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].ciphertext, j,
+                      plaintext, tests[i].plaintext_length);
+      TEST_CHECK(!r);
+    }
+
+    for (j = -1; j < tests[i].plaintext_length; j++) {
+      r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].ciphertext, tests[i].ciphertext_length,
+                      plaintext, j);
+      TEST_CHECK(!r);
+
+      r = SIV_Decrypt(siv, tests[i].nonce, tests[i].nonce_length,
+                      tests[i].assoc, tests[i].assoc_length,
+                      tests[i].ciphertext, j + SIV_GetTagLength(siv),
+                      plaintext, j);
+      TEST_CHECK(!r);
+    }
+
+    SIV_DestroyInstance(siv);
+  }
+
+  siv = SIV_CreateInstance(tests[0].algorithm);
+  for (i = 0; i < 1000; i++) {
+    for (j = 0; tests[j].algorithm == tests[0].algorithm; j++) {
+      r = SIV_SetKey(siv, tests[j].key, tests[j].key_length);
+      TEST_CHECK(r);
+      r = SIV_Encrypt(siv, tests[j].nonce, tests[j].nonce_length,
+                      tests[j].assoc, tests[j].assoc_length,
+                      tests[j].plaintext, tests[j].plaintext_length,
+                      ciphertext, tests[j].ciphertext_length);
+      TEST_CHECK(r);
+      r = SIV_Decrypt(siv, tests[j].nonce, tests[j].nonce_length,
+                      tests[j].assoc, tests[j].assoc_length,
+                      tests[j].ciphertext, tests[j].ciphertext_length,
+                      plaintext, tests[j].plaintext_length);
+      TEST_CHECK(r);
+    }
+  }
+  SIV_DestroyInstance(siv);
+}
+#else
+void
+test_unit(void)
+{
+  TEST_REQUIRE(0);
+}
+#endif
diff --git a/chrony_3_3/test/unit/smooth.c b/chrony/test/unit/smooth.c
similarity index 100%
rename from chrony_3_3/test/unit/smooth.c
rename to chrony/test/unit/smooth.c
diff --git a/chrony/test/unit/sources.c b/chrony/test/unit/sources.c
new file mode 100644
index 0000000..6d85b82
--- /dev/null
+++ b/chrony/test/unit/sources.c
@@ -0,0 +1,242 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2016, 2018
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <sources.c>
+#include "test.h"
+
+static SRC_Instance
+create_source(SRC_Type type, IPAddr *addr, int authenticated, int sel_options)
+{
+  TST_GetRandomAddress(addr, IPADDR_UNSPEC, -1);
+
+  return SRC_CreateNewInstance(UTI_IPToRefid(addr), type, authenticated, sel_options,
+                               type == SRC_NTP ? addr : NULL,
+                               SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES, 0.0, 1.0);
+}
+
+void
+test_unit(void)
+{
+  SRC_AuthSelectMode sel_mode;
+  SRC_Instance srcs[16];
+  IPAddr addrs[16];
+  RPT_SourceReport report;
+  NTP_Sample sample;
+  int i, j, k, l, n1, n2, n3, n4, samples, sel_options;
+  char conf[128];
+
+  CNF_Initialise(0, 0);
+  LCL_Initialise();
+  TST_RegisterDummyDrivers();
+  SCH_Initialise();
+  SRC_Initialise();
+  REF_Initialise();
+  NSR_Initialise();
+
+  REF_SetMode(REF_ModeIgnore);
+
+  for (i = 0; i < 1000; i++) {
+    DEBUG_LOG("iteration %d", i);
+
+    for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
+      TEST_CHECK(n_sources == j);
+
+      sel_options = i & random() & (SRC_SELECT_NOSELECT | SRC_SELECT_PREFER |
+                                    SRC_SELECT_TRUST | SRC_SELECT_REQUIRE);
+
+      DEBUG_LOG("added source %d options %d", j, sel_options);
+      srcs[j] = create_source(SRC_NTP, &addrs[j], 0, sel_options);
+      SRC_UpdateReachability(srcs[j], 1);
+
+      samples = (i + j) % 5 + 3;
+
+      sample.offset = TST_GetRandomDouble(-1.0, 1.0);
+
+      for (k = 0; k < samples; k++) {
+        SCH_GetLastEventTime(&sample.time, NULL, NULL);
+        UTI_AddDoubleToTimespec(&sample.time, TST_GetRandomDouble(k - samples, k - samples + 1),
+                                &sample.time);
+
+        sample.offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
+        sample.peer_delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
+        sample.peer_dispersion = TST_GetRandomDouble(1.0e-6, 1.0e-1);
+        sample.root_delay = sample.peer_delay;
+        sample.root_dispersion = sample.peer_dispersion;
+
+        if (random() % 2)
+          SRC_UpdateStatus(srcs[j], 1, random() % 4);
+
+        DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k,
+                  sample.offset, sample.peer_delay, sample.peer_dispersion);
+
+        SRC_AccumulateSample(srcs[j], &sample);
+      }
+
+      for (k = 0; k <= j; k++) {
+        int passed = 0, trusted = 0, trusted_passed = 0, required = 0, required_passed = 0;
+        double trusted_lo = DBL_MAX, trusted_hi = DBL_MIN;
+        double passed_lo = DBL_MAX, passed_hi = DBL_MIN;
+
+        SRC_SelectSource(srcs[k]);
+        DEBUG_LOG("source %d status %u", k, sources[k]->status);
+
+        for (l = 0; l <= j; l++) {
+          TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED);
+          if (sources[l]->sel_options & SRC_SELECT_NOSELECT) {
+            TEST_CHECK(sources[l]->status == SRC_UNSELECTABLE);
+          } else if (sources[l]->status != SRC_BAD_DISTANCE) {
+            if (sources[l]->status >= SRC_NONPREFERRED) {
+              passed++;
+              if (passed_lo > sources[l]->sel_info.lo_limit)
+                passed_lo = sources[l]->sel_info.lo_limit;
+              if (passed_hi < sources[l]->sel_info.hi_limit)
+                passed_hi = sources[l]->sel_info.hi_limit;
+            }
+            if (sources[l]->sel_options & SRC_SELECT_TRUST) {
+              trusted++;
+              if (trusted_lo > sources[l]->sel_info.lo_limit)
+                trusted_lo = sources[l]->sel_info.lo_limit;
+              if (trusted_hi < sources[l]->sel_info.hi_limit)
+                trusted_hi = sources[l]->sel_info.hi_limit;
+              if (sources[l]->status >= SRC_NONPREFERRED)
+                trusted_passed++;
+            }
+            if (sources[l]->sel_options & SRC_SELECT_REQUIRE) {
+              required++;
+              if (sources[l]->status >= SRC_NONPREFERRED)
+                required_passed++;
+            }
+            if (sources[l]->sel_options & SRC_SELECT_PREFER)
+              TEST_CHECK(sources[l]->status != SRC_NONPREFERRED);
+          }
+        }
+
+        DEBUG_LOG("sources %d passed %d trusted %d/%d required %d/%d", j + 1, passed,
+                  trusted_passed, trusted, required_passed, required);
+
+        TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi));
+        TEST_CHECK(!passed || !trusted || trusted_passed >= 1);
+        TEST_CHECK(!passed || !required || required_passed > 0);
+
+        for (l = 0; l <= j; l++) {
+          TEST_CHECK(sources[l]->leap_vote ==
+                     (sources[l]->status >= SRC_NONPREFERRED &&
+                      (!trusted || sources[l]->sel_options & SRC_SELECT_TRUST)));
+        }
+      }
+    }
+
+    for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
+      SRC_ReportSource(j, &report, &sample.time);
+      SRC_DestroyInstance(srcs[j]);
+    }
+  }
+
+  TEST_CHECK(CNF_GetAuthSelectMode() == SRC_AUTHSELECT_MIX);
+
+  for (i = 0; i < 1000; i++) {
+    DEBUG_LOG("iteration %d", i);
+
+    switch (i % 4) {
+      case 0:
+        snprintf(conf, sizeof (conf), "authselectmode require");
+        sel_mode = SRC_AUTHSELECT_REQUIRE;
+        break;
+      case 1:
+        snprintf(conf, sizeof (conf), "authselectmode prefer");
+        sel_mode = SRC_AUTHSELECT_PREFER;
+        break;
+      case 2:
+        snprintf(conf, sizeof (conf), "authselectmode mix");
+        sel_mode = SRC_AUTHSELECT_MIX;
+        break;
+      case 3:
+        snprintf(conf, sizeof (conf), "authselectmode ignore");
+        sel_mode = SRC_AUTHSELECT_IGNORE;
+        break;
+    }
+
+    CNF_ParseLine(NULL, 0, conf);
+    TEST_CHECK(CNF_GetAuthSelectMode() == sel_mode);
+
+    sel_options = random() & (SRC_SELECT_PREFER | SRC_SELECT_TRUST | SRC_SELECT_REQUIRE);
+
+    n1 = random() % 3;
+    n2 = random() % 3;
+    n3 = random() % 3;
+    n4 = random() % 3;
+    assert(n1 + n2 + n3 < sizeof (srcs) / sizeof (srcs[0]));
+
+    for (j = 0; j < n1; j++)
+      srcs[j] = create_source(SRC_REFCLOCK, &addrs[j], random() % 2, sel_options);
+    for (; j < n1 + n2; j++)
+      srcs[j] = create_source(SRC_NTP, &addrs[j], 1, sel_options);
+    for (; j < n1 + n2 + n3; j++)
+      srcs[j] = create_source(SRC_NTP, &addrs[j], 0, sel_options);
+    for (; j < n1 + n2 + n3 + n4; j++)
+      srcs[j] = create_source(random() % 2 ? SRC_REFCLOCK : SRC_NTP, &addrs[j],
+                              random() % 2, sel_options | SRC_SELECT_NOSELECT);
+
+    switch (sel_mode) {
+      case SRC_AUTHSELECT_IGNORE:
+        for (j = 0; j < n1 + n2 + n3; j++)
+          TEST_CHECK(srcs[j]->sel_options == sel_options);
+        break;
+      case SRC_AUTHSELECT_MIX:
+        for (j = 0; j < n1 + n2; j++)
+          TEST_CHECK(srcs[j]->sel_options ==
+                     (sel_options | (n2 > 0 && n3 > 0 ? SRC_SELECT_REQUIRE | SRC_SELECT_TRUST : 0)));
+        for (; j < n1 + n2 + n3; j++)
+          TEST_CHECK(srcs[j]->sel_options == sel_options);
+        break;
+      case SRC_AUTHSELECT_PREFER:
+        for (j = 0; j < n1 + n2; j++)
+          TEST_CHECK(srcs[j]->sel_options == sel_options);
+        for (; j < n1 + n2 + n3; j++)
+          TEST_CHECK(srcs[j]->sel_options == (sel_options | (n2 > 0 ? SRC_SELECT_NOSELECT : 0)));
+        break;
+      case SRC_AUTHSELECT_REQUIRE:
+        for (j = 0; j < n1 + n2; j++)
+          TEST_CHECK(srcs[j]->sel_options == sel_options);
+        for (; j < n1 + n2 + n3; j++)
+          TEST_CHECK(srcs[j]->sel_options == (sel_options | SRC_SELECT_NOSELECT));
+        break;
+      default:
+        assert(0);
+    }
+
+    for (j = n1 + n2 + n3; j < n1 + n2 + n3 + n4; j++)
+      TEST_CHECK(srcs[j]->sel_options == (sel_options | SRC_SELECT_NOSELECT));
+
+    for (j = n1 + n2 + n3 + n4 - 1; j >= 0; j--) {
+      if (j < n1 + n2)
+        TEST_CHECK(srcs[j]->sel_options == sel_options);
+      SRC_DestroyInstance(srcs[j]);
+    }
+  }
+
+  NSR_Finalise();
+  REF_Finalise();
+  SRC_Finalise();
+  SCH_Finalise();
+  LCL_Finalise();
+  CNF_Finalise();
+  HSH_Finalise();
+}
diff --git a/chrony_3_3/test/unit/test.c b/chrony/test/unit/test.c
similarity index 92%
rename from chrony_3_3/test/unit/test.c
rename to chrony/test/unit/test.c
index 67f7678..94619c1 100644
--- a/chrony_3_3/test/unit/test.c
+++ b/chrony/test/unit/test.c
@@ -22,6 +22,7 @@
 #include <sysincl.h>
 #include <logging.h>
 #include <localp.h>
+#include <util.h>
 
 #include "test.h"
 
@@ -32,9 +33,17 @@
   exit(1);
 }
 
+void
+TST_Skip(int line)
+{
+  printf("SKIP (on line %d)\n", line);
+  exit(0);
+}
+
 int
 main(int argc, char **argv)
 {
+  LOG_Severity log_severity;
   char *test_name, *s;
   int i, seed = 0;
   struct timeval tv;
@@ -47,9 +56,11 @@
   if (s)
     test_name = s + 1;
 
+  log_severity = LOGS_FATAL;
+
   for (i = 1; i < argc; i++) {
     if (!strcmp(argv[i], "-d")) {
-      LOG_SetDebugLevel(2);
+      log_severity = LOGS_DEBUG;
     } else if (!strcmp(argv[i], "-s") && i + 1 < argc) {
       seed = atoi(argv[++i]);
     } else {
@@ -65,6 +76,7 @@
   fflush(stdout);
 
   LOG_Initialise();
+  LOG_SetMinSeverity(log_severity);
 
   test_unit();
 
@@ -78,7 +90,7 @@
 double
 TST_GetRandomDouble(double min, double max)
 {
-  return min + (double)random() / RAND_MAX * (max - min);
+  return min + random() / 2147483647.0 * (max - min);
 }
 
 void
diff --git a/chrony_3_3/test/unit/test.h b/chrony/test/unit/test.h
similarity index 89%
rename from chrony_3_3/test/unit/test.h
rename to chrony/test/unit/test.h
index d96f3af..00d261d 100644
--- a/chrony_3_3/test/unit/test.h
+++ b/chrony/test/unit/test.h
@@ -33,7 +33,16 @@
     } \
   } while (0)
 
+#define TEST_REQUIRE(expr) \
+  do { \
+    if (!(expr)) { \
+      TST_Skip(__LINE__); \
+      exit(0); \
+    } \
+  } while (0)
+
 extern void TST_Fail(int line);
+extern void TST_Skip(int line);
 
 extern double TST_GetRandomDouble(double min, double max);
 extern void TST_GetRandomAddress(IPAddr *ip, int family, int bits);
diff --git a/chrony/test/unit/util.c b/chrony/test/unit/util.c
new file mode 100644
index 0000000..112676d
--- /dev/null
+++ b/chrony/test/unit/util.c
@@ -0,0 +1,744 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar  2017-2018, 2021
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+ */
+
+#include <util.c>
+#include "test.h"
+
+static volatile int handled_signal = 0;
+
+static void
+handle_signal(int signal)
+{
+  handled_signal = signal;
+}
+
+void
+test_unit(void)
+{
+  struct timespec ts, ts2, ts3, ts4;
+  char buf[16], *s, *s2, *words[3];
+  NTP_int64 ntp_ts, ntp_ts2, ntp_fuzz;
+  NTP_int32 ntp32_ts;
+  struct timeval tv;
+  double x, y, nan, inf;
+  IPAddr ip, ip2, ip3;
+  IPSockAddr ip_saddr;
+  Timespec tspec;
+  Float f;
+  int i, j, c;
+  uid_t uid;
+  gid_t gid;
+  struct stat st;
+  FILE *file;
+
+  for (i = -31; i < 31; i++) {
+    x = pow(2.0, i);
+    y = UTI_Log2ToDouble(i);
+    TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
+  }
+
+  for (i = -89; i < 63; i++) {
+    x = pow(2.0, i);
+    y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
+    TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
+  }
+
+  for (i = 0; i < 100000; i++) {
+    x = TST_GetRandomDouble(-1000.0, 1000.0);
+    y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
+    TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
+
+    UTI_GetRandomBytes(&f, sizeof (f));
+    x = UTI_FloatNetworkToHost(f);
+    TEST_CHECK(x > 0.0 || x <= 0.0);
+  }
+
+  TEST_CHECK(UTI_DoubleToNtp32(1.0) == htonl(65536));
+  TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0));
+  TEST_CHECK(UTI_DoubleToNtp32(1.0 / (65536.0)) == htonl(1));
+  TEST_CHECK(UTI_DoubleToNtp32(1.000001 / (65536.0)) == htonl(2));
+  TEST_CHECK(UTI_DoubleToNtp32(1.000001) == htonl(65537));
+  TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff));
+  TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0));
+
+  UTI_DoubleToTimeval(0.4e-6, &tv);
+  TEST_CHECK(tv.tv_sec == 0);
+  TEST_CHECK(tv.tv_usec == 0);
+  UTI_DoubleToTimeval(-0.4e-6, &tv);
+  TEST_CHECK(tv.tv_sec == 0);
+  TEST_CHECK(tv.tv_usec == 0);
+  UTI_DoubleToTimeval(0.5e-6, &tv);
+  TEST_CHECK(tv.tv_sec == 0);
+  TEST_CHECK(tv.tv_usec == 1);
+  UTI_DoubleToTimeval(-0.5e-6, &tv);
+  TEST_CHECK(tv.tv_sec == -1);
+  TEST_CHECK(tv.tv_usec == 999999);
+
+  UTI_DoubleToTimespec(0.9e-9, &ts);
+  TEST_CHECK(ts.tv_sec == 0);
+  TEST_CHECK(ts.tv_nsec == 0);
+  UTI_DoubleToTimespec(1.0e-9, &ts);
+  TEST_CHECK(ts.tv_sec == 0);
+  TEST_CHECK(ts.tv_nsec == 1);
+  UTI_DoubleToTimespec(-0.9e-9, &ts);
+  TEST_CHECK(ts.tv_sec == 0);
+  TEST_CHECK(ts.tv_nsec == 0);
+  UTI_DoubleToTimespec(-1.0e-9, &ts);
+  TEST_CHECK(ts.tv_sec == -1);
+  TEST_CHECK(ts.tv_nsec == 999999999);
+
+  ntp_ts.hi = htonl(JAN_1970);
+  ntp_ts.lo = 0xffffffff;
+  UTI_Ntp64ToTimespec(&ntp_ts, &ts);
+#if defined(HAVE_LONG_TIME_T) && NTP_ERA_SPLIT > 0
+  TEST_CHECK(ts.tv_sec == 0x100000000LL * (1 + (NTP_ERA_SPLIT - 1) / 0x100000000LL));
+#else
+  TEST_CHECK(ts.tv_sec == 0);
+#endif
+  TEST_CHECK(ts.tv_nsec == 999999999);
+
+  ntp_ts.hi = htonl(JAN_1970 - 1);
+  ntp_ts.lo = htonl(0xffffffff);
+  ntp_ts2.hi = htonl(JAN_1970 + 1);
+  ntp_ts2.lo = htonl(0x80000000);
+  TEST_CHECK(fabs(UTI_DiffNtp64ToDouble(&ntp_ts, &ntp_ts2) + 1.5) < 1e-9);
+  TEST_CHECK(fabs(UTI_DiffNtp64ToDouble(&ntp_ts2, &ntp_ts) - 1.5) < 1e-9);
+
+  UTI_AddDoubleToTimespec(&ts, 1e-9, &ts);
+#if defined(HAVE_LONG_TIME_T) && NTP_ERA_SPLIT > 0
+  TEST_CHECK(ts.tv_sec == 1 + 0x100000000LL * (1 + (NTP_ERA_SPLIT - 1) / 0x100000000LL));
+#else
+  TEST_CHECK(ts.tv_sec == 1);
+#endif
+  TEST_CHECK(ts.tv_nsec == 0);
+
+  ntp_fuzz.hi = 0;
+  ntp_fuzz.lo = htonl(0xff1234ff);
+
+  UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
+  TEST_CHECK(ntp_ts.hi == htonl(JAN_1970 + 1));
+  TEST_CHECK(ntp_ts.lo == ntp_fuzz.lo);
+
+  ts.tv_sec = ts.tv_nsec = 1;
+  UTI_ZeroTimespec(&ts);
+  TEST_CHECK(ts.tv_sec == 0);
+  TEST_CHECK(ts.tv_nsec == 0);
+  TEST_CHECK(UTI_IsZeroTimespec(&ts));
+
+  ntp_ts.hi = ntp_ts.lo == 1;
+  UTI_ZeroNtp64(&ntp_ts);
+  TEST_CHECK(ntp_ts.hi == 0);
+  TEST_CHECK(ntp_ts.lo == 0);
+
+  tv.tv_sec = tv.tv_usec = 1;
+  UTI_TimevalToTimespec(&tv, &ts);
+  TEST_CHECK(ts.tv_sec == 1);
+  TEST_CHECK(ts.tv_nsec == 1000);
+
+  UTI_TimespecToTimeval(&ts, &tv);
+  TEST_CHECK(tv.tv_sec == 1);
+  TEST_CHECK(tv.tv_usec == 1);
+
+  ts.tv_sec = 1;
+  ts.tv_nsec = 500000000;
+  TEST_CHECK(fabs(UTI_TimespecToDouble(&ts) - 1.5) < 1.0e-15);
+
+  UTI_DoubleToTimespec(2.75, &ts);
+  TEST_CHECK(ts.tv_sec == 2);
+  TEST_CHECK(ts.tv_nsec == 750000000);
+
+  ts.tv_sec = 1;
+  ts.tv_nsec = 1200000000;
+  UTI_NormaliseTimespec(&ts);
+  TEST_CHECK(ts.tv_sec == 2);
+  TEST_CHECK(ts.tv_nsec == 200000000);
+
+  ts.tv_sec = 1;
+  ts.tv_nsec = -200000000;
+  UTI_NormaliseTimespec(&ts);
+  TEST_CHECK(ts.tv_sec == 0);
+  TEST_CHECK(ts.tv_nsec == 800000000);
+
+  tv.tv_sec = 1;
+  tv.tv_usec = 500000;
+  TEST_CHECK(fabs(UTI_TimevalToDouble(&tv) - 1.5) < 1.0e-15);
+
+  UTI_DoubleToTimeval(2.75, &tv);
+  TEST_CHECK(tv.tv_sec == 2);
+  TEST_CHECK(tv.tv_usec == 750000);
+
+  tv.tv_sec = 1;
+  tv.tv_usec = 1200000;
+  UTI_NormaliseTimeval(&tv);
+  TEST_CHECK(tv.tv_sec == 2);
+  TEST_CHECK(tv.tv_usec == 200000);
+
+  tv.tv_sec = 1;
+  tv.tv_usec = -200000;
+  UTI_NormaliseTimeval(&tv);
+  TEST_CHECK(tv.tv_sec == 0);
+  TEST_CHECK(tv.tv_usec == 800000);
+
+  UTI_ZeroTimespec(&ts);
+  UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
+  TEST_CHECK(ntp_ts.hi == 0);
+  TEST_CHECK(ntp_ts.lo == 0);
+
+  TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
+
+  ts.tv_sec = 1;
+  ntp_ts.hi = htonl(1);
+
+  TEST_CHECK(!UTI_IsZeroTimespec(&ts));
+  TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
+
+  ts.tv_sec = 0;
+  ntp_ts.hi = 0;
+  ts.tv_nsec = 1;
+  ntp_ts.lo = htonl(1);
+
+  TEST_CHECK(!UTI_IsZeroTimespec(&ts));
+  TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
+
+  ntp_ts.hi = 0;
+  ntp_ts.lo = 0;
+
+  UTI_Ntp64ToTimespec(&ntp_ts, &ts);
+  TEST_CHECK(UTI_IsZeroTimespec(&ts));
+  UTI_TimespecToNtp64(&ts, &ntp_ts, NULL);
+  TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
+
+  ntp_fuzz.hi = htonl(1);
+  ntp_fuzz.lo = htonl(3);
+  ntp_ts.hi = htonl(1);
+  ntp_ts.lo = htonl(2);
+
+  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
+  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
+  TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
+
+  ntp_ts.hi = htonl(0x80000002);
+  ntp_ts.lo = htonl(2);
+
+  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
+  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
+  TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
+
+  ntp_fuzz.hi = htonl(0x90000001);
+
+  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
+  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
+  TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
+
+  TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_ts, NULL, NULL));
+  TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, &ntp_ts, NULL));
+  TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, NULL, &ntp_ts));
+  TEST_CHECK(!UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_fuzz, &ntp_fuzz, &ntp_fuzz));
+
+  ts.tv_sec = 1;
+  ts.tv_nsec = 2;
+  ts2.tv_sec = 1;
+  ts2.tv_nsec = 3;
+
+  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
+  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
+  TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
+
+  ts2.tv_sec = 2;
+
+  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
+  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
+  TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
+
+  ts.tv_sec = 2;
+  ts.tv_nsec = 250000000;
+  ts2.tv_sec = 1;
+  ts2.tv_nsec = 750000000;
+  UTI_DiffTimespecs(&ts3, &ts, &ts2);
+  TEST_CHECK(ts3.tv_sec == 0);
+  TEST_CHECK(ts3.tv_nsec == 500000000);
+  TEST_CHECK(fabs(UTI_DiffTimespecsToDouble(&ts, &ts2) - 0.5) < 1.0e-15);
+
+  ts.tv_sec = 2;
+  ts.tv_nsec = 250000000;
+  ts2.tv_sec = 3;
+  ts2.tv_nsec = 750000000;
+  UTI_DiffTimespecs(&ts3, &ts, &ts2);
+  TEST_CHECK(ts3.tv_sec == -2);
+  TEST_CHECK(ts3.tv_nsec == 500000000);
+  TEST_CHECK(fabs(UTI_DiffTimespecsToDouble(&ts, &ts2) - -1.5) < 1.0e-15);
+
+  ts.tv_sec = 2;
+  ts.tv_nsec = 250000000;
+  UTI_AddDoubleToTimespec(&ts, 2.5, &ts2);
+  TEST_CHECK(ts2.tv_sec == 4);
+  TEST_CHECK(ts2.tv_nsec == 750000000);
+
+  ts.tv_sec = 4;
+  ts.tv_nsec = 500000000;
+  ts2.tv_sec = 1;
+  ts2.tv_nsec = 750000000;
+  UTI_AverageDiffTimespecs(&ts, &ts2, &ts3, &x);
+  TEST_CHECK(ts3.tv_sec == 3);
+  TEST_CHECK(ts3.tv_nsec == 125000000);
+  TEST_CHECK(x == -2.75);
+
+  ts.tv_sec = 4;
+  ts.tv_nsec = 500000000;
+  ts2.tv_sec = 1;
+  ts2.tv_nsec = 750000000;
+  ts3.tv_sec = 5;
+  ts3.tv_nsec = 250000000;
+  UTI_AddDiffToTimespec(&ts, &ts2, &ts3, &ts4);
+  TEST_CHECK(ts4.tv_sec == 8);
+  TEST_CHECK(ts4.tv_nsec == 0);
+
+  ts.tv_sec = 1600000000;
+  ts.tv_nsec = 123;
+  s = UTI_TimespecToString(&ts);
+  TEST_CHECK(strcmp(s, "1600000000.000000123") == 0);
+
+  ntp_ts.hi = 1;
+  ntp_ts.hi = 2;
+  UTI_Ntp64ToTimespec(&ntp_ts, &ts);
+  s = UTI_Ntp64ToString(&ntp_ts);
+  s2 = UTI_TimespecToString(&ts);
+  TEST_CHECK(strcmp(s, s2) == 0);
+
+  s = UTI_RefidToString(0x41424344);
+  TEST_CHECK(strcmp(s, "ABCD") == 0);
+
+  ip.family = IPADDR_UNSPEC;
+  s = UTI_IPToString(&ip);
+  TEST_CHECK(strcmp(s, "[UNSPEC]") == 0);
+  TEST_CHECK(UTI_IPToRefid(&ip) == 0);
+  TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip));
+
+  ip.family = IPADDR_INET4;
+  ip.addr.in4 = 0x7f010203;
+  s = UTI_IPToString(&ip);
+  TEST_CHECK(strcmp(s, "127.1.2.3") == 0);
+  TEST_CHECK(UTI_IPToRefid(&ip) == 0x7f010203);
+  TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip));
+
+  ip.family = IPADDR_INET6;
+  memset(&ip.addr.in6, 0, sizeof (ip.addr.in6));
+  ip.addr.in6[0] = 0xab;
+  ip.addr.in6[15] = 0xcd;
+  s = UTI_IPToString(&ip);
+#ifdef FEAT_IPV6
+  TEST_CHECK(strcmp(s, "ab00::cd") == 0);
+#else
+  TEST_CHECK(strcmp(s, "ab00:0000:0000:0000:0000:0000:0000:00cd") == 0);
+#endif
+  TEST_CHECK(UTI_IPToRefid(&ip) == 0x5f9aa602);
+  TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip));
+
+  ip.family = IPADDR_ID;
+  ip.addr.id = 12345;
+  s = UTI_IPToString(&ip);
+  TEST_CHECK(strcmp(s, "ID#0000012345") == 0);
+  TEST_CHECK(UTI_IPToRefid(&ip) == 0);
+  TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip));
+
+  ip.family = IPADDR_UNSPEC + 10;
+  s = UTI_IPToString(&ip);
+  TEST_CHECK(strcmp(s, "[UNKNOWN]") == 0);
+  TEST_CHECK(UTI_IPToRefid(&ip) == 0);
+  TEST_CHECK(UTI_IPToHash(&ip) == UTI_IPToHash(&ip));
+
+  TEST_CHECK(UTI_StringToIP("200.4.5.6", &ip));
+  TEST_CHECK(ip.family == IPADDR_INET4);
+  TEST_CHECK(ip.addr.in4 == 0xc8040506);
+
+#ifdef FEAT_IPV6
+  TEST_CHECK(UTI_StringToIP("1234::7890", &ip));
+  TEST_CHECK(ip.family == IPADDR_INET6);
+  TEST_CHECK(ip.addr.in6[0] == 0x12 && ip.addr.in6[1] == 0x34);
+  TEST_CHECK(ip.addr.in6[2] == 0x00 && ip.addr.in6[13] == 0x00);
+  TEST_CHECK(ip.addr.in6[14] == 0x78 && ip.addr.in6[15] == 0x90);
+#else
+  TEST_CHECK(!UTI_StringToIP("1234::7890", &ip));
+#endif
+
+  TEST_CHECK(!UTI_StringToIP("ID#0000012345", &ip));
+
+  TEST_CHECK(UTI_IsStringIP("1.2.3.4"));
+  TEST_CHECK(!UTI_IsStringIP("127.3.3"));
+  TEST_CHECK(!UTI_IsStringIP("127.3"));
+  TEST_CHECK(!UTI_IsStringIP("127"));
+#ifdef FEAT_IPV6
+  TEST_CHECK(UTI_IsStringIP("1234:5678::aaaa"));
+#else
+  TEST_CHECK(!UTI_IsStringIP("1234:5678::aaaa"));
+#endif
+  TEST_CHECK(!UTI_StringToIP("ID#0000012345", &ip));
+
+  TEST_CHECK(!UTI_StringToIdIP("1.2.3.4", &ip));
+  TEST_CHECK(UTI_StringToIdIP("ID#0000056789", &ip));
+  TEST_CHECK(ip.family == IPADDR_ID);
+  TEST_CHECK(ip.addr.id == 56789);
+
+  for (i = IPADDR_UNSPEC; i <= IPADDR_ID + 1; i++) {
+    ip.family = i;
+    TEST_CHECK(UTI_IsIPReal(&ip) == (i == IPADDR_INET4 || i == IPADDR_INET6));
+  }
+
+  ip.family = IPADDR_UNSPEC;
+  UTI_IPHostToNetwork(&ip, &ip2);
+  TEST_CHECK(ip2.family == htons(IPADDR_UNSPEC));
+  UTI_IPNetworkToHost(&ip2, &ip3);
+  TEST_CHECK(ip3.family == IPADDR_UNSPEC);
+
+  ip.family = IPADDR_INET4;
+  ip.addr.in4 = 0x12345678;
+  UTI_IPHostToNetwork(&ip, &ip2);
+  TEST_CHECK(ip2.family == htons(IPADDR_INET4));
+  TEST_CHECK(ip2.addr.in4 == htonl(0x12345678));
+  UTI_IPNetworkToHost(&ip2, &ip3);
+  TEST_CHECK(ip3.family == IPADDR_INET4);
+  TEST_CHECK(ip3.addr.in4 == 0x12345678);
+
+  ip.family = IPADDR_INET6;
+  for (i = 0; i < 16; i++)
+    ip.addr.in6[i] = i;
+  UTI_IPHostToNetwork(&ip, &ip2);
+  TEST_CHECK(ip2.family == htons(IPADDR_INET6));
+  for (i = 0; i < 16; i++)
+    TEST_CHECK(ip.addr.in6[i] == i);
+  UTI_IPNetworkToHost(&ip2, &ip3);
+  TEST_CHECK(ip3.family == IPADDR_INET6);
+  for (i = 0; i < 16; i++)
+    TEST_CHECK(ip.addr.in6[i] == i);
+
+  ip.family = IPADDR_ID;
+  ip.addr.in4 = 0x87654321;
+  UTI_IPHostToNetwork(&ip, &ip2);
+  TEST_CHECK(ip2.family == htons(IPADDR_ID));
+  TEST_CHECK(ip2.addr.in4 == htonl(0x87654321));
+  UTI_IPNetworkToHost(&ip2, &ip3);
+  TEST_CHECK(ip3.family == IPADDR_ID);
+  TEST_CHECK(ip3.addr.in4 == 0x87654321);
+
+  for (i = 0; i < 16; i++)
+    ip.addr.in6[i] = 0x80;
+  ip2 = ip;
+
+  for (i = IPADDR_UNSPEC; i <= IPADDR_ID; i++) {
+    ip.family = i;
+    ip2.family = i + 1;
+    TEST_CHECK(UTI_CompareIPs(&ip, &ip2, NULL) < 0);
+    TEST_CHECK(UTI_CompareIPs(&ip2, &ip, NULL) > 0);
+    ip2 = ip;
+    ip2.addr.in4++;
+    if (i == IPADDR_UNSPEC) {
+      TEST_CHECK(UTI_CompareIPs(&ip, &ip2, NULL) == 0);
+      TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip) == 0);
+    } else {
+      TEST_CHECK(UTI_CompareIPs(&ip, &ip2, NULL) < 0);
+      TEST_CHECK(UTI_CompareIPs(&ip2, &ip, NULL) > 0);
+      if (i == IPADDR_ID) {
+        TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip) < 0);
+        TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip2) < 0);
+      } else {
+        TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip) == 0);
+        TEST_CHECK(UTI_CompareIPs(&ip, &ip2, &ip2) < 0);
+      }
+    }
+  }
+
+  ip_saddr.ip_addr.family = IPADDR_INET4;
+  ip_saddr.ip_addr.addr.in4 = 0x01020304;
+  ip_saddr.port = 12345;
+  s = UTI_IPSockAddrToString(&ip_saddr);
+  TEST_CHECK(strcmp(s, "1.2.3.4:12345") == 0);
+
+  s = UTI_TimeToLogForm(2000000000);
+  TEST_CHECK(strcmp(s, "2033-05-18 03:33:20") == 0);
+
+  ts.tv_sec = 3;
+  ts.tv_nsec = 500000000;
+  ts2.tv_sec = 4;
+  ts2.tv_nsec = 250000000;
+  UTI_AdjustTimespec(&ts, &ts2, &ts3, &x, 2.0, -5.0);
+  TEST_CHECK(fabs(x - 6.5) < 1.0e-15);
+  TEST_CHECK((ts3.tv_sec == 10 && ts3.tv_nsec == 0) ||
+             (ts3.tv_sec == 9 && ts3.tv_nsec == 999999999));
+
+  for (i = -32; i <= 32; i++) {
+    for (j = c = 0; j < 1000; j++) {
+      UTI_GetNtp64Fuzz(&ntp_fuzz, i);
+      if (i <= 0)
+        TEST_CHECK(ntp_fuzz.hi == 0);
+      if (i < 0)
+        TEST_CHECK(ntohl(ntp_fuzz.lo) < 1U << (32 + i));
+      else if (i < 32)
+        TEST_CHECK(ntohl(ntp_fuzz.hi) < 1U << i);
+      if (ntohl(ntp_fuzz.lo) >= 1U << (31 + CLAMP(-31, i, 0)))
+        c++;
+    }
+
+    if (i == -32)
+      TEST_CHECK(c == 0);
+    else
+      TEST_CHECK(c > 400 && c < 600);
+  }
+
+  TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0));
+  TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0));
+  TEST_CHECK(UTI_DoubleToNtp32(1e-9) == htonl(1));
+  TEST_CHECK(UTI_DoubleToNtp32(32768.0) == htonl(0x80000000));
+  TEST_CHECK(UTI_DoubleToNtp32(65536.0) == htonl(0xffffffff));
+  TEST_CHECK(UTI_DoubleToNtp32(65537.0) == htonl(0xffffffff));
+
+  TEST_CHECK(UTI_DoubleToNtp32f28(-1.0) == htonl(0));
+  TEST_CHECK(UTI_DoubleToNtp32f28(0.0) == htonl(0));
+  TEST_CHECK(UTI_DoubleToNtp32f28(1e-9) == htonl(1));
+  TEST_CHECK(UTI_DoubleToNtp32f28(4e-9) == htonl(2));
+  TEST_CHECK(UTI_DoubleToNtp32f28(8.0) == htonl(0x80000000));
+  TEST_CHECK(UTI_DoubleToNtp32f28(16.0) == htonl(0xffffffff));
+  TEST_CHECK(UTI_DoubleToNtp32f28(16.1) == htonl(0xffffffff));
+  TEST_CHECK(UTI_DoubleToNtp32f28(16.1) == htonl(0xffffffff));
+
+  TEST_CHECK(UTI_Ntp32f28ToDouble(htonl(0xffffffff)) >= 65535.999);
+  for (i = 0; i < 100000; i++) {
+    UTI_GetRandomBytes(&ntp32_ts, sizeof (ntp32_ts));
+    TEST_CHECK(UTI_DoubleToNtp32(UTI_Ntp32ToDouble(ntp32_ts)) == ntp32_ts);
+    TEST_CHECK(UTI_DoubleToNtp32f28(UTI_Ntp32f28ToDouble(ntp32_ts)) == ntp32_ts);
+  }
+
+  ts.tv_nsec = 0;
+
+  ts.tv_sec = 10;
+  TEST_CHECK(!UTI_IsTimeOffsetSane(&ts, -20.0));
+
+#ifdef HAVE_LONG_TIME_T
+  ts.tv_sec = NTP_ERA_SPLIT + (1LL << 32);
+#else
+  ts.tv_sec = 0x7fffffff - MIN_ENDOFTIME_DISTANCE;
+#endif
+  TEST_CHECK(!UTI_IsTimeOffsetSane(&ts, 10.0));
+  TEST_CHECK(UTI_IsTimeOffsetSane(&ts, -20.0));
+
+  TEST_CHECK(UTI_Log2ToDouble(-1) == 0.5);
+  TEST_CHECK(UTI_Log2ToDouble(0) == 1.0);
+  TEST_CHECK(UTI_Log2ToDouble(1) == 2.0);
+  TEST_CHECK(UTI_Log2ToDouble(-31) < UTI_Log2ToDouble(-30));
+  TEST_CHECK(UTI_Log2ToDouble(-32) == UTI_Log2ToDouble(-31));
+  TEST_CHECK(UTI_Log2ToDouble(30) < UTI_Log2ToDouble(32));
+  TEST_CHECK(UTI_Log2ToDouble(31) == UTI_Log2ToDouble(32));
+
+  UTI_TimespecHostToNetwork(&ts, &tspec);
+#ifdef HAVE_LONG_TIME_T
+  TEST_CHECK(tspec.tv_sec_high == htonl(ts.tv_sec >> 32));
+#else
+  TEST_CHECK(tspec.tv_sec_high == htonl(TV_NOHIGHSEC));
+#endif
+  TEST_CHECK(tspec.tv_sec_low == htonl(ts.tv_sec));
+  TEST_CHECK(tspec.tv_nsec == htonl(ts.tv_nsec));
+  UTI_TimespecNetworkToHost(&tspec, &ts2);
+  TEST_CHECK(!UTI_CompareTimespecs(&ts, &ts2));
+
+  TEST_CHECK(UTI_CmacNameToAlgorithm("AES128") == CMC_AES128);
+  TEST_CHECK(UTI_CmacNameToAlgorithm("AES256") == CMC_AES256);
+  TEST_CHECK(UTI_CmacNameToAlgorithm("NOSUCHCMAC") == CMC_INVALID);
+
+  TEST_CHECK(UTI_HashNameToAlgorithm("MD5") == HSH_MD5);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA1") == HSH_SHA1);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA256") == HSH_SHA256);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA384") == HSH_SHA384);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA512") == HSH_SHA512);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-224") == HSH_SHA3_224);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-256") == HSH_SHA3_256);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-384") == HSH_SHA3_384);
+  TEST_CHECK(UTI_HashNameToAlgorithm("SHA3-512") == HSH_SHA3_512);
+  TEST_CHECK(UTI_HashNameToAlgorithm("TIGER") == HSH_TIGER);
+  TEST_CHECK(UTI_HashNameToAlgorithm("WHIRLPOOL") == HSH_WHIRLPOOL);
+  TEST_CHECK(UTI_HashNameToAlgorithm("NOSUCHHASH") == HSH_INVALID);
+
+  i = open("/dev/null", 0);
+  TEST_CHECK(UTI_FdSetCloexec(i));
+  j = fcntl(i, F_GETFD);
+  TEST_CHECK(j & F_GETFD);
+  close(i);
+
+  UTI_SetQuitSignalsHandler(handle_signal, 0);
+  TEST_CHECK(handled_signal == 0);
+  kill(getpid(), SIGPIPE);
+  while (handled_signal == 0)
+    ;
+  TEST_CHECK(handled_signal == SIGPIPE);
+
+  s = UTI_PathToDir("/aaa/bbb/ccc/ddd");
+  TEST_CHECK(!strcmp(s, "/aaa/bbb/ccc"));
+  Free(s);
+  s = UTI_PathToDir("aaa");
+  TEST_CHECK(!strcmp(s, "."));
+  Free(s);
+  s = UTI_PathToDir("/aaaa");
+  TEST_CHECK(!strcmp(s, "/"));
+  Free(s);
+
+  nan = strtod("nan", NULL);
+  inf = strtod("inf", NULL);
+
+  TEST_CHECK(MIN(2.0, -1.0) == -1.0);
+  TEST_CHECK(MIN(-1.0, 2.0) == -1.0);
+  TEST_CHECK(MIN(inf, 2.0) == 2.0);
+
+  TEST_CHECK(MAX(2.0, -1.0) == 2.0);
+  TEST_CHECK(MAX(-1.0, 2.0) == 2.0);
+  TEST_CHECK(MAX(inf, 2.0) == inf);
+
+  TEST_CHECK(CLAMP(1.0, -1.0, 2.0) == 1.0);
+  TEST_CHECK(CLAMP(1.0, 3.0, 2.0) == 2.0);
+  TEST_CHECK(CLAMP(1.0, inf, 2.0) == 2.0);
+  TEST_CHECK(CLAMP(1.0, nan, 2.0) == 2.0);
+
+  TEST_CHECK(SQUARE(3.0) == 3.0 * 3.0);
+
+  rmdir("testdir");
+
+  uid = geteuid();
+  gid = getegid();
+
+  TEST_CHECK(UTI_CreateDirAndParents("testdir", 0700, uid, gid));
+
+  TEST_CHECK(UTI_CheckDirPermissions("testdir", 0700, uid, gid));
+  TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0300, uid, gid));
+  TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid + 1, gid));
+  TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid, gid + 1));
+
+  umask(0);
+
+  unlink("testfile");
+  file = UTI_OpenFile(NULL, "testfile", NULL, 'r', 0);
+  TEST_CHECK(!file);
+  TEST_CHECK(stat("testfile", &st) < 0);
+
+  file = UTI_OpenFile(NULL, "testfile", NULL, 'w', 0644);
+  TEST_CHECK(file);
+  TEST_CHECK(stat("testfile", &st) == 0);
+  TEST_CHECK((st.st_mode & 0777) == 0644);
+  fclose(file);
+
+  file = UTI_OpenFile(".", "test", "file", 'W', 0640);
+  TEST_CHECK(file);
+  TEST_CHECK(stat("testfile", &st) == 0);
+  TEST_CHECK((st.st_mode & 0777) == 0640);
+  fclose(file);
+
+  file = UTI_OpenFile(NULL, "test", "file", 'r', 0);
+  TEST_CHECK(file);
+  fclose(file);
+
+  TEST_CHECK(UTI_RenameTempFile(NULL, "testfil", "e", NULL));
+  TEST_CHECK(stat("testfil", &st) == 0);
+  file = UTI_OpenFile(NULL, "testfil", NULL, 'R', 0);
+  TEST_CHECK(file);
+  fclose(file);
+
+  TEST_CHECK(UTI_RenameTempFile(NULL, "test", "fil", "file"));
+  TEST_CHECK(stat("testfile", &st) == 0);
+  file = UTI_OpenFile(NULL, "testfile", NULL, 'R', 0);
+  TEST_CHECK(file);
+  fclose(file);
+
+  TEST_CHECK(UTI_RemoveFile(NULL, "testfile", NULL));
+  TEST_CHECK(stat("testfile", &st) < 0);
+  TEST_CHECK(!UTI_RemoveFile(NULL, "testfile", NULL));
+
+  for (i = c = 0; i < 100000; i++) {
+    j = random() % (sizeof (buf) + 1);
+    UTI_GetRandomBytesUrandom(buf, j);
+    if (j && buf[j - 1] % 2)
+      c++;
+    if (random() % 10000 == 0) {
+      UTI_ResetGetRandomFunctions();
+      TEST_CHECK(!urandom_file);
+    }
+  }
+  TEST_CHECK(c > 46000 && c < 48000);
+
+  for (i = c = 0; i < 100000; i++) {
+    j = random() % (sizeof (buf) + 1);
+    UTI_GetRandomBytes(buf, j);
+    if (j && buf[j - 1] % 2)
+      c++;
+    if (random() % 10000 == 0) {
+      UTI_ResetGetRandomFunctions();
+#if HAVE_GETRANDOM
+      TEST_CHECK(getrandom_buf_available == 0);
+#endif
+    }
+  }
+  TEST_CHECK(c > 46000 && c < 48000);
+
+  assert(sizeof (buf) >= 16);
+  TEST_CHECK(UTI_HexToBytes("", buf, sizeof (buf)) == 0);
+  TEST_CHECK(UTI_HexToBytes("0", buf, sizeof (buf)) == 0);
+  TEST_CHECK(UTI_HexToBytes("00123456789ABCDEF", buf, sizeof (buf)) == 0);
+  TEST_CHECK(UTI_HexToBytes("00123456789ABCDEF0", buf, 8) == 0);
+  TEST_CHECK(UTI_HexToBytes("00123456789ABCDEF0", buf, sizeof (buf)) == 9);
+  TEST_CHECK(memcmp(buf, "\x00\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 9) == 0);
+  memcpy(buf, "AB123456780001", 15);
+  TEST_CHECK(UTI_HexToBytes(buf, buf, sizeof (buf)) == 7);
+  TEST_CHECK(memcmp(buf, "\xAB\x12\x34\x56\x78\x00\x01", 7) == 0);
+
+  TEST_CHECK(UTI_BytesToHex("", 0, buf, 0) == 0);
+  TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 14) == 0);
+  TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 7, buf, 15) == 1);
+  TEST_CHECK(strcmp(buf, "AB123456780001") == 0);
+  TEST_CHECK(UTI_BytesToHex("\xAB\x12\x34\x56\x78\x00\x01", 0, buf, 15) == 1);
+  TEST_CHECK(strcmp(buf, "") == 0);
+
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 3) == 0);
+  TEST_CHECK(!words[0]);
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "     ") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 3) == 0);
+  TEST_CHECK(!words[0]);
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a  \n ") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 3) == 1);
+  TEST_CHECK(words[0] == buf + 0);
+  TEST_CHECK(strcmp(words[0], "a") == 0);
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "  a  ") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 3) == 1);
+  TEST_CHECK(words[0] == buf + 2);
+  TEST_CHECK(strcmp(words[0], "a") == 0);
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " \n  a") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 3) == 1);
+  TEST_CHECK(words[0] == buf + 4);
+  TEST_CHECK(strcmp(words[0], "a") == 0);
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a   b") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 1) == 2);
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", "a   b") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 2) == 2);
+  TEST_CHECK(words[0] == buf + 0);
+  TEST_CHECK(words[1] == buf + 4);
+  TEST_CHECK(strcmp(words[0], "a") == 0);
+  TEST_CHECK(strcmp(words[1], "b") == 0);
+  TEST_CHECK(snprintf(buf, sizeof (buf), "%s", " a b ") < sizeof (buf));
+  TEST_CHECK(UTI_SplitString(buf, words, 3) == 2);
+  TEST_CHECK(words[0] == buf + 1);
+  TEST_CHECK(words[1] == buf + 3);
+  TEST_CHECK(strcmp(words[0], "a") == 0);
+  TEST_CHECK(strcmp(words[1], "b") == 0);
+}
diff --git a/chrony_3_3/util.c b/chrony/util.c
similarity index 65%
rename from chrony_3_3/util.c
rename to chrony/util.c
index 4b3e455..c4c8934 100644
--- a/chrony_3_3/util.c
+++ b/chrony/util.c
@@ -3,7 +3,7 @@
 
  **********************************************************************
  * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2009, 2012-2017
+ * Copyright (C) Miroslav Lichvar  2009, 2012-2021
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -56,7 +56,7 @@
 /* ================================================== */
 
 void
-UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts)
+UTI_TimevalToTimespec(const struct timeval *tv, struct timespec *ts)
 {
   ts->tv_sec = tv->tv_sec;
   ts->tv_nsec = 1000 * tv->tv_usec;
@@ -65,7 +65,7 @@
 /* ================================================== */
 
 void
-UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv)
+UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv)
 {
   tv->tv_sec = ts->tv_sec;
   tv->tv_usec = ts->tv_nsec / 1000;
@@ -74,7 +74,7 @@
 /* ================================================== */
 
 double
-UTI_TimespecToDouble(struct timespec *ts)
+UTI_TimespecToDouble(const struct timespec *ts)
 {
   return ts->tv_sec + 1.0e-9 * ts->tv_nsec;
 }
@@ -109,7 +109,7 @@
 /* ================================================== */
 
 double
-UTI_TimevalToDouble(struct timeval *tv)
+UTI_TimevalToDouble(const struct timeval *tv)
 {
   return tv->tv_sec + 1.0e-6 * tv->tv_usec;
 }
@@ -123,7 +123,7 @@
 
   b->tv_sec = a;
   frac_part = 1.0e6 * (a - b->tv_sec);
-  b->tv_usec = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
+  b->tv_usec = round(frac_part);
   UTI_NormaliseTimeval(b);
 }
 
@@ -149,7 +149,7 @@
 /* ================================================== */
 
 int
-UTI_CompareTimespecs(struct timespec *a, struct timespec *b)
+UTI_CompareTimespecs(const struct timespec *a, const struct timespec *b)
 {
   if (a->tv_sec < b->tv_sec)
     return -1;
@@ -165,7 +165,7 @@
 /* ================================================== */
 
 void
-UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b)
+UTI_DiffTimespecs(struct timespec *result, const struct timespec *a, const struct timespec *b)
 {
   result->tv_sec = a->tv_sec - b->tv_sec;
   result->tv_nsec = a->tv_nsec - b->tv_nsec;
@@ -176,7 +176,7 @@
 
 /* Calculate result = a - b and return as a double */
 double
-UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b)
+UTI_DiffTimespecsToDouble(const struct timespec *a, const struct timespec *b)
 {
   return ((double)a->tv_sec - (double)b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
 }
@@ -184,7 +184,7 @@
 /* ================================================== */
 
 void
-UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end)
+UTI_AddDoubleToTimespec(const struct timespec *start, double increment, struct timespec *end)
 {
   time_t int_part;
 
@@ -198,7 +198,7 @@
 
 /* Calculate the average and difference (as a double) of two timespecs */
 void
-UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later,
+UTI_AverageDiffTimespecs(const struct timespec *earlier, const struct timespec *later,
                          struct timespec *average, double *diff)
 {
   *diff = UTI_DiffTimespecsToDouble(later, earlier);
@@ -208,8 +208,8 @@
 /* ================================================== */
 
 void
-UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b,
-                      struct timespec *c, struct timespec *result)
+UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b,
+                      const struct timespec *c, struct timespec *result)
 {
   double diff;
 
@@ -230,7 +230,7 @@
 /* Convert a timespec into a temporary string, largely for diagnostic display */
 
 char *
-UTI_TimespecToString(struct timespec *ts)
+UTI_TimespecToString(const struct timespec *ts)
 {
   char *result;
 
@@ -250,7 +250,7 @@
    for diagnostic display */
 
 char *
-UTI_Ntp64ToString(NTP_int64 *ntp_ts)
+UTI_Ntp64ToString(const NTP_int64 *ntp_ts)
 {
   struct timespec ts;
   UTI_Ntp64ToTimespec(ntp_ts, &ts);
@@ -281,10 +281,10 @@
 /* ================================================== */
 
 char *
-UTI_IPToString(IPAddr *addr)
+UTI_IPToString(const IPAddr *addr)
 {
   unsigned long a, b, c, d, ip;
-  uint8_t *ip6;
+  const uint8_t *ip6;
   char *result;
 
   result = NEXT_BUFFER;
@@ -298,18 +298,22 @@
       b = (ip>>16) & 0xff;
       c = (ip>> 8) & 0xff;
       d = (ip>> 0) & 0xff;
-      snprintf(result, BUFFER_LENGTH, "%ld.%ld.%ld.%ld", a, b, c, d);
+      snprintf(result, BUFFER_LENGTH, "%lu.%lu.%lu.%lu", a, b, c, d);
       break;
     case IPADDR_INET6:
       ip6 = addr->addr.in6;
 #ifdef FEAT_IPV6
       inet_ntop(AF_INET6, ip6, result, BUFFER_LENGTH);
 #else
-      snprintf(result, BUFFER_LENGTH, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
-               ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7],
-               ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15]);
+      assert(BUFFER_LENGTH >= 40);
+      for (a = 0; a < 8; a++)
+        snprintf(result + a * 5, 40 - a * 5, "%04x:",
+                 (unsigned int)(ip6[2 * a] << 8 | ip6[2 * a + 1]));
 #endif
       break;
+    case IPADDR_ID:
+      snprintf(result, BUFFER_LENGTH, "ID#%010"PRIu32, addr->addr.id);
+      break;
     default:
       snprintf(result, BUFFER_LENGTH, "[UNKNOWN]");
   }
@@ -321,9 +325,10 @@
 int
 UTI_StringToIP(const char *addr, IPAddr *ip)
 {
-#ifdef FEAT_IPV6
   struct in_addr in4;
+#ifdef FEAT_IPV6
   struct in6_addr in6;
+#endif
 
   if (inet_pton(AF_INET, addr, &in4) > 0) {
     ip->family = IPADDR_INET4;
@@ -332,23 +337,13 @@
     return 1;
   }
 
+#ifdef FEAT_IPV6
   if (inet_pton(AF_INET6, addr, &in6) > 0) {
     ip->family = IPADDR_INET6;
     ip->_pad = 0;
     memcpy(ip->addr.in6, in6.s6_addr, sizeof (ip->addr.in6));
     return 1;
   }
-#else
-  unsigned long a, b, c, d, n;
-
-  n = sscanf(addr, "%lu.%lu.%lu.%lu", &a, &b, &c, &d);
-  if (n == 4) {
-    ip->family = IPADDR_INET4;
-    ip->_pad = 0;
-    ip->addr.in4 = ((a & 0xff) << 24) | ((b & 0xff) << 16) | 
-                   ((c & 0xff) << 8) | (d & 0xff);
-    return 1;
-  }
 #endif
 
   return 0;
@@ -356,8 +351,46 @@
 
 /* ================================================== */
 
+int
+UTI_IsStringIP(const char *string)
+{
+  IPAddr ip;
+
+  return UTI_StringToIP(string, &ip);
+}
+
+/* ================================================== */
+
+int
+UTI_StringToIdIP(const char *addr, IPAddr *ip)
+{
+  if (sscanf(addr, "ID#%"SCNu32, &ip->addr.id) == 1) {
+    ip->family = IPADDR_ID;
+    ip->_pad = 0;
+    return 1;
+  }
+
+  return 0;
+}
+
+/* ================================================== */
+
+int
+UTI_IsIPReal(const IPAddr *ip)
+{
+  switch (ip->family) {
+    case IPADDR_INET4:
+    case IPADDR_INET6:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+/* ================================================== */
+
 uint32_t
-UTI_IPToRefid(IPAddr *ip)
+UTI_IPToRefid(const IPAddr *ip)
 {
   static int MD5_hash = -1;
   unsigned char buf[16];
@@ -367,7 +400,7 @@
       return ip->addr.in4;
     case IPADDR_INET6:
       if (MD5_hash < 0)
-        MD5_hash = HSH_GetHashId("MD5");
+        MD5_hash = HSH_GetHashId(HSH_MD5_NONCRYPTO);
 
       if (MD5_hash < 0 ||
           HSH_Hash(MD5_hash, (const unsigned char *)ip->addr.in6, sizeof (ip->addr.in6),
@@ -382,10 +415,10 @@
 /* ================================================== */
 
 uint32_t
-UTI_IPToHash(IPAddr *ip)
+UTI_IPToHash(const IPAddr *ip)
 {
   static uint32_t seed = 0;
-  unsigned char *addr;
+  const unsigned char *addr;
   unsigned int i, len;
   uint32_t hash;
 
@@ -398,6 +431,10 @@
       addr = ip->addr.in6;
       len = sizeof (ip->addr.in6);
       break;
+    case IPADDR_ID:
+      addr = (unsigned char *)&ip->addr.id;
+      len = sizeof (ip->addr.id);
+      break;
     default:
       return 0;
   }
@@ -416,7 +453,7 @@
 /* ================================================== */
 
 void
-UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest)
+UTI_IPHostToNetwork(const IPAddr *src, IPAddr *dest)
 {
   /* Don't send uninitialized bytes over network */
   memset(dest, 0, sizeof (IPAddr));
@@ -430,6 +467,9 @@
     case IPADDR_INET6:
       memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
       break;
+    case IPADDR_ID:
+      dest->addr.id = htonl(src->addr.id);
+      break;
     default:
       dest->family = htons(IPADDR_UNSPEC);
   }
@@ -438,7 +478,7 @@
 /* ================================================== */
 
 void
-UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest)
+UTI_IPNetworkToHost(const IPAddr *src, IPAddr *dest)
 {
   dest->family = ntohs(src->family);
   dest->_pad = 0;
@@ -450,6 +490,9 @@
     case IPADDR_INET6:
       memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
       break;
+    case IPADDR_ID:
+      dest->addr.id = ntohl(src->addr.id);
+      break;
     default:
       dest->family = IPADDR_UNSPEC;
   }
@@ -458,7 +501,7 @@
 /* ================================================== */
 
 int
-UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask)
+UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask)
 {
   int i, d;
 
@@ -485,124 +528,29 @@
           d = a->addr.in6[i] - b->addr.in6[i];
       }
       return d;
+    case IPADDR_ID:
+      return a->addr.id - b->addr.id;
   }
   return 0;
 }
 
 /* ================================================== */
 
-void
-UTI_SockaddrToIPAndPort(struct sockaddr *sa, IPAddr *ip, unsigned short *port)
+char *
+UTI_IPSockAddrToString(const IPSockAddr *sa)
 {
-  switch (sa->sa_family) {
-    case AF_INET:
-      ip->family = IPADDR_INET4;
-      ip->addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
-      *port = ntohs(((struct sockaddr_in *)sa)->sin_port);
-      break;
-#ifdef FEAT_IPV6
-    case AF_INET6:
-      ip->family = IPADDR_INET6;
-      memcpy(ip->addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
-             sizeof (ip->addr.in6));
-      *port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
-      break;
-#endif
-    default:
-      ip->family = IPADDR_UNSPEC;
-      *port = 0;
-  }
-}
-
-/* ================================================== */
-
-int
-UTI_IPAndPortToSockaddr(IPAddr *ip, unsigned short port, struct sockaddr *sa)
-{
-  switch (ip->family) {
-    case IPADDR_INET4:
-      memset(sa, 0, sizeof (struct sockaddr_in));
-      sa->sa_family = AF_INET;
-      ((struct sockaddr_in *)sa)->sin_addr.s_addr = htonl(ip->addr.in4);
-      ((struct sockaddr_in *)sa)->sin_port = htons(port);
-#ifdef SIN6_LEN
-      ((struct sockaddr_in *)sa)->sin_len = sizeof (struct sockaddr_in);
-#endif
-      return sizeof (struct sockaddr_in);
-#ifdef FEAT_IPV6
-    case IPADDR_INET6:
-      memset(sa, 0, sizeof (struct sockaddr_in6));
-      sa->sa_family = AF_INET6;
-      memcpy(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, ip->addr.in6,
-             sizeof (ip->addr.in6));
-      ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
-#ifdef SIN6_LEN
-      ((struct sockaddr_in6 *)sa)->sin6_len = sizeof (struct sockaddr_in6);
-#endif
-      return sizeof (struct sockaddr_in6);
-#endif
-    default:
-      memset(sa, 0, sizeof (struct sockaddr));
-      sa->sa_family = AF_UNSPEC;
-      return 0;
-  }
-}
-
-/* ================================================== */
-
-char *UTI_SockaddrToString(struct sockaddr *sa)
-{
-  unsigned short port;
-  IPAddr ip;
-  char *result, *sun_path;
+  char *result;
 
   result = NEXT_BUFFER;
-
-  switch (sa->sa_family) {
-    case AF_INET:
-#ifdef AF_INET6
-    case AF_INET6:
-#endif
-      UTI_SockaddrToIPAndPort(sa, &ip, &port);
-      snprintf(result, BUFFER_LENGTH, "%s:%hu", UTI_IPToString(&ip), port);
-      break;
-    case AF_UNIX:
-      sun_path = ((struct sockaddr_un *)sa)->sun_path;
-      snprintf(result, BUFFER_LENGTH, "%.*s", BUFFER_LENGTH - 1, sun_path);
-      /* Indicate truncated path */
-      if (strlen(sun_path) >= BUFFER_LENGTH)
-        result[BUFFER_LENGTH - 2] = '>';
-      break;
-    default:
-      snprintf(result, BUFFER_LENGTH, "[UNKNOWN]");
-  }
+  snprintf(result, BUFFER_LENGTH,
+           sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu",
+           UTI_IPToString(&sa->ip_addr), sa->port);
 
   return result;
 }
 
 /* ================================================== */
 
-const char *
-UTI_SockaddrFamilyToString(int family)
-{
-  switch (family) {
-    case AF_INET:
-      return "IPv4";
-#ifdef AF_INET6
-    case AF_INET6:
-      return "IPv6";
-#endif
-    case AF_UNIX:
-      return "Unix";
-    case AF_UNSPEC:
-      return "UNSPEC";
-    default:
-      return "?";
-  }
-}
-
-/* ================================================== */
-
 char *
 UTI_TimeToLogForm(time_t t)
 {
@@ -624,7 +572,8 @@
 /* ================================================== */
 
 void
-UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
+UTI_AdjustTimespec(const struct timespec *old_ts, const struct timespec *when,
+                   struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
 {
   double elapsed;
 
@@ -688,6 +637,43 @@
 
 /* ================================================== */
 
+double
+UTI_Ntp32f28ToDouble(NTP_int32 x)
+{
+  uint32_t r = ntohl(x);
+
+  /* Maximum value is special */
+  if (r == 0xffffffff)
+    return MAX_NTP_INT32;
+
+  return r / (double)(1U << 28);
+}
+
+/* ================================================== */
+
+NTP_int32
+UTI_DoubleToNtp32f28(double x)
+{
+  NTP_int32 r;
+
+  if (x >= 4294967295.0 / (1U << 28)) {
+    r = 0xffffffff;
+  } else if (x <= 0.0) {
+    r = 0;
+  } else {
+    x *= 1U << 28;
+    r = x;
+
+    /* Round up */
+    if (r < x)
+      r++;
+  }
+
+  return htonl(r);
+}
+
+/* ================================================== */
+
 void
 UTI_ZeroNtp64(NTP_int64 *ts)
 {
@@ -697,7 +683,7 @@
 /* ================================================== */
 
 int
-UTI_IsZeroNtp64(NTP_int64 *ts)
+UTI_IsZeroNtp64(const NTP_int64 *ts)
 {
   return !ts->hi && !ts->lo;
 }
@@ -705,7 +691,7 @@
 /* ================================================== */
 
 int
-UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
+UTI_CompareNtp64(const NTP_int64 *a, const NTP_int64 *b)
 {
   int32_t diff;
 
@@ -725,7 +711,8 @@
 /* ================================================== */
 
 int
-UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3)
+UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1, const NTP_int64 *b2,
+                    const NTP_int64 *b3)
 {
   if (b1 && a->lo == b1->lo && a->hi == b1->hi)
     return 1;
@@ -747,7 +734,7 @@
 #define NSEC_PER_NTP64 4.294967296
 
 void
-UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz)
+UTI_TimespecToNtp64(const struct timespec *src, NTP_int64 *dest, const NTP_int64 *fuzz)
 {
   uint32_t hi, lo, sec, nsec;
 
@@ -776,7 +763,7 @@
 /* ================================================== */
 
 void
-UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
+UTI_Ntp64ToTimespec(const NTP_int64 *src, struct timespec *dest)
 {
   uint32_t ntp_sec, ntp_frac;
 
@@ -801,6 +788,16 @@
 
 /* ================================================== */
 
+double
+UTI_DiffNtp64ToDouble(const NTP_int64 *a, const NTP_int64 *b)
+{
+  /* Don't convert to timespec to allow any epoch */
+  return (int32_t)(ntohl(a->hi) - ntohl(b->hi)) +
+         ((double)ntohl(a->lo) - (double)ntohl(b->lo)) / (1.0e9 * NSEC_PER_NTP64);
+}
+
+/* ================================================== */
+
 /* Maximum offset between two sane times */
 #define MAX_OFFSET 4294967296.0
 
@@ -808,7 +805,7 @@
 #define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600)
 
 int
-UTI_IsTimeOffsetSane(struct timespec *ts, double offset)
+UTI_IsTimeOffsetSane(const struct timespec *ts, double offset)
 {
   double t;
 
@@ -854,7 +851,7 @@
 /* ================================================== */
 
 void
-UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
+UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest)
 {
   uint32_t sec_low, nsec;
 #ifdef HAVE_LONG_TIME_T
@@ -879,7 +876,7 @@
 /* ================================================== */
 
 void
-UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest)
+UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest)
 {
   dest->tv_nsec = htonl(src->tv_nsec);
 #ifdef HAVE_LONG_TIME_T
@@ -978,50 +975,103 @@
 
 /* ================================================== */
 
+CMC_Algorithm
+UTI_CmacNameToAlgorithm(const char *name)
+{
+  if (strcmp(name, "AES128") == 0)
+    return CMC_AES128;
+  else if (strcmp(name, "AES256") == 0)
+    return CMC_AES256;
+  return CMC_INVALID;
+}
+
+/* ================================================== */
+
+HSH_Algorithm
+UTI_HashNameToAlgorithm(const char *name)
+{
+  if (strcmp(name, "MD5") == 0)
+    return HSH_MD5;
+  else if (strcmp(name, "SHA1") == 0)
+    return HSH_SHA1;
+  else if (strcmp(name, "SHA256") == 0)
+    return HSH_SHA256;
+  else if (strcmp(name, "SHA384") == 0)
+    return HSH_SHA384;
+  else if (strcmp(name, "SHA512") == 0)
+    return HSH_SHA512;
+  else if (strcmp(name, "SHA3-224") == 0)
+    return HSH_SHA3_224;
+  else if (strcmp(name, "SHA3-256") == 0)
+    return HSH_SHA3_256;
+  else if (strcmp(name, "SHA3-384") == 0)
+    return HSH_SHA3_384;
+  else if (strcmp(name, "SHA3-512") == 0)
+    return HSH_SHA3_512;
+  else if (strcmp(name, "TIGER") == 0)
+    return HSH_TIGER;
+  else if (strcmp(name, "WHIRLPOOL") == 0)
+    return HSH_WHIRLPOOL;
+  return HSH_INVALID;
+}
+
+/* ================================================== */
+
 int
 UTI_FdSetCloexec(int fd)
 {
   int flags;
 
   flags = fcntl(fd, F_GETFD);
-  if (flags != -1) {
-    flags |= FD_CLOEXEC;
-    return !fcntl(fd, F_SETFD, flags);
+  if (flags == -1) {
+    DEBUG_LOG("fcntl() failed : %s", strerror(errno));
+    return 0;
   }
 
-  return 0;
+  flags |= FD_CLOEXEC;
+
+  if (fcntl(fd, F_SETFD, flags) < 0) {
+    DEBUG_LOG("fcntl() failed : %s", strerror(errno));
+    return 0;
+  }
+
+  return 1;
 }
 
 /* ================================================== */
 
-int
-UTI_SetQuitSignalsHandler(void (*handler)(int))
+void
+UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe)
 {
   struct sigaction sa;
 
   sa.sa_handler = handler;
   sa.sa_flags = SA_RESTART;
   if (sigemptyset(&sa.sa_mask) < 0)
-    return 0;
+    LOG_FATAL("sigemptyset() failed");
 
 #ifdef SIGINT
   if (sigaction(SIGINT, &sa, NULL) < 0)
-    return 0;
+    LOG_FATAL("sigaction(%d) failed", SIGINT);
 #endif
 #ifdef SIGTERM
   if (sigaction(SIGTERM, &sa, NULL) < 0)
-    return 0;
+    LOG_FATAL("sigaction(%d) failed", SIGTERM);
 #endif
 #ifdef SIGQUIT
   if (sigaction(SIGQUIT, &sa, NULL) < 0)
-    return 0;
+    LOG_FATAL("sigaction(%d) failed", SIGQUIT);
 #endif
 #ifdef SIGHUP
   if (sigaction(SIGHUP, &sa, NULL) < 0)
-    return 0;
+    LOG_FATAL("sigaction(%d) failed", SIGHUP);
 #endif
 
-  return 1;
+  if (ignore_sigpipe)
+    sa.sa_handler = SIG_IGN;
+
+  if (sigaction(SIGPIPE, &sa, NULL) < 0)
+    LOG_FATAL("sigaction(%d) failed", SIGPIPE);
 }
 
 /* ================================================== */
@@ -1030,6 +1080,7 @@
 UTI_PathToDir(const char *path)
 {
   char *dir, *slash;
+  size_t dir_len;
 
   slash = strrchr(path, '/');
 
@@ -1039,8 +1090,11 @@
   if (slash == path)
     return Strdup("/");
 
-  dir = Malloc(slash - path + 1);
-  snprintf(dir, slash - path + 1, "%s", path);
+  dir_len = slash - path;
+
+  dir = Malloc(dir_len + 1);
+  memcpy(dir, path, dir_len);
+  dir[dir_len] = '\0';
 
   return dir;
 }
@@ -1160,12 +1214,12 @@
   }
 
   if (buf.st_uid != uid) {
-    LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "UID", uid);
+    LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "UID", uid);
     return 0;
   }
 
   if (buf.st_gid != gid) {
-    LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "GID", gid);
+    LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "GID", gid);
     return 0;
   }
 
@@ -1174,6 +1228,158 @@
 
 /* ================================================== */
 
+static int
+join_path(const char *basedir, const char *name, const char *suffix,
+          char *buffer, size_t length, LOG_Severity severity)
+{
+  const char *sep;
+
+  if (!basedir) {
+    basedir = "";
+    sep = "";
+  } else {
+    sep = "/";
+  }
+
+  if (!suffix)
+    suffix = "";
+
+  if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) {
+    LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix);
+    return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+FILE *
+UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
+             char mode, mode_t perm)
+{
+  const char *file_mode;
+  char path[PATH_MAX];
+  LOG_Severity severity;
+  int fd, flags;
+  FILE *file;
+
+  severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR;
+
+  if (!join_path(basedir, name, suffix, path, sizeof (path), severity))
+    return NULL;
+
+  switch (mode) {
+    case 'r':
+    case 'R':
+      flags = O_RDONLY;
+      file_mode = "r";
+      if (severity != LOGS_FATAL)
+        severity = LOGS_DEBUG;
+      break;
+    case 'w':
+    case 'W':
+      flags = O_WRONLY | O_CREAT | O_EXCL;
+      file_mode = "w";
+      break;
+    case 'a':
+    case 'A':
+      flags = O_WRONLY | O_CREAT | O_APPEND | O_NOFOLLOW;
+      file_mode = "a";
+      break;
+    default:
+      assert(0);
+      return NULL;
+  }
+
+try_again:
+  fd = open(path, flags, perm);
+  if (fd < 0) {
+    if (errno == EEXIST) {
+      if (unlink(path) < 0) {
+        LOG(severity, "Could not remove %s : %s", path, strerror(errno));
+        return NULL;
+      }
+      DEBUG_LOG("Removed %s", path);
+      goto try_again;
+    }
+    LOG(severity, "Could not open %s : %s", path, strerror(errno));
+    return NULL;
+  }
+
+  UTI_FdSetCloexec(fd);
+
+  file = fdopen(fd, file_mode);
+  if (!file) {
+    LOG(severity, "Could not open %s : %s", path, strerror(errno));
+    close(fd);
+    return NULL;
+  }
+
+  DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode);
+
+  return file;
+}
+
+/* ================================================== */
+
+int
+UTI_RenameTempFile(const char *basedir, const char *name,
+                   const char *old_suffix, const char *new_suffix)
+{
+  char old_path[PATH_MAX], new_path[PATH_MAX];
+
+  if (!join_path(basedir, name, old_suffix, old_path, sizeof (old_path), LOGS_ERR))
+    return 0;
+
+  if (!join_path(basedir, name, new_suffix, new_path, sizeof (new_path), LOGS_ERR))
+    goto error;
+
+  if (rename(old_path, new_path) < 0) {
+    LOG(LOGS_ERR, "Could not replace %s with %s : %s", new_path, old_path, strerror(errno));
+    goto error;
+  }
+
+  DEBUG_LOG("Renamed %s to %s", old_path, new_path);
+
+  return 1;
+
+error:
+  if (unlink(old_path) < 0)
+    LOG(LOGS_ERR, "Could not remove %s : %s", old_path, strerror(errno));
+
+  return 0;
+}
+
+/* ================================================== */
+
+int
+UTI_RemoveFile(const char *basedir, const char *name, const char *suffix)
+{
+  char path[PATH_MAX];
+  struct stat buf;
+
+  if (!join_path(basedir, name, suffix, path, sizeof (path), LOGS_ERR))
+    return 0;
+
+  /* Avoid logging an error message if the file is not accessible */
+  if (stat(path, &buf) < 0) {
+    DEBUG_LOG("Could not remove %s : %s", path, strerror(errno));
+    return 0;
+  }
+
+  if (unlink(path) < 0) {
+    LOG(LOGS_ERR, "Could not remove %s : %s", path, strerror(errno));
+    return 0;
+  }
+
+  DEBUG_LOG("Removed %s", path);
+
+  return 1;
+}
+
+/* ================================================== */
+
 void
 UTI_DropRoot(uid_t uid, gid_t gid)
 {
@@ -1183,56 +1389,57 @@
 
   /* Set effective, saved and real group ID */
   if (setgid(gid))
-    LOG_FATAL("setgid(%d) failed : %s", gid, strerror(errno));
+    LOG_FATAL("setgid(%u) failed : %s", gid, strerror(errno));
 
   /* Set effective, saved and real user ID */
   if (setuid(uid))
-    LOG_FATAL("setuid(%d) failed : %s", uid, strerror(errno));
+    LOG_FATAL("setuid(%u) failed : %s", uid, strerror(errno));
 
-  DEBUG_LOG("Dropped root privileges: UID %d GID %d", uid, gid);
+  DEBUG_LOG("Dropped root privileges: UID %u GID %u", uid, gid);
 }
 
 /* ================================================== */
 
 #define DEV_URANDOM "/dev/urandom"
 
+static FILE *urandom_file = NULL;
+
 void
 UTI_GetRandomBytesUrandom(void *buf, unsigned int len)
 {
-  static FILE *f = NULL;
-
-  if (!f)
-    f = fopen(DEV_URANDOM, "r");
-  if (!f)
-    LOG_FATAL("Can't open %s : %s", DEV_URANDOM, strerror(errno));
-  if (fread(buf, 1, len, f) != len)
+  if (!urandom_file)
+    urandom_file = UTI_OpenFile(NULL, DEV_URANDOM, NULL, 'R', 0);
+  if (fread(buf, 1, len, urandom_file) != len)
     LOG_FATAL("Can't read from %s", DEV_URANDOM);
 }
 
 /* ================================================== */
 
 #ifdef HAVE_GETRANDOM
+
+static unsigned int getrandom_buf_available = 0;
+
 static void
 get_random_bytes_getrandom(char *buf, unsigned int len)
 {
   static char rand_buf[256];
-  static unsigned int available = 0, disabled = 0;
+  static unsigned int disabled = 0;
   unsigned int i;
 
   for (i = 0; i < len; i++) {
-    if (!available) {
+    if (getrandom_buf_available == 0) {
       if (disabled)
         break;
 
-      if (getrandom(rand_buf, sizeof (rand_buf), 0) != sizeof (rand_buf)) {
+      if (getrandom(rand_buf, sizeof (rand_buf), GRND_NONBLOCK) != sizeof (rand_buf)) {
         disabled = 1;
         break;
       }
 
-      available = sizeof (rand_buf);
+      getrandom_buf_available = sizeof (rand_buf);
     }
 
-    buf[i] = rand_buf[--available];
+    buf[i] = rand_buf[--getrandom_buf_available];
   }
 
   if (i < len)
@@ -1253,3 +1460,91 @@
   UTI_GetRandomBytesUrandom(buf, len);
 #endif
 }
+
+/* ================================================== */
+
+void
+UTI_ResetGetRandomFunctions(void)
+{
+  if (urandom_file) {
+    fclose(urandom_file);
+    urandom_file = NULL;
+  }
+#ifdef HAVE_GETRANDOM
+  getrandom_buf_available = 0;
+#endif
+}
+
+/* ================================================== */
+
+int
+UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsigned int hex_len)
+{
+  unsigned int i, l;
+
+  if (hex_len < 1)
+    return 0;
+
+  hex[0] = '\0';
+
+  for (i = l = 0; i < buf_len; i++, l += 2) {
+    if (l + 2 >= hex_len ||
+        snprintf(hex + l, hex_len - l, "%02hhX", ((const char *)buf)[i]) != 2)
+      return 0;
+  }
+
+  return 1;
+}
+
+/* ================================================== */
+
+unsigned int
+UTI_HexToBytes(const char *hex, void *buf, unsigned int len)
+{
+  char *p, byte[3];
+  unsigned int i;
+
+  for (i = 0; i < len && *hex != '\0'; i++) {
+    byte[0] = *hex++;
+    if (*hex == '\0')
+      return 0;
+    byte[1] = *hex++;
+    byte[2] = '\0';
+    ((char *)buf)[i] = strtol(byte, &p, 16);
+
+    if (p != byte + 2)
+      return 0;
+  }
+
+  return *hex == '\0' ? i : 0;
+}
+
+/* ================================================== */
+
+int
+UTI_SplitString(char *string, char **words, int max_saved_words)
+{
+  char *s = string;
+  int i;
+
+  for (i = 0; i < max_saved_words; i++)
+    words[i] = NULL;
+
+  for (i = 0; ; i++) {
+    /* Zero white-space characters before the word */
+    while (*s != '\0' && isspace((unsigned char)*s))
+      *s++ = '\0';
+
+    if (*s == '\0')
+      break;
+
+    if (i < max_saved_words)
+      words[i] = s;
+
+    /* Find the next word */
+    while (*s != '\0' && !isspace((unsigned char)*s))
+      s++;
+  }
+
+  return i;
+}
diff --git a/chrony/util.h b/chrony/util.h
new file mode 100644
index 0000000..a57ef9b
--- /dev/null
+++ b/chrony/util.h
@@ -0,0 +1,258 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow  1997-2003
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ **********************************************************************
+
+  =======================================================================
+
+  Various utility functions
+  */
+
+#ifndef GOT_UTIL_H
+#define GOT_UTIL_H
+
+#include "sysincl.h"
+
+#include "addressing.h"
+#include "ntp.h"
+#include "candm.h"
+#include "cmac.h"
+#include "hash.h"
+
+/* Zero a timespec */
+extern void UTI_ZeroTimespec(struct timespec *ts);
+
+/* Check if a timespec is zero */
+extern int UTI_IsZeroTimespec(struct timespec *ts);
+
+/* Convert a timeval into a timespec */
+extern void UTI_TimevalToTimespec(const struct timeval *tv, struct timespec *ts);
+
+/* Convert a timespec into a timeval */
+extern void UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv);
+
+/* Convert a timespec into a floating point number of seconds */
+extern double UTI_TimespecToDouble(const struct timespec *ts);
+
+/* Convert a number of seconds expressed in floating point into a
+   timespec */
+extern void UTI_DoubleToTimespec(double d, struct timespec *ts);
+
+/* Normalise a timespec, by adding or subtracting seconds to bring
+   its nanosecond field into range */
+extern void UTI_NormaliseTimespec(struct timespec *ts);
+
+/* Convert a timeval into a floating point number of seconds */
+extern double UTI_TimevalToDouble(const struct timeval *tv);
+
+/* Convert a number of seconds expressed in floating point into a
+   timeval */
+extern void UTI_DoubleToTimeval(double a, struct timeval *b);
+
+/* Normalise a struct timeval, by adding or subtracting seconds to bring
+   its microseconds field into range */
+extern void UTI_NormaliseTimeval(struct timeval *x);
+
+/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
+   and +1 if a comes after b */
+extern int UTI_CompareTimespecs(const struct timespec *a, const struct timespec *b);
+
+/* Calculate result = a - b */
+extern void UTI_DiffTimespecs(struct timespec *result,
+                              const struct timespec *a, const struct timespec *b);
+
+/* Calculate result = a - b and return as a double */
+extern double UTI_DiffTimespecsToDouble(const struct timespec *a, const struct timespec *b);
+
+/* Add a double increment to a timespec to get a new one. 'start' is
+   the starting time, 'end' is the result that we return.  This is
+   safe to use if start and end are the same */
+extern void UTI_AddDoubleToTimespec(const struct timespec *start, double increment,
+                                    struct timespec *end);
+
+/* Calculate the average and difference (as a double) of two timespecs */
+extern void UTI_AverageDiffTimespecs(const struct timespec *earlier, const struct timespec *later,
+                                     struct timespec *average, double *diff);
+
+/* Calculate result = a - b + c */
+extern void UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b,
+                                  const struct timespec *c, struct timespec *result);
+
+/* Convert a timespec into a temporary string, largely for diagnostic
+   display */
+extern char *UTI_TimespecToString(const struct timespec *ts);
+
+/* Convert an NTP timestamp into a temporary string, largely for
+   diagnostic display */
+extern char *UTI_Ntp64ToString(const NTP_int64 *ts);
+
+/* Convert ref_id into a temporary string, for diagnostics */
+extern char *UTI_RefidToString(uint32_t ref_id);
+
+/* Convert an IP address to string, for diagnostics */
+extern char *UTI_IPToString(const IPAddr *ip);
+
+extern int UTI_StringToIP(const char *addr, IPAddr *ip);
+extern int UTI_IsStringIP(const char *string);
+extern int UTI_StringToIdIP(const char *addr, IPAddr *ip);
+extern int UTI_IsIPReal(const IPAddr *ip);
+extern uint32_t UTI_IPToRefid(const IPAddr *ip);
+extern uint32_t UTI_IPToHash(const IPAddr *ip);
+extern void UTI_IPHostToNetwork(const IPAddr *src, IPAddr *dest);
+extern void UTI_IPNetworkToHost(const IPAddr *src, IPAddr *dest);
+extern int UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask);
+
+extern char *UTI_IPSockAddrToString(const IPSockAddr *sa);
+
+extern char *UTI_TimeToLogForm(time_t t);
+
+/* Adjust time following a frequency/offset change */
+extern void UTI_AdjustTimespec(const struct timespec *old_ts, const struct timespec *when,
+                               struct timespec *new_ts, double *delta_time,
+                               double dfreq, double doffset);
+
+/* Get zero NTP timestamp with random bits below precision */
+extern void UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision);
+
+extern double UTI_Ntp32ToDouble(NTP_int32 x);
+extern NTP_int32 UTI_DoubleToNtp32(double x);
+
+extern double UTI_Ntp32f28ToDouble(NTP_int32 x);
+extern NTP_int32 UTI_DoubleToNtp32f28(double x);
+
+/* Zero an NTP timestamp */
+extern void UTI_ZeroNtp64(NTP_int64 *ts);
+
+/* Check if an NTP timestamp is zero */
+extern int UTI_IsZeroNtp64(const NTP_int64 *ts);
+
+/* Compare two NTP timestamps.  Returns -1 if a is before b, 0 if a is equal to
+   b, and 1 if a is after b. */
+extern int UTI_CompareNtp64(const NTP_int64 *a, const NTP_int64 *b);
+
+/* Compare an NTP timestamp with up to three other timestamps.  Returns 0
+   if a is not equal to any of b1, b2, and b3, 1 otherwise. */
+extern int UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1,
+                               const NTP_int64 *b2, const NTP_int64 *b3);
+
+/* Convert a timespec into an NTP timestamp */
+extern void UTI_TimespecToNtp64(const struct timespec *src, NTP_int64 *dest,
+                                const NTP_int64 *fuzz);
+
+/* Convert an NTP timestamp into a timespec */
+extern void UTI_Ntp64ToTimespec(const NTP_int64 *src, struct timespec *dest);
+
+/* Calculate a - b in any epoch */
+extern double UTI_DiffNtp64ToDouble(const NTP_int64 *a, const NTP_int64 *b);
+
+/* Check if time + offset is sane */
+extern int UTI_IsTimeOffsetSane(const struct timespec *ts, double offset);
+
+/* Get 2 raised to power of a signed integer */
+extern double UTI_Log2ToDouble(int l);
+
+extern void UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest);
+extern void UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest);
+
+extern double UTI_FloatNetworkToHost(Float x);
+extern Float UTI_FloatHostToNetwork(double x);
+
+extern CMC_Algorithm UTI_CmacNameToAlgorithm(const char *name);
+extern HSH_Algorithm UTI_HashNameToAlgorithm(const char *name);
+
+/* Set FD_CLOEXEC on descriptor */
+extern int UTI_FdSetCloexec(int fd);
+
+extern void UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe);
+
+/* Get directory (as an allocated string) for a path */
+extern char *UTI_PathToDir(const char *path);
+
+/* Create a directory with a specified mode (umasked) and set its uid/gid.
+   Create also any parent directories that don't exist with mode 755 and
+   default uid/gid.  Returns 1 if created or already exists (even with
+   different mode/uid/gid), 0 otherwise. */
+extern int UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid_t gid);
+
+/* Check if a directory is secure.  It must not have other than the specified
+   permissions and its uid/gid must match the specified values. */
+extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid);
+
+/* Open a file.  The full path of the file is constructed from the basedir
+   (may be NULL), '/' (if basedir is not NULL), name, and suffix (may be NULL).
+   Created files have specified permissions (umasked).  Returns NULL on error.
+   The following modes are supported (if the mode is an uppercase character,
+   errors are fatal):
+   r/R - open an existing file for reading
+   w/W - open a new file for writing (remove existing file)
+   a/A - open an existing file for appending (create if does not exist) */
+extern FILE *UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
+                          char mode, mode_t perm);
+
+/* Rename a temporary file by changing its suffix.  The paths are constructed as
+   in UTI_OpenFile().  If the renaming fails, the file will be removed. */
+extern int UTI_RenameTempFile(const char *basedir, const char *name,
+                              const char *old_suffix, const char *new_suffix);
+
+/* Remove a file.  The path is constructed as in UTI_OpenFile(). */
+extern int UTI_RemoveFile(const char *basedir, const char *name, const char *suffix);
+
+/* Set process user/group IDs and drop supplementary groups */
+extern void UTI_DropRoot(uid_t uid, gid_t gid);
+
+/* Fill buffer with random bytes from /dev/urandom */
+extern void UTI_GetRandomBytesUrandom(void *buf, unsigned int len);
+
+/* Fill buffer with random bytes from /dev/urandom or a faster source if it's
+   available (e.g. arc4random()), which may not necessarily be suitable for
+   generating long-term keys */
+extern void UTI_GetRandomBytes(void *buf, unsigned int len);
+
+/* Close /dev/urandom and drop any cached data used by the GetRandom functions
+   to prevent forked processes getting the same sequence of random numbers */
+extern void UTI_ResetGetRandomFunctions(void);
+
+/* Print data in hexadecimal format */
+extern int UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsigned int hex_len);
+
+/* Parse a string containing data in hexadecimal format.  In-place conversion
+   is supported. */
+extern unsigned int UTI_HexToBytes(const char *hex, void *buf, unsigned int len);
+
+/* Split a string into words separated by whitespace characters.  It returns
+   the number of words found in the string, but saves only up to the specified
+   number of pointers to the words. */
+extern int UTI_SplitString(char *string, char **words, int max_saved_words);
+
+/* Macros to get maximum and minimum of two values */
+#ifdef MAX
+#undef MAX
+#endif
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#ifdef MIN
+#undef MIN
+#endif
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+/* Macro to clamp a value between two values */
+#define CLAMP(min, x, max) (MAX((min), MIN((x), (max))))
+
+#define SQUARE(x) ((x) * (x))
+
+#endif /* GOT_UTIL_H */
diff --git a/chrony/version.txt b/chrony/version.txt
new file mode 100644
index 0000000..bf77d54
--- /dev/null
+++ b/chrony/version.txt
@@ -0,0 +1 @@
+4.2
diff --git a/chrony_3_3/FAQ b/chrony_3_3/FAQ
deleted file mode 100644
index e8edbf7..0000000
--- a/chrony_3_3/FAQ
+++ /dev/null
@@ -1,460 +0,0 @@
-Frequently Asked Questions
-
-Table of Contents
-
-  o 1. chrony compared to other programs
-      ? 1.1. How does chrony compare to ntpd?
-  o 2. Configuration issues
-      ? 2.1. What is the minimum recommended configuration for an NTP client?
-      ? 2.2. How do I make an NTP server from an NTP client?
-      ? 2.3. I have several computers on a LAN. Should be all clients of an
-        external server?
-      ? 2.4. Must I specify servers by IP address if DNS is not available on
-        chronyd start?
-      ? 2.5. How can I make chronyd more secure?
-      ? 2.6. How can I improve the accuracy of the system clock with NTP
-        sources?
-      ? 2.7. Does chronyd have an ntpdate mode?
-      ? 2.8. What happened to the commandkey and generatecommandkey directives?
-  o 3. Computer is not synchronising
-      ? 3.1. Behind a firewall?
-      ? 3.2. Are NTP servers specified with the offline option?
-      ? 3.3. Is chronyd allowed to step the system clock?
-  o 4. Issues with chronyc
-      ? 4.1. I keep getting the error 506 Cannot talk to daemon
-      ? 4.2. I keep getting the error 501 Not authorised
-      ? 4.3. Why does chronyc tracking always print an IPv4 address as
-        reference ID?
-      ? 4.4. Is the chronyc / chronyd protocol documented anywhere?
-  o 5. Real-time clock issues
-      ? 5.1. What is the real-time clock (RTC)?
-      ? 5.2. I want to use chronyd's RTC support. Must I disable hwclock?
-      ? 5.3. I just keep getting the 513 RTC driver not running message
-      ? 5.4. I get Could not open /dev/rtc, Device or resource busy in my
-        syslog file
-      ? 5.5. What if my computer does not have an RTC or backup battery?
-  o 6. NTP-specific issues
-      ? 6.1. Can chronyd be driven from broadcast/multicast NTP servers?
-      ? 6.2. Can chronyd transmit broadcast NTP packets?
-      ? 6.3. Can chronyd keep the system clock a fixed offset away from real
-        time?
-      ? 6.4. What happens if the network connection is dropped without using
-        chronyc's offline command first?
-  o 7. Operating systems
-      ? 7.1. Does chrony support Windows?
-      ? 7.2. Are there any plans to support Windows?
-
-1. chrony compared to other programs
-
-1.1. How does chrony compare to ntpd?
-
-chronyd was designed to work well in a wide range of conditions and it can
-usually synchronise the system clock faster and with better time accuracy. It
-doesn't implement some of the less useful NTP modes like broadcast client or
-multicast server/client.
-
-If your computer is connected to the Internet only for few minutes at a time,
-the network connection is often congested, you turn your computer off or
-suspend it frequently, the clock is not very stable (e.g. there are rapid
-changes in the temperature or it's a virtual machine), or you want to use NTP
-on an isolated network with no hardware reference clocks in sight, chrony will
-probably work much better for you.
-
-For a more detailed comparison of features and performance, see the comparison
-page on the chrony website.
-
-2. Configuration issues
-
-2.1. What is the minimum recommended configuration for an NTP client?
-
-First, the client needs to know which NTP servers it should ask for the current
-time. They are specified by the server or pool directive. The pool directive
-can be used for names that resolve to multiple addresses. For good reliability
-the client should have at least three servers. The iburst option speeds up the
-initial synchronisation.
-
-To stabilise the initial synchronisation on the next start, the estimated drift
-of the system clock is saved to a file specified by the driftfile directive.
-
-If the system clock can be far from the true time after boot for any reason,
-chronyd should be allowed to correct it quickly by stepping instead of slewing,
-which would take a very long time. The makestep directive does that.
-
-In order to keep the real-time clock (RTC) close to the true time, so the
-system time is reasonably close to the true time when it's initialised on the
-next boot from the RTC, the rtcsync directive enables a mode in which the
-system time is periodically copied to the RTC. It is supported on Linux and
-macOS.
-
-If you want to use public NTP servers from the pool.ntp.org project, the
-minimal chrony.conf file could be:
-
-pool pool.ntp.org iburst
-driftfile /var/lib/chrony/drift
-makestep 1 3
-rtcsync
-
-2.2. How do I make an NTP server from an NTP client?
-
-You need to add an allow directive to the chrony.conf file in order to open the
-NTP port and allow chronyd to reply to client requests. allow with no specified
-subnet allows access from all IPv4 and IPv6 addresses.
-
-2.3. I have several computers on a LAN. Should be all clients of an external
-server?
-
-The best configuration is usually to make one computer the server, with the
-others as clients of it. Add a local directive to the server's chrony.conf
-file. This configuration will be better because
-
-  o the load on the external connection is less
-
-  o the load on the external NTP server(s) is less
-
-  o if your external connection goes down, the computers on the LAN will
-    maintain a common time with each other.
-
-2.4. Must I specify servers by IP address if DNS is not available on chronyd
-start?
-
-No. Starting from version 1.25, chronyd will keep trying to resolve the names
-specified by the server, pool, and peer directives in an increasing interval
-until it succeeds. The online command can be issued from chronyc to force
-chronyd to try to resolve the names immediately.
-
-2.5. How can I make chronyd more secure?
-
-If you don't need to serve time to NTP clients or peers, you can add port 0 to
-the chrony.conf file to completely disable the NTP server functionality and
-prevent NTP requests from reaching chronyd. Starting from version 2.0, the NTP
-server port is open only when client access is allowed by the allow directive
-or command, an NTP peer is configured, or the broadcast directive is used.
-
-If you don't need to use chronyc remotely, you can add the following directives
-to the configuration file to bind the command sockets to the loopback
-interface. This is done by default since version 2.0.
-
-bindcmdaddress 127.0.0.1
-bindcmdaddress ::1
-
-If you don't need to use chronyc at all or you need to run chronyc only under
-the root or chrony user (which can access chronyd through a Unix domain socket
-since version 2.2), you can disable the internet command sockets completely by
-adding cmdport 0 to the configuration file.
-
-You can specify an unprivileged user with the -u option, or the user directive
-in the chrony.conf file, to which chronyd will switch after start in order to
-drop root privileges. The configure script has a --with-user option, which sets
-the default user. On Linux, chronyd needs to be compiled with support for the
-libcap library. On other systems, chronyd forks into two processes. The child
-process retains root privileges, but can only perform a very limited range of
-privileged system calls on behalf of the parent.
-
-Also, if chronyd is compiled with support for the Linux secure computing
-(seccomp) facility, you can enable a system call filter with the -F option. It
-will significantly reduce the kernel attack surface and possibly prevent kernel
-exploits from the chronyd process if it's compromised. It's recommended to
-enable the filter only when it's known to work on the version of the system
-where chrony is installed as the filter needs to allow also system calls made
-from libraries that chronyd is using (e.g. libc) and different versions or
-implementations of the libraries may make different system calls. If the filter
-is missing some system call, chronyd could be killed even in normal operation.
-
-2.6. How can I improve the accuracy of the system clock with NTP sources?
-
-Select NTP servers that are well synchronised, stable and close to your
-network. It's better to use more than one server, three or four is usually
-recommended as the minimum, so chronyd can detect servers that serve false time
-and combine measurements from multiple sources.
-
-If you have a network card with hardware timestamping supported on Linux, it
-can be enabled by the hwtimestamp directive in the chrony.conf file. It should
-make local receive and transmit timestamps of NTP packets much more accurate.
-
-There are also useful options which can be set in the server directive, they
-are minpoll, maxpoll, polltarget, maxdelay, maxdelayratio, maxdelaydevratio,
-and xleave.
-
-The first three options set the minimum and maximum allowed polling interval,
-and how should be the actual interval adjusted in the specified range. Their
-default values are 6 (64 seconds) for minpoll, 10 (1024 seconds) for maxpoll
-and 8 (samples) for polltarget. The default values should be used for general
-servers on the Internet. With your own NTP servers, or if you have permission
-to poll some servers more frequently, setting these options for shorter polling
-intervals may significantly improve the accuracy of the system clock.
-
-The optimal polling interval depends mainly on two factors, stability of the
-network latency and stability of the system clock (which mainly depends on the
-temperature sensitivity of the crystal oscillator and the maximum rate of the
-temperature change).
-
-An example of the directive for an NTP server on the Internet that you are
-allowed to poll frequently could be
-
-server foo.example.net minpoll 4 maxpoll 6 polltarget 16
-
-An example using very short polling intervals for a server located in the same
-LAN could be
-
-server ntp.local minpoll 2 maxpoll 4 polltarget 30
-
-The maxdelay options are useful to ignore measurements with larger delay (e.g.
-due to congestion in the network) and improve the stability of the
-synchronisation. The maxdelaydevratio option could be added to the example with
-local NTP server
-
-server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
-
-If your server supports the interleaved mode, the xleave option should be added
-to the server directive in order to allow the server to send the client more
-accurate hardware or kernel transmit timestamps. When combined with local
-hardware timestamping, sub-microsecond accuracy may be possible. An example
-could be
-
-server ntp.local minpoll 2 maxpoll 2 xleave
-hwtimestamp eth0
-
-2.7. Does chronyd have an ntpdate mode?
-
-Yes. With the -q option chronyd will set the system clock once and exit. With
-the -Q option it will print the measured offset without setting the clock. If
-you don't want to use a configuration file, NTP servers can be specified on the
-command line. For example:
-
-# chronyd -q 'pool pool.ntp.org iburst'
-
-2.8. What happened to the commandkey and generatecommandkey directives?
-
-They were removed in version 2.2. Authentication is no longer supported in the
-command protocol. Commands that required authentication are now allowed only
-through a Unix domain socket, which is accessible only by the root and chrony
-users. If you need to configure chronyd remotely or locally without the root
-password, please consider using ssh and/or sudo to run chronyc under the root
-or chrony user on the host where chronyd is running.
-
-3. Computer is not synchronising
-
-This is the most common problem. There are a number of reasons, see the
-following questions.
-
-3.1. Behind a firewall?
-
-Check the Reach value printed by the chronyc's sources command. If it's zero,
-it means chronyd did not get any valid responses from the NTP server you are
-trying to use. If there is a firewall between you and the server, the packets
-may be blocked. Try using a tool like wireshark or tcpdump to see if you're
-getting any responses from the server.
-
-When chronyd is receiving responses from the servers, the output of the sources
-command issued few minutes after chronyd start might look like this:
-
-210 Number of sources = 3
-MS Name/IP address         Stratum Poll Reach LastRx Last sample
-===============================================================================
-^* foo.example.net               2   6   377    34   +484us[ -157us] +/-   30ms
-^- bar.example.net               2   6   377    34    +33ms[  +32ms] +/-   47ms
-^+ baz.example.net               3   6   377    35  -1397us[-2033us] +/-   60ms
-
-3.2. Are NTP servers specified with the offline option?
-
-Check that you're using chronyc's online and offline commands appropriately.
-The activity command prints the number of sources that are currently online and
-offline. For example:
-
-200 OK
-3 sources online
-0 sources offline
-0 sources doing burst (return to online)
-0 sources doing burst (return to offline)
-0 sources with unknown address
-
-3.3. Is chronyd allowed to step the system clock?
-
-By default, chronyd adjusts the clock gradually by slowing it down or speeding
-it up. If the clock is too far from the true time, it will take a long time to
-correct the error. The System time value printed by the chronyc's tracking
-command is the remaining correction that needs to be applied to the system
-clock.
-
-The makestep directive can be used to allow chronyd to step the clock. For
-example, if chrony.conf had
-
-makestep 1 3
-
-the clock would be stepped in the first three updates if its offset was larger
-than one second. Normally, it's recommended to allow the step only in the first
-few updates, but in some cases (e.g. a computer without an RTC or virtual
-machine which can be suspended and resumed with an incorrect time) it may be
-necessary to allow the step on any clock update. The example above would change
-to
-
-makestep 1 -1
-
-4. Issues with chronyc
-
-4.1. I keep getting the error 506 Cannot talk to daemon
-
-When accessing chronyd remotely, make sure that the chrony.conf file (on the
-computer where chronyd is running) has a cmdallow entry for the computer you
-are running chronyc on and an appropriate bindcmdaddress directive. This isn't
-necessary for localhost.
-
-Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, ps
--auxw) to see if it's running. Or try netstat -a and see if the ports 123/udp
-and 323/udp are listening. If chronyd is not running, you may have a problem
-with the way you are trying to start it (e.g. at boot time).
-
-Perhaps you have a firewall set up in a way that blocks packets on port 323/
-udp. You need to amend the firewall configuration in this case.
-
-4.2. I keep getting the error 501 Not authorised
-
-Since version 2.2, the password command doesn't do anything and chronyc needs
-to run locally under the root or chrony user, which are allowed to access the
-chronyd's Unix domain command socket.
-
-With older versions, you need to authenticate with the password command first
-or use the -a option to authenticate automatically on start. The configuration
-file needs to specify a file which contains keys (keyfile directive) and which
-key in the key file should be used for chronyc authentication (commandkey
-directive).
-
-4.3. Why does chronyc tracking always print an IPv4 address as reference ID?
-
-The reference ID is a 32-bit value and in versions before 3.0 it was printed in
-quad-dotted notation, even if the reference source did not actually have an
-IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
-for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
-reference clocks, the reference ID is the value specified with the refid option
-in the refclock directive.
-
-Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
-confusion with IPv4 addresses.
-
-If you need to get the IP address of the current reference source, use the -n
-option to disable resolving of IP addresses and read the second field (printed
-in parentheses) on the Reference ID line.
-
-4.4. Is the chronyc / chronyd protocol documented anywhere?
-
-Only by the source code. See cmdmon.c (chronyd side) and client.c (chronyc
-side).
-
-5. Real-time clock issues
-
-5.1. What is the real-time clock (RTC)?
-
-This is the clock which keeps the time even when your computer is turned off.
-It is used to initialise the system clock on boot. It normally doesn't drift
-more than few seconds per day.
-
-There are two approaches how chronyd can work with it. One is to use the
-rtcsync directive, which tells chronyd to enable a kernel mode which sets the
-RTC from the system clock every 11 minutes. chronyd itself won't touch the RTC.
-If the computer is not turned off for a long time, the RTC should still be
-close to the true time when the system clock will be initialised from it on the
-next boot.
-
-The other option is to use the rtcfile directive, which tells chronyd to
-monitor the rate at which the RTC gains or loses time. When chronyd is started
-with the -s option on the next boot, it will set the system time from the RTC
-and also compensate for the drift it has measured previously. The rtcautotrim
-directive can be used to keep the RTC close to the true time, but it's not
-strictly necessary if its only purpose is to set the system clock when chronyd
-is started on boot. See the documentation for details.
-
-5.2. I want to use chronyd's RTC support. Must I disable hwclock?
-
-The hwclock program is often set-up by default in the boot and shutdown scripts
-with many Linux installations. With the kernel RTC synchronisation (rtcsync
-directive), the RTC will be set also every 11 minutes as long as the system
-clock is synchronised. If you want to use chronyd's RTC monitoring (rtcfile
-directive), it's important to disable hwclock in the shutdown procedure. If you
-don't, it will over-write the RTC with a new value, unknown to chronyd. At the
-next reboot, chronyd started with the -s option will compensate this (wrong)
-time with its estimate of how far the RTC has drifted whilst the power was off,
-giving a meaningless initial system time.
-
-There is no need to remove hwclock from the boot process, as long as chronyd is
-started after it has run.
-
-5.3. I just keep getting the 513 RTC driver not running message
-
-For the real-time clock support to work, you need the following three things
-
-  o an RTC in your computer
-
-  o a Linux kernel with enabled RTC support
-
-  o an rtcfile directive in your chrony.conf file
-
-5.4. I get Could not open /dev/rtc, Device or resource busy in my syslog file
-
-Some other program running on the system may be using the device.
-
-5.5. What if my computer does not have an RTC or backup battery?
-
-In this case you can still use the -s option to set the system clock to the
-last modification time of the drift file, which should correspond to the system
-time when chronyd was previously stopped. The initial system time will be
-increasing across reboots and applications started after chronyd will not
-observe backward steps.
-
-6. NTP-specific issues
-
-6.1. Can chronyd be driven from broadcast/multicast NTP servers?
-
-No, the broadcast/multicast client mode is not supported and there is currently
-no plan to implement it. While the mode may be useful to simplify configuration
-of clients in large networks, it is inherently less accurate and less secure
-(even with authentication) than the ordinary client/server mode.
-
-When configuring a large number of clients in a network, it is recommended to
-use the pool directive with a DNS name which resolves to addresses of multiple
-NTP servers. The clients will automatically replace the servers when they
-become unreachable, or otherwise unsuitable for synchronisation, with new
-servers from the pool.
-
-Even with very modest hardware, an NTP server can serve time to hundreds of
-thousands of clients using the ordinary client/server mode.
-
-6.2. Can chronyd transmit broadcast NTP packets?
-
-Yes, the broadcast directive can be used to enable the broadcast server mode to
-serve time to clients in the network which support the broadcast client mode
-(it's not supported in chronyd, see the previous question).
-
-6.3. Can chronyd keep the system clock a fixed offset away from real time?
-
-Yes. Starting from version 3.0, an offset can be specified by the offset option
-for all time sources in the chrony.conf file.
-
-6.4. What happens if the network connection is dropped without using chronyc's
-offline command first?
-
-chronyd will keep trying to access the sources that it thinks are online, and
-it will take longer before new measurements are actually made and the clock is
-corrected when the network is connected again. If the sources were set to
-offline, chronyd would make new measurements immediately after issuing the
-online command.
-
-Unless the network connection lasts only few minutes (less than the maximum
-polling interval), the delay is usually not a problem, and it may be acceptable
-to keep all sources online all the time.
-
-7. Operating systems
-
-7.1. Does chrony support Windows?
-
-No. The chronyc program (the command-line client used for configuring chronyd
-while it is running) has been successfully built and run under Cygwin in the
-past. chronyd is not portable, because part of it is very system-dependent. It
-needs adapting to work with Windows' equivalent of the adjtimex() call, and it
-needs to be made to work as a service.
-
-7.2. Are there any plans to support Windows?
-
-We have no plans to do this. Anyone is welcome to pick this work up and
-contribute it back to the project.
-
-Last updated 2018-04-04 09:18:44 CEST
diff --git a/chrony_3_3/README b/chrony_3_3/README
deleted file mode 100644
index aad727e..0000000
--- a/chrony_3_3/README
+++ /dev/null
@@ -1,239 +0,0 @@
-This is the README for chrony.
-
-What is chrony?
-===============
-
-chrony is a versatile implementation of the Network Time Protocol (NTP).
-It can synchronise the system clock with NTP servers, reference clocks
-(e.g. GPS receiver), and manual input using wristwatch and keyboard.
-It can also operate as an NTPv4 (RFC 5905) server and peer to provide
-a time service to other computers in the network.
-
-It is designed to perform well in a wide range of conditions, including
-intermittent network connections, heavily congested networks, changing
-temperatures (ordinary computer clocks are sensitive to temperature),
-and systems that do not run continuosly, or run on a virtual machine.
-
-Typical accuracy between two machines synchronised over the Internet is
-within a few milliseconds; on a LAN, accuracy is typically in tens of
-microseconds.  With hardware timestamping, or a hardware reference clock,
-sub-microsecond accuracy may be possible.
-
-Two programs are included in chrony, chronyd is a daemon that can be
-started at boot time and chronyc is a command-line interface program
-which can be used to monitor chronyd's performance and to change various
-operating parameters whilst it is running.
-
-What will chrony run on?
-========================
-
-The software is known to work on Linux, FreeBSD, NetBSD, macOS and
-Solaris.  Closely related systems may work too.  Any other system will
-likely require a porting exercise.  You would need to start from one
-of the existing system-specific drivers and look into the quirks of
-certain system calls and the kernel on your target system.
-
-How do I set it up?
-===================
-
-The file INSTALL gives instructions.  On supported systems the
-compilation process should be automatic.  You will need a C compiler,
-e.g. gcc or clang.
-
-What documentation is there?
-============================
-
-The distribution includes manual pages and a document containing
-Frequently Asked Questions (FAQ).
-
-The documentation is also available on the chrony web pages, accessible
-through the URL 
-
-    https://chrony.tuxfamily.org/
-
-Where are new versions announced?
-=================================
-
-There is a low volume mailing list where new versions and other
-important news relating to chrony is announced.  You can join this list
-by sending mail with the subject "subscribe" to
-
-chrony-announce-request@chrony.tuxfamily.org
-
-These messages will be copied to chrony-users (see below).
-
-How can I get support for chrony?
-and where can I discuss new features, possible bugs etc?
-========================================================
-
-There are 3 mailing lists relating to chrony.  chrony-announce was
-mentioned above.  chrony-users is a users' discussion list, e.g. for
-general questions and answers about using chrony.  chrony-dev is a more
-technical list, e.g. for discussing how new features should be
-implemented, exchange of information between developers etc.  To
-subscribe to either of these lists, send a message with the subject
-"subscribe" to
-
-chrony-users-request@chrony.tuxfamily.org
-or
-chrony-dev-request@chrony.tuxfamily.org
-
-as applicable.
-
-When you are reporting a bug, please send us all the information you can.
-Unfortunately, chrony has proven to be one of those programs where it is very
-difficult to reproduce bugs in a different environment. So we may have to
-interact with you quite a lot to obtain enough extra logging and tracing to
-pin-point the problem in some cases. Please be patient and plan for this!
-
-License
-=======
-
-chrony is distributed under the GNU General Public License version 2.
-
-Authors
-=======
-
-Richard P. Curnow <rc@rc0.org.uk>
-Miroslav Lichvar <mlichvar@redhat.com>
-
-Acknowledgements
-================
-
-In writing the chronyd program, extensive use has been made of RFC 1305
-and RFC 5905, written by David Mills. The source code of the NTP reference
-implementation has been used to check the details of the protocol.
-
-The following people have provided patches and other major contributions
-to the program :
-
-Lonnie Abelbeck <lonnie@abelbeck.com>
-    Patch to add tab-completion to chronyc
-
-Benny Lyne Amorsen <benny@amorsen.dk>
-    Patch to add minstratum option
-
-Andrew Bishop <amb@gedanken.demon.co.uk>
-    Fixes for bugs in logging when in daemon mode
-    Fixes for compiler warnings
-    Robustness improvements for drift file
-    Improve installation (directory checking etc)
-    Entries in contrib directory
-    Improvements to 'sources' and 'sourcestats' output from chronyc
-    Improvements to documentation
-    Investigation of required dosynctodr behaviour for various Solaris
-      versions
-
-Stephan I. Boettcher <stephan@nevis1.columbia.edu>
-    Entries in contrib directory
-
-Erik Bryer <ebryer@spots.ab.ca>
-    Entries in contrib directory
-
-Bryan Christianson <bryan@whatroute.net>
-    Support for macOS
-    Support for privilege separation
-    Entries in contrib directory
-
-Juliusz Chroboczek <jch@pps.jussieu.fr>
-    Patch to fix install rule in Makefile if chronyd file is in use
-
-Christian Ehrhardt <christian.ehrhardt@canonical.com>
-    Patch to generate a warning message when CAP_SYS_TIME is missing
-
-Paul Elliott <pelliott@io.com>
-    Entries in contrib directory
-
-Mike Fleetwood <mike@rockover.demon.co.uk>
-    Fixes for compiler warnings
-
-Alexander Gretencord <arutha@gmx.de>
-    Changes to installation directory system to make it easier for
-    package builders
-
-Andrew Griffiths <agriffit@redhat.com>
-    Patch to add support for seccomp filter
-
-Walter Haidinger <walter.haidinger@gmx.at>
-    Access to a Linux installation where v1.12 wouldn't compile
-    Disc space for an independent backup of the sources
-
-Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
-    Port to NetBSD
-
-John Hasler <john@dhh.gt.org>
-    Project and website at tuxfamily.org
-    Changes to support 64 bit machines (i.e. those where
-      sizeof(unsigned long) > 4)
-    Bug fix to initstepslew directive
-    Fix to remove potential buffer overrun errors
-    Memory locking and real-time scheduler support
-    Fix fault where chronyd enters an endless loop
-
-Tjalling Hattink <t.hattink@fugro.nl>
-    Fix scheduler to allow stepping clock from timeout handler
-    Patch to take leap second in PPS refclock from locked source
-    Patch to make reading of RTC for initial trim more reliable
-
-Liam Hatton <me@liamhatton.com>
-    Advice on configuring for Linux on PPC
-
-Jachym Holecek <jakym@volny.cz>
-    Patch to make Linux real time clock work with devfs
-
-Håkan Johansson <f96hajo@chalmers.se>
-    Patch to avoid large values in sources and sourcestats output
-
-Jim Knoble <jmknoble@pobox.com>
-    Fixes for compiler warnings
-
-Antti Jrvinen <costello@iki.fi>
-    Advice on configuring for BSD/386
-
-Victor Moroz <vim@prv.adlum.ru>
-    Patch to support Linux with HZ!=100
-
-Kalle Olavi Niemitalo  <tosi@stekt.oulu.fi>
-    Patch to add acquisitionport directive
-
-Frank Otto <sandwichmacher@web.de>
-    Handling arbitrary HZ values
-
-Denny Page <dennypage@me.com>
-    Advice on support for hardware timestamping
-
-Chris Perl <cperl@janestreet.com>
-    Patches to improve support for refclocks keeping time in TAI
-
-Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
-    Patch to add refresh command to chronyc
-
-Andreas Piesk <apiesk@virbus.de>
-    Patch to make chronyc use the readline library if available
-
-Andreas Steinmetz <ast@domdv.de>
-    Patch to make stratum of refclocks configurable
-
-Timo Teras <timo.teras@iki.fi>
-    Patch to reply correctly on multihomed hosts
-
-Bill Unruh <unruh@physics.ubc.ca>
-    Advice on statistics
-
-Stephen Wadeley <swadeley@redhat.com>
-    Improvements to man pages
-
-Wolfgang Weisselberg <weissel@netcologne.de>
-    Entries in contrib directory
-
-Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
-    Many robustness and security improvements
-    
-Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
-    Information about the Linux 2.2 kernel functionality compared to 2.0
-
-Doug Woodward <dougw@whistler.com>
-    Advice on configuring for Solaris 2.8 on x86
-
-Many other people have contributed bug reports and suggestions.  We are sorry
-we cannot identify all of you individually.
diff --git a/chrony_3_3/clientlog.c b/chrony_3_3/clientlog.c
deleted file mode 100644
index 86962a7..0000000
--- a/chrony_3_3/clientlog.c
+++ /dev/null
@@ -1,695 +0,0 @@
-/*
-  chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2009, 2015-2017
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
-
-  =======================================================================
-
-  This module keeps a count of the number of successful accesses by
-  clients, and the times of the last accesses.
-
-  This can be used for status reporting, and (in the case of a
-  server), if it needs to know which clients have made use of its data
-  recently.
-
-  */
-
-#include "config.h"
-
-#include "sysincl.h"
-
-#include "array.h"
-#include "clientlog.h"
-#include "conf.h"
-#include "memory.h"
-#include "ntp.h"
-#include "reports.h"
-#include "util.h"
-#include "logging.h"
-
-typedef struct {
-  IPAddr ip_addr;
-  uint32_t last_ntp_hit;
-  uint32_t last_cmd_hit;
-  uint32_t ntp_hits;
-  uint32_t cmd_hits;
-  uint16_t ntp_drops;
-  uint16_t cmd_drops;
-  uint16_t ntp_tokens;
-  uint16_t cmd_tokens;
-  int8_t ntp_rate;
-  int8_t cmd_rate;
-  int8_t ntp_timeout_rate;
-  uint8_t flags;
-  NTP_int64 ntp_rx_ts;
-  NTP_int64 ntp_tx_ts;
-} Record;
-
-/* Hash table of records, there is a fixed number of records per slot */
-static ARR_Instance records;
-
-#define SLOT_BITS 4
-
-/* Number of records in one slot of the hash table */
-#define SLOT_SIZE (1U << SLOT_BITS)
-
-/* Minimum number of slots */
-#define MIN_SLOTS 1
-
-/* Maximum number of slots, this is a hard limit */
-#define MAX_SLOTS (1U << (24 - SLOT_BITS))
-
-/* Number of slots in the hash table */
-static unsigned int slots;
-
-/* Maximum number of slots given memory allocation limit */
-static unsigned int max_slots;
-
-/* Times of last hits are saved as 32-bit fixed point values */
-#define TS_FRAC 4
-#define INVALID_TS 0
-
-/* Static offset included in conversion to the fixed-point timestamps to
-   randomise their alignment */
-static uint32_t ts_offset;
-
-/* Request rates are saved in the record as 8-bit scaled log2 values */
-#define RATE_SCALE 4
-#define MIN_RATE (-14 * RATE_SCALE)
-#define INVALID_RATE -128
-
-/* Response rates are controlled by token buckets.  The capacity and
-   number of tokens spent on response are determined from configured
-   minimum inverval between responses (in log2) and burst length. */
-
-#define MIN_LIMIT_INTERVAL (-15 - TS_FRAC)
-#define MAX_LIMIT_INTERVAL 12
-#define MIN_LIMIT_BURST 1
-#define MAX_LIMIT_BURST 255
-
-static uint16_t max_ntp_tokens;
-static uint16_t max_cmd_tokens;
-static uint16_t ntp_tokens_per_packet;
-static uint16_t cmd_tokens_per_packet;
-
-/* Reduction of token rates to avoid overflow of 16-bit counters.  Negative
-   shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
-static int ntp_token_shift;
-static int cmd_token_shift;
-
-/* Rates at which responses are randomly allowed (in log2) when the
-   buckets don't have enough tokens.  This is necessary in order to
-   prevent an attacker sending requests with spoofed source address
-   from blocking responses to the address completely. */
-
-#define MIN_LEAK_RATE 1
-#define MAX_LEAK_RATE 4
-
-static int ntp_leak_rate;
-static int cmd_leak_rate;
-
-/* Flag indicating whether the last response was dropped */
-#define FLAG_NTP_DROPPED 0x1
-
-/* NTP limit interval in log2 */
-static int ntp_limit_interval;
-
-/* Flag indicating whether facility is turned on or not */
-static int active;
-
-/* Global statistics */
-static uint32_t total_ntp_hits;
-static uint32_t total_cmd_hits;
-static uint32_t total_ntp_drops;
-static uint32_t total_cmd_drops;
-static uint32_t total_record_drops;
-
-#define NSEC_PER_SEC 1000000000U
-
-/* ================================================== */
-
-static int expand_hashtable(void);
-
-/* ================================================== */
-
-static int
-compare_ts(uint32_t x, uint32_t y)
-{
-  if (x == y)
-    return 0;
-  if (y == INVALID_TS)
-    return 1;
-  return (int32_t)(x - y) > 0 ? 1 : -1;
-}
-
-/* ================================================== */
-
-static Record *
-get_record(IPAddr *ip)
-{
-  unsigned int first, i;
-  time_t last_hit, oldest_hit = 0;
-  Record *record, *oldest_record;
-
-  if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
-    return NULL;
-
-  while (1) {
-    /* Get index of the first record in the slot */
-    first = UTI_IPToHash(ip) % slots * SLOT_SIZE;
-
-    for (i = 0, oldest_record = NULL; i < SLOT_SIZE; i++) {
-      record = ARR_GetElement(records, first + i);
-
-      if (!UTI_CompareIPs(ip, &record->ip_addr, NULL))
-        return record;
-
-      if (record->ip_addr.family == IPADDR_UNSPEC)
-        break;
-
-      last_hit = compare_ts(record->last_ntp_hit, record->last_cmd_hit) > 0 ?
-                 record->last_ntp_hit : record->last_cmd_hit;
-
-      if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 ||
-          (oldest_hit == last_hit && record->ntp_hits + record->cmd_hits <
-           oldest_record->ntp_hits + oldest_record->cmd_hits)) {
-        oldest_record = record;
-        oldest_hit = last_hit;
-      }
-    }
-
-    /* If the slot still has an empty record, use it */
-    if (record->ip_addr.family == IPADDR_UNSPEC)
-      break;
-
-    /* Resize the table if possible and try again as the new slot may
-       have some empty records */
-    if (expand_hashtable())
-      continue;
-
-    /* There is no other option, replace the oldest record */
-    record = oldest_record;
-    total_record_drops++;
-    break;
-  }
-
-  record->ip_addr = *ip;
-  record->last_ntp_hit = record->last_cmd_hit = INVALID_TS;
-  record->ntp_hits = record->cmd_hits = 0;
-  record->ntp_drops = record->cmd_drops = 0;
-  record->ntp_tokens = max_ntp_tokens;
-  record->cmd_tokens = max_cmd_tokens;
-  record->ntp_rate = record->cmd_rate = INVALID_RATE;
-  record->ntp_timeout_rate = INVALID_RATE;
-  record->flags = 0;
-  UTI_ZeroNtp64(&record->ntp_rx_ts);
-  UTI_ZeroNtp64(&record->ntp_tx_ts);
-
-  return record;
-}
-
-/* ================================================== */
-
-static int
-expand_hashtable(void)
-{
-  ARR_Instance old_records;
-  Record *old_record, *new_record;
-  unsigned int i;
-
-  old_records = records;
-
-  if (2 * slots > max_slots)
-    return 0;
-
-  records = ARR_CreateInstance(sizeof (Record));
-
-  slots = MAX(MIN_SLOTS, 2 * slots);
-  assert(slots <= max_slots);
-
-  ARR_SetSize(records, slots * SLOT_SIZE);
-
-  /* Mark all new records as empty */
-  for (i = 0; i < slots * SLOT_SIZE; i++) {
-    new_record = ARR_GetElement(records, i);
-    new_record->ip_addr.family = IPADDR_UNSPEC;
-  }
-
-  if (!old_records)
-    return 1;
-
-  /* Copy old records to the new hash table */
-  for (i = 0; i < ARR_GetSize(old_records); i++) {
-    old_record = ARR_GetElement(old_records, i);
-    if (old_record->ip_addr.family == IPADDR_UNSPEC)
-      continue;
-
-    new_record = get_record(&old_record->ip_addr);
-
-    assert(new_record);
-    *new_record = *old_record;
-  }
-
-  ARR_DestroyInstance(old_records);
-
-  return 1;
-}
-
-/* ================================================== */
-
-static void
-set_bucket_params(int interval, int burst, uint16_t *max_tokens,
-                  uint16_t *tokens_per_packet, int *token_shift)
-{
-  interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
-  burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST);
-
-  if (interval >= -TS_FRAC) {
-    /* Find the smallest shift with which the maximum number fits in 16 bits */
-    for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
-      if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
-        break;
-    }
-  } else {
-    /* Coarse rate limiting */
-    *token_shift = interval + TS_FRAC;
-    *tokens_per_packet = 1;
-    burst = MAX(1U << -*token_shift, burst);
-  }
-
-  *tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
-  *max_tokens = *tokens_per_packet * burst;
-
-  DEBUG_LOG("Tokens max %d packet %d shift %d",
-            *max_tokens, *tokens_per_packet, *token_shift);
-}
-
-/* ================================================== */
-
-void
-CLG_Initialise(void)
-{
-  int interval, burst, leak_rate;
-
-  max_ntp_tokens = max_cmd_tokens = 0;
-  ntp_tokens_per_packet = cmd_tokens_per_packet = 0;
-  ntp_token_shift = cmd_token_shift = 0;
-  ntp_leak_rate = cmd_leak_rate = 0;
-  ntp_limit_interval = MIN_LIMIT_INTERVAL;
-
-  if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) {
-    set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
-                      &ntp_token_shift);
-    ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
-    ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
-  }
-
-  if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) {
-    set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet,
-                      &cmd_token_shift);
-    cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
-  }
-
-  active = !CNF_GetNoClientLog();
-  if (!active) {
-    if (ntp_leak_rate || cmd_leak_rate)
-      LOG_FATAL("ratelimit cannot be used with noclientlog");
-    return;
-  }
-
-  /* Calculate the maximum number of slots that can be allocated in the
-     configured memory limit.  Take into account expanding of the hash
-     table where two copies exist at the same time. */
-  max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2);
-  max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
-
-  slots = 0;
-  records = NULL;
-
-  expand_hashtable();
-
-  UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
-  ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
-}
-
-/* ================================================== */
-
-void
-CLG_Finalise(void)
-{
-  if (!active)
-    return;
-
-  ARR_DestroyInstance(records);
-}
-
-/* ================================================== */
-
-static uint32_t
-get_ts_from_timespec(struct timespec *ts)
-{
-  uint32_t sec = ts->tv_sec, nsec = ts->tv_nsec;
-
-  nsec += ts_offset;
-  if (nsec >= NSEC_PER_SEC) {
-    nsec -= NSEC_PER_SEC;
-    sec++;
-  }
-
-  /* This is fast and accurate enough */
-  return sec << TS_FRAC | (140740U * (nsec >> 15)) >> (32 - TS_FRAC);
-}
-
-/* ================================================== */
-
-static void
-update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
-              uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
-{
-  uint32_t interval, now_ts, prev_hit, new_tokens;
-  int interval2;
-
-  now_ts = get_ts_from_timespec(now);
-
-  prev_hit = *last_hit;
-  *last_hit = now_ts;
-  (*hits)++;
-
-  interval = now_ts - prev_hit;
-
-  if (prev_hit == INVALID_TS || (int32_t)interval < 0)
-    return;
-
-  if (token_shift >= 0)
-    new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift);
-  else if (now_ts - prev_hit > max_tokens)
-    new_tokens = max_tokens;
-  else
-    new_tokens = (now_ts - prev_hit) << -token_shift;
-  *tokens = MIN(*tokens + new_tokens, max_tokens);
-
-  /* Convert the interval to scaled and rounded log2 */
-  if (interval) {
-    interval += interval >> 1;
-    for (interval2 = -RATE_SCALE * TS_FRAC; interval2 < -MIN_RATE;
-         interval2 += RATE_SCALE) {
-      if (interval <= 1)
-        break;
-      interval >>= 1;
-    }
-  } else {
-    interval2 = -RATE_SCALE * (TS_FRAC + 1);
-  }
-
-  /* Update the rate in a rough approximation of exponential moving average */
-  if (*rate == INVALID_RATE) {
-    *rate = -interval2;
-  } else {
-    if (*rate < -interval2) {
-      (*rate)++;
-    } else if (*rate > -interval2) {
-      if (*rate > RATE_SCALE * 5 / 2 - interval2)
-        *rate = RATE_SCALE * 5 / 2 - interval2;
-      else
-        *rate = (*rate - interval2 - 1) / 2;
-    }
-  }
-}
-
-/* ================================================== */
-
-static int
-get_index(Record *record)
-{
-  return record - (Record *)ARR_GetElements(records);
-}
-
-/* ================================================== */
-
-int
-CLG_GetClientIndex(IPAddr *client)
-{
-  Record *record;
-
-  record = get_record(client);
-  if (record == NULL)
-    return -1;
-
-  return get_index(record);
-}
-
-/* ================================================== */
-
-int
-CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
-{
-  Record *record;
-
-  total_ntp_hits++;
-
-  record = get_record(client);
-  if (record == NULL)
-    return -1;
-
-  /* Update one of the two rates depending on whether the previous request
-     of the client had a reply or it timed out */
-  update_record(now, &record->last_ntp_hit, &record->ntp_hits,
-                &record->ntp_tokens, max_ntp_tokens, ntp_token_shift,
-                record->flags & FLAG_NTP_DROPPED ?
-                &record->ntp_timeout_rate : &record->ntp_rate);
-
-  DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d",
-            record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
-            record->ntp_tokens);
-
-  return get_index(record);
-}
-
-/* ================================================== */
-
-int
-CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
-{
-  Record *record;
-
-  total_cmd_hits++;
-
-  record = get_record(client);
-  if (record == NULL)
-    return -1;
-
-  update_record(now, &record->last_cmd_hit, &record->cmd_hits,
-                &record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
-                &record->cmd_rate);
-
-  DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d",
-            record->cmd_hits, record->cmd_rate, record->cmd_tokens);
-
-  return get_index(record);
-}
-
-/* ================================================== */
-
-static int
-limit_response_random(int leak_rate)
-{
-  static uint32_t rnd;
-  static int bits_left = 0;
-  int r;
-
-  if (bits_left < leak_rate) {
-    UTI_GetRandomBytes(&rnd, sizeof (rnd));
-    bits_left = 8 * sizeof (rnd);
-  }
-
-  /* Return zero on average once per 2^leak_rate */
-  r = rnd % (1U << leak_rate) ? 1 : 0;
-  rnd >>= leak_rate;
-  bits_left -= leak_rate;
-
-  return r;
-}
-
-/* ================================================== */
-
-int
-CLG_LimitNTPResponseRate(int index)
-{
-  Record *record;
-  int drop;
-
-  if (!ntp_tokens_per_packet)
-    return 0;
-
-  record = ARR_GetElement(records, index);
-  record->flags &= ~FLAG_NTP_DROPPED;
-
-  if (record->ntp_tokens >= ntp_tokens_per_packet) {
-    record->ntp_tokens -= ntp_tokens_per_packet;
-    return 0;
-  }
-
-  drop = limit_response_random(ntp_leak_rate);
-
-  /* Poorly implemented clients may send new requests at even a higher rate
-     when they are not getting replies.  If the request rate seems to be more
-     than twice as much as when replies are sent, give up on rate limiting to
-     reduce the amount of traffic.  Invert the sense of the leak to respond to
-     most of the requests, but still keep the estimated rate updated. */
-  if (record->ntp_timeout_rate != INVALID_RATE &&
-      record->ntp_timeout_rate > record->ntp_rate + RATE_SCALE)
-    drop = !drop;
-
-  if (!drop) {
-    record->ntp_tokens = 0;
-    return 0;
-  }
-
-  record->flags |= FLAG_NTP_DROPPED;
-  record->ntp_drops++;
-  total_ntp_drops++;
-
-  return 1;
-}
-
-/* ================================================== */
-
-int
-CLG_LimitCommandResponseRate(int index)
-{
-  Record *record;
-
-  if (!cmd_tokens_per_packet)
-    return 0;
-
-  record = ARR_GetElement(records, index);
-
-  if (record->cmd_tokens >= cmd_tokens_per_packet) {
-    record->cmd_tokens -= cmd_tokens_per_packet;
-    return 0;
-  }
-
-  if (!limit_response_random(cmd_leak_rate)) {
-    record->cmd_tokens = 0;
-    return 0;
-  }
-
-  record->cmd_drops++;
-  total_cmd_drops++;
-
-  return 1;
-}
-
-/* ================================================== */
-
-void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
-{
-  Record *record;
-
-  record = ARR_GetElement(records, index);
-
-  *rx_ts = &record->ntp_rx_ts;
-  *tx_ts = &record->ntp_tx_ts;
-}
-
-/* ================================================== */
-
-int
-CLG_GetNtpMinPoll(void)
-{
-  return ntp_limit_interval;
-}
-
-/* ================================================== */
-
-int
-CLG_GetNumberOfIndices(void)
-{
-  if (!active)
-    return -1;
-
-  return ARR_GetSize(records);
-}
-
-/* ================================================== */
-
-static int get_interval(int rate)
-{
-  if (rate == INVALID_RATE)
-    return 127;
-
-  rate += rate > 0 ? RATE_SCALE / 2 : -RATE_SCALE / 2;
-
-  return rate / -RATE_SCALE;
-}
-
-/* ================================================== */
-
-static uint32_t get_last_ago(uint32_t x, uint32_t y)
-{
-  if (y == INVALID_TS || (int32_t)(x - y) < 0)
-    return -1;
-
-  return (x - y) >> TS_FRAC;
-}
-
-/* ================================================== */
-
-int
-CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now)
-{
-  Record *record;
-  uint32_t now_ts;
-
-  if (!active || index < 0 || index >= ARR_GetSize(records))
-    return 0;
-
-  record = ARR_GetElement(records, index);
-
-  if (record->ip_addr.family == IPADDR_UNSPEC)
-    return 0;
-
-  now_ts = get_ts_from_timespec(now);
-
-  report->ip_addr = record->ip_addr;
-  report->ntp_hits = record->ntp_hits;
-  report->cmd_hits = record->cmd_hits;
-  report->ntp_drops = record->ntp_drops;
-  report->cmd_drops = record->cmd_drops;
-  report->ntp_interval = get_interval(record->ntp_rate);
-  report->cmd_interval = get_interval(record->cmd_rate);
-  report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
-  report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_ntp_hit);
-  report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_cmd_hit);
-
-  return 1;
-}
-
-/* ================================================== */
-
-void
-CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
-{
-  report->ntp_hits = total_ntp_hits;
-  report->cmd_hits = total_cmd_hits;
-  report->ntp_drops = total_ntp_drops;
-  report->cmd_drops = total_cmd_drops;
-  report->log_drops = total_record_drops;
-}
diff --git a/chrony_3_3/config.log b/chrony_3_3/config.log
deleted file mode 100644
index 22380e4..0000000
--- a/chrony_3_3/config.log
+++ /dev/null
@@ -1,303 +0,0 @@
-docheck.c:
-#include "config.h"
-int main(int argc, char **argv) {
-
-return 0; }
-gcc -o docheck docheck.c
-
-docheck.c:
-#include "config.h"
-int main(int argc, char **argv) {
-
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-int main(int argc, char **argv) {
-
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <time.h>
-int main(int argc, char **argv) {
-
-  char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
-  return x[0];
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <math.h>
-int main(int argc, char **argv) {
-return (int) pow(2.0, log(sqrt((double)argc)));
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-/tmp/ccDkvCtP.o: In function `main':
-/google/src/cloud/ctalbott/chrony/google3/third_party/chrony/chrony-3.3/docheck.c:4: undefined reference to `log'
-/google/src/cloud/ctalbott/chrony/google3/third_party/chrony/chrony-3.3/docheck.c:4: undefined reference to `pow'
-/google/src/cloud/ctalbott/chrony/google3/third_party/chrony/chrony-3.3/docheck.c:4: undefined reference to `sqrt'
-collect2: error: ld returned 1 exit status
-
-docheck.c:
-#include "config.h"
-#include <math.h>
-int main(int argc, char **argv) {
-return (int) pow(2.0, log(sqrt((double)argc)));
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -lm -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <stdint.h>
-int main(int argc, char **argv) {
-
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <inttypes.h>
-int main(int argc, char **argv) {
-
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/socket.h>
-#include <netinet/in.h>
-int main(int argc, char **argv) {
-
-  struct in_pktinfo ipi;
-  return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-int main(int argc, char **argv) {
-
-    struct sockaddr_in6 n;
-    char p[100];
-    n.sin6_addr = in6addr_any;
-    return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/socket.h>
-#include <netinet/in.h>
-int main(int argc, char **argv) {
-
-    return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-docheck.c: In function 'main':
-docheck.c:6:20: error: invalid application of 'sizeof' to incomplete type 'struct in6_pktinfo'
-     return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;
-                    ^~~~~~
-
-docheck.c:
-#include "config.h"
-#include <sys/socket.h>
-#include <netinet/in.h>
-int main(int argc, char **argv) {
-return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -D_GNU_SOURCE -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <time.h>
-int main(int argc, char **argv) {
-clock_gettime(CLOCK_REALTIME, NULL);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-int main(int argc, char **argv) {
-return getaddrinfo(0, 0, 0, 0);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <pthread.h>
-int main(int argc, char **argv) {
-return pthread_create((void *)1, NULL, (void *)1, NULL);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <stdlib.h>
-int main(int argc, char **argv) {
-arc4random_buf(NULL, 0);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-docheck.c: In function 'main':
-docheck.c:4:1: warning: implicit declaration of function 'arc4random_buf'; did you mean 'srandom_r'? [-Wimplicit-function-declaration]
- arc4random_buf(NULL, 0);
- ^~~~~~~~~~~~~~
- srandom_r
-/tmp/ccgWPyl2.o: In function `main':
-/google/src/cloud/ctalbott/chrony/google3/third_party/chrony/chrony-3.3/docheck.c:4: undefined reference to `arc4random_buf'
-collect2: error: ld returned 1 exit status
-
-docheck.c:
-#include "config.h"
-#include <stdlib.h>
-#include <sys/random.h>
-int main(int argc, char **argv) {
-return getrandom(NULL, 256, 0);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-docheck.c:3:10: fatal error: sys/random.h: No such file or directory
- #include <sys/random.h>
-          ^~~~~~~~~~~~~~
-compilation terminated.
-
-docheck.c:
-#include "config.h"
-#include <sys/socket.h>
-int main(int argc, char **argv) {
-
-  struct mmsghdr hdr;
-  return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/net_tstamp.h>
-#include <linux/errqueue.h>
-#include <linux/ptp_clock.h>
-int main(int argc, char **argv) {
-
-    int val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE |
-              SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG;
-    return sizeof (struct scm_timestamping) + SCM_TSTAMP_SND + PTP_SYS_OFFSET +
-           setsockopt(0, SOL_SOCKET, SO_SELECT_ERR_QUEUE + SO_TIMESTAMPING,
-                      &val, sizeof (val));
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/net_tstamp.h>
-int main(int argc, char **argv) {
-
-    struct scm_ts_pktinfo pktinfo;
-    pktinfo.if_index = pktinfo.pkt_length = 0;
-    return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
-           SCM_TIMESTAMPING_PKTINFO +
-           SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/timepps.h>
-int main(int argc, char **argv) {
-
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-docheck.c:2:10: fatal error: sys/timepps.h: No such file or directory
- #include <sys/timepps.h>
-          ^~~~~~~~~~~~~~~
-compilation terminated.
-
-docheck.c:
-#include "config.h"
-#include <timepps.h>
-int main(int argc, char **argv) {
-
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-docheck.c:2:10: fatal error: timepps.h: No such file or directory
- #include <timepps.h>
-          ^~~~~~~~~~~
-compilation terminated.
-
-docheck.c:
-#include "config.h"
-#include <sys/ioctl.h>
-#include <linux/rtc.h>
-int main(int argc, char **argv) {
-ioctl(1, RTC_UIE_ON&RTC_UIE_OFF&RTC_RD_TIME&RTC_SET_TIME, 0&RTC_UF);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/ioctl.h>
-#include <linux/ptp_clock.h>
-int main(int argc, char **argv) {
-ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sched.h>
-int main(int argc, char **argv) {
-
-     struct sched_param sched;
-     sched_get_priority_max(SCHED_FIFO);
-     sched_setscheduler(0, SCHED_FIFO, &sched);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <sys/mman.h>
-#include <sys/resource.h>
-int main(int argc, char **argv) {
-
-     struct rlimit rlim;
-     setrlimit(RLIMIT_MEMLOCK, &rlim);
-     mlockall(MCL_CURRENT|MCL_FUTURE);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-
-docheck.c:
-#include "config.h"
-#include <nettle/nettle-meta.h>
-#include <nettle/sha2.h>
-int main(int argc, char **argv) {
-return nettle_hashes[0]->context_size;
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -o docheck docheck.c -pie -Wl,-z,relro,-z,now
-docheck.c:2:10: fatal error: nettle/nettle-meta.h: No such file or directory
- #include <nettle/nettle-meta.h>
-          ^~~~~~~~~~~~~~~~~~~~~~
-compilation terminated.
-
-docheck.c:
-#include "config.h"
-#include <tomcrypt.h>
-int main(int argc, char **argv) {
-hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);
-return 0; }
-gcc -O2 -g -D_FORTIFY_SOURCE=2 -fPIE -fstack-protector-strong --param=ssp-buffer-size=4 -Wmissing-prototypes -Wall -pthread -I/usr/include/tomcrypt -o docheck docheck.c -ltomcrypt -pie -Wl,-z,relro,-z,now
-docheck.c:2:10: fatal error: tomcrypt.h: No such file or directory
- #include <tomcrypt.h>
-          ^~~~~~~~~~~~
-compilation terminated.
-
diff --git a/chrony_3_3/doc/chronyd.man.in b/chrony_3_3/doc/chronyd.man.in
deleted file mode 100644
index ab3acf8..0000000
--- a/chrony_3_3/doc/chronyd.man.in
+++ /dev/null
@@ -1,211 +0,0 @@
-'\" t
-.\"     Title: chronyd
-.\"    Author: [see the "AUTHORS" section]
-.\" Generator: Asciidoctor 1.5.6.1
-.\"      Date: 2018-04-04
-.\"    Manual: System Administration
-.\"    Source: chrony @CHRONY_VERSION@
-.\"  Language: English
-.\"
-.TH "CHRONYD" "8" "2018-04-04" "chrony @CHRONY_VERSION@" "System Administration"
-.ie \n(.g .ds Aq \(aq
-.el       .ds Aq '
-.ss \n[.ss] 0
-.nh
-.ad l
-.de URL
-\\$2 \(laURL: \\$1 \(ra\\$3
-..
-.if \n[.g] .mso www.tmac
-.LINKSTYLE blue R < >
-.SH "NAME"
-chronyd \- chrony daemon
-.SH "SYNOPSIS"
-.sp
-\fBchronyd\fP [\fIOPTION\fP]... [\fIDIRECTIVE\fP]...
-.SH "DESCRIPTION"
-.sp
-\fBchronyd\fP is a daemon for synchronisation of the system clock. It can
-synchronise the clock with NTP servers, reference clocks (e.g. a GPS receiver),
-and manual input using wristwatch and keyboard via \fBchronyc\fP. It can also
-operate as an NTPv4 (RFC 5905) server and peer to provide a time service to
-other computers in the network.
-.sp
-If no configuration directives are specified on the command line, \fBchronyd\fP
-will read them from a configuration file. The compiled\-in default location of
-the file is \fI@SYSCONFDIR@/chrony.conf\fP.
-.sp
-Information messages and warnings will be logged to syslog.
-.SH "OPTIONS"
-.sp
-\fB\-4\fP
-.RS 4
-With this option hostnames will be resolved only to IPv4 addresses and only
-IPv4 sockets will be created.
-.RE
-.sp
-\fB\-6\fP
-.RS 4
-With this option hostnames will be resolved only to IPv6 addresses and only
-IPv6 sockets will be created.
-.RE
-.sp
-\fB\-f\fP \fIfile\fP
-.RS 4
-This option can be used to specify an alternate location for the configuration
-file (default \fI@SYSCONFDIR@/chrony.conf\fP).
-.RE
-.sp
-\fB\-n\fP
-.RS 4
-When run in this mode, the program will not detach itself from the terminal.
-.RE
-.sp
-\fB\-d\fP
-.RS 4
-When run in this mode, the program will not detach itself from the terminal,
-and all messages will be written to the terminal instead of syslog. When
-\fBchronyd\fP was compiled with debugging support, this option can be used twice to
-print also debugging messages.
-.RE
-.sp
-\fB\-l\fP \fIfile\fP
-.RS 4
-This option specifies a file which should be used for logging instead of syslog
-or terminal.
-.RE
-.sp
-\fB\-q\fP
-.RS 4
-When run in this mode, \fBchronyd\fP will set the system clock once and exit. It
-will not detach from the terminal.
-.RE
-.sp
-\fB\-Q\fP
-.RS 4
-This option is similar to the \fB\-q\fP option, except it only prints the offset
-without making any corrections of the clock and it allows \fBchronyd\fP to be
-started without root privileges.
-.RE
-.sp
-\fB\-r\fP
-.RS 4
-This option will try to reload and then delete files containing sample
-histories for each of the servers and reference clocks being used. These
-histories are created by using the \fBdump\fP command in
-\fBchronyc\fP, or by setting the \fBdumponexit\fP
-directive in the configuration file. This option is useful if you want to stop
-and restart \fBchronyd\fP briefly for any reason, e.g. to install a new version.
-However, it should be used only on systems where the kernel can maintain clock
-compensation whilst not under \fBchronyd\fP\(cqs control (i.e. Linux, FreeBSD, NetBSD,
-Solaris, and macOS 10.13 or later).
-.RE
-.sp
-\fB\-R\fP
-.RS 4
-When this option is used, the \fBinitstepslew\fP
-directive and the \fBmakestep\fP directive used with
-a positive limit will be ignored. This option is useful when restarting
-\fBchronyd\fP and can be used in conjunction with the \fB\-r\fP option.
-.RE
-.sp
-\fB\-s\fP
-.RS 4
-This option will set the system clock from the computer\(cqs real\-time clock (RTC)
-or to the last modification time of the file specified by the
-\fBdriftfile\fP directive. Real\-time clocks are
-supported only on Linux.
-.sp
-If used in conjunction with the \fB\-r\fP flag, \fBchronyd\fP will attempt to preserve
-the old samples after setting the system clock from the RTC. This can be used
-to allow \fBchronyd\fP to perform long term averaging of the gain or loss rate
-across system reboots, and is useful for systems with intermittent access to
-network that are shut down when not in use. For this to work well, it relies
-on \fBchronyd\fP having been able to determine accurate statistics for the
-difference between the RTC and system clock last time the computer was on.
-.sp
-If the last modification time of the drift file is later than both the current
-time and the RTC time, the system time will be set to it to restore the time
-when \fBchronyd\fP was previously stopped. This is useful on computers that have no
-RTC or the RTC is broken (e.g. it has no battery).
-.RE
-.sp
-\fB\-t\fP \fItimeout\fP
-.RS 4
-This option sets a timeout (in seconds) after which \fBchronyd\fP will exit. If the
-clock is not synchronised, it will exit with a non\-zero status. This is useful
-with the \fB\-q\fP or \fB\-Q\fP option to shorten the maximum time waiting for
-measurements, or with the \fB\-r\fP option to limit the time when \fBchronyd\fP is
-running, but still allow it to adjust the frequency of the system clock.
-.RE
-.sp
-\fB\-u\fP \fIuser\fP
-.RS 4
-This option sets the name of the system user to which \fBchronyd\fP will switch
-after start in order to drop root privileges. It overrides the
-\fBuser\fP directive (default \fI@DEFAULT_USER@\fP).
-.sp
-On Linux, \fBchronyd\fP needs to be compiled with support for the \fBlibcap\fP library.
-On macOS, FreeBSD, NetBSD and Solaris \fBchronyd\fP forks into two processes.
-The child process retains root privileges, but can only perform a very limited
-range of privileged system calls on behalf of the parent.
-.RE
-.sp
-\fB\-F\fP \fIlevel\fP
-.RS 4
-This option configures a system call filter when \fBchronyd\fP is compiled with
-support for the Linux secure computing (seccomp) facility. In level 1 the
-process is killed when a forbidden system call is made, in level \-1 the SIGSYS
-signal is thrown instead and in level 0 the filter is disabled (default 0).
-.sp
-It\(cqs recommended to enable the filter only when it\(cqs known to work on the
-version of the system where \fBchrony\fP is installed as the filter needs to allow
-also system calls made from libraries that \fBchronyd\fP is using (e.g. libc) and
-different versions or implementations of the libraries may make different
-system calls. If the filter is missing some system call, \fBchronyd\fP could be
-killed even in normal operation.
-.RE
-.sp
-\fB\-P\fP \fIpriority\fP
-.RS 4
-On Linux, this option will select the SCHED_FIFO real\-time scheduler at the
-specified priority (which must be between 0 and 100). On macOS, this option
-must have either a value of 0 (the default) to disable the thread time
-constraint policy or 1 for the policy to be enabled. Other systems do not
-support this option.
-.RE
-.sp
-\fB\-m\fP
-.RS 4
-This option will lock \fBchronyd\fP into RAM so that it will never be paged out.
-This mode is only supported on Linux.
-.RE
-.sp
-\fB\-x\fP
-.RS 4
-This option disables the control of the system clock. \fBchronyd\fP will not try to
-make any adjustments of the clock. It will assume the clock is free running and
-still track its offset and frequency relative to the estimated true time. This
-option allows \fBchronyd\fP to run without the capability to adjust or set the
-system clock (e.g. in some containers) in order to operate as an NTP server. It
-is not recommended to run \fBchronyd\fP (with or without \fB\-x\fP) when another process
-is controlling the system clock.
-.RE
-.sp
-\fB\-v\fP
-.RS 4
-With this option \fBchronyd\fP will print version number to the terminal and exit.
-.RE
-.SH "FILES"
-.sp
-\fI@SYSCONFDIR@/chrony.conf\fP
-.SH "SEE ALSO"
-.sp
-\fBchronyc(1)\fP, \fBchrony.conf(5)\fP
-.SH "BUGS"
-.sp
-For instructions on how to report bugs, please visit
-.URL "https://chrony.tuxfamily.org/" "" "."
-.SH "AUTHORS"
-.sp
-chrony was written by Richard Curnow, Miroslav Lichvar, and others.
\ No newline at end of file
diff --git a/chrony_3_3/doc/faq.adoc b/chrony_3_3/doc/faq.adoc
deleted file mode 100644
index 5bad34f..0000000
--- a/chrony_3_3/doc/faq.adoc
+++ /dev/null
@@ -1,459 +0,0 @@
-// This file is part of chrony
-//
-// Copyright (C) Richard P. Curnow  1997-2003
-// Copyright (C) Miroslav Lichvar  2014-2016
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of version 2 of the GNU General Public License as
-// published by the Free Software Foundation.
-//
-// 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, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-= Frequently Asked Questions
-:toc:
-:numbered:
-
-== `chrony` compared to other programs
-
-=== How does `chrony` compare to `ntpd`?
-
-`chronyd` was designed to work well in a wide range of conditions and it can
-usually synchronise the system clock faster and with better time accuracy. It
-doesn't implement some of the less useful NTP modes like broadcast client or
-multicast server/client.
-
-If your computer is connected to the Internet only for few minutes at a time,
-the network connection is often congested, you turn your computer off or
-suspend it frequently, the clock is not very stable (e.g. there are rapid
-changes in the temperature or it's a virtual machine), or you want to use NTP
-on an isolated network with no hardware reference clocks in sight, `chrony`
-will probably work much better for you.
-
-For a more detailed comparison of features and performance, see the
-https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
-website.
-
-== Configuration issues
-
-=== What is the minimum recommended configuration for an NTP client?
-
-First, the client needs to know which NTP servers it should ask for the current
-time. They are specified by the `server` or `pool` directive. The `pool`
-directive can be used for names that resolve to multiple addresses. For good
-reliability the client should have at least three servers. The `iburst` option
-speeds up the initial synchronisation.
-
-To stabilise the initial synchronisation on the next start, the estimated drift
-of the system clock is saved to a file specified by the `driftfile` directive.
-
-If the system clock can be far from the true time after boot for any reason,
-`chronyd` should be allowed to correct it quickly by stepping instead of
-slewing, which would take a very long time. The `makestep` directive does
-that.
-
-In order to keep the real-time clock (RTC) close to the true time, so the
-system time is reasonably close to the true time when it's initialised on the
-next boot from the RTC, the `rtcsync` directive enables a mode in which the
-system time is periodically copied to the RTC. It is supported on Linux and
-macOS.
-
-If you want to use public NTP servers from the
-http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
-could be:
-
-----
-pool pool.ntp.org iburst
-driftfile /var/lib/chrony/drift
-makestep 1 3
-rtcsync
-----
-
-=== How do I make an NTP server from an NTP client?
-
-You need to add an `allow` directive to the _chrony.conf_ file in order to open
-the NTP port and allow `chronyd` to reply to client requests. `allow` with no
-specified subnet allows access from all IPv4 and IPv6 addresses.
-
-=== I have several computers on a LAN. Should be all clients of an external server?
-
-The best configuration is usually to make one computer the server, with
-the others as clients of it. Add a `local` directive to the server's
-_chrony.conf_ file. This configuration will be better because
-
-* the load on the external connection is less
-* the load on the external NTP server(s) is less
-* if your external connection goes down, the computers on the LAN
-  will maintain a common time with each other.
-
-=== Must I specify servers by IP address if DNS is not available on chronyd start?
-
-No. Starting from version 1.25, `chronyd` will keep trying to resolve
-the names specified by the `server`, `pool`, and `peer` directives in an
-increasing interval until it succeeds. The `online` command can be issued from
-`chronyc` to force `chronyd` to try to resolve the names immediately.
-
-=== How can I make `chronyd` more secure?
-
-If you don't need to serve time to NTP clients or peers, you can add `port 0`
-to the _chrony.conf_ file to completely disable the NTP server functionality
-and prevent NTP requests from reaching `chronyd`. Starting from version 2.0,
-the NTP server port is open only when client access is allowed by the `allow`
-directive or command, an NTP peer is configured, or the `broadcast` directive
-is used.
-
-If you don't need to use `chronyc` remotely, you can add the following
-directives to the configuration file to bind the command sockets to the
-loopback interface. This is done by default since version 2.0.
-
-----
-bindcmdaddress 127.0.0.1
-bindcmdaddress ::1
-----
-
-If you don't need to use `chronyc` at all or you need to run `chronyc` only
-under the root or _chrony_ user (which can access `chronyd` through a Unix
-domain socket since version 2.2), you can disable the internet command sockets
-completely by adding `cmdport 0` to the configuration file.
-
-You can specify an unprivileged user with the `-u` option, or the `user`
-directive in the _chrony.conf_ file, to which `chronyd` will switch after start
-in order to drop root privileges. The configure script has a `--with-user`
-option, which sets the default user. On Linux, `chronyd` needs to be compiled
-with support for the `libcap` library. On other systems, `chronyd` forks into
-two processes. The child process retains root privileges, but can only perform
-a very limited range of privileged system calls on behalf of the parent.
-
-Also, if `chronyd` is compiled with support for the Linux secure computing
-(seccomp) facility, you can enable a system call filter with the `-F` option.
-It will significantly reduce the kernel attack surface and possibly prevent
-kernel exploits from the `chronyd` process if it's compromised. It's
-recommended to enable the filter only when it's known to work on the version of
-the system where `chrony` is installed as the filter needs to allow also system
-calls made from libraries that `chronyd` is using (e.g. libc) and different
-versions or implementations of the libraries may make different system calls.
-If the filter is missing some system call, `chronyd` could be killed even in
-normal operation.
-
-=== How can I improve the accuracy of the system clock with NTP sources?
-
-Select NTP servers that are well synchronised, stable and close to your
-network. It's better to use more than one server, three or four is usually
-recommended as the minimum, so `chronyd` can detect servers that serve false
-time and combine measurements from multiple sources.
-
-If you have a network card with hardware timestamping supported on Linux, it
-can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It
-should make local receive and transmit timestamps of NTP packets much more
-accurate.
-
-There are also useful options which can be set in the `server` directive, they
-are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`,
-`maxdelaydevratio`, and `xleave`.
-
-The first three options set the minimum and maximum allowed polling interval,
-and how should be the actual interval adjusted in the specified range. Their
-default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
-`maxpoll` and 8 (samples) for `polltarget`. The default values should be used
-for general servers on the Internet. With your own NTP servers, or if you have
-permission to poll some servers more frequently, setting these options for
-shorter polling intervals may significantly improve the accuracy of the system
-clock.
-
-The optimal polling interval depends mainly on two factors, stability of the
-network latency and stability of the system clock (which mainly depends on the
-temperature sensitivity of the crystal oscillator and the maximum rate of the
-temperature change).
-
-An example of the directive for an NTP server on the Internet that you are
-allowed to poll frequently could be
-
-----
-server foo.example.net minpoll 4 maxpoll 6 polltarget 16
-----
-
-An example using very short polling intervals for a server located in the same
-LAN could be
-
-----
-server ntp.local minpoll 2 maxpoll 4 polltarget 30
-----
-
-The maxdelay options are useful to ignore measurements with larger delay (e.g.
-due to congestion in the network) and improve the stability of the
-synchronisation. The `maxdelaydevratio` option could be added to the example
-with local NTP server
-
-----
-server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
-----
-
-If your server supports the interleaved mode, the `xleave` option should be
-added to the `server` directive in order to allow the server to send the
-client more accurate hardware or kernel transmit timestamps. When combined with
-local hardware timestamping, sub-microsecond accuracy may be possible. An
-example could be
-
-----
-server ntp.local minpoll 2 maxpoll 2 xleave
-hwtimestamp eth0
-----
-
-=== Does `chronyd` have an ntpdate mode?
-
-Yes. With the `-q` option `chronyd` will set the system clock once and exit.
-With the `-Q` option it will print the measured offset without setting the
-clock. If you don't want to use a configuration file, NTP servers can be
-specified on the command line. For example:
-
-----
-# chronyd -q 'pool pool.ntp.org iburst'
-----
-
-=== What happened to the `commandkey` and `generatecommandkey` directives?
-
-They were removed in version 2.2. Authentication is no longer supported in the
-command protocol. Commands that required authentication are now allowed only
-through a Unix domain socket, which is accessible only by the root and _chrony_
-users. If you need to configure `chronyd` remotely or locally without the root
-password, please consider using ssh and/or sudo to run `chronyc` under the root
-or _chrony_ user on the host where `chronyd` is running.
-
-== Computer is not synchronising
-
-This is the most common problem. There are a number of reasons, see the
-following questions.
-
-=== Behind a firewall?
-
-Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it's
-zero, it means `chronyd` did not get any valid responses from the NTP server
-you are trying to use. If there is a firewall between you and the server, the
-packets may be blocked. Try using a tool like `wireshark` or `tcpdump` to see
-if you're getting any responses from the server.
-
-When `chronyd` is receiving responses from the servers, the output of the
-`sources` command issued few minutes after `chronyd` start might look like
-this:
-
-----
-210 Number of sources = 3
-MS Name/IP address         Stratum Poll Reach LastRx Last sample
-===============================================================================
-^* foo.example.net               2   6   377    34   +484us[ -157us] +/-   30ms
-^- bar.example.net               2   6   377    34    +33ms[  +32ms] +/-   47ms
-^+ baz.example.net               3   6   377    35  -1397us[-2033us] +/-   60ms
-----
-
-=== Are NTP servers specified with the `offline` option?
-
-Check that you're using ``chronyc``'s `online` and `offline` commands
-appropriately. The `activity` command prints the number of sources that are
-currently online and offline. For example:
-
-----
-200 OK
-3 sources online
-0 sources offline
-0 sources doing burst (return to online)
-0 sources doing burst (return to offline)
-0 sources with unknown address
-----
-
-=== Is `chronyd` allowed to step the system clock?
-
-By default, `chronyd` adjusts the clock gradually by slowing it down or
-speeding it up. If the clock is too far from the true time, it will take
-a long time to correct the error. The `System time` value printed by the
-``chronyc``'s `tracking` command is the remaining correction that needs to be
-applied to the system clock.
-
-The `makestep` directive can be used to allow `chronyd` to step the clock. For
-example, if _chrony.conf_ had
-
-----
-makestep 1 3
-----
-
-the clock would be stepped in the first three updates if its offset was larger
-than one second. Normally, it's recommended to allow the step only in the first
-few updates, but in some cases (e.g. a computer without an RTC or virtual
-machine which can be suspended and resumed with an incorrect time) it may be
-necessary to allow the step on any clock update. The example above would change
-to
-
-----
-makestep 1 -1
-----
-
-== Issues with `chronyc`
-
-=== I keep getting the error `506 Cannot talk to daemon`
-
-When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on
-the computer where `chronyd` is running) has a `cmdallow` entry for the
-computer you are running `chronyc` on and an appropriate `bindcmdaddress`
-directive. This isn't necessary for localhost.
-
-Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux,
-`ps -auxw`) to see if it's running. Or try `netstat -a` and see if the ports
-123/udp and 323/udp are listening. If `chronyd` is not running, you may have a
-problem with the way you are trying to start it (e.g. at boot time).
-
-Perhaps you have a firewall set up in a way that blocks packets on port
-323/udp. You need to amend the firewall configuration in this case.
-
-=== I keep getting the error `501 Not authorised`
-
-Since version 2.2, the `password` command doesn't do anything and `chronyc`
-needs to run locally under the root or _chrony_ user, which are allowed to
-access the ``chronyd``'s Unix domain command socket.
-
-With older versions, you need to authenticate with the `password` command first
-or use the `-a` option to authenticate automatically on start. The
-configuration file needs to specify a file which contains keys (`keyfile`
-directive) and which key in the key file should be used for `chronyc`
-authentication (`commandkey` directive).
-
-=== Why does `chronyc tracking` always print an IPv4 address as reference ID?
-
-The reference ID is a 32-bit value and in versions before 3.0 it was printed in
-quad-dotted notation, even if the reference source did not actually have an
-IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
-for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
-reference clocks, the reference ID is the value specified with the `refid`
-option in the `refclock` directive.
-
-Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
-confusion with IPv4 addresses.
-
-If you need to get the IP address of the current reference source, use the `-n`
-option to disable resolving of IP addresses and read the second field (printed
-in parentheses) on the `Reference ID` line.
-
-=== Is the `chronyc` / `chronyd` protocol documented anywhere?
-
-Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
-(`chronyc` side).
-
-== Real-time clock issues
-
-=== What is the real-time clock (RTC)?
-
-This is the clock which keeps the time even when your computer is turned off.
-It is used to initialise the system clock on boot. It normally doesn't drift
-more than few seconds per day.
-
-There are two approaches how `chronyd` can work with it. One is to use the
-`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
-the RTC from the system clock every 11 minutes. `chronyd` itself won't touch
-the RTC. If the computer is not turned off for a long time, the RTC should
-still be close to the true time when the system clock will be initialised from
-it on the next boot.
-
-The other option is to use the `rtcfile` directive, which tells `chronyd` to
-monitor the rate at which the RTC gains or loses time. When `chronyd` is
-started with the `-s` option on the next boot, it will set the system time from
-the RTC and also compensate for the drift it has measured previously. The
-`rtcautotrim` directive can be used to keep the RTC close to the true time, but
-it's not strictly necessary if its only purpose is to set the system clock when
-`chronyd` is started on boot. See the documentation for details.
-
-=== I want to use ``chronyd``'s RTC support. Must I disable `hwclock`?
-
-The `hwclock` program is often set-up by default in the boot and shutdown
-scripts with many Linux installations. With the kernel RTC synchronisation
-(`rtcsync` directive), the RTC will be set also every 11 minutes as long as the
-system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring
-(`rtcfile` directive), it's important to disable `hwclock` in the shutdown
-procedure. If you don't, it will over-write the RTC with a new value, unknown
-to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will
-compensate this (wrong) time with its estimate of how far the RTC has drifted
-whilst the power was off, giving a meaningless initial system time.
-
-There is no need to remove `hwclock` from the boot process, as long as `chronyd`
-is started after it has run.
-
-=== I just keep getting the `513 RTC driver not running` message
-
-For the real-time clock support to work, you need the following three
-things
-
-* an RTC in your computer
-* a Linux kernel with enabled RTC support
-* an `rtcfile` directive in your _chrony.conf_ file
-
-=== I get `Could not open /dev/rtc, Device or resource busy` in my syslog file
-
-Some other program running on the system may be using the device.
-
-=== What if my computer does not have an RTC or backup battery?
-
-In this case you can still use the `-s` option to set the system clock to the
-last modification time of the drift file, which should correspond to the system
-time when `chronyd` was previously stopped. The initial system time will be
-increasing across reboots and applications started after `chronyd` will not
-observe backward steps.
-
-== NTP-specific issues
-
-=== Can `chronyd` be driven from broadcast/multicast NTP servers?
-
-No, the broadcast/multicast client mode is not supported and there is currently
-no plan to implement it. While the mode may be useful to simplify configuration
-of clients in large networks, it is inherently less accurate and less secure
-(even with authentication) than the ordinary client/server mode.
-
-When configuring a large number of clients in a network, it is recommended to
-use the `pool` directive with a DNS name which resolves to addresses of
-multiple NTP servers. The clients will automatically replace the servers when
-they become unreachable, or otherwise unsuitable for synchronisation, with new
-servers from the pool.
-
-Even with very modest hardware, an NTP server can serve time to hundreds of
-thousands of clients using the ordinary client/server mode.
-
-=== Can `chronyd` transmit broadcast NTP packets?
-
-Yes, the `broadcast` directive can be used to enable the broadcast server mode
-to serve time to clients in the network which support the broadcast client mode
-(it's not supported in `chronyd`, see the previous question).
-
-=== Can `chronyd` keep the system clock a fixed offset away from real time?
-
-Yes. Starting from version 3.0, an offset can be specified by the `offset`
-option for all time sources in the _chrony.conf_ file.
-
-=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
-
-`chronyd` will keep trying to access the sources that it thinks are online, and
-it will take longer before new measurements are actually made and the clock is
-corrected when the network is connected again. If the sources were set to
-offline, `chronyd` would make new measurements immediately after issuing the
-`online` command.
-
-Unless the network connection lasts only few minutes (less than the maximum
-polling interval), the delay is usually not a problem, and it may be acceptable
-to keep all sources online all the time.
-
-== Operating systems
-
-=== Does `chrony` support Windows?
-
-No. The `chronyc` program (the command-line client used for configuring
-`chronyd` while it is running) has been successfully built and run under
-Cygwin in the past. `chronyd` is not portable, because part of it is
-very system-dependent. It needs adapting to work with Windows'
-equivalent of the adjtimex() call, and it needs to be made to work as a
-service.
-
-=== Are there any plans to support Windows?
-
-We have no plans to do this. Anyone is welcome to pick this work up and
-contribute it back to the project.
diff --git a/chrony_3_3/examples/chrony-wait.service b/chrony_3_3/examples/chrony-wait.service
deleted file mode 100644
index 0f5e2e7..0000000
--- a/chrony_3_3/examples/chrony-wait.service
+++ /dev/null
@@ -1,18 +0,0 @@
-[Unit]
-Description=Wait for chrony to synchronize system clock
-Documentation=man:chronyc(1)
-After=chronyd.service
-Requires=chronyd.service
-Before=time-sync.target
-Wants=time-sync.target
-
-[Service]
-Type=oneshot
-# Wait up to ~10 minutes for chronyd to synchronize and the remaining
-# clock correction to be less than 0.1 seconds
-ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1
-RemainAfterExit=yes
-StandardOutput=null
-
-[Install]
-WantedBy=multi-user.target
diff --git a/chrony_3_3/examples/chrony.keys.example b/chrony_3_3/examples/chrony.keys.example
deleted file mode 100644
index 2dd19cd..0000000
--- a/chrony_3_3/examples/chrony.keys.example
+++ /dev/null
@@ -1,12 +0,0 @@
-# This is an example chrony keys file.  It is used for NTP authentication with
-# symmetric keys.  It should be readable only by root or the user to which
-# chronyd is configured to switch to after start.
-#
-# Don't use the example keys!  It's recommended to generate random keys using
-# the chronyc keygen command.
-
-# Examples of valid keys:
-
-#1 MD5 AVeryLongAndRandomPassword
-#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
-#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
diff --git a/chrony_3_3/examples/chrony.nm-dispatcher b/chrony_3_3/examples/chrony.nm-dispatcher
deleted file mode 100644
index a609a66..0000000
--- a/chrony_3_3/examples/chrony.nm-dispatcher
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
-# online or offline when a network interface is configured or removed
-
-export LC_ALL=C
-
-[ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
-
-# Check if there is a default route
-
-if /sbin/ip route list 2> /dev/null | grep -q '^default'; then
-  chronyc online > /dev/null 2>&1
-  exit 0
-fi
-
-sources=$(chronyc -c -n sources 2> /dev/null)
-
-[ $? -ne 0 ] && exit 0
-
-# Check each configured source if it has a route
-
-echo "$sources" | while IFS=, read mode state address rest; do
-  [ "$mode" != '^' ] && [ "$mode" != '=' ] && continue
-
-  /sbin/ip route get "$address" > /dev/null 2>&1 && command="online" || command="offline"
-
-  # Set priority of sources so that the selected source is set as
-  # last if offline to avoid unnecessary reselection
-  [ "$state" != '*' ] && priority=1 || priority=2
-
-  echo "$priority $command $address"
-
-done | sort | while read priority command address; do
-  echo "$command $address"
-done | chronyc > /dev/null 2>&1
-
-exit 0
diff --git a/chrony_3_3/examples/chrony.spec b/chrony_3_3/examples/chrony.spec
deleted file mode 100644
index 8d03d09..0000000
--- a/chrony_3_3/examples/chrony.spec
+++ /dev/null
@@ -1,46 +0,0 @@
-%global chrony_version 3.3
-%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
-%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
-%endif
-Summary: An NTP client/server
-Name: chrony
-Version: %(echo %{chrony_version} | sed 's/-.*//')
-Release: %{!?prerelease:1}%{?prerelease:0.1.%{prerelease}}
-Source: chrony-%{version}%{?prerelease:-%{prerelease}}.tar.gz
-License: GPLv2
-Group: Applications/Utilities
-BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n)
-
-%description
-chrony is a client and server for the Network Time Protocol (NTP).
-This program keeps your computer's clock accurate. It was specially
-designed to support systems with intermittent Internet connections,
-but it also works well in permanently connected environments. It can
-also use hardware reference clocks, the system real-time clock, or
-manual input as time references.
-
-%prep
-%setup -q -n %{name}-%{version}%{?prerelease:-%{prerelease}}
-
-%build
-./configure \
-	--prefix=%{_prefix} \
-	--bindir=%{_bindir} \
-	--sbindir=%{_sbindir} \
-	--mandir=%{_mandir}
-make
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make install DESTDIR=$RPM_BUILD_ROOT
-
-%files
-%{_sbindir}/chronyd
-%{_bindir}/chronyc
-%{_mandir}/man1/chronyc.1.gz
-%{_mandir}/man5/chrony.conf.5.gz
-%{_mandir}/man8/chronyd.8.gz
-%doc README FAQ NEWS COPYING
-%doc examples/chrony.conf.example*
-%doc examples/chrony.keys.example
-
diff --git a/chrony_3_3/examples/chronyd.service b/chrony_3_3/examples/chronyd.service
deleted file mode 100644
index 4ffe3b1..0000000
--- a/chrony_3_3/examples/chronyd.service
+++ /dev/null
@@ -1,18 +0,0 @@
-[Unit]
-Description=NTP client/server
-Documentation=man:chronyd(8) man:chrony.conf(5)
-After=ntpdate.service sntp.service ntpd.service
-Conflicts=ntpd.service systemd-timesyncd.service
-ConditionCapability=CAP_SYS_TIME
-
-[Service]
-Type=forking
-PIDFile=/var/run/chronyd.pid
-EnvironmentFile=-/etc/sysconfig/chronyd
-ExecStart=/usr/sbin/chronyd $OPTIONS
-PrivateTmp=yes
-ProtectHome=yes
-ProtectSystem=full
-
-[Install]
-WantedBy=multi-user.target
diff --git a/chrony_3_3/hash_tomcrypt.c b/chrony_3_3/hash_tomcrypt.c
deleted file mode 100644
index 5e16233..0000000
--- a/chrony_3_3/hash_tomcrypt.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
-  chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Miroslav Lichvar  2012
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
-
-  =======================================================================
-
-  Routines implementing crypto hashing using tomcrypt library.
-
-  */
-
-#include <tomcrypt.h>
-
-#include "config.h"
-#include "hash.h"
-
-struct hash {
-  const char *name;
-  const char *int_name;
-  const struct ltc_hash_descriptor *desc;
-};
-
-static const struct hash hashes[] = {
-  { "MD5", "md5", &md5_desc },
-#ifdef LTC_RIPEMD128
-  { "RMD128", "rmd128", &rmd128_desc },
-#endif
-#ifdef LTC_RIPEMD160
-  { "RMD160", "rmd160", &rmd160_desc },
-#endif
-#ifdef LTC_RIPEMD256
-  { "RMD256", "rmd256", &rmd256_desc },
-#endif
-#ifdef LTC_RIPEMD320
-  { "RMD320", "rmd320", &rmd320_desc },
-#endif
-#ifdef LTC_SHA1
-  { "SHA1", "sha1", &sha1_desc },
-#endif
-#ifdef LTC_SHA256
-  { "SHA256", "sha256", &sha256_desc },
-#endif
-#ifdef LTC_SHA384
-  { "SHA384", "sha384", &sha384_desc },
-#endif
-#ifdef LTC_SHA512
-  { "SHA512", "sha512", &sha512_desc },
-#endif
-#ifdef LTC_SHA3
-  { "SHA3-224", "sha3-224", &sha3_224_desc },
-  { "SHA3-256", "sha3-256", &sha3_256_desc },
-  { "SHA3-384", "sha3-384", &sha3_384_desc },
-  { "SHA3-512", "sha3-512", &sha3_512_desc },
-#endif
-#ifdef LTC_TIGER
-  { "TIGER", "tiger", &tiger_desc },
-#endif
-#ifdef LTC_WHIRLPOOL
-  { "WHIRLPOOL", "whirlpool", &whirlpool_desc },
-#endif
-  { NULL, NULL, NULL }
-};
-
-int
-HSH_GetHashId(const char *name)
-{
-  int i, h;
-
-  for (i = 0; hashes[i].name; i++) {
-    if (!strcmp(name, hashes[i].name))
-      break;
-  }
-
-  if (!hashes[i].name)
-    return -1; /* not found */
-
-  h = find_hash(hashes[i].int_name);
-  if (h >= 0)
-    return h; /* already registered */
-  
-  /* register and try again */
-  register_hash(hashes[i].desc);
-
-  return find_hash(hashes[i].int_name);
-}
-
-unsigned int
-HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
-    const unsigned char *in2, unsigned int in2_len,
-    unsigned char *out, unsigned int out_len)
-{
-  unsigned long len;
-  int r;
-
-  len = out_len;
-  if (in2)
-    r = hash_memory_multi(id, out, &len,
-        in1, (unsigned long)in1_len, in2, (unsigned long)in2_len, NULL, 0);
-  else
-    r = hash_memory(id, in1, in1_len, out, &len);
-
-  if (r != CRYPT_OK)
-    return 0;
-
-  return len;
-}
-
-void
-HSH_Finalise(void)
-{
-}
diff --git a/chrony_3_3/ntp.h b/chrony_3_3/ntp.h
deleted file mode 100644
index 801f264..0000000
--- a/chrony_3_3/ntp.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-  chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Richard P. Curnow  1997-2003
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
-
-  =======================================================================
-
-  Header file containing common NTP bits and pieces
-  */
-
-#ifndef GOT_NTP_H
-#define GOT_NTP_H
-
-#include "sysincl.h"
-
-#include "hash.h"
-
-typedef struct {
-  uint32_t hi;
-  uint32_t lo;
-} NTP_int64;
-
-typedef uint32_t NTP_int32;
-
-/* The UDP port number used by NTP */
-#define NTP_PORT 123
-
-/* The NTP protocol version that we support */
-#define NTP_VERSION 4
-
-/* Maximum stratum number (infinity) */
-#define NTP_MAX_STRATUM 16
-
-/* The minimum valid length of an extension field */
-#define NTP_MIN_EXTENSION_LENGTH 16
-
-/* The maximum assumed length of all extension fields in received
-   packets (RFC 5905 doesn't specify a limit on length or number of
-   extension fields in one packet) */
-#define NTP_MAX_EXTENSIONS_LENGTH 1024
-
-/* The minimum and maximum supported length of MAC */
-#define NTP_MIN_MAC_LENGTH (4 + 16)
-#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
-
-/* The maximum length of MAC in NTPv4 packets which allows deterministic
-   parsing of extension fields (RFC 7822) */
-#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
-
-/* Type definition for leap bits */
-typedef enum {
-  LEAP_Normal = 0,
-  LEAP_InsertSecond = 1,
-  LEAP_DeleteSecond = 2,
-  LEAP_Unsynchronised = 3
-} NTP_Leap;
-
-typedef enum {
-  MODE_UNDEFINED = 0,
-  MODE_ACTIVE = 1,
-  MODE_PASSIVE = 2,
-  MODE_CLIENT = 3,
-  MODE_SERVER = 4,
-  MODE_BROADCAST = 5
-} NTP_Mode;
-
-typedef struct {
-  uint8_t lvm;
-  uint8_t stratum;
-  int8_t poll;
-  int8_t precision;
-  NTP_int32 root_delay;
-  NTP_int32 root_dispersion;
-  NTP_int32 reference_id;
-  NTP_int64 reference_ts;
-  NTP_int64 originate_ts;
-  NTP_int64 receive_ts;
-  NTP_int64 transmit_ts;
-
-  /* Optional extension fields, we don't send packets with them yet */
-  /* uint8_t extensions[] */
-
-  /* Optional message authentication code (MAC) */
-  NTP_int32 auth_keyid;
-  uint8_t auth_data[NTP_MAX_MAC_LENGTH - 4];
-} NTP_Packet;
-
-#define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid)
-
-/* The buffer used to hold a datagram read from the network */
-typedef struct {
-  NTP_Packet ntp_pkt;
-  uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
-} NTP_Receive_Buffer;
-
-/* Macros to work with the lvm field */
-#define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3)
-#define NTP_LVM_TO_VERSION(lvm) (((lvm) >> 3) & 0x7)
-#define NTP_LVM_TO_MODE(lvm) ((lvm) & 0x7)
-#define NTP_LVM(leap, version, mode) \
-  ((((leap) << 6) & 0xc0) | (((version) << 3) & 0x38) | ((mode) & 0x07))
-
-/* Special NTP reference IDs */
-#define NTP_REFID_UNSYNC 0x0UL
-#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
-#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
-
-#endif /* GOT_NTP_H */
diff --git a/chrony_3_3/ntp_io.c b/chrony_3_3/ntp_io.c
deleted file mode 100644
index 5c8c47a..0000000
--- a/chrony_3_3/ntp_io.c
+++ /dev/null
@@ -1,853 +0,0 @@
-/*
-  chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Timo Teras  2009
- * Copyright (C) Miroslav Lichvar  2009, 2013-2016
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
-
-  =======================================================================
-
-  This file deals with the IO aspects of reading and writing NTP packets
-  */
-
-#include "config.h"
-
-#include "sysincl.h"
-
-#include "array.h"
-#include "ntp_io.h"
-#include "ntp_core.h"
-#include "ntp_sources.h"
-#include "sched.h"
-#include "local.h"
-#include "logging.h"
-#include "conf.h"
-#include "privops.h"
-#include "util.h"
-
-#ifdef HAVE_LINUX_TIMESTAMPING
-#include "ntp_io_linux.h"
-#endif
-
-#define INVALID_SOCK_FD -1
-#define CMSGBUF_SIZE 256
-
-union sockaddr_in46 {
-  struct sockaddr_in in4;
-#ifdef FEAT_IPV6
-  struct sockaddr_in6 in6;
-#endif
-  struct sockaddr u;
-};
-
-struct Message {
-  union sockaddr_in46 name;
-  struct iovec iov;
-  NTP_Receive_Buffer buf;
-  /* Aligned buffer for control messages */
-  struct cmsghdr cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
-};
-
-#ifdef HAVE_RECVMMSG
-#define MAX_RECV_MESSAGES 4
-#define MessageHeader mmsghdr
-#else
-/* Compatible with mmsghdr */
-struct MessageHeader {
-  struct msghdr msg_hdr;
-  unsigned int msg_len;
-};
-
-#define MAX_RECV_MESSAGES 1
-#endif
-
-/* Arrays of Message and MessageHeader */
-static ARR_Instance recv_messages;
-static ARR_Instance recv_headers;
-
-/* The server/peer and client sockets for IPv4 and IPv6 */
-static int server_sock_fd4;
-static int client_sock_fd4;
-#ifdef FEAT_IPV6
-static int server_sock_fd6;
-static int client_sock_fd6;
-#endif
-
-/* Reference counters for server sockets to keep them open only when needed */
-static int server_sock_ref4;
-#ifdef FEAT_IPV6
-static int server_sock_ref6;
-#endif
-
-/* Flag indicating we create a new connected client socket for each
-   server instead of sharing client_sock_fd4 and client_sock_fd6 */
-static int separate_client_sockets;
-
-/* Flag indicating the server sockets are not created dynamically when needed,
-   either to have a socket for client requests when separate client sockets
-   are disabled and client port is equal to server port, or the server port is
-   disabled */
-static int permanent_server_sockets;
-
-/* Flag indicating that we have been initialised */
-static int initialised=0;
-
-/* ================================================== */
-
-/* Forward prototypes */
-static void read_from_socket(int sock_fd, int event, void *anything);
-
-/* ================================================== */
-
-static int
-prepare_socket(int family, int port_number, int client_only)
-{
-  union sockaddr_in46 my_addr;
-  socklen_t my_addr_len;
-  int sock_fd;
-  IPAddr bind_address;
-  int events = SCH_FILE_INPUT, on_off = 1;
-
-  /* Open Internet domain UDP socket for NTP message transmissions */
-
-  sock_fd = socket(family, SOCK_DGRAM, 0);
-
-  if (sock_fd < 0) {
-    if (!client_only) {
-      LOG(LOGS_ERR, "Could not open %s NTP socket : %s",
-          UTI_SockaddrFamilyToString(family), strerror(errno));
-    } else {
-      DEBUG_LOG("Could not open %s NTP socket : %s",
-                UTI_SockaddrFamilyToString(family), strerror(errno));
-    }
-    return INVALID_SOCK_FD;
-  }
-
-  /* Close on exec */
-  UTI_FdSetCloexec(sock_fd);
-
-  /* Prepare local address */
-  memset(&my_addr, 0, sizeof (my_addr));
-  my_addr_len = 0;
-
-  switch (family) {
-    case AF_INET:
-      if (!client_only)
-        CNF_GetBindAddress(IPADDR_INET4, &bind_address);
-      else
-        CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address);
-
-      if (bind_address.family == IPADDR_INET4)
-        my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
-      else if (port_number)
-        my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
-      else
-        break;
-
-      my_addr.in4.sin_family = family;
-      my_addr.in4.sin_port = htons(port_number);
-      my_addr_len = sizeof (my_addr.in4);
-
-      break;
-#ifdef FEAT_IPV6
-    case AF_INET6:
-      if (!client_only)
-        CNF_GetBindAddress(IPADDR_INET6, &bind_address);
-      else
-        CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address);
-
-      if (bind_address.family == IPADDR_INET6)
-        memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
-            sizeof (my_addr.in6.sin6_addr.s6_addr));
-      else if (port_number)
-        my_addr.in6.sin6_addr = in6addr_any;
-      else
-        break;
-
-      my_addr.in6.sin6_family = family;
-      my_addr.in6.sin6_port = htons(port_number);
-      my_addr_len = sizeof (my_addr.in6);
-
-      break;
-#endif
-    default:
-      assert(0);
-  }
-
-  /* Make the socket capable of re-using an old address if binding to a specific port */
-  if (port_number &&
-      setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
-    LOG(LOGS_ERR, "Could not set %s socket option", "SO_REUSEADDR");
-    /* Don't quit - we might survive anyway */
-  }
-  
-  /* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
-  if (!client_only &&
-      setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
-    LOG(LOGS_ERR, "Could not set %s socket option", "SO_BROADCAST");
-    /* Don't quit - we might survive anyway */
-  }
-
-  /* Enable kernel/HW timestamping of packets */
-#ifdef HAVE_LINUX_TIMESTAMPING
-  if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
-#endif
-#ifdef SO_TIMESTAMPNS
-    if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
-#endif
-#ifdef SO_TIMESTAMP
-      if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0)
-        LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP");
-#endif
-      ;
-
-#ifdef IP_FREEBIND
-  /* Allow binding to address that doesn't exist yet */
-  if (my_addr_len > 0 &&
-      setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
-    LOG(LOGS_ERR, "Could not set %s socket option", "IP_FREEBIND");
-  }
-#endif
-
-  if (family == AF_INET) {
-#ifdef HAVE_IN_PKTINFO
-    /* We want the local IP info on server sockets */
-    if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
-      LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO");
-      /* Don't quit - we might survive anyway */
-    }
-#endif
-  }
-#ifdef FEAT_IPV6
-  else if (family == AF_INET6) {
-#ifdef IPV6_V6ONLY
-    /* Receive IPv6 packets only */
-    if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
-      LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_V6ONLY");
-    }
-#endif
-
-#ifdef HAVE_IN6_PKTINFO
-#ifdef IPV6_RECVPKTINFO
-    if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
-      LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_RECVPKTINFO");
-    }
-#else
-    if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
-      LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_PKTINFO");
-    }
-#endif
-#endif
-  }
-#endif
-
-  /* Bind the socket if a port or address was specified */
-  if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) {
-    LOG(LOGS_ERR, "Could not bind %s NTP socket : %s",
-        UTI_SockaddrFamilyToString(family), strerror(errno));
-    close(sock_fd);
-    return INVALID_SOCK_FD;
-  }
-
-  /* Register handler for read and possibly exception events on the socket */
-  SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
-
-  return sock_fd;
-}
-
-/* ================================================== */
-
-static int
-prepare_separate_client_socket(int family)
-{
-  switch (family) {
-    case IPADDR_INET4:
-      return prepare_socket(AF_INET, 0, 1);
-#ifdef FEAT_IPV6
-    case IPADDR_INET6:
-      return prepare_socket(AF_INET6, 0, 1);
-#endif
-    default:
-      return INVALID_SOCK_FD;
-  }
-}
-
-/* ================================================== */
-
-static int
-connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
-{
-  union sockaddr_in46 addr;
-  socklen_t addr_len;
-
-  addr_len = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &addr.u);
-
-  assert(addr_len);
-
-  if (connect(sock_fd, &addr.u, addr_len) < 0) {
-    DEBUG_LOG("Could not connect NTP socket to %s:%d : %s",
-        UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
-        strerror(errno));
-    return 0;
-  }
-
-  return 1;
-}
-
-/* ================================================== */
-
-static void
-close_socket(int sock_fd)
-{
-  if (sock_fd == INVALID_SOCK_FD)
-    return;
-
-#ifdef HAVE_LINUX_TIMESTAMPING
-  NIO_Linux_NotifySocketClosing(sock_fd);
-#endif
-  SCH_RemoveFileHandler(sock_fd);
-  close(sock_fd);
-}
-
-/* ================================================== */
-
-static void
-prepare_buffers(unsigned int n)
-{
-  struct MessageHeader *hdr;
-  struct Message *msg;
-  unsigned int i;
-
-  for (i = 0; i < n; i++) {
-    msg = ARR_GetElement(recv_messages, i);
-    hdr = ARR_GetElement(recv_headers, i);
-
-    msg->iov.iov_base = &msg->buf;
-    msg->iov.iov_len = sizeof (msg->buf);
-    hdr->msg_hdr.msg_name = &msg->name;
-    hdr->msg_hdr.msg_namelen = sizeof (msg->name);
-    hdr->msg_hdr.msg_iov = &msg->iov;
-    hdr->msg_hdr.msg_iovlen = 1;
-    hdr->msg_hdr.msg_control = &msg->cmsgbuf;
-    hdr->msg_hdr.msg_controllen = sizeof (msg->cmsgbuf);
-    hdr->msg_hdr.msg_flags = 0;
-    hdr->msg_len = 0;
-  }
-}
-
-/* ================================================== */
-
-void
-NIO_Initialise(int family)
-{
-  int server_port, client_port;
-
-  assert(!initialised);
-  initialised = 1;
-
-#ifdef HAVE_LINUX_TIMESTAMPING
-  NIO_Linux_Initialise();
-#else
-  if (1) {
-    CNF_HwTsInterface *conf_iface;
-    if (CNF_GetHwTsInterface(0, &conf_iface))
-      LOG_FATAL("HW timestamping not supported");
-  }
-#endif
-
-  recv_messages = ARR_CreateInstance(sizeof (struct Message));
-  ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
-  recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
-  ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
-  prepare_buffers(MAX_RECV_MESSAGES);
-
-  server_port = CNF_GetNTPPort();
-  client_port = CNF_GetAcquisitionPort();
-
-  /* Use separate connected sockets if client port is negative */
-  separate_client_sockets = client_port < 0;
-  if (client_port < 0)
-    client_port = 0;
-
-  permanent_server_sockets = !server_port || (!separate_client_sockets &&
-                                              client_port == server_port);
-
-  server_sock_fd4 = INVALID_SOCK_FD;
-  client_sock_fd4 = INVALID_SOCK_FD;
-  server_sock_ref4 = 0;
-#ifdef FEAT_IPV6
-  server_sock_fd6 = INVALID_SOCK_FD;
-  client_sock_fd6 = INVALID_SOCK_FD;
-  server_sock_ref6 = 0;
-#endif
-
-  if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
-    if (permanent_server_sockets && server_port)
-      server_sock_fd4 = prepare_socket(AF_INET, server_port, 0);
-    if (!separate_client_sockets) {
-      if (client_port != server_port || !server_port)
-        client_sock_fd4 = prepare_socket(AF_INET, client_port, 1);
-      else
-        client_sock_fd4 = server_sock_fd4;
-    }
-  }
-#ifdef FEAT_IPV6
-  if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
-    if (permanent_server_sockets && server_port)
-      server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0);
-    if (!separate_client_sockets) {
-      if (client_port != server_port || !server_port)
-        client_sock_fd6 = prepare_socket(AF_INET6, client_port, 1);
-      else
-        client_sock_fd6 = server_sock_fd6;
-    }
-  }
-#endif
-
-  if ((server_port && server_sock_fd4 == INVALID_SOCK_FD &&
-       permanent_server_sockets 
-#ifdef FEAT_IPV6
-       && server_sock_fd6 == INVALID_SOCK_FD
-#endif
-      ) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD
-#ifdef FEAT_IPV6
-       && client_sock_fd6 == INVALID_SOCK_FD
-#endif
-      )) {
-    LOG_FATAL("Could not open NTP sockets");
-  }
-}
-
-/* ================================================== */
-
-void
-NIO_Finalise(void)
-{
-  if (server_sock_fd4 != client_sock_fd4)
-    close_socket(client_sock_fd4);
-  close_socket(server_sock_fd4);
-  server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
-#ifdef FEAT_IPV6
-  if (server_sock_fd6 != client_sock_fd6)
-    close_socket(client_sock_fd6);
-  close_socket(server_sock_fd6);
-  server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
-#endif
-  ARR_DestroyInstance(recv_headers);
-  ARR_DestroyInstance(recv_messages);
-
-#ifdef HAVE_LINUX_TIMESTAMPING
-  NIO_Linux_Finalise();
-#endif
-
-  initialised = 0;
-}
-
-/* ================================================== */
-
-int
-NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
-{
-  if (separate_client_sockets) {
-    int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
-
-    if (sock_fd == INVALID_SOCK_FD)
-      return INVALID_SOCK_FD;
-
-    if (!connect_socket(sock_fd, remote_addr)) {
-      close_socket(sock_fd);
-      return INVALID_SOCK_FD;
-    }
-
-    return sock_fd;
-  } else {
-    switch (remote_addr->ip_addr.family) {
-      case IPADDR_INET4:
-        return client_sock_fd4;
-#ifdef FEAT_IPV6
-      case IPADDR_INET6:
-        return client_sock_fd6;
-#endif
-      default:
-        return INVALID_SOCK_FD;
-    }
-  }
-}
-
-/* ================================================== */
-
-int
-NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
-{
-  switch (remote_addr->ip_addr.family) {
-    case IPADDR_INET4:
-      if (permanent_server_sockets)
-        return server_sock_fd4;
-      if (server_sock_fd4 == INVALID_SOCK_FD)
-        server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0);
-      if (server_sock_fd4 != INVALID_SOCK_FD)
-        server_sock_ref4++;
-      return server_sock_fd4;
-#ifdef FEAT_IPV6
-    case IPADDR_INET6:
-      if (permanent_server_sockets)
-        return server_sock_fd6;
-      if (server_sock_fd6 == INVALID_SOCK_FD)
-        server_sock_fd6 = prepare_socket(AF_INET6, CNF_GetNTPPort(), 0);
-      if (server_sock_fd6 != INVALID_SOCK_FD)
-        server_sock_ref6++;
-      return server_sock_fd6;
-#endif
-    default:
-      return INVALID_SOCK_FD;
-  }
-}
-
-/* ================================================== */
-
-void
-NIO_CloseClientSocket(int sock_fd)
-{
-  if (separate_client_sockets)
-    close_socket(sock_fd);
-}
-
-/* ================================================== */
-
-void
-NIO_CloseServerSocket(int sock_fd)
-{
-  if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD)
-    return;
-
-  if (sock_fd == server_sock_fd4) {
-    if (--server_sock_ref4 <= 0) {
-      close_socket(server_sock_fd4);
-      server_sock_fd4 = INVALID_SOCK_FD;
-    }
-  }
-#ifdef FEAT_IPV6
-  else if (sock_fd == server_sock_fd6) {
-    if (--server_sock_ref6 <= 0) {
-      close_socket(server_sock_fd6);
-      server_sock_fd6 = INVALID_SOCK_FD;
-    }
-  }
-#endif
-  else {
-    assert(0);
-  }
-}
-
-/* ================================================== */
-
-int
-NIO_IsServerSocket(int sock_fd)
-{
-  return sock_fd != INVALID_SOCK_FD &&
-    (sock_fd == server_sock_fd4
-#ifdef FEAT_IPV6
-     || sock_fd == server_sock_fd6
-#endif
-    );
-}
-
-/* ================================================== */
-
-static void
-process_message(struct msghdr *hdr, int length, int sock_fd)
-{
-  NTP_Remote_Address remote_addr;
-  NTP_Local_Address local_addr;
-  NTP_Local_Timestamp local_ts;
-  struct timespec sched_ts;
-  struct cmsghdr *cmsg;
-
-  SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
-  local_ts.source = NTP_TS_DAEMON;
-  sched_ts = local_ts.ts;
-
-  if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
-    DEBUG_LOG("Truncated source address");
-    return;
-  }
-
-  if (hdr->msg_namelen >= sizeof (((struct sockaddr *)hdr->msg_name)->sa_family)) {
-    UTI_SockaddrToIPAndPort((struct sockaddr *)hdr->msg_name,
-                            &remote_addr.ip_addr, &remote_addr.port);
-  } else {
-    remote_addr.ip_addr.family = IPADDR_UNSPEC;
-    remote_addr.port = 0;
-  }
-
-  local_addr.ip_addr.family = IPADDR_UNSPEC;
-  local_addr.if_index = INVALID_IF_INDEX;
-  local_addr.sock_fd = sock_fd;
-
-  if (hdr->msg_flags & MSG_TRUNC) {
-    DEBUG_LOG("Received truncated message from %s:%d",
-              UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
-    return;
-  }
-
-  if (hdr->msg_flags & MSG_CTRUNC) {
-    DEBUG_LOG("Truncated control message");
-    /* Continue */
-  }
-
-  for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
-#ifdef HAVE_IN_PKTINFO
-    if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
-      struct in_pktinfo ipi;
-
-      memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
-      local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
-      local_addr.ip_addr.family = IPADDR_INET4;
-      local_addr.if_index = ipi.ipi_ifindex;
-    }
-#endif
-
-#ifdef HAVE_IN6_PKTINFO
-    if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
-      struct in6_pktinfo ipi;
-
-      memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
-      memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
-             sizeof (local_addr.ip_addr.addr.in6));
-      local_addr.ip_addr.family = IPADDR_INET6;
-      local_addr.if_index = ipi.ipi6_ifindex;
-    }
-#endif
-
-#ifdef SCM_TIMESTAMP
-    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
-      struct timeval tv;
-      struct timespec ts;
-
-      memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
-      UTI_TimevalToTimespec(&tv, &ts);
-      LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
-      local_ts.source = NTP_TS_KERNEL;
-    }
-#endif
-
-#ifdef SCM_TIMESTAMPNS
-    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
-      struct timespec ts;
-
-      memcpy(&ts, CMSG_DATA(cmsg), sizeof (ts));
-      LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
-      local_ts.source = NTP_TS_KERNEL;
-    }
-#endif
-  }
-
-#ifdef HAVE_LINUX_TIMESTAMPING
-  if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length))
-    return;
-#endif
-
-  DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
-            length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
-            UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
-            local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
-
-  /* Just ignore the packet if it's not of a recognized length */
-  if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer))
-    return;
-
-  NSR_ProcessRx(&remote_addr, &local_addr, &local_ts,
-                (NTP_Packet *)hdr->msg_iov[0].iov_base, length);
-}
-
-/* ================================================== */
-
-static void
-read_from_socket(int sock_fd, int event, void *anything)
-{
-  /* This should only be called when there is something
-     to read, otherwise it may block */
-
-  struct MessageHeader *hdr;
-  unsigned int i, n;
-  int status, flags = 0;
-
-#ifdef HAVE_LINUX_TIMESTAMPING
-  if (NIO_Linux_ProcessEvent(sock_fd, event))
-    return;
-#endif
-
-  hdr = ARR_GetElements(recv_headers);
-  n = ARR_GetSize(recv_headers);
-  assert(n >= 1);
-
-  if (event == SCH_FILE_EXCEPTION) {
-#ifdef HAVE_LINUX_TIMESTAMPING
-    flags |= MSG_ERRQUEUE;
-#else
-    assert(0);
-#endif
-  }
-
-#ifdef HAVE_RECVMMSG
-  status = recvmmsg(sock_fd, hdr, n, flags | MSG_DONTWAIT, NULL);
-  if (status >= 0)
-    n = status;
-#else
-  n = 1;
-  status = recvmsg(sock_fd, &hdr[0].msg_hdr, flags);
-  if (status >= 0)
-    hdr[0].msg_len = status;
-#endif
-
-  if (status < 0) {
-#ifdef HAVE_LINUX_TIMESTAMPING
-    /* If reading from the error queue failed, the exception should be
-       for a socket error.  Clear the error to avoid a busy loop. */
-    if (flags & MSG_ERRQUEUE) {
-      int error = 0;
-      socklen_t len = sizeof (error);
-
-      if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len))
-        DEBUG_LOG("Could not get SO_ERROR");
-      if (error)
-        errno = error;
-    }
-#endif
-
-    DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
-              strerror(errno));
-    return;
-  }
-
-  for (i = 0; i < n; i++) {
-    hdr = ARR_GetElement(recv_headers, i);
-    process_message(&hdr->msg_hdr, hdr->msg_len, sock_fd);
-  }
-
-  /* Restore the buffers to their original state */
-  prepare_buffers(n);
-}
-
-/* ================================================== */
-/* Send a packet to remote address from local address */
-
-int
-NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
-               NTP_Local_Address *local_addr, int length, int process_tx)
-{
-  union sockaddr_in46 remote;
-  struct msghdr msg;
-  struct iovec iov;
-  struct cmsghdr *cmsg, cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
-  int cmsglen;
-  socklen_t addrlen = 0;
-
-  assert(initialised);
-
-  if (local_addr->sock_fd == INVALID_SOCK_FD) {
-    DEBUG_LOG("No socket to send to %s:%d",
-              UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
-    return 0;
-  }
-
-  /* Don't set address with connected socket */
-  if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
-    addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port,
-                                      &remote.u);
-    if (!addrlen)
-      return 0;
-  }
-
-  if (addrlen) {
-    msg.msg_name = &remote.u;
-    msg.msg_namelen = addrlen;
-  } else {
-    msg.msg_name = NULL;
-    msg.msg_namelen = 0;
-  }
-
-  iov.iov_base = packet;
-  iov.iov_len = length;
-  msg.msg_iov = &iov;
-  msg.msg_iovlen = 1;
-  msg.msg_control = cmsgbuf;
-  msg.msg_controllen = sizeof(cmsgbuf);
-  msg.msg_flags = 0;
-  cmsglen = 0;
-
-#ifdef HAVE_IN_PKTINFO
-  if (local_addr->ip_addr.family == IPADDR_INET4) {
-    struct in_pktinfo *ipi;
-
-    cmsg = CMSG_FIRSTHDR(&msg);
-    memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo)));
-    cmsglen += CMSG_SPACE(sizeof(struct in_pktinfo));
-
-    cmsg->cmsg_level = IPPROTO_IP;
-    cmsg->cmsg_type = IP_PKTINFO;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
-
-    ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
-    ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
-  }
-#endif
-
-#ifdef HAVE_IN6_PKTINFO
-  if (local_addr->ip_addr.family == IPADDR_INET6) {
-    struct in6_pktinfo *ipi;
-
-    cmsg = CMSG_FIRSTHDR(&msg);
-    memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo)));
-    cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo));
-
-    cmsg->cmsg_level = IPPROTO_IPV6;
-    cmsg->cmsg_type = IPV6_PKTINFO;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
-
-    ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
-    memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
-        sizeof(ipi->ipi6_addr.s6_addr));
-  }
-#endif
-
-#ifdef HAVE_LINUX_TIMESTAMPING
-  if (process_tx)
-   cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd);
-#endif
-
-  msg.msg_controllen = cmsglen;
-  /* This is apparently required on some systems */
-  if (!cmsglen)
-    msg.msg_control = NULL;
-
-  if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
-    DEBUG_LOG("Could not send to %s:%d from %s fd %d : %s",
-        UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
-        UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
-        strerror(errno));
-    return 0;
-  }
-
-  DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length,
-      UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
-      UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
-
-  return 1;
-}
diff --git a/chrony_3_3/ntp_sources.c b/chrony_3_3/ntp_sources.c
deleted file mode 100644
index e7fb60f..0000000
--- a/chrony_3_3/ntp_sources.c
+++ /dev/null
@@ -1,1172 +0,0 @@
-/*
-  chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Richard P. Curnow  1997-2003
- * Copyright (C) Miroslav Lichvar  2011-2012, 2014, 2016
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
-
-  =======================================================================
-
-  Functions which manage the pool of NTP sources that we are currently
-  a client of or peering with.
-
-  */
-
-#include "config.h"
-
-#include "sysincl.h"
-
-#include "array.h"
-#include "ntp_sources.h"
-#include "ntp_core.h"
-#include "util.h"
-#include "logging.h"
-#include "local.h"
-#include "memory.h"
-#include "nameserv_async.h"
-#include "privops.h"
-#include "sched.h"
-
-/* ================================================== */
-
-/* Record type private to this file, used to store information about
-   particular sources */
-typedef struct {
-  NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL
-                                      means this slot in table is in use */
-  NCR_Instance data;            /* Data for the protocol engine for this source */
-  char *name;                   /* Name of the source, may be NULL */
-  int pool;                     /* Number of the pool from which was this source
-                                   added or INVALID_POOL */
-  int tentative;                /* Flag indicating there was no valid response
-                                   received from the source yet */
-} SourceRecord;
-
-/* Hash table of SourceRecord, its size is a power of two and it's never
-   more than half full */
-static ARR_Instance records;
-
-/* Number of sources in the hash table */
-static int n_sources;
-
-/* Flag indicating new sources will be started automatically when added */
-static int auto_start_sources = 0;
-
-/* Source with unknown address (which may be resolved later) */
-struct UnresolvedSource {
-  char *name;
-  int port;
-  int random_order;
-  int replacement;
-  union {
-    struct {
-      NTP_Source_Type type;
-      SourceParameters params;
-      int pool;
-      int max_new_sources;
-    } new_source;
-    NTP_Remote_Address replace_source;
-  };
-  struct UnresolvedSource *next;
-};
-
-#define RESOLVE_INTERVAL_UNIT 7
-#define MIN_RESOLVE_INTERVAL 2
-#define MAX_RESOLVE_INTERVAL 9
-#define MIN_REPLACEMENT_INTERVAL 8
-
-static struct UnresolvedSource *unresolved_sources = NULL;
-static int resolving_interval = 0;
-static SCH_TimeoutID resolving_id;
-static struct UnresolvedSource *resolving_source = NULL;
-static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
-
-#define MAX_POOL_SOURCES 16
-#define INVALID_POOL (-1)
-
-/* Pool of sources with the same name */
-struct SourcePool {
-  /* Number of sources added from this pool (ignoring tentative sources) */
-  int sources;
-  /* Maximum number of sources */
-  int max_sources;
-};
-
-/* Array of SourcePool */
-static ARR_Instance pools;
-
-/* ================================================== */
-/* Forward prototypes */
-
-static void resolve_sources(void *arg);
-static void rehash_records(void);
-static void clean_source_record(SourceRecord *record);
-
-static void
-slew_sources(struct timespec *raw,
-             struct timespec *cooked,
-             double dfreq,
-             double doffset,
-             LCL_ChangeType change_type,
-             void *anything);
-
-/* ================================================== */
-
-/* Flag indicating whether module is initialised */
-static int initialised = 0;
-
-/* ================================================== */
-
-static SourceRecord *
-get_record(unsigned index)
-{
-  return (SourceRecord *)ARR_GetElement(records, index);
-}
-
-/* ================================================== */
-
-void
-NSR_Initialise(void)
-{
-  n_sources = 0;
-  initialised = 1;
-
-  records = ARR_CreateInstance(sizeof (SourceRecord));
-  rehash_records();
-
-  pools = ARR_CreateInstance(sizeof (struct SourcePool));
-
-  LCL_AddParameterChangeHandler(slew_sources, NULL);
-}
-
-/* ================================================== */
-
-void
-NSR_Finalise(void)
-{
-  SourceRecord *record;
-  struct UnresolvedSource *us;
-  unsigned int i;
-
-  ARR_DestroyInstance(pools);
-
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (record->remote_addr)
-      clean_source_record(record);
-  }
-
-  ARR_DestroyInstance(records);
-
-  while (unresolved_sources) {
-    us = unresolved_sources;
-    unresolved_sources = us->next;
-    Free(us->name);
-    Free(us);
-  }
-
-  initialised = 0;
-}
-
-/* ================================================== */
-/* Return slot number and whether the IP address was matched or not.
-   found = 0 => Neither IP nor port matched, empty slot returned
-   found = 1 => Only IP matched, port doesn't match
-   found = 2 => Both IP and port matched.
-
-   It is assumed that there can only ever be one record for a
-   particular IP address.  (If a different port comes up, it probably
-   means someone is running ntpdate -d or something).  Thus, if we
-   match the IP address we stop the search regardless of whether the
-   port number matches.
-
-  */
-
-static void
-find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
-{
-  SourceRecord *record;
-  uint32_t hash;
-  unsigned int i, size;
-  unsigned short port;
-
-  size = ARR_GetSize(records);
-
-  *slot = 0;
-  *found = 0;
-  
-  if (remote_addr->ip_addr.family != IPADDR_INET4 &&
-      remote_addr->ip_addr.family != IPADDR_INET6)
-    return;
-
-  hash = UTI_IPToHash(&remote_addr->ip_addr);
-  port = remote_addr->port;
-
-  for (i = 0; i < size / 2; i++) {
-    /* Use quadratic probing */
-    *slot = (hash + (i + i * i) / 2) % size;
-    record = get_record(*slot);
-
-    if (!record->remote_addr)
-      break;
-
-    if (!UTI_CompareIPs(&record->remote_addr->ip_addr,
-                        &remote_addr->ip_addr, NULL)) {
-      *found = record->remote_addr->port == port ? 2 : 1;
-      return;
-    }
-  }
-}
-
-/* ================================================== */
-/* Check if hash table of given size is sufficient to contain sources */
-
-static int
-check_hashtable_size(unsigned int sources, unsigned int size)
-{
-  return sources * 2 <= size;
-}
-
-/* ================================================== */
-
-static void
-rehash_records(void)
-{
-  SourceRecord *temp_records;
-  unsigned int i, old_size, new_size;
-  int slot, found;
-
-  old_size = ARR_GetSize(records);
-
-  temp_records = MallocArray(SourceRecord, old_size);
-  memcpy(temp_records, ARR_GetElements(records), old_size * sizeof (SourceRecord));
-
-  /* The size of the hash table is always a power of two */
-  for (new_size = 1; !check_hashtable_size(n_sources, new_size); new_size *= 2)
-    ;
-
-  ARR_SetSize(records, new_size);
-
-  for (i = 0; i < new_size; i++)
-    get_record(i)->remote_addr = NULL;
-
-  for (i = 0; i < old_size; i++) {
-    if (!temp_records[i].remote_addr)
-      continue;
-
-    find_slot(temp_records[i].remote_addr, &slot, &found);
-    assert(!found);
-
-    *get_record(slot) = temp_records[i];
-  }
-
-  Free(temp_records);
-}
-
-/* ================================================== */
-
-/* Procedure to add a new source */
-static NSR_Status
-add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, SourceParameters *params, int pool)
-{
-  SourceRecord *record;
-  int slot, found;
-
-  assert(initialised);
-
-  /* Find empty bin & check that we don't have the address already */
-  find_slot(remote_addr, &slot, &found);
-  if (found) {
-    return NSR_AlreadyInUse;
-  } else {
-    if (remote_addr->ip_addr.family != IPADDR_INET4 &&
-               remote_addr->ip_addr.family != IPADDR_INET6) {
-      return NSR_InvalidAF;
-    } else {
-      n_sources++;
-
-      if (!check_hashtable_size(n_sources, ARR_GetSize(records))) {
-        rehash_records();
-        find_slot(remote_addr, &slot, &found);
-      }
-
-      assert(!found);
-      record = get_record(slot);
-      record->data = NCR_GetInstance(remote_addr, type, params);
-      record->remote_addr = NCR_GetRemoteAddress(record->data);
-      record->name = name ? Strdup(name) : NULL;
-      record->pool = pool;
-      record->tentative = 1;
-
-      if (auto_start_sources)
-        NCR_StartInstance(record->data);
-
-      return NSR_Success;
-    }
-  }
-}
-
-/* ================================================== */
-
-static NSR_Status
-replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
-{
-  int slot1, slot2, found;
-  SourceRecord *record;
-  struct SourcePool *pool;
-
-  find_slot(old_addr, &slot1, &found);
-  if (!found)
-    return NSR_NoSuchSource;
-
-  find_slot(new_addr, &slot2, &found);
-  if (found)
-    return NSR_AlreadyInUse;
-
-  record = get_record(slot1);
-  NCR_ChangeRemoteAddress(record->data, new_addr);
-  record->remote_addr = NCR_GetRemoteAddress(record->data);
-
-  if (!record->tentative) {
-    record->tentative = 1;
-
-    if (record->pool != INVALID_POOL) {
-      pool = ARR_GetElement(pools, record->pool);
-      pool->sources--;
-    }
-  }
-
-  /* The hash table must be rebuilt for the new address */
-  rehash_records();
-
-  LOG(LOGS_INFO, "Source %s replaced with %s",
-      UTI_IPToString(&old_addr->ip_addr),
-      UTI_IPToString(&new_addr->ip_addr));
-
-  return NSR_Success;
-}
-
-/* ================================================== */
-
-static void
-process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs)
-{
-  NTP_Remote_Address address;
-  int i, added;
-  unsigned short first = 0;
-
-  if (us->random_order)
-    UTI_GetRandomBytes(&first, sizeof (first));
-
-  for (i = added = 0; i < n_addrs; i++) {
-    address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
-    address.port = us->port;
-
-    DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
-
-    if (us->replacement) {
-      if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
-        break;
-    } else {
-      if (add_source(&address, us->name, us->new_source.type, &us->new_source.params,
-                     us->new_source.pool) == NSR_Success)
-        added++;
-
-      if (added >= us->new_source.max_new_sources)
-        break;
-    }
-  }
-}
-
-/* ================================================== */
-
-static void
-name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything)
-{
-  struct UnresolvedSource *us, **i, *next;
-
-  us = (struct UnresolvedSource *)anything;
-
-  assert(us == resolving_source);
-
-  DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs);
-
-  switch (status) {
-    case DNS_TryAgain:
-      break;
-    case DNS_Success:
-      process_resolved_name(us, ip_addrs, n_addrs);
-      break;
-    case DNS_Failure:
-      LOG(LOGS_WARN, "Invalid host %s", us->name);
-      break;
-    default:
-      assert(0);
-  }
-
-  next = us->next;
-
-  /* Remove the source from the list on success or failure, replacements
-     are removed on any status */
-  if (us->replacement || status != DNS_TryAgain) {
-    for (i = &unresolved_sources; *i; i = &(*i)->next) {
-      if (*i == us) {
-        *i = us->next;
-        Free(us->name);
-        Free(us);
-        break;
-      }
-    }
-  }
-
-  resolving_source = next;
-
-  if (next) {
-    /* Continue with the next source in the list */
-    DEBUG_LOG("resolving %s", next->name);
-    DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
-  } else {
-    /* This was the last source in the list. If some sources couldn't
-       be resolved, try again in exponentially increasing interval. */
-    if (unresolved_sources) {
-      if (resolving_interval < MIN_RESOLVE_INTERVAL)
-        resolving_interval = MIN_RESOLVE_INTERVAL;
-      else if (resolving_interval < MAX_RESOLVE_INTERVAL)
-        resolving_interval++;
-      resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT *
-          (1 << resolving_interval), resolve_sources, NULL);
-    } else {
-      resolving_interval = 0;
-    }
-
-    /* This round of resolving is done */
-    if (resolving_end_handler)
-      (resolving_end_handler)();
-  }
-}
-
-/* ================================================== */
-
-static void
-resolve_sources(void *arg)
-{
-  struct UnresolvedSource *us;
-
-  assert(!resolving_source);
-
-  PRV_ReloadDNS();
-
-  /* Start with the first source in the list, name_resolve_handler
-     will iterate over the rest */
-  us = unresolved_sources;
-
-  resolving_source = us;
-  DEBUG_LOG("resolving %s", us->name);
-  DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
-}
-
-/* ================================================== */
-
-static void
-append_unresolved_source(struct UnresolvedSource *us)
-{
-  struct UnresolvedSource **i;
-
-  for (i = &unresolved_sources; *i; i = &(*i)->next)
-    ;
-  *i = us;
-  us->next = NULL;
-}
-
-/* ================================================== */
-
-NSR_Status
-NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
-{
-  return add_source(remote_addr, NULL, type, params, INVALID_POOL);
-}
-
-/* ================================================== */
-
-void
-NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params)
-{
-  struct UnresolvedSource *us;
-  struct SourcePool *sp;
-  NTP_Remote_Address remote_addr;
-
-  /* If the name is an IP address, don't bother with full resolving now
-     or later when trying to replace the source */
-  if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
-    remote_addr.port = port;
-    NSR_AddSource(&remote_addr, type, params);
-    return;
-  }
-
-  us = MallocNew(struct UnresolvedSource);
-  us->name = Strdup(name);
-  us->port = port;
-  us->random_order = 0;
-  us->replacement = 0;
-  us->new_source.type = type;
-  us->new_source.params = *params;
-
-  if (!pool) {
-    us->new_source.pool = INVALID_POOL;
-    us->new_source.max_new_sources = 1;
-  } else {
-    sp = (struct SourcePool *)ARR_GetNewElement(pools);
-    sp->sources = 0;
-    sp->max_sources = params->max_sources;
-    us->new_source.pool = ARR_GetSize(pools) - 1;
-    us->new_source.max_new_sources = MAX_POOL_SOURCES;
-  }
-
-  append_unresolved_source(us);
-}
-
-/* ================================================== */
-
-void
-NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
-{
-  resolving_end_handler = handler;
-}
-
-/* ================================================== */
-
-void
-NSR_ResolveSources(void)
-{
-  /* Try to resolve unresolved sources now */
-  if (unresolved_sources) {
-    /* Make sure no resolving is currently running */
-    if (!resolving_source) {
-      if (resolving_interval) {
-        SCH_RemoveTimeout(resolving_id);
-        resolving_interval--;
-      }
-      resolve_sources(NULL);
-    }
-  } else {
-    /* No unresolved sources, we are done */
-    if (resolving_end_handler)
-      (resolving_end_handler)();
-  }
-}
-
-/* ================================================== */
-
-void NSR_StartSources(void)
-{
-  unsigned int i;
-
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    if (!get_record(i)->remote_addr)
-      continue;
-    NCR_StartInstance(get_record(i)->data);
-  }
-}
-
-/* ================================================== */
-
-void NSR_AutoStartSources(void)
-{
-  auto_start_sources = 1;
-}
-
-/* ================================================== */
-
-static void
-clean_source_record(SourceRecord *record)
-{
-  assert(record->remote_addr);
-  record->remote_addr = NULL;
-  NCR_DestroyInstance(record->data);
-  if (record->name)
-    Free(record->name);
-
-  n_sources--;
-}
-
-/* ================================================== */
-
-/* Procedure to remove a source.  We don't bother whether the port
-   address is matched - we're only interested in removing a record for
-   the right IP address.  Thus the caller can specify the port number
-   as zero if it wishes. */
-NSR_Status
-NSR_RemoveSource(NTP_Remote_Address *remote_addr)
-{
-  int slot, found;
-
-  assert(initialised);
-
-  find_slot(remote_addr, &slot, &found);
-  if (!found) {
-    return NSR_NoSuchSource;
-  }
-
-  clean_source_record(get_record(slot));
-
-  /* Rehash the table to make sure there are no broken probe sequences.
-     This is costly, but it's not expected to happen frequently. */
-
-  rehash_records();
-
-  return NSR_Success;
-}
-
-/* ================================================== */
-
-void
-NSR_RemoveAllSources(void)
-{
-  SourceRecord *record;
-  unsigned int i;
-
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (!record->remote_addr)
-      continue;
-    clean_source_record(record);
-  }
-
-  rehash_records();
-}
-
-/* ================================================== */
-
-static void
-resolve_source_replacement(SourceRecord *record)
-{
-  struct UnresolvedSource *us;
-
-  DEBUG_LOG("trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr));
-
-  us = MallocNew(struct UnresolvedSource);
-  us->name = Strdup(record->name);
-  us->port = record->remote_addr->port;
-  /* If there never was a valid reply from this source (e.g. it was a bad
-     replacement), ignore the order of addresses from the resolver to not get
-     stuck to a pair of addresses if the order doesn't change, or a group of
-     IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
-  us->random_order = record->tentative;
-  us->replacement = 1;
-  us->replace_source = *record->remote_addr;
-
-  append_unresolved_source(us);
-  NSR_ResolveSources();
-}
-
-/* ================================================== */
-
-void
-NSR_HandleBadSource(IPAddr *address)
-{
-  static struct timespec last_replacement;
-  struct timespec now;
-  NTP_Remote_Address remote_addr;
-  SourceRecord *record;
-  int slot, found;
-  double diff;
-
-  remote_addr.ip_addr = *address;
-  remote_addr.port = 0;
-
-  find_slot(&remote_addr, &slot, &found);
-  if (!found)
-    return;
-
-  record = get_record(slot);
-
-  /* Only sources with a name can be replaced */
-  if (!record->name)
-    return;
-
-  /* Don't resolve names too frequently */
-  SCH_GetLastEventTime(NULL, NULL, &now);
-  diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
-  if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
-    DEBUG_LOG("replacement postponed");
-    return;
-  }
-  last_replacement = now;
-
-  resolve_source_replacement(record);
-}
-
-/* ================================================== */
-
-void
-NSR_RefreshAddresses(void)
-{
-  SourceRecord *record;
-  unsigned int i;
-
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (!record->remote_addr || !record->name)
-      continue;
-
-    resolve_source_replacement(record);
-  }
-}
-
-/* ================================================== */
-
-static void remove_tentative_pool_sources(int pool)
-{
-  SourceRecord *record;
-  unsigned int i, removed;
-
-  for (i = removed = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-
-    if (!record->remote_addr || record->pool != pool || !record->tentative)
-      continue;
-
-    DEBUG_LOG("removing tentative source %s",
-              UTI_IPToString(&record->remote_addr->ip_addr));
-
-    clean_source_record(record);
-    removed++;
-  }
-
-  if (removed)
-    rehash_records();
-}
-
-/* ================================================== */
-
-uint32_t
-NSR_GetLocalRefid(IPAddr *address)
-{
-  NTP_Remote_Address remote_addr;
-  int slot, found;
-
-  remote_addr.ip_addr = *address;
-  remote_addr.port = 0;
-
-  find_slot(&remote_addr, &slot, &found);
-  if (!found)
-    return 0;
-
-  return NCR_GetLocalRefid(get_record(slot)->data);
-}
-
-/* ================================================== */
-
-/* This routine is called by ntp_io when a new packet arrives off the network,
-   possibly with an authentication tail */
-void
-NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
-              NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
-{
-  SourceRecord *record;
-  struct SourcePool *pool;
-  int slot, found;
-
-  assert(initialised);
-
-  find_slot(remote_addr, &slot, &found);
-  if (found == 2) { /* Must match IP address AND port number */
-    record = get_record(slot);
-
-    if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
-      return;
-
-    if (record->tentative) {
-      /* This was the first good reply from the source */
-      record->tentative = 0;
-
-      if (record->pool != INVALID_POOL) {
-        pool = ARR_GetElement(pools, record->pool);
-        pool->sources++;
-
-        DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources);
-
-        /* If the number of sources from the pool reached the configured
-           maximum, remove the remaining tentative sources */
-        if (pool->sources >= pool->max_sources)
-          remove_tentative_pool_sources(record->pool);
-      }
-    }
-  } else {
-    NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
-  }
-}
-
-/* ================================================== */
-
-void
-NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
-              NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
-{
-  SourceRecord *record;
-  int slot, found;
-
-  find_slot(remote_addr, &slot, &found);
-
-  if (found == 2) { /* Must match IP address AND port number */
-    record = get_record(slot);
-    NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
-  } else {
-    NCR_ProcessTxUnknown(remote_addr, local_addr, tx_ts, message, length);
-  }
-}
-
-/* ================================================== */
-
-static void
-slew_sources(struct timespec *raw,
-             struct timespec *cooked,
-             double dfreq,
-             double doffset,
-             LCL_ChangeType change_type,
-             void *anything)
-{
-  SourceRecord *record;
-  unsigned int i;
-
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (record->remote_addr) {
-      if (change_type == LCL_ChangeUnknownStep) {
-        NCR_ResetInstance(record->data);
-        NCR_ResetPoll(record->data);
-      } else {
-        NCR_SlewTimes(record->data, cooked, dfreq, doffset);
-      }
-    }
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
-{
-  SourceRecord *record;
-  unsigned int i;
-  int any;
-
-  NSR_ResolveSources();
-
-  any = 0;
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (record->remote_addr) {
-      if (address->family == IPADDR_UNSPEC ||
-          !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
-        any = 1;
-        NCR_TakeSourceOnline(record->data);
-      }
-    }
-  }
-
-  if (address->family == IPADDR_UNSPEC) {
-    struct UnresolvedSource *us;
-
-    for (us = unresolved_sources; us; us = us->next) {
-      if (us->replacement)
-        continue;
-      any = 1;
-      us->new_source.params.online = 1;
-    }
-  }
-
-  return any;
-}
-
-/* ================================================== */
-
-int
-NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
-{
-  SourceRecord *record, *syncpeer;
-  unsigned int i, any;
-
-  any = 0;
-  syncpeer = NULL;
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (record->remote_addr) {
-      if (address->family == IPADDR_UNSPEC ||
-          !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
-        any = 1;
-        if (NCR_IsSyncPeer(record->data)) {
-          syncpeer = record;
-          continue;
-        }
-        NCR_TakeSourceOffline(record->data);
-      }
-    }
-  }
-
-  /* Take sync peer offline as last to avoid reference switching */
-  if (syncpeer) {
-    NCR_TakeSourceOffline(syncpeer->data);
-  }
-
-  if (address->family == IPADDR_UNSPEC) {
-    struct UnresolvedSource *us;
-
-    for (us = unresolved_sources; us; us = us->next) {
-      if (us->replacement)
-        continue;
-      any = 1;
-      us->new_source.params.online = 0;
-    }
-  }
-
-  return any;
-}
-
-/* ================================================== */
-
-int
-NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
-{
-  int slot, found;
-  NTP_Remote_Address addr;
-  addr.ip_addr = *address;
-  addr.port = 0;
-
-  find_slot(&addr, &slot, &found);
-  if (found == 0) {
-    return 0;
-  } else {
-    NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll);
-    return 1;
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
-{
-  int slot, found;
-  NTP_Remote_Address addr;
-  addr.ip_addr = *address;
-  addr.port = 0;
-
-  find_slot(&addr, &slot, &found);
-  if (found == 0) {
-    return 0;
-  } else {
-    NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll);
-    return 1;
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
-{
-  int slot, found;
-  NTP_Remote_Address addr;
-  addr.ip_addr = *address;
-  addr.port = 0;
-
-  find_slot(&addr, &slot, &found);
-  if (found == 0) {
-    return 0;
-  } else {
-    NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay);
-    return 1;
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
-{
-  int slot, found;
-  NTP_Remote_Address addr;
-  addr.ip_addr = *address;
-  addr.port = 0;
-
-  find_slot(&addr, &slot, &found);
-  if (found == 0) {
-    return 0;
-  } else {
-    NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio);
-    return 1;
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
-{
-  int slot, found;
-  NTP_Remote_Address addr;
-  addr.ip_addr = *address;
-  addr.port = 0;
-
-  find_slot(&addr, &slot, &found);
-  if (found == 0) {
-    return 0;
-  } else {
-    NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio);
-    return 1;
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
-{
-  int slot, found;
-  NTP_Remote_Address addr;
-  addr.ip_addr = *address;
-  addr.port = 0;
-
-  find_slot(&addr, &slot, &found);
-  if (found == 0) {
-    return 0;
-  } else {
-    NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum);
-    return 1;
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
-{
-  int slot, found;
-  NTP_Remote_Address addr;
-  addr.ip_addr = *address;
-  addr.port = 0;
-
-  find_slot(&addr, &slot, &found);
-  if (found == 0) {
-    return 0;
-  } else {
-    NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target);
-    return 1;
-  }
-}
-
-/* ================================================== */
-
-int
-NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
-                        IPAddr *mask, IPAddr *address)
-{
-  SourceRecord *record;
-  unsigned int i;
-  int any;
-
-  any = 0;
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (record->remote_addr) {
-      if (address->family == IPADDR_UNSPEC ||
-          !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
-        any = 1;
-        NCR_InitiateSampleBurst(record->data, n_good_samples, n_total_samples);
-      }
-    }
-  }
-
-  return any;
-
-}
-
-/* ================================================== */
-/* The ip address is assumed to be completed on input, that is how we
-   identify the source record. */
-
-void
-NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
-{
-  NTP_Remote_Address rem_addr;
-  int slot, found;
-
-  rem_addr.ip_addr = report->ip_addr;
-  rem_addr.port = 0;
-  find_slot(&rem_addr, &slot, &found);
-  if (found) {
-    NCR_ReportSource(get_record(slot)->data, report, now);
-  } else {
-    report->poll = 0;
-    report->latest_meas_ago = 0;
-  }
-}
-
-/* ================================================== */
-/* The ip address is assumed to be completed on input, that is how we
-   identify the source record. */
-
-int
-NSR_GetNTPReport(RPT_NTPReport *report)
-{
-  NTP_Remote_Address rem_addr;
-  int slot, found;
-
-  rem_addr.ip_addr = report->remote_addr;
-  rem_addr.port = 0;
-  find_slot(&rem_addr, &slot, &found);
-  if (!found)
-    return 0;
-
-  NCR_GetNTPReport(get_record(slot)->data, report);
-  return 1;
-}
-
-/* ================================================== */
-
-void
-NSR_GetActivityReport(RPT_ActivityReport *report)
-{
-  SourceRecord *record;
-  unsigned int i;
-  struct UnresolvedSource *us;
-
-  report->online = 0;
-  report->offline = 0;
-  report->burst_online = 0;
-  report->burst_offline = 0;
-
-  for (i = 0; i < ARR_GetSize(records); i++) {
-    record = get_record(i);
-    if (record->remote_addr) {
-      NCR_IncrementActivityCounters(record->data, &report->online, &report->offline,
-                                    &report->burst_online, &report->burst_offline);
-    }
-  }
-
-  report->unresolved = 0;
-
-  for (us = unresolved_sources; us; us = us->next) {
-    report->unresolved++;
-  }
-}
-
-
-/* ================================================== */
-
diff --git a/chrony_3_3/refclock.c b/chrony_3_3/refclock.c
deleted file mode 100644
index 0791d47..0000000
--- a/chrony_3_3/refclock.c
+++ /dev/null
@@ -1,1050 +0,0 @@
-/*
-  chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Miroslav Lichvar  2009-2011, 2013-2014, 2016-2017
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
-
-  =======================================================================
-
-  Routines implementing reference clocks.
-
-  */
-
-#include "config.h"
-
-#include "array.h"
-#include "refclock.h"
-#include "reference.h"
-#include "conf.h"
-#include "local.h"
-#include "memory.h"
-#include "util.h"
-#include "sources.h"
-#include "logging.h"
-#include "regress.h"
-#include "sched.h"
-
-/* list of refclock drivers */
-extern RefclockDriver RCL_SHM_driver;
-extern RefclockDriver RCL_SOCK_driver;
-extern RefclockDriver RCL_PPS_driver;
-extern RefclockDriver RCL_PHC_driver;
-
-struct FilterSample {
-  double offset;
-  double dispersion;
-  struct timespec sample_time;
-};
-
-struct MedianFilter {
-  int length;
-  int index;
-  int used;
-  int last;
-  int avg_var_n;
-  double avg_var;
-  double max_var;
-  struct FilterSample *samples;
-  int *selected;
-  double *x_data;
-  double *y_data;
-  double *w_data;
-};
-
-struct RCL_Instance_Record {
-  RefclockDriver *driver;
-  void *data;
-  char *driver_parameter;
-  int driver_parameter_length;
-  int driver_poll;
-  int driver_polled;
-  int poll;
-  int leap_status;
-  int pps_forced;
-  int pps_rate;
-  int pps_active;
-  int max_lock_age;
-  int stratum;
-  int tai;
-  struct MedianFilter filter;
-  uint32_t ref_id;
-  uint32_t lock_ref;
-  double offset;
-  double delay;
-  double precision;
-  double pulse_width;
-  SCH_TimeoutID timeout_id;
-  SRC_Instance source;
-};
-
-/* Array of pointers to RCL_Instance_Record */
-static ARR_Instance refclocks;
-
-static LOG_FileID logfileid;
-
-static int valid_sample_time(RCL_Instance instance, struct timespec *sample_time);
-static int pps_stratum(RCL_Instance instance, struct timespec *ts);
-static void poll_timeout(void *arg);
-static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
-             double doffset, LCL_ChangeType change_type, void *anything);
-static void add_dispersion(double dispersion, void *anything);
-static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
-
-static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
-static void filter_fini(struct MedianFilter *filter);
-static void filter_reset(struct MedianFilter *filter);
-static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
-static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion);
-static int filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
-static int filter_get_samples(struct MedianFilter *filter);
-static int filter_select_samples(struct MedianFilter *filter);
-static int filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
-static void filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset);
-static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
-
-static RCL_Instance
-get_refclock(unsigned int index)
-{
-  return *(RCL_Instance *)ARR_GetElement(refclocks, index);
-}
-
-void
-RCL_Initialise(void)
-{
-  refclocks = ARR_CreateInstance(sizeof (RCL_Instance));
-
-  CNF_AddRefclocks();
-
-  if (ARR_GetSize(refclocks) > 0) {
-    LCL_AddParameterChangeHandler(slew_samples, NULL);
-    LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
-  }
-
-  logfileid = CNF_GetLogRefclocks() ? LOG_FileOpen("refclocks",
-      "   Date (UTC) Time         Refid  DP L P  Raw offset   Cooked offset      Disp.")
-    : -1;
-}
-
-void
-RCL_Finalise(void)
-{
-  unsigned int i;
-
-  for (i = 0; i < ARR_GetSize(refclocks); i++) {
-    RCL_Instance inst = get_refclock(i);
-
-    if (inst->driver->fini)
-      inst->driver->fini(inst);
-
-    filter_fini(&inst->filter);
-    Free(inst->driver_parameter);
-    SRC_DestroyInstance(inst->source);
-    Free(inst);
-  }
-
-  if (ARR_GetSize(refclocks) > 0) {
-    LCL_RemoveParameterChangeHandler(slew_samples, NULL);
-    LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL);
-  }
-
-  ARR_DestroyInstance(refclocks);
-}
-
-int
-RCL_AddRefclock(RefclockParameters *params)
-{
-  RCL_Instance inst;
-
-  inst = MallocNew(struct RCL_Instance_Record);
-  *(RCL_Instance *)ARR_GetNewElement(refclocks) = inst;
-
-  if (strcmp(params->driver_name, "SHM") == 0) {
-    inst->driver = &RCL_SHM_driver;
-  } else if (strcmp(params->driver_name, "SOCK") == 0) {
-    inst->driver = &RCL_SOCK_driver;
-  } else if (strcmp(params->driver_name, "PPS") == 0) {
-    inst->driver = &RCL_PPS_driver;
-  } else if (strcmp(params->driver_name, "PHC") == 0) {
-    inst->driver = &RCL_PHC_driver;
-  } else {
-    LOG_FATAL("unknown refclock driver %s", params->driver_name);
-  }
-
-  if (!inst->driver->init && !inst->driver->poll)
-    LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
-
-  if (params->tai && !CNF_GetLeapSecTimezone())
-    LOG_FATAL("refclock tai option requires leapsectz");
-
-  inst->data = NULL;
-  inst->driver_parameter = params->driver_parameter;
-  inst->driver_parameter_length = 0;
-  inst->driver_poll = params->driver_poll;
-  inst->poll = params->poll;
-  inst->driver_polled = 0;
-  inst->leap_status = LEAP_Normal;
-  inst->pps_forced = params->pps_forced;
-  inst->pps_rate = params->pps_rate;
-  inst->pps_active = 0;
-  inst->max_lock_age = params->max_lock_age;
-  inst->stratum = params->stratum;
-  inst->tai = params->tai;
-  inst->lock_ref = params->lock_ref_id;
-  inst->offset = params->offset;
-  inst->delay = params->delay;
-  inst->precision = LCL_GetSysPrecisionAsQuantum();
-  inst->precision = MAX(inst->precision, params->precision);
-  inst->pulse_width = params->pulse_width;
-  inst->timeout_id = -1;
-  inst->source = NULL;
-
-  if (inst->driver_parameter) {
-    int i;
-
-    inst->driver_parameter_length = strlen(inst->driver_parameter);
-    for (i = 0; i < inst->driver_parameter_length; i++)
-      if (inst->driver_parameter[i] == ':')
-        inst->driver_parameter[i] = '\0';
-  }
-
-  if (inst->pps_rate < 1)
-    inst->pps_rate = 1;
-
-  if (params->ref_id)
-    inst->ref_id = params->ref_id;
-  else {
-    unsigned char ref[5] = { 0, 0, 0, 0, 0 };
-    unsigned int index = ARR_GetSize(refclocks) - 1;
-
-    snprintf((char *)ref, sizeof (ref), "%3.3s", params->driver_name);
-    ref[3] = index % 10 + '0';
-    if (index >= 10)
-      ref[2] = (index / 10) % 10 + '0';
-
-    inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
-  }
-
-  if (inst->driver->poll) {
-    int max_samples;
-
-    if (inst->driver_poll > inst->poll)
-      inst->driver_poll = inst->poll;
-
-    max_samples = 1 << (inst->poll - inst->driver_poll);
-    if (max_samples < params->filter_length) {
-      if (max_samples < 4) {
-        LOG(LOGS_WARN, "Setting filter length for %s to %d",
-            UTI_RefidToString(inst->ref_id), max_samples);
-      }
-      params->filter_length = max_samples;
-    }
-  }
-
-  if (inst->driver->init && !inst->driver->init(inst))
-    LOG_FATAL("refclock %s initialisation failed", params->driver_name);
-
-  filter_init(&inst->filter, params->filter_length, params->max_dispersion);
-
-  inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
-                                       params->min_samples, params->max_samples, 0.0, 0.0);
-
-  DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
-      params->driver_name, UTI_RefidToString(inst->ref_id),
-      inst->poll, inst->driver_poll, params->filter_length);
-
-  Free(params->driver_name);
-
-  return 1;
-}
-
-void
-RCL_StartRefclocks(void)
-{
-  unsigned int i, j, n;
-
-  n = ARR_GetSize(refclocks);
-
-  for (i = 0; i < n; i++) {
-    RCL_Instance inst = get_refclock(i);
-
-    SRC_SetActive(inst->source);
-    inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
-
-    if (inst->lock_ref) {
-      /* Replace lock refid with index to refclocks */
-      for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++)
-        ;
-      inst->lock_ref = j < n ? j : -1;
-    } else
-      inst->lock_ref = -1;
-  }
-}
-
-void
-RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
-{
-  unsigned int i;
-  uint32_t ref_id;
-
-  assert(report->ip_addr.family == IPADDR_INET4);
-  ref_id = report->ip_addr.addr.in4;
-
-  for (i = 0; i < ARR_GetSize(refclocks); i++) {
-    RCL_Instance inst = get_refclock(i);
-    if (inst->ref_id == ref_id) {
-      report->poll = inst->poll;
-      report->mode = RPT_LOCAL_REFERENCE;
-      break;
-    }
-  }
-}
-
-void
-RCL_SetDriverData(RCL_Instance instance, void *data)
-{
-  instance->data = data;
-}
-
-void *
-RCL_GetDriverData(RCL_Instance instance)
-{
-  return instance->data;
-}
-
-char *
-RCL_GetDriverParameter(RCL_Instance instance)
-{
-  return instance->driver_parameter;
-}
-
-char *
-RCL_GetDriverOption(RCL_Instance instance, char *name)
-{
-  char *s, *e;
-  int n;
-
-  s = instance->driver_parameter;
-  e = s + instance->driver_parameter_length;
-  n = strlen(name);
-
-  while (1) {
-    s += strlen(s) + 1;
-    if (s >= e)
-      break;
-    if (!strncmp(name, s, n)) {
-      if (s[n] == '=')
-        return s + n + 1;
-      if (s[n] == '\0')
-        return s + n;
-    }
-  }
-
-  return NULL;
-}
-
-static int
-convert_tai_offset(struct timespec *sample_time, double *offset)
-{
-  struct timespec tai_ts, utc_ts;
-  int tai_offset;
-
-  /* Get approximate TAI-UTC offset for the reference time in TAI */
-  UTI_AddDoubleToTimespec(sample_time, *offset, &tai_ts);
-  tai_offset = REF_GetTaiOffset(&tai_ts);
-
-  /* Get TAI-UTC offset for the reference time in UTC +/- 1 second */
-  UTI_AddDoubleToTimespec(&tai_ts, -tai_offset, &utc_ts);
-  tai_offset = REF_GetTaiOffset(&utc_ts);
-
-  if (!tai_offset)
-    return 0;
-
-  *offset -= tai_offset;
-
-  return 1;
-}
-
-int
-RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
-{
-  double correction, dispersion;
-  struct timespec cooked_time;
-
-  if (instance->pps_forced)
-    return RCL_AddPulse(instance, sample_time, -offset);
-
-  LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
-  UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
-  dispersion += instance->precision;
-
-  /* Make sure the timestamp and offset provided by the driver are sane */
-  if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
-      !valid_sample_time(instance, &cooked_time))
-    return 0;
-
-  switch (leap) {
-    case LEAP_Normal:
-    case LEAP_InsertSecond:
-    case LEAP_DeleteSecond:
-      instance->leap_status = leap;
-      break;
-    default:
-      DEBUG_LOG("refclock sample ignored bad leap %d", leap);
-      return 0;
-  }
-
-  if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
-    DEBUG_LOG("refclock sample ignored unknown TAI offset");
-    return 0;
-  }
-
-  filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
-  instance->pps_active = 0;
-
-  log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
-
-  /* for logging purposes */
-  if (!instance->driver->poll)
-    instance->driver_polled++;
-
-  return 1;
-}
-
-int
-RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
-{
-  double correction, dispersion;
-  struct timespec cooked_time;
-
-  LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
-  UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
-  second += correction;
-
-  if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
-    return 0;
-
-  return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
-}
-
-static int
-check_pulse_edge(RCL_Instance instance, double offset, double distance)
-{
-  double max_error;
-
-  if (instance->pulse_width <= 0.0)
-    return 1;
-
-  max_error = 1.0 / instance->pps_rate - instance->pulse_width;
-  max_error = MIN(instance->pulse_width, max_error);
-  max_error *= 0.5;
-
-  if (fabs(offset) > max_error || distance > max_error) {
-      DEBUG_LOG("refclock pulse ignored offset=%.9f distance=%.9f max_error=%.9f",
-                offset, distance, max_error);
-      return 0;
-  }
-
-  return 1;
-}
-
-int
-RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
-                   double second, double dispersion, double raw_correction)
-{
-  double offset;
-  int rate;
-  NTP_Leap leap;
-
-  if (!UTI_IsTimeOffsetSane(cooked_time, second) ||
-      !valid_sample_time(instance, cooked_time))
-    return 0;
-
-  leap = LEAP_Normal;
-  dispersion += instance->precision;
-  rate = instance->pps_rate;
-
-  offset = -second + instance->offset;
-
-  /* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
-  offset -= (long)(offset * rate) / (double)rate;
-  if (offset < -0.5 / rate)
-    offset += 1.0 / rate;
-  else if (offset >= 0.5 / rate)
-    offset -= 1.0 / rate;
-
-  if (instance->lock_ref != -1) {
-    RCL_Instance lock_refclock;
-    struct timespec ref_sample_time;
-    double sample_diff, ref_offset, ref_dispersion, shift;
-
-    lock_refclock = get_refclock(instance->lock_ref);
-
-    if (!filter_get_last_sample(&lock_refclock->filter,
-          &ref_sample_time, &ref_offset, &ref_dispersion)) {
-      DEBUG_LOG("refclock pulse ignored no ref sample");
-      return 0;
-    }
-
-    ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
-
-    sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample_time);
-    if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
-      DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
-          sample_diff);
-      return 0;
-    }
-
-    /* Align the offset to the reference sample */
-    if ((ref_offset - offset) >= 0.0)
-      shift = (long)((ref_offset - offset) * rate + 0.5) / (double)rate;
-    else
-      shift = (long)((ref_offset - offset) * rate - 0.5) / (double)rate;
-
-    offset += shift;
-
-    if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
-      DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
-          ref_offset - offset, ref_dispersion, dispersion);
-      return 0;
-    }
-
-    if (!check_pulse_edge(instance, ref_offset - offset, 0.0))
-      return 0;
-
-    leap = lock_refclock->leap_status;
-
-    DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
-              offset, ref_offset - offset, sample_diff);
-  } else {
-    struct timespec ref_time;
-    int is_synchronised, stratum;
-    double root_delay, root_dispersion, distance;
-    uint32_t ref_id;
-
-    /* Ignore the pulse if we are not well synchronized and the local
-       reference is not active */
-
-    REF_GetReferenceParams(cooked_time, &is_synchronised, &leap, &stratum,
-        &ref_id, &ref_time, &root_delay, &root_dispersion);
-    distance = fabs(root_delay) / 2 + root_dispersion;
-
-    if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) {
-      DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
-                offset, leap != LEAP_Unsynchronised, distance);
-      /* Drop also all stored samples */
-      filter_reset(&instance->filter);
-      return 0;
-    }
-
-    if (!check_pulse_edge(instance, offset, distance))
-      return 0;
-  }
-
-  filter_add_sample(&instance->filter, cooked_time, offset, dispersion);
-  instance->leap_status = leap;
-  instance->pps_active = 1;
-
-  log_sample(instance, cooked_time, 0, 1, offset + raw_correction - instance->offset,
-             offset, dispersion);
-
-  /* for logging purposes */
-  if (!instance->driver->poll)
-    instance->driver_polled++;
-
-  return 1;
-}
-
-double
-RCL_GetPrecision(RCL_Instance instance)
-{
-  return instance->precision;
-}
-
-int
-RCL_GetDriverPoll(RCL_Instance instance)
-{
-  return instance->driver_poll;
-}
-
-static int
-valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
-{
-  struct timespec now, last_sample_time;
-  double diff, last_offset, last_dispersion;
-
-  LCL_ReadCookedTime(&now, NULL);
-  diff = UTI_DiffTimespecsToDouble(&now, sample_time);
-
-  if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
-      (filter_get_samples(&instance->filter) > 0 &&
-       filter_get_last_sample(&instance->filter, &last_sample_time,
-                              &last_offset, &last_dispersion) &&
-       UTI_CompareTimespecs(&last_sample_time, sample_time) >= 0)) {
-    DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
-              UTI_RefidToString(instance->ref_id),
-              UTI_TimespecToString(sample_time), diff);
-    return 0;
-  }
-
-  return 1;
-}
-
-static int
-pps_stratum(RCL_Instance instance, struct timespec *ts)
-{
-  struct timespec ref_time;
-  int is_synchronised, stratum;
-  unsigned int i;
-  double root_delay, root_dispersion;
-  NTP_Leap leap;
-  uint32_t ref_id;
-  RCL_Instance refclock;
-
-  REF_GetReferenceParams(ts, &is_synchronised, &leap, &stratum,
-      &ref_id, &ref_time, &root_delay, &root_dispersion);
-
-  /* Don't change our stratum if the local reference is active
-     or this is the current source */
-  if (ref_id == instance->ref_id ||
-      (!is_synchronised && leap != LEAP_Unsynchronised))
-    return stratum - 1;
-
-  /* Or the current source is another PPS refclock */ 
-  for (i = 0; i < ARR_GetSize(refclocks); i++) {
-    refclock = get_refclock(i);
-    if (refclock->ref_id == ref_id &&
-        refclock->pps_active && refclock->lock_ref == -1)
-      return stratum - 1;
-  }
-
-  return 0;
-}
-
-static void
-poll_timeout(void *arg)
-{
-  int poll;
-
-  RCL_Instance inst = (RCL_Instance)arg;
-
-  poll = inst->poll;
-
-  if (inst->driver->poll) {
-    poll = inst->driver_poll;
-    inst->driver->poll(inst);
-    inst->driver_polled++;
-  }
-  
-  if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
-    double offset, dispersion;
-    struct timespec sample_time;
-    int sample_ok, stratum;
-
-    sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
-    inst->driver_polled = 0;
-
-    if (sample_ok) {
-      if (inst->pps_active && inst->lock_ref == -1)
-        /* Handle special case when PPS is used with local stratum */
-        stratum = pps_stratum(inst, &sample_time);
-      else
-        stratum = inst->stratum;
-
-      SRC_UpdateReachability(inst->source, 1);
-      SRC_AccumulateSample(inst->source, &sample_time, offset,
-          inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
-      SRC_SelectSource(inst->source);
-
-      log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion);
-    } else {
-      SRC_UpdateReachability(inst->source, 0);
-    }
-  }
-
-  inst->timeout_id = SCH_AddTimeoutByDelay(UTI_Log2ToDouble(poll), poll_timeout, arg);
-}
-
-static void
-slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
-             double doffset, LCL_ChangeType change_type, void *anything)
-{
-  unsigned int i;
-
-  for (i = 0; i < ARR_GetSize(refclocks); i++) {
-    if (change_type == LCL_ChangeUnknownStep)
-      filter_reset(&get_refclock(i)->filter);
-    else
-      filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset);
-  }
-}
-
-static void
-add_dispersion(double dispersion, void *anything)
-{
-  unsigned int i;
-
-  for (i = 0; i < ARR_GetSize(refclocks); i++)
-    filter_add_dispersion(&get_refclock(i)->filter, dispersion);
-}
-
-static void
-log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
-{
-  char sync_stats[4] = {'N', '+', '-', '?'};
-
-  if (logfileid == -1)
-    return;
-
-  if (!filtered) {
-    LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e",
-      UTI_TimeToLogForm(sample_time->tv_sec),
-      (int)sample_time->tv_nsec / 1000,
-      UTI_RefidToString(instance->ref_id),
-      instance->driver_polled,
-      sync_stats[instance->leap_status],
-      pulse,
-      raw_offset,
-      cooked_offset,
-      dispersion);
-  } else {
-    LOG_FileWrite(logfileid, "%s.%06d %-5s   - %1c -       -       %13.6e %10.3e",
-      UTI_TimeToLogForm(sample_time->tv_sec),
-      (int)sample_time->tv_nsec / 1000,
-      UTI_RefidToString(instance->ref_id),
-      sync_stats[instance->leap_status],
-      cooked_offset,
-      dispersion);
-  }
-}
-
-static void
-filter_init(struct MedianFilter *filter, int length, double max_dispersion)
-{
-  if (length < 1)
-    length = 1;
-
-  filter->length = length;
-  filter->index = -1;
-  filter->used = 0;
-  filter->last = -1;
-  /* set first estimate to system precision */
-  filter->avg_var_n = 0;
-  filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
-  filter->max_var = max_dispersion * max_dispersion;
-  filter->samples = MallocArray(struct FilterSample, filter->length);
-  filter->selected = MallocArray(int, filter->length);
-  filter->x_data = MallocArray(double, filter->length);
-  filter->y_data = MallocArray(double, filter->length);
-  filter->w_data = MallocArray(double, filter->length);
-}
-
-static void
-filter_fini(struct MedianFilter *filter)
-{
-  Free(filter->samples);
-  Free(filter->selected);
-  Free(filter->x_data);
-  Free(filter->y_data);
-  Free(filter->w_data);
-}
-
-static void
-filter_reset(struct MedianFilter *filter)
-{
-  filter->index = -1;
-  filter->used = 0;
-}
-
-static double
-filter_get_avg_sample_dispersion(struct MedianFilter *filter)
-{
-  return sqrt(filter->avg_var);
-}
-
-static void
-filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
-{
-  filter->index++;
-  filter->index %= filter->length;
-  filter->last = filter->index;
-  if (filter->used < filter->length)
-    filter->used++;
-
-  filter->samples[filter->index].sample_time = *sample_time;
-  filter->samples[filter->index].offset = offset;
-  filter->samples[filter->index].dispersion = dispersion;
-
-  DEBUG_LOG("filter sample %d t=%s offset=%.9f dispersion=%.9f",
-      filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
-}
-
-static int
-filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
-{
-  if (filter->last < 0)
-    return 0;
-
-  *sample_time = filter->samples[filter->last].sample_time;
-  *offset = filter->samples[filter->last].offset;
-  *dispersion = filter->samples[filter->last].dispersion;
-  return 1;
-}
-
-static int
-filter_get_samples(struct MedianFilter *filter)
-{
-  return filter->used;
-}
-
-static const struct FilterSample *tmp_sorted_array;
-
-static int
-sample_compare(const void *a, const void *b)
-{
-  const struct FilterSample *s1, *s2;
-
-  s1 = &tmp_sorted_array[*(int *)a];
-  s2 = &tmp_sorted_array[*(int *)b];
-
-  if (s1->offset < s2->offset)
-    return -1;
-  else if (s1->offset > s2->offset)
-    return 1;
-  return 0;
-}
-
-int
-filter_select_samples(struct MedianFilter *filter)
-{
-  int i, j, k, o, from, to, *selected;
-  double min_dispersion;
-
-  if (filter->used < 1)
-    return 0;
-
-  /* for lengths below 4 require full filter,
-     for 4 and above require at least 4 samples */
-  if ((filter->length < 4 && filter->used != filter->length) ||
-      (filter->length >= 4 && filter->used < 4))
-    return 0;
-
-  selected = filter->selected;
-
-  if (filter->used > 4) {
-    /* select samples with dispersion better than 1.5 * minimum */
-
-    for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) {
-      if (min_dispersion > filter->samples[i].dispersion)
-        min_dispersion = filter->samples[i].dispersion;
-    }
-
-    for (i = j = 0; i < filter->used; i++) {
-      if (filter->samples[i].dispersion <= 1.5 * min_dispersion)
-        selected[j++] = i;
-    }
-  } else {
-    j = 0;
-  }
-
-  if (j < 4) {
-    /* select all samples */
-
-    for (j = 0; j < filter->used; j++)
-      selected[j] = j;
-  }
-
-  /* and sort their indices by offset */
-  tmp_sorted_array = filter->samples;
-  qsort(selected, j, sizeof (int), sample_compare);
-
-  /* select 60 percent of the samples closest to the median */ 
-  if (j > 2) {
-    from = j / 5;
-    if (from < 1)
-      from = 1;
-    to = j - from;
-  } else {
-    from = 0;
-    to = j;
-  }
-
-  /* mark unused samples and sort the rest from oldest to newest */
-
-  o = filter->used - filter->index - 1;
-
-  for (i = 0; i < from; i++)
-    selected[i] = -1;
-  for (; i < to; i++)
-    selected[i] = (selected[i] + o) % filter->used;
-  for (; i < filter->used; i++)
-    selected[i] = -1;
-
-  for (i = from; i < to; i++) {
-    j = selected[i];
-    selected[i] = -1;
-    while (j != -1 && selected[j] != j) {
-      k = selected[j];
-      selected[j] = j;
-      j = k;
-    }
-  }
-
-  for (i = j = 0, k = -1; i < filter->used; i++) {
-    if (selected[i] != -1)
-      selected[j++] = (selected[i] + filter->used - o) % filter->used;
-  }
-
-  return j;
-}
-
-static int
-filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
-{
-  struct FilterSample *s, *ls;
-  int i, n, dof;
-  double x, y, d, e, var, prev_avg_var;
-
-  n = filter_select_samples(filter);
-
-  if (n < 1)
-    return 0;
-
-  ls = &filter->samples[filter->selected[n - 1]];
-
-  /* prepare data */
-  for (i = 0; i < n; i++) {
-    s = &filter->samples[filter->selected[i]];
-
-    filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
-    filter->y_data[i] = s->offset;
-    filter->w_data[i] = s->dispersion;
-  }
-
-  /* mean offset, sample time and sample dispersion */ 
-  for (i = 0, x = y = e = 0.0; i < n; i++) {
-    x += filter->x_data[i];
-    y += filter->y_data[i];
-    e += filter->w_data[i];
-  }
-  x /= n;
-  y /= n;
-  e /= n;
-
-  if (n >= 4) {
-    double b0, b1, s2, sb0, sb1;
-
-    /* set y axis to the mean sample time */
-    for (i = 0; i < n; i++)
-      filter->x_data[i] -= x;
-
-    /* make a linear fit and use the estimated standard deviation of intercept
-       as dispersion */
-    RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
-        &b0, &b1, &s2, &sb0, &sb1);
-    var = s2;
-    d = sb0;
-    dof = n - 2;
-  } else if (n >= 2) {
-    for (i = 0, d = 0.0; i < n; i++)
-      d += (filter->y_data[i] - y) * (filter->y_data[i] - y);
-    var = d / (n - 1);
-    d = sqrt(var);
-    dof = n - 1;
-  } else {
-    var = filter->avg_var;
-    d = sqrt(var);
-    dof = 1;
-  }
-
-  /* avoid having zero dispersion */
-  if (var < 1e-20) {
-    var = 1e-20;
-    d = sqrt(var);
-  }
-
-  /* drop the sample if variance is larger than allowed maximum */
-  if (filter->max_var > 0.0 && var > filter->max_var) {
-    DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
-        sqrt(var), sqrt(filter->max_var));
-    return 0;
-  }
-
-  prev_avg_var = filter->avg_var;
-
-  /* update exponential moving average of the variance */
-  if (filter->avg_var_n > 50) {
-    filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
-  } else {
-    filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
-      (dof + filter->avg_var_n);
-    if (filter->avg_var_n == 0)
-      prev_avg_var = filter->avg_var;
-    filter->avg_var_n += dof;
-  }
-
-  /* reduce noise in sourcestats weights by using the long-term average
-     instead of the estimated variance if it's not significantly lower */
-  if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
-    d = sqrt(filter->avg_var) * d / sqrt(var);
-
-  if (d < e)
-    d = e;
-
-  UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
-  *offset = y;
-  *dispersion = d;
-
-  filter_reset(filter);
-
-  return 1;
-}
-
-static void
-filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
-{
-  int i, first, last;
-  double delta_time;
-  struct timespec *sample;
-
-  if (filter->last < 0)
-    return;
-
-  /* always slew the last sample as it may be needed by PPS refclocks */
-  if (filter->used > 0) {
-    first = 0;
-    last = filter->used - 1;
-  } else {
-    first = last = filter->last;
-  }
-
-  for (i = first; i <= last; i++) {
-    sample = &filter->samples[i].sample_time;
-    UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
-    filter->samples[i].offset -= delta_time;
-  }
-}
-
-static void
-filter_add_dispersion(struct MedianFilter *filter, double dispersion)
-{
-  int i;
-
-  for (i = 0; i < filter->used; i++) {
-    filter->samples[i].dispersion += dispersion;
-  }
-}
diff --git a/chrony_3_3/test/simulation/008-ntpera b/chrony_3_3/test/simulation/008-ntpera
deleted file mode 100755
index 3c63419..0000000
--- a/chrony_3_3/test/simulation/008-ntpera
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/bash
-
-. ./test.common
-test_start "NTP eras"
-
-# Assume NTP_ERA_SPLIT is between years 1960 and 1990
-
-# Set date to 500 seconds before NTP second overflows, this should
-# work correctly with both 32-bit and 64-bit time_t
-export CLKNETSIM_START_DATE=$(date -d 'Feb  7 06:19:56 UTC 2036' +'%s')
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_source_selection || test_fail
-check_packet_interval || test_fail
-check_sync || test_fail
-
-# The following tests need 64-bit time_t
-grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
-
-for year in 1990 2090; do
-	export CLKNETSIM_START_DATE=$(date -d "Jan  1 00:00:00 UTC $year" +'%s')
-	run_test || test_fail
-	check_chronyd_exit || test_fail
-	check_source_selection || test_fail
-	check_packet_interval || test_fail
-	check_sync || test_fail
-done
-
-for year in 1950 2130; do
-	export CLKNETSIM_START_DATE=$(date -d "Jan  1 00:00:00 UTC $year" +'%s')
-	run_test || test_fail
-	check_chronyd_exit || test_fail
-	check_source_selection || test_fail
-	check_packet_interval || test_fail
-	# This check is expected to fail
-	check_sync && test_fail
-done
-
-test_pass
diff --git a/chrony_3_3/test/simulation/103-initstepslew b/chrony_3_3/test/simulation/103-initstepslew
deleted file mode 100755
index 303020e..0000000
--- a/chrony_3_3/test/simulation/103-initstepslew
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-
-. ./test.common
-test_start "initstepslew directive"
-
-freq_offset=0.0
-wander=0.0
-time_rms_limit=1e-3
-limit=100
-
-client_conf="initstepslew 5 192.168.123.1"
-
-min_sync_time=6
-max_sync_time=35
-
-for time_offset in -2.0 -0.2 0.2 2.0; do
-	run_test || test_fail
-	check_chronyd_exit || test_fail
-	check_packet_interval || test_fail
-	check_sync || test_fail
-done
-
-min_sync_time=5
-max_sync_time=5
-
-for time_offset in -1e8 -1e2 1e2 1e8; do
-	run_test || test_fail
-	check_packet_interval || test_fail
-	check_sync || test_fail
-done
-
-test_pass
diff --git a/chrony_3_3/test/simulation/106-refclock b/chrony_3_3/test/simulation/106-refclock
deleted file mode 100755
index c22cd42..0000000
--- a/chrony_3_3/test/simulation/106-refclock
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-
-. ./test.common
-test_start "SHM refclock"
-
-servers=0
-limit=1000
-refclock_jitter=$jitter
-min_sync_time=45
-max_sync_time=70
-chronyc_start=70
-client_conf="refclock SHM 0 stratum 3 delay 1e-3 refid GPS"
-chronyc_conf="tracking"
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_source_selection || test_fail
-check_sync || test_fail
-check_chronyc_output "^Reference ID.*47505300 \(GPS\)
-Stratum.*: 4
-.*
-Root delay      : 0.001000000 seconds
-.*
-Update interval : 16\.. seconds
-.*$" || test_fail
-
-test_pass
diff --git a/chrony_3_3/test/simulation/108-peer b/chrony_3_3/test/simulation/108-peer
deleted file mode 100755
index 0679f6e..0000000
--- a/chrony_3_3/test/simulation/108-peer
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-
-. ./test.common
-
-test_start "NTP peers"
-
-# Allow and drop packets to the server in 1000 second intervals, so only one
-# client has access to it and the other is forced to switch to the peer.
-base_delay=$(cat <<-EOF | tr -d '\n'
-  (+ 1e-4
-     (* -1
-        (equal 0.1 from 2)
-        (equal 0.1 to 1)
-        (equal 0.1 (min (% time 2000) 1000) 1000))
-     (* -1
-        (equal 0.1 from 3)
-        (equal 0.1 to 1)
-	(equal 0.1 (max (% time 2000) 1000) 1000)))
-EOF
-)
-
-clients=2
-peers=2
-max_sync_time=1000
-client_server_options="minpoll 6 maxpoll 6"
-client_peer_options="minpoll 6 maxpoll 6"
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_source_selection || test_fail
-check_sync || test_fail
-
-test_pass
diff --git a/chrony_3_3/test/simulation/110-chronyc b/chrony_3_3/test/simulation/110-chronyc
deleted file mode 100755
index d56e724..0000000
--- a/chrony_3_3/test/simulation/110-chronyc
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/bash
-
-. ./test.common
-
-test_start "chronyc"
-
-refclock_jitter=$jitter
-client_conf="
-refclock SHM 0 noselect
-smoothtime 400 0.001 leaponly"
-
-chronyc_conf="activity
-tracking
-sources
-sourcestats
-manual list
-smoothing
-waitsync
-rtcdata"
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-
-check_chronyc_output "^200 OK
-1 sources online
-0 sources offline
-0 sources doing burst \(return to online\)
-0 sources doing burst \(return to offline\)
-0 sources with unknown address
-Reference ID    : C0A87B01 \(192\.168\.123\.1\)
-Stratum         : 2
-Ref time \(UTC\)  : Fri Jan 01 00:1.:.. 2010
-System time     : 0\.0000..... seconds (slow|fast) of NTP time
-Last offset     : [+-]0\.000...... seconds
-RMS offset      : 0\.000...... seconds
-Frequency       : (99|100)\.... ppm fast
-Residual freq   : [+-][0-9]\.... ppm
-Skew            : [0-9]\.... ppm
-Root delay      : 0\.000...... seconds
-Root dispersion : 0\.000...... seconds
-Update interval : [0-9]+\.. seconds
-Leap status     : Normal
-210 Number of sources = 2
-MS Name/IP address         Stratum Poll Reach LastRx Last sample               
-===============================================================================
-#\? SHM0                          0   4   377    [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
-\^\* 192\.168\.123\.1                 1   [67]   377    [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
-210 Number of sources = 2
-Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
-==============================================================================
-SHM0                       [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\....  [0-9 +-]+[un]s [0-9 ]+[un]s
-192\.168\.123\.1              [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\....  [0-9 +-]+[un]s [0-9 ]+[un]s
-210 n_samples = 0
-#    Date     Time\(UTC\)    Slewed   Original   Residual
-=======================================================
-Active         : Yes \(leap second only\)
-Offset         : \+0\.000000000 seconds
-Frequency      : \+0\.000000 ppm
-Wander         : \+0\.000000 ppm per second
-Last update    : [0-9]+\.. seconds ago
-Remaining time : 0\.0 seconds
-try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
-513 RTC driver not running$" \
-|| test_fail
-
-test_pass
diff --git a/chrony_3_3/test/simulation/115-cmdmontime b/chrony_3_3/test/simulation/115-cmdmontime
deleted file mode 100755
index 2806a1f..0000000
--- a/chrony_3_3/test/simulation/115-cmdmontime
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-. ./test.common
-
-test_start "cmdmon timestamps"
-
-# The following tests need 64-bit time_t
-grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
-
-limit=2
-client_server_options="noselect"
-client_conf="local stratum 1"
-chronyc_start="1.5"
-chronyc_conf="tracking"
-
-for year in `seq 1850 100 2300`; do
-	date="Jan 01 00:00:00 $year"
-	export CLKNETSIM_START_DATE=$(date -d "$date UTC" +'%s')
-	run_test || test_fail
-	check_chronyd_exit || test_fail
-	check_chronyc_output "^.*Ref time \(UTC\).*$date.*$" || test_fail
-done
-
-test_pass
diff --git a/chrony_3_3/test/simulation/122-xleave b/chrony_3_3/test/simulation/122-xleave
deleted file mode 100755
index 93f767e..0000000
--- a/chrony_3_3/test/simulation/122-xleave
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-. ./test.common
-test_start "interleaved mode"
-
-client_server_options="xleave"
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_source_selection || test_fail
-check_packet_interval || test_fail
-check_sync || test_fail
-
-clients=2
-peers=2
-max_sync_time=500
-base_delay="(+ 1e-4 (* -1 (equal 0.1 from 2) (equal 0.1 to 1)))"
-
-client_lpeer_options="xleave minpoll 5 maxpoll 5"
-client_rpeer_options="minpoll 5 maxpoll 5"
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-# These checks are expected to fail
-check_source_selection && test_fail
-check_sync && test_fail
-
-for rpoll in 4 5 6; do
-	client_rpeer_options="xleave minpoll $rpoll maxpoll $rpoll"
-
-	run_test || test_fail
-	check_chronyd_exit || test_fail
-	check_source_selection || test_fail
-	check_sync || test_fail
-done
-
-test_pass
diff --git a/chrony_3_3/test/unit/clientlog.c b/chrony_3_3/test/unit/clientlog.c
deleted file mode 100644
index 515ad1a..0000000
--- a/chrony_3_3/test/unit/clientlog.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- **********************************************************************
- * Copyright (C) Miroslav Lichvar  2016
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
- */
-
-#include <clientlog.c>
-#include "test.h"
-
-void
-test_unit(void)
-{
-  int i, j, index;
-  struct timespec ts;
-  IPAddr ip;
-  char conf[][100] = {
-    "clientloglimit 10000",
-    "ratelimit interval 3 burst 4 leak 3",
-    "cmdratelimit interval 3 burst 4 leak 3",
-  };
-
-  CNF_Initialise(0, 0);
-  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
-    CNF_ParseLine(NULL, i + 1, conf[i]);
-
-  CLG_Initialise();
-
-  TEST_CHECK(ARR_GetSize(records) == 16);
-
-  for (i = 0; i < 500; i++) {
-    DEBUG_LOG("iteration %d", i);
-
-    ts.tv_sec = (time_t)random() & 0x0fffffff;
-    ts.tv_nsec = 0;
-
-    for (j = 0; j < 1000; j++) {
-      TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
-      DEBUG_LOG("address %s", UTI_IPToString(&ip));
-
-      if (random() % 2) {
-        index = CLG_LogNTPAccess(&ip, &ts);
-        TEST_CHECK(index >= 0);
-        CLG_LimitNTPResponseRate(index);
-      } else {
-        index = CLG_LogCommandAccess(&ip, &ts);
-        TEST_CHECK(index >= 0);
-        CLG_LimitCommandResponseRate(index);
-      }
-
-      UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts);
-    }
-  }
-
-  DEBUG_LOG("records %d", ARR_GetSize(records));
-  TEST_CHECK(ARR_GetSize(records) == 64);
-
-  for (i = j = 0; i < 10000; i++) {
-    ts.tv_sec += 1;
-    index = CLG_LogNTPAccess(&ip, &ts);
-    TEST_CHECK(index >= 0);
-    if (!CLG_LimitNTPResponseRate(index))
-      j++;
-  }
-
-  DEBUG_LOG("requests %u responses %u", i, j);
-  TEST_CHECK(j * 4 < i && j * 6 > i);
-
-  CLG_Finalise();
-  CNF_Finalise();
-}
diff --git a/chrony_3_3/test/unit/hwclock.c b/chrony_3_3/test/unit/hwclock.c
deleted file mode 100644
index 1cbd312..0000000
--- a/chrony_3_3/test/unit/hwclock.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- **********************************************************************
- * Copyright (C) Miroslav Lichvar  2016-2017
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
- */
-
-#include <hwclock.c>
-#include "test.h"
-
-void
-test_unit(void)
-{
-  struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
-  HCL_Instance clock;
-  double freq, jitter, interval, dj, sum;
-  int i, j, count;
-
-  LCL_Initialise();
-
-  clock = HCL_CreateInstance(1.0);
-
-  for (i = 0, count = 0, sum = 0.0; i < 2000; i++) {
-    UTI_ZeroTimespec(&start_hw_ts);
-    UTI_ZeroTimespec(&start_local_ts);
-    UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts);
-    UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts);
-
-    DEBUG_LOG("iteration %d", i);
-
-    freq = TST_GetRandomDouble(0.9, 1.1);
-    jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
-    interval = TST_GetRandomDouble(0.1, 10.0);
-
-    clock->n_samples = 0;
-    clock->valid_coefs = 0;
-
-    for (j = 0; j < 100; j++) {
-      UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq, &hw_ts);
-      UTI_AddDoubleToTimespec(&start_local_ts, j * interval, &local_ts);
-      if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
-        dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
-        DEBUG_LOG("delta/jitter %f", dj);
-        if (clock->n_samples >= 8)
-          sum += dj, count++;
-        TEST_CHECK(clock->n_samples < 4 || dj <= 4.0);
-        TEST_CHECK(clock->n_samples < 8 || dj <= 3.0);
-      }
-
-      UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
-
-      if (HCL_NeedsNewSample(clock, &local_ts))
-        HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
-
-      TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
-
-      if (!clock->valid_coefs)
-        continue;
-
-      TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
-    }
-  }
-
-  TEST_CHECK(sum / count < 0.4);
-
-  HCL_DestroyInstance(clock);
-
-  LCL_Finalise();
-}
diff --git a/chrony_3_3/test/unit/ntp_core.c b/chrony_3_3/test/unit/ntp_core.c
deleted file mode 100644
index 074c4d7..0000000
--- a/chrony_3_3/test/unit/ntp_core.c
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- **********************************************************************
- * Copyright (C) Miroslav Lichvar  2017-2018
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
- */
-
-#include <config.h>
-#include <sysincl.h>
-#include <cmdparse.h>
-#include <conf.h>
-#include <keys.h>
-#include <ntp_io.h>
-#include <sched.h>
-#include <local.h>
-#include "test.h"
-
-static struct timespec current_time;
-static NTP_Receive_Buffer req_buffer, res_buffer;
-static int req_length, res_length;
-
-#define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0)
-#define NIO_CloseServerSocket(fd) assert(fd == 100)
-#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
-#define NIO_CloseClientSocket(fd) assert(fd == 101)
-#define NIO_IsServerSocket(fd) (fd == 100)
-#define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1)
-#define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1))
-#define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \
-  add_timeout_in_class(delay, separation, randomness, class, handler, arg)
-#define SCH_RemoveTimeout(id) assert(!id || id == 102)
-#define LCL_ReadRawTime(ts) (*ts = current_time)
-#define LCL_ReadCookedTime(ts, err) do {double *p = err; *ts = current_time; if (p) *p = 0.0;} while (0)
-#define LCL_GetSysPrecisionAsLog() (random() % 10 - 30)
-#define SRC_UpdateReachability(inst, reach)
-#define SRC_ResetReachability(inst)
-
-static SCH_TimeoutID
-add_timeout_in_class(double min_delay, double separation, double randomness,
-                     SCH_TimeoutClass class, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
-{
-  return 102;
-}
-
-#include <ntp_core.c>
-
-static void
-advance_time(double x)
-{
-  UTI_AddDoubleToTimespec(&current_time, x, &current_time);
-}
-
-static void
-send_request(NCR_Instance inst)
-{
-  NTP_Local_Address local_addr;
-  NTP_Local_Timestamp local_ts;
-  uint32_t prev_tx_count;
-
-  prev_tx_count = inst->report.total_tx_count;
-
-  transmit_timeout(inst);
-  TEST_CHECK(!inst->valid_rx);
-  TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count);
-
-  advance_time(1e-5);
-
-  if (random() % 2) {
-    local_addr.ip_addr.family = IPADDR_UNSPEC;
-    local_addr.if_index = INVALID_IF_INDEX;
-    local_addr.sock_fd = 101;
-    local_ts.ts = current_time;
-    local_ts.err = 0.0;
-    local_ts.source = NTP_TS_KERNEL;
-
-    NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length);
-  }
-}
-
-static void
-process_request(NTP_Remote_Address *remote_addr)
-{
-  NTP_Local_Address local_addr;
-  NTP_Local_Timestamp local_ts;
-
-  local_addr.ip_addr.family = IPADDR_UNSPEC;
-  local_addr.if_index = INVALID_IF_INDEX;
-  local_addr.sock_fd = 100;
-  local_ts.ts = current_time;
-  local_ts.err = 0.0;
-  local_ts.source = NTP_TS_KERNEL;
-
-  res_length = 0;
-  NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts,
-                       &req_buffer.ntp_pkt, req_length);
-  res_length = req_length;
-  res_buffer = req_buffer;
-
-  advance_time(1e-5);
-
-  if (random() % 2) {
-    local_ts.ts = current_time;
-    NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts,
-                         &res_buffer.ntp_pkt, res_length);
-  }
-}
-
-static void
-send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth)
-{
-  NTP_Packet *req, *res;
-
-  req = &req_buffer.ntp_pkt;
-  res = &res_buffer.ntp_pkt;
-
-  TEST_CHECK(req_length >= NTP_NORMAL_PACKET_LENGTH);
-
-  res->lvm = NTP_LVM(LEAP_Normal, NTP_LVM_TO_VERSION(req->lvm),
-                     NTP_LVM_TO_MODE(req->lvm) == MODE_CLIENT ? MODE_SERVER : MODE_ACTIVE);
-  res->stratum = 1;
-  res->poll = req->poll;
-  res->precision = -20;
-  res->root_delay = UTI_DoubleToNtp32(0.1);
-  res->root_dispersion = UTI_DoubleToNtp32(0.1);
-  res->reference_id = 0;
-  UTI_ZeroNtp64(&res->reference_ts);
-  res->originate_ts = interleaved ? req->receive_ts : req->transmit_ts;
-
-  advance_time(TST_GetRandomDouble(1e-4, 1e-2));
-  UTI_TimespecToNtp64(&current_time, &res->receive_ts, NULL);
-  advance_time(TST_GetRandomDouble(-1e-4, 1e-3));
-  UTI_TimespecToNtp64(&current_time, &res->transmit_ts, NULL);
-  advance_time(TST_GetRandomDouble(1e-4, 1e-2));
-
-  if (!valid_ts) {
-    switch (random() % (allow_update ? 4 : 5)) {
-      case 0:
-        res->originate_ts.hi = random();
-        break;
-      case 1:
-        res->originate_ts.lo = random();
-        break;
-      case 2:
-        UTI_ZeroNtp64(&res->originate_ts);
-        break;
-      case 3:
-        UTI_ZeroNtp64(&res->receive_ts);
-        break;
-      case 4:
-        UTI_ZeroNtp64(&res->transmit_ts);
-        break;
-      default:
-        assert(0);
-    }
-  }
-
-  if (authenticated) {
-    res->auth_keyid = req->auth_keyid;
-    KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, NTP_NORMAL_PACKET_LENGTH,
-                     res->auth_data, 16);
-    res_length = NTP_NORMAL_PACKET_LENGTH + 4 + 16;
-  } else {
-    res_length = NTP_NORMAL_PACKET_LENGTH;
-  }
-
-  if (!valid_auth) {
-    switch (random() % 3) {
-      case 0:
-        res->auth_keyid++;
-        break;
-      case 1:
-        res->auth_data[random() % 16]++;
-        break;
-      case 2:
-        res_length = NTP_NORMAL_PACKET_LENGTH;
-        break;
-      default:
-        assert(0);
-    }
-  }
-}
-
-static void
-process_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init)
-{
-  NTP_Local_Address local_addr;
-  NTP_Local_Timestamp local_ts;
-  NTP_Packet *res;
-  uint32_t prev_rx_count, prev_valid_count;
-  struct timespec prev_rx_ts, prev_init_rx_ts;
-  int prev_open_socket, ret;
-
-  res = &res_buffer.ntp_pkt;
-
-  local_addr.ip_addr.family = IPADDR_UNSPEC;
-  local_addr.if_index = INVALID_IF_INDEX;
-  local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) != MODE_SERVER ? 100 : 101;
-  local_ts.ts = current_time;
-  local_ts.err = 0.0;
-  local_ts.source = NTP_TS_KERNEL;
-
-  prev_rx_count = inst->report.total_rx_count;
-  prev_valid_count = inst->report.total_valid_count;
-  prev_rx_ts = inst->local_rx.ts;
-  prev_init_rx_ts = inst->init_local_rx.ts;
-  prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD;
-
-  ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
-
-  if (good > 0)
-    TEST_CHECK(ret);
-  else if (!good)
-    TEST_CHECK(!ret);
-
-  if (prev_open_socket)
-    TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count);
-  else
-    TEST_CHECK(prev_rx_count == inst->report.total_rx_count);
-
-  if (valid)
-    TEST_CHECK(prev_valid_count + 1 == inst->report.total_valid_count);
-  else
-    TEST_CHECK(prev_valid_count == inst->report.total_valid_count);
-
-  if (updated_sync)
-    TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
-  else
-    TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
-
-  if (updated_init > 0)
-    TEST_CHECK(UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
-  else if (!updated_init)
-    TEST_CHECK(!UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
-
-  if (valid) {
-    TEST_CHECK(UTI_IsZeroTimespec(&inst->init_local_rx.ts));
-    TEST_CHECK(UTI_IsZeroNtp64(&inst->init_remote_ntp_tx));
-  }
-}
-
-static void
-process_replay(NCR_Instance inst, NTP_Receive_Buffer *packet_queue,
-               int queue_length, int updated_init)
-{
-  do {
-    res_buffer = packet_queue[random() % queue_length];
-  } while (!UTI_CompareNtp64(&res_buffer.ntp_pkt.transmit_ts,
-                             &inst->remote_ntp_tx));
-  process_response(inst, 0, 0, 0, updated_init);
-  advance_time(1e-6);
-}
-
-#define PACKET_QUEUE_LENGTH 10
-
-void
-test_unit(void)
-{
-  char source_line[] = "127.0.0.1 maxdelaydevratio 1e6";
-  char conf[][100] = {
-    "allow",
-    "port 0",
-    "local",
-    "keyfile ntp_core.keys"
-  };
-  int i, j, k, interleaved, authenticated, valid, updated, has_updated;
-  CPS_NTP_Source source;
-  NTP_Remote_Address remote_addr;
-  NCR_Instance inst1, inst2;
-  NTP_Receive_Buffer packet_queue[PACKET_QUEUE_LENGTH];
-
-  CNF_Initialise(0, 0);
-  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
-    CNF_ParseLine(NULL, i + 1, conf[i]);
-
-  LCL_Initialise();
-  TST_RegisterDummyDrivers();
-  SCH_Initialise();
-  SRC_Initialise();
-  NIO_Initialise(IPADDR_UNSPEC);
-  NCR_Initialise();
-  REF_Initialise();
-  KEY_Initialise();
-
-  CNF_SetupAccessRestrictions();
-
-  CPS_ParseNTPSourceAdd(source_line, &source);
-
-  for (i = 0; i < 1000; i++) {
-    if (random() % 2)
-      source.params.interleaved = 1;
-    if (random() % 2)
-      source.params.authkey = 1;
-    source.params.version = random() % 4 + 1;
-
-    UTI_ZeroTimespec(&current_time);
-    advance_time(TST_GetRandomDouble(1.0, 1e9));
-
-    TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
-    remote_addr.port = 123;
-
-    inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
-    NCR_StartInstance(inst1);
-    has_updated = 0;
-
-    for (j = 0; j < 50; j++) {
-      DEBUG_LOG("client/peer test iteration %d/%d", i, j);
-
-      interleaved = random() % 2 && (inst1->mode != MODE_CLIENT ||
-                                     inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
-      authenticated = random() % 2;
-      valid = (!interleaved || (source.params.interleaved && has_updated)) &&
-              (!source.params.authkey || authenticated);
-      updated = (valid || inst1->mode == MODE_ACTIVE) &&
-                (!source.params.authkey || authenticated);
-      has_updated = has_updated || updated;
-      if (inst1->mode == MODE_CLIENT)
-        updated = 0;
-
-      send_request(inst1);
-
-      send_response(interleaved, authenticated, 1, 0, 1);
-      DEBUG_LOG("response 1");
-      process_response(inst1, 0, 0, 0, updated);
-
-      if (source.params.authkey) {
-        send_response(interleaved, authenticated, 1, 1, 0);
-        DEBUG_LOG("response 2");
-        process_response(inst1, 0, 0, 0, 0);
-      }
-
-      send_response(interleaved, authenticated, 1, 1, 1);
-      DEBUG_LOG("response 3");
-      process_response(inst1, -1, valid, valid, updated);
-      DEBUG_LOG("response 4");
-      process_response(inst1, 0, 0, 0, 0);
-
-      advance_time(-1.0);
-
-      send_response(interleaved, authenticated, 1, 1, 1);
-      DEBUG_LOG("response 5");
-      process_response(inst1, 0, 0, 0, updated && valid);
-
-      advance_time(1.0);
-
-      send_response(interleaved, authenticated, 1, 1, 1);
-      DEBUG_LOG("response 6");
-      process_response(inst1, 0, 0, valid && updated, updated);
-    }
-
-    NCR_DestroyInstance(inst1);
-
-    inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
-    NCR_StartInstance(inst1);
-
-    for (j = 0; j < 20; j++) {
-      DEBUG_LOG("server test iteration %d/%d", i, j);
-
-      send_request(inst1);
-      process_request(&remote_addr);
-      process_response(inst1, 1, 1, 1, 0);
-      advance_time(1 << inst1->local_poll);
-    }
-
-    NCR_DestroyInstance(inst1);
-
-    inst1 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params);
-    NCR_StartInstance(inst1);
-    inst2 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params);
-    NCR_StartInstance(inst2);
-
-    res_length = req_length = 0;
-
-    for (j = 0; j < 20; j++) {
-      DEBUG_LOG("peer replay test iteration %d/%d", i, j);
-
-      send_request(inst1);
-      res_buffer = req_buffer;
-      assert(!res_length || res_length == req_length);
-      res_length = req_length;
-
-      TEST_CHECK(inst1->valid_timestamps == (j > 0));
-
-      DEBUG_LOG("response 1->2");
-      process_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1);
-
-      packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer;
-
-      for (k = 0; k < j % 4 + 1; k++) {
-        DEBUG_LOG("replay ?->1 %d", k);
-        process_replay(inst1, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), k ? -1 : 1);
-        DEBUG_LOG("replay ?->2 %d", k);
-        process_replay(inst2, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), -1);
-      }
-
-      advance_time(1 << (source.params.minpoll - 1));
-
-      send_request(inst2);
-      res_buffer = req_buffer;
-      assert(res_length == req_length);
-
-      TEST_CHECK(inst2->valid_timestamps == (j > 0));
-
-      DEBUG_LOG("response 2->1");
-      process_response(inst1, 1, 1, 1, 1);
-
-      packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer;
-
-      for (k = 0; k < j % 4 + 1; k++) {
-        DEBUG_LOG("replay ?->1 %d", k);
-        process_replay(inst1, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), k ? -1 : 1);
-        DEBUG_LOG("replay ?->2 %d", k);
-        process_replay(inst2, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), -1);
-      }
-
-      advance_time(1 << (source.params.minpoll - 1));
-    }
-
-    NCR_DestroyInstance(inst1);
-    NCR_DestroyInstance(inst2);
-  }
-
-  KEY_Finalise();
-  REF_Finalise();
-  NCR_Finalise();
-  NIO_Finalise();
-  SRC_Finalise();
-  SCH_Finalise();
-  LCL_Finalise();
-  CNF_Finalise();
-  HSH_Finalise();
-}
diff --git a/chrony_3_3/test/unit/ntp_core.keys b/chrony_3_3/test/unit/ntp_core.keys
deleted file mode 100644
index 7a70e52..0000000
--- a/chrony_3_3/test/unit/ntp_core.keys
+++ /dev/null
@@ -1,2 +0,0 @@
-1 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
-2 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
diff --git a/chrony_3_3/test/unit/ntp_sources.c b/chrony_3_3/test/unit/ntp_sources.c
deleted file mode 100644
index ea8f19c..0000000
--- a/chrony_3_3/test/unit/ntp_sources.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- **********************************************************************
- * Copyright (C) Miroslav Lichvar  2016
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
- */
-
-#include <ntp_sources.c>
-#include <conf.h>
-#include <ntp_io.h>
-#include "test.h"
-
-void
-test_unit(void)
-{
-  int i, j, k, slot, found;
-  uint32_t hash = 0;
-  NTP_Remote_Address addrs[256], addr;
-  SourceParameters params;
-  char conf[] = "port 0";
-
-  memset(&params, 0, sizeof (params));
-
-  CNF_Initialise(0, 0);
-  CNF_ParseLine(NULL, 1, conf);
-
-  LCL_Initialise();
-  SCH_Initialise();
-  SRC_Initialise();
-  NIO_Initialise(IPADDR_UNSPEC);
-  NCR_Initialise();
-  NSR_Initialise();
-
-  for (i = 0; i < 6; i++) {
-    TEST_CHECK(ARR_GetSize(records) == 1);
-
-    DEBUG_LOG("collision mod %u", 1U << i);
-
-    for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
-      do {
-        TST_GetRandomAddress(&addrs[j].ip_addr, IPADDR_UNSPEC, -1);
-      } while (UTI_IPToHash(&addrs[j].ip_addr) % (1U << i) != hash % (1U << i));
-
-      addrs[j].port = random() % 1024;
-
-      if (!j)
-        hash = UTI_IPToHash(&addrs[j].ip_addr);
-
-      DEBUG_LOG("adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr),
-                UTI_IPToHash(&addrs[j].ip_addr) % (1U << i));
-
-      NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, &params);
-
-      for (k = 0; k < j; k++) {
-        addr = addrs[k];
-        find_slot(&addr, &slot, &found);
-        TEST_CHECK(found == 2);
-        TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
-                                   &addr.ip_addr, NULL));
-        addr.port++;
-        find_slot(&addr, &slot, &found);
-        TEST_CHECK(found == 1);
-        TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
-                                   &addr.ip_addr, NULL));
-      }
-    }
-
-    for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
-      DEBUG_LOG("removing source %s", UTI_IPToString(&addrs[j].ip_addr));
-      NSR_RemoveSource(&addrs[j]);
-
-      for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) {
-        find_slot(&addrs[k], &slot, &found);
-        TEST_CHECK(found == (k <= j ? 0 : 2));
-      }
-    }
-  }
-
-  NSR_Finalise();
-  NCR_Finalise();
-  NIO_Finalise();
-  SRC_Finalise();
-  SCH_Finalise();
-  LCL_Finalise();
-  CNF_Finalise();
-  HSH_Finalise();
-}
diff --git a/chrony_3_3/test/unit/sources.c b/chrony_3_3/test/unit/sources.c
deleted file mode 100644
index 341e22e..0000000
--- a/chrony_3_3/test/unit/sources.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- **********************************************************************
- * Copyright (C) Miroslav Lichvar  2016
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
- */
-
-#include <sources.c>
-#include "test.h"
-
-void
-test_unit(void)
-{
-  SRC_Instance srcs[16];
-  RPT_SourceReport report;
-  IPAddr addr;
-  int i, j, k, l, samples, sel_options;
-  double offset, delay, disp;
-  struct timespec ts;
-
-  CNF_Initialise(0, 0);
-  LCL_Initialise();
-  TST_RegisterDummyDrivers();
-  SCH_Initialise();
-  SRC_Initialise();
-  REF_Initialise();
-
-  REF_SetMode(REF_ModeIgnore);
-
-  for (i = 0; i < 1000; i++) {
-    DEBUG_LOG("iteration %d", i);
-
-    for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
-      TEST_CHECK(n_sources == j);
-
-      TST_GetRandomAddress(&addr, IPADDR_UNSPEC, -1);
-
-      sel_options = i & random() & (SRC_SELECT_NOSELECT | SRC_SELECT_PREFER |
-                                    SRC_SELECT_TRUST | SRC_SELECT_REQUIRE);
-
-      DEBUG_LOG("added source %d options %d", j, sel_options);
-      srcs[j] = SRC_CreateNewInstance(UTI_IPToRefid(&addr), SRC_NTP, sel_options, &addr,
-                                      SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES,
-                                      0.0, 1.0);
-      SRC_UpdateReachability(srcs[j], 1);
-
-      samples = (i + j) % 5 + 3;
-
-      offset = TST_GetRandomDouble(-1.0, 1.0);
-
-      for (k = 0; k < samples; k++) {
-        SCH_GetLastEventTime(&ts, NULL, NULL);
-        UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(k - samples, k - samples + 1), &ts);
-
-        offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
-        delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
-        disp = TST_GetRandomDouble(1.0e-6, 1.0e-1);
-
-        DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k,
-                  offset, delay, disp);
-
-        SRC_AccumulateSample(srcs[j], &ts, offset, delay, disp, delay, disp,
-                             1, LEAP_Normal);
-      }
-
-      for (k = 0; k <= j; k++) {
-        int passed = 0, trusted = 0, trusted_passed = 0, required = 0, required_passed = 0;
-        double trusted_lo = DBL_MAX, trusted_hi = DBL_MIN;
-        double passed_lo = DBL_MAX, passed_hi = DBL_MIN;
-
-        SRC_SelectSource(srcs[k]);
-        DEBUG_LOG("source %d status %d", k, sources[k]->status);
-
-        for (l = 0; l <= j; l++) {
-          TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED);
-          if (sources[l]->sel_options & SRC_SELECT_NOSELECT) {
-            TEST_CHECK(sources[l]->status == SRC_UNSELECTABLE);
-          } else if (sources[l]->status != SRC_BAD_DISTANCE) {
-            if (sources[l]->status >= SRC_NONPREFERRED) {
-              passed++;
-              if (passed_lo > sources[l]->sel_info.lo_limit)
-                passed_lo = sources[l]->sel_info.lo_limit;
-              if (passed_hi < sources[l]->sel_info.hi_limit)
-                passed_hi = sources[l]->sel_info.hi_limit;
-            }
-            if (sources[l]->sel_options & SRC_SELECT_TRUST) {
-              trusted++;
-              if (trusted_lo > sources[l]->sel_info.lo_limit)
-                trusted_lo = sources[l]->sel_info.lo_limit;
-              if (trusted_hi < sources[l]->sel_info.hi_limit)
-                trusted_hi = sources[l]->sel_info.hi_limit;
-              if (sources[l]->status >= SRC_NONPREFERRED)
-                trusted_passed++;
-            }
-            if (sources[l]->sel_options & SRC_SELECT_REQUIRE) {
-              required++;
-              if (sources[l]->status >= SRC_NONPREFERRED)
-                required_passed++;
-            }
-            if (sources[l]->sel_options & SRC_SELECT_PREFER)
-              TEST_CHECK(sources[l]->status != SRC_NONPREFERRED);
-          }
-        }
-
-        DEBUG_LOG("sources %d passed %d trusted %d/%d required %d/%d", j, passed,
-                  trusted_passed, trusted, required_passed, required);
-
-        TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi));
-        TEST_CHECK(!passed || trusted != 1 || (trusted == 1 && trusted_passed == 1));
-        TEST_CHECK(!passed || !required || required_passed > 0);
-      }
-    }
-
-    for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
-      SRC_ReportSource(j, &report, &ts);
-      SRC_DestroyInstance(srcs[j]);
-    }
-  }
-
-  REF_Finalise();
-  SRC_Finalise();
-  SCH_Finalise();
-  LCL_Finalise();
-  CNF_Finalise();
-  HSH_Finalise();
-}
diff --git a/chrony_3_3/test/unit/util.c b/chrony_3_3/test/unit/util.c
deleted file mode 100644
index 5f1a653..0000000
--- a/chrony_3_3/test/unit/util.c
+++ /dev/null
@@ -1,199 +0,0 @@
-#include <util.c>
-#include "test.h"
-
-void test_unit(void) {
-  NTP_int64 ntp_ts, ntp_fuzz;
-  struct timespec ts, ts2;
-  struct timeval tv;
-  struct sockaddr_un sun;
-  double x, y;
-  Float f;
-  int i, j, c;
-  char buf[16], *s;
-
-  for (i = -31; i < 31; i++) {
-    x = pow(2.0, i);
-    y = UTI_Log2ToDouble(i);
-    TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
-  }
-
-  for (i = -89; i < 63; i++) {
-    x = pow(2.0, i);
-    y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
-    TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
-  }
-
-  for (i = 0; i < 100000; i++) {
-    x = TST_GetRandomDouble(-1000.0, 1000.0);
-    y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
-    TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
-
-    UTI_GetRandomBytes(&f, sizeof (f));
-    x = UTI_FloatNetworkToHost(f);
-    TEST_CHECK(x > 0.0 || x <= 0.0);
-  }
-
-  TEST_CHECK(UTI_DoubleToNtp32(1.0) == htonl(65536));
-  TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0));
-  TEST_CHECK(UTI_DoubleToNtp32(1.0 / (65536.0)) == htonl(1));
-  TEST_CHECK(UTI_DoubleToNtp32(1.000001 / (65536.0)) == htonl(2));
-  TEST_CHECK(UTI_DoubleToNtp32(1.000001) == htonl(65537));
-  TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff));
-  TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0));
-
-  UTI_DoubleToTimeval(0.4e-6, &tv);
-  TEST_CHECK(tv.tv_sec == 0);
-  TEST_CHECK(tv.tv_usec == 0);
-  UTI_DoubleToTimeval(-0.4e-6, &tv);
-  TEST_CHECK(tv.tv_sec == 0);
-  TEST_CHECK(tv.tv_usec == 0);
-  UTI_DoubleToTimeval(0.5e-6, &tv);
-  TEST_CHECK(tv.tv_sec == 0);
-  TEST_CHECK(tv.tv_usec == 1);
-  UTI_DoubleToTimeval(-0.5e-6, &tv);
-  TEST_CHECK(tv.tv_sec == -1);
-  TEST_CHECK(tv.tv_usec == 999999);
-
-  UTI_DoubleToTimespec(0.9e-9, &ts);
-  TEST_CHECK(ts.tv_sec == 0);
-  TEST_CHECK(ts.tv_nsec == 0);
-  UTI_DoubleToTimespec(1.0e-9, &ts);
-  TEST_CHECK(ts.tv_sec == 0);
-  TEST_CHECK(ts.tv_nsec == 1);
-  UTI_DoubleToTimespec(-0.9e-9, &ts);
-  TEST_CHECK(ts.tv_sec == 0);
-  TEST_CHECK(ts.tv_nsec == 0);
-  UTI_DoubleToTimespec(-1.0e-9, &ts);
-  TEST_CHECK(ts.tv_sec == -1);
-  TEST_CHECK(ts.tv_nsec == 999999999);
-
-  ntp_ts.hi = htonl(JAN_1970);
-  ntp_ts.lo = 0xffffffff;
-  UTI_Ntp64ToTimespec(&ntp_ts, &ts);
-  TEST_CHECK(ts.tv_sec == 0);
-  TEST_CHECK(ts.tv_nsec == 999999999);
-
-  UTI_AddDoubleToTimespec(&ts, 1e-9, &ts);
-  TEST_CHECK(ts.tv_sec == 1);
-  TEST_CHECK(ts.tv_nsec == 0);
-
-  ntp_fuzz.hi = 0;
-  ntp_fuzz.lo = htonl(0xff1234ff);
-
-  UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
-  TEST_CHECK(ntp_ts.hi == htonl(JAN_1970 + 1));
-  TEST_CHECK(ntp_ts.lo == ntp_fuzz.lo);
-
-  ts.tv_sec = ts.tv_nsec = 0;
-  UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
-  TEST_CHECK(ntp_ts.hi == 0);
-  TEST_CHECK(ntp_ts.lo == 0);
-
-  TEST_CHECK(UTI_IsZeroTimespec(&ts));
-  TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
-
-  ts.tv_sec = 1;
-  ntp_ts.hi = htonl(1);
-
-  TEST_CHECK(!UTI_IsZeroTimespec(&ts));
-  TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
-
-  ts.tv_sec = 0;
-  ntp_ts.hi = 0;
-  ts.tv_nsec = 1;
-  ntp_ts.lo = htonl(1);
-
-  TEST_CHECK(!UTI_IsZeroTimespec(&ts));
-  TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
-
-  ntp_ts.hi = 0;
-  ntp_ts.lo = 0;
-
-  UTI_Ntp64ToTimespec(&ntp_ts, &ts);
-  TEST_CHECK(UTI_IsZeroTimespec(&ts));
-  UTI_TimespecToNtp64(&ts, &ntp_ts, NULL);
-  TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
-
-  ntp_fuzz.hi = htonl(1);
-  ntp_fuzz.lo = htonl(3);
-  ntp_ts.hi = htonl(1);
-  ntp_ts.lo = htonl(2);
-
-  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
-  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
-  TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
-
-  ntp_ts.hi = htonl(0x80000002);
-  ntp_ts.lo = htonl(2);
-
-  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
-  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
-  TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
-
-  ntp_fuzz.hi = htonl(0x90000001);
-
-  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
-  TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
-  TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
-
-  TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_ts, NULL, NULL));
-  TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, &ntp_ts, NULL));
-  TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, NULL, &ntp_ts));
-  TEST_CHECK(!UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_fuzz, &ntp_fuzz, &ntp_fuzz));
-
-  ts.tv_sec = 1;
-  ts.tv_nsec = 2;
-  ts2.tv_sec = 1;
-  ts2.tv_nsec = 3;
-
-  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
-  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
-  TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
-
-  ts2.tv_sec = 2;
-
-  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
-  TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
-  TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
-
-  for (i = -32; i <= 32; i++) {
-    for (j = c = 0; j < 1000; j++) {
-      UTI_GetNtp64Fuzz(&ntp_fuzz, i);
-      if (i <= 0)
-        TEST_CHECK(ntp_fuzz.hi == 0);
-      if (i < 0)
-        TEST_CHECK(ntohl(ntp_fuzz.lo) < 1U << (32 + i));
-      else if (i < 32)
-        TEST_CHECK(ntohl(ntp_fuzz.hi) < 1U << i);
-      if (ntohl(ntp_fuzz.lo) >= 1U << (31 + CLAMP(-31, i, 0)))
-        c++;
-    }
-
-    if (i == -32)
-      TEST_CHECK(c == 0);
-    else
-      TEST_CHECK(c > 400 && c < 600);
-  }
-
-  for (i = c = 0; i < 100000; i++) {
-    j = random() % (sizeof (buf) + 1);
-    UTI_GetRandomBytes(buf, j);
-    if (j && buf[j - 1] % 2)
-      c++;
-  }
-  TEST_CHECK(c > 46000 && c < 48000);
-
-  for (i = 1; i < 2 * BUFFER_LENGTH; i++) {
-    sun.sun_family = AF_UNIX;
-    for (j = 0; j + 1 < i && j + 1 < sizeof (sun.sun_path); j++)
-      sun.sun_path[j] = 'A' + j % 26;
-    sun.sun_path[j] = '\0';
-    s = UTI_SockaddrToString((struct sockaddr *)&sun);
-    if (i <= BUFFER_LENGTH) {
-      TEST_CHECK(!strcmp(s, sun.sun_path));
-    } else {
-      TEST_CHECK(!strncmp(s, sun.sun_path, BUFFER_LENGTH - 2));
-      TEST_CHECK(s[BUFFER_LENGTH - 2] == '>');
-    }
-  }
-}
diff --git a/chrony_3_3/util.h b/chrony_3_3/util.h
deleted file mode 100644
index 40ff729..0000000
--- a/chrony_3_3/util.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
-  chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Richard P. Curnow  1997-2003
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- * 
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- * 
- **********************************************************************
-
-  =======================================================================
-
-  Various utility functions
-  */
-
-#ifndef GOT_UTIL_H
-#define GOT_UTIL_H
-
-#include "sysincl.h"
-
-#include "addressing.h"
-#include "ntp.h"
-#include "candm.h"
-#include "hash.h"
-
-/* Zero a timespec */
-extern void UTI_ZeroTimespec(struct timespec *ts);
-
-/* Check if a timespec is zero */
-extern int UTI_IsZeroTimespec(struct timespec *ts);
-
-/* Convert a timeval into a timespec */
-extern void UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts);
-
-/* Convert a timespec into a timeval */
-extern void UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv);
-
-/* Convert a timespec into a floating point number of seconds */
-extern double UTI_TimespecToDouble(struct timespec *ts);
-
-/* Convert a number of seconds expressed in floating point into a
-   timespec */
-extern void UTI_DoubleToTimespec(double d, struct timespec *ts);
-
-/* Normalise a timespec, by adding or subtracting seconds to bring
-   its nanosecond field into range */
-extern void UTI_NormaliseTimespec(struct timespec *ts);
-
-/* Convert a timeval into a floating point number of seconds */
-extern double UTI_TimevalToDouble(struct timeval *tv);
-
-/* Convert a number of seconds expressed in floating point into a
-   timeval */
-extern void UTI_DoubleToTimeval(double a, struct timeval *b);
-
-/* Normalise a struct timeval, by adding or subtracting seconds to bring
-   its microseconds field into range */
-extern void UTI_NormaliseTimeval(struct timeval *x);
-
-/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
-   and +1 if a comes after b */
-extern int UTI_CompareTimespecs(struct timespec *a, struct timespec *b);
-
-/* Calculate result = a - b */
-extern void UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b);
-
-/* Calculate result = a - b and return as a double */
-extern double UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b);
-
-/* Add a double increment to a timespec to get a new one. 'start' is
-   the starting time, 'end' is the result that we return.  This is
-   safe to use if start and end are the same */
-extern void UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end);
-
-/* Calculate the average and difference (as a double) of two timespecs */
-extern void UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later, struct timespec *average, double *diff);
-
-/* Calculate result = a - b + c */
-extern void UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b, struct timespec *c, struct timespec *result);
-
-/* Convert a timespec into a temporary string, largely for diagnostic
-   display */
-extern char *UTI_TimespecToString(struct timespec *ts);
-
-/* Convert an NTP timestamp into a temporary string, largely for
-   diagnostic display */
-extern char *UTI_Ntp64ToString(NTP_int64 *ts);
-
-/* Convert ref_id into a temporary string, for diagnostics */
-extern char *UTI_RefidToString(uint32_t ref_id);
-
-/* Convert an IP address to string, for diagnostics */
-extern char *UTI_IPToString(IPAddr *ip);
-
-extern int UTI_StringToIP(const char *addr, IPAddr *ip);
-extern uint32_t UTI_IPToRefid(IPAddr *ip);
-extern uint32_t UTI_IPToHash(IPAddr *ip);
-extern void UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest);
-extern void UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest);
-extern int UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask);
-
-extern void UTI_SockaddrToIPAndPort(struct sockaddr *sa, IPAddr *ip, unsigned short *port);
-extern int UTI_IPAndPortToSockaddr(IPAddr *ip, unsigned short port, struct sockaddr *sa);
-extern char *UTI_SockaddrToString(struct sockaddr *sa);
-extern const char *UTI_SockaddrFamilyToString(int family);
-
-extern char *UTI_TimeToLogForm(time_t t);
-
-/* Adjust time following a frequency/offset change */
-extern void UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset);
-
-/* Get zero NTP timestamp with random bits below precision */
-extern void UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision);
-
-extern double UTI_Ntp32ToDouble(NTP_int32 x);
-extern NTP_int32 UTI_DoubleToNtp32(double x);
-
-/* Zero an NTP timestamp */
-extern void UTI_ZeroNtp64(NTP_int64 *ts);
-
-/* Check if an NTP timestamp is zero */
-extern int UTI_IsZeroNtp64(NTP_int64 *ts);
-
-/* Compare two NTP timestamps.  Returns -1 if a is before b, 0 if a is equal to
-   b, and 1 if a is after b. */
-extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b);
-
-/* Compare an NTP timestamp with up to three other timestamps.  Returns 0
-   if a is not equal to any of b1, b2, and b3, 1 otherwise. */
-extern int UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3);
-
-/* Convert a timespec into an NTP timestamp */
-extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz);
-
-/* Convert an NTP timestamp into a timespec */
-extern void UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest);
-
-/* Check if time + offset is sane */
-extern int UTI_IsTimeOffsetSane(struct timespec *ts, double offset);
-
-/* Get 2 raised to power of a signed integer */
-extern double UTI_Log2ToDouble(int l);
-
-extern void UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest);
-extern void UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest);
-
-extern double UTI_FloatNetworkToHost(Float x);
-extern Float UTI_FloatHostToNetwork(double x);
-
-/* Set FD_CLOEXEC on descriptor */
-extern int UTI_FdSetCloexec(int fd);
-
-extern int UTI_SetQuitSignalsHandler(void (*handler)(int));
-
-/* Get directory (as an allocated string) for a path */
-extern char *UTI_PathToDir(const char *path);
-
-/* Create a directory with a specified mode (umasked) and set its uid/gid.
-   Create also any parent directories that don't exist with mode 755 and
-   default uid/gid.  Returns 1 if created or already exists (even with
-   different mode/uid/gid), 0 otherwise. */
-extern int UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid_t gid);
-
-/* Check if a directory is secure.  It must not have other than the specified
-   permissions and its uid/gid must match the specified values. */
-extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid);
-
-/* Set process user/group IDs and drop supplementary groups */
-extern void UTI_DropRoot(uid_t uid, gid_t gid);
-
-/* Fill buffer with random bytes from /dev/urandom */
-extern void UTI_GetRandomBytesUrandom(void *buf, unsigned int len);
-
-/* Fill buffer with random bytes from /dev/urandom or a faster source if it's
-   available (e.g. arc4random()), which may not necessarily be suitable for
-   generating long-term keys */
-extern void UTI_GetRandomBytes(void *buf, unsigned int len);
-
-/* Macros to get maximum and minimum of two values */
-#ifdef MAX
-#undef MAX
-#endif
-#define MAX(x, y) ((x) > (y) ? (x) : (y))
-#ifdef MIN
-#undef MIN
-#endif
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-
-/* Macro to clamp a value between two values */
-#define CLAMP(min, x, max) (MAX((min), MIN((x), (max))))
-
-#endif /* GOT_UTIL_H */
diff --git a/chrony_3_3/version.txt b/chrony_3_3/version.txt
deleted file mode 100644
index eb39e53..0000000
--- a/chrony_3_3/version.txt
+++ /dev/null
@@ -1 +0,0 @@
-3.3