diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:43:21 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:43:21 +0000 |
commit | 104f986b0650b8f93540785d2bcf486905e49b62 (patch) | |
tree | 2b2ae5113d9b57425d4bb3f726e325316b87e00a | |
parent | Initial commit. (diff) | |
download | chrony-upstream/3.4.tar.xz chrony-upstream/3.4.zip |
Adding upstream version 3.4.upstream/3.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
217 files changed, 61137 insertions, 0 deletions
@@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. @@ -0,0 +1,541 @@ +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. Can chronyd be configured to control the clock like ntpd? + ? 2.9. 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? + ? 3.4. Using a Windows NTP server? + ? 3.5. 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. 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). + +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 may 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 unusally 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 in order to allow the +server to send the client 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 may be possible. For example: + +server ntp.local minpoll 0 maxpoll 0 xleave +hwtimestamp eth0 + +If it is acceptable for NTP clients in the network to send requests at an +excessive rate, a sub-second polling interval may 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 + +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. 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. + +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 + +2.9. 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 + +3.4. 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 may 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.5. 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 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-09-19 16:38:15 CEST @@ -0,0 +1,174 @@ +Installation + +The software is distributed as source code which has to be compiled. The source +code is supplied in the form of a gzipped tar file, which unpacks to a +subdirectory identifying the name and version of the program. + +After unpacking the source code, change directory into it, and type + +./configure + +This is a shell script that automatically determines the system type. There is +an optional parameter --prefix, which indicates the directory tree where the +software should be installed. For example, + +./configure --prefix=/opt/free + +will install the chronyd daemon into /opt/free/sbin and the chronyc control +program into /opt/free/bin. The default value for the prefix is /usr/local. + +The configure script assumes you want to use gcc as your compiler. If you want +to use a different compiler, you can configure this way: + +CC=cc ./configure --prefix=/opt/free + +for Bourne-family shells, or + +setenv CC cc +setenv CFLAGS -O +./configure --prefix=/opt/free + +for C-family shells. + +If the software cannot (yet) be built on your system, an error message will be +shown. Otherwise, Makefile will be generated. + +On Linux, if development files for the libcap library are available, chronyd +will be built with support for dropping root privileges. On other systems no +extra library is needed. The default user which chronyd should run as can be +specified with the --with-user option of the configure script. + +If development files for the POSIX threads library are available, chronyd will +be built with support for asynchronous resolving of hostnames specified in the +server, peer, and pool directives. This allows chronyd operating as a server to +respond to client requests when resolving a hostname. If you don't want to +enable the support, specify the --disable-asyncdns flag to configure. + +If development files for the Nettle, NSS, or 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, +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 +in a location that isn't normally searched by the compiler, you can add it to +the searched locations by setting the CPPFLAGS variable to -I/path/to/timepps. + +The --help option can be specified to configure to print all options supported +by the script. + +Now type + +make + +to build the programs. + +If you want to build the manual in HTML, type + +make docs + +Once the programs have been successfully compiled, they need to be installed in +their target locations. This step normally needs to be performed by the +superuser, and requires the following command to be entered. + +make install + +This will install the binaries and man pages. + +To install the HTML version of the manual, enter the command + +make install-docs + +Now that the software is successfully installed, the next step is to set up a +configuration file. The default location of the file is /etc/chrony.conf. +Several examples of configuration with comments are included in the examples +directory. Suppose you want to use public NTP servers from the pool.ntp.org +project as your time reference. A minimal useful configuration file could be + +pool pool.ntp.org iburst +makestep 1.0 3 +rtcsync + +Then, chronyd can be run. For security reasons, it's recommended to create an +unprivileged user for chronyd and specify it with the -u command-line option or +the user directive in the configuration file, or set the default user with the +--with-user configure option before building. + +Support for system call filtering + +chronyd can be built with support for the Linux secure computing (seccomp) +facility. This requires development files for the libseccomp library and the +--enable-scfilter option specified to configure. The -F option of chronyd will +enable a system call filter, which should significantly reduce 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 useful if +you are building a distribution package for chrony. + +The --mandir=DIR option to configure specifies an installation directory for +the man pages. This overrides the man subdirectory of the argument to the +--prefix option. + +./configure --prefix=/usr --mandir=/usr/share/man + +to set both options together. + +The final option is the DESTDIR option to the make command. For example, you +could use the commands + +./configure --prefix=/usr --mandir=/usr/share/man +make all docs +make install DESTDIR=./tmp +cd tmp +tar cvf - . | gzip -9 > chrony.tar.gz + +to build a package. When untarred within the root directory, this will install +the files to the intended final locations. + +Last updated 2018-09-19 16:38:15 CEST diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..2d3e67d --- /dev/null +++ b/Makefile.in @@ -0,0 +1,136 @@ +################################################## +# +# 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. +# +# ======================================================================= +# +# Makefile template + +SYSCONFDIR=@SYSCONFDIR@ +BINDIR=@BINDIR@ +SBINDIR=@SBINDIR@ +LOCALSTATEDIR=@LOCALSTATEDIR@ +CHRONYVARDIR=@CHRONYVARDIR@ + +CC = @CC@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ + +DESTDIR= + +HASH_OBJ = @HASH_OBJ@ + +OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \ + reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \ + smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ) + +EXTRA_OBJS=@EXTRA_OBJECTS@ + +CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \ + pktlength.o util.o $(HASH_OBJ) + +ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS) + +LDFLAGS = @LDFLAGS@ +LIBS = @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) + +chronyc : $(CLI_OBJS) + $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS) + +distclean : clean + $(MAKE) -C doc distclean + $(MAKE) -C test/unit distclean + -rm -f .DS_Store + -rm -f Makefile config.h config.log + +clean : + -rm -f *.o *.s chronyc chronyd core.* *~ + -rm -f *.gcda *.gcno + -rm -rf .deps + -rm -rf *.dSYM + +getdate.c : getdate.y + bison -o getdate.c getdate.y + +# This can be used to force regeneration of getdate.c +getdate : + bison -o getdate.c getdate.y + +# For install, don't use the install command, because its switches +# seem to vary between systems. + +install: chronyd chronyc + [ -d $(DESTDIR)$(SYSCONFDIR) ] || mkdir -p $(DESTDIR)$(SYSCONFDIR) + [ -d $(DESTDIR)$(SBINDIR) ] || mkdir -p $(DESTDIR)$(SBINDIR) + [ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + [ -d $(DESTDIR)$(CHRONYVARDIR) ] || mkdir -p $(DESTDIR)$(CHRONYVARDIR) + if [ -f $(DESTDIR)$(SBINDIR)/chronyd ]; then rm -f $(DESTDIR)$(SBINDIR)/chronyd ; fi + if [ -f $(DESTDIR)$(BINDIR)/chronyc ]; then rm -f $(DESTDIR)$(BINDIR)/chronyc ; fi + cp chronyd $(DESTDIR)$(SBINDIR)/chronyd + chmod 755 $(DESTDIR)$(SBINDIR)/chronyd + cp chronyc $(DESTDIR)$(BINDIR)/chronyc + chmod 755 $(DESTDIR)$(BINDIR)/chronyc + $(MAKE) -C doc install + +docs : + $(MAKE) -C doc docs + +install-docs : + $(MAKE) -C doc install-docs + +%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< + +%.s : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) -S $< + +quickcheck : chronyd chronyc + $(MAKE) -C test/unit check + cd test/simulation && ./run + +check : chronyd chronyc + $(MAKE) -C test/unit check + cd test/simulation && ./run -i 20 -m 2 + +print-chronyd-objects : + @echo $(OBJS) $(EXTRA_OBJS) + +Makefile : Makefile.in configure + @echo + @echo Makefile needs to be regenerated, run ./configure + @echo + @exit 1 + +.deps: + @mkdir .deps + +.deps/%.d: %.c | .deps + @$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@ + +-include $(ALL_OBJS:%.o=.deps/%.d) @@ -0,0 +1,836 @@ +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 +================== + +Enhancements +------------ +* Add burst option to server/pool directive +* Add stratum and tai options to refclock directive +* Add support for Nettle crypto library +* Add workaround for missing kernel receive timestamps on Linux +* Wait for late hardware transmit timestamps +* Improve source selection with unreachable sources +* Improve protection against replay attacks on symmetric mode +* Allow PHC refclock to use socket in /var/run/chrony +* Add shutdown command to stop chronyd +* Simplify format of response to manual list command +* Improve handling of unknown responses in chronyc + +Bug fixes +--------- +* Respond to NTPv1 client requests with zero mode +* Fix -x option to not require CAP_SYS_TIME under non-root user +* Fix acquisitionport directive to work with privilege separation +* Fix handling of socket errors on Linux to avoid high CPU usage +* Fix chronyc to not get stuck in infinite loop after clock step + +New in version 3.2 +================== + +Enhancements +------------ +* Improve stability with NTP sources and reference clocks +* Improve stability with hardware timestamping +* Improve support for NTP interleaved modes +* Control frequency of system clock on macOS 10.13 and later +* Set TAI-UTC offset of system clock with leapsectz directive +* Minimise data in client requests to improve privacy +* Allow transmit-only hardware timestamping +* Add support for new timestamping options introduced in Linux 4.13 +* Add root delay, root dispersion and maximum error to tracking log +* Add mindelay and asymmetry options to server/peer/pool directive +* Add extpps option to PHC refclock to timestamp external PPS signal +* Add pps option to refclock directive to treat any refclock as PPS +* Add width option to refclock directive to filter wrong pulse edges +* Add rxfilter option to hwtimestamp directive +* Add -x option to disable control of system clock +* Add -l option to log to specified file instead of syslog +* Allow multiple command-line options to be specified together +* Allow starting without root privileges with -Q option +* Update seccomp filter for new glibc versions +* Dump history on exit by default with dumpdir directive +* Use hardening compiler options by default + +Bug fixes +--------- +* Don't drop PHC samples with low-resolution system clock +* Ignore outliers in PHC tracking, RTC tracking, manual input +* Increase polling interval when peer is not responding +* Exit with error message when include directive fails +* Don't allow slash after hostname in allow/deny directive/command +* Try to connect to all addresses in chronyc before giving up + +New in version 3.1 +================== + +Enhancements +------------ +* Add support for precise cross timestamping of PHC on Linux +* Add minpoll, precision, nocrossts options to hwtimestamp directive +* Add rawmeasurements option to log directive and modify measurements + option to log only valid measurements from synchronised sources +* Allow sub-second polling interval with NTP sources + +Bug fixes +--------- +* Fix time smoothing in interleaved mode + +New in version 3.0 +================== + +Enhancements +------------ +* Add support for software and hardware timestamping on Linux +* Add support for client/server and symmetric interleaved modes +* Add support for MS-SNTP authentication in Samba +* Add support for truncated MACs in NTPv4 packets +* Estimate and correct for asymmetric network jitter +* Increase default minsamples and polltarget to improve stability + with very low jitter +* Add maxjitter directive to limit source selection by jitter +* Add offset option to server/pool/peer directive +* Add maxlockage option to refclock directive +* Add -t option to chronyd to exit after specified time +* Add partial protection against replay attacks on symmetric mode +* Don't reset polling interval when switching sources to online state +* Allow rate limiting with very short intervals +* Improve maximum server throughput on Linux and NetBSD +* Remove dump files after start +* Add tab-completion to chronyc with libedit/readline +* Add ntpdata command to print details about NTP measurements +* Allow all source options to be set in add server/peer command +* Indicate truncated addresses/hostnames in chronyc output +* Print reference IDs as hexadecimal numbers to avoid confusion with + IPv4 addresses + +Bug fixes +--------- +* Fix crash with disabled asynchronous name resolving + +New in version 2.4.1 +==================== + +Bug fixes +--------- +* Fix processing of kernel timestamps on non-Linux systems +* Fix crash with smoothtime directive +* Fix validation of refclock sample times +* Fix parsing of refclock directive + +New in version 2.4 +================== + +Enhancements +------------ +* Add orphan option to local directive for orphan mode compatible with ntpd +* Add distance option to local directive to set activation threshold + (1 second by default) +* Add maxdrift directive to set maximum allowed drift of system clock +* Try to replace NTP sources exceeding maximum distance +* Randomise source replacement to avoid getting stuck with bad sources +* Randomise selection of sources from pools on start +* Ignore reference timestamp as ntpd doesn't always set it correctly +* Modify tracking report to use same values as seen by NTP clients +* Add -c option to chronyc to write reports in CSV format +* Provide detailed manual pages + +Bug fixes +--------- +* Fix SOCK refclock to work correctly when not specified as last refclock +* Fix initstepslew and -q/-Q options to accept time from own NTP clients +* Fix authentication with keys using 512-bit hash functions +* Fix crash on exit when multiple signals are received +* Fix conversion of very small floating-point numbers in command packets + +Removed features +---------------- +* Drop documentation in Texinfo format + +New in version 2.3 +================== + +Enhancements +------------ +* Add support for NTP and command response rate limiting +* Add support for dropping root privileges on Mac OS X, FreeBSD, Solaris +* Add require and trust options for source selection +* Enable logchange by default (1 second threshold) +* Set RTC on Mac OS X with rtcsync directive +* Allow binding to NTP port after dropping root privileges on NetBSD +* Drop CAP_NET_BIND_SERVICE capability on Linux when NTP port is disabled +* Resolve names in separate process when seccomp filter is enabled +* Replace old records in client log when memory limit is reached +* Don't reveal local time and synchronisation state in client packets +* Don't keep client sockets open for longer than necessary +* Ignore poll in KoD RATE packets as ntpd doesn't always set it correctly +* Warn when using keys shorter than 80 bits +* Add keygen command to generate random keys easily +* Add serverstats command to report NTP and command packet statistics + +Bug fixes +--------- +* Fix clock correction after making step on Mac OS X +* Fix building on Solaris + +New in version 2.2.1 +==================== + +Security fixes +-------------- +* Restrict authentication of NTP server/peer to specified key (CVE-2016-1567) + +New in version 2.2 +================== + +Enhancements +------------ +* Add support for configuration and monitoring over Unix domain socket + (accessible by root or chrony user when root privileges are dropped) +* Add support for system call filtering with seccomp on Linux (experimental) +* Add support for dropping root privileges on NetBSD +* Control frequency of system clock on FreeBSD, NetBSD, Solaris +* Add system leap second handling mode on FreeBSD, NetBSD, Solaris +* Add dynamic drift removal on Mac OS X +* Add support for setting real-time priority on Mac OS X +* Add maxdistance directive to limit source selection by root distance + (3 seconds by default) +* Add refresh command to get new addresses of NTP sources +* Allow wildcard patterns in include directive +* Restore time from driftfile with -s option if later than RTC time +* Add configure option to set default hwclockfile +* Add -d option to chronyc to enable debug messages +* Allow multiple addresses to be specified for chronyc with -h option + and reconnect when no valid reply is received +* Make check interval in waitsync command configurable + +Bug fixes +--------- +* Fix building on NetBSD, Solaris +* Restore time from driftfile with -s option if reading RTC failed + +Removed features +---------------- +* Drop support for authentication with command key (run-time configuration + is now allowed only for local users that can access the Unix domain socket) + +New in version 2.1.1 +==================== + +Bug fixes +--------- +* Fix clock stepping by integer number of seconds on Linux + +New in version 2.1 +================== + +Enhancements +------------ +* Add support for Mac OS X +* Try to replace unreachable and falseticker servers/peers specified + by name like pool sources +* Add leaponly option to smoothtime directive to allow synchronised + leap smear between multiple servers +* Use specific reference ID when smoothing served time +* Add smoothing command to report time smoothing status +* Add smoothtime command to activate or reset time smoothing + +Bug fixes +--------- +* Fix crash in source selection with preferred sources +* Fix resetting of time smoothing +* Include packet precision in peer dispersion +* Fix crash in chronyc on invalid command syntax + +New in version 2.0 +================== + +Enhancements +------------ +* Update to NTP version 4 (RFC 5905) +* Add pool directive to specify pool of NTP servers +* Add leapsecmode directive to select how to correct clock for leap second +* Add smoothtime directive to smooth served time and enable leap smear +* Add minsources directive to set required number of selectable sources +* Add minsamples and maxsamples options for all sources +* Add tempcomp configuration with list of points +* Allow unlimited number of NTP sources, refclocks and keys +* Allow unreachable sources to remain selected +* Improve source selection +* Handle offline sources as unreachable +* Open NTP server port only when necessary (client access is allowed by + allow directive/command or peer/broadcast is configured) +* Change default bindcmdaddress to loopback address +* Change default maxdelay to 3 seconds +* Change default stratumweight to 0.001 +* Update adjtimex synchronisation status +* Use system headers for adjtimex +* Check for memory allocation errors +* Reduce memory usage +* Add configure options to compile without NTP, cmdmon, refclock support +* Extend makestep command to set automatic clock stepping + +Bug fixes +--------- +* Add sanity checks for time and frequency offset +* Don't report synchronised status during leap second +* Don't combine reference clocks with close NTP sources +* Fix accepting requests from configured sources +* Fix initial fallback drift setting + +New in version 1.31.1 +===================== + +Security fixes +-------------- +* Protect authenticated symmetric NTP associations against DoS attacks + (CVE-2015-1853) +* Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821) +* Fix initialization of reply slots for authenticated commands (CVE-2015-1822) + +New in version 1.31 +=================== + +Enhancements +------------ +* Support operation in other NTP eras (next era begins in 2036), + NTP time is mapped to [-50, +86] years around build date by default +* Restore time from driftfile with -s when RTC is missing/unsupported +* Close connected client sockets when not waiting for reply +* Use one client socket with random port when acquisitionport is 0 +* Use NTP packets instead of UDP echo for presend +* Don't adjust polling interval when sending fails +* Allow binding to addresses that don't exist yet +* Ignore measurements around leap second +* Improve detection of unexpected time jumps +* Include example of logrotate configuration, systemd services and + NetworkManager dispatcher script + +Bug fixes +--------- +* Reconnect client sockets for each request to follow changes + in network configuration automatically +* Restart timer when polling interval is changed on reset + +New in version 1.30 +=================== + +Enhancements +------------ +* Add asynchronous name resolving with POSIX threads +* Add PTP hardware clock (PHC) refclock driver +* Add new generic clock driver to slew by adjusting frequency only + (without kernel PLL or adjtime) and use it on Linux +* Add rtcautotrim directive to trim RTC automatically +* Add hwclockfile directive to share RTC LOCAL/UTC setting with hwclock +* Add maxslewrate directive to set maximum allowed slew rate +* Add maxdispersion option for refclocks +* Add -q/-Q options to set clock/print offset once and exit +* Allow directives to be specified on chronyd command line +* Replace frequency scaling in Linux driver with retaining of tick +* Try to detect unexpected forward time jumps and reset state +* Exit with non-zero code when maxchange limit is reached +* Improve makestep to not start and stop slew unnecessarily +* Change default corrtimeratio to 3.0 to improve frequency accuracy +* Announce leap second only on last day of June and December +* Use separate connected client sockets for each NTP server +* Remove separate NTP implementation used for initstepslew +* Limit maximum minpoll set by KoD RATE to default maxpoll +* Don't send NTP requests with unknown key +* Print warning when source is added with unknown key +* Take leap second in PPS refclock from locked source +* Make reading of RTC for initial trim more reliable +* Don't create cmdmon sockets when cmdport is 0 +* Add configure option to set default user to drop root privileges +* Add configure option to compile with debug messages +* Print debug messages when -d is used more than once +* Change format of messages written to terminal with -d +* Write fatal messages also to stderr with -n +* Use IP_RECVERR socket option in chronyc to not wait unnecessarily +* Shorten default chronyc timeout for localhost +* Change default hostname in chronyc from localhost to 127.0.0.1 +* Print error message on invalid syntax with all chronyc commands +* Include simulation test suite using clknetsim + +Bug fixes +--------- +* Fix crash when selecting with multiple preferred sources +* Fix frequency calculation with large frequency offsets +* Fix code writing drift and RTC files to compile correctly +* Fix -4/-6 options in chronyc to not reset hostname set by -h +* Fix refclock sample validation with sub-second polling interval +* Set stratum correctly with non-PPS SOCK refclock and local stratum +* Modify dispersion accounting in refclocks to prevent PPS getting + stuck with large dispersion and not accepting new samples + +New in version 1.29.1 +===================== + +Security fixes +-------------- +* Modify chronyc protocol to prevent amplification attacks (CVE-2014-0021) + (incompatible with previous protocol version, chronyc supports both) + +New in version 1.29 +=================== + +Security fixes +-------------- +* Fix crash when processing crafted commands (CVE-2012-4502) + (possible with IP addresses allowed by cmdallow and localhost) +* Don't send uninitialized data in SUBNETS_ACCESSED and CLIENT_ACCESSES + replies (CVE-2012-4503) (not used by chronyc) + +Other changes +------------- +* Drop support for SUBNETS_ACCESSED and CLIENT_ACCESSES commands + +New in version 1.28 +=================== + +* Combine sources to improve accuracy +* Make config and command parser strict +* Add -a option to chronyc to authenticate automatically +* Add -R option to ignore initstepslew and makestep directives +* Add generatecommandkey, minsamples, maxsamples and user directives +* Improve compatibility with NTPv1 and NTPv2 clients +* Create sockets only in selected family with -4/-6 option +* Treat address bind errors as non-fatal +* Extend tracking log +* Accept float values as initstepslew threshold +* Allow hostnames in offline, online and burst commands +* Fix and improve peer polling +* Fix crash in config parsing with too many servers +* Fix crash with duplicated initstepslew address +* Fix delta calculation with extreme frequency offsets +* Set local stratum correctly +* Remove unnecessary adjtimex calls +* Set paths in documentation by configure +* Update chrony.spec + +New in version 1.27 +=================== + +* Support for stronger keys via NSS or libtomcrypt library +* Support reading leap second data from tz database +* Support for precise clock stepping on Linux +* Support for nanoseconds in SHM refclock +* Make offset corrections smoother on Linux +* Make transmit timestamps random below clock precision +* Add corrtimeratio and maxchange directives +* Extend tracking, sources and activity reports +* Wait in foreground process until daemon is fully initialized +* Fix crash with slow name resolving +* Fix iburst with jittery sources +* Fix offset stored in rtc data right after trimrtc +* Fix crash and hang with RTC or manual samples +* Don't use readonly adjtime on Linux kernels before 2.6.28 +* Changed chronyc protocol, incompatible with older versions + +New in version 1.26 +=================== + +* Add compatibility with Linux 3.0 and later +* Use proper source address in NTP replies on multihomed IPv6 hosts +* Accept NTP packets with versions 4, 3 and 2 +* Cope with unexpected backward time jumps +* Don't reset kernel frequency on start without drift file +* Retry on permanent DNS error by default +* Add waitsync command + +New in version 1.25 +=================== + +* Improve accuracy with NTP sources +* Improve accuracy with reference clocks +* Improve polling interval adjustment +* Improve stability with temporary asymmetric delays +* Improve source selection +* Improve initial synchronisation +* Add delayed server name resolving +* Add temperature compensation +* Add nanosecond slewing to Linux driver +* Add fallback drifts +* Add iburst, minstratum, maxdelaydevratio, polltarget, + prefer, noselect options +* Add rtcsync directive to enable Linux 11-minute mode +* Add reselectdist, stratumweight, logbanner, maxclockerror, + include directives +* Add -n option to not detach daemon from terminal +* Fix pidfile directive +* Fix name resolving with disabled IPv6 support +* Fix reloading sample histories with reference clocks +* Fix crash with auto_offline option +* Fix online command on auto_offline sources +* Fix file descriptor leaks +* Increase burst polling interval and stop on KoD RATE +* Set maxupdateskew to 1000 ppm by default +* Require password for clients command +* Update drift file at most once per hour +* Use system headers for Linux RTC support +* Reduce default chronyc timeout and make it configurable +* Avoid large values in chronyc sources and sourcestats output +* Add reselect command to force reselecting best source +* Add -m option to allow multiple commands on command line + +New in version 1.24 +=================== + +Security fixes +-------------- +* Don't reply to invalid cmdmon packets (CVE-2010-0292) +* Limit client log memory size (CVE-2010-0293) +* Limit rate of syslog messages (CVE-2010-0294) + +Bug fixes/Enhancements +---------------------- +* Support for reference clocks (SHM, SOCK, PPS drivers) +* IPv6 support +* Linux capabilities support (to drop root privileges) +* Memory locking support on Linux +* Real-time scheduler support on Linux +* Leap second support on Linux +* Support for editline library +* Support for new Linux readonly adjtime +* NTP client support for KoD RATE +* Read kernel timestamps for received NTP packets +* Reply to NTP requests with correct address on multihomed hosts +* Retry name resolving after temporary failure +* Fix makestep command, make it available on all systems +* Add makestep directive for automatic clock stepping +* Don't require _bigadj kernel symbol on NetBSD +* Avoid blocking read in Linux RTC driver +* Support for Linux on S/390 and PowerPC +* Fix various bugs on 64-bit systems +* Fix valgrind errors and compiler warnings +* Improve configure to support common options and variables +* Improve status checking and printing in chronyc +* Return non-zero exit code on errors in chronyc +* Reduce request timeout in chronyc +* Print estimated offset in sourcestats +* Changed chronyc protocol, incompatible with older versions + +New in version 1.23 +=================== + +* Support for MIPS, x86_64, sparc, alpha, arm, FreeBSD +* Fix serious sign-extension error in handling IP addresses +* RTC support can be excluded at compile time +* Make sources gcc-4 compatible +* Fix various compiler warnings +* Handle fluctuations in peer distance better. +* Fixed handling of stratum zero. +* Fix various problems for 64-bit systems +* Flush chronyc output streams after each command, to allow it to be driven + through pipes +* Manpage improvements + +Version 1.22 +============ + +This release number was claimed by a release that Mandriva made to patch +important bugs in 1.21. The official numbering has jumped to 1.23 as a +consequence. + +New in version 1.21 +=================== + +* Don't include Linux kernel header files any longer : allows chrony to compile + on recent distros. +* Stop trying to use RTC if continuous streams of error messages would occur + (Linux with HPET). + +New in version 1.20 +=================== + +* Many small tidy-ups and security improvements +* Improve documentation (RTC support in post 2.0 kernels) +* Remove trailing \n from syslog messages +* Syslog messages now include IP and port number when packet cannot be sent. +* Added the "acquisitionport" directive. (Kalle Olavi Niemitalo) +* Use uname(2) instead of /proc/version to get kernel version. +* Merge support for Linux on Alpha +* Merge support for 64bit architectures +* Don't link -lm if it's not needed +* Fix Solaris build (broken by 64bit change) +* Add detection of Linux 2.5 +* Allow arbitrary value of HZ in Linux kernel +* Fix for chrony.spec on SuSE (Paul Elliot) +* Fix handling of initstepslew if no servers are listed (John Hasler) +* Fix install rule in Makefile if chronyd is in use (Juliusz Chroboczek) +* Replace sprintf by snprintf to remove risk of buffer overrun (John Hasler) +* Add --help to configure script + +New in version 1.19 +=================== + +* Auto-detect kernel's timer interrupt rate (so-called 'HZ') when chronyd + starts instead of relying on compiled-in value. +* Fix 2 bugs in function that creates the directory for the log and dump files. +* Amended webpage URL and contact details. +* Generate more informative syslog messages before exiting on failed + assertions. +* Fix bugs in clamping code for the tick value used when slewing a large + offset. +* Don't chown files to root during install (should be pointless, and makes RPM + building awkward as ordinary user.) +* Include chrony.spec file for building RPMs + +New in version 1.18 +=================== +* Amend homepage and mailing list information to chrony.sunsite.dk +* Delete pidfile on exit from chronyd. +* Improvements to readline interface to chronyc +* Only generate syslog message when synchronisation is initially lost (instead + of on every failed synchronisation attempt) +* Use double fork approach when initialising daemon. +* More things in contrib directory. +* New options to help package builders: --infodir/--mandir for configure, and + DESTDIR=xxx for make. (See section 2.2 of chrony.txt for details). +* Changed the wording of the messages generated by mailonchange and logchange + directives. + +New in version 1.17 +=================== +* Port to NetBSD +* Configuration supports Linux on PPC +* Fix compilation warnings +* Several documentation improvements +* Bundled manpages (taken from the 'missing manpages project') +* Cope with lack of bzero function for Solaris 2.3 systems +* Store chronyd's pid in a file (default /var/run/chronyd.pid) and check if + chronyd may already be running when starting up. New pidfile directive in + configuration file. +* Any size subnet is now allowed in allow and deny commands. (Example: + 6.7.8/20 or 6.7.8.x/20 (any x) mean a 20 bit subnet). +* The environment variables CC and CFLAGS passed to configure can now be used + to select the compiler and optimisation/debug options to use +* Write syslog messages when chronyd loses synchronisation. +* Print GPL text when chronyc is run. +* Add NTP broadcast server capability (new broadcast directive). +* Add 'auto_offline' option to server/peer (conf file) or add server/peer (via + chronyc). +* Add 'activity' command to chronyc, to report how many servers/peers are + currently online/offline. +* Fix long-standing bug with how the system time quantum was calculated. +* Include support for systems with HZ!=100 (HZ is the timer interrupt + frequency). +* Include example chrony.conf and chrony.keys files (examples subdirectory). +* Include support for readline in chronyc. + +New in version 1.16.1 +===================== +* Fix compilation problem on Linux 2.4.13 (spinlock.h / spinlock_t) + +New in version 1.16 +=================== +* More informative captions for 'sources' and 'sourcestats' commands in chronyc + (use 'sources -v' and 'sourcestats -v' to get them). +* Correct behaviour for Solaris versions>=2.6 (dosynctodr not required on these + versions.) +* Remove some compiler warnings (Solaris) +* If last line of keys file doesn't have end-of-line, don't truncate final + character of that key. +* Change timestamp format used in logfiles to make it fully numeric (to aid + importing data into spreadsheets etc) +* Minor documentation updates and improvements. + +New in version 1.15 +=================== +* Add contributed change to 'configure' to support Solaris 2.8 on x86 +* Workaround for assertion failure that arises if two received packets occur + close together. (Still need to find out why this happens at all.) +* Hopefully fix problem where fast slewing was incompatible with machines + that have a large background drift rate (=> tick value went out of range + for adjtimex() on Linux.) +* Fix rtc_linux.c compile problems with 2.4.x kernel include files. +* Include support for RTC device not being at /dev/rtc (new rtcdevice directive + in configuration file). +* Include support for restricting network interfaces for commands (new + bindcmdaddress directive in configuration file) +* Fix potential linking fault in pktlength.c (use of CROAK macro replaced by + normal assert). +* Add some material on bug reporting + contributing to the chrony.texi file +* Made the chrony.texi file "Vim6-friendly" (removed xrefs on @node lines, + added folding markers to chapters + sections.) +* Switched over to GPL for the licence + +New in version 1.14 +=================== +* Fix compilation for certain other Linux distributions (including Mandrake + 7.1) + +New in version 1.13 +=================== +* Fixed compilation problems on Redhat/SuSE installations with recent 2.2.x + kernels. +* Minor tidy-ups and documentation enhancements. +* Add support for Linux 2.4 kernels + +New in version 1.12 +=================== + +* Trial fix for long-standing bug in Linux RTC estimator when system time is + slewed. +* Fix bug in chronyc if -h is specified without a hostname +* Fixes to logging various error conditions when operating in daemon mode. +* More stuff under contrib/ +* Changes to README file (e.g. about the new chrony-users mailing list) + +New in version 1.11a +==================== + +* Minor changes to contact details +* Minor changes to installation details (chrony subdirectory under doc/) + +New in version 1.11 +=================== + +* Improve robustness of installation procedure +* Tidy up documenation and contact details +* Distribute manual as .txt rather than as .ps +* Add -n option to chronyc to work with numeric IP addresses rather than + names. +* Add material in contrib subdirectory +* Improve robustness of handling drift file and RTC coefficients file +* Improve robustness of regression algorithm + +New in version 1.1 +================== + +Bug fixes +--------- + +* Made linear regression more resistant to rounding errors (old one + occasionally generated negative variances which made everything go + haywire). Trap infinite or 'not-a-number' values being used to + alter system clock to increase robustness further. + +Other changes/Enhancements +-------------------------- + +* Support for Linux 2.1 and 2.2 kernels + +* New command 'makestep' in chronyc to immediately jump the system + time to match the NTP estimated time (Linux only) - a response to + systems booting an hour wrong after summertime/wintertime changes, + due to RTCs running on local time. Needs extending to Sun driver + files too. + +* New directives 'logchange' and 'mailonchange' to log to syslog or + email to a specific address respectively if chronyd detects a clock + offset exceeding a defined threshold. + +* Added capability to log all client/peer NTP accesses and command + accesses (can be turned off with conf file directive 'noclientlog'). + Added 'clients' command to chronyc to display this data. + +* Improved manual mode to use robust regression rather than 2 point + fit. + +* Added 'manual list' and 'manual delete' commands to chronyc to + allow display of entered timestamps and discretionary deletion of + outliers. + +* If host goes unsynchronised the dummy IP address 0.0.0.0 is detected + to avoid attempting a reverse name lookup (to stop dial on demand IP + links from being started) + +* Changed chronyc/chronyd protocol so messages are now all variable + length. Saves on network bandwidth particularly for large replies + from chronyd to chronyc (to support the clients command). + +* Added bindaddress directive to configuration file, to give + additional control over limiting which hosts can access the local + server. + +* Groundwork done for a port to Windows NT to compile with Cygwin + toolkit. chronyc works (to monitor another host). sys_winnt.c + needs finishing to use NT clock control API. Program structure + needs adapting to use Windows NT service functions, so it can be + started at boot time. Hopefully a Windows NT / Cygwin guru with + some spare time can take this port over :-) + +New in version 1.02 +=================== + +Bug fixes +--------- + +* Fix error messages in chronyc if daemon is not reachable. + +* Fix config file problem for 'allow all' and 'deny all' without a + trailing machine address. + +* Remove fatal failed assertion if command socket cannot be read from + in daemon. + +* Rewrote timezone handling for Linux real time clock, following + various reported problems related to daylight saving. + +Other changes/Enhancements +-------------------------- + +* Configure script recognizes BSD/386 and uses SunOS 4.1 driver for + it. + +* Log files now print date as day-month-year rather than as a day + number. Milliseconds removed from timestamps of logged data. + Banners included in file to give meanings of columns. + +* Only do 1 initial step (followed by a trimming slew) when + initialising from RTC on Linux (previously did 2 steps). + +New in version 1.01 +=================== + +Bug fixes +--------- + +* Handle timezone of RTC correctly with respect to daylight saving + time + +* Syntax check the chronyc 'local' command properly + +* Fixed assertion failed fault in median finder (used by RTC + regression fitting) + +Other changes/Enhancements +-------------------------- + +* Log selection of new NTP reference source to syslog. + +* Don't zero-pad IP address fields + +* Add new command to chronyc to allow logfiles to be cycled. + +* Extend allow/deny directive syntax in configuration file to so + directive can apply to all hosts on the Internet. + +* Tidy up printout of timestamps to make it clear they are in UTC + +* Make 'configure' check the processor type as well as the operating + system. @@ -0,0 +1,239 @@ +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/addressing.h b/addressing.h new file mode 100644 index 0000000..9ecc18b --- /dev/null +++ b/addressing.h @@ -0,0 +1,62 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2002 + * + * 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. + * + ********************************************************************** + + ======================================================================= + + Types used for addressing sources etc + */ + +#ifndef GOT_ADDRESSING_H +#define GOT_ADDRESSING_H + +#include "sysincl.h" + +/* This type is used to represent an IPv4 address or IPv6 address. + All parts are in HOST order, NOT network order. */ + +#define IPADDR_UNSPEC 0 +#define IPADDR_INET4 1 +#define IPADDR_INET6 2 + +typedef struct { + union { + uint32_t in4; + uint8_t in6[16]; + } addr; + uint16_t family; + uint16_t _pad; +} IPAddr; + +typedef struct { + IPAddr ip_addr; + unsigned short port; +} NTP_Remote_Address; + +#define INVALID_IF_INDEX -1 + +typedef struct { + IPAddr ip_addr; + int if_index; + int sock_fd; +} NTP_Local_Address; + +#endif /* GOT_ADDRESSING_H */ + diff --git a/addrfilt.c b/addrfilt.c new file mode 100644 index 0000000..dd16700 --- /dev/null +++ b/addrfilt.c @@ -0,0 +1,403 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997,1998,1999,2000,2001,2002,2005 + * Copyright (C) Miroslav Lichvar 2009, 2015 + * + * 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 provides a set of routines for checking IP addresses + against a set of rules and deciding whether they are allowed or + disallowed. + + */ + +#include "config.h" + +#include "sysincl.h" + +#include "addrfilt.h" +#include "memory.h" + +/* Define the number of bits which are stripped off per level of + indirection in the tables */ +#define NBITS 4 + +/* Define the table size */ +#define TABLE_SIZE (1UL<<NBITS) + +typedef enum {DENY, ALLOW, AS_PARENT} State; + +typedef struct _TableNode { + State state; + struct _TableNode *extended; +} TableNode; + +struct ADF_AuthTableInst { + TableNode base4; /* IPv4 node */ + TableNode base6; /* IPv6 node */ +}; + +/* ================================================== */ + +static void +split_ip6(IPAddr *ip, uint32_t *dst) +{ + int i; + + for (i = 0; i < 4; i++) + dst[i] = (uint32_t)ip->addr.in6[i * 4 + 0] << 24 | + ip->addr.in6[i * 4 + 1] << 16 | + ip->addr.in6[i * 4 + 2] << 8 | + ip->addr.in6[i * 4 + 3]; +} + +/* ================================================== */ + +inline static uint32_t +get_subnet(uint32_t *addr, unsigned int where) +{ + int off; + + off = where / 32; + where %= 32; + + return (addr[off] >> (32 - NBITS - where)) & ((1UL << NBITS) - 1); +} + +/* ================================================== */ + +ADF_AuthTable +ADF_CreateTable(void) +{ + ADF_AuthTable result; + result = MallocNew(struct ADF_AuthTableInst); + + /* Default is that nothing is allowed */ + result->base4.state = DENY; + result->base4.extended = NULL; + result->base6.state = DENY; + result->base6.extended = NULL; + + return result; +} + +/* ================================================== */ +/* This function deletes all definitions of child nodes, in effect + pruning a whole subnet definition back to a single parent + record. */ +static void +close_node(TableNode *node) +{ + int i; + TableNode *child_node; + + if (node->extended != NULL) { + for (i=0; i<TABLE_SIZE; i++) { + child_node = &(node->extended[i]); + close_node(child_node); + } + Free(node->extended); + node->extended = NULL; + } +} + + +/* ================================================== */ +/* Allocate the extension field in a node, and set all the children's + states to default to that of the node being extended */ + +static void +open_node(TableNode *node) +{ + int i; + TableNode *child_node; + + if (node->extended == NULL) { + + node->extended = MallocArray(struct _TableNode, TABLE_SIZE); + + for (i=0; i<TABLE_SIZE; i++) { + child_node = &(node->extended[i]); + child_node->state = AS_PARENT; + child_node->extended = NULL; + } + } +} + +/* ================================================== */ + +static ADF_Status +set_subnet(TableNode *start_node, + uint32_t *ip, + int ip_len, + int subnet_bits, + State new_state, + int delete_children) +{ + int bits_to_go, bits_consumed; + uint32_t subnet; + TableNode *node; + + bits_consumed = 0; + bits_to_go = subnet_bits; + node = start_node; + + if ((subnet_bits < 0) || + (subnet_bits > 32 * ip_len)) { + + return ADF_BADSUBNET; + + } else { + + if ((bits_to_go & (NBITS-1)) == 0) { + + while (bits_to_go > 0) { + subnet = get_subnet(ip, bits_consumed); + if (!(node->extended)) { + open_node(node); + } + node = &(node->extended[subnet]); + bits_to_go -= NBITS; + bits_consumed += NBITS; + } + + if (delete_children) { + close_node(node); + } + node->state = new_state; + + } else { /* Have to set multiple entries */ + int N, i, j; + TableNode *this_node; + + while (bits_to_go >= NBITS) { + subnet = get_subnet(ip, bits_consumed); + if (!(node->extended)) { + open_node(node); + } + node = &(node->extended[subnet]); + bits_to_go -= NBITS; + bits_consumed += NBITS; + } + + /* How many subnet entries to set : 1->8, 2->4, 3->2 */ + N = 1 << (NBITS-bits_to_go); + + subnet = get_subnet(ip, bits_consumed) & ~(N - 1); + assert(subnet + N <= TABLE_SIZE); + + if (!(node->extended)) { + open_node(node); + } + + for (i=subnet, j=0; j<N; i++, j++) { + this_node = &(node->extended[i]); + if (delete_children) { + close_node(this_node); + } + this_node->state = new_state; + } + } + + return ADF_SUCCESS; + } + +} + +/* ================================================== */ + +static ADF_Status +set_subnet_(ADF_AuthTable table, + IPAddr *ip_addr, + int subnet_bits, + State new_state, + int delete_children) +{ + uint32_t ip6[4]; + + switch (ip_addr->family) { + case IPADDR_INET4: + return set_subnet(&table->base4, &ip_addr->addr.in4, 1, subnet_bits, new_state, delete_children); + case IPADDR_INET6: + split_ip6(ip_addr, ip6); + return set_subnet(&table->base6, ip6, 4, subnet_bits, new_state, delete_children); + case IPADDR_UNSPEC: + /* Apply to both, subnet_bits has to be 0 */ + if (subnet_bits != 0) + return ADF_BADSUBNET; + memset(ip6, 0, sizeof (ip6)); + if (set_subnet(&table->base4, ip6, 1, 0, new_state, delete_children) == ADF_SUCCESS && + set_subnet(&table->base6, ip6, 4, 0, new_state, delete_children) == ADF_SUCCESS) + return ADF_SUCCESS; + break; + } + + return ADF_BADSUBNET; +} + +ADF_Status +ADF_Allow(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits) +{ + return set_subnet_(table, ip, subnet_bits, ALLOW, 0); +} + +/* ================================================== */ + + +ADF_Status +ADF_AllowAll(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits) +{ + return set_subnet_(table, ip, subnet_bits, ALLOW, 1); +} + +/* ================================================== */ + +ADF_Status +ADF_Deny(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits) +{ + return set_subnet_(table, ip, subnet_bits, DENY, 0); +} + +/* ================================================== */ + +ADF_Status +ADF_DenyAll(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits) +{ + return set_subnet_(table, ip, subnet_bits, DENY, 1); +} + +/* ================================================== */ + +void +ADF_DestroyTable(ADF_AuthTable table) +{ + close_node(&table->base4); + close_node(&table->base6); + Free(table); +} + +/* ================================================== */ + +static int +check_ip_in_node(TableNode *start_node, uint32_t *ip) +{ + uint32_t subnet; + int bits_consumed = 0; + int result = 0; + int finished = 0; + TableNode *node; + State state=DENY; + + node = start_node; + + do { + if (node->state != AS_PARENT) { + state = node->state; + } + if (node->extended) { + subnet = get_subnet(ip, bits_consumed); + node = &(node->extended[subnet]); + bits_consumed += NBITS; + } else { + /* Make decision on this node */ + finished = 1; + } + } while (!finished); + + switch (state) { + case ALLOW: + result = 1; + break; + case DENY: + result = 0; + break; + case AS_PARENT: + assert(0); + break; + } + + return result; +} + + +/* ================================================== */ + +int +ADF_IsAllowed(ADF_AuthTable table, + IPAddr *ip_addr) +{ + uint32_t ip6[4]; + + switch (ip_addr->family) { + case IPADDR_INET4: + return check_ip_in_node(&table->base4, &ip_addr->addr.in4); + case IPADDR_INET6: + split_ip6(ip_addr, ip6); + return check_ip_in_node(&table->base6, ip6); + } + + return 0; +} + +/* ================================================== */ + +static int +is_any_allowed(TableNode *node, State parent) +{ + State state; + int i; + + state = node->state != AS_PARENT ? node->state : parent; + assert(state != AS_PARENT); + + if (node->extended) { + for (i = 0; i < TABLE_SIZE; i++) { + if (is_any_allowed(&node->extended[i], state)) + return 1; + } + } else if (state == ALLOW) { + return 1; + } + + return 0; +} + +/* ================================================== */ + +int +ADF_IsAnyAllowed(ADF_AuthTable table, int family) +{ + switch (family) { + case IPADDR_INET4: + return is_any_allowed(&table->base4, AS_PARENT); + case IPADDR_INET6: + return is_any_allowed(&table->base6, AS_PARENT); + default: + return 0; + } +} diff --git a/addrfilt.h b/addrfilt.h new file mode 100644 index 0000000..b8c131f --- /dev/null +++ b/addrfilt.h @@ -0,0 +1,80 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2002 + * + * 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. + * + ********************************************************************** + + ======================================================================= + + Module for providing an authorisation filter on IP addresses + */ + +#ifndef GOT_ADDRFILT_H +#define GOT_ADDRFILT_H + +#include "addressing.h" + +typedef struct ADF_AuthTableInst *ADF_AuthTable; + +typedef enum { + ADF_SUCCESS, + ADF_BADSUBNET +} ADF_Status; + + +/* Create a new table. The default rule is deny for everything */ +extern ADF_AuthTable ADF_CreateTable(void); + +/* Allow anything in the supplied subnet, EXCEPT for any more specific + subnets that are already defined */ +extern ADF_Status ADF_Allow(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits); + +/* Allow anything in the supplied subnet, overwriting existing + definitions for any more specific subnets */ +extern ADF_Status ADF_AllowAll(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits); + +/* Deny anything in the supplied subnet, EXCEPT for any more specific + subnets that are already defined */ +extern ADF_Status ADF_Deny(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits); + +/* Deny anything in the supplied subnet, overwriting existing + definitions for any more specific subnets */ +extern ADF_Status ADF_DenyAll(ADF_AuthTable table, + IPAddr *ip, + int subnet_bits); + +/* Clear up the table */ +extern void ADF_DestroyTable(ADF_AuthTable table); + +/* Check whether a given IP address is allowed by the rules in + the table */ +extern int ADF_IsAllowed(ADF_AuthTable table, + IPAddr *ip); + +/* Check if at least one address from a given family is allowed by + the rules in the table */ +extern int ADF_IsAnyAllowed(ADF_AuthTable table, + int family); + +#endif /* GOT_ADDRFILT_H */ @@ -0,0 +1,130 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2014 + * + * 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 implementing an array with automatic memory allocation. + + */ + +#include "config.h" + +#include "sysincl.h" + +#include "array.h" +#include "memory.h" + +struct ARR_Instance_Record { + void *data; + unsigned int elem_size; + unsigned int used; + unsigned int allocated; +}; + +ARR_Instance +ARR_CreateInstance(unsigned int elem_size) +{ + ARR_Instance array; + + assert(elem_size > 0); + + array = MallocNew(struct ARR_Instance_Record); + + array->data = NULL; + array->elem_size = elem_size; + array->used = 0; + array->allocated = 0; + + return array; +} + +void +ARR_DestroyInstance(ARR_Instance array) +{ + Free(array->data); + Free(array); +} + +static void +realloc_array(ARR_Instance array, unsigned int min_size) +{ + assert(min_size <= 2 * min_size); + if (array->allocated >= min_size && array->allocated <= 2 * min_size) + return; + + if (array->allocated < min_size) { + while (array->allocated < min_size) + array->allocated = array->allocated ? 2 * array->allocated : 1; + } else { + array->allocated = min_size; + } + + array->data = Realloc2(array->data, array->allocated, array->elem_size); +} + +void * +ARR_GetNewElement(ARR_Instance array) +{ + array->used++; + realloc_array(array, array->used); + return ARR_GetElement(array, array->used - 1); +} + +void * +ARR_GetElement(ARR_Instance array, unsigned int index) +{ + assert(index < array->used); + return (void *)((char *)array->data + (size_t)index * array->elem_size); +} + +void * +ARR_GetElements(ARR_Instance array) +{ + /* Return a non-NULL pointer when the array has zero size */ + if (!array->data) { + assert(!array->used); + return array; + } + + return array->data; +} + +void +ARR_AppendElement(ARR_Instance array, void *element) +{ + void *e; + + e = ARR_GetNewElement(array); + memcpy(e, element, array->elem_size); +} + +void +ARR_SetSize(ARR_Instance array, unsigned int size) +{ + realloc_array(array, size); + array->used = size; +} + +unsigned int +ARR_GetSize(ARR_Instance array) +{ + return array->used; +} @@ -0,0 +1,56 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2014 + * + * 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 array functions. + */ + +#ifndef GOT_ARRAY_H +#define GOT_ARRAY_H + +typedef struct ARR_Instance_Record *ARR_Instance; + +/* Create a new array with given element size */ +extern ARR_Instance ARR_CreateInstance(unsigned int elem_size); + +/* Destroy the array */ +extern void ARR_DestroyInstance(ARR_Instance array); + +/* Return pointer to a new element added to the end of the array */ +extern void *ARR_GetNewElement(ARR_Instance array); + +/* Return element with given index */ +extern void *ARR_GetElement(ARR_Instance array, unsigned int index); + +/* Return pointer to the internal array of elements */ +extern void *ARR_GetElements(ARR_Instance array); + +/* Add a new element to the end of the array */ +extern void ARR_AppendElement(ARR_Instance array, void *element); + +/* Set the size of the array */ +extern void ARR_SetSize(ARR_Instance array, unsigned int size); + +/* Return current size of the array */ +extern unsigned int ARR_GetSize(ARR_Instance array); + +#endif @@ -0,0 +1,726 @@ +/* + 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. + * + ********************************************************************** + + ======================================================================= + + Definitions for the network protocol used for command and monitoring + of the timeserver. + + */ + +#ifndef GOT_CANDM_H +#define GOT_CANDM_H + +#include "sysincl.h" +#include "addressing.h" + +/* This is the default port to use for CANDM, if no alternative is + defined */ +#define DEFAULT_CANDM_PORT 323 + +/* Request codes */ +#define REQ_NULL 0 +#define REQ_ONLINE 1 +#define REQ_OFFLINE 2 +#define REQ_BURST 3 +#define REQ_MODIFY_MINPOLL 4 +#define REQ_MODIFY_MAXPOLL 5 +#define REQ_DUMP 6 +#define REQ_MODIFY_MAXDELAY 7 +#define REQ_MODIFY_MAXDELAYRATIO 8 +#define REQ_MODIFY_MAXUPDATESKEW 9 +#define REQ_LOGON 10 +#define REQ_SETTIME 11 +#define REQ_LOCAL 12 +#define REQ_MANUAL 13 +#define REQ_N_SOURCES 14 +#define REQ_SOURCE_DATA 15 +#define REQ_REKEY 16 +#define REQ_ALLOW 17 +#define REQ_ALLOWALL 18 +#define REQ_DENY 19 +#define REQ_DENYALL 20 +#define REQ_CMDALLOW 21 +#define REQ_CMDALLOWALL 22 +#define REQ_CMDDENY 23 +#define REQ_CMDDENYALL 24 +#define REQ_ACCHECK 25 +#define REQ_CMDACCHECK 26 +#define REQ_ADD_SERVER 27 +#define REQ_ADD_PEER 28 +#define REQ_DEL_SOURCE 29 +#define REQ_WRITERTC 30 +#define REQ_DFREQ 31 +#define REQ_DOFFSET 32 +#define REQ_TRACKING 33 +#define REQ_SOURCESTATS 34 +#define REQ_RTCREPORT 35 +#define REQ_TRIMRTC 36 +#define REQ_CYCLELOGS 37 +#define REQ_SUBNETS_ACCESSED 38 +#define REQ_CLIENT_ACCESSES 39 +#define REQ_CLIENT_ACCESSES_BY_INDEX 40 +#define REQ_MANUAL_LIST 41 +#define REQ_MANUAL_DELETE 42 +#define REQ_MAKESTEP 43 +#define REQ_ACTIVITY 44 +#define REQ_MODIFY_MINSTRATUM 45 +#define REQ_MODIFY_POLLTARGET 46 +#define REQ_MODIFY_MAXDELAYDEVRATIO 47 +#define REQ_RESELECT 48 +#define REQ_RESELECTDISTANCE 49 +#define REQ_MODIFY_MAKESTEP 50 +#define REQ_SMOOTHING 51 +#define REQ_SMOOTHTIME 52 +#define REQ_REFRESH 53 +#define REQ_SERVER_STATS 54 +#define REQ_CLIENT_ACCESSES_BY_INDEX2 55 +#define REQ_LOCAL2 56 +#define REQ_NTP_DATA 57 +#define REQ_ADD_SERVER2 58 +#define REQ_ADD_PEER2 59 +#define REQ_ADD_SERVER3 60 +#define REQ_ADD_PEER3 61 +#define REQ_SHUTDOWN 62 +#define REQ_ONOFFLINE 63 +#define N_REQUEST_TYPES 64 + +/* Structure used to exchange timespecs independent of time_t size */ +typedef struct { + uint32_t tv_sec_high; + uint32_t tv_sec_low; + uint32_t tv_nsec; +} Timespec; + +/* This is used in tv_sec_high for 32-bit timestamps */ +#define TV_NOHIGHSEC 0x7fffffff + +/* 32-bit floating-point format consisting of 7-bit signed exponent + and 25-bit signed coefficient without hidden bit. + The result is calculated as: 2^(exp - 25) * coef */ +typedef struct { + int32_t f; +} Float; + +/* The EOR (end of record) fields are used by the offsetof operator in + pktlength.c, to get the number of bytes that ought to be + transmitted for each packet type. */ + +typedef struct { + int32_t EOR; +} REQ_Null; + +typedef struct { + IPAddr mask; + IPAddr address; + int32_t EOR; +} REQ_Online; + +typedef struct { + IPAddr mask; + IPAddr address; + int32_t EOR; +} REQ_Offline; + +typedef struct { + IPAddr mask; + IPAddr address; + int32_t n_good_samples; + int32_t n_total_samples; + int32_t EOR; +} REQ_Burst; + +typedef struct { + IPAddr address; + int32_t new_minpoll; + int32_t EOR; +} REQ_Modify_Minpoll; + +typedef struct { + IPAddr address; + int32_t new_maxpoll; + int32_t EOR; +} REQ_Modify_Maxpoll; + +typedef struct { + int32_t pad; + int32_t EOR; +} REQ_Dump; + +typedef struct { + IPAddr address; + Float new_max_delay; + int32_t EOR; +} REQ_Modify_Maxdelay; + +typedef struct { + IPAddr address; + Float new_max_delay_ratio; + int32_t EOR; +} REQ_Modify_Maxdelayratio; + +typedef struct { + IPAddr address; + Float new_max_delay_dev_ratio; + int32_t EOR; +} REQ_Modify_Maxdelaydevratio; + +typedef struct { + IPAddr address; + int32_t new_min_stratum; + int32_t EOR; +} REQ_Modify_Minstratum; + +typedef struct { + IPAddr address; + int32_t new_poll_target; + int32_t EOR; +} REQ_Modify_Polltarget; + +typedef struct { + Float new_max_update_skew; + int32_t EOR; +} REQ_Modify_Maxupdateskew; + +typedef struct { + int32_t limit; + Float threshold; + int32_t EOR; +} REQ_Modify_Makestep; + +typedef struct { + Timespec ts; + int32_t EOR; +} REQ_Logon; + +typedef struct { + Timespec ts; + int32_t EOR; +} REQ_Settime; + +typedef struct { + int32_t on_off; + int32_t stratum; + Float distance; + int32_t orphan; + int32_t EOR; +} REQ_Local; + +typedef struct { + int32_t option; + int32_t EOR; +} REQ_Manual; + +typedef struct { + int32_t index; + int32_t EOR; +} REQ_Source_Data; + +typedef struct { + IPAddr ip; + int32_t subnet_bits; + int32_t EOR; +} REQ_Allow_Deny; + +typedef struct { + IPAddr ip; + int32_t EOR; +} REQ_Ac_Check; + +/* Flags used in NTP source requests */ +#define REQ_ADDSRC_ONLINE 0x1 +#define REQ_ADDSRC_AUTOOFFLINE 0x2 +#define REQ_ADDSRC_IBURST 0x4 +#define REQ_ADDSRC_PREFER 0x8 +#define REQ_ADDSRC_NOSELECT 0x10 +#define REQ_ADDSRC_TRUST 0x20 +#define REQ_ADDSRC_REQUIRE 0x40 +#define REQ_ADDSRC_INTERLEAVED 0x80 +#define REQ_ADDSRC_BURST 0x100 + +typedef struct { + IPAddr ip_addr; + uint32_t port; + int32_t minpoll; + int32_t maxpoll; + int32_t presend_minpoll; + uint32_t min_stratum; + uint32_t poll_target; + uint32_t version; + uint32_t max_sources; + int32_t min_samples; + int32_t max_samples; + uint32_t authkey; + Float max_delay; + Float max_delay_ratio; + Float max_delay_dev_ratio; + Float min_delay; + Float asymmetry; + Float offset; + uint32_t flags; + int32_t filter_length; + uint32_t reserved[3]; + int32_t EOR; +} REQ_NTP_Source; + +typedef struct { + IPAddr ip_addr; + int32_t EOR; +} REQ_Del_Source; + +typedef struct { + Float dfreq; + int32_t EOR; +} REQ_Dfreq; + +typedef struct { + int32_t sec; + int32_t usec; + int32_t EOR; +} REQ_Doffset; + +typedef struct { + uint32_t index; + int32_t EOR; +} REQ_Sourcestats; + +/* This is based on the response size rather than the + request size */ +#define MAX_CLIENT_ACCESSES 8 + +typedef struct { + uint32_t first_index; + uint32_t n_clients; + int32_t EOR; +} REQ_ClientAccessesByIndex; + +typedef struct { + int32_t index; + int32_t EOR; +} REQ_ManualDelete; + +typedef struct { + Float distance; + int32_t EOR; +} REQ_ReselectDistance; + +#define REQ_SMOOTHTIME_RESET 0 +#define REQ_SMOOTHTIME_ACTIVATE 1 + +typedef struct { + int32_t option; + int32_t EOR; +} REQ_SmoothTime; + +typedef struct { + IPAddr ip_addr; + int32_t EOR; +} REQ_NTPData; + +/* ================================================== */ + +#define PKT_TYPE_CMD_REQUEST 1 +#define PKT_TYPE_CMD_REPLY 2 + +/* This version number needs to be incremented whenever the packet + size and/or the format of any of the existing messages is changed. + Other changes, e.g. new command types, should be handled cleanly by + client.c and cmdmon.c anyway, so the version can stay the same. + + Version 1 : original version with fixed size packets + + Version 2 : both command and reply packet sizes made capable of + being variable length. + + Version 3 : NTP_Source message lengthened (auto_offline) + + Version 4 : IPv6 addressing added, 64-bit time values, sourcestats + and tracking reports extended, added flags to NTP source request, + trimmed source report, replaced fixed-point format with floating-point + and used also instead of integer microseconds, new commands: modify stratum, + modify polltarget, modify maxdelaydevratio, reselect, reselectdistance + + Version 5 : auth data moved to the end of the packet to allow hashes with + different sizes, extended sources, tracking and activity reports, dropped + subnets accessed and client accesses + + Version 6 : added padding to requests to prevent amplification attack, + changed maximum number of samples in manual list to 16, new commands: modify + makestep, smoothing, smoothtime + + Support for authentication was removed later in version 6 of the protocol + and commands that required authentication are allowed only locally over Unix + 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 + flags to NTP source request and report, made length of manual list constant, + added new commands: ntpdata, refresh, serverstats, shutdown + */ + +#define PROTO_VERSION_NUMBER 6 + +/* The oldest protocol versions that are compatible enough with the current + version to report a version mismatch for the server and the client */ +#define PROTO_VERSION_MISMATCH_COMPAT_SERVER 5 +#define PROTO_VERSION_MISMATCH_COMPAT_CLIENT 4 + +/* The first protocol version using padding in requests */ +#define PROTO_VERSION_PADDING 6 + +/* The maximum length of padding in request packet, currently + defined by MANUAL_LIST */ +#define MAX_PADDING_LENGTH 396 + +/* ================================================== */ + +typedef struct { + uint8_t version; /* Protocol version */ + uint8_t pkt_type; /* What sort of packet this is */ + uint8_t res1; + uint8_t res2; + uint16_t command; /* Which command is being issued */ + uint16_t attempt; /* How many resends the client has done + (count up from zero for same sequence + number) */ + uint32_t sequence; /* Client's sequence number */ + uint32_t pad1; + uint32_t pad2; + + union { + REQ_Null null; + REQ_Online online; + REQ_Offline offline; + REQ_Burst burst; + REQ_Modify_Minpoll modify_minpoll; + REQ_Modify_Maxpoll modify_maxpoll; + REQ_Dump dump; + REQ_Modify_Maxdelay modify_maxdelay; + REQ_Modify_Maxdelayratio modify_maxdelayratio; + REQ_Modify_Maxdelaydevratio modify_maxdelaydevratio; + REQ_Modify_Minstratum modify_minstratum; + REQ_Modify_Polltarget modify_polltarget; + REQ_Modify_Maxupdateskew modify_maxupdateskew; + REQ_Modify_Makestep modify_makestep; + REQ_Logon logon; + REQ_Settime settime; + REQ_Local local; + REQ_Manual manual; + REQ_Source_Data source_data; + REQ_Allow_Deny allow_deny; + REQ_Ac_Check ac_check; + REQ_NTP_Source ntp_source; + REQ_Del_Source del_source; + REQ_Dfreq dfreq; + REQ_Doffset doffset; + REQ_Sourcestats sourcestats; + REQ_ClientAccessesByIndex client_accesses_by_index; + REQ_ManualDelete manual_delete; + REQ_ReselectDistance reselect_distance; + REQ_SmoothTime smoothtime; + REQ_NTPData ntp_data; + } data; /* Command specific parameters */ + + /* Padding used to prevent traffic amplification. It only defines the + maximum size of the packet, there is no hole after the data field. */ + uint8_t padding[MAX_PADDING_LENGTH]; + +} CMD_Request; + +/* ================================================== */ +/* Authority codes for command types */ + +#define PERMIT_OPEN 0 +#define PERMIT_LOCAL 1 +#define PERMIT_AUTH 2 + +/* ================================================== */ + +/* Reply codes */ +#define RPY_NULL 1 +#define RPY_N_SOURCES 2 +#define RPY_SOURCE_DATA 3 +#define RPY_MANUAL_TIMESTAMP 4 +#define RPY_TRACKING 5 +#define RPY_SOURCESTATS 6 +#define RPY_RTC 7 +#define RPY_SUBNETS_ACCESSED 8 +#define RPY_CLIENT_ACCESSES 9 +#define RPY_CLIENT_ACCESSES_BY_INDEX 10 +#define RPY_MANUAL_LIST 11 +#define RPY_ACTIVITY 12 +#define RPY_SMOOTHING 13 +#define RPY_SERVER_STATS 14 +#define RPY_CLIENT_ACCESSES_BY_INDEX2 15 +#define RPY_NTP_DATA 16 +#define RPY_MANUAL_TIMESTAMP2 17 +#define RPY_MANUAL_LIST2 18 +#define N_REPLY_TYPES 19 + +/* Status codes */ +#define STT_SUCCESS 0 +#define STT_FAILED 1 +#define STT_UNAUTH 2 +#define STT_INVALID 3 +#define STT_NOSUCHSOURCE 4 +#define STT_INVALIDTS 5 +#define STT_NOTENABLED 6 +#define STT_BADSUBNET 7 +#define STT_ACCESSALLOWED 8 +#define STT_ACCESSDENIED 9 +/* Deprecated */ +#define STT_NOHOSTACCESS 10 +#define STT_SOURCEALREADYKNOWN 11 +#define STT_TOOMANYSOURCES 12 +#define STT_NORTC 13 +#define STT_BADRTCFILE 14 +#define STT_INACTIVE 15 +#define STT_BADSAMPLE 16 +#define STT_INVALIDAF 17 +#define STT_BADPKTVERSION 18 +#define STT_BADPKTLENGTH 19 + +typedef struct { + int32_t EOR; +} RPY_Null; + +typedef struct { + uint32_t n_sources; + int32_t EOR; +} RPY_N_Sources; + +#define RPY_SD_MD_CLIENT 0 +#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_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 + +typedef struct { + IPAddr ip_addr; + int16_t poll; + uint16_t stratum; + uint16_t state; + uint16_t mode; + uint16_t flags; + uint16_t reachability; + uint32_t since_sample; + Float orig_latest_meas; + Float latest_meas; + Float latest_meas_err; + int32_t EOR; +} RPY_Source_Data; + +typedef struct { + uint32_t ref_id; + IPAddr ip_addr; + uint16_t stratum; + uint16_t leap_status; + Timespec ref_time; + Float current_correction; + Float last_offset; + Float rms_offset; + Float freq_ppm; + Float resid_freq_ppm; + Float skew_ppm; + Float root_delay; + Float root_dispersion; + Float last_update_interval; + int32_t EOR; +} RPY_Tracking; + +typedef struct { + uint32_t ref_id; + IPAddr ip_addr; + uint32_t n_samples; + uint32_t n_runs; + uint32_t span_seconds; + Float sd; + Float resid_freq_ppm; + Float skew_ppm; + Float est_offset; + Float est_offset_err; + int32_t EOR; +} RPY_Sourcestats; + +typedef struct { + Timespec ref_time; + uint16_t n_samples; + uint16_t n_runs; + uint32_t span_seconds; + Float rtc_seconds_fast; + Float rtc_gain_rate_ppm; + int32_t EOR; +} RPY_Rtc; + +typedef struct { + Float offset; + Float dfreq_ppm; + Float new_afreq_ppm; + int32_t EOR; +} RPY_ManualTimestamp; + +typedef struct { + IPAddr ip; + uint32_t ntp_hits; + uint32_t cmd_hits; + uint32_t ntp_drops; + uint32_t cmd_drops; + int8_t ntp_interval; + int8_t cmd_interval; + int8_t ntp_timeout_interval; + int8_t pad; + uint32_t last_ntp_hit_ago; + uint32_t last_cmd_hit_ago; +} RPY_ClientAccesses_Client; + +typedef struct { + uint32_t n_indices; /* how many indices there are in the server's table */ + uint32_t next_index; /* the index 1 beyond those processed on this call */ + uint32_t n_clients; /* the number of valid entries in the following array */ + RPY_ClientAccesses_Client clients[MAX_CLIENT_ACCESSES]; + int32_t EOR; +} RPY_ClientAccessesByIndex; + +typedef struct { + uint32_t ntp_hits; + uint32_t cmd_hits; + uint32_t ntp_drops; + uint32_t cmd_drops; + uint32_t log_drops; + int32_t EOR; +} RPY_ServerStats; + +#define MAX_MANUAL_LIST_SAMPLES 16 + +typedef struct { + Timespec when; + Float slewed_offset; + Float orig_offset; + Float residual; +} RPY_ManualListSample; + +typedef struct { + uint32_t n_samples; + RPY_ManualListSample samples[MAX_MANUAL_LIST_SAMPLES]; + int32_t EOR; +} RPY_ManualList; + +typedef struct { + int32_t online; + int32_t offline; + int32_t burst_online; + int32_t burst_offline; + int32_t unresolved; + int32_t EOR; +} RPY_Activity; + +#define RPY_SMT_FLAG_ACTIVE 0x1 +#define RPY_SMT_FLAG_LEAPONLY 0x2 + +typedef struct { + uint32_t flags; + Float offset; + Float freq_ppm; + Float wander_ppm; + Float last_update_ago; + Float remaining_time; + int32_t EOR; +} RPY_Smoothing; + +#define RPY_NTP_FLAGS_TESTS 0x3ff +#define RPY_NTP_FLAG_INTERLEAVED 0x4000 +#define RPY_NTP_FLAG_AUTHENTICATED 0x8000 + +typedef struct { + IPAddr remote_addr; + IPAddr local_addr; + uint16_t remote_port; + uint8_t leap; + uint8_t version; + uint8_t mode; + uint8_t stratum; + int8_t poll; + int8_t precision; + Float root_delay; + Float root_dispersion; + uint32_t ref_id; + Timespec ref_time; + Float offset; + Float peer_delay; + Float peer_dispersion; + Float response_time; + Float jitter_asymmetry; + uint16_t flags; + uint8_t tx_tss_char; + uint8_t rx_tss_char; + uint32_t total_tx_count; + uint32_t total_rx_count; + uint32_t total_valid_count; + uint32_t reserved[4]; + int32_t EOR; +} RPY_NTPData; + +typedef struct { + uint8_t version; + uint8_t pkt_type; + uint8_t res1; + uint8_t res2; + uint16_t command; /* Which command is being replied to */ + uint16_t reply; /* Which format of reply this is */ + uint16_t status; /* Status of command processing */ + uint16_t pad1; /* Padding for compatibility and 4 byte alignment */ + uint16_t pad2; + uint16_t pad3; + uint32_t sequence; /* Echo of client's sequence number */ + uint32_t pad4; + uint32_t pad5; + + union { + RPY_Null null; + RPY_N_Sources n_sources; + RPY_Source_Data source_data; + RPY_ManualTimestamp manual_timestamp; + RPY_Tracking tracking; + RPY_Sourcestats sourcestats; + RPY_Rtc rtc; + RPY_ClientAccessesByIndex client_accesses_by_index; + RPY_ServerStats server_stats; + RPY_ManualList manual_list; + RPY_Activity activity; + RPY_Smoothing smoothing; + RPY_NTPData ntp_data; + } data; /* Reply specific parameters */ + +} CMD_Reply; + +/* ================================================== */ + +#endif /* GOT_CANDM_H */ diff --git a/client.c b/client.c new file mode 100644 index 0000000..029860c --- /dev/null +++ b/client.c @@ -0,0 +1,3290 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Lonnie Abelbeck 2016, 2018 + * Copyright (C) Miroslav Lichvar 2009-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. + * + ********************************************************************** + + ======================================================================= + + Command line client for configuring the daemon and obtaining status + from it whilst running. + */ + +#include "config.h" + +#include "sysincl.h" + +#include "array.h" +#include "candm.h" +#include "logging.h" +#include "memory.h" +#include "nameserv.h" +#include "getdate.h" +#include "cmdparse.h" +#include "pktlength.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; +}; + +static ARR_Instance sockaddrs; + +static int sock_fd = -1; + +static int quit = 0; + +static int on_terminal = 0; + +static int no_dns = 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; + +void LOG_Message(LOG_Severity severity, +#if DEBUG > 0 + int line_number, const char *filename, const char *function_name, +#endif + const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + putc('\n', stderr); + va_end(ap); +} + +/* ================================================== */ +/* Read a single line of commands from standard input */ + +#ifdef FEAT_READLINE +static char **command_name_completion(const char *text, int start, int end); +#endif + +static char * +read_line(void) +{ + static char line[2048]; + static const char *prompt = "chronyc> "; + + if (on_terminal) { +#ifdef FEAT_READLINE + char *cmd; + + rl_attempted_completion_function = command_name_completion; + rl_basic_word_break_characters = " \t\n\r"; + + /* save line only if not empty */ + cmd = readline(prompt); + if( cmd == NULL ) return( NULL ); + + /* user pressed return */ + if( *cmd != '\0' ) { + strncpy(line, cmd, sizeof(line) - 1); + line[sizeof(line) - 1] = '\0'; + add_history(cmd); + /* free the buffer allocated by readline */ + Free(cmd); + } else { + /* simulate the user has entered an empty line */ + *line = '\0'; + } + return( line ); +#else + printf("%s", prompt); + fflush(stdout); +#endif + } + if (fgets(line, sizeof(line), stdin)) { + return line; + } else { + return NULL; + } + +} + +/* ================================================== */ + +static ARR_Instance +get_sockaddrs(const char *hostnames, int port) +{ + 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)); + s1 = Strdup(hostnames); + + /* Parse the comma-separated list of hostnames */ + for (hostname = s1; hostname && *hostname; hostname = s2) { + s2 = strchr(hostname, ','); + if (s2) + *s2++ = '\0'; + + /* 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; + } else { + if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) { + DEBUG_LOG("Could not get IP address for %s", hostname); + continue; + } + + 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); + DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i])); + } + } + } + + Free(s1); + return addrs; +} + +/* ================================================== */ +/* Initialise the socket used to talk to the daemon */ + +static int +prepare_socket(union sockaddr_all *addr) +{ + socklen_t addr_len; + char *dir; + + switch (addr->sa.sa_family) { + case AF_UNIX: + addr_len = sizeof (addr->un); + break; + case AF_INET: + addr_len = sizeof (addr->in4); + 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)); + 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; +} + +/* ================================================== */ + +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); + sock_fd = -1; +} + +/* ================================================== */ + +static int +open_io(void) +{ + static unsigned int address_index = 0; + union sockaddr_all *addr; + + /* If a socket is already opened, close it and try the next address */ + if (sock_fd >= 0) { + close_io(); + address_index++; + } + + /* 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)); + + if (prepare_socket(addr)) + return 1; + + close_io(); + } + + return 0; +} + +/* ================================================== */ + +static void +bits_to_mask(int bits, int family, IPAddr *mask) +{ + int i; + + mask->family = family; + switch (family) { + case IPADDR_INET4: + if (bits > 32 || bits < 0) + bits = 32; + if (bits > 0) { + mask->addr.in4 = -1; + mask->addr.in4 <<= 32 - bits; + } else { + mask->addr.in4 = 0; + } + break; + case IPADDR_INET6: + if (bits > 128 || bits < 0) + bits = 128; + for (i = 0; i < bits / 8; i++) + mask->addr.in6[i] = 0xff; + if (i < 16) + mask->addr.in6[i++] = (0xff << (8 - bits % 8)) & 0xff; + for (; i < 16; i++) + mask->addr.in6[i] = 0x0; + break; + default: + assert(0); + } +} + +/* ================================================== */ + +static int +read_mask_address(char *line, IPAddr *mask, IPAddr *address) +{ + unsigned int bits; + char *p, *q; + + p = line; + if (!*p) { + mask->family = address->family = IPADDR_UNSPEC; + return 1; + } else { + q = strchr(p, '/'); + if (q) { + *q++ = 0; + if (UTI_StringToIP(p, mask)) { + p = q; + if (UTI_StringToIP(p, address)) { + if (address->family == mask->family) + return 1; + } else if (sscanf(p, "%u", &bits) == 1) { + *address = *mask; + bits_to_mask(bits, address->family, mask); + return 1; + } + } + } else { + if (DNS_Name2IPAddress(p, address, 1) == DNS_Success) { + bits_to_mask(-1, address->family, mask); + return 1; + } else { + LOG(LOGS_ERR, "Could not get address for hostname"); + return 0; + } + } + } + + LOG(LOGS_ERR, "Invalid syntax for mask/address"); + return 0; +} + +/* ================================================== */ + +static int +process_cmd_offline(CMD_Request *msg, char *line) +{ + IPAddr mask, address; + int ok; + + if (read_mask_address(line, &mask, &address)) { + UTI_IPHostToNetwork(&mask, &msg->data.offline.mask); + UTI_IPHostToNetwork(&address, &msg->data.offline.address); + msg->command = htons(REQ_OFFLINE); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + + +static int +process_cmd_online(CMD_Request *msg, char *line) +{ + IPAddr mask, address; + int ok; + + if (read_mask_address(line, &mask, &address)) { + UTI_IPHostToNetwork(&mask, &msg->data.online.mask); + UTI_IPHostToNetwork(&address, &msg->data.online.address); + msg->command = htons(REQ_ONLINE); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +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) +{ + char *hostname; + int ok = 0; + + hostname = line; + line = CPS_SplitWord(line); + + if (sscanf(line, "%d", value) != 1) { + LOG(LOGS_ERR, "Invalid syntax for address value"); + ok = 0; + } else { + if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not get address for hostname"); + ok = 0; + } else { + ok = 1; + } + } + + return ok; + +} + + +/* ================================================== */ + +static int +read_address_double(char *line, IPAddr *address, double *value) +{ + char *hostname; + int ok = 0; + + hostname = line; + line = CPS_SplitWord(line); + + if (sscanf(line, "%lf", value) != 1) { + LOG(LOGS_ERR, "Invalid syntax for address value"); + ok = 0; + } else { + if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not get address for hostname"); + ok = 0; + } else { + ok = 1; + } + } + + return ok; + +} + + +/* ================================================== */ + +static int +process_cmd_minpoll(CMD_Request *msg, char *line) +{ + IPAddr address; + int minpoll; + int ok; + + if (read_address_integer(line, &address, &minpoll)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_minpoll.address); + msg->data.modify_minpoll.new_minpoll = htonl(minpoll); + msg->command = htons(REQ_MODIFY_MINPOLL); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxpoll(CMD_Request *msg, char *line) +{ + IPAddr address; + int maxpoll; + int ok; + + if (read_address_integer(line, &address, &maxpoll)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxpoll.address); + msg->data.modify_maxpoll.new_maxpoll = htonl(maxpoll); + msg->command = htons(REQ_MODIFY_MAXPOLL); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxdelay(CMD_Request *msg, char *line) +{ + IPAddr address; + double max_delay; + int ok; + + if (read_address_double(line, &address, &max_delay)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelay.address); + msg->data.modify_maxdelay.new_max_delay = UTI_FloatHostToNetwork(max_delay); + msg->command = htons(REQ_MODIFY_MAXDELAY); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxdelaydevratio(CMD_Request *msg, char *line) +{ + IPAddr address; + double max_delay_dev_ratio; + int ok; + + if (read_address_double(line, &address, &max_delay_dev_ratio)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelaydevratio.address); + msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_dev_ratio); + msg->command = htons(REQ_MODIFY_MAXDELAYDEVRATIO); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxdelayratio(CMD_Request *msg, char *line) +{ + IPAddr address; + double max_delay_ratio; + int ok; + + if (read_address_double(line, &address, &max_delay_ratio)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelayratio.address); + msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_ratio); + msg->command = htons(REQ_MODIFY_MAXDELAYRATIO); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_minstratum(CMD_Request *msg, char *line) +{ + IPAddr address; + int min_stratum; + int ok; + + if (read_address_integer(line, &address, &min_stratum)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_minstratum.address); + msg->data.modify_minstratum.new_min_stratum = htonl(min_stratum); + msg->command = htons(REQ_MODIFY_MINSTRATUM); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_polltarget(CMD_Request *msg, char *line) +{ + IPAddr address; + int poll_target; + int ok; + + if (read_address_integer(line, &address, &poll_target)) { + UTI_IPHostToNetwork(&address, &msg->data.modify_polltarget.address); + msg->data.modify_polltarget.new_poll_target = htonl(poll_target); + msg->command = htons(REQ_MODIFY_POLLTARGET); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static int +process_cmd_maxupdateskew(CMD_Request *msg, char *line) +{ + int ok; + double new_max_update_skew; + + if (sscanf(line, "%lf", &new_max_update_skew) == 1) { + msg->data.modify_maxupdateskew.new_max_update_skew = UTI_FloatHostToNetwork(new_max_update_skew); + msg->command = htons(REQ_MODIFY_MAXUPDATESKEW); + ok = 1; + } else { + ok = 0; + } + + return ok; + +} + +/* ================================================== */ + +static void +process_cmd_dump(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_DUMP); + msg->data.dump.pad = htonl(0); +} + +/* ================================================== */ + +static void +process_cmd_writertc(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_WRITERTC); +} + +/* ================================================== */ + +static void +process_cmd_trimrtc(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_TRIMRTC); +} + +/* ================================================== */ + +static void +process_cmd_cyclelogs(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_CYCLELOGS); +} + +/* ================================================== */ + +static int +process_cmd_burst(CMD_Request *msg, char *line) +{ + int n_good_samples, n_total_samples; + char *s1, *s2; + IPAddr address, mask; + + s1 = line; + s2 = CPS_SplitWord(s1); + CPS_SplitWord(s2); + + if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) { + LOG(LOGS_ERR, "Invalid syntax for burst command"); + return 0; + } + + mask.family = address.family = IPADDR_UNSPEC; + if (*s2 && !read_mask_address(s2, &mask, &address)) { + return 0; + } + + msg->command = htons(REQ_BURST); + msg->data.burst.n_good_samples = ntohl(n_good_samples); + msg->data.burst.n_total_samples = ntohl(n_total_samples); + + UTI_IPHostToNetwork(&mask, &msg->data.burst.mask); + UTI_IPHostToNetwork(&address, &msg->data.burst.address); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_local(CMD_Request *msg, char *line) +{ + int on_off, stratum = 0, orphan = 0; + double distance = 0.0; + + if (!strcmp(line, "off")) { + on_off = 0; + } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) { + on_off = 1; + } else { + LOG(LOGS_ERR, "Invalid syntax for local command"); + return 0; + } + + msg->command = htons(REQ_LOCAL2); + msg->data.local.on_off = htonl(on_off); + msg->data.local.stratum = htonl(stratum); + msg->data.local.distance = UTI_FloatHostToNetwork(distance); + msg->data.local.orphan = htonl(orphan); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_manual(CMD_Request *msg, const char *line) +{ + const char *p; + + p = line; + + if (!strcmp(p, "off")) { + msg->data.manual.option = htonl(0); + } else if (!strcmp(p, "on")) { + msg->data.manual.option = htonl(1); + } else if (!strcmp(p, "reset")) { + msg->data.manual.option = htonl(2); + } else { + LOG(LOGS_ERR, "Invalid syntax for manual command"); + return 0; + } + msg->command = htons(REQ_MANUAL); + + return 1; +} + +/* ================================================== */ + +static int +parse_allow_deny(CMD_Request *msg, char *line) +{ + unsigned long a, b, c, d; + int n, specified_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) { + 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; + } + } + } +} + +/* ================================================== */ + +static int +process_cmd_accheck(CMD_Request *msg, char *line) +{ + IPAddr ip; + msg->command = htons(REQ_ACCHECK); + if (accheck_getaddr(line, &ip)) { + UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); + return 1; + } else { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } +} + +/* ================================================== */ + +static int +process_cmd_cmdaccheck(CMD_Request *msg, char *line) +{ + IPAddr ip; + msg->command = htons(REQ_CMDACCHECK); + if (accheck_getaddr(line, &ip)) { + UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); + return 1; + } else { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } +} + +/* ================================================== */ + +static void +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; + } + 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); + } +} + +/* ================================================== */ + +static int +process_cmd_add_server_or_peer(CMD_Request *msg, char *line) +{ + CPS_NTP_Source data; + IPAddr ip_addr; + int result = 0, status; + const char *opt_name; + + 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) { + LOG(LOGS_ERR, "Invalid host/IP address"); + break; + } + + opt_name = NULL; + if (opt_name) { + LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name); + 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.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); + msg->data.ntp_source.min_stratum = htonl(data.params.min_stratum); + msg->data.ntp_source.poll_target = htonl(data.params.poll_target); + msg->data.ntp_source.version = htonl(data.params.version); + msg->data.ntp_source.max_sources = htonl(data.params.max_sources); + 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.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 = + UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio); + msg->data.ntp_source.min_delay = UTI_FloatHostToNetwork(data.params.min_delay); + 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.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.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); + memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved)); + + result = 1; + + break; + } + + return result; +} + +/* ================================================== */ + +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; + int ok = 0; + IPAddr address; + + msg->command = htons(REQ_DEL_SOURCE); + hostname = line; + CPS_SplitWord(line); + + if (!*hostname) { + LOG(LOGS_ERR, "Invalid syntax for address"); + ok = 0; + } else { + if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not get address for hostname"); + ok = 0; + } else { + UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr); + ok = 1; + } + } + + return ok; + +} + +/* ================================================== */ + +static void +give_help(void) +{ + int line, len; + const char *s, cols[] = + "System clock:\0\0" + "tracking\0Display system time information\0" + "makestep\0Correct clock by stepping immediately\0" + "makestep <threshold> <updates>\0Configure automatic clock stepping\0" + "maxupdateskew <skew>\0Modify maximum valid skew to update frequency\0" + "waitsync [<max-tries> [<max-correction> [<max-skew> [<interval>]]]]\0" + "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" + "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" + "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" + "delete <address>\0Remove server or peer\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" + "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" + "\0\0" + "Manual time input:\0\0" + "manual off|on|reset\0Disable/enable/reset settime command\0" + "manual list\0Show previous settime entries\0" + "manual delete <index>\0Delete previous settime entry\0" + "settime <time>\0Set daemon time\0" + "\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" + "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" + "deny [<subnet>]\0Deny access to subnet as a default\0" + "deny all [<subnet>]\0Deny access to subnet and all children\0" + "local [options]\0Serve time even when not synchronised\0" + "local off\0Don't serve time when not synchronised\0" + "smoothtime reset|activate\0Reset/activate time smoothing\0" + "smoothing\0Display current time smoothing state\0" + "\0\0" + "Monitoring access:\0\0" + "cmdaccheck <address>\0Check whether address is allowed\0" + "cmdallow [<subnet>]\0Allow access to subnet as a default\0" + "cmdallow all [<subnet>]\0Allow access to subnet and all children\0" + "cmddeny [<subnet>]\0Deny access to subnet as a default\0" + "cmddeny all [<subnet>]\0Deny access to subnet and all children\0" + "\0\0" + "Real-time clock:\0\0" + "rtcdata\0Print current RTC performance parameters\0" + "trimrtc\0Correct RTC relative to system clock\0" + "writertc\0Save RTC performance parameters to file\0" + "\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" + "shutdown\0Stop daemon\0" + "\0\0" + "Client commands:\0\0" + "dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0" + "dns -4|-6|-46\0Resolve hostnames only to IPv4/IPv6/both addresses\0" + "timeout <milliseconds>\0Set initial response timeout\0" + "retries <retries>\0Set maximum number of retries\0" + "keygen [<id> [<type> [<bits>]]]\0Generate key for key file\0" + "exit|quit\0Leave the program\0" + "help\0Generate this help\0" + "\0"; + + /* Indent the second column */ + for (s = cols, line = 0; s < cols + sizeof (cols); s += len + 1, line++) { + len = strlen(s); + printf(line % 2 == 0 ? (len >= 28 ? "%s\n%28s" : "%-28s%s") : "%s%s\n", + s, ""); + } +} + +/* ================================================== */ +/* Tab-completion when editline/readline is available */ + +#ifdef FEAT_READLINE + +enum { + TAB_COMPLETE_BASE_CMDS, + TAB_COMPLETE_ADD_OPTS, + TAB_COMPLETE_MANUAL_OPTS, + TAB_COMPLETE_SOURCES_OPTS, + TAB_COMPLETE_SOURCESTATS_OPTS, + TAB_COMPLETE_MAX_INDEX +}; + +static int tab_complete_index; + +static char * +command_name_generator(const char *text, int state) +{ + const char *name, **names[TAB_COMPLETE_MAX_INDEX]; + const char *base_commands[] = { + "accheck", "activity", "add", "allow", "burst", + "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete", + "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep", + "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll", + "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline", + "polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist", + "retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing", + "smoothtime", "sources", "sourcestats", + "timeout", "tracking", "trimrtc", "waitsync", "writertc", + NULL + }; + const char *add_options[] = { "peer", "server", NULL }; + const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL }; + const char *sources_options[] = { "-v", NULL }; + const char *sourcestats_options[] = { "-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_SOURCES_OPTS] = sources_options; + names[TAB_COMPLETE_SOURCESTATS_OPTS] = sourcestats_options; + + if (!state) { + list_index = 0; + len = strlen(text); + } + + while ((name = names[tab_complete_index][list_index++])) { + if (strncmp(name, text, len) == 0) { + return strdup(name); + } + } + + return NULL; +} + +/* ================================================== */ + +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, "manual ")) { + tab_complete_index = TAB_COMPLETE_MANUAL_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 + +/* ================================================== */ + +static int max_retries = 2; +static int initial_timeout = 1000; +static int proto_version = PROTO_VERSION_NUMBER; + +/* This is the core protocol module. Complete particular fields in + the outgoing packet, send it, wait for a response, handle retries, + etc. Returns a Boolean indicating whether the protocol was + successful or not.*/ + +static int +submit_request(CMD_Request *request, CMD_Reply *reply) +{ + int select_status; + int recv_status; + int read_length; + int command_length; + int padding_length; + struct timespec ts_now, ts_start; + struct timeval tv; + int n_attempts, new_attempt; + double timeout; + fd_set rdfd; + + request->pkt_type = PKT_TYPE_CMD_REQUEST; + request->res1 = 0; + request->res2 = 0; + request->pad1 = 0; + request->pad2 = 0; + + n_attempts = 0; + new_attempt = 1; + + do { + if (gettimeofday(&tv, NULL)) + return 0; + + if (new_attempt) { + new_attempt = 0; + + if (n_attempts > max_retries) + return 0; + + UTI_TimevalToTimespec(&tv, &ts_start); + + UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence)); + request->attempt = htons(n_attempts); + request->version = proto_version; + command_length = PKL_CommandLength(request); + padding_length = PKL_CommandPaddingLength(request); + assert(command_length > 0 && command_length > padding_length); + + n_attempts++; + + /* Zero the padding to not send any uninitialized data */ + memset(((char *)request) + command_length - padding_length, 0, padding_length); + + if (sock_fd < 0) { + DEBUG_LOG("No socket to send request"); + return 0; + } + + if (send(sock_fd, (void *)request, command_length, 0) < 0) { + DEBUG_LOG("Could not send %d bytes : %s", command_length, strerror(errno)); + return 0; + } + + DEBUG_LOG("Sent %d bytes", command_length); + } + + UTI_TimevalToTimespec(&tv, &ts_now); + + /* Check if the clock wasn't stepped back */ + if (UTI_CompareTimespecs(&ts_now, &ts_start) < 0) + ts_start = ts_now; + + timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) - + UTI_DiffTimespecsToDouble(&ts_now, &ts_start); + DEBUG_LOG("Timeout %f seconds", timeout); + + /* Avoid calling select() with an invalid timeout */ + if (timeout <= 0.0) { + new_attempt = 1; + continue; + } + + UTI_DoubleToTimeval(timeout, &tv); + + FD_ZERO(&rdfd); + FD_SET(sock_fd, &rdfd); + + if (quit) + return 0; + + select_status = select(sock_fd + 1, &rdfd, NULL, NULL, &tv); + + if (select_status < 0) { + DEBUG_LOG("select failed : %s", strerror(errno)); + return 0; + } else if (select_status == 0) { + /* Timeout must have elapsed, try a resend? */ + new_attempt = 1; + } else { + recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_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 */ + if (read_length < offsetof(CMD_Reply, data) || + (reply->version != proto_version && + !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT && + ntohs(reply->status) == STT_BADPKTVERSION)) || + reply->pkt_type != PKT_TYPE_CMD_REPLY || + reply->res1 != 0 || + reply->res2 != 0 || + reply->command != request->command || + reply->sequence != request->sequence) { + DEBUG_LOG("Invalid reply"); + continue; + } + +#if PROTO_VERSION_NUMBER == 6 + /* Protocol version 5 is similar to 6 except there is no padding. + If a version 5 reply with STT_BADPKTVERSION is received, + switch our version and try again. */ + if (proto_version == PROTO_VERSION_NUMBER && + reply->version == PROTO_VERSION_NUMBER - 1) { + proto_version = PROTO_VERSION_NUMBER - 1; + n_attempts--; + new_attempt = 1; + continue; + } +#else +#error unknown compatibility with PROTO_VERSION - 1 +#endif + + /* Check that the packet contains all data it is supposed to have. + Unknown responses will always pass this test as their expected + length is zero. */ + if (read_length < PKL_ReplyLength(reply)) { + DEBUG_LOG("Reply too short"); + new_attempt = 1; + continue; + } + + /* Good packet received, print out results */ + DEBUG_LOG("Reply cmd=%d reply=%d stat=%d", + ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status)); + break; + } + } + } while (1); + + return 1; +} + +/* ================================================== */ + +static int +request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int verbose) +{ + int status; + + while (!submit_request(request, reply)) { + /* Try connecting to other addresses before giving up */ + if (open_io()) + continue; + printf("506 Cannot talk to daemon\n"); + return 0; + } + + status = ntohs(reply->status); + + if (verbose || status != STT_SUCCESS) { + switch (status) { + case STT_SUCCESS: + printf("200 OK"); + break; + case STT_ACCESSALLOWED: + printf("208 Access allowed"); + break; + case STT_ACCESSDENIED: + printf("209 Access denied"); + break; + case STT_FAILED: + printf("500 Failure"); + break; + case STT_UNAUTH: + printf("501 Not authorised"); + break; + case STT_INVALID: + printf("502 Invalid command"); + break; + case STT_NOSUCHSOURCE: + printf("503 No such source"); + break; + case STT_INVALIDTS: + printf("504 Duplicate or stale logon detected"); + break; + case STT_NOTENABLED: + printf("505 Facility not enabled in daemon"); + break; + case STT_BADSUBNET: + printf("507 Bad subnet"); + break; + case STT_NOHOSTACCESS: + printf("510 No command access from this host"); + break; + case STT_SOURCEALREADYKNOWN: + printf("511 Source already present"); + break; + case STT_TOOMANYSOURCES: + printf("512 Too many sources present"); + break; + case STT_NORTC: + printf("513 RTC driver not running"); + break; + case STT_BADRTCFILE: + printf("514 Can't write RTC parameters"); + break; + case STT_INVALIDAF: + printf("515 Invalid address family"); + break; + case STT_BADSAMPLE: + printf("516 Sample index out of range"); + break; + case STT_BADPKTVERSION: + printf("517 Protocol version mismatch"); + break; + case STT_BADPKTLENGTH: + printf("518 Packet length mismatch"); + break; + case STT_INACTIVE: + printf("519 Client logging is not active in the daemon"); + break; + default: + printf("520 Got unexpected error from daemon"); + } + printf("\n"); + } + + if (status != STT_SUCCESS && + status != STT_ACCESSALLOWED && status != STT_ACCESSDENIED) { + return 0; + } + + if (ntohs(reply->reply) != requested_reply) { + printf("508 Bad reply from daemon\n"); + return 0; + } + + /* Make sure an unknown response was not requested */ + assert(PKL_ReplyLength(reply)); + + return 1; +} + +/* ================================================== */ + +static void +print_seconds(unsigned long s) +{ + unsigned long d; + + if (s == (uint32_t)-1) { + printf(" -"); + } else if (s < 1200) { + printf("%4lu", s); + } else if (s < 36000) { + printf("%3lum", s / 60); + } else if (s < 345600) { + printf("%3luh", s / 3600); + } else { + d = s / 86400; + if (d > 999) { + printf("%3luy", d / 365); + } else { + printf("%3lud", d); + } + } +} + +/* ================================================== */ + +static void +print_nanoseconds(double s) +{ + s = fabs(s); + + if (s < 9999.5e-9) { + printf("%4.0fns", s * 1e9); + } else if (s < 9999.5e-6) { + printf("%4.0fus", s * 1e6); + } else if (s < 9999.5e-3) { + printf("%4.0fms", s * 1e3); + } else if (s < 999.5) { + printf("%5.1fs", s); + } else if (s < 99999.5) { + printf("%5.0fs", s); + } else if (s < 99999.5 * 60) { + printf("%5.0fm", s / 60); + } else if (s < 99999.5 * 3600) { + printf("%5.0fh", s / 3600); + } else if (s < 99999.5 * 3600 * 24) { + printf("%5.0fd", s / (3600 * 24)); + } else { + printf("%5.0fy", s / (3600 * 24 * 365)); + } +} + +/* ================================================== */ + +static void +print_signed_nanoseconds(double s) +{ + double x; + + x = fabs(s); + + if (x < 9999.5e-9) { + printf("%+5.0fns", s * 1e9); + } else if (x < 9999.5e-6) { + printf("%+5.0fus", s * 1e6); + } else if (x < 9999.5e-3) { + printf("%+5.0fms", s * 1e3); + } else if (x < 999.5) { + printf("%+6.1fs", s); + } else if (x < 99999.5) { + printf("%+6.0fs", s); + } else if (x < 99999.5 * 60) { + printf("%+6.0fm", s / 60); + } else if (x < 99999.5 * 3600) { + printf("%+6.0fh", s / 3600); + } else if (x < 99999.5 * 3600 * 24) { + printf("%+6.0fd", s / (3600 * 24)); + } else { + printf("%+6.0fy", s / (3600 * 24 * 365)); + } +} + +/* ================================================== */ + +static void +print_freq_ppm(double f) +{ + if (fabs(f) < 99999.5) { + printf("%10.3f", f); + } else { + printf("%10.0f", f); + } +} + +/* ================================================== */ + +static void +print_signed_freq_ppm(double f) +{ + if (fabs(f) < 99999.5) { + printf("%+10.3f", f); + } else { + printf("%+10.0f", f); + } +} + +/* ================================================== */ + +static void +print_clientlog_interval(int rate) +{ + if (rate >= 127) { + printf(" -"); + } else { + printf("%2d", rate); + } +} + +/* ================================================== */ + +static void +print_header(const char *header) +{ + int len; + + if (csv_mode) + return; + + printf("%s\n", header); + + len = strlen(header); + while (len--) + printf("="); + printf("\n"); +} + +/* ================================================== */ + +#define REPORT_END 0x1234 + +/* Print a report. The syntax of the format is similar to printf(), but not all + specifiers are supported and some are different! */ + +static void +print_report(const char *format, ...) +{ + char buf[256]; + va_list ap; + int i, field, sign, width, prec, spec; + const char *string; + unsigned long long_uinteger; + unsigned int uinteger; + int integer; + struct timespec *ts; + struct tm *tm; + double dbl; + + va_start(ap, format); + + for (field = 0; ; field++) { + /* Search for text between format specifiers and print it + if not in the CSV mode */ + for (i = 0; i < sizeof (buf) && format[i] != '%' && format[i] != '\0'; i++) + buf[i] = format[i]; + + if (i >= sizeof (buf)) + break; + + buf[i] = '\0'; + + if (!csv_mode) + printf("%s", buf); + + if (format[i] == '\0' || format[i + 1] == '\0') + break; + + format += i + 1; + + sign = 0; + width = 0; + prec = 5; + + if (*format == '+' || *format == '-') { + sign = 1; + format++; + } + + if (isdigit((unsigned char)*format)) { + width = atoi(format); + while (isdigit((unsigned char)*format)) + format++; + } + + if (*format == '.') { + format++; + prec = atoi(format); + while (isdigit((unsigned char)*format)) + format++; + } + + spec = *format; + format++; + + /* Disable human-readable formatting in the CSV mode */ + if (csv_mode) { + sign = width = 0; + + if (field > 0) + printf(","); + + switch (spec) { + case 'C': + spec = 'd'; + break; + case 'F': + case 'P': + prec = 3; + spec = 'f'; + break; + case 'O': + case 'S': + prec = 9; + spec = 'f'; + break; + case 'I': + spec = 'U'; + break; + case 'T': + spec = 'V'; + break; + } + } + + switch (spec) { + case 'B': /* boolean */ + integer = va_arg(ap, int); + printf("%s", integer ? "Yes" : "No"); + break; + case 'C': /* clientlog interval */ + integer = va_arg(ap, int); + print_clientlog_interval(integer); + break; + case 'F': /* absolute frequency in ppm with fast/slow keyword */ + case 'O': /* absolute offset in seconds with fast/slow keyword */ + dbl = va_arg(ap, double); + printf("%*.*f %s %s", width, prec, fabs(dbl), + spec == 'O' ? "seconds" : "ppm", + (dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast"); + break; + case 'I': /* interval with unit */ + long_uinteger = va_arg(ap, unsigned long); + print_seconds(long_uinteger); + break; + case 'L': /* leap status */ + integer = va_arg(ap, int); + switch (integer) { + case LEAP_Normal: + string = "Normal"; + break; + case LEAP_InsertSecond: + string = "Insert second"; + break; + case LEAP_DeleteSecond: + string = "Delete second"; + break; + case LEAP_Unsynchronised: + string = "Not synchronised"; + break; + default: + string = "Invalid"; + break; + } + printf("%s", string); + break; + case 'M': /* NTP mode */ + integer = va_arg(ap, int); + switch (integer) { + case MODE_ACTIVE: + string = "Symmetric active"; + break; + case MODE_PASSIVE: + string = "Symmetric passive"; + break; + case MODE_SERVER: + string = "Server"; + break; + default: + string = "Invalid"; + break; + } + printf("%s", string); + break; + case 'N': /* Timestamp source */ + integer = va_arg(ap, int); + switch (integer) { + case 'D': + string = "Daemon"; + break; + case 'K': + string = "Kernel"; + break; + case 'H': + string = "Hardware"; + break; + default: + string = "Invalid"; + break; + } + printf("%s", string); + break; + case 'P': /* frequency in ppm */ + dbl = va_arg(ap, double); + if (sign) + print_signed_freq_ppm(dbl); + else + print_freq_ppm(dbl); + break; + case 'R': /* reference ID in hexdecimal */ + long_uinteger = va_arg(ap, unsigned long); + printf("%08lX", long_uinteger); + break; + case 'S': /* offset with unit */ + dbl = va_arg(ap, double); + if (sign) + print_signed_nanoseconds(dbl); + else + print_nanoseconds(dbl); + break; + case 'T': /* timespec as date and time in UTC */ + ts = va_arg(ap, struct timespec *); + tm = gmtime(&ts->tv_sec); + if (!tm) + break; + strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm); + printf("%s", buf); + break; + case 'U': /* unsigned long in decimal */ + long_uinteger = va_arg(ap, unsigned long); + printf("%*lu", width, long_uinteger); + break; + case 'V': /* timespec as seconds since epoch */ + ts = va_arg(ap, struct timespec *); + printf("%s", UTI_TimespecToString(ts)); + break; + case 'b': /* unsigned int in binary */ + uinteger = va_arg(ap, unsigned int); + for (i = prec - 1; i >= 0; i--) + printf("%c", uinteger & 1U << i ? '1' : '0'); + break; + + /* Classic printf specifiers */ + case 'c': /* character */ + integer = va_arg(ap, int); + printf("%c", integer); + break; + case 'd': /* signed int in decimal */ + integer = va_arg(ap, int); + printf("%*d", width, integer); + break; + case 'f': /* double */ + dbl = va_arg(ap, double); + printf(sign ? "%+*.*f" : "%*.*f", width, prec, dbl); + break; + case 'o': /* unsigned int in octal */ + uinteger = va_arg(ap, unsigned int); + printf("%*o", width, uinteger); + break; + case 's': /* string */ + string = va_arg(ap, const char *); + if (sign) + printf("%-*s", width, string); + else + printf("%*s", width, string); + break; + case 'u': /* unsigned int in decimal */ + uinteger = va_arg(ap, unsigned int); + printf("%*u", width, uinteger); + break; + } + } + + /* Require terminating argument to catch bad type conversions */ + if (va_arg(ap, int) != REPORT_END) + assert(0); + + va_end(ap); + + if (csv_mode) + printf("\n"); +} + +/* ================================================== */ + +static void +print_info_field(const char *format, ...) +{ + va_list ap; + + if (csv_mode) + return; + + va_start(ap, format); + vprintf(format, ap); + va_end(ap); +} + +/* ================================================== */ + +static void +format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id, + IPAddr *ip_addr) +{ + if (ref) { + snprintf(buf, size, "%s", UTI_RefidToString(ref_id)); + } else if (no_dns || csv_mode) { + snprintf(buf, size, "%s", UTI_IPToString(ip_addr)); + } else { + DNS_IPAddress2Name(ip_addr, buf, size); + if (trunc_dns > 0 && strlen(buf) > trunc_dns) { + buf[trunc_dns - 1] = '>'; + buf[trunc_dns] = '\0'; + } + } +} + +/* ================================================== */ + +static int +check_for_verbose_flag(char *line) +{ + if (!csv_mode && !strcmp(line, "-v")) + return 1; + return 0; +} + +/* ================================================== */ + +static int +process_cmd_sources(char *line) +{ + CMD_Request request; + CMD_Reply reply; + IPAddr ip_addr; + uint32_t i, mode, n_sources; + char name[50], mode_ch, state_ch; + int verbose; + + /* Check whether to output verbose headers */ + verbose = check_for_verbose_flag(line); + + 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("|| .- xxxx [ yyyy ] +/- zzzz\n"); + printf("|| Reachability register (octal) -. | xxxx = adjusted offset,\n"); + printf("|| Log2(Polling interval) --. | | yyyy = measured offset,\n"); + printf("|| \\ | | zzzz = estimated error.\n"); + printf("|| | | \\\n"); + } + + print_header("MS Name/IP address Stratum Poll Reach LastRx Last sample "); + + /* "MS NNNNNNNNNNNNNNNNNNNNNNNNNNN SS PP RRR RRRR SSSSSSS[SSSSSSS] +/- SSSSSS" */ + + 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; + + mode = ntohs(reply.data.source_data.mode); + UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr); + format_name(name, sizeof (name), 25, + mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4, + ip_addr.addr.in4, &ip_addr); + + switch (mode) { + case RPY_SD_MD_CLIENT: + mode_ch = '^'; + break; + case RPY_SD_MD_PEER: + mode_ch = '='; + break; + case RPY_SD_MD_REF: + mode_ch = '#'; + break; + default: + mode_ch = ' '; + } + + switch (ntohs(reply.data.source_data.state)) { + case RPY_SD_ST_SYNC: + state_ch = '*'; + break; + case RPY_SD_ST_UNREACH: + state_ch = '?'; + break; + case RPY_SD_ST_FALSETICKER: + state_ch = 'x'; + break; + case RPY_SD_ST_JITTERY: + state_ch = '~'; + break; + case RPY_SD_ST_CANDIDATE: + state_ch = '+'; + break; + case RPY_SD_ST_OUTLIER: + state_ch = '-'; + break; + default: + state_ch = ' '; + } + + switch (ntohs(reply.data.source_data.flags)) { + default: + break; + } + + print_report("%c%c %-27s %2d %2d %3o %I %+S[%+S] +/- %S\n", + mode_ch, state_ch, name, + ntohs(reply.data.source_data.stratum), + (int16_t)ntohs(reply.data.source_data.poll), + ntohs(reply.data.source_data.reachability), + (unsigned long)ntohl(reply.data.source_data.since_sample), + UTI_FloatNetworkToHost(reply.data.source_data.latest_meas), + UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas), + UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err), + REPORT_END); + } + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_sourcestats(char *line) +{ + CMD_Request request; + CMD_Reply reply; + uint32_t i, n_sources; + int verbose = 0; + char name[50]; + IPAddr ip_addr; + + verbose = check_for_verbose_flag(line); + + 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"); + printf(" / .- Number of residual runs with same sign.\n"); + printf(" | / .- Length of measurement set (time).\n"); + printf(" | | / .- Est. clock freq error (ppm).\n"); + printf(" | | | / .- Est. error in freq.\n"); + printf(" | | | | / .- Est. offset.\n"); + printf(" | | | | | | On the -.\n"); + printf(" | | | | | | samples. \\\n"); + printf(" | | | | | | |\n"); + } + + print_header("Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev"); + + /* "NNNNNNNNNNNNNNNNNNNNNNNNN NP NR SSSS FFFFFFFFFF SSSSSSSSSS SSSSSSS SSSSSS" */ + + for (i = 0; i < n_sources; i++) { + request.command = htons(REQ_SOURCESTATS); + request.data.source_data.index = htonl(i); + if (!request_reply(&request, &reply, RPY_SOURCESTATS, 0)) + return 0; + + UTI_IPNetworkToHost(&reply.data.sourcestats.ip_addr, &ip_addr); + format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC, + ntohl(reply.data.sourcestats.ref_id), &ip_addr); + + print_report("%-25s %3U %3U %I %+P %P %+S %S\n", + name, + (unsigned long)ntohl(reply.data.sourcestats.n_samples), + (unsigned long)ntohl(reply.data.sourcestats.n_runs), + (unsigned long)ntohl(reply.data.sourcestats.span_seconds), + UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm), + UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm), + UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset), + UTI_FloatNetworkToHost(reply.data.sourcestats.sd), + REPORT_END); + } + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_tracking(char *line) +{ + CMD_Request request; + CMD_Reply reply; + IPAddr ip_addr; + uint32_t ref_id; + char name[50]; + struct timespec ref_time; + + request.command = htons(REQ_TRACKING); + if (!request_reply(&request, &reply, RPY_TRACKING, 0)) + return 0; + + ref_id = ntohl(reply.data.tracking.ref_id); + + 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); + + UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time); + + print_report("Reference ID : %R (%s)\n" + "Stratum : %u\n" + "Ref time (UTC) : %T\n" + "System time : %.9O of NTP time\n" + "Last offset : %+.9f seconds\n" + "RMS offset : %.9f seconds\n" + "Frequency : %.3F\n" + "Residual freq : %+.3f ppm\n" + "Skew : %.3f ppm\n" + "Root delay : %.9f seconds\n" + "Root dispersion : %.9f seconds\n" + "Update interval : %.1f seconds\n" + "Leap status : %L\n", + (unsigned long)ref_id, name, + ntohs(reply.data.tracking.stratum), + &ref_time, + UTI_FloatNetworkToHost(reply.data.tracking.current_correction), + UTI_FloatNetworkToHost(reply.data.tracking.last_offset), + UTI_FloatNetworkToHost(reply.data.tracking.rms_offset), + UTI_FloatNetworkToHost(reply.data.tracking.freq_ppm), + UTI_FloatNetworkToHost(reply.data.tracking.resid_freq_ppm), + UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm), + UTI_FloatNetworkToHost(reply.data.tracking.root_delay), + UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion), + UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval), + ntohs(reply.data.tracking.leap_status), REPORT_END); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_ntpdata(char *line) +{ + CMD_Request request; + CMD_Reply reply; + IPAddr remote_addr, local_addr; + struct timespec ref_time; + uint32_t i, n_sources; + uint16_t mode; + int specified_addr; + + if (*line) { + specified_addr = 1; + n_sources = 1; + } else { + specified_addr = 0; + 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); + } + + for (i = 0; i < n_sources; i++) { + if (specified_addr) { + if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) { + LOG(LOGS_ERR, "Could not get address for hostname"); + return 0; + } + } else { + request.command = htons(REQ_SOURCE_DATA); + request.data.source_data.index = htonl(i); + if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0)) + return 0; + + mode = ntohs(reply.data.source_data.mode); + if (mode != RPY_SD_MD_CLIENT && mode != RPY_SD_MD_PEER) + continue; + + UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr); + } + + request.command = htons(REQ_NTP_DATA); + UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr); + if (!request_reply(&request, &reply, RPY_NTP_DATA, 0)) + return 0; + + UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr); + UTI_IPNetworkToHost(&reply.data.ntp_data.local_addr, &local_addr); + UTI_TimespecNetworkToHost(&reply.data.ntp_data.ref_time, &ref_time); + + if (!specified_addr && !csv_mode) + printf("\n"); + + print_report("Remote address : %s (%R)\n" + "Remote port : %u\n" + "Local address : %s (%R)\n" + "Leap status : %L\n" + "Version : %u\n" + "Mode : %M\n" + "Stratum : %u\n" + "Poll interval : %d (%.0f seconds)\n" + "Precision : %d (%.9f seconds)\n" + "Root delay : %.6f seconds\n" + "Root dispersion : %.6f seconds\n" + "Reference ID : %R (%s)\n" + "Reference time : %T\n" + "Offset : %+.9f seconds\n" + "Peer delay : %.9f seconds\n" + "Peer dispersion : %.9f seconds\n" + "Response time : %.9f seconds\n" + "Jitter asymmetry: %+.2f\n" + "NTP tests : %.3b %.3b %.4b\n" + "Interleaved : %B\n" + "Authenticated : %B\n" + "TX timestamping : %N\n" + "RX timestamping : %N\n" + "Total TX : %U\n" + "Total RX : %U\n" + "Total valid RX : %U\n", + UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr), + ntohs(reply.data.ntp_data.remote_port), + UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr), + reply.data.ntp_data.leap, reply.data.ntp_data.version, + reply.data.ntp_data.mode, reply.data.ntp_data.stratum, + reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll), + reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision), + UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay), + UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion), + (unsigned long)ntohl(reply.data.ntp_data.ref_id), + reply.data.ntp_data.stratum <= 1 ? + UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "", + &ref_time, + UTI_FloatNetworkToHost(reply.data.ntp_data.offset), + UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay), + UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion), + UTI_FloatNetworkToHost(reply.data.ntp_data.response_time), + UTI_FloatNetworkToHost(reply.data.ntp_data.jitter_asymmetry), + ntohs(reply.data.ntp_data.flags) >> 7, + ntohs(reply.data.ntp_data.flags) >> 4, + ntohs(reply.data.ntp_data.flags), + ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED, + ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED, + reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char, + (unsigned long)ntohl(reply.data.ntp_data.total_tx_count), + (unsigned long)ntohl(reply.data.ntp_data.total_rx_count), + (unsigned long)ntohl(reply.data.ntp_data.total_valid_count), + 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)) + 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", + (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), + REPORT_END); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_smoothing(char *line) +{ + CMD_Request request; + CMD_Reply reply; + uint32_t flags; + + request.command = htons(REQ_SMOOTHING); + if (!request_reply(&request, &reply, RPY_SMOOTHING, 0)) + return 0; + + flags = ntohl(reply.data.smoothing.flags); + + print_report("Active : %B %s\n" + "Offset : %+.9f seconds\n" + "Frequency : %+.6f ppm\n" + "Wander : %+.6f ppm per second\n" + "Last update : %.1f seconds ago\n" + "Remaining time : %.1f seconds\n", + !!(flags & RPY_SMT_FLAG_ACTIVE), + flags & RPY_SMT_FLAG_LEAPONLY ? "(leap second only)" : "", + UTI_FloatNetworkToHost(reply.data.smoothing.offset), + UTI_FloatNetworkToHost(reply.data.smoothing.freq_ppm), + UTI_FloatNetworkToHost(reply.data.smoothing.wander_ppm), + UTI_FloatNetworkToHost(reply.data.smoothing.last_update_ago), + UTI_FloatNetworkToHost(reply.data.smoothing.remaining_time), + REPORT_END); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_smoothtime(CMD_Request *msg, const char *line) +{ + if (!strcmp(line, "reset")) { + msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_RESET); + } else if (!strcmp(line, "activate")) { + msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE); + } else { + LOG(LOGS_ERR, "Invalid syntax for smoothtime command"); + return 0; + } + + msg->command = htons(REQ_SMOOTHTIME); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_rtcreport(char *line) +{ + CMD_Request request; + CMD_Reply reply; + struct timespec ref_time; + + request.command = htons(REQ_RTCREPORT); + if (!request_reply(&request, &reply, RPY_RTC, 0)) + return 0; + + UTI_TimespecNetworkToHost(&reply.data.rtc.ref_time, &ref_time); + + print_report("RTC ref time (UTC) : %T\n" + "Number of samples : %u\n" + "Number of runs : %u\n" + "Sample span period : %I\n" + "RTC is fast by : %12.6f seconds\n" + "RTC gains time at : %9.3f ppm\n", + &ref_time, + ntohs(reply.data.rtc.n_samples), + ntohs(reply.data.rtc.n_runs), + (unsigned long)ntohl(reply.data.rtc.span_seconds), + UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast), + UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm), + REPORT_END); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_clients(char *line) +{ + CMD_Request request; + CMD_Reply reply; + IPAddr ip; + uint32_t i, n_clients, next_index, n_indices; + RPY_ClientAccesses_Client *client; + char name[50]; + + next_index = 0; + + print_header("Hostname NTP Drop Int IntL Last Cmd Drop Int Last"); + + while (1) { + request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX2); + request.data.client_accesses_by_index.first_index = htonl(next_index); + request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES); + + if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX2, 0)) + return 0; + + n_clients = ntohl(reply.data.client_accesses_by_index.n_clients); + n_indices = ntohl(reply.data.client_accesses_by_index.n_indices); + + for (i = 0; i < n_clients && i < MAX_CLIENT_ACCESSES; i++) { + client = &reply.data.client_accesses_by_index.clients[i]; + + UTI_IPNetworkToHost(&client->ip, &ip); + + /* UNSPEC means the record could not be found in the daemon's tables. + We shouldn't ever generate this case, but ignore it if we do. */ + if (ip.family == IPADDR_UNSPEC) + continue; + + format_name(name, sizeof (name), 25, 0, 0, &ip); + + print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n", + name, + (unsigned long)ntohl(client->ntp_hits), + (unsigned long)ntohl(client->ntp_drops), + 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), + REPORT_END); + } + + /* Set the next index to probe based on what the server tells us */ + next_index = ntohl(reply.data.client_accesses_by_index.next_index); + + if (next_index >= n_indices || n_clients < MAX_CLIENT_ACCESSES) + break; + } + + return 1; +} + + +/* ================================================== */ +/* Process the manual list command */ +static int +process_cmd_manual_list(const char *line) +{ + CMD_Request request; + CMD_Reply reply; + uint32_t i, n_samples; + RPY_ManualListSample *sample; + struct timespec when; + + request.command = htons(REQ_MANUAL_LIST); + if (!request_reply(&request, &reply, RPY_MANUAL_LIST2, 0)) + return 0; + + n_samples = ntohl(reply.data.manual_list.n_samples); + print_info_field("210 n_samples = %lu\n", (unsigned long)n_samples); + + print_header("# Date Time(UTC) Slewed Original Residual"); + + for (i = 0; i < n_samples && i < MAX_MANUAL_LIST_SAMPLES; i++) { + sample = &reply.data.manual_list.samples[i]; + UTI_TimespecNetworkToHost(&sample->when, &when); + + print_report("%2d %s %10.2f %10.2f %10.2f\n", + i, UTI_TimeToLogForm(when.tv_sec), + UTI_FloatNetworkToHost(sample->slewed_offset), + UTI_FloatNetworkToHost(sample->orig_offset), + UTI_FloatNetworkToHost(sample->residual), + REPORT_END); + } + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_manual_delete(CMD_Request *msg, const char *line) +{ + int index; + + if (sscanf(line, "%d", &index) != 1) { + LOG(LOGS_ERR, "Bad syntax for manual delete command"); + return 0; + } + + msg->command = htons(REQ_MANUAL_DELETE); + msg->data.manual_delete.index = htonl(index); + return 1; +} + +/* ================================================== */ + +static int +process_cmd_settime(char *line) +{ + struct timespec ts; + time_t now, new_time; + CMD_Request request; + CMD_Reply reply; + double dfreq_ppm, new_afreq_ppm; + double offset; + + now = time(NULL); + new_time = get_date(line, &now); + + if (new_time == -1) { + printf("510 - Could not parse date string\n"); + } else { + ts.tv_sec = new_time; + ts.tv_nsec = 0; + UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts); + request.command = htons(REQ_SETTIME); + if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP2, 1)) { + offset = UTI_FloatNetworkToHost(reply.data.manual_timestamp.offset); + dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm); + new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm); + printf("Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm\n", + offset, dfreq_ppm, new_afreq_ppm); + return 1; + } + } + return 0; +} + +/* ================================================== */ + +static void +process_cmd_rekey(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_REKEY); +} + +/* ================================================== */ + +static int +process_cmd_makestep(CMD_Request *msg, char *line) +{ + int limit; + double threshold; + + if (*line) { + if (sscanf(line, "%lf %d", &threshold, &limit) != 2) { + LOG(LOGS_ERR, "Bad syntax for makestep command"); + return 0; + } + msg->command = htons(REQ_MODIFY_MAKESTEP); + msg->data.modify_makestep.limit = htonl(limit); + msg->data.modify_makestep.threshold = UTI_FloatHostToNetwork(threshold); + } else { + msg->command = htons(REQ_MAKESTEP); + } + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_activity(const char *line) +{ + CMD_Request request; + CMD_Reply reply; + + request.command = htons(REQ_ACTIVITY); + if (!request_reply(&request, &reply, RPY_ACTIVITY, 0)) + return 0; + + print_info_field("200 OK\n"); + + print_report("%U sources online\n" + "%U sources offline\n" + "%U sources doing burst (return to online)\n" + "%U sources doing burst (return to offline)\n" + "%U sources with unknown address\n", + (unsigned long)ntohl(reply.data.activity.online), + (unsigned long)ntohl(reply.data.activity.offline), + (unsigned long)ntohl(reply.data.activity.burst_online), + (unsigned long)ntohl(reply.data.activity.burst_offline), + (unsigned long)ntohl(reply.data.activity.unresolved), + REPORT_END); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_reselectdist(CMD_Request *msg, char *line) +{ + double dist; + int ok; + msg->command = htons(REQ_RESELECTDISTANCE); + if (sscanf(line, "%lf", &dist) == 1) { + msg->data.reselect_distance.distance = UTI_FloatHostToNetwork(dist); + ok = 1; + } else { + ok = 0; + } + return ok; +} + +/* ================================================== */ + +static void +process_cmd_reselect(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_RESELECT); +} + +/* ================================================== */ + +static void +process_cmd_refresh(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_REFRESH); +} + +/* ================================================== */ + +static void +process_cmd_shutdown(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_SHUTDOWN); +} + +/* ================================================== */ + +static int +process_cmd_waitsync(char *line) +{ + CMD_Request request; + CMD_Reply reply; + IPAddr ip_addr; + uint32_t ref_id; + double correction, skew_ppm, max_correction, max_skew_ppm, interval; + int ret = 0, max_tries, i; + struct timeval timeout; + + max_tries = 0; + max_correction = 0.0; + max_skew_ppm = 0.0; + interval = 10.0; + + if (sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval)) + ; + + /* Don't allow shorter interval than 0.1 seconds */ + if (interval < 0.1) + interval = 0.1; + + request.command = htons(REQ_TRACKING); + + for (i = 1; ; i++) { + if (request_reply(&request, &reply, RPY_TRACKING, 0)) { + ref_id = ntohl(reply.data.tracking.ref_id); + UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr); + + correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction); + correction = fabs(correction); + skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm); + + print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n", + i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END); + + if ((ip_addr.family != IPADDR_UNSPEC || + (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) && + (max_correction == 0.0 || correction <= max_correction) && + (max_skew_ppm == 0.0 || skew_ppm <= max_skew_ppm)) { + ret = 1; + } + } + + if (!ret && (!max_tries || i < max_tries) && !quit) { + UTI_DoubleToTimeval(interval, &timeout); + if (select(0, NULL, NULL, NULL, &timeout)) + break; + } else { + break; + } + } + return ret; +} + +/* ================================================== */ + +static int +process_cmd_dns(const char *line) +{ + if (!strcmp(line, "-46")) { + DNS_SetAddressFamily(IPADDR_UNSPEC); + } else if (!strcmp(line, "-4")) { + DNS_SetAddressFamily(IPADDR_INET4); + } else if (!strcmp(line, "-6")) { + DNS_SetAddressFamily(IPADDR_INET6); + } else if (!strcmp(line, "-n")) { + no_dns = 1; + } else if (!strcmp(line, "+n")) { + no_dns = 0; + } else { + LOG(LOGS_ERR, "Unrecognized dns command"); + return 0; + } + return 1; +} + +/* ================================================== */ + +static int +process_cmd_timeout(const char *line) +{ + int timeout; + + timeout = atoi(line); + if (timeout < 100) { + LOG(LOGS_ERR, "Timeout %d is too short", timeout); + return 0; + } + initial_timeout = timeout; + return 1; +} + +/* ================================================== */ + +static int +process_cmd_retries(const char *line) +{ + int retries; + + retries = atoi(line); + if (retries < 0 || retries > 30) { + LOG(LOGS_ERR, "Invalid maximum number of retries"); + return 0; + } + max_retries = retries; + return 1; +} + +/* ================================================== */ + +static int +process_cmd_keygen(char *line) +{ + char hash_name[17]; + unsigned char key[512]; + unsigned int i, length, id = 1, bits = 160; + +#ifdef FEAT_SECHASH + snprintf(hash_name, sizeof (hash_name), "SHA1"); +#else + snprintf(hash_name, sizeof (hash_name), "MD5"); +#endif + + if (sscanf(line, "%u %16s %u", &id, hash_name, &bits)) + ; + + length = CLAMP(10, (bits + 7) / 8, sizeof (key)); + if (HSH_GetHashId(hash_name) < 0) { + LOG(LOGS_ERR, "Unknown hash function %s", hash_name); + return 0; + } + + UTI_GetRandomBytesUrandom(key, length); + + printf("%u %s HEX:", id, hash_name); + for (i = 0; i < length; i++) + printf("%02hhX", key[i]); + printf("\n"); + + return 1; +} + +/* ================================================== */ + +static int +process_line(char *line) +{ + char *command; + int do_normal_submit; + int ret; + CMD_Request tx_message; + CMD_Reply rx_message; + + ret = 0; + + do_normal_submit = 1; + + CPS_NormalizeLine(line); + + if (!*line) { + fflush(stderr); + fflush(stdout); + return 1; + }; + + command = line; + line = CPS_SplitWord(line); + + if (!strcmp(command, "accheck")) { + do_normal_submit = process_cmd_accheck(&tx_message, line); + } 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, "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); + } + } else if (!strcmp(command, "burst")) { + do_normal_submit = process_cmd_burst(&tx_message, line); + } else if (!strcmp(command, "clients")) { + ret = process_cmd_clients(line); + do_normal_submit = 0; + } 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); + } + } 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); + } + } 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); + } + } else if (!strcmp(command, "dfreq")) { + 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); + } else if (!strcmp(command, "dump")) { + process_cmd_dump(&tx_message, line); + } else if (!strcmp(command, "exit")) { + do_normal_submit = 0; + quit = 1; + ret = 1; + } else if (!strcmp(command, "help")) { + do_normal_submit = 0; + give_help(); + ret = 1; + } else if (!strcmp(command, "keygen")) { + ret = process_cmd_keygen(line); + do_normal_submit = 0; + } else if (!strcmp(command, "local")) { + do_normal_submit = process_cmd_local(&tx_message, line); + } else if (!strcmp(command, "makestep")) { + do_normal_submit = process_cmd_makestep(&tx_message, line); + } else if (!strcmp(command, "manual")) { + if (!strncmp(line, "list", 4)) { + do_normal_submit = 0; + ret = process_cmd_manual_list(CPS_SplitWord(line)); + } else if (!strncmp(line, "delete", 6)) { + do_normal_submit = process_cmd_manual_delete(&tx_message, CPS_SplitWord(line)); + } else { + do_normal_submit = process_cmd_manual(&tx_message, line); + } + } else if (!strcmp(command, "maxdelay")) { + do_normal_submit = process_cmd_maxdelay(&tx_message, line); + } else if (!strcmp(command, "maxdelaydevratio")) { + do_normal_submit = process_cmd_maxdelaydevratio(&tx_message, line); + } else if (!strcmp(command, "maxdelayratio")) { + do_normal_submit = process_cmd_maxdelayratio(&tx_message, line); + } else if (!strcmp(command, "maxpoll")) { + do_normal_submit = process_cmd_maxpoll(&tx_message, line); + } else if (!strcmp(command, "maxupdateskew")) { + do_normal_submit = process_cmd_maxupdateskew(&tx_message, line); + } else if (!strcmp(command, "minpoll")) { + do_normal_submit = process_cmd_minpoll(&tx_message, line); + } else if (!strcmp(command, "minstratum")) { + do_normal_submit = process_cmd_minstratum(&tx_message, line); + } else if (!strcmp(command, "ntpdata")) { + do_normal_submit = 0; + ret = process_cmd_ntpdata(line); + } else if (!strcmp(command, "offline")) { + 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")) { + do_normal_submit = 0; + quit = 1; + ret = 1; + } else if (!strcmp(command, "refresh")) { + process_cmd_refresh(&tx_message, line); + } else if (!strcmp(command, "rekey")) { + process_cmd_rekey(&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, "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, "serverstats")) { + do_normal_submit = 0; + ret = process_cmd_serverstats(line); + } else if (!strcmp(command, "settime")) { + do_normal_submit = 0; + ret = process_cmd_settime(line); + } else if (!strcmp(command, "shutdown")) { + process_cmd_shutdown(&tx_message, line); + } else if (!strcmp(command, "smoothing")) { + do_normal_submit = 0; + ret = process_cmd_smoothing(line); + } else if (!strcmp(command, "smoothtime")) { + do_normal_submit = process_cmd_smoothtime(&tx_message, line); + } else if (!strcmp(command, "sources")) { + do_normal_submit = 0; + ret = process_cmd_sources(line); + } else if (!strcmp(command, "sourcestats")) { + do_normal_submit = 0; + ret = process_cmd_sourcestats(line); + } else if (!strcmp(command, "timeout")) { + ret = process_cmd_timeout(line); + do_normal_submit = 0; + } else if (!strcmp(command, "tracking")) { + ret = process_cmd_tracking(line); + do_normal_submit = 0; + } else if (!strcmp(command, "trimrtc")) { + process_cmd_trimrtc(&tx_message, line); + } else if (!strcmp(command, "waitsync")) { + ret = process_cmd_waitsync(line); + do_normal_submit = 0; + } else if (!strcmp(command, "writertc")) { + process_cmd_writertc(&tx_message, line); + } else if (!strcmp(command, "authhash") || + !strcmp(command, "password")) { + /* Warn, but don't return error to not break scripts */ + LOG(LOGS_WARN, "Authentication is no longer supported"); + do_normal_submit = 0; + ret = 1; + } else { + LOG(LOGS_ERR, "Unrecognized command"); + do_normal_submit = 0; + } + + if (do_normal_submit) { + ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1); + } + fflush(stderr); + fflush(stdout); + return ret; +} + +/* ================================================== */ + +static int +process_args(int argc, char **argv, int multi) +{ + int total_length, i, ret = 0; + char *line; + + total_length = 0; + for(i=0; i<argc; i++) { + total_length += strlen(argv[i]) + 1; + } + + line = (char *) Malloc((2 + total_length) * sizeof(char)); + + for (i = 0; i < argc; i++) { + line[0] = '\0'; + if (multi) { + strcat(line, argv[i]); + } else { + for (; i < argc; i++) { + strcat(line, argv[i]); + if (i + 1 < argc) + strcat(line, " "); + } + } + + ret = process_line(line); + if (!ret || quit) + break; + } + + Free(line); + + return ret; +} + +/* ================================================== */ + +static void +signal_handler(int signum) +{ + quit = 1; +} + +/* ================================================== */ + +static void +display_gpl(void) +{ + printf("chrony version %s\n" + "Copyright (C) 1997-2003, 2007, 2009-2018 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", + CHRONY_VERSION); +} + +/* ================================================== */ + +static void +print_help(const char *progname) +{ + printf("Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]\n", + progname); +} + +/* ================================================== */ + +static void +print_version(void) +{ + printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES); +} + +/* ================================================== */ + +int +main(int argc, char **argv) +{ + char *line; + const char *progname = argv[0]; + const char *hostnames = NULL; + int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC; + int port = DEFAULT_CANDM_PORT; + + /* Parse (undocumented) long command-line options */ + for (optind = 1; optind < argc; optind++) { + if (!strcmp("--help", argv[optind])) { + print_help(progname); + return 0; + } else if (!strcmp("--version", argv[optind])) { + print_version(); + return 0; + } + } + + optind = 1; + + /* Parse short command-line options */ + while ((opt = getopt(argc, argv, "+46acdf:h:mnp:v")) != -1) { + switch (opt) { + case '4': + case '6': + family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6; + break; + case 'a': + case 'f': + /* For compatibility only */ + break; + case 'c': + csv_mode = 1; + break; + case 'd': + log_debug_enabled = 1; + break; + case 'h': + hostnames = optarg; + break; + case 'm': + multi = 1; + break; + case 'n': + no_dns = 1; + break; + case 'p': + port = atoi(optarg); + break; + case 'v': + print_version(); + return 0; + default: + print_help(progname); + return 1; + } + } + + if (isatty(0) && isatty(1) && isatty(2)) { + on_terminal = 1; + } + + if (on_terminal && optind == argc) { + display_gpl(); + } + + DNS_SetAddressFamily(family); + + if (!hostnames) { + hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1"; + } + + UTI_SetQuitSignalsHandler(signal_handler, 0); + + sockaddrs = get_sockaddrs(hostnames, port); + + if (!open_io()) + LOG_FATAL("Could not open connection to daemon"); + + if (optind < argc) { + ret = process_args(argc - optind, argv + optind, multi); + } else { + do { + line = read_line(); + if (line && !quit) { + ret = process_line(line); + }else { + /* supply the final '\n' when user exits via ^D */ + if( on_terminal ) printf("\n"); + } + } while (line && !quit); + } + + close_io(); + + ARR_DestroyInstance(sockaddrs); + + return !ret; +} + + diff --git a/clientlog.c b/clientlog.c new file mode 100644 index 0000000..86962a7 --- /dev/null +++ b/clientlog.c @@ -0,0 +1,695 @@ +/* + 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/clientlog.h b/clientlog.h new file mode 100644 index 0000000..552c767 --- /dev/null +++ b/clientlog.h @@ -0,0 +1,50 @@ +/* + 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. + * + ********************************************************************** + + ======================================================================= + + This module contains facilities for logging access by clients. + + */ + +#ifndef GOT_CLIENTLOG_H +#define GOT_CLIENTLOG_H + +#include "sysincl.h" +#include "reports.h" + +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_GetNtpMinPoll(void); + +/* 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 void CLG_GetServerStatsReport(RPT_ServerStatsReport *report); + +#endif /* GOT_CLIENTLOG_H */ diff --git a/cmdmon.c b/cmdmon.c new file mode 100644 index 0000000..3b4277a --- /dev/null +++ b/cmdmon.c @@ -0,0 +1,1724 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Miroslav Lichvar 2009-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. + * + ********************************************************************** + + ======================================================================= + + Command and monitoring module in the main program + */ + +#include "config.h" + +#include "sysincl.h" + +#include "cmdmon.h" +#include "candm.h" +#include "sched.h" +#include "util.h" +#include "logging.h" +#include "keys.h" +#include "ntp_sources.h" +#include "ntp_core.h" +#include "smooth.h" +#include "sources.h" +#include "sourcestats.h" +#include "reference.h" +#include "manual.h" +#include "memory.h" +#include "local.h" +#include "addrfilt.h" +#include "conf.h" +#include "rtc.h" +#include "pktlength.h" +#include "clientlog.h" +#include "refclock.h" + +/* ================================================== */ + +union sockaddr_all { + struct sockaddr_in in4; +#ifdef FEAT_IPV6 + struct sockaddr_in6 in6; +#endif + struct sockaddr_un un; + struct sockaddr sa; +}; + +/* 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 whether this module has been initialised or not */ +static int initialised = 0; + +/* ================================================== */ +/* Array of permission levels for command types */ + +static const char permissions[] = { + PERMIT_OPEN, /* NULL */ + PERMIT_AUTH, /* ONLINE */ + PERMIT_AUTH, /* OFFLINE */ + PERMIT_AUTH, /* BURST */ + PERMIT_AUTH, /* MODIFY_MINPOLL */ + PERMIT_AUTH, /* MODIFY_MAXPOLL */ + PERMIT_AUTH, /* DUMP */ + PERMIT_AUTH, /* MODIFY_MAXDELAY */ + PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */ + PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */ + PERMIT_OPEN, /* LOGON */ + PERMIT_AUTH, /* SETTIME */ + PERMIT_AUTH, /* LOCAL */ + PERMIT_AUTH, /* MANUAL */ + PERMIT_OPEN, /* N_SOURCES */ + PERMIT_OPEN, /* SOURCE_DATA */ + PERMIT_AUTH, /* REKEY */ + PERMIT_AUTH, /* ALLOW */ + PERMIT_AUTH, /* ALLOWALL */ + PERMIT_AUTH, /* DENY */ + PERMIT_AUTH, /* DENYALL */ + PERMIT_AUTH, /* CMDALLOW */ + PERMIT_AUTH, /* CMDALLOWALL */ + PERMIT_AUTH, /* CMDDENY */ + PERMIT_AUTH, /* CMDDENYALL */ + PERMIT_AUTH, /* ACCHECK */ + PERMIT_AUTH, /* CMDACCHECK */ + PERMIT_AUTH, /* ADD_SERVER */ + PERMIT_AUTH, /* ADD_PEER */ + PERMIT_AUTH, /* DEL_SOURCE */ + PERMIT_AUTH, /* WRITERTC */ + PERMIT_AUTH, /* DFREQ */ + PERMIT_AUTH, /* DOFFSET */ + PERMIT_OPEN, /* TRACKING */ + PERMIT_OPEN, /* SOURCESTATS */ + PERMIT_OPEN, /* RTCREPORT */ + PERMIT_AUTH, /* TRIMRTC */ + PERMIT_AUTH, /* CYCLELOGS */ + PERMIT_AUTH, /* SUBNETS_ACCESSED */ + PERMIT_AUTH, /* CLIENT_ACCESSES (by subnet) */ + PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX */ + PERMIT_OPEN, /* MANUAL_LIST */ + PERMIT_AUTH, /* MANUAL_DELETE */ + PERMIT_AUTH, /* MAKESTEP */ + PERMIT_OPEN, /* ACTIVITY */ + PERMIT_AUTH, /* MODIFY_MINSTRATUM */ + PERMIT_AUTH, /* MODIFY_POLLTARGET */ + PERMIT_AUTH, /* MODIFY_MAXDELAYDEVRATIO */ + PERMIT_AUTH, /* RESELECT */ + PERMIT_AUTH, /* RESELECTDISTANCE */ + PERMIT_AUTH, /* MODIFY_MAKESTEP */ + PERMIT_OPEN, /* SMOOTHING */ + PERMIT_AUTH, /* SMOOTHTIME */ + PERMIT_AUTH, /* REFRESH */ + PERMIT_AUTH, /* SERVER_STATS */ + PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */ + PERMIT_AUTH, /* LOCAL2 */ + PERMIT_AUTH, /* NTP_DATA */ + PERMIT_AUTH, /* ADD_SERVER2 */ + PERMIT_AUTH, /* ADD_PEER2 */ + PERMIT_AUTH, /* ADD_SERVER3 */ + PERMIT_AUTH, /* ADD_PEER3 */ + PERMIT_AUTH, /* SHUTDOWN */ + PERMIT_AUTH, /* ONOFFLINE */ +}; + +/* ================================================== */ + +/* This authorisation table is used for checking whether particular + machines are allowed to make command and monitoring requests. */ +static ADF_AuthTable access_auth_table; + +/* ================================================== */ +/* Forward prototypes */ +static void read_from_cmd_socket(int sock_fd, int event, void *anything); + +/* ================================================== */ + +static int +prepare_socket(int family, int port_number) +{ + 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)); + + 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); + + CNF_GetBindCommandAddress(IPADDR_INET4, &bind_address); + + 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); + 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); + + CNF_GetBindCommandAddress(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 + 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); + + return sock_fd; +} + +/* ================================================== */ + +static void +do_size_checks(void) +{ + int i, request_length, padding_length, reply_length; + CMD_Request request; + CMD_Reply reply; + + assert(offsetof(CMD_Request, data) == 20); + assert(offsetof(CMD_Reply, data) == 28); + + for (i = 0; i < N_REQUEST_TYPES; i++) { + request.version = PROTO_VERSION_NUMBER; + request.command = htons(i); + request_length = PKL_CommandLength(&request); + padding_length = PKL_CommandPaddingLength(&request); + if (padding_length > MAX_PADDING_LENGTH || padding_length > request_length || + request_length > sizeof (CMD_Request) || + (request_length && request_length < offsetof(CMD_Request, data))) + assert(0); + } + + for (i = 1; i < N_REPLY_TYPES; i++) { + reply.reply = htons(i); + reply.status = STT_SUCCESS; + reply_length = PKL_ReplyLength(&reply); + if ((reply_length && reply_length < offsetof(CMD_Reply, data)) || + reply_length > sizeof (CMD_Reply)) + assert(0); + } +} + +/* ================================================== */ + +void +CAM_Initialise(int family) +{ + 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 + + if (port_number && sock_fd4 < 0 +#ifdef FEAT_IPV6 + && sock_fd6 < 0 +#endif + ) { + LOG_FATAL("Could not open any command socket"); + } + + access_auth_table = ADF_CreateTable(); + +} + +/* ================================================== */ + +void +CAM_Finalise(void) +{ + if (sock_fdu >= 0) { + SCH_RemoveFileHandler(sock_fdu); + close(sock_fdu); + unlink(CNF_GetBindCommandPath()); + } + sock_fdu = -1; + if (sock_fd4 >= 0) { + SCH_RemoveFileHandler(sock_fd4); + close(sock_fd4); + } + sock_fd4 = -1; +#ifdef FEAT_IPV6 + if (sock_fd6 >= 0) { + SCH_RemoveFileHandler(sock_fd6); + close(sock_fd6); + } + sock_fd6 = -1; +#endif + + ADF_DestroyTable(access_auth_table); + + initialised = 0; +} + +/* ================================================== */ + +void +CAM_OpenUnixSocket(void) +{ + /* 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); +} + +/* ================================================== */ + +static void +transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to) +{ + int status; + int tx_message_length; + int sock_fd; + socklen_t addrlen; + + 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)); + return; + } + + DEBUG_LOG("Sent %d bytes to %s fd %d", status, + UTI_SockaddrToString(&where_to->sa), sock_fd); +} + +/* ================================================== */ + +static void +handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + SRC_DumpSources(); +} + +/* ================================================== */ + +static void +handle_online(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address, mask; + + UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask); + UTI_IPNetworkToHost(&rx_message->data.online.address, &address); + if (!NSR_SetConnectivity(&mask, &address, SRC_ONLINE)) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address, mask; + + UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask); + UTI_IPNetworkToHost(&rx_message->data.offline.address, &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; + + UTI_IPNetworkToHost(&rx_message->data.burst.mask, &mask); + UTI_IPNetworkToHost(&rx_message->data.burst.address, &address); + if (!NSR_InitiateSampleBurst(ntohl(rx_message->data.burst.n_good_samples), + ntohl(rx_message->data.burst.n_total_samples), + &mask, &address)) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_minpoll(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address; + + UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); + if (!NSR_ModifyMinpoll(&address, + ntohl(rx_message->data.modify_minpoll.new_minpoll))) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_maxpoll(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address; + + UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); + if (!NSR_ModifyMaxpoll(&address, + ntohl(rx_message->data.modify_minpoll.new_minpoll))) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_maxdelay(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address; + + UTI_IPNetworkToHost(&rx_message->data.modify_maxdelay.address, &address); + if (!NSR_ModifyMaxdelay(&address, + UTI_FloatNetworkToHost(rx_message->data.modify_maxdelay.new_max_delay))) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_maxdelayratio(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address; + + UTI_IPNetworkToHost(&rx_message->data.modify_maxdelayratio.address, &address); + if (!NSR_ModifyMaxdelayratio(&address, + UTI_FloatNetworkToHost(rx_message->data.modify_maxdelayratio.new_max_delay_ratio))) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_maxdelaydevratio(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address; + + UTI_IPNetworkToHost(&rx_message->data.modify_maxdelaydevratio.address, &address); + if (!NSR_ModifyMaxdelaydevratio(&address, + UTI_FloatNetworkToHost(rx_message->data.modify_maxdelaydevratio.new_max_delay_dev_ratio))) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_minstratum(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address; + + UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); + if (!NSR_ModifyMinstratum(&address, + ntohl(rx_message->data.modify_minstratum.new_min_stratum))) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_polltarget(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr address; + + UTI_IPNetworkToHost(&rx_message->data.modify_polltarget.address, &address); + if (!NSR_ModifyPolltarget(&address, + ntohl(rx_message->data.modify_polltarget.new_poll_target))) + tx_message->status = htons(STT_NOSUCHSOURCE); +} + +/* ================================================== */ + +static void +handle_modify_maxupdateskew(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + REF_ModifyMaxupdateskew(UTI_FloatNetworkToHost(rx_message->data.modify_maxupdateskew.new_max_update_skew)); +} + +/* ================================================== */ + +static void +handle_modify_makestep(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + REF_ModifyMakestep(ntohl(rx_message->data.modify_makestep.limit), + UTI_FloatNetworkToHost(rx_message->data.modify_makestep.threshold)); +} + +/* ================================================== */ + +static void +handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + struct timespec ts; + double offset, dfreq_ppm, new_afreq_ppm; + UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts); + if (!MNL_IsEnabled()) { + tx_message->status = htons(STT_NOTENABLED); + } else if (MNL_AcceptTimestamp(&ts, &offset, &dfreq_ppm, &new_afreq_ppm)) { + tx_message->reply = htons(RPY_MANUAL_TIMESTAMP2); + tx_message->data.manual_timestamp.offset = UTI_FloatHostToNetwork(offset); + tx_message->data.manual_timestamp.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm); + tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm); + } else { + tx_message->status = htons(STT_FAILED); + } +} + +/* ================================================== */ + +static void +handle_local(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + if (ntohl(rx_message->data.local.on_off)) { + REF_EnableLocal(ntohl(rx_message->data.local.stratum), + UTI_FloatNetworkToHost(rx_message->data.local.distance), + ntohl(rx_message->data.local.orphan)); + } else { + REF_DisableLocal(); + } +} + +/* ================================================== */ + +static void +handle_manual(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + int option; + option = ntohl(rx_message->data.manual.option); + switch (option) { + case 0: + MNL_Disable(); + break; + case 1: + MNL_Enable(); + break; + case 2: + MNL_Reset(); + break; + default: + tx_message->status = htons(STT_INVALID); + break; + } +} + +/* ================================================== */ + +static void +handle_n_sources(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + int n_sources; + n_sources = SRC_ReadNumberOfSources(); + tx_message->reply = htons(RPY_N_SOURCES); + tx_message->data.n_sources.n_sources = htonl(n_sources); +} + +/* ================================================== */ + +static void +handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_SourceReport report; + struct timespec now_corr; + + /* Get data */ + SCH_GetLastEventTime(&now_corr, NULL, NULL); + if (SRC_ReportSource(ntohl(rx_message->data.source_data.index), &report, &now_corr)) { + switch (SRC_GetType(ntohl(rx_message->data.source_data.index))) { + case SRC_NTP: + NSR_ReportSource(&report, &now_corr); + break; + case SRC_REFCLOCK: + RCL_ReportSource(&report, &now_corr); + break; + } + + tx_message->reply = htons(RPY_SOURCE_DATA); + + UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.source_data.ip_addr); + 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); + break; + case RPT_FALSETICKER: + tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER); + break; + 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); + break; + case RPT_OUTLIER: + tx_message->data.source_data.state = htons(RPY_SD_ST_OUTLIER); + break; + } + switch (report.mode) { + case RPT_NTP_CLIENT: + tx_message->data.source_data.mode = htons(RPY_SD_MD_CLIENT); + break; + case RPT_NTP_PEER: + tx_message->data.source_data.mode = htons(RPY_SD_MD_PEER); + break; + case RPT_LOCAL_REFERENCE: + 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.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); + tx_message->data.source_data.latest_meas = UTI_FloatHostToNetwork(report.latest_meas); + tx_message->data.source_data.latest_meas_err = UTI_FloatHostToNetwork(report.latest_meas_err); + } else { + tx_message->status = htons(STT_NOSUCHSOURCE); + } +} + +/* ================================================== */ + +static void +handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + KEY_Reload(); +} + +/* ================================================== */ + +static void +handle_allowdeny(CMD_Request *rx_message, CMD_Reply *tx_message, int allow, int all) +{ + IPAddr ip; + int subnet_bits; + + UTI_IPNetworkToHost(&rx_message->data.allow_deny.ip, &ip); + subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits); + if (!NCR_AddAccessRestriction(&ip, subnet_bits, allow, all)) + tx_message->status = htons(STT_BADSUBNET); +} + +/* ================================================== */ + +static void +handle_cmdallowdeny(CMD_Request *rx_message, CMD_Reply *tx_message, int allow, int all) +{ + IPAddr ip; + int subnet_bits; + + UTI_IPNetworkToHost(&rx_message->data.allow_deny.ip, &ip); + subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits); + if (!CAM_AddAccessRestriction(&ip, subnet_bits, allow, all)) + tx_message->status = htons(STT_BADSUBNET); +} + +/* ================================================== */ + +static void +handle_accheck(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr ip; + UTI_IPNetworkToHost(&rx_message->data.ac_check.ip, &ip); + if (NCR_CheckAccessRestriction(&ip)) { + tx_message->status = htons(STT_ACCESSALLOWED); + } else { + tx_message->status = htons(STT_ACCESSDENIED); + } +} + +/* ================================================== */ + +static void +handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + IPAddr ip; + UTI_IPNetworkToHost(&rx_message->data.ac_check.ip, &ip); + if (CAM_CheckAccessRestriction(&ip)) { + tx_message->status = htons(STT_ACCESSALLOWED); + } else { + tx_message->status = htons(STT_ACCESSDENIED); + } +} + +/* ================================================== */ + +static void +handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_message) +{ + NTP_Remote_Address rem_addr; + SourceParameters params; + NSR_Status status; + + 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)); + 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); + params.min_stratum = ntohl(rx_message->data.ntp_source.min_stratum); + params.poll_target = ntohl(rx_message->data.ntp_source.poll_target); + params.version = ntohl(rx_message->data.ntp_source.version); + 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.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); + params.max_delay_dev_ratio = + UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio); + params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay); + params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry); + params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset); + + 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.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, ¶ms); + switch (status) { + case NSR_Success: + 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); + break; + case NSR_NoSuchSource: + assert(0); + break; + } +} + +/* ================================================== */ + +static void +handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + NTP_Remote_Address rem_addr; + NSR_Status status; + + UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr); + rem_addr.port = 0; + + status = NSR_RemoveSource(&rem_addr); + switch (status) { + case NSR_Success: + break; + case NSR_NoSuchSource: + tx_message->status = htons(STT_NOSUCHSOURCE); + break; + case NSR_TooManySources: + case NSR_AlreadyInUse: + case NSR_InvalidAF: + assert(0); + break; + } +} + +/* ================================================== */ + +static void +handle_writertc(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + switch (RTC_WriteParameters()) { + case RTC_ST_OK: + break; + case RTC_ST_NODRV: + tx_message->status = htons(STT_NORTC); + break; + case RTC_ST_BADFILE: + tx_message->status = htons(STT_BADRTCFILE); + break; + } +} + +/* ================================================== */ + +static void +handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + double dfreq; + dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq); + LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6); + LOG(LOGS_INFO, "Accumulated delta freq of %.3fppm", dfreq); +} + +/* ================================================== */ + +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); +} + +/* ================================================== */ + +static void +handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_TrackingReport rpt; + + REF_GetTrackingReport(&rpt); + tx_message->reply = htons(RPY_TRACKING); + tx_message->data.tracking.ref_id = htonl(rpt.ref_id); + UTI_IPHostToNetwork(&rpt.ip_addr, &tx_message->data.tracking.ip_addr); + tx_message->data.tracking.stratum = htons(rpt.stratum); + tx_message->data.tracking.leap_status = htons(rpt.leap_status); + UTI_TimespecHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time); + tx_message->data.tracking.current_correction = UTI_FloatHostToNetwork(rpt.current_correction); + tx_message->data.tracking.last_offset = UTI_FloatHostToNetwork(rpt.last_offset); + tx_message->data.tracking.rms_offset = UTI_FloatHostToNetwork(rpt.rms_offset); + tx_message->data.tracking.freq_ppm = UTI_FloatHostToNetwork(rpt.freq_ppm); + tx_message->data.tracking.resid_freq_ppm = UTI_FloatHostToNetwork(rpt.resid_freq_ppm); + tx_message->data.tracking.skew_ppm = UTI_FloatHostToNetwork(rpt.skew_ppm); + tx_message->data.tracking.root_delay = UTI_FloatHostToNetwork(rpt.root_delay); + tx_message->data.tracking.root_dispersion = UTI_FloatHostToNetwork(rpt.root_dispersion); + tx_message->data.tracking.last_update_interval = UTI_FloatHostToNetwork(rpt.last_update_interval); +} + +/* ================================================== */ + +static void +handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_SmoothingReport report; + struct timespec now; + + SCH_GetLastEventTime(&now, NULL, NULL); + + if (!SMT_GetSmoothingReport(&report, &now)) { + tx_message->status = htons(STT_NOTENABLED); + return; + } + + tx_message->reply = htons(RPY_SMOOTHING); + tx_message->data.smoothing.flags = htonl((report.active ? RPY_SMT_FLAG_ACTIVE : 0) | + (report.leap_only ? RPY_SMT_FLAG_LEAPONLY : 0)); + tx_message->data.smoothing.offset = UTI_FloatHostToNetwork(report.offset); + tx_message->data.smoothing.freq_ppm = UTI_FloatHostToNetwork(report.freq_ppm); + tx_message->data.smoothing.wander_ppm = UTI_FloatHostToNetwork(report.wander_ppm); + tx_message->data.smoothing.last_update_ago = UTI_FloatHostToNetwork(report.last_update_ago); + tx_message->data.smoothing.remaining_time = UTI_FloatHostToNetwork(report.remaining_time); +} + +/* ================================================== */ + +static void +handle_smoothtime(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + struct timespec now; + int option; + + if (!SMT_IsEnabled()) { + tx_message->status = htons(STT_NOTENABLED); + return; + } + + option = ntohl(rx_message->data.smoothtime.option); + SCH_GetLastEventTime(&now, NULL, NULL); + + switch (option) { + case REQ_SMOOTHTIME_RESET: + SMT_Reset(&now); + break; + case REQ_SMOOTHTIME_ACTIVATE: + SMT_Activate(&now); + break; + default: + tx_message->status = htons(STT_INVALID); + break; + } +} + +/* ================================================== */ + +static void +handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + int status; + RPT_SourcestatsReport report; + struct timespec now_corr; + + SCH_GetLastEventTime(&now_corr, NULL, NULL); + status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index), + &report, &now_corr); + + if (status) { + tx_message->reply = htons(RPY_SOURCESTATS); + tx_message->data.sourcestats.ref_id = htonl(report.ref_id); + UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.sourcestats.ip_addr); + tx_message->data.sourcestats.n_samples = htonl(report.n_samples); + tx_message->data.sourcestats.n_runs = htonl(report.n_runs); + tx_message->data.sourcestats.span_seconds = htonl(report.span_seconds); + tx_message->data.sourcestats.resid_freq_ppm = UTI_FloatHostToNetwork(report.resid_freq_ppm); + tx_message->data.sourcestats.skew_ppm = UTI_FloatHostToNetwork(report.skew_ppm); + tx_message->data.sourcestats.sd = UTI_FloatHostToNetwork(report.sd); + tx_message->data.sourcestats.est_offset = UTI_FloatHostToNetwork(report.est_offset); + tx_message->data.sourcestats.est_offset_err = UTI_FloatHostToNetwork(report.est_offset_err); + } else { + tx_message->status = htons(STT_NOSUCHSOURCE); + } +} + +/* ================================================== */ + +static void +handle_rtcreport(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + int status; + RPT_RTC_Report report; + status = RTC_GetReport(&report); + if (status) { + tx_message->reply = htons(RPY_RTC); + UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time); + tx_message->data.rtc.n_samples = htons(report.n_samples); + tx_message->data.rtc.n_runs = htons(report.n_runs); + tx_message->data.rtc.span_seconds = htonl(report.span_seconds); + tx_message->data.rtc.rtc_seconds_fast = UTI_FloatHostToNetwork(report.rtc_seconds_fast); + tx_message->data.rtc.rtc_gain_rate_ppm = UTI_FloatHostToNetwork(report.rtc_gain_rate_ppm); + } else { + tx_message->status = htons(STT_NORTC); + } +} + +/* ================================================== */ + +static void +handle_trimrtc(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + if (!RTC_Trim()) + tx_message->status = htons(STT_NORTC); +} + +/* ================================================== */ + +static void +handle_cyclelogs(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + LOG_CycleLogFiles(); +} + +/* ================================================== */ + +static void +handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_ClientAccessByIndex_Report report; + RPY_ClientAccesses_Client *client; + int n_indices; + uint32_t i, j, req_first_index, req_n_clients; + struct timespec now; + + SCH_GetLastEventTime(&now, NULL, NULL); + + req_first_index = ntohl(rx_message->data.client_accesses_by_index.first_index); + 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; + + n_indices = CLG_GetNumberOfIndices(); + if (n_indices < 0) { + tx_message->status = htons(STT_INACTIVE); + return; + } + + tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2); + 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)) + 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->cmd_hits = htonl(report.cmd_hits); + client->ntp_drops = htonl(report.ntp_drops); + client->cmd_drops = htonl(report.cmd_drops); + client->ntp_interval = report.ntp_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_cmd_hit_ago = htonl(report.last_cmd_hit_ago); + } + + tx_message->data.client_accesses_by_index.next_index = htonl(i); + tx_message->data.client_accesses_by_index.n_clients = htonl(j); +} + +/* ================================================== */ + +static void +handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + int n_samples; + int i; + RPY_ManualListSample *sample; + RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES]; + + tx_message->reply = htons(RPY_MANUAL_LIST2); + + MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples); + tx_message->data.manual_list.n_samples = htonl(n_samples); + + for (i=0; i<n_samples; i++) { + sample = &tx_message->data.manual_list.samples[i]; + UTI_TimespecHostToNetwork(&report[i].when, &sample->when); + sample->slewed_offset = UTI_FloatHostToNetwork(report[i].slewed_offset); + sample->orig_offset = UTI_FloatHostToNetwork(report[i].orig_offset); + sample->residual = UTI_FloatHostToNetwork(report[i].residual); + } +} + +/* ================================================== */ + +static void +handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + int index; + + index = ntohl(rx_message->data.manual_delete.index); + if (!MNL_DeleteSample(index)) + tx_message->status = htons(STT_BADSAMPLE); +} + +/* ================================================== */ + +static void +handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + if (!LCL_MakeStep()) + tx_message->status = htons(STT_FAILED); +} + +/* ================================================== */ + +static void +handle_activity(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_ActivityReport report; + NSR_GetActivityReport(&report); + tx_message->data.activity.online = htonl(report.online); + tx_message->data.activity.offline = htonl(report.offline); + tx_message->data.activity.burst_online = htonl(report.burst_online); + tx_message->data.activity.burst_offline = htonl(report.burst_offline); + tx_message->data.activity.unresolved = htonl(report.unresolved); + tx_message->reply = htons(RPY_ACTIVITY); +} + +/* ================================================== */ + +static void +handle_reselect_distance(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + double dist; + dist = UTI_FloatNetworkToHost(rx_message->data.reselect_distance.distance); + SRC_SetReselectDistance(dist); +} + +/* ================================================== */ + +static void +handle_reselect(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + SRC_ReselectSource(); +} + +/* ================================================== */ + +static void +handle_refresh(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + NSR_RefreshAddresses(); +} + +/* ================================================== */ + +static void +handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_ServerStatsReport report; + + CLG_GetServerStatsReport(&report); + tx_message->reply = htons(RPY_SERVER_STATS); + tx_message->data.server_stats.ntp_hits = htonl(report.ntp_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.cmd_drops = htonl(report.cmd_drops); + tx_message->data.server_stats.log_drops = htonl(report.log_drops); +} + +/* ================================================== */ + +static void +handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_NTPReport report; + + UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &report.remote_addr); + + if (!NSR_GetNTPReport(&report)) { + tx_message->status = htons(STT_NOSUCHSOURCE); + return; + } + + tx_message->reply = htons(RPY_NTP_DATA); + UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr); + UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr); + tx_message->data.ntp_data.remote_port = htons(report.remote_port); + tx_message->data.ntp_data.leap = report.leap; + tx_message->data.ntp_data.version = report.version; + tx_message->data.ntp_data.mode = report.mode; + tx_message->data.ntp_data.stratum = report.stratum; + tx_message->data.ntp_data.poll = report.poll; + tx_message->data.ntp_data.precision = report.precision; + tx_message->data.ntp_data.root_delay = UTI_FloatHostToNetwork(report.root_delay); + tx_message->data.ntp_data.root_dispersion = UTI_FloatHostToNetwork(report.root_dispersion); + tx_message->data.ntp_data.ref_id = htonl(report.ref_id); + UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.ntp_data.ref_time); + tx_message->data.ntp_data.offset = UTI_FloatHostToNetwork(report.offset); + tx_message->data.ntp_data.peer_delay = UTI_FloatHostToNetwork(report.peer_delay); + tx_message->data.ntp_data.peer_dispersion = UTI_FloatHostToNetwork(report.peer_dispersion); + tx_message->data.ntp_data.response_time = UTI_FloatHostToNetwork(report.response_time); + tx_message->data.ntp_data.jitter_asymmetry = UTI_FloatHostToNetwork(report.jitter_asymmetry); + tx_message->data.ntp_data.flags = htons((report.tests & RPY_NTP_FLAGS_TESTS) | + (report.interleaved ? RPY_NTP_FLAG_INTERLEAVED : 0) | + (report.authenticated ? RPY_NTP_FLAG_AUTHENTICATED : 0)); + tx_message->data.ntp_data.tx_tss_char = report.tx_tss_char; + tx_message->data.ntp_data.rx_tss_char = report.rx_tss_char; + tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count); + tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count); + tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count); + memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved)); +} + +/* ================================================== */ + +static void +handle_shutdown(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + LOG(LOGS_INFO, "Received shutdown command"); + SCH_QuitProgram(); +} + +/* ================================================== */ +/* Read a packet and process it */ + +static void +read_from_cmd_socket(int sock_fd, int event, void *anything) +{ + CMD_Request rx_message; + CMD_Reply tx_message; + int status, read_length, expected_length, rx_message_length; + int localhost, allowed, log_index; + union sockaddr_all where_from; + socklen_t from_length; + IPAddr remote_ip; + unsigned short remote_port, 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); + 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; + + /* 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) */ + 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) + return; + assert(sock_fd == sock_fdu); + 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; + } + + 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) { + + /* We don't know how to process anything like this or an error reply + would be larger than the request */ + DEBUG_LOG("Command packet dropped"); + return; + } + + expected_length = PKL_CommandLength(&rx_message); + rx_command = ntohs(rx_message.command); + + memset(&tx_message, 0, sizeof (tx_message)); + + tx_message.version = PROTO_VERSION_NUMBER; + tx_message.pkt_type = PKT_TYPE_CMD_REPLY; + tx_message.command = rx_message.command; + tx_message.reply = htons(RPY_NULL); + tx_message.status = htons(STT_SUCCESS); + tx_message.sequence = rx_message.sequence; + + if (rx_message.version != PROTO_VERSION_NUMBER) { + DEBUG_LOG("Command packet has invalid version (%d != %d)", + rx_message.version, PROTO_VERSION_NUMBER); + + if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) { + tx_message.status = htons(STT_BADPKTVERSION); + transmit_reply(&tx_message, &where_from); + } + return; + } + + if (rx_command >= N_REQUEST_TYPES || + expected_length < (int)offsetof(CMD_Request, data)) { + DEBUG_LOG("Command packet has invalid command %d", rx_command); + + tx_message.status = htons(STT_INVALID); + transmit_reply(&tx_message, &where_from); + return; + } + + if (read_length < expected_length) { + DEBUG_LOG("Command packet is too short (%d < %d)", read_length, + expected_length); + + tx_message.status = htons(STT_BADPKTLENGTH); + transmit_reply(&tx_message, &where_from); + 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); + } else { + /* 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) { + assert(sock_fd == sock_fdu); + allowed = 1; + } else { + switch (permissions[rx_command]) { + case PERMIT_AUTH: + allowed = 0; + break; + case PERMIT_LOCAL: + allowed = localhost; + break; + case PERMIT_OPEN: + allowed = 1; + break; + default: + assert(0); + allowed = 0; + } + } + + if (allowed) { + switch(rx_command) { + case REQ_NULL: + /* Do nothing */ + break; + + case REQ_DUMP: + handle_dump(&rx_message, &tx_message); + break; + + case REQ_ONLINE: + handle_online(&rx_message, &tx_message); + break; + + case REQ_OFFLINE: + handle_offline(&rx_message, &tx_message); + break; + + case REQ_BURST: + handle_burst(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MINPOLL: + handle_modify_minpoll(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MAXPOLL: + handle_modify_maxpoll(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MAXDELAY: + handle_modify_maxdelay(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MAXDELAYRATIO: + handle_modify_maxdelayratio(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MAXDELAYDEVRATIO: + handle_modify_maxdelaydevratio(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MAXUPDATESKEW: + handle_modify_maxupdateskew(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MAKESTEP: + handle_modify_makestep(&rx_message, &tx_message); + break; + + case REQ_LOGON: + /* Authentication is no longer supported, log-on always fails */ + tx_message.status = htons(STT_FAILED); + break; + + case REQ_SETTIME: + handle_settime(&rx_message, &tx_message); + break; + + case REQ_LOCAL2: + handle_local(&rx_message, &tx_message); + break; + + case REQ_MANUAL: + handle_manual(&rx_message, &tx_message); + break; + + case REQ_N_SOURCES: + handle_n_sources(&rx_message, &tx_message); + break; + + case REQ_SOURCE_DATA: + handle_source_data(&rx_message, &tx_message); + break; + + case REQ_REKEY: + handle_rekey(&rx_message, &tx_message); + break; + + case REQ_ALLOW: + handle_allowdeny(&rx_message, &tx_message, 1, 0); + break; + + case REQ_ALLOWALL: + handle_allowdeny(&rx_message, &tx_message, 1, 1); + break; + + case REQ_DENY: + handle_allowdeny(&rx_message, &tx_message, 0, 0); + break; + + case REQ_DENYALL: + handle_allowdeny(&rx_message, &tx_message, 0, 1); + break; + + case REQ_CMDALLOW: + handle_cmdallowdeny(&rx_message, &tx_message, 1, 0); + break; + + case REQ_CMDALLOWALL: + handle_cmdallowdeny(&rx_message, &tx_message, 1, 1); + break; + + case REQ_CMDDENY: + handle_cmdallowdeny(&rx_message, &tx_message, 0, 0); + break; + + case REQ_CMDDENYALL: + handle_cmdallowdeny(&rx_message, &tx_message, 0, 1); + break; + + case REQ_ACCHECK: + handle_accheck(&rx_message, &tx_message); + break; + + case REQ_CMDACCHECK: + 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); + break; + + case REQ_DEL_SOURCE: + handle_del_source(&rx_message, &tx_message); + break; + + case REQ_WRITERTC: + handle_writertc(&rx_message, &tx_message); + break; + + case REQ_DFREQ: + handle_dfreq(&rx_message, &tx_message); + break; + + case REQ_DOFFSET: + handle_doffset(&rx_message, &tx_message); + break; + + case REQ_TRACKING: + handle_tracking(&rx_message, &tx_message); + break; + + case REQ_SMOOTHING: + handle_smoothing(&rx_message, &tx_message); + break; + + case REQ_SMOOTHTIME: + handle_smoothtime(&rx_message, &tx_message); + break; + + case REQ_SOURCESTATS: + handle_sourcestats(&rx_message, &tx_message); + break; + + case REQ_RTCREPORT: + handle_rtcreport(&rx_message, &tx_message); + break; + + case REQ_TRIMRTC: + handle_trimrtc(&rx_message, &tx_message); + break; + + case REQ_CYCLELOGS: + handle_cyclelogs(&rx_message, &tx_message); + break; + + case REQ_CLIENT_ACCESSES_BY_INDEX2: + handle_client_accesses_by_index(&rx_message, &tx_message); + break; + + case REQ_MANUAL_LIST: + handle_manual_list(&rx_message, &tx_message); + break; + + case REQ_MANUAL_DELETE: + handle_manual_delete(&rx_message, &tx_message); + break; + + case REQ_MAKESTEP: + handle_make_step(&rx_message, &tx_message); + break; + + case REQ_ACTIVITY: + handle_activity(&rx_message, &tx_message); + break; + + case REQ_RESELECTDISTANCE: + handle_reselect_distance(&rx_message, &tx_message); + break; + + case REQ_RESELECT: + handle_reselect(&rx_message, &tx_message); + break; + + case REQ_MODIFY_MINSTRATUM: + handle_modify_minstratum(&rx_message, &tx_message); + break; + + case REQ_MODIFY_POLLTARGET: + handle_modify_polltarget(&rx_message, &tx_message); + break; + + case REQ_REFRESH: + handle_refresh(&rx_message, &tx_message); + break; + + case REQ_SERVER_STATS: + handle_server_stats(&rx_message, &tx_message); + break; + + case REQ_NTP_DATA: + handle_ntp_data(&rx_message, &tx_message); + break; + + case REQ_SHUTDOWN: + handle_shutdown(&rx_message, &tx_message); + break; + + case REQ_ONOFFLINE: + handle_onoffline(&rx_message, &tx_message); + break; + + default: + DEBUG_LOG("Unhandled command %d", rx_command); + tx_message.status = htons(STT_FAILED); + break; + } + } else { + tx_message.status = htons(STT_UNAUTH); + } + } + + /* 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 + } +} + +/* ================================================== */ + +int +CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all) + { + ADF_Status status; + + if (allow) { + if (all) { + status = ADF_AllowAll(access_auth_table, ip_addr, subnet_bits); + } else { + status = ADF_Allow(access_auth_table, ip_addr, subnet_bits); + } + } else { + if (all) { + status = ADF_DenyAll(access_auth_table, ip_addr, subnet_bits); + } else { + status = ADF_Deny(access_auth_table, ip_addr, subnet_bits); + } + } + + if (status == ADF_BADSUBNET) { + return 0; + } else if (status == ADF_SUCCESS) { + return 1; + } else { + return 0; + } +} + +/* ================================================== */ + +int +CAM_CheckAccessRestriction(IPAddr *ip_addr) +{ + return ADF_IsAllowed(access_auth_table, ip_addr); +} + + +/* ================================================== */ +/* ================================================== */ diff --git a/cmdmon.h b/cmdmon.h new file mode 100644 index 0000000..5b717d2 --- /dev/null +++ b/cmdmon.h @@ -0,0 +1,40 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2002 + * + * 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 control and monitoring module in the software + */ + +#ifndef GOT_CMDMON_H +#define GOT_CMDMON_H + +#include "addressing.h" + +extern void CAM_Initialise(int family); + +extern void CAM_Finalise(void); + +extern void CAM_OpenUnixSocket(void); +extern int CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all); +extern int CAM_CheckAccessRestriction(IPAddr *ip_addr); + +#endif /* GOT_CMDMON_H */ diff --git a/cmdparse.c b/cmdparse.c new file mode 100644 index 0000000..6fae81c --- /dev/null +++ b/cmdparse.c @@ -0,0 +1,289 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Miroslav Lichvar 2013-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. + * + ********************************************************************** + + ======================================================================= + + Module for parsing various forms of directive and command lines that + are common to the configuration file and to the command client. + + */ + +#include "config.h" + +#include "sysincl.h" + +#include "cmdparse.h" +#include "memory.h" +#include "nameserv.h" +#include "ntp.h" +#include "util.h" + +/* ================================================== */ + +int +CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) +{ + char *hostname, *cmd; + int n; + + src->port = SRC_DEFAULT_PORT; + src->params.minpoll = SRC_DEFAULT_MINPOLL; + src->params.maxpoll = SRC_DEFAULT_MAXPOLL; + src->params.connectivity = SRC_ONLINE; + src->params.auto_offline = 0; + src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL; + src->params.burst = 0; + src->params.iburst = 0; + src->params.min_stratum = SRC_DEFAULT_MINSTRATUM; + src->params.poll_target = SRC_DEFAULT_POLLTARGET; + src->params.version = 0; + 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.authkey = INACTIVE_AUTHKEY; + src->params.max_delay = SRC_DEFAULT_MAXDELAY; + src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; + src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; + src->params.min_delay = 0.0; + src->params.asymmetry = SRC_DEFAULT_ASYMMETRY; + src->params.offset = 0.0; + + hostname = line; + line = CPS_SplitWord(line); + + if (!*hostname) + return 0; + + src->name = hostname; + + /* Parse options */ + for (; *line; line += n) { + cmd = line; + line = CPS_SplitWord(line); + n = 0; + + if (!strcasecmp(cmd, "auto_offline")) { + src->params.auto_offline = 1; + } else if (!strcasecmp(cmd, "burst")) { + src->params.burst = 1; + } else if (!strcasecmp(cmd, "iburst")) { + src->params.iburst = 1; + } else if (!strcasecmp(cmd, "offline")) { + src->params.connectivity = SRC_OFFLINE; + } else if (!strcasecmp(cmd, "noselect")) { + src->params.sel_options |= SRC_SELECT_NOSELECT; + } else if (!strcasecmp(cmd, "prefer")) { + src->params.sel_options |= SRC_SELECT_PREFER; + } else if (!strcasecmp(cmd, "require")) { + src->params.sel_options |= SRC_SELECT_REQUIRE; + } else if (!strcasecmp(cmd, "trust")) { + src->params.sel_options |= SRC_SELECT_TRUST; + } else if (!strcasecmp(cmd, "key")) { + if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 || + src->params.authkey == INACTIVE_AUTHKEY) + return 0; + } else if (!strcasecmp(cmd, "asymmetry")) { + if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1) + 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; + } else if (!strcasecmp(cmd, "maxdelayratio")) { + if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "maxdelaydevratio")) { + if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "maxpoll")) { + if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "maxsamples")) { + if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "maxsources")) { + if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "mindelay")) { + if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "minpoll")) { + if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "minsamples")) { + if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "minstratum")) { + if (sscanf(line, "%d%n", &src->params.min_stratum, &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) + return 0; + } else if (!strcasecmp(cmd, "polltarget")) { + if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "presend")) { + if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "version")) { + if (sscanf(line, "%d%n", &src->params.version, &n) != 1) + return 0; + } else if (!strcasecmp(cmd, "xleave")) { + src->params.interleaved = 1; + } else { + return 0; + } + } + + return 1; +} + +/* ================================================== */ + +int +CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance) +{ + int n; + char *cmd; + + *stratum = 10; + *distance = 1.0; + *orphan = 0; + + while (*line) { + cmd = line; + line = CPS_SplitWord(line); + + if (!strcasecmp(cmd, "stratum")) { + if (sscanf(line, "%d%n", stratum, &n) != 1 || + *stratum >= NTP_MAX_STRATUM || *stratum <= 0) + return 0; + } else if (!strcasecmp(cmd, "orphan")) { + *orphan = 1; + n = 0; + } else if (!strcasecmp(cmd, "distance")) { + if (sscanf(line, "%lf%n", distance, &n) != 1) + return 0; + } else { + return 0; + } + + line += n; + } + + return 1; +} + +/* ================================================== */ + +void +CPS_NormalizeLine(char *line) +{ + char *p, *q; + int space = 1, first = 1; + + /* Remove white-space at beginning and replace white-spaces with space char */ + for (p = q = line; *p; p++) { + if (isspace((unsigned char)*p)) { + if (!space) + *q++ = ' '; + space = 1; + continue; + } + + /* Discard comment lines */ + if (first && strchr("!;#%", *p)) + break; + + *q++ = *p; + space = first = 0; + } + + /* Strip trailing space */ + if (q > line && q[-1] == ' ') + q--; + + *q = '\0'; +} + +/* ================================================== */ + +char * +CPS_SplitWord(char *line) +{ + char *p = line, *q = line; + + /* Skip white-space before the word */ + while (*q && isspace((unsigned char)*q)) + q++; + + /* Move the word to the beginning */ + while (*q && !isspace((unsigned char)*q)) + *p++ = *q++; + + /* Find the next word */ + while (*q && isspace((unsigned char)*q)) + q++; + + *p = '\0'; + + /* Return pointer to the next word or NUL */ + return q; +} + +/* ================================================== */ + +int +CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key) +{ + char *s1, *s2, *s3, *s4; + + s1 = line; + s2 = CPS_SplitWord(s1); + s3 = CPS_SplitWord(s2); + s4 = CPS_SplitWord(s3); + + /* Require two or three words */ + if (!*s2 || *s4) + return 0; + + if (sscanf(s1, "%"SCNu32, id) != 1) + return 0; + + if (*s3) { + *hash = s2; + *key = s3; + } else { + *hash = "MD5"; + *key = s2; + } + + return 1; +} diff --git a/cmdparse.h b/cmdparse.h new file mode 100644 index 0000000..19f4bb7 --- /dev/null +++ b/cmdparse.h @@ -0,0 +1,54 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2002 + * + * 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 command parser + */ + +#ifndef GOT_CMDPARSE_H +#define GOT_CMDPARSE_H + +#include "srcparams.h" +#include "addressing.h" + +typedef struct { + char *name; + unsigned short 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 enable local reference */ +extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance); + +/* Remove extra white-space and comments */ +extern void CPS_NormalizeLine(char *line); + +/* Terminate first word and return pointer to the next word */ +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); + +#endif /* GOT_CMDPARSE_H */ @@ -0,0 +1,2036 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Miroslav Lichvar 2009-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. + * + ********************************************************************** + + ======================================================================= + + Module that reads and processes the configuration file. + */ + +#include "config.h" + +#include "sysincl.h" + +#include "array.h" +#include "conf.h" +#include "ntp_sources.h" +#include "ntp_core.h" +#include "refclock.h" +#include "cmdmon.h" +#include "srcparams.h" +#include "logging.h" +#include "nameserv.h" +#include "memory.h" +#include "cmdparse.h" +#include "util.h" + +/* ================================================== */ +/* Forward prototypes */ + +static int parse_string(char *line, char **result); +static int parse_int(char *line, int *result); +static int parse_double(char *line, double *result); +static int parse_null(char *line); + +static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow); +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_fallbackdrift(char *); +static void parse_hwtimestamp(char *); +static void parse_include(char *); +static void parse_initstepslew(char *); +static void parse_leapsecmode(char *); +static void parse_local(char *); +static void parse_log(char *); +static void parse_mailonchange(char *); +static void parse_makestep(char *); +static void parse_maxchange(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_tempcomp(char *); + +/* ================================================== */ +/* Configuration variables */ + +static int restarted = 0; +static char *rtc_device; +static int acquisition_port = -1; +static int ntp_port = NTP_PORT; +static char *keys_file = NULL; +static char *drift_file = NULL; +static char *rtc_file = NULL; +static double max_update_skew = 1000.0; +static double correction_time_ratio = 3.0; +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 max_distance = 3.0; +static double max_jitter = 1.0; +static double reselect_distance = 1e-4; +static double stratum_weight = 1e-3; +static double combine_limit = 3.0; + +static int cmd_port = DEFAULT_CANDM_PORT; + +static int raw_measurements = 0; +static int do_log_measurements = 0; +static int do_log_statistics = 0; +static int do_log_tracking = 0; +static int do_log_rtc = 0; +static int do_log_refclocks = 0; +static int do_log_tempcomp = 0; +static int log_banner = 32; +static char *logdir; +static char *dumpdir; + +static int enable_local=0; +static int local_stratum; +static int local_orphan; +static double local_distance; + +/* Threshold (in seconds) - if absolute value of initial error is less + than this, slew instead of stepping */ +static double init_slew_threshold; +/* Array of IPAddr */ +static ARR_Instance init_sources; + +static int enable_manual=0; + +/* Flag set if the RTC runs UTC (default is it runs local time + incl. daylight saving). */ +static int rtc_on_utc = 0; + +/* Filename used to read the hwclock(8) LOCAL/UTC setting */ +static char *hwclock_file; + +/* Flag set if the RTC should be automatically synchronised by kernel */ +static int rtc_sync = 0; + +/* Limit and threshold for clock stepping */ +static int make_step_limit = 0; +static double make_step_threshold = 0.0; + +/* Threshold for automatic RTC trimming */ +static double rtc_autotrim_threshold = 0.0; + +/* Minimum number of selectables sources required to update the clock */ +static int min_sources = 1; + +/* Number of updates before offset checking, number of ignored updates + before exiting and the maximum allowed offset */ +static int max_offset_delay = -1; +static int max_offset_ignore; +static double max_offset; + +/* Maximum and minimum number of samples per source */ +static int max_samples = 0; /* no limit */ +static int min_samples = 6; + +/* Threshold for a time adjustment to be logged to syslog */ +static double log_change_threshold = 1.0; + +static char *mail_user_on_change = NULL; +static double mail_change_threshold = 0.0; + +/* Flag indicating that we don't want to log clients, e.g. to save + memory */ +static int no_client_log = 0; + +/* Limit memory allocated for the clients log */ +static unsigned long client_log_limit = 524288; + +/* Minimum and maximum fallback drift intervals */ +static int fb_drift_min = 0; +static int fb_drift_max = 0; + +/* IP addresses for binding the NTP server sockets to. UNSPEC family means + INADDR_ANY will be used */ +static IPAddr bind_address4, bind_address6; + +/* IP addresses for binding the NTP client sockets to. UNSPEC family means + INADDR_ANY will be used */ +static IPAddr bind_acq_address4, bind_acq_address6; + +/* IP addresses for binding the command socket to. UNSPEC family means + the loopback address will be used */ +static IPAddr bind_cmd_address4, bind_cmd_address6; + +/* Path to the Unix domain command socket. */ +static char *bind_cmd_path; + +/* 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; + +/* 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 cmd_ratelimit_enabled = 0; +static int cmd_ratelimit_interval = -4; +static int cmd_ratelimit_burst = 8; +static int cmd_ratelimit_leak = 2; + +/* Smoothing constants */ +static double smooth_max_freq = 0.0; /* in ppm */ +static double smooth_max_wander = 0.0; /* in ppm/s */ +static int smooth_leap_only = 0; + +/* Temperature sensor, update interval and compensation coefficients */ +static char *tempcomp_sensor_file = NULL; +static char *tempcomp_point_file = NULL; +static double tempcomp_interval; +static double tempcomp_T0, tempcomp_k0, tempcomp_k1, tempcomp_k2; + +static int sched_priority = 0; +static int lock_memory = 0; + +/* Leap second handling mode */ +static REF_LeapMode leapsec_mode = REF_LeapModeSystem; + +/* Name of a system timezone containing leap seconds occuring at midnight */ +static char *leapsec_tz = NULL; + +/* Name of the user to which will be dropped root privileges. */ +static char *user; + +/* Array of CNF_HwTsInterface */ +static ARR_Instance hwts_interfaces; + +typedef struct { + NTP_Source_Type type; + int pool; + CPS_NTP_Source params; +} NTP_Source; + +/* Array of NTP_Source */ +static ARR_Instance ntp_sources; + +/* Array of RefclockParameters */ +static ARR_Instance refclock_sources; + +typedef struct _AllowDeny { + IPAddr ip; + int subnet_bits; + int all; /* 1 to override existing more specific defns */ + int allow; /* 0 for deny, 1 for allow */ +} AllowDeny; + +/* Arrays of AllowDeny */ +static ARR_Instance ntp_restrictions; +static ARR_Instance cmd_restrictions; + +typedef struct { + IPAddr addr; + unsigned short port; + int interval; +} NTP_Broadcast_Destination; + +/* Array of NTP_Broadcast_Destination */ +static ARR_Instance broadcasts; + +/* ================================================== */ + +/* The line number in the configuration file being processed */ +static int line_number; +static const char *processed_file; +static const char *processed_command; + +/* ================================================== */ + +static void +command_parse_error(void) +{ + LOG_FATAL("Could not parse %s directive at line %d%s%s", + processed_command, line_number, processed_file ? " in file " : "", + processed_file ? processed_file : ""); +} + +/* ================================================== */ + +static void +other_parse_error(const char *message) +{ + LOG_FATAL("%s at line %d%s%s", + message, line_number, processed_file ? " in file " : "", + processed_file ? processed_file : ""); +} + +/* ================================================== */ + +static int +get_number_of_args(char *line) +{ + int num = 0; + + /* The line is normalized, between arguments is just one space */ + if (*line == ' ') + line++; + if (*line) + num++; + for (; *line; line++) { + if (*line == ' ') + num++; + } + + return num; +} + +/* ================================================== */ + +static void +check_number_of_args(char *line, int num) +{ + num -= get_number_of_args(line); + + if (num) { + LOG_FATAL("%s arguments for %s directive at line %d%s%s", + num > 0 ? "Missing" : "Too many", + processed_command, line_number, processed_file ? " in file " : "", + processed_file ? processed_file : ""); + } +} + +/* ================================================== */ + +void +CNF_Initialise(int r, int client_only) +{ + restarted = r; + + hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface)); + + init_sources = ARR_CreateInstance(sizeof (IPAddr)); + ntp_sources = ARR_CreateInstance(sizeof (NTP_Source)); + 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(""); + 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); + } +} + +/* ================================================== */ + +void +CNF_Finalise(void) +{ + unsigned int i; + + for (i = 0; i < ARR_GetSize(hwts_interfaces); i++) + Free(((CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, i))->name); + ARR_DestroyInstance(hwts_interfaces); + + for (i = 0; i < ARR_GetSize(ntp_sources); i++) + Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name); + + ARR_DestroyInstance(init_sources); + ARR_DestroyInstance(ntp_sources); + ARR_DestroyInstance(refclock_sources); + ARR_DestroyInstance(broadcasts); + + ARR_DestroyInstance(ntp_restrictions); + ARR_DestroyInstance(cmd_restrictions); + + Free(drift_file); + Free(dumpdir); + Free(hwclock_file); + Free(keys_file); + Free(leapsec_tz); + Free(logdir); + Free(bind_cmd_path); + Free(ntp_signd_socket); + Free(pidfile); + Free(rtc_device); + Free(rtc_file); + Free(user); + Free(mail_user_on_change); + Free(tempcomp_sensor_file); + Free(tempcomp_point_file); +} + +/* ================================================== */ + +/* Read the configuration file */ +void +CNF_ReadFile(const char *filename) +{ + FILE *in; + char line[2048]; + int i; + + in = fopen(filename, "r"); + if (!in) { + LOG_FATAL("Could not open configuration file %s : %s", + filename, strerror(errno)); + return; + } + + DEBUG_LOG("Reading %s", filename); + + for (i = 1; fgets(line, sizeof(line), in); i++) { + CNF_ParseLine(filename, i, line); + } + + fclose(in); +} + +/* ================================================== */ + +/* Parse one configuration line */ +void +CNF_ParseLine(const char *filename, int number, char *line) +{ + char *p, *command; + + /* Set global variables used in error messages */ + processed_file = filename; + line_number = number; + + /* Remove extra white-space and comments */ + CPS_NormalizeLine(line); + + /* Skip blank lines */ + if (!*line) + return; + + /* We have a real line, now try to match commands */ + processed_command = command = line; + p = CPS_SplitWord(line); + + 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, "bindacqaddress")) { + parse_bindacqaddress(p); + } else if (!strcasecmp(command, "bindaddress")) { + parse_bindaddress(p); + } else if (!strcasecmp(command, "bindcmdaddress")) { + parse_bindcmdaddress(p); + } else if (!strcasecmp(command, "broadcast")) { + parse_broadcast(p); + } else if (!strcasecmp(command, "clientloglimit")) { + parse_clientloglimit(p); + } else if (!strcasecmp(command, "cmdallow")) { + parse_allow_deny(p, cmd_restrictions, 1); + } else if (!strcasecmp(command, "cmddeny")) { + parse_allow_deny(p, cmd_restrictions, 0); + } else if (!strcasecmp(command, "cmdport")) { + parse_int(p, &cmd_port); + } else if (!strcasecmp(command, "cmdratelimit")) { + parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval, + &cmd_ratelimit_burst, &cmd_ratelimit_leak); + } else if (!strcasecmp(command, "combinelimit")) { + parse_double(p, &combine_limit); + } 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, "dumpdir")) { + parse_string(p, &dumpdir); + } else if (!strcasecmp(command, "dumponexit")) { + /* Silently ignored */ + } else if (!strcasecmp(command, "fallbackdrift")) { + parse_fallbackdrift(p); + } else if (!strcasecmp(command, "hwclockfile")) { + parse_string(p, &hwclock_file); + } else if (!strcasecmp(command, "hwtimestamp")) { + parse_hwtimestamp(p); + } else if (!strcasecmp(command, "include")) { + parse_include(p); + } else if (!strcasecmp(command, "initstepslew")) { + parse_initstepslew(p); + } else if (!strcasecmp(command, "keyfile")) { + parse_string(p, &keys_file); + } else if (!strcasecmp(command, "leapsecmode")) { + parse_leapsecmode(p); + } else if (!strcasecmp(command, "leapsectz")) { + parse_string(p, &leapsec_tz); + } else if (!strcasecmp(command, "local")) { + parse_local(p); + } else if (!strcasecmp(command, "lock_all")) { + lock_memory = parse_null(p); + } else if (!strcasecmp(command, "log")) { + parse_log(p); + } else if (!strcasecmp(command, "logbanner")) { + parse_int(p, &log_banner); + } else if (!strcasecmp(command, "logchange")) { + parse_double(p, &log_change_threshold); + } else if (!strcasecmp(command, "logdir")) { + parse_string(p, &logdir); + } else if (!strcasecmp(command, "mailonchange")) { + parse_mailonchange(p); + } else if (!strcasecmp(command, "makestep")) { + parse_makestep(p); + } else if (!strcasecmp(command, "manual")) { + enable_manual = parse_null(p); + } else if (!strcasecmp(command, "maxchange")) { + parse_maxchange(p); + } else if (!strcasecmp(command, "maxclockerror")) { + parse_double(p, &max_clock_error); + } else if (!strcasecmp(command, "maxdistance")) { + parse_double(p, &max_distance); + } else if (!strcasecmp(command, "maxdrift")) { + parse_double(p, &max_drift); + } else if (!strcasecmp(command, "maxjitter")) { + parse_double(p, &max_jitter); + } else if (!strcasecmp(command, "maxsamples")) { + parse_int(p, &max_samples); + } else if (!strcasecmp(command, "maxslewrate")) { + parse_double(p, &max_slew_rate); + } else if (!strcasecmp(command, "maxupdateskew")) { + parse_double(p, &max_update_skew); + } else if (!strcasecmp(command, "minsamples")) { + parse_int(p, &min_samples); + } else if (!strcasecmp(command, "minsources")) { + parse_int(p, &min_sources); + } else if (!strcasecmp(command, "noclientlog")) { + no_client_log = parse_null(p); + } else if (!strcasecmp(command, "ntpsigndsocket")) { + parse_string(p, &ntp_signd_socket); + } else if (!strcasecmp(command, "peer")) { + parse_source(p, NTP_PEER, 0); + } else if (!strcasecmp(command, "pidfile")) { + parse_string(p, &pidfile); + } else if (!strcasecmp(command, "pool")) { + parse_source(p, NTP_SERVER, 1); + } else if (!strcasecmp(command, "port")) { + parse_int(p, &ntp_port); + } else if (!strcasecmp(command, "ratelimit")) { + parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval, + &ntp_ratelimit_burst, &ntp_ratelimit_leak); + } else if (!strcasecmp(command, "refclock")) { + parse_refclock(p); + } else if (!strcasecmp(command, "reselectdist")) { + parse_double(p, &reselect_distance); + } else if (!strcasecmp(command, "rtcautotrim")) { + parse_double(p, &rtc_autotrim_threshold); + } else if (!strcasecmp(command, "rtcdevice")) { + parse_string(p, &rtc_device); + } else if (!strcasecmp(command, "rtcfile")) { + parse_string(p, &rtc_file); + } else if (!strcasecmp(command, "rtconutc")) { + rtc_on_utc = parse_null(p); + } else if (!strcasecmp(command, "rtcsync")) { + rtc_sync = parse_null(p); + } else if (!strcasecmp(command, "sched_priority")) { + parse_int(p, &sched_priority); + } else if (!strcasecmp(command, "server")) { + parse_source(p, NTP_SERVER, 0); + } else if (!strcasecmp(command, "smoothtime")) { + parse_smoothtime(p); + } else if (!strcasecmp(command, "stratumweight")) { + parse_double(p, &stratum_weight); + } else if (!strcasecmp(command, "tempcomp")) { + parse_tempcomp(p); + } else if (!strcasecmp(command, "user")) { + parse_string(p, &user); + } else if (!strcasecmp(command, "commandkey") || + !strcasecmp(command, "generatecommandkey") || + !strcasecmp(command, "linux_freq_scale") || + !strcasecmp(command, "linux_hz")) { + LOG(LOGS_WARN, "%s directive is no longer supported", command); + } else { + other_parse_error("Invalid command"); + } +} + +/* ================================================== */ + +static int +parse_string(char *line, char **result) +{ + check_number_of_args(line, 1); + Free(*result); + *result = Strdup(line); + return 1; +} + +/* ================================================== */ + +static int +parse_int(char *line, int *result) +{ + check_number_of_args(line, 1); + if (sscanf(line, "%d", result) != 1) { + command_parse_error(); + return 0; + } + return 1; +} + +/* ================================================== */ + +static int +parse_double(char *line, double *result) +{ + check_number_of_args(line, 1); + if (sscanf(line, "%lf", result) != 1) { + command_parse_error(); + return 0; + } + return 1; +} + +/* ================================================== */ + +static int +parse_null(char *line) +{ + check_number_of_args(line, 0); + return 1; +} + +/* ================================================== */ + +static void +parse_source(char *line, NTP_Source_Type type, int pool) +{ + NTP_Source source; + + source.type = type; + source.pool = pool; + + if (!CPS_ParseNTPSourceAdd(line, &source.params)) { + command_parse_error(); + return; + } + + source.params.name = Strdup(source.params.name); + ARR_AppendElement(ntp_sources, &source); +} + +/* ================================================== */ + +static void +parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak) +{ + int n, val; + char *opt; + + *enabled = 1; + + while (*line) { + opt = line; + line = CPS_SplitWord(line); + if (sscanf(line, "%d%n", &val, &n) != 1) { + command_parse_error(); + return; + } + line += n; + if (!strcasecmp(opt, "interval")) + *interval = val; + else if (!strcasecmp(opt, "burst")) + *burst = val; + else if (!strcasecmp(opt, "leak")) + *leak = val; + else + command_parse_error(); + } +} + +/* ================================================== */ + +static void +parse_refclock(char *line) +{ + int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options; + int max_lock_age, pps_forced, stratum, tai; + uint32_t ref_id, lock_ref_id; + double offset, delay, precision, max_dispersion, pulse_width; + char *p, *cmd, *name, *param; + unsigned char ref[5]; + RefclockParameters *refclock; + + poll = 4; + dpoll = 0; + filter_length = 64; + pps_forced = 0; + pps_rate = 0; + min_samples = SRC_DEFAULT_MINSAMPLES; + max_samples = SRC_DEFAULT_MAXSAMPLES; + sel_options = 0; + offset = 0.0; + delay = 1e-9; + precision = 0.0; + max_dispersion = 0.0; + pulse_width = 0.0; + ref_id = 0; + max_lock_age = 2; + lock_ref_id = 0; + stratum = 0; + tai = 0; + + if (!*line) { + command_parse_error(); + return; + } + + p = line; + line = CPS_SplitWord(line); + + if (!*line) { + command_parse_error(); + return; + } + + name = Strdup(p); + + p = line; + line = CPS_SplitWord(line); + param = Strdup(p); + + for (cmd = line; *cmd; line += n, cmd = line) { + line = CPS_SplitWord(line); + + if (!strcasecmp(cmd, "refid")) { + if (sscanf(line, "%4s%n", (char *)ref, &n) != 1) + break; + ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3]; + } else if (!strcasecmp(cmd, "lock")) { + if (sscanf(line, "%4s%n", (char *)ref, &n) != 1) + break; + lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3]; + } else if (!strcasecmp(cmd, "poll")) { + if (sscanf(line, "%d%n", &poll, &n) != 1) { + break; + } + } else if (!strcasecmp(cmd, "dpoll")) { + if (sscanf(line, "%d%n", &dpoll, &n) != 1) { + break; + } + } else if (!strcasecmp(cmd, "filter")) { + if (sscanf(line, "%d%n", &filter_length, &n) != 1) { + break; + } + } else if (!strcasecmp(cmd, "rate")) { + if (sscanf(line, "%d%n", &pps_rate, &n) != 1) + break; + } else if (!strcasecmp(cmd, "minsamples")) { + if (sscanf(line, "%d%n", &min_samples, &n) != 1) + break; + } else if (!strcasecmp(cmd, "maxlockage")) { + if (sscanf(line, "%d%n", &max_lock_age, &n) != 1) + break; + } else if (!strcasecmp(cmd, "maxsamples")) { + if (sscanf(line, "%d%n", &max_samples, &n) != 1) + break; + } else if (!strcasecmp(cmd, "offset")) { + if (sscanf(line, "%lf%n", &offset, &n) != 1) + break; + } else if (!strcasecmp(cmd, "delay")) { + if (sscanf(line, "%lf%n", &delay, &n) != 1) + break; + } else if (!strcasecmp(cmd, "pps")) { + n = 0; + pps_forced = 1; + } else if (!strcasecmp(cmd, "precision")) { + if (sscanf(line, "%lf%n", &precision, &n) != 1) + break; + } else if (!strcasecmp(cmd, "maxdispersion")) { + if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1) + break; + } else if (!strcasecmp(cmd, "stratum")) { + if (sscanf(line, "%d%n", &stratum, &n) != 1 || + stratum >= NTP_MAX_STRATUM || stratum < 0) + break; + } else if (!strcasecmp(cmd, "tai")) { + n = 0; + tai = 1; + } else if (!strcasecmp(cmd, "width")) { + if (sscanf(line, "%lf%n", &pulse_width, &n) != 1) + break; + } else if (!strcasecmp(cmd, "noselect")) { + n = 0; + sel_options |= SRC_SELECT_NOSELECT; + } else if (!strcasecmp(cmd, "prefer")) { + n = 0; + sel_options |= SRC_SELECT_PREFER; + } else if (!strcasecmp(cmd, "trust")) { + n = 0; + sel_options |= SRC_SELECT_TRUST; + } else if (!strcasecmp(cmd, "require")) { + n = 0; + sel_options |= SRC_SELECT_REQUIRE; + } else { + other_parse_error("Invalid refclock option"); + return; + } + } + + if (*cmd) { + command_parse_error(); + return; + } + + refclock = (RefclockParameters *)ARR_GetNewElement(refclock_sources); + refclock->driver_name = name; + refclock->driver_parameter = param; + refclock->driver_poll = dpoll; + refclock->poll = poll; + refclock->filter_length = filter_length; + refclock->pps_forced = pps_forced; + refclock->pps_rate = pps_rate; + refclock->min_samples = min_samples; + refclock->max_samples = max_samples; + refclock->sel_options = sel_options; + refclock->stratum = stratum; + refclock->tai = tai; + refclock->offset = offset; + refclock->delay = delay; + refclock->precision = precision; + refclock->max_dispersion = max_dispersion; + refclock->pulse_width = pulse_width; + refclock->ref_id = ref_id; + refclock->max_lock_age = max_lock_age; + refclock->lock_ref_id = lock_ref_id; +} + +/* ================================================== */ + +static void +parse_log(char *line) +{ + char *log_name; + do { + log_name = line; + line = CPS_SplitWord(line); + if (*log_name) { + if (!strcmp(log_name, "rawmeasurements")) { + do_log_measurements = 1; + raw_measurements = 1; + } else if (!strcmp(log_name, "measurements")) { + do_log_measurements = 1; + } else if (!strcmp(log_name, "statistics")) { + do_log_statistics = 1; + } else if (!strcmp(log_name, "tracking")) { + do_log_tracking = 1; + } else if (!strcmp(log_name, "rtc")) { + do_log_rtc = 1; + } else if (!strcmp(log_name, "refclocks")) { + do_log_refclocks = 1; + } else if (!strcmp(log_name, "tempcomp")) { + do_log_tempcomp = 1; + } else { + other_parse_error("Invalid log parameter"); + break; + } + } else { + break; + } + } while (1); +} + +/* ================================================== */ + +static void +parse_local(char *line) +{ + if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance)) + command_parse_error(); + enable_local = 1; +} + +/* ================================================== */ + +static void +parse_initstepslew(char *line) +{ + char *p, *hostname; + IPAddr ip_addr; + + /* Ignore the line if chronyd was started with -R. */ + if (restarted) { + return; + } + + ARR_SetSize(init_sources, 0); + p = CPS_SplitWord(line); + + if (sscanf(line, "%lf", &init_slew_threshold) != 1) { + command_parse_error(); + return; + } + + while (*p) { + hostname = p; + p = CPS_SplitWord(p); + if (*hostname) { + if (DNS_Name2IPAddress(hostname, &ip_addr, 1) == DNS_Success) { + ARR_AppendElement(init_sources, &ip_addr); + } else { + LOG(LOGS_WARN, "Could not resolve address of initstepslew server %s", hostname); + } + } + } +} + +/* ================================================== */ + +static void +parse_leapsecmode(char *line) +{ + if (!strcasecmp(line, "system")) + leapsec_mode = REF_LeapModeSystem; + else if (!strcasecmp(line, "slew")) + leapsec_mode = REF_LeapModeSlew; + else if (!strcasecmp(line, "step")) + leapsec_mode = REF_LeapModeStep; + else if (!strcasecmp(line, "ignore")) + leapsec_mode = REF_LeapModeIgnore; + else + command_parse_error(); +} + +/* ================================================== */ + +static void +parse_clientloglimit(char *line) +{ + check_number_of_args(line, 1); + if (sscanf(line, "%lu", &client_log_limit) != 1) { + command_parse_error(); + } +} + +/* ================================================== */ + +static void +parse_fallbackdrift(char *line) +{ + check_number_of_args(line, 2); + if (sscanf(line, "%d %d", &fb_drift_min, &fb_drift_max) != 2) { + command_parse_error(); + } +} + +/* ================================================== */ + +static void +parse_makestep(char *line) +{ + check_number_of_args(line, 2); + if (sscanf(line, "%lf %d", &make_step_threshold, &make_step_limit) != 2) { + make_step_limit = 0; + command_parse_error(); + } + + /* Disable limited makestep if chronyd was started with -R. */ + if (restarted && make_step_limit > 0) { + make_step_limit = 0; + } +} + +/* ================================================== */ + +static void +parse_maxchange(char *line) +{ + check_number_of_args(line, 3); + if (sscanf(line, "%lf %d %d", &max_offset, &max_offset_delay, &max_offset_ignore) != 3) { + max_offset_delay = -1; + command_parse_error(); + } +} + +/* ================================================== */ + +static void +parse_mailonchange(char *line) +{ + char *address; + check_number_of_args(line, 2); + address = line; + line = CPS_SplitWord(line); + Free(mail_user_on_change); + if (sscanf(line, "%lf", &mail_change_threshold) == 1) { + mail_user_on_change = Strdup(address); + } else { + mail_user_on_change = NULL; + command_parse_error(); + } +} + +/* ================================================== */ + +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; + + p = line; + + 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(); + } + } + } +} + +/* ================================================== */ + +static void +parse_bindacqaddress(char *line) +{ + IPAddr ip; + + check_number_of_args(line, 1); + if (UTI_StringToIP(line, &ip)) { + if (ip.family == IPADDR_INET4) + bind_acq_address4 = ip; + else if (ip.family == IPADDR_INET6) + bind_acq_address6 = ip; + } else { + command_parse_error(); + } +} + +/* ================================================== */ + +static void +parse_bindaddress(char *line) +{ + IPAddr ip; + + check_number_of_args(line, 1); + if (UTI_StringToIP(line, &ip)) { + if (ip.family == IPADDR_INET4) + bind_address4 = ip; + else if (ip.family == IPADDR_INET6) + bind_address6 = ip; + } else { + command_parse_error(); + } +} + +/* ================================================== */ + +static void +parse_bindcmdaddress(char *line) +{ + IPAddr ip; + + check_number_of_args(line, 1); + + /* Address starting with / is for the Unix domain socket */ + if (line[0] == '/') { + parse_string(line, &bind_cmd_path); + /* / disables the socket */ + if (!strcmp(bind_cmd_path, "/")) + bind_cmd_path[0] = '\0'; + } else if (UTI_StringToIP(line, &ip)) { + if (ip.family == IPADDR_INET4) + bind_cmd_address4 = ip; + else if (ip.family == IPADDR_INET6) + bind_cmd_address6 = ip; + } else { + command_parse_error(); + } +} + +/* ================================================== */ + +static void +parse_broadcast(char *line) +{ + /* Syntax : broadcast <interval> <broadcast-IP-addr> [<port>] */ + NTP_Broadcast_Destination *destination; + int port; + int interval; + char *p; + IPAddr ip; + + p = line; + line = CPS_SplitWord(line); + + if (sscanf(p, "%d", &interval) != 1) { + command_parse_error(); + return; + } + + p = line; + line = CPS_SplitWord(line); + + if (!UTI_StringToIP(p, &ip)) { + command_parse_error(); + return; + } + + p = line; + line = CPS_SplitWord(line); + + if (*p) { + if (sscanf(p, "%d", &port) != 1 || *line) { + command_parse_error(); + return; + } + } else { + /* default port */ + port = NTP_PORT; + } + + destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts); + destination->addr = ip; + destination->port = port; + destination->interval = interval; +} + +/* ================================================== */ + +static void +parse_smoothtime(char *line) +{ + if (get_number_of_args(line) != 3) + check_number_of_args(line, 2); + + if (sscanf(line, "%lf %lf", &smooth_max_freq, &smooth_max_wander) != 2) { + smooth_max_freq = 0.0; + command_parse_error(); + } + + line = CPS_SplitWord(CPS_SplitWord(line)); + smooth_leap_only = 0; + + if (*line) { + if (!strcasecmp(line, "leaponly")) + smooth_leap_only = 1; + else + command_parse_error(); + } +} + +/* ================================================== */ +static void +parse_tempcomp(char *line) +{ + char *p; + int point_form; + + point_form = get_number_of_args(line) == 3; + + if (!point_form) + check_number_of_args(line, 6); + + p = line; + line = CPS_SplitWord(line); + + if (!*p) { + command_parse_error(); + return; + } + + Free(tempcomp_point_file); + + if (point_form) { + if (sscanf(line, "%lf", &tempcomp_interval) != 1) { + command_parse_error(); + return; + } + tempcomp_point_file = Strdup(CPS_SplitWord(line)); + } else { + if (sscanf(line, "%lf %lf %lf %lf %lf", &tempcomp_interval, + &tempcomp_T0, &tempcomp_k0, &tempcomp_k1, &tempcomp_k2) != 5) { + command_parse_error(); + return; + } + tempcomp_point_file = NULL; + } + + Free(tempcomp_sensor_file); + tempcomp_sensor_file = Strdup(p); +} + +/* ================================================== */ + +static void +parse_hwtimestamp(char *line) +{ + CNF_HwTsInterface *iface; + char *p, filter[5]; + int n; + + if (!*line) { + command_parse_error(); + return; + } + + p = line; + line = CPS_SplitWord(line); + + 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; + iface->tx_comp = 0.0; + iface->rx_comp = 0.0; + + for (p = line; *p; line += n, p = line) { + line = CPS_SplitWord(line); + + 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; + } else if (!strcasecmp(p, "rxcomp")) { + if (sscanf(line, "%lf%n", &iface->rx_comp, &n) != 1) + break; + } else if (!strcasecmp(p, "txcomp")) { + if (sscanf(line, "%lf%n", &iface->tx_comp, &n) != 1) + break; + } else if (!strcasecmp(p, "rxfilter")) { + if (sscanf(line, "%4s%n", filter, &n) != 1) + break; + if (!strcasecmp(filter, "none")) + iface->rxfilter = CNF_HWTS_RXFILTER_NONE; + else if (!strcasecmp(filter, "ntp")) + iface->rxfilter = CNF_HWTS_RXFILTER_NTP; + else if (!strcasecmp(filter, "all")) + iface->rxfilter = CNF_HWTS_RXFILTER_ALL; + else + break; + } else if (!strcasecmp(p, "nocrossts")) { + n = 0; + iface->nocrossts = 1; + } else { + break; + } + } + + if (*p) + command_parse_error(); +} + +/* ================================================== */ + +static void +parse_include(char *line) +{ + glob_t gl; + size_t i; + int r; + + check_number_of_args(line, 1); + + if ((r = glob(line, +#ifdef GLOB_NOMAGIC + GLOB_NOMAGIC | +#endif + GLOB_ERR, NULL, &gl)) != 0) { + if (r != GLOB_NOMATCH) + LOG_FATAL("Could not search for files matching %s", line); + + DEBUG_LOG("glob of %s failed", line); + return; + } + + for (i = 0; i < gl.gl_pathc; i++) + CNF_ReadFile(gl.gl_pathv[i]); + + globfree(&gl); +} + +/* ================================================== */ + +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]) { + dir = UTI_PathToDir(bind_cmd_path); + UTI_CreateDirAndParents(dir, 0770, uid, gid); + + /* Check the permissions and owner/group in case the directory already + existed. It MUST NOT be accessible by others as permissions on Unix + 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(dir); + } + + if (logdir[0]) + UTI_CreateDirAndParents(logdir, 0755, uid, gid); + if (dumpdir[0]) + UTI_CreateDirAndParents(dumpdir, 0755, uid, gid); +} + +/* ================================================== */ + +void +CNF_AddInitSources(void) +{ + CPS_NTP_Source cps_source; + NTP_Remote_Address ntp_addr; + char dummy_hostname[2] = "H"; + unsigned int i; + + for (i = 0; i < ARR_GetSize(init_sources); i++) { + /* Get the default NTP params */ + CPS_ParseNTPSourceAdd(dummy_hostname, &cps_source); + + /* Add the address as an offline iburst server */ + ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i); + ntp_addr.port = cps_source.port; + cps_source.params.iburst = 1; + cps_source.params.connectivity = SRC_OFFLINE; + + NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params); + } + + ARR_SetSize(init_sources, 0); +} + +/* ================================================== */ + +void +CNF_AddSources(void) +{ + NTP_Source *source; + unsigned int i; + + 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); + Free(source->params.name); + } + + ARR_SetSize(ntp_sources, 0); +} + +/* ================================================== */ + +void +CNF_AddRefclocks(void) +{ + unsigned int i; + + for (i = 0; i < ARR_GetSize(refclock_sources); i++) { + RCL_AddRefclock((RefclockParameters *)ARR_GetElement(refclock_sources, i)); + } + + ARR_SetSize(refclock_sources, 0); +} + +/* ================================================== */ + +void +CNF_AddBroadcasts(void) +{ + unsigned int i; + NTP_Broadcast_Destination *destination; + + for (i = 0; i < ARR_GetSize(broadcasts); i++) { + destination = (NTP_Broadcast_Destination *)ARR_GetElement(broadcasts, i); + NCR_AddBroadcastDestination(&destination->addr, destination->port, + destination->interval); + } + + ARR_SetSize(broadcasts, 0); +} + +/* ================================================== */ + +int +CNF_GetNTPPort(void) +{ + return ntp_port; +} + +/* ================================================== */ + +int +CNF_GetAcquisitionPort(void) +{ + return acquisition_port; +} + +/* ================================================== */ + +char * +CNF_GetDriftFile(void) +{ + return drift_file; +} + +/* ================================================== */ + +int +CNF_GetLogBanner(void) +{ + return log_banner; +} + +/* ================================================== */ + +char * +CNF_GetLogDir(void) +{ + return logdir; +} + +/* ================================================== */ + +char * +CNF_GetDumpDir(void) +{ + return dumpdir; +} + +/* ================================================== */ + +int +CNF_GetLogMeasurements(int *raw) +{ + *raw = raw_measurements; + return do_log_measurements; +} + +/* ================================================== */ + +int +CNF_GetLogStatistics(void) +{ + return do_log_statistics; +} + +/* ================================================== */ + +int +CNF_GetLogTracking(void) +{ + return do_log_tracking; +} + +/* ================================================== */ + +int +CNF_GetLogRtc(void) +{ + return do_log_rtc; +} + +/* ================================================== */ + +int +CNF_GetLogRefclocks(void) +{ + return do_log_refclocks; +} + +/* ================================================== */ + +int +CNF_GetLogTempComp(void) +{ + return do_log_tempcomp; +} + +/* ================================================== */ + +char * +CNF_GetKeysFile(void) +{ + return keys_file; +} + +/* ================================================== */ + +double +CNF_GetRtcAutotrim(void) +{ + return rtc_autotrim_threshold; +} + +/* ================================================== */ + +char * +CNF_GetRtcFile(void) +{ + return rtc_file; +} + +/* ================================================== */ + +char * +CNF_GetRtcDevice(void) +{ + return rtc_device; +} + +/* ================================================== */ + +double +CNF_GetMaxUpdateSkew(void) +{ + return max_update_skew; +} + +/* ================================================== */ + +double +CNF_GetMaxDrift(void) +{ + return max_drift; +} + +/* ================================================== */ + +double +CNF_GetMaxClockError(void) +{ + return max_clock_error; +} + +/* ================================================== */ + +double +CNF_GetCorrectionTimeRatio(void) +{ + return correction_time_ratio; +} + +/* ================================================== */ + +double +CNF_GetMaxSlewRate(void) +{ + return max_slew_rate; +} + +/* ================================================== */ + +double +CNF_GetMaxDistance(void) +{ + return max_distance; +} + +/* ================================================== */ + +double +CNF_GetMaxJitter(void) +{ + return max_jitter; +} + +/* ================================================== */ + +double +CNF_GetReselectDistance(void) +{ + return reselect_distance; +} + +/* ================================================== */ + +double +CNF_GetStratumWeight(void) +{ + return stratum_weight; +} + +/* ================================================== */ + +double +CNF_GetCombineLimit(void) +{ + return combine_limit; +} + +/* ================================================== */ + +int +CNF_GetManualEnabled(void) +{ + return enable_manual; +} + +/* ================================================== */ + +int +CNF_GetCommandPort(void) { + return cmd_port; +} + +/* ================================================== */ + +int +CNF_AllowLocalReference(int *stratum, int *orphan, double *distance) +{ + if (enable_local) { + *stratum = local_stratum; + *orphan = local_orphan; + *distance = local_distance; + return 1; + } else { + return 0; + } +} + +/* ================================================== */ + +int +CNF_GetRtcOnUtc(void) +{ + return rtc_on_utc; +} + +/* ================================================== */ + +int +CNF_GetRtcSync(void) +{ + return rtc_sync; +} + +/* ================================================== */ + +void +CNF_GetMakeStep(int *limit, double *threshold) +{ + *limit = make_step_limit; + *threshold = make_step_threshold; +} + +/* ================================================== */ + +void +CNF_GetMaxChange(int *delay, int *ignore, double *offset) +{ + *delay = max_offset_delay; + *ignore = max_offset_ignore; + *offset = max_offset; +} + +/* ================================================== */ + +double +CNF_GetLogChange(void) +{ + return log_change_threshold; +} + +/* ================================================== */ + +void +CNF_GetMailOnChange(int *enabled, double *threshold, char **user) +{ + if (mail_user_on_change) { + *enabled = 1; + *threshold = mail_change_threshold; + *user = mail_user_on_change; + } else { + *enabled = 0; + *threshold = 0.0; + *user = NULL; + } +} + +/* ================================================== */ + +void +CNF_SetupAccessRestrictions(void) +{ + AllowDeny *node; + int status; + unsigned int i; + + for (i = 0; i < ARR_GetSize(ntp_restrictions); i++) { + node = ARR_GetElement(ntp_restrictions, i); + status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); + if (!status) { + LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits); + } + } + + for (i = 0; i < ARR_GetSize(cmd_restrictions); i++) { + node = ARR_GetElement(cmd_restrictions, i); + status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); + if (!status) { + LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits); + } + } + + ARR_SetSize(ntp_restrictions, 0); + ARR_SetSize(cmd_restrictions, 0); +} + +/* ================================================== */ + +int +CNF_GetNoClientLog(void) +{ + return no_client_log; +} + +/* ================================================== */ + +unsigned long +CNF_GetClientLogLimit(void) +{ + return client_log_limit; +} + +/* ================================================== */ + +void +CNF_GetFallbackDrifts(int *min, int *max) +{ + *min = fb_drift_min; + *max = fb_drift_max; +} + +/* ================================================== */ + +void +CNF_GetBindAddress(int family, IPAddr *addr) +{ + if (family == IPADDR_INET4) + *addr = bind_address4; + else if (family == IPADDR_INET6) + *addr = bind_address6; + else + addr->family = IPADDR_UNSPEC; +} + +/* ================================================== */ + +void +CNF_GetBindAcquisitionAddress(int family, IPAddr *addr) +{ + if (family == IPADDR_INET4) + *addr = bind_acq_address4; + else if (family == IPADDR_INET6) + *addr = bind_acq_address6; + else + addr->family = IPADDR_UNSPEC; +} + +/* ================================================== */ + +char * +CNF_GetBindCommandPath(void) +{ + return bind_cmd_path; +} + +/* ================================================== */ + +void +CNF_GetBindCommandAddress(int family, IPAddr *addr) +{ + if (family == IPADDR_INET4) + *addr = bind_cmd_address4; + else if (family == IPADDR_INET6) + *addr = bind_cmd_address6; + else + addr->family = IPADDR_UNSPEC; +} + +/* ================================================== */ + +char * +CNF_GetNtpSigndSocket(void) +{ + return ntp_signd_socket; +} + +/* ================================================== */ + +char * +CNF_GetPidFile(void) +{ + return pidfile; +} + +/* ================================================== */ + +REF_LeapMode +CNF_GetLeapSecMode(void) +{ + return leapsec_mode; +} + +/* ================================================== */ + +char * +CNF_GetLeapSecTimezone(void) +{ + return leapsec_tz; +} + +/* ================================================== */ + +int +CNF_GetSchedPriority(void) +{ + return sched_priority; +} + +/* ================================================== */ + +int +CNF_GetLockMemory(void) +{ + return lock_memory; +} + +/* ================================================== */ + +int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak) +{ + *interval = ntp_ratelimit_interval; + *burst = ntp_ratelimit_burst; + *leak = ntp_ratelimit_leak; + return ntp_ratelimit_enabled; +} + +/* ================================================== */ + +int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak) +{ + *interval = cmd_ratelimit_interval; + *burst = cmd_ratelimit_burst; + *leak = cmd_ratelimit_leak; + return cmd_ratelimit_enabled; +} + +/* ================================================== */ + +void +CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only) +{ + *max_freq = smooth_max_freq; + *max_wander = smooth_max_wander; + *leap_only = smooth_leap_only; +} + +/* ================================================== */ + +void +CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2) +{ + *file = tempcomp_sensor_file; + *point_file = tempcomp_point_file; + *interval = tempcomp_interval; + *T0 = tempcomp_T0; + *k0 = tempcomp_k0; + *k1 = tempcomp_k1; + *k2 = tempcomp_k2; +} + +/* ================================================== */ + +char * +CNF_GetUser(void) +{ + return user; +} + +/* ================================================== */ + +int +CNF_GetMaxSamples(void) +{ + return max_samples; +} + +/* ================================================== */ + +int +CNF_GetMinSamples(void) +{ + return min_samples; +} + +/* ================================================== */ + +int +CNF_GetMinSources(void) +{ + return min_sources; +} + +/* ================================================== */ + +char * +CNF_GetHwclockFile(void) +{ + return hwclock_file; +} + +/* ================================================== */ + +int +CNF_GetInitSources(void) +{ + return ARR_GetSize(init_sources); +} + +/* ================================================== */ + +double +CNF_GetInitStepThreshold(void) +{ + return init_slew_threshold; +} + +/* ================================================== */ + +int +CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface) +{ + if (index >= ARR_GetSize(hwts_interfaces)) + return 0; + + *iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index); + return 1; +} @@ -0,0 +1,142 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Miroslav Lichvar 2013-2014 + * + * 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 configuration module + */ + +#ifndef GOT_CONF_H +#define GOT_CONF_H + +#include "addressing.h" +#include "reference.h" + +extern void CNF_Initialise(int restarted, int client_only); +extern void CNF_Finalise(void); + +extern char *CNF_GetRtcDevice(void); + +extern void CNF_ReadFile(const char *filename); +extern void CNF_ParseLine(const char *filename, int number, char *line); + +extern void CNF_CreateDirs(uid_t uid, gid_t gid); + +extern void CNF_AddInitSources(void); +extern void CNF_AddSources(void); +extern void CNF_AddBroadcasts(void); +extern void CNF_AddRefclocks(void); + +extern int CNF_GetAcquisitionPort(void); +extern int CNF_GetNTPPort(void); +extern char *CNF_GetDriftFile(void); +extern char *CNF_GetLogDir(void); +extern char *CNF_GetDumpDir(void); +extern int CNF_GetLogBanner(void); +extern int CNF_GetLogMeasurements(int *raw); +extern int CNF_GetLogStatistics(void); +extern int CNF_GetLogTracking(void); +extern int CNF_GetLogRtc(void); +extern int CNF_GetLogRefclocks(void); +extern int CNF_GetLogTempComp(void); +extern char *CNF_GetKeysFile(void); +extern char *CNF_GetRtcFile(void); +extern int CNF_GetManualEnabled(void); +extern int CNF_GetCommandPort(void); +extern int CNF_GetRtcOnUtc(void); +extern int CNF_GetRtcSync(void); +extern void CNF_GetMakeStep(int *limit, double *threshold); +extern void CNF_GetMaxChange(int *delay, int *ignore, double *offset); +extern double CNF_GetLogChange(void); +extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user); +extern int CNF_GetNoClientLog(void); +extern unsigned long CNF_GetClientLogLimit(void); +extern void CNF_GetFallbackDrifts(int *min, int *max); +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_GetBindCommandPath(void); +extern char *CNF_GetNtpSigndSocket(void); +extern char *CNF_GetPidFile(void); +extern REF_LeapMode CNF_GetLeapSecMode(void); +extern char *CNF_GetLeapSecTimezone(void); + +/* Value returned in ppm, as read from file */ +extern double CNF_GetMaxUpdateSkew(void); +extern double CNF_GetMaxClockError(void); +extern double CNF_GetMaxDrift(void); +extern double CNF_GetCorrectionTimeRatio(void); +extern double CNF_GetMaxSlewRate(void); + +extern double CNF_GetMaxDistance(void); +extern double CNF_GetMaxJitter(void); +extern double CNF_GetReselectDistance(void); +extern double CNF_GetStratumWeight(void); +extern double CNF_GetCombineLimit(void); + +extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance); + +extern void CNF_SetupAccessRestrictions(void); + +extern int CNF_GetSchedPriority(void); +extern int CNF_GetLockMemory(void); + +extern int CNF_GetNTPRateLimit(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); + +extern char *CNF_GetUser(void); + +extern int CNF_GetMaxSamples(void); +extern int CNF_GetMinSamples(void); + +extern int CNF_GetMinSources(void); + +extern double CNF_GetRtcAutotrim(void); +extern char *CNF_GetHwclockFile(void); + +extern int CNF_GetInitSources(void); +extern double CNF_GetInitStepThreshold(void); + +typedef enum { + CNF_HWTS_RXFILTER_ANY, + CNF_HWTS_RXFILTER_NONE, + CNF_HWTS_RXFILTER_NTP, + 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; + double tx_comp; + double rx_comp; +} CNF_HwTsInterface; + +extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface); + +#endif /* GOT_CONF_H */ diff --git a/configure b/configure new file mode 100755 index 0000000..486b0bc --- /dev/null +++ b/configure @@ -0,0 +1,1007 @@ +#!/bin/sh +# ======================================================================= +# +# chronyd/chronyc - Programs for keeping computer clocks accurate. +# +# Copyright (C) Richard P. Curnow 1997-2003 +# Copyright (C) Bryan Christianson 2016 +# Copyright (C) Miroslav Lichvar 2009, 2012-2018 +# +# ======================================================================= + +# This configure script determines the operating system type and version + +# ====================================================================== +# FUNCTIONS + +#{{{ test_code +test_code () { + name=$1 + headers=$2 + cflags=$3 + ldflags=$4 + code=$5 + + printf "%s" "Checking for $name : " + + ( + echo "#include \"config.h\"" + for h in $headers; do + echo "#include <$h>" + done + echo "int main(int argc, char **argv) {" + echo "$code" + echo "return 0; }" + ) > docheck.c + + echo "docheck.c:" >> config.log + cat docheck.c >> config.log + echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ + $MYLDFLAGS >> config.log + $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ + $MYLDFLAGS >> config.log 2>&1 + + if [ $? -eq 0 ] + then + echo "Yes" + result=0 + else + echo "No" + result=1 + fi + rm -f docheck.c docheck + echo >> config.log + return $result +} +#}}} +#{{{ usage +usage () { + cat <<EOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: ./configure [OPTION]... + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [/usr/local] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`/usr/local/bin', \`/usr/local/lib' etc. You can specify +an installation prefix other than \`/usr/local' using \`--prefix', +for instance \`--prefix=$HOME'. + +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-cmdmon Disable command and monitoring support + --disable-ntp Disable NTP support + --disable-refclock Disable reference clock support + --disable-phc Disable PHC refclock driver + --disable-pps Disable PPS refclock driver + --disable-ipv6 Disable IPv6 support + --disable-rtc Don't include RTC even on Linux + --disable-privdrop Disable support for dropping root privileges + --without-libcap Don't use libcap even if it is available + --enable-scfilter Enable support for system call filtering + --without-seccomp Don't use seccomp even if it is available + --disable-asyncdns Disable asynchronous name resolving + --disable-forcednsretry Don't retry on permanent DNS error + --without-clock-gettime Don't use clock_gettime() even if it is available + --disable-timestamping Disable support for SW/HW timestamping + --enable-ntp-signd Enable support for MS-SNTP authentication in Samba + --with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds + 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/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 + +Fine tuning of the installation directories: + --sysconfdir=DIR chrony.conf location [/etc] + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --datarootdir=DIR data root [PREFIX/share] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/chrony] + --localstatedir=DIR modifiable single-machine data [/var] + --chronyrundir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony] + --chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony] + +Overriding system detection when cross-compiling: + --host-system=OS Specify system name (uname -s) + --host-release=REL Specify system release (uname -r) + --host-machine=CPU Specify machine (uname -m) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + CPPFLAGS C preprocessor flags, e.g. -I<include dir> if you have + 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> + +Use these variables to override the choices made by \`configure' or to help +it to find libraries and programs with nonstandard names/locations. + +EOF + +} +#}}} +#{{{ +add_def () { + if [ "x$2" = "x" ]; then + echo "#define $1 1" >> config.h + else + echo "#define $1 $2" >> config.h + 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 + for f; do + if [ "$ff" = "0" ]; then + printf " " + fi + if grep "define FEAT_$f" config.h > /dev/null; then + printf "%s" "+$f" + else + printf "%s" "-$f" + fi + ff=0 + done +} +#}}} + +# ====================================================================== + + + +OPERATINGSYSTEM=`uname -s` +VERSION=`uname -r` +MACHINE=`uname -m` + +EXTRA_LIBS="" +EXTRA_CLI_LIBS="" +EXTRA_OBJECTS="" +EXTRA_DEFS="" +SYSDEFS="" + +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_rtc=1 +try_rtc=0 +feat_droproot=1 +try_libcap=-1 +try_clockctl=0 +feat_scfilter=0 +try_seccomp=-1 +priv_ops="" +readline_lib="" +readline_inc="" +ncurses_lib="" +feat_ipv6=1 +feat_phc=1 +try_phc=0 +feat_pps=1 +try_setsched=0 +try_lockmem=0 +feat_asyncdns=1 +feat_forcednsretry=1 +try_clock_gettime=1 +try_recvmmsg=1 +feat_timestamping=1 +try_timestamping=0 +feat_ntp_signd=0 +ntp_era_split="" +default_user="root" +default_hwclockfile="" +default_pidfile="/var/run/chrony/chronyd.pid" +default_rtcdevice="/dev/rtc" +mail_program="/usr/lib/sendmail" + +for option +do + case "$option" in + --enable-debug ) + feat_debug=1 + ;; + --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/[^=]*=//;'` + ;; + --exec-prefix=* ) + SETEPREFIX=`echo $option | sed -e 's/[^=]*=//;'` + ;; + --sysconfdir=* ) + SETSYSCONFDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --bindir=* ) + SETBINDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --sbindir=* ) + SETSBINDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --datarootdir=* ) + SETDATAROOTDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --mandir=* ) + SETMANDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --docdir=* ) + SETDOCDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --localstatedir=* ) + SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --chronyrundir=* | --chronysockdir=* ) + SETCHRONYRUNDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --chronyvardir=* ) + SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'` + ;; + --disable-cmdmon) + feat_cmdmon=0 + ;; + --disable-ntp) + feat_ntp=0 + ;; + --disable-refclock) + feat_refclock=0 + ;; + --disable-rtc) + feat_rtc=0 + ;; + --disable-ipv6) + feat_ipv6=0 + ;; + --disable-phc) + feat_phc=0 + ;; + --disable-pps) + feat_pps=0 + ;; + --disable-privdrop) + feat_droproot=0 + ;; + --without-libcap|--disable-linuxcaps) + try_libcap=0 + ;; + --enable-scfilter) + feat_scfilter=1 + ;; + --disable-scfilter) + feat_scfilter=0 + ;; + --without-seccomp) + try_seccomp=0 + ;; + --disable-asyncdns) + feat_asyncdns=0 + ;; + --disable-forcednsretry) + feat_forcednsretry=0 + ;; + --without-clock-gettime) + try_clock_gettime=0 + ;; + --disable-timestamping) + feat_timestamping=0 + ;; + --enable-ntp-signd) + feat_ntp_signd=1 + ;; + --with-ntp-era=* ) + ntp_era_split=`echo $option | sed -e 's/^.*=//;'` + ;; + --with-user=* ) + default_user=`echo $option | sed -e 's/^.*=//;'` + ;; + --with-hwclockfile=* ) + default_hwclockfile=`echo $option | sed -e 's/^.*=//;'` + ;; + --with-pidfile=* ) + default_pidfile=`echo $option | sed -e 's/^.*=//;'` + ;; + --with-rtcdevice=* ) + default_rtcdevice=`echo $option | sed -e 's/^.*=//;'` + ;; + --with-sendmail=* ) + mail_program=`echo $option | sed -e 's/^.*=//;'` + ;; + --disable-sechash ) + feat_sechash=0 + ;; + --without-nettle ) + try_nettle=0 + ;; + --without-nss ) + try_nss=0 + ;; + --without-tomcrypt ) + try_tomcrypt=0 + ;; + --host-system=* ) + OPERATINGSYSTEM=`echo $option | sed -e 's/^.*=//;'` + ;; + --host-release=* ) + VERSION=`echo $option | sed -e 's/^.*=//;'` + ;; + --host-machine=* ) + MACHINE=`echo $option | sed -e 's/^.*=//;'` + ;; + --help | -h ) + usage + exit 0 + ;; + * ) + echo "Unrecognized option : " $option + esac +done + +rm -f config.h config.log + +SYSTEM=${OPERATINGSYSTEM}-${MACHINE} + +case $OPERATINGSYSTEM in + Linux) + EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o" + [ $try_libcap != "0" ] && try_libcap=1 + try_rtc=1 + [ $try_seccomp != "0" ] && try_seccomp=1 + try_timestamping=1 + try_setsched=1 + try_lockmem=1 + try_phc=1 + add_def LINUX + echo "Configuring for " $SYSTEM + ;; + FreeBSD) + # 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" + add_def FREEBSD + if [ $feat_droproot = "1" ]; then + add_def FEAT_PRIVDROP + priv_ops="ADJUSTTIME ADJUSTTIMEX SETTIME BINDSOCKET" + fi + echo "Configuring for $SYSTEM" + ;; + NetBSD) + EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o" + try_clockctl=1 + add_def NETBSD + echo "Configuring for $SYSTEM" + ;; + Darwin) + EXTRA_OBJECTS="sys_macosx.o" + EXTRA_LIBS="-lresolv" + EXTRA_CLI_LIBS="-lresolv" + add_def MACOSX + if [ $feat_droproot = "1" ]; then + add_def FEAT_PRIVDROP + priv_ops="ADJUSTTIME SETTIME BINDSOCKET" + fi + major=`echo $VERSION | cut -d. -f1` + # ntp_adjtime is not available in macOS 10.12 (Darwin 16.x.x) and earlier + if [ $major -gt "16" ]; then + add_def HAVE_MACOS_SYS_TIMEX + EXTRA_OBJECTS="$EXTRA_OBJECTS sys_generic.o sys_netbsd.o sys_timex.o" + if [ $feat_droproot = "1" ]; then + priv_ops="$priv_ops ADJUSTTIMEX" + fi + fi + 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" + add_def SOLARIS + # These are needed to have msg_control in struct msghdr + add_def __EXTENSIONS__ + add_def _XOPEN_SOURCE 1 + add_def _XOPEN_SOURCE_EXTENDED 1 + if [ $feat_droproot = "1" ]; then + add_def FEAT_PRIVDROP + priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET" + fi + echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")" + ;; + * ) + echo "error: $SYSTEM is not supported (yet?)" + exit 1 + ;; +esac + +if [ $feat_debug = "1" ]; then + add_def FEAT_DEBUG +fi +add_def DEBUG $feat_debug + +if [ $feat_cmdmon = "1" ]; then + add_def FEAT_CMDMON + EXTRA_OBJECTS="$EXTRA_OBJECTS cmdmon.o manual.o pktlength.o" +fi + +if [ $feat_ntp = "1" ]; then + add_def FEAT_NTP + EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o" + if [ $feat_ntp_signd = "1" ]; then + add_def FEAT_SIGND + EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o" + fi +else + feat_asyncdns=0 + feat_timestamping=0 +fi + +if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then + EXTRA_OBJECTS="$EXTRA_OBJECTS addrfilt.o clientlog.o keys.o nameserv.o" +else + feat_ipv6=0 +fi + +if [ $feat_refclock = "1" ]; then + add_def FEAT_REFCLOCK + EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o" +fi + +MYCC="$CC" +MYCFLAGS="$CFLAGS" +MYCPPFLAGS="$CPPFLAGS" +MYLDFLAGS="$LDFLAGS" + +if [ "x$MYCC" = "x" ]; then + for cc in gcc clang cc ""; do + if [ "x$cc" = "x" ]; then + echo "error: no C compiler found" + exit 1 + fi + MYCC=$cc + if test_code "$MYCC" '' '' '' ''; then + break + fi + done +else + if ! test_code "$MYCC" '' '' '' ''; then + echo "error: C compiler $MYCC cannot create executables" + exit 1 + fi +fi + +if [ "x$MYCFLAGS" = "x" ]; then + MYCFLAGS="-O2 -g" + + TESTCFLAGS="-D_FORTIFY_SOURCE=2 -fPIE" + TESTLDFLAGS="-pie -Wl,-z,relro,-z,now" + if test_code 'hardening compiler options' '' "$TESTCFLAGS" "$TESTLDFLAGS" ''; then + MYCFLAGS="$MYCFLAGS $TESTCFLAGS" + MYLDFLAGS="$MYLDFLAGS $TESTLDFLAGS" + fi + TESTCFLAGS="-fstack-protector-strong --param=ssp-buffer-size=4" + if test_code '-fstack-protector-strong' '' "$TESTCFLAGS" '' ''; then + MYCFLAGS="$MYCFLAGS $TESTCFLAGS" + else + TESTCFLAGS="-fstack-protector --param=ssp-buffer-size=4" + if test_code '-fstack-protector' '' "$TESTCFLAGS" '' ''; then + MYCFLAGS="$MYCFLAGS $TESTCFLAGS" + fi + fi +fi + +if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then + MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall" +fi + +if test_code '64-bit time_t' 'time.h' '' '' ' + char x[sizeof(time_t) > 4 ? 1 : -1] = {0}; + return x[0];' +then + add_def HAVE_LONG_TIME_T 1 + + if [ "x$ntp_era_split" != "x" ]; then + split_seconds=$ntp_era_split + split_days=0 + else + if [ "x$SOURCE_DATE_EPOCH" != "x" ]; then + split_seconds=$SOURCE_DATE_EPOCH + else + split_seconds=`date '+%s'` + fi + if [ "x$split_seconds" = "x" ]; then + echo "error: could not get current time, --with-ntp-era option is needed" + exit 1 + fi + split_days=$((50 * 365)) + fi + + add_def NTP_ERA_SPLIT "(${split_seconds}LL - $split_days * 24 * 3600)" + + date_format='+%Y-%m-%dT%H:%M:%SZ' + + # Print the full NTP interval if a suitable date is found + if [ "x`date -u -d '1970-01-01 UTC 9 days ago 5 seconds 3 seconds' \ + $date_format 2> /dev/null`" = "x1969-12-23T00:00:08Z" ] + then + time1="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds" \ + $date_format`" + time2="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds 4294967296 seconds" \ + $date_format`" + echo "NTP time mapped to $time1/$time2" + fi +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 in -lm' 'math.h' '' '-lm' "$MATHCODE"; then + LIBS="-lm" + else + echo "error: could not compile/link a program which uses sqrt(), log(), pow()" + exit 1 + fi +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;' +then + add_def HAVE_IN_PKTINFO +fi + +if [ $feat_ipv6 = "1" ] && \ + test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$EXTRA_LIBS" ' + 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));' +then + add_def FEAT_IPV6 + if test_code 'struct in6_pktinfo' 'sys/socket.h netinet/in.h' '' '' ' + return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;' + then + add_def HAVE_IN6_PKTINFO + else + if test_code 'struct in6_pktinfo with _GNU_SOURCE' 'sys/socket.h netinet/in.h' \ + '-D_GNU_SOURCE' '' 'return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;' + then + add_def _GNU_SOURCE + add_def HAVE_IN6_PKTINFO + fi + fi +fi + +if [ $try_clock_gettime = "1" ]; then + if test_code 'clock_gettime()' 'time.h' '' '' \ + 'clock_gettime(CLOCK_REALTIME, NULL);' + then + add_def HAVE_CLOCK_GETTIME + else + if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \ + 'clock_gettime(CLOCK_REALTIME, NULL);' + then + add_def HAVE_CLOCK_GETTIME + EXTRA_LIBS="$EXTRA_LIBS -lrt" + fi + fi +fi + +if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \ + 'return getaddrinfo(0, 0, 0, 0);' +then + add_def HAVE_GETADDRINFO +fi + +if [ $feat_asyncdns = "1" ] && \ + test_code 'pthread' 'pthread.h' '-pthread' '' \ + 'return (int)pthread_create((void *)1, NULL, (void *)1, NULL);' +then + add_def FEAT_ASYNCDNS + add_def USE_PTHREAD_ASYNCDNS + EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o" + MYCFLAGS="$MYCFLAGS -pthread" +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 +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 + add_def HAVE_RECVMMSG + else + if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \ + "$EXTRA_LIBS" "$RECVMMSG_CODE" + then + add_def _GNU_SOURCE + add_def HAVE_RECVMMSG + fi + fi +fi + +if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] && + test_code 'SW/HW timestamping' 'sys/types.h sys/socket.h linux/net_tstamp.h + linux/errqueue.h linux/ptp_clock.h' '' '' ' + 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));' +then + add_def HAVE_LINUX_TIMESTAMPING + EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o" + + if test_code 'other timestamping options' \ + 'sys/types.h sys/socket.h linux/net_tstamp.h' '' '' ' + 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;'; then + add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1 + add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1 + add_def HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW 1 + fi +fi + +timepps_h="" +if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; 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>' 'inttypes.h time.h timepps.h' '' '' ''; then + timepps_h="timepps.h" + add_def HAVE_TIMEPPS_H + fi + fi +fi + +if [ "x$timepps_h" != "x" ] && \ + test_code 'PPSAPI' "inttypes.h string.h time.h $timepps_h" '' '' ' + pps_handle_t h = 0; + pps_info_t i; + struct timespec ts; + return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);' +then + add_def FEAT_PPS +fi + +if [ $feat_droproot = "1" ] && [ $try_libcap = "1" ] && \ + test_code \ + libcap \ + 'sys/types.h pwd.h sys/prctl.h sys/capability.h grp.h' \ + '' '-lcap' \ + 'prctl(PR_SET_KEEPCAPS, 1);cap_set_proc(cap_from_text("cap_sys_time=ep"));' +then + add_def FEAT_PRIVDROP + EXTRA_LIBS="$EXTRA_LIBS -lcap" +fi + +if [ $feat_droproot = "1" ] && [ $try_clockctl = "1" ] && \ + test_code '<sys/clockctl.h>' 'sys/clockctl.h' '' '' '' +then + add_def FEAT_PRIVDROP + priv_ops="BINDSOCKET" +fi + +if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \ + test_code seccomp 'seccomp.h' '' '-lseccomp' \ + '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" + EXTRA_LIBS="$EXTRA_LIBS -lseccomp" +fi + +if [ "x$priv_ops" != "x" ]; then + EXTRA_OBJECTS="$EXTRA_OBJECTS privops.o" + add_def PRIVOPS_HELPER + for o in $priv_ops; do + add_def PRIVOPS_$o + done +fi + +if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \ + test_code '<linux/rtc.h>' 'sys/ioctl.h linux/rtc.h' '' '' \ + 'ioctl(1, RTC_UIE_ON&RTC_UIE_OFF&RTC_RD_TIME&RTC_SET_TIME, 0&RTC_UF);' +then + EXTRA_OBJECTS="$EXTRA_OBJECTS rtc_linux.o" + add_def FEAT_RTC +fi + +if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \ + grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \ + test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \ + 'ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);' +then + grep 'HAVE_LINUX_TIMESTAMPING' config.h > /dev/null || + EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o" + add_def FEAT_PHC +fi + +if [ $try_setsched = "1" ] && \ + test_code \ + 'sched_setscheduler()' \ + 'sched.h' '' '' ' + struct sched_param sched; + sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &sched);' +then + add_def HAVE_SCHED_SETSCHEDULER +fi + +if [ $try_lockmem = "1" ] && \ + test_code \ + 'mlockall()' \ + 'sys/mman.h sys/resource.h' '' '' ' + struct rlimit rlim; + setrlimit(RLIMIT_MEMLOCK, &rlim); + mlockall(MCL_CURRENT|MCL_FUTURE);' +then + add_def HAVE_MLOCKALL +fi + +if [ $feat_forcednsretry = "1" ] +then + add_def FORCE_DNSRETRY +fi + +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" \ + '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" + fi + fi + + EXTRA_CLI_LIBS="$EXTRA_CLI_LIBS $READLINE_LINK" +fi + +HASH_OBJ="hash_intmd5.o" +HASH_LINK="" + +if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ]; then + test_cflags="`pkg_config --cflags nettle`" + test_link="`pkg_config --libs nettle`" + if test_code 'nettle' 'nettle/nettle-meta.h nettle/sha2.h' \ + "$test_cflags" "$test_link" \ + 'return nettle_hashes[0]->context_size;' + then + HASH_OBJ="hash_nettle.o" + HASH_LINK="$test_link" + LIBS="$LIBS $HASH_LINK" + MYCPPFLAGS="$MYCPPFLAGS $test_cflags" + add_def FEAT_SECHASH + 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" + 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 +fi + +if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then + if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \ + 'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);' + 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 + +SYSCONFDIR=/etc +if [ "x$SETSYSCONFDIR" != "x" ]; then + SYSCONFDIR=$SETSYSCONFDIR +fi + +PREFIX=/usr/local +if [ "x$SETPREFIX" != "x" ]; then + PREFIX=$SETPREFIX +fi + +EPREFIX=${PREFIX} +if [ "x$SETEPREFIX" != "x" ]; then + EPREFIX=$SETEPREFIX +fi + +BINDIR=${EPREFIX}/bin +if [ "x$SETBINDIR" != "x" ]; then + BINDIR=$SETBINDIR +fi + +SBINDIR=${EPREFIX}/sbin +if [ "x$SETSBINDIR" != "x" ]; then + SBINDIR=$SETSBINDIR +fi + +DATAROOTDIR=${PREFIX}/share +if [ "x$SETDATAROOTDIR" != "x" ]; then + DATAROOTDIR=$SETDATAROOTDIR +fi + +MANDIR=${DATAROOTDIR}/man +if [ "x$SETMANDIR" != "x" ]; then + MANDIR=$SETMANDIR +fi + +DOCDIR=${DATAROOTDIR}/doc/chrony +if [ "x$SETDOCDIR" != "x" ]; then + DOCDIR=$SETDOCDIR +fi + +LOCALSTATEDIR=/var +if [ "x$SETLOCALSTATEDIR" != "x" ]; then + LOCALSTATEDIR=$SETLOCALSTATEDIR +fi + +CHRONYRUNDIR=${LOCALSTATEDIR}/run/chrony +if [ "x$SETCHRONYRUNDIR" != "x" ]; then + CHRONYRUNDIR=$SETCHRONYRUNDIR +fi + +CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony +if [ "x$SETCHRONYVARDIR" != "x" ]; then + CHRONYVARDIR=$SETCHRONYVARDIR +fi + +add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\"" +add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\"" +add_def DEFAULT_PID_FILE "\"$default_pidfile\"" +add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\"" +add_def DEFAULT_USER "\"$default_user\"" +add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\"" +add_def MAIL_PROGRAM "\"$mail_program\"" + +common_features="`get_features SECHASH IPV6 DEBUG`" +chronyc_features="`get_features READLINE`" +chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`" +add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\"" +add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\"" +echo "Features : $chronyd_features $chronyc_features $common_features" + +if [ -f version.txt ]; then + CHRONY_VERSION="`cat version.txt`" +else + CHRONY_VERSION="DEVELOPMENT" +fi + +add_def CHRONY_VERSION "\"${CHRONY_VERSION}\"" + +for f in Makefile doc/Makefile test/unit/Makefile +do + echo Creating $f + sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\ + s%@CC@%${MYCC}%;\ + s%@CFLAGS@%${MYCFLAGS}%;\ + s%@CPPFLAGS@%${MYCPPFLAGS}%;\ + s%@LIBS@%${LIBS}%;\ + s%@LDFLAGS@%${MYLDFLAGS}%;\ + 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}%;\ + s%@DOCDIR@%${DOCDIR}%;\ + s%@MANDIR@%${MANDIR}%;\ + s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\ + s%@CHRONYRUNDIR@%${CHRONYRUNDIR}%;\ + s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\ + s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\ + s%@DEFAULT_PID_FILE@%${default_pidfile}%;\ + s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\ + s%@DEFAULT_USER@%${default_user}%;\ + s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \ + < ${f}.in > $f +done + +# ======================================================================= +# vim:et:sw=2:ht=2:sts=2:fdm=marker:cms=#%s + diff --git a/contrib/andrew_bishop_1 b/contrib/andrew_bishop_1 new file mode 100644 index 0000000..4c0b437 --- /dev/null +++ b/contrib/andrew_bishop_1 @@ -0,0 +1,114 @@ +From amb@gedanken.demon.co.uk Tue Aug 17 22:14:00 1999 +Date: Fri, 6 Aug 1999 19:00:24 +0100 +From: Andrew M. Bishop <amb@gedanken.demon.co.uk> +To: richard@rrbcurnow.freeserve.co.uk +Subject: Re: Chrony and laptop configuration + +Hi, + +Attached is the apmd_proxy script from the apmd-3.0beta9 distribution. + +The changes that I would make are the following: + +Replace the update_clock function (line 122) with + +update_clock () { + +chronyd -f /etc/chrony.conf + +} + +Around line 171 (in the suspend actions section) I would kill chronyd. + +begin 644 apmd_proxy.gz +M'XL("+L@JS<``V%P;61?<')O>'D`I5K[;]M&$OY9^BLV2JZQ6TF.`Q2XUDA1 +MQU8<]>('_&A[.!R,-;D2MR&Y+)?TXZ[WO]\W,\N'%-E)[X08CJ2=V7G/-T,_ +M?[9S8_,=GPR?*_S3119?%Z6[?U`3A=_+4F<JMK[059284BU<J?;/CE6L3>9R +M$!R4VB[5L2X_WNDRKM161!]D/Z:)T9FVZ73I%]$TUUY/E^YV6[W>Q>$'M?O= +M=]^!^E#?VEB]+=U=;M)4;<4W/Q;.5]-$E[=@-S5QO:V^4S_5N0DD(+I,K%<^ +M(0(?E;:H%-Y'.DU-K&X>5)68GHAJBU3:5G>)R?DK7^G*@(M;*)T_J,+=0:U, +MYWII,I-7:E'G465!F&AP372^-/%4X5*CS+V.*F5N<<R#EZ[`IBKM<@D.Q)ID +ML/F267=VC$UA\E@11W?'7Z@[8NWRA5W6I8G)[!7>9X5-C:IL9J:LYR^V2BP) +M3>J*HJS`@Z],IG2<V=SZJM05?.(35Z>Q*NJ*M0(SJ!1[TK-4FA7RL(&-DN;H +MC5&%*>'/#&:K"XC'EH%".O>6"42,GBJ=G>\@F_*%B>S"1DJ7RSIKK0*-25Q< +M0.*RN<A0]`W9U$51799LU#F[KJ?@2Z]*XR&,MS<VM16\Z=3OM2G9K>""P(@1 +M:(;4:ASM37EK(Z.V;JU6.Y`TVH',VQRKF<-9FY.:FG0:DT+P!MA6^J-(J`O0 +M%*4E[<54HO@[T,/E69&:<?";1<R1!=2H9Q5?>_:P.&:D?JM]!?/B3L-ZMRYC +M-1/$-%D0$BP=1,/OAD'F8A-";<6[X)(A9^ZL3X@LN$W!2V;2^2`X.;50J\Y3 +MIV.*Q;BTMZ:$U8D+$EG?<(B25+FI[ESYD60`1QW1W;^T:=**#(?4%",I[%.. +M0T`\8@HYJW(R=SH:4UQ+T%%4@Z_SC8DI-+M(+`T)B9A@P\\7+,)'4Z(J2"ZS +M%B0-G:/LW)WNOJ(@2.R2ZM)6[<69),T(/$(&D+T6EM*OKK;'_5B.=(YKJ[K, +M$1#*E"6817``AXT$6^,7XH/DR&-4%\G^,8P6VP@A!5NBM%1)*`%TGCXC5ZYI +M^)N)*H[Z3^(J2$0^CN%-R&`78W6C81`E1?;LBG1M'$86O*7DP&?0&W;)$2FU +MV!4L6\$10A6%-=7%$(IDS;?STXM-HK'Q\7KV3)V<7L[HMY)JBW^YJT*VM`+7 +M(B"41<`?FAL+0TK<^&>!U_GL\NK\1/V\_^%J]KUP5Z_06G*'^-9I\,#>$Z96 +ME.TZBDQ1<:FDURXXB-0]9:D*!LJMXZN+2_5^_^<9ZWMX/O]Y=BXA<WJNWL^/ +MWL_.MUG"]X:XEV:E?GN#DI-'QG,L=`I_OUX/<5]9J977I-][J-SQ&2GR?4)7 +MJ"<(N<'5%8=7C.:X1KU2<'K4X0.ZMB0=/+F^7UTV\T'IK1#,Z3J?$..P0WNB +M(=GZ>OL34[#IOU"F<)ID4IN%XL!N3'-%;[Y8K<#\LQQZ0JQR"&6LN4(X7/05 +MD9*X*%WVA"`-FW#/9]A\3IJ>EYY@L^ZI55:"9U!:JHIZZB=>*DWA*%I5"J@2 +M3FWD(+!)/<%A_V"GN49H-O+AOO`DG]R0:E23Z6A=Q-2F/PF_P"[2A0[`X1%V +M`KNF#<'6-U('P'`U`ZD8HS;"&^BNTJZYVZ%.H-.T%8)(OV'26X!=-%>JW1LZ +MEZ\+$@"5%!`Y?(:?Z5H?$V$NYL=G'^;OYK-#=7!Z\FY^='6^?SD_/5$JX%_` +M'X``!C0$-OL(D=H:Y(::5>D8JAG+W8ERTTA%D>ZVP-?NCMX#:EL2W?>A4/HP +M)KFH#L>V(6L!=^5-NI"&<6$J=75YP)"JK%G_!U>CF:8N^DB5K&ED<HK@;>@J +M)`]Q!A.\<[!':>,8``27$HH-\L)HN=HQ5;0#9XK_=ICY=`B.;Q8Z]::5Y.+J +MXFQV<GA]>G*]SS+QUT&H%D+I6V?C34T'7$+;X8&!%<ET!!QN&/VZ/.=>24S` +MG],`ZKRESKW0=5IUL,&S+QCMDO*-&T).T!S5T`]7A'Y#5F1]S@Z.#^;[8%,& +M9AR0`G[A+;@+H)MCCE3*)=Z#3JS%5,`4:9[H6S0ZYB50'/RHI=^T90Y*D?-- +MF:*!^AJ@#;5%JXN#BSGA*1`"0%5D%<1PAQY&`3V,`N15AN`DM]$^K`UQ*[I0 +MPZ7+B^3!4['"C8'+GD)8PTYC05-<96A$`$C@'NH6"PZT!#E-<)\A'S&-JE1X +MC!KH-V:_,H!EP,0XB`<=\)&:.1V*B:]G/\T.+LG\P1-=4-&0.GM[=70T/SGB +M=U<Y\:=)L1FR@+9CB!D<?#R[?']Z"(CBVO^_YOH5FYMZN91PS^`]#)P^Z-)4 +MJ=2A,JD36*8T<DL<BM_2Y$CXE'K:&,`?I8>2IN(RU6@GT0J,_U&B!&[.N7RV +M`ZP'5W((\*H3"`\NN%1196O5(;,)'F[%1*US-?E51NRN\$H):'6>J`]NZ5OP +MG5+:=,/AZESL\O1!KJ?YN??%BZ^I&W1L7S=LP<1"K[+.UUC!BCM55NSTY`); +MRN4H<>KEUVNOE^J''YXB&+UX!2%&?73V*`&WHPVOIV]X.5E[/272O8DV7/#Z +M40*4336Y9\?L4\5");#4K*@0)ZK$)!2J&<(HK6,I9IZ6$_33#MQ->2*.E:[J +ML`Y8-^;_\!H.A\SY&L+1\&ZVMM6_AP,IX//#&4_Y$LB^``@%``X#I".\GZ"1 +M\GLB.>215L(RE&#(.=CQM-5*XD)C4)[\KB87:O>O:B<VM_A0#P:@Q/LW;]!_ +MOU4816J@W.&`*N_>\#^->&'0UK!=(V&X[A$I_YQ`KUIY>C<_Y]'K>]4-:LT$ +MCR88<,7KZ6M.4I[(I;(CU6/>*J"O#@4D74L#;B0/R"FP@"4S6NEU"Q4^/1R@ +M4?Y#318;^ZWZYQXW@.&``G"Z\8Q\AT*=4D]]2(U\P&Q'+PX^G![\[?CT<#92 +M;]31\67'<4"MG#L?GU_8X8!^F.X%`8<WH9.OR/#NP_[1F]%H.#!4KGL?3>J1 +M,(`R]TJ,'W107WT5L,G$JQ=T7/WQATKN5C\C7Q"DX`D/U2;3L#_MEDQ<EX1@ +MYDWRD/'".,\ESCM$:F9I&Q?2I[==!(>%I2%S')8QJX&",HVX+B<"F:0K]!%% +M#ZFTB*`9W\G&=S+,HL=UX.+Y<!B!':R_.T(0#8?DGO7Z$UX-?.*++B[W3P[? +M_OV1L\SF/8>=BFH,M)G]%REC$(_6=9OB9JYAT?O@Q#,'M$RRYJ%I-B<NH"Y1 +M?LS@I;_]"HB'K>P6S*-*6@^T:S#&.`)QIZ3R*-P]^F,4)!IM#RDO]ION*2C: +M+-E/:W@Q^"HLMMK9:O`)6OS$_"W4%;[4]!0M<9#/"Z)G7;NE#'MYM+Y1;#8A +M"\IW>-W&*1<_%"CKJ+GOI]X%4\GRD9ID3N;4Z@.JVWUO)"'";@+9N$RC#LN+ +MM)7UV12434:NPNPF-R=:O7B-=P'.=,D-(I=?Z^A:$-T/7/CR.DW;$P-S#SBS +M2^X\E\W.%N^@@MQA?FK5DRB(MT$92D4CV2[='V*N$X"YOY*2P%G=WS!5#T5` +MH;%C7+ITJM"^DI`HG,VKL;JI&3,3>0B.!@?"-K(9N\$(PY@'K?)/AU8`^V&9 +M[:5DTU9^)6<"D`N'@QO#,HTBBR#=H+>YY"4M0>X`S@D+)S+,K:EFR@HECHC; +M>>.)^0!?06J,B#0>"`7'!TGG&YC?.`LW1V91DX#>+2IJ-Y-N'2H#"NO;0FN6 +MCK:Q8>)@*4H!``CUE9.<"9$KY<%!+`L\,;4`\_/9Q=7Q##<=A/U>,S#T+#7B +M%2=C9:Y<88V)O*:5=3`W!IF:I^22[]3MR$\&)&,ANR::U.M=1B"]1>..8)AE +MY])Y--\,=NDE5M>LPDP30EA)$B&^'YE8$/4\?7<A/UAEQ`:G3.!.N?YM6]&Z +M=.J/.AL"&#-)J8O$1EZ&RM@961++:,5YDG=G>-/7Q8\UM$)7OR)B"3DA>$*A +ME/K-+N4-C6_6#H8>3#@,?655:YI6<N^H(M?9#7R\.PX/&L2XM:\Y'S`@FONJ +M.8P[KBBE1,)Q2$.D".^8*M?="J@,+(#K6-%?V]O0R9FQ>.RY:GU6^Y*?HT;) +M;=7YX+D:K'ZSNZ=\:DRA=NG+8&;'<R.9&K*%_@4`*,]PX%57TVZ&^EV`@D0T +M^W7_^.P#@.+I!=H&AK.8NR0Y(/AR)^P-&Z,3E:Q"`^.D9<A=6)Z+4:R5A@_4 +MG:T"K5)GF,[YHEM=/M#BHTO,T)A'+(S+:1_`S7?P7(P`>*53>=C,)Q;H?WM[ +M3\*1%I9("C]U;"B=@S6>3J>K.U6**^#JLS%@`CU(D&T_XSV!Q5-""*(J8`&5 +ML;8ET%%IH:&K"1V%#(_OC,`P<D'1/N[^?`/H%B<K>Q[N(DTI;8!A;]]G^=9& +MV"[L>;%M2ZE?7-D^4\+6*E:ON7,+#QT$+?VQFO1_ER1+T]_C-:D)O7Y)>B17 +M0L1^+E?"L2YEPJ'I>F`_&K3YE\<LX//YY9.GF,U!>*!.B<=/5A>V1-ODQT9^ +MRD?F?6#?+!`9287V2!]E%-8(C&:F'C7XG[`O,4-@*_4%2.R3V;>9K@:K0[N` +MJ2^WQNG9TX<V6X/"W92$;_@))9TY##F1,3Y8.8JB,@X9<D//_=6HI\<H;"+9 +M(*Z@1-^@Z]Z>>EHC=?!^_^1H1MSAXLNKBT=/TDW23^@N'L*06S;,KB.^G+ZA +M9+VS]`<VU(5VN'0U`\18Z1B@!,4\0UV(&_>CDE71EP'K#3JV";?N478I^50D +MI"5+$%`FGU0ZC/P1"#Y?J7@]PB!EH&U"ELIPI4;H"B.5`L^F:DLV'&9!H!EV +M5&:ZG-+>^=M7?Q%:P-"7%>'RC#;2\D<[%2/8VA.F[-_:/?T)%PLWLA_J]5VW +>Z]AB9"M2/=L.+(S7D<0S_V\81H;_`M>*^#$A)0`` +` +end + +-- +Andrew. +---------------------------------------------------------------------- +Andrew M. Bishop amb@gedanken.demon.co.uk + http://www.gedanken.demon.co.uk/ + diff --git a/contrib/andrew_bishop_2 b/contrib/andrew_bishop_2 new file mode 100644 index 0000000..d3ede74 --- /dev/null +++ b/contrib/andrew_bishop_2 @@ -0,0 +1,95 @@ +From amb@gedanken.demon.co.uk Wed Sep 1 22:26:59 1999 +Date: Thu, 19 Aug 1999 17:30:14 +0100 +From: Andrew M. Bishop <amb@gedanken.demon.co.uk> +To: richard@rrbcurnow.freeserve.co.uk +Subject: [amb@gedanken.demon.co.uk: Chrony and laptop configuration] + +Hi, + +What you need to do is replace 10.0.0.0 with the network of the +freeserve nameservers in the two scripts below. + +Other than that you can use it as is. + +------- Start of forwarded message ------- +From: "Andrew M. Bishop" <amb@gedanken.demon.co.uk> +To: richard@rrbcurnow.freeserve.co.uk +Subject: Chrony and laptop configuration +Date: Sat, 31 Jul 1999 11:02:04 +0100 + +Attached are the ip-up and ip-down files that I use for chrony. +(Actually because of the way that debian works they are separate file +in the /etc/ppp/ip-up.d directory that are run in a SysV init style). + +They rely on the presence of an 'ipparam demon' or 'ipparam freeserve' +line in the PPP options file. + +-------------------- /etc/ppp/ip-up -------------------- +#!/bin/sh -f +# +# A script to start chrony +# + +PPP_IPPARAM="$6" + +if [ $PPP_IPPARAM = "demon" ]; then + + /usr/local/bin/chronyc << EOF +password xxxxxxx +online 255.255.255.0/158.152.1.0 +online 255.255.255.0/194.159.253.0 +EOF + +fi + +if [ $PPP_IPPARAM = "freeserve" ]; then + + /usr/local/bin/chronyc << EOF +password xxxxxxx +online 255.255.255.0/10.0.0.0 +EOF + +fi +-------------------- /etc/ppp/ip-up -------------------- + +-------------------- /etc/ppp/ip-down -------------------- +#!/bin/sh -f +# +# A script to stop chrony +# + +PPP_IPPARAM="$6" + +if [ $PPP_IPPARAM = "demon" ]; then + + /usr/local/bin/chronyc << EOF +password xxxxxxx +offline 255.255.255.0/158.152.1.0 +offline 255.255.255.0/194.159.253.0 +EOF + +fi + +if [ $PPP_IPPARAM = "freeserve" ]; then + + /usr/local/bin/chronyc << EOF +password xxxxxxx +offline 255.255.255.0/10.0.0.0 +EOF + +fi +-------------------- /etc/ppp/ip-down -------------------- + +-- +Andrew. +---------------------------------------------------------------------- +Andrew M. Bishop amb@gedanken.demon.co.uk + http://www.gedanken.demon.co.uk/ +------- End of forwarded message ------- + +-- +Andrew. +---------------------------------------------------------------------- +Andrew M. Bishop amb@gedanken.demon.co.uk + http://www.gedanken.demon.co.uk/ + diff --git a/contrib/bryan_christianson_1/README.txt b/contrib/bryan_christianson_1/README.txt new file mode 100644 index 0000000..3a0a2ef --- /dev/null +++ b/contrib/bryan_christianson_1/README.txt @@ -0,0 +1,103 @@ +Notes for installing chrony on macOS +Author: Bryan Christianson (bryan@whatroute.net) +------------------------------------------------ + +These files are for those admins/users who would prefer to install chrony +from the source distribution and are intended as guidelines rather than +being definitive. They can be edited with a plain text editor, such as +vi, emacs or your favourite IDE (Xcode) + +It is assumed you are comfortable with installing software from the +terminal command line and know how to use sudo to acquire root access. + +If you are not familiar with the macOS command line then +please consider using ChronyControl from http://whatroute.net/chronycontrol.html + +ChronyControl provides a gui wrapper for installing these files and sets the +necessary permissions on each file. + + +Install the chrony software +--------------------------- + +You will need xcode and the commandline additions to build and install chrony. +These can be obtained from Apple's website via the App Store. + +cd to the chrony directory +./configure +make +sudo make install + +chrony is now installed in default locations (/usr/local/sbin/chronyd, +/usr/local/bin/chronyc) + +Create a chrony.conf file - see the chrony website for details + +The support files here assume the following directives are specified in the +chrony.conf file + +keyfile /etc/chrony.d/chrony.keys +driftfile /var/db/chrony/chrony.drift +bindcmdaddress /var/db/chrony/chronyd.sock +logdir /var/log/chrony +dumpdir /var/db/chrony + +Install this file as /etc/chrony.d/chrony.conf and create +the directories specified in the above directives if they don't exist. +You will need root permissions to create the directories. + + +Running chronyd +--------------- +At this point chronyd *could* be run as a daemon. Apple discourage running +daemons and their preferred method uses the launchd facility. The +support files here provide a launchd configuration file for chronyd and also +a shell script and launchd configuration file to rotate the chronyd logs on a daily basis. + + +Support files +------------- +Dates and sizes may differ +-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt +-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh +-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.tuxfamily.chronyc.plist +-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.tuxfamily.chronyd.plist + +If you have used chrony support directories other than those suggested, you +will need to edit each file and make the appropriate changes. + + +Installing the support files +---------------------------- + +1. chronylogrotate.sh +This is a simple shell script that deletes old log files. Unfortunately because +of the need to run chronyc, the standard macOS logrotation does not work with +chrony logs. + +This script runs on a daily basis under control of launchd and should be +installed in the /usr/local/bin directory + +sudo cp chronylogrotate.sh /usr/local/bin +sudo chmod +x /usr/local/bin/chronylogrotate.sh +sudo chown root:wheel /usr/local/bin/chronylogrotate.sh + + +2. org.tuxfamily.chronyc.plist +This file is the launchd plist that runs logrotation each day. You may +wish to edit this file to change the time of day at which the rotation +will run, currently 04:05 am + +sudo cp org.tuxfamily.chronyc.plist /Library/LaunchDaemons +sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyc.plist +sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyc.plist +sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyc.plist + + +3. org.tuxfamily.chronyd.plist +This file is the launchd plist that runs chronyd when the Macintosh starts. + +sudo cp org.tuxfamily.chronyd.plist /Library/LaunchDaemons +sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyd.plist +sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyd.plist +sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyd.plist diff --git a/contrib/bryan_christianson_1/chronylogrotate.sh b/contrib/bryan_christianson_1/chronylogrotate.sh new file mode 100755 index 0000000..f919544 --- /dev/null +++ b/contrib/bryan_christianson_1/chronylogrotate.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +# chronyd/chronyc - Programs for keeping computer clocks accurate. +# +# ********************************************************************** +# * Copyright (C) Bryan Christianson 2015 +# * +# * 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. +# * +# ********************************************************************** + +LOGDIR=/var/log/chrony + +rotate () { + prefix=$1 + + rm -f $prefix.log.10 + + for (( count=9; count>= 0; count-- )) + do + next=$(( $count+1 )) + if [ -f $prefix.log.$count ]; then + mv $prefix.log.$count $prefix.log.$next + fi + done + + if [ -f $prefix.log ]; then + mv $prefix.log $prefix.log.0 + fi +} + +if [ ! -e "$LOGDIR" ]; then + logger -s "missing directory: $LOGDIR" + exit 1 +fi + +cd $LOGDIR + +rotate measurements +rotate statistics +rotate tracking + +# +# signal chronyd via chronyc +/usr/local/bin/chronyc cyclelogs > /dev/null + +exit $?
\ No newline at end of file diff --git a/contrib/bryan_christianson_1/org.tuxfamily.chronyc.plist b/contrib/bryan_christianson_1/org.tuxfamily.chronyc.plist new file mode 100644 index 0000000..a3c42c6 --- /dev/null +++ b/contrib/bryan_christianson_1/org.tuxfamily.chronyc.plist @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>org.tuxfamily.logrotate</string> + <key>KeepAlive</key> + <false/> + <key>ProgramArguments</key> + <array> + <string>/bin/sh</string> + <string>/usr/local/bin/chronylogrotate.sh</string> + </array> + <key>StartCalendarInterval</key> + <dict> + <key>Minute</key> + <integer>5</integer> + <key>Hour</key> + <integer>4</integer> + </dict> +</dict> +</plist> diff --git a/contrib/bryan_christianson_1/org.tuxfamily.chronyd.plist b/contrib/bryan_christianson_1/org.tuxfamily.chronyd.plist new file mode 100644 index 0000000..2bf42aa --- /dev/null +++ b/contrib/bryan_christianson_1/org.tuxfamily.chronyd.plist @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>org.tuxfamily.chronyd</string> + <key>Program</key> + <string>/usr/local/sbin/chronyd</string> + <key>ProgramArguments</key> + <array> + <string>chronyd</string> + <string>-n</string> + <string>-f</string> + <string>/private/etc/chrony.d/chrony.conf</string> + </array> + <key>KeepAlive</key> + <true/> +</dict> +</plist> diff --git a/contrib/erik_bryer_1 b/contrib/erik_bryer_1 new file mode 100644 index 0000000..c551dfe --- /dev/null +++ b/contrib/erik_bryer_1 @@ -0,0 +1,65 @@ +#!/bin/sh +# +# chrony Start time synchronization. This script +# starts chronyd. +# +# Hacked by: Erik Bryer <ebryer@spots.ab.ca> using inet as a template +# +# chkconfig: 2345 02 82 +# description: chronyd helps keep the system time accurate by calculating \ +# and applying correction factors to compensate for the drift \ +# in the clock. chronyd can also correct the hardware clock \ +# (RTC) on some systems. +# processname: chronyd +# config: /etc/chrony.conf + +# Source function library. +. /etc/rc.d/init.d/functions + +# Source networking configuration. +. /etc/sysconfig/network + +# Set path to include chronyd in /usr/local/sbin +PATH="$PATH:/usr/local/sbin" + +[ -f /usr/local/sbin/chronyd ] || exit 0 + +[ -f /etc/chrony.conf ] || exit 0 + +RETVAL=0 + +# See how we were called. +case "$1" in + start) + # Start daemons. + echo -n "Starting chronyd: " + daemon chronyd + RETVAL=$? + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/chrony + echo + ;; + stop) + # Stop daemons. + echo -n "Shutting down chronyd: " +# If not dead killproc automatically sleeps for 4.1 seconds then does +# kill -9. "chrony.txt" prefers a 5 second delay, but this should be ok. + killproc chronyd -15 + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/chrony + echo + ;; + status) + status chronyd + exit $? + ;; + restart) + $0 stop + $0 start + ;; + *) + echo "Usage: named {start|stop|status|restart}" + exit 1 +esac + +exit $RETVAL + diff --git a/contrib/ken_gillett_1 b/contrib/ken_gillett_1 new file mode 100644 index 0000000..48b7999 --- /dev/null +++ b/contrib/ken_gillett_1 @@ -0,0 +1,100 @@ +#!/bin/sh +# +# chronyd This shell script takes care of starting and stopping +# chronyd (NTP daemon). +# +# chkconfig: 45 80 20 +# description: chronyd is the NTP daemon. + +# Source function library. +. /etc/rc.d/init.d/functions + +# Source networking configuration. +. /etc/sysconfig/network + +# Check that networking is up. +[ ${NETWORKING} = "no" ] && exit 0 + +PREDIR="/usr/local" +CHRONYD=$PREDIR"/sbin/chronyd" +CHRONYC=$PREDIR"/bin/chronyc" + +[ -x $CHRONYD -a -x $CHRONYC -a -f /etc/chrony.conf ] || exit 0 + +dochrony() { + if [ -z "$(pidofproc chronyd)" ]; then + echo -e "\n\tchronyd not running\n\n" + exit 2 + fi + KEY=`awk '$1 == "commandkey" {print $2; exit}' /etc/chrony.conf` + PASSWORD=`awk '$1 == '$KEY' {print $2; exit}' /etc/chrony/keys` + + $CHRONYC <<- EOF + password $PASSWORD + $@ + quit + EOF +} + +# make the first parameter' lower case +set - `echo $1 | awk '{print tolower($1)}';shift;echo "$@"` + +# Expand any shortcuts. +case "$1" in + on|1) + set - "online" + ;; + off|0) + set - "offline" +esac + +# See how we were called. +case "$1" in + start) + # Start daemons. + echo -n "Starting chronyd: " + daemon $CHRONYD + if [ $? -eq 0 ]; then + echo $(pidofproc chronyd) > /var/run/chronyd.pid + touch /var/lock/subsys/chronyd + fi + echo + ;; + stop) + # Stop daemons. + echo -n "Shutting down chronyd: " + killproc chronyd + echo + rm -f /var/lock/subsys/chronyd + ;; + status) + status chronyd + ;; + restart|reload) + $0 stop + $0 start + ;; + condrestart) + if [ -f /var/lock/subsys/chronyd ]; then + $0 stop + $0 start + fi + ;; + "") + echo "Usage: chronyd +{start|stop|restart|reload|condrestart|status|[on|off]line etc}" + exit 1 + ;; + +accheck|cmdaccheck|clients|manual|rtcdata|sources|sourcestats|tracking|clients) + dochrony "$@" + ;; + *) + echo -n "Chrony $1: " + dochrony "$@" > /dev/null + [ $? -eq 0 ] && echo_success || echo_failure + echo +esac + +exit 0 + diff --git a/contrib/stephan_boettcher_1 b/contrib/stephan_boettcher_1 new file mode 100644 index 0000000..e5eda11 --- /dev/null +++ b/contrib/stephan_boettcher_1 @@ -0,0 +1,162 @@ +From stephan@nevis1.nevis.columbia.edu Mon Jun 7 20:51:57 1999 +Date: 04 Jun 1999 00:17:25 -0400 +From: Stephan I. Boettcher <stephan@nevis1.nevis.columbia.edu> +To: richard@rrbcurnow.freeserve.co.uk +Subject: chrony 1.1 sysV startup script for notebooks + + +Dear Richard, + +I installed chrony on my notebook, running RedHat 5.1 Linux. +It looks like it works. No problems. + +Thank you! + +I like to donate my sysV startup script, appended below. + +Special feature: the `online' command scans the config file to +selectively turn some servers online, depending on the pcmcia SCHEME. + +booting: /etc/rc.d/init.d/chrony start +/etc/ppp/ip-up: /etc/rc.d/init.d/chrony online +/etc/ppp/ip-down: /etc/rc.d/init.d/chrony offline +logrotate cron: /etc/rc.d/init.d/chrony cyclelogs +a user: /etc/rc.d/init.d/chrony status +a sysadmin: /etc/rc.d/init.d/chrony restart +shutdown: /etc/rc.d/init.d/chrony stop + +Best regards +Stephan + +-- + +------------------------------------------------------------------------ +Stephan Boettcher FAX: +1-914-591-4540 +Columbia University, Nevis Labs Tel: +1-914-591-2863 +P.O. Box 137, 136 South Broadway mailto:stephan@nevis1.columbia.edu +Irvington, NY 10533, USA http://www.nevis.columbia.edu/~stephan +------------------------------------------------------------------------ + +########################### cut here ################################### +#! /bin/bash +# +# /etc/rc.d/init.d/chrony +# +# SYS V startup script for +# chrony ntp daemon +# on Linux 2.0.3x notebooks with pcmcia scheme support +# $Id: stephan_boettcher_1,v 1.1 2000/04/24 21:36:04 richard Exp $ +# +# 1999-06-02 SiB <stephan@nevis1.columbia.edu> +# +# For PCMCIA users: +# In /etc/chrony.conf, precede the server commands for each SCHEME +# with a comment line that contains the word SCHEME and the name of +# the scheme(s) that should use the servers, up to the next line that +# contains the word SCHEME. The servers must be `offline' and +# specified by their IP address. The hostname will not do. +# +# Like: +# +# # SCHEME nevisppp nevislan +# # stephanpc.nevis.columbia.edu +# server 192.12.82.222 offline +# +# # SCHEME desyppp desylan +# +# # dsygw2.desy.de +# server 131.169.30.15 offline +# # dscomsa.desy.de +# server 131.169.197.35 offline + +CONF=/etc/chrony.conf +CHRONYD=/usr/local/sbin/chronyd +CHRONYC=/usr/local/bin/chronyc +KEYS=/etc/chrony.keys + +# See if we got all we need: + +[ -f $CHRONYD -a -f $CHRONYC -a -r $CONF ] || exit + + +[ -r $KEYS ] \ +&& CMDKEY=`awk '/^commandkey/{print $2}' $CONF` \ +&& PASSWORD=`awk -v KEY=$CMDKEY '$1==KEY{print $2}' $KEYS` + + +case "$1" in + + start) + echo -n "Starting chronyd " + $CHRONYD -r -s -f $CONF + echo + ;; + + stop) + echo -n "Shutting down chronyd " + /usr/bin/killall chronyd + echo + ;; + + restart) + $0 stop + $0 start + ;; + + on*) + + [ -f /var/run/pcmcia-scheme ] && SCHEME=`cat /var/run/pcmcia-scheme` + + awk -v SCHEME=${SCHEME:-default} -v PASSWORD=$PASSWORD \ + ' + BEGIN { + SEL=1; + print "password", PASSWORD; + } + /SCHEME/ { + SEL=match($0, SCHEME); + } + SEL && /^server[ \t]*[0-9.]+[ \t].*offline/ { + print "online 255.255.255.255/" $2; + } + ' \ + $CONF \ + | $CHRONYC + + ;; + + off*) + cat <<-EOF | $CHRONYC + password $PASSWORD + offline + trimrtc + dump + EOF + ;; + + *log*) + cat <<-EOF | $CHRONYC + password $PASSWORD + cyclelogs + EOF + ;; + + stat*) + cat <<-EOF | $CHRONYC + sources + sourcestats + tracking + rtcdata + EOF + ;; + + *) + echo "Usage: chronyd {start|stop|restart|status|online|offline|cyclelogs}" + exit 1 + ;; + +esac + +exit 0 + + diff --git a/contrib/wolfgang_weisselberg1 b/contrib/wolfgang_weisselberg1 new file mode 100644 index 0000000..2c41752 --- /dev/null +++ b/contrib/wolfgang_weisselberg1 @@ -0,0 +1,118 @@ + +> Is it possible to limit chronyc to only those commands that +> are readonly plus those necessary to bring a dialup connection up +> and down? That is: online offline dump writertc and password. + +This is trivial on the same host and workable for non-local +hosts: use a wrapper program or script. An *untested* +sample follows. To use it, best create a special user (say +chronyc) and a special group (say chronyg). Make the script +chronyc:chronyg, and 4750 (suid, rwxr-x---). Add all users +who may run the script to the group chronyg. + +Make a chrony password file e.g. +/usr/local/etc/chrony_password. It should be owned by chronyc +and readable only for the owner, containing only the chrony +password (and maybe a newline) in the first line. + +In this way only the script (call it run_chrony, for example) +can read the password. It will allow only those commands you +explicitely allow. You can add a password check -- especially +if you add an internet port so you can access it over the +internet this is advisable. You really want to add logging +to this untested script as well. + + +BTW, if you use some sort of PPP, you probably can use +/etc/ppp/ip-up and /etc/ppp/ip-down to transparently set chrony +on- and offline as the ip connection goes up and comes down. +This is _far_ more user friendly, IMHO, and a DOS by switching +chrony offline all the time is avoided as well. + + +#! /usr/bin/perl -T +use v5.6.1; +use warnings; +use strict; + +sub laundered_command(); +sub order_chrony($$); +sub read_password(); +sub usage($); + +our $CHRONY = "/usr/local/bin/chronyc"; + +# NOTE: select the file system protection wisely for the +# PASSWORDFILE! +our $PASSWORDFILE = "/usr/local/etc/chrony_password"; + +our @ALLOWED_COMMANDS = ( + 'online', # switch online mode on + 'offline', # switch online mode off + 'dump', # save measurements to file + 'writerc', # save RTC accumulated data + + 'clients', # which clients are served by us? + 'rtcdata', # Quality of RTC measurements + 'sources(?: -v)?', # Show our sources (verbose) + 'sourcestats(?: -v)?', # How good are our sources (verbose)? + 'tracking', # whom do we adjust to? + + # 'burst \d+/\d+', # allow them to send bursts? +); + +usage("No command given.") unless $ARGV[0]; + +%ENV = (); # nuke all environment variables. Rather + # drastic, but better safe than sorry! + # Add whatever you really need to get it + # working (again). +$ENV{'PATH'} = '/usr/local/bin:/bin:/usr/bin'; + +order_chrony(laundered_command(), read_password()); + +exit 0; # command succeeded + +############################################################ + +sub usage($) { + print STDERR "Error: ", shift, "\n"; + + # OK, this eats the -v... + print STDERR "Legal commands are:\n\t", join "\n", + map { $_ =~ m:(\w+):; $1 } @ALLOWED_COMMANDS; + exit 1; # error +} + +############################################################ + +sub laundered_command() { + my $regexp = "^(" . join ( "|", @ALLOWED_COMMANDS ) . ")\$"; + my $parameters = join " ", @ARGV; + $parameters =~ m:$regexp: or usage("Command $parameters not allowed."); + + return $1; # this value, then, is untainted. +}; + +############################################################ + +sub read_password() { + open PASS, $PASSWORDFILE + or die "Could not read protected password file: $!"; + my $password = <PASS>; + chomp $password; + return $password; +}; + +############################################################ + +sub order_chrony($$) { + my ($clean_command, $password) = @_; + open CHRONY, "| $CHRONY &> /dev/null" or die "could not run $CHRONY: $!\n"; + print CHRONY "password $password\n"; + print CHRONY "$clean_command\n"; + close CHRONY + or die "Error running command $clean_command\n", "\ton $CHRONY: $!\n"; +} + +############################################################ diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..1777da5 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,76 @@ +ADOC = asciidoctor +ADOC_FLAGS = +SED = sed +HTML_TO_TXT = w3m -dump -T text/html + +MAN_FILES = chrony.conf.man chronyc.man chronyd.man +TXT_FILES = faq.txt installation.txt +HTML_FILES = $(MAN_FILES:%.man=%.html) $(TXT_FILES:%.txt=%.html) +MAN_IN_FILES = $(MAN_FILES:%.man=%.man.in) + +SYSCONFDIR = @SYSCONFDIR@ +BINDIR = @BINDIR@ +SBINDIR = @SBINDIR@ +MANDIR = @MANDIR@ +DOCDIR = @DOCDIR@ +CHRONYRUNDIR = @CHRONYRUNDIR@ +CHRONYVARDIR = @CHRONYVARDIR@ +CHRONY_VERSION = @CHRONY_VERSION@ +DEFAULT_USER = @DEFAULT_USER@ +DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@ +DEFAULT_PID_FILE = @DEFAULT_PID_FILE@ +DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@ + +SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\ + s%\@BINDIR\@%$(BINDIR)%g;\ + s%\@SBINDIR\@%$(SBINDIR)%g;\ + s%\@CHRONY_VERSION\@%$(CHRONY_VERSION)%g;\ + s%\@DEFAULT_HWCLOCK_FILE\@%$(DEFAULT_HWCLOCK_FILE)%g;\ + s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\ + s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\ + s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\ + s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\ + s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;" + +man: $(MAN_FILES) $(MAN_IN_FILES) +html: $(HTML_FILES) +txt: $(TXT_FILES) +docs: man html + +%.html: %.adoc + $(ADOC) $(ADOC_FLAGS) -b html -o - $< | $(SED) -e $(SED_COMMANDS) > $@ + +%.man.in: %.adoc + $(ADOC) $(ADOC_FLAGS) -b manpage -o $@ $< + +%.man: %.man.in + $(SED) -e $(SED_COMMANDS) < $< > $@ + +%.txt: %.html + $(HTML_TO_TXT) < $< > $@ + +install: $(MAN_FILES) + [ -d $(DESTDIR)$(MANDIR)/man1 ] || mkdir -p $(DESTDIR)$(MANDIR)/man1 + [ -d $(DESTDIR)$(MANDIR)/man5 ] || mkdir -p $(DESTDIR)$(MANDIR)/man5 + [ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8 + cp chronyc.man $(DESTDIR)$(MANDIR)/man1/chronyc.1 + chmod 644 $(DESTDIR)$(MANDIR)/man1/chronyc.1 + cp chronyd.man $(DESTDIR)$(MANDIR)/man8/chronyd.8 + chmod 644 $(DESTDIR)$(MANDIR)/man8/chronyd.8 + cp chrony.conf.man $(DESTDIR)$(MANDIR)/man5/chrony.conf.5 + chmod 644 $(DESTDIR)$(MANDIR)/man5/chrony.conf.5 + +install-docs: $(HTML_FILES) + [ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR) + for f in $(HTML_FILES); do \ + cp $$f $(DESTDIR)$(DOCDIR); \ + chmod 644 $(DESTDIR)$(DOCDIR)/$$f; \ + done + +clean: + rm -f $(MAN_FILES) $(TXT_FILES) $(HTML_FILES) + rm -f $(MAN_IN_FILES) + +distclean: + rm -f $(MAN_FILES) $(TXT_FILES) $(HTML_FILES) + rm -f Makefile diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc new file mode 100644 index 0000000..4b1239f --- /dev/null +++ b/doc/chrony.conf.adoc @@ -0,0 +1,2460 @@ +// This file is part of chrony +// +// Copyright (C) Richard P. Curnow 1997-2003 +// Copyright (C) Stephen Wadeley 2016 +// Copyright (C) Bryan Christianson 2017 +// Copyright (C) Miroslav Lichvar 2009-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. + += chrony.conf(5) +:doctype: manpage +:man manual: Configuration Files +:man source: chrony @CHRONY_VERSION@ + +== NAME +chrony.conf - chronyd configuration file + +== SYNOPSIS +*chrony.conf* + +== DESCRIPTION + +This file configures the *chronyd* daemon. The compiled-in location is +_@SYSCONFDIR@/chrony.conf_, but 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. + +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 +configuration file is ignored. + +While the number of supported directives is large, only a few of them are +typically needed. See the <<examples,*EXAMPLES*>> section for configuration in +typical operating scenarios. + +The configuration file might contain comment lines. A comment line is any line +that starts with zero or more spaces followed by any one of the following +characters: *!*, *;*, *#*, *%*. Any line with this format will be ignored. + +== DIRECTIVES + +=== Time sources + +[[server]]*server* _hostname_ [_option_]...:: +The *server* directive specifies an NTP server which can be used as a time +source. The client-server relationship is strictly hierarchical: a client might +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: ++ +*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 -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. 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 -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 or less instead of the interval specified by the +*minpoll* option, which allows *chronyd* to make the first update of the clock +shortly after start. +*burst*::: +With this option, *chronyd* will shorten the interval between up to four +requests to 2 seconds or less 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, +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) +should *chronyd* use to authenticate requests sent to the server and verify its +responses. The server must have the same key for this number configured, +otherwise no relationship between the computers will be possible. ++ +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. +*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 +indicate that the request, or the response, or both were delayed. If only one +of the messages was delayed the measurement error is likely to be substantial. ++ +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). ++ +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* +option. For example, *maxdelay 0.3* would indicate that measurements with a +round-trip delay of 0.3 seconds or more should be ignored. The default value is +3 seconds and the maximum value is 1000 seconds. +*maxdelayratio* _ratio_::: +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. +*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 +the previous measurements that is greater than the specified ratio, it will be +rejected. The default is 10.0. +*mindelay* _delay_::: +This option specifies a fixed minimum round-trip delay to be used instead of +the minimum amongst the previous measurements. This can be useful in networks +with static configuration to improve the stability of corrections for +asymmetric jitter, weighting of the measurements, and the *maxdelayratio* and +*maxdelaydevratio* tests. The value should be set accurately in order to have a +positive effect on the synchronisation. +*asymmetry* _ratio_::: +This option specifies the asymmetry of the network jitter on the path to the +source, which is used to correct the measured offset according to the delay. +The asymmetry can be between -0.5 and +0.5. A negative value means the delay of +packets sent to the source is more variable than the delay of packets sent from +the source back. By default, *chronyd* estimates the asymmetry automatically. +*offset* _offset_::: +This option specifies a correction (in seconds) which will be applied to +offsets measured with this source. It's particularly useful to compensate for a +known asymmetry in network delay or timestamping errors. For example, if +packets sent to the source were on average delayed by 100 microseconds more +than packets sent from the source back, the correction would be -0.00005 (-50 +microseconds). The default is 0.0. +*minsamples* _samples_::: +Set the minimum number of samples kept for this source. This overrides the +<<minsamples,*minsamples*>> directive. +*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 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. (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*::: +Never select this source. This is particularly useful for monitoring. +*trust*::: +Assume time from this source is always true. It can be rejected as a +falseticker in the source selection only if another source with this option +does not agree with it. +*require*::: +Require that at least one of the sources specified with this option is +selectable (i.e. recently reachable and not a falseticker) before updating the +clock. Together with the *trust* option this might be useful to allow a trusted +authenticated source to be safely combined with unauthenticated sources in +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. ++ +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 +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. +<<clientloglimit,*clientloglimit*>> is too small), or it might be overwritten +by other clients that have the same IP address (e.g. computers behind NAT or +someone sending requests with a spoofed source address). ++ +The *xleave* option can be combined with the *presend* option in order to +shorten the interval in which the server has to keep the state to be able to +respond in the interleaved mode. +*polltarget* _target_::: +Target number of measurements to use for the regression algorithm which +*chronyd* will try to maintain by adjusting the polling interval between +*minpoll* and *maxpoll*. A higher target makes *chronyd* prefer shorter polling +intervals. The default is 8 and a useful range is from 6 to 60. +*port* _port_::: +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). +*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 +skewed due to either the client or the server having to do an ARP lookup on the +other party prior to transmitting a packet. This is more of a problem with long +sampling intervals, which might be similar in duration to the lifetime of entries +in the ARP caches of the machines. ++ +In order to avoid this problem, the *presend* option can be used. It takes a +single integer argument, which is the smallest polling interval for which an +extra pair of NTP packets will be exchanged between the client and the server +prior to the actual measurement. For example, with the following option +included in a *server* directive: ++ +---- +presend 9 +---- ++ +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. ++ +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. +*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 +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, +the default version is 4. + +[[pool]]*pool* _name_ [_option_]...:: +The syntax of this directive is similar to that for the <<server,*server*>> +directive, except that it is used to specify a pool of NTP servers rather than +a single NTP server. The pool name is expected to resolve to multiple addresses +which might change over time. ++ +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, +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. ++ +An example of the *pool* directive is ++ +---- +pool pool.ntp.org iburst maxsources 3 +---- + +[[peer]]*peer* _hostname_ [_option_]...:: +The syntax of this directive is identical to that for the <<server,*server*>> +directive, except that it specifies a symmetric association with an NTP peer +instead of a client/server association with an NTP server. A single symmetric +association allows the peers to be both servers and clients to each other. This +is mainly useful when the NTP implementation of the peer (e.g. *ntpd*) supports +ephemeral symmetric associations and does not need to be configured with an +address of this host. *chronyd* does not support ephemeral associations. ++ +When a key is specified by the *key* option to enable authentication, both +peers must use the same key and the same key number. ++ +Note that the symmetric mode is less secure than the client/server mode. A +denial-of-service attack is possible on unauthenticated symmetric associations, +i.e. when the peer was specified without the *key* option. An attacker who does +not see network traffic between two hosts, but knows that they are peering with +each other, can periodically send them unauthenticated packets with spoofed +source addresses in order to disrupt their NTP state and prevent them from +synchronising to each other. When the association is authenticated, an attacker +who does see the network traffic, but cannot prevent the packets from reaching +the other host, can still disrupt the state by replaying old packets. The +attacker has effectively the same power as a man-in-the-middle attacker. A +partial protection against this attack is implemented in *chronyd*, which can +protect the peers if they are using the same polling interval and they never +sent an authenticated packet with a timestamp from future, but it should not be +relied on as it is difficult to ensure the conditions are met. If two hosts +should be able to synchronise to each other in both directions, it is +recommended to use two separate client/server associations (specified by the +<<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. ++ +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 +system clock by stepping before normal operation begins. Since this would +normally be performed only at an appropriate point in the system boot sequence, +no other software should be adversely affected by the step. ++ +If the correction required is less than a specified threshold, a slew is used +instead. This makes it safer to restart *chronyd* whilst the system is in +normal operation. ++ +The *initstepslew* directive takes a threshold and a list of NTP servers as +arguments. Each of the servers is rapidly polled several times, and a majority +voting mechanism used to find the most likely range of system clock error that +is present. A step or slew is applied to the system clock to correct this +error. *chronyd* then enters its normal operating mode. ++ +An example of the use of the directive is: ++ +---- +initstepslew 30 foo.example.net bar.example.net +---- ++ +where 2 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. ++ +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. + +[[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). ++ +There are four drivers included in *chronyd*: ++ +*PPS*::: +Driver for the kernel PPS (pulse per second) API. The parameter is the path to +the PPS device (typically _/dev/pps?_). As PPS refclocks do not supply full +time, another time source (e.g. NTP server or non-PPS refclock) is needed to +complete samples from the PPS refclock. An alternative is to enable the +<<local,*local*>> directive to allow synchronisation with some unknown but +constant offset. The driver supports the following option: ++ +*clear*:::: +By default, the PPS refclock uses assert events (rising edge) for +synchronisation. With this option, it will use clear events (falling edge) +instead. ++ +::: +Examples: ++ +---- +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 +---- ++ +*SHM*::: +NTP shared memory driver. This driver uses a shared memory segment to receive +samples from another process (e.g. *gpsd*). The parameter is the number of the +shared memory segment, typically a small number like 0, 1, 2, or 3. The driver +supports the following option: ++ +*perm*=_mode_:::: +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). +::: ++ +Examples: ++ +---- +refclock SHM 0 poll 3 refid GPS1 +refclock SHM 1:perm=0644 refid GPS2 +---- ++ +*SOCK*::: +Unix domain socket driver. It is similar to the SHM driver, but samples are +received from a Unix domain socket instead of shared memory and the messages +have a different format. The parameter is the path to the socket, which +*chronyd* creates on start. An advantage over the SHM driver is that SOCK does +not require polling and it can receive PPS samples with incomplete time. The +format of the messages is described in the _refclock_sock.c_ file in the chrony +source code. ++ +An application which supports the SOCK protocol is the *gpsd* daemon. The path +where *gpsd* expects the socket to be created is described in the *gpsd(8)* man +page. For example: ++ +---- +refclock SOCK /var/run/chrony.ttyS0.sock +---- ++ +*PHC*::: +PTP hardware clock (PHC) driver. The parameter is the path to the device of +the PTP clock which should be used as a time source. If the clock is kept in +TAI instead of UTC (e.g. it is synchronised by a PTP daemon), the current +UTC-TAI offset needs to be specified by the *offset* option. Alternatively, the +*pps* refclock option can be enabled to treat the PHC as a PPS refclock, using +only the sub-second offset for synchronisation. The driver supports the +following options: ++ +*nocrossts*:::: +This option disables use of precise cross timestamping. +*extpps*:::: +This option enables a PPS mode in which the PTP clock is timestamping pulses +of an external PPS signal connected to the clock. The clock does not need to be +synchronised, but another time source is needed to complete the PPS samples. +Note that some PTP clocks cannot be configured to timestamp only assert or +clear events, and it is necessary to use the *width* option to filter wrong +PPS samples. +*pin*=_index_:::: +This option specifies the index of the pin to which is connected the PPS +signal. The default value is 0. +*channel*=_index_:::: +This option specifies the index of the channel for the PPS mode. The default +value is 0. +*clear*:::: +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. +::: ++ +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 +---- ++ +:: +The *refclock* directive supports the following options: ++ +*poll* _poll_::: +Timestamps produced by refclock drivers are not used immediately, but they are +stored and processed by a median filter in the polling interval specified by +this option. This is defined as a power of 2 and can be negative to specify a +sub-second interval. The default is 4 (16 seconds). A shorter interval allows +*chronyd* to react faster to changes in the frequency of the system clock, but +it might have a negative effect on its accuracy if the samples have a lot of +jitter. +*dpoll* _dpoll_::: +Some drivers do not listen for external events and try to produce samples in +their own polling interval. This is defined as a power of 2 and can be negative +to specify a sub-second interval. The default is 0 (1 second). +*refid* _refid_::: +This option is used to specify the reference ID of the refclock, as up to four +ASCII characters. The default reference ID is composed from the first three +characters of the driver name and the number of the refclock. Each refclock +must have a unique reference ID. +*lock* _refid_::: +This option can be used to lock a PPS refclock to another refclock, which is +specified by its reference ID. In this mode received PPS samples are paired +directly with raw samples from the specified refclock. +*rate* _rate_::: +This option sets the rate of the pulses in the PPS signal (in Hz). This option +controls how the pulses will be completed with real time. To actually receive +more than one pulse per second, a negative *dpoll* has to be specified (-3 for +a 5Hz signal). The default is 1. +*maxlockage* _pulses_::: +This option specifies in number of pulses how old can be samples from the +refclock specified by the *lock* option to be paired with the pulses. +Increasing this value is useful when the samples are produced at a lower rate +than the pulses. The default is 2. +*width* _width_::: +This option specifies the width of the pulses (in seconds). It is used to +filter PPS samples when the driver provides samples for both rising and falling +edges. Note that it reduces the maximum allowed error of the time source which +completes the PPS samples. If the duty cycle is configurable, 50% should be +preferred in order to maximise the allowed error. +*pps*::: +This options forces *chronyd* to treat any refclock (e.g. SHM or PHC) as a PPS +refclock. This can be useful when the refclock provides time with a variable +offset of a whole number of seconds (e.g. it uses TAI instead of UTC). Another +time source is needed to complete samples from the refclock. +*offset* _offset_::: +This option can be used to compensate for a constant error. The specified +offset (in seconds) is applied to all samples produced by the reference clock. +The default is 0.0. +*delay* _delay_::: +This option sets the NTP delay of the source (in seconds). Half of this value +is included in the maximum assumed error which is used in the source selection +algorithm. Increasing the delay is useful to avoid having no majority in the +source selection or to make it prefer other sources. The default is 1e-9 (1 +nanosecond). +*stratum* _stratum_::: +This option sets the NTP stratum of the refclock. This can be useful when the +refclock provides time with a stratum other than 0. The default is 0. +*precision* _precision_::: +This option sets the precision of the reference clock (in seconds). The default +value is the estimated precision of the system clock. +*maxdispersion* _dispersion_::: +Maximum allowed dispersion for filtered samples (in seconds). Samples with +larger estimated dispersion are ignored. By default, this limit is disabled. +*filter* _samples_::: +This option sets the length of the median filter which is used to reduce the +noise in the measurements. With each poll about 40 percent of the stored +samples are discarded and one final sample is calculated as an average of the +remaining samples. If the length is 4 or more, at least 4 samples have to be +collected between polls. For lengths below 4, the filter has to be full. The +default is 64. +*prefer*::: +Prefer this source over sources without the prefer option. +*noselect*::: +Never select this source. This is useful for monitoring or with sources which +are not very accurate, but are locked with a PPS refclock. +*trust*::: +Assume time from this source is always true. It can be rejected as a +falseticker in the source selection only if another source with this option +does not agree with it. +*require*::: +Require that at least one of the sources specified with this option is +selectable (i.e. recently reachable and not a falseticker) before updating the +clock. Together with the *trust* option this can be useful to allow a trusted, +but not very precise, reference clock to be safely combined with +unauthenticated NTP sources in 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. +*tai*::: +This option indicates that the reference clock keeps time in TAI instead of UTC +and that *chronyd* should correct its offset by the current TAI-UTC offset. The +<<leapsectz,*leapsectz*>> directive must be used with this option and the +database must be kept up to date in order for this correction to work as +expected. This option does not make sense with PPS refclocks. +*minsamples* _samples_::: +Set the minimum number of samples kept for this source. This overrides the +<<minsamples,*minsamples*>> directive. +*maxsamples* _samples_::: +Set the maximum number of samples kept for this source. This overrides the +<<maxsamples,*maxsamples*>> directive. + +[[manual]]*manual*:: +The *manual* directive enables support at run-time for the +<<chronyc.adoc#settime,*settime*>> command in *chronyc*. If no *manual* +directive is included, any attempt to use the *settime* command in *chronyc* +will be met with an error message. ++ +Note that the *settime* command can be enabled at run-time using +the <<chronyc.adoc#manual,*manual*>> command in *chronyc*. (The idea of the two +commands is that the *manual* command controls the manual clock driver's +behaviour, whereas the *settime* command allows samples of manually entered +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. ++ +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 +NTP packets. ++ +An example of the *acquisitionport* directive is: ++ +---- +acquisitionport 1123 +---- ++ +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. + +[[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*>> +directives. ++ +For each of the IPv4 and IPv6 protocols, only one *bindacqaddress* directive +can be specified. + +[[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. ++ +All supported systems, with the exception of macOS 10.12 and earlier, have +operating system support for setting the rate of gain or loss to compensate for +known errors. +(On macOS 10.12 and earlier, *chronyd* must simulate such a capability by +periodically slewing the system clock forwards or backwards by a suitable amount +to compensate for the error built up since the previous slew.) ++ +For such systems, it is possible to save the measurement history across +restarts of *chronyd* (assuming no changes are made to the system clock +behaviour whilst it is not running). The *dumpdir* directive defines the +directory where the measurement histories are saved when *chronyd* exits, +or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued. ++ +An example of the directive is: ++ +---- +dumpdir @CHRONYRUNDIR@ +---- ++ +A source whose IP address is _1.2.3.4_ would have its measurement history saved +in the file _@CHRONYRUNDIR@/1.2.3.4.dat_. History of reference clocks is saved +to files named by their reference ID in form of _refid:XXXXXXXX.dat_. + +[[maxsamples]]*maxsamples* _samples_:: +The *maxsamples* directive sets the default maximum 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 0, which disables the configurable limit. The +useful range is 4 to 64. + +[[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. + +=== Source selection + +[[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 +frequencies of the system clock relative to the other sources, however, can be +combined with the selected source to improve the accuracy of the system clock. ++ +The *combinelimit* directive limits which sources are included in the combining +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. ++ +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. ++ +By default, the maximum root distance is 3 seconds. ++ +Setting *maxdistance* to a larger value can be useful to allow synchronisation +with a server that only has a very infrequent connection to its sources and can +accumulate a large dispersion between updates of its clock. + +[[maxjitter]]*maxjitter* _jitter_:: +The *maxjitter* directive sets the maximum allowed jitter of the sources to not +be rejected by the source selection algorithm. This prevents synchronisation +with sources that have a small root distance, but their time is too variable. ++ +By default, the maximum jitter is 1 second. + +[[minsources]]*minsources* _sources_:: +The *minsources* directive sets the minimum number of sources that need to be +considered as selectable in the source selection algorithm before the local +clock is updated. The default value is 1. ++ +Setting this option to a larger number can be used to improve the reliability. +More sources will have to agree with each other and the clock will not be +updated when only one source (which could be serving incorrect time) is +reachable. + +[[reselectdist]]*reselectdist* _distance_:: +When *chronyd* selects a synchronisation source from available sources, it +will prefer the one with the shortest synchronisation distance. However, to +avoid frequent reselecting when there are sources with similar distance, a +fixed distance is added to the distance for sources that are currently not +selected. This can be set with the *reselectdist* directive. By default, the +distance is 100 microseconds. + +[[stratumweight]]*stratumweight* _distance_:: +The *stratumweight* directive sets how much distance should be added per +stratum to the synchronisation distance when *chronyd* selects the +synchronisation source from available sources. ++ +By default, the weight is 0.001 seconds. This means that the stratum of the sources +in the selection process matters only when the differences between the +distances are in milliseconds. + +=== System clock + +[[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 +systems, with the exception of macOS 12 and earlier, this rate can be +controlled. ++ +The *corrtimeratio* directive sets the ratio between the duration in which the +clock is slewed for an average correction according to the source history and +the interval in which the corrections are done (usually the NTP polling +interval). Corrections larger than the average take less time and smaller +corrections take more time, the amount of the correction and the correction +time are inversely proportional. ++ +Increasing *corrtimeratio* improves the overall frequency error of the system +clock, but increases the overall time error as the corrections take longer. ++ +By default, the ratio is set to 3, the time accuracy of the clock is preferred +over its frequency accuracy. ++ +The maximum allowed slew rate can be set by the <<maxslewrate,*maxslewrate*>> +directive. The current remaining correction is shown in the +<<chronyc.adoc#tracking,*tracking*>> report as the *System time* value. + +[[driftfile]]*driftfile* _file_:: +One of the main activities of the *chronyd* program is to work out the rate at +which the system clock gains or loses time relative to real time. ++ +Whenever *chronyd* computes a new value of the gain or loss rate, it is desirable +to record it somewhere. This allows *chronyd* to begin compensating the system +clock at that rate whenever it is restarted, even before it has had a chance to +obtain an equally good estimate of the rate during the new run. (This process +can take many minutes, at least.) ++ +The *driftfile* directive allows a file to be specified into which *chronyd* +can store the rate information. Two parameters are recorded in the file. The +first is the rate at which the system clock gains or loses time, expressed in +parts per million, with gains positive. Therefore, a value of 100.0 indicates +that when the system clock has advanced by a second, it has gained 100 +microseconds in reality (so the true time has only advanced by 999900 +microseconds). The second is an estimate of the error bound around the first +value in which the true rate actually lies. ++ +An example of the driftfile directive is: ++ +---- +driftfile @CHRONYVARDIR@/drift +---- + +[[fallbackdrift]]*fallbackdrift* _min-interval_ _max-interval_:: +Fallback drifts are long-term averages of the system clock drift calculated +over exponentially increasing intervals. They are used to avoid quickly +drifting away from true time when the clock was not updated for a longer period +of time and there was a short-term deviation in the drift before the updates +stopped. ++ +The directive specifies the minimum and maximum interval since the last clock +update to switch between fallback drifts. They are defined as a power of 2 (in +seconds). The syntax is as follows: ++ +---- +fallbackdrift 16 19 +---- ++ +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 +hours after last clock update, to the second after 36 hours, and so on. This +might be a good setting to cover frequency changes due to daily and weekly +temperature fluctuations. When the frequency is set to a fallback, the state of +the clock will change to '`Not synchronised`'. ++ +By default (or if the specified maximum or minimum is 0), no fallbacks are used +and the clock frequency changes only with new measurements from NTP sources, +reference clocks, or manual input. + +[[leapsecmode]]*leapsecmode* _mode_:: +A leap second is an adjustment that is occasionally applied to UTC to keep it +close to the mean solar time. When a leap second is inserted, the last day of +June or December has an extra second 23:59:60. ++ +For computer clocks that is a problem. The Unix time is defined as number of +seconds since 00:00:00 UTC on 1 January 1970 without leap seconds. The system +clock cannot have time 23:59:60, every minute has 60 seconds and every day has +86400 seconds by definition. The inserted leap second is skipped and the clock +is suddenly ahead of UTC by one second. The *leapsecmode* directive selects how +that error is corrected. There are four options: ++ +*system*::: +When inserting a leap second, the kernel steps the system clock backwards by +one second when the clock gets to 00:00:00 UTC. When deleting a leap second, it +steps forward by one second when the clock gets to 23:59:59 UTC. This is the +default mode when the system driver supports leap seconds (i.e. all supported +systems with the exception of macOS 12 and earlier). +*step*::: +This is similar to the *system* mode, except the clock is stepped by +*chronyd* instead of the kernel. It can be useful to avoid bugs in the kernel +code that would be executed in the *system* mode. This is the default mode +when the system driver does not support leap seconds. +*slew*::: +The clock is corrected by slewing started at 00:00:00 UTC when a leap second +is inserted or 23:59:59 UTC when a leap second is deleted. This might be +preferred over the *system* and *step* modes when applications running on the +system are sensitive to jumps in the system time and it is acceptable that the +clock will be off for a longer time. On Linux with the default +<<maxslewrate,*maxslewrate*>> value the correction takes 12 seconds. +*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. +:: ++ +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 +slightly different rates when it is necessary to keep them close together, the +*slew* mode can be combined with the <<smoothtime,*smoothtime*>> directive to +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 +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 +leap second in exactly the same way for synchronisation. ++ +This feature must be used carefully, because the server is intentionally not +serving its best estimate of the true time. ++ +A recommended configuration to enable a server leap smear is: ++ +---- +leapsecmode slew +maxslewrate 1000 +smoothtime 400 0.001 leaponly +---- ++ +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 +*leaponly* option makes the duration of the leap smear constant and allows the +clients to safely synchronise with multiple identically configured leap +smearing servers. + +[[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 +_right/UTC_ timezone. ++ +When a leap second is announced, the timezone needs to be updated at least 12 +hours before the leap second. It is not necessary to restart *chronyd*. ++ +This directive is useful with reference clocks and other time sources which do +not announce leap seconds, or announce them too late for an NTP server to +forward them to its own clients. Clients of leap smearing servers must not +use this directive. ++ +It is also useful when the system clock is required to have correct TAI-UTC +offset. Note that the offset is set only when leap seconds are handled by the +kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*. ++ +The specified timezone is not used as an exclusive source of information about +leap seconds. If a majority of time sources announce on the last day of June or +December that a leap second should be inserted or deleted, it will be accepted +even if it is not included in the timezone. ++ +An example of the directive is: ++ +---- +leapsectz right/UTC +---- ++ +The following shell command verifies that the timezone contains leap seconds +and can be used with this directive: ++ +---- +$ TZ=right/UTC date -d 'Dec 31 2008 23:59:60' +Wed Dec 31 23:59:60 UTC 2008 +---- + +[[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. ++ +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). ++ +This is particularly useful when using reference clocks, because the +<<initstepslew,*initstepslew*>> directive works only with NTP sources. ++ +An example of the use of this directive is: ++ +---- +makestep 0.1 3 +---- ++ +This would step the system clock if the adjustment is larger than 0.1 seconds, but +only in the first three clock updates. + +[[maxchange]]*maxchange* _offset_ _start_ _ignore_:: +This directive sets the maximum allowed offset corrected on a clock update. The +check is performed only after the specified number of updates to allow a large +initial adjustment of the system clock. When an offset larger than the +specified maximum occurs, it will be ignored for the specified number of times +and then *chronyd* will give up and exit (a negative value can be used to never +exit). In both cases a message is sent to syslog. ++ +An example of the use of this directive is: ++ +---- +maxchange 1000 1 2 +---- ++ +After the first clock update, *chronyd* will check the offset on every clock +update, it will ignore two adjustments larger than 1000 seconds and exit on +another one. + +[[maxclockerror]]*maxclockerror* _error-in-ppm_:: +The *maxclockerror* directive sets the maximum assumed frequency error that the +system clock can gain on its own between clock updates. It describes the +stability of the clock. ++ +By default, the maximum error is 1 ppm. ++ +Typical values for _error-in-ppm_ might be 10 for a low quality clock and 0.1 +for a high quality clock using a temperature compensated crystal oscillator. + +[[maxdrift]]*maxdrift* _drift-in-ppm_:: +This directive specifies the maximum assumed drift (frequency error) of the +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). ++ +By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is +limited by the system driver rather than this directive. + +[[maxupdateskew]]*maxupdateskew* _skew-in-ppm_:: +One of *chronyd*'s tasks is to work out how fast or slow the computer's clock +runs relative to its reference sources. In addition, it computes an estimate of +the error bounds around the estimated value. ++ +If the range of error is too large, it probably indicates that the measurements +have not settled down yet, and that the estimated gain or loss rate is not very +reliable. ++ +The *maxupdateskew* directive sets the threshold for determining whether an +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. ++ +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 +gain or loss rate, and the error bound on the estimate. When a new estimate is +generated following another measurement from one of the sources, a weighted +combination algorithm is used to update the master estimate. So if *chronyd* +has an existing highly-reliable master estimate and a new estimate is generated +which has large error bounds, the existing master estimate will dominate in the +new master estimate. + +[[maxslewrate]]*maxslewrate* _rate-in-ppm_:: +The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed +to slew the time. It limits the slew rate controlled by the correction time +ratio (which can be set by the <<corrtimeratio,*corrtimeratio*>> directive) and +is effective only on systems where *chronyd* is able to control the rate (i.e. +all supported systems with the exception of macOS 12 or earlier). ++ +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, +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]] +*tempcomp* _file_ _interval_ _T0_ _k0_ _k1_ _k2_:: +*tempcomp* _file_ _interval_ _points-file_:: +Normally, changes in the rate of drift of the system clock are caused mainly by +changes in the temperature of the crystal oscillator on the motherboard. ++ +If there are temperature measurements available from a sensor close to the +oscillator, the *tempcomp* directive can be used to compensate for the changes +in the temperature and improve the stability and accuracy of the clock. ++ +The result depends on many factors, including the resolution of the sensor, the +amount of noise in the measurements, the polling interval of the time source, +the compensation update interval, how well the compensation is specified, and +how close the sensor is to the oscillator. When it is working well, the +frequency reported in the _tracking.log_ file is more stable and the maximum +reached offset is smaller. ++ +There are two forms of the directive. The first one has six parameters: a path +to the file containing the current temperature from the sensor (in text +format), the compensation update interval (in seconds), and temperature +coefficients _T0_, _k0_, _k1_, _k2_. ++ +The frequency compensation is calculated (in ppm) as ++ +---- +k0 + (T - T0) * k1 + (T - T0)^2 * k2 +---- ++ +The result has to be between -10 ppm and 10 ppm, otherwise the measurement is +considered invalid and will be ignored. The _k0_ coefficient can be adjusted to +keep the compensation in that range. ++ +An example of the use is: ++ +---- +tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 26000 0.0 0.000183 0.0 +---- ++ +The measured temperature will be read from the file in the Linux sysfs +filesystem every 30 seconds. When the temperature is 26000 (26 degrees +Celsius), the frequency correction will be zero. When it is 27000 (27 degrees +Celsius), the clock will be set to run faster by 0.183 ppm, etc. ++ +The second form has three parameters: the path to the sensor file, the update +interval, and a path to a file containing a list of (temperature, compensation) +points, from which the compensation is linearly interpolated or extrapolated. ++ +An example is: ++ +---- +tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 /etc/chrony.tempcomp +---- ++ +where the _/etc/chrony.tempcomp_ file could have ++ +---- +20000 1.0 +21000 0.64 +22000 0.36 +23000 0.16 +24000 0.04 +25000 0.0 +26000 0.04 +27000 0.16 +28000 0.36 +29000 0.64 +30000 1.0 +---- ++ +Valid measurements with corresponding compensations are logged to the +_tempcomp.log_ file if enabled by the <<log,*log tempcomp*>> directive. + +=== NTP server + +[[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. ++ +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. ++ +Examples of the use of the directive are as follows: ++ +---- +allow 1.2.3.4 +allow 1.2 +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). ++ +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, +consider the two examples: ++ +---- +allow 1.2.3.4 +deny 1.2.3 +allow 1.2 +---- ++ +and ++ +---- +allow 1.2.3.4 +deny 1.2.3 +allow all 1.2 +---- ++ +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. ++ +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. ++ +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). ++ +Note, if the <<initstepslew,*initstepslew*>> directive is used in the +configuration file, each of the computers listed in that directive must allow +client access by this computer for it to work. + +[[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. ++ +The syntax is identical. ++ +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. ++ +An example of the use of the directive is: ++ +---- +bindaddress 192.168.1.1 +---- ++ +Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress* +directive can be specified. Therefore, it is not useful on computers which +should serve NTP on multiple network interfaces. + +[[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. ++ +The syntax is as follows: ++ +---- +broadcast 30 192.168.1.255 +broadcast 60 192.168.2.255 12123 +broadcast 60 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 +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 +*chronyd* is running. ++ +You can have more than 1 *broadcast* directive if you have more than 1 network +interface onto which you want to send NTP broadcast packets. ++ +*chronyd* itself cannot act as a broadcast client; it must always be configured +as a point-to-point client by defining specific NTP servers and peers. This +broadcast server feature is intended for providing a time source to other NTP +implementations. ++ +If *ntpd* is used as the broadcast client, it will try to measure the +round-trip delay between the server and client with normal client mode packets. +Thus, the broadcast subnet should also be the subject of an <<allow,*allow*>> +directive. + +[[clientloglimit]]*clientloglimit* _limit_:: +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. ++ +An example of the use of this directive is: ++ +---- +clientloglimit 1048576 +---- + +[[noclientlog]]*noclientlog*:: +This directive, which takes no arguments, specifies that client accesses are +not to be logged. Normally they are logged, allowing statistics to be reported +using the <<chronyc.adoc#clients,*clients*>> command in *chronyc*. This option +also effectively disables server support for the NTP interleaved mode. + +[[local]]*local* [_option_]...:: +The *local* directive enables a local reference mode, which allows *chronyd* +operating as an NTP server to appear synchronised to real time (from the +viewpoint of clients polling it), even when it was never synchronised or +the last update of the clock happened a long time ago. ++ +This directive is normally used in an isolated network, where computers are +required to be synchronised to one another, but not necessarily to real time. +The server can be kept vaguely in line with real time by manual input. ++ +The *local* directive has the following options: ++ +*stratum* _stratum_::: +This option sets the stratum of the server which will be reported to clients +when the local reference is active. The specified value is in the range 1 +through 15, and the default value is 10. It should be larger than the maximum +expected stratum in the network when external NTP servers are accessible. ++ +Stratum 1 indicates a computer that has a true real-time reference directly +connected to it (e.g. GPS, atomic clock, etc.), such computers are expected to +be very close to real time. Stratum 2 computers are those which have a stratum +1 server; stratum 3 computers have a stratum 2 server and so on. A value +of 10 indicates that the clock is so many hops away from a reference clock that +its time is fairly unreliable. +*distance* _distance_::: +This option sets the threshold for the root distance which will activate the local +reference. If *chronyd* was synchronised to some source, the local reference +will not be activated until its root distance reaches the specified value (the +rate at which the distance is increasing depends on how well the clock was +tracking the source). The default value is 1 second. ++ +The current root distance can be calculated from root delay and root dispersion +(reported by the <<chronyc.adoc#tracking,*tracking*>> command in *chronyc*) as: ++ +---- +distance = delay / 2 + dispersion +---- +*orphan*::: +This option enables a special '`orphan`' mode, where sources with stratum equal +to the local _stratum_ are assumed to not serve real time. They are ignored +unless no other source is selectable and their reference IDs are smaller than +the local reference ID. ++ +This allows multiple servers in the network to use the same *local* +configuration and to be synchronised to one another, without confusing clients +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. ++ +The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the +*tos orphan* command). +:: ++ +An example of the directive is: ++ +---- +local stratum 10 orphan +---- + +[[ntpsigndsocket]]*ntpsigndsocket* _directory_:: +This directive specifies the location of the Samba *ntp_signd* socket when it +is running as a Domain Controller (DC). If *chronyd* is compiled with this +feature, responses to MS-SNTP clients will be signed by the *smbd* daemon. ++ +Note that MS-SNTP requests are not authenticated and any client that is allowed +to access the server by the <<allow,*allow*>> directive, or the +<<chronyc.adoc#allow,*allow*>> command in *chronyc*, can get an MS-SNTP +response signed with a trust account's password and try to crack the password +in a brute-force attack. Access to the server should be carefully controlled. ++ +An example of the directive is: ++ +---- +ntpsigndsocket /var/lib/samba/ntp_signd +---- + +[[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 +<<allow,*allow*>> directive or the <<chronyc.adoc#allow,*allow*>> command in +*chronyc*, an NTP peer is configured, or the broadcast server mode is enabled. ++ +The default value is 123, the standard NTP port. If set to 0, *chronyd* will +never open the server port and will operate strictly in a client-only mode. The +source port used in NTP client requests can be set by the +<<acquisitionport,*acquisitionport*>> directive. + +[[ratelimit]]*ratelimit* [_option_]...:: +This directive enables response rate limiting for NTP packets. Its purpose is +to reduce network traffic with misconfigured or broken NTP clients that are +polling the server too frequently. The limits are applied to individual IP +addresses. If multiple clients share one IP address (e.g. multiple hosts behind +NAT), the sum of their traffic will be limited. If a client that increases its +polling rate when it does not receive a reply is detected, its rate limiting +will be temporarily suspended to avoid increasing the overall amount of +traffic. The maximum number of IP addresses which can be monitored at the same +time depends on the memory limit set by the <<clientloglimit,*clientloglimit*>> +directive. ++ +The *ratelimit* directive supports a number of options (which can be defined +in any order): ++ +*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*::: +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*::: +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 +source address from completely blocking responses to that address. The leak +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. +:: ++ +An example use of the directive is: ++ +---- +ratelimit interval 1 burst 16 +---- ++ +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 *leak* of 2). + +[[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 +their clocks close together even when large offset or frequency corrections are +applied to the server's clock, for example after being offline for a longer +time. ++ +BE WARNED: The server is intentionally not serving its best estimate of the +true time. If a large offset has been accumulated, it can take a very long time +to smooth it out. This directive should be used only when the clients are not +configured to also poll another NTP server, because they could reject this +server as a falseticker or fail to select a source completely. ++ +The smoothing process is implemented with a quadratic spline function with two +or three pieces. It is independent from any slewing applied to the local system +clock, but the accumulated offset and frequency will be reset when the clock is +corrected by stepping, e.g. by the <<makestep,*makestep*>> directive or the +<<chronyc.adoc#makestep,*makestep*>> command in *chronyc*. The process can be +reset without stepping the clock by the <<chronyc.adoc#smoothtime,*smoothtime +reset*>> command. ++ +The first two arguments of the directive are the maximum frequency offset of +the smoothed time to the tracked NTP time (in ppm) and the maximum rate at +which the frequency offset is allowed to change (in ppm per second). *leaponly* +is an optional third argument which enables a mode where only leap seconds are +smoothed out and normal offset and frequency changes are ignored. The *leaponly* +option is useful in a combination with the <<leapsecmode,*leapsecmode slew*>> +directive to allow the clients to use multiple time smoothing servers safely. ++ +The smoothing process is activated automatically when 1/10000 of the estimated +skew of the local clock falls below the maximum rate of frequency change. It +can be also activated manually by the <<chronyc.adoc#smoothtime,*smoothtime +activate*>> command, which is particularly useful when the clock is +synchronised only with manual input and the skew is always larger than the +threshold. The <<chronyc.adoc#smoothing,*smoothing*>> command can be used to +monitor the process. ++ +An example suitable for clients using *ntpd* and 1024 second polling interval +could be: ++ +---- +smoothtime 400 0.001 +---- ++ +An example suitable for clients using *chronyd* on Linux could be: ++ +---- +smoothtime 50000 0.01 +---- + +=== 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 +by *chronyc*). On systems other than Linux, the address of the interface needs +to be already configured when *chronyd* is started. ++ +This directive can also change the path of the Unix domain command socket, +which is used by *chronyc* to send configuration commands. The socket must be +in a directory that is accessible only by the root or _chrony_ user. The +directory will be created on start if it does not exist. The compiled-in default +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: ++ +---- +bindcmdaddress 0.0.0.0 +bindcmdaddress :: +---- ++ +to the configuration file. ++ +For each of the IPv4, IPv6, and Unix domain protocols, only one +*bindcmdaddress* directive can be specified. ++ +An example that sets the path of the Unix domain command socket is: ++ +---- +bindcmdaddress /var/run/chrony/chronyd.sock +---- + +[[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 +host. (By '`monitoring access`' is meant that *chronyc* can be run on those +hosts and retrieve monitoring data from *chronyd* on this computer.) ++ +The syntax is identical to the *allow* directive. ++ +There is also a *cmdallow all* directive with similar behaviour to the *allow +all* directive (but applying to monitoring access in this case, of course). ++ +Note that *chronyd* has to be configured with the +<<bindcmdaddress,*bindcmdaddress*>> directive to not listen only on the +loopback interface to actually allow remote access. + +[[cmddeny]]*cmddeny* [*all*] [_subnet_]:: +This is similar to the <<cmdallow,*cmdallow*>> directive, except that it denies +monitoring access to a particular subnet or host, rather than allowing it. ++ +The syntax is identical. ++ +There is also a *cmddeny all* directive with similar behaviour to the *cmdallow +all* directive. + +[[cmdport]]*cmdport* _port_:: +The *cmdport* directive allows the port that is used for run-time monitoring +(via the *chronyc* program) to be altered from its default (323). If set to 0, +*chronyd* will not open the port, this is useful to disable *chronyc* +access from the Internet. (It does not disable the Unix domain command socket.) ++ +An example shows the syntax: ++ +---- +cmdport 257 +---- ++ +This would make *chronyd* use UDP 257 as its command port. (*chronyc* would +need to be run with the *-p 257* switch to inter-operate correctly.) + +[[cmdratelimit]]*cmdratelimit* [_option_]...:: +This directive enables response rate limiting for command packets. It is +similar to the <<ratelimit,*ratelimit*>> directive, except responses to +localhost are never limited and the default interval is -4 (16 packets per +second). ++ +An example of the use of the directive is: ++ +---- +cmdratelimit interval 2 +---- + +=== Real-time clock (RTC) + +[[hwclockfile]]*hwclockfile* _file_:: +The *hwclockfile* directive sets the location of the adjtime file which is +used by the *hwclock* program on Linux. *chronyd* parses the file to find out +if the RTC keeps local time or UTC. It overrides the <<rtconutc,*rtconutc*>> +directive. ++ +The compiled-in default value is '_@DEFAULT_HWCLOCK_FILE@_'. ++ +An example of the directive is: ++ +---- +hwclockfile /etc/adjtime +---- + +[[rtcautotrim]]*rtcautotrim* _threshold_:: +The *rtcautotrim* directive is used to keep the RTC close to the system clock +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. ++ +This directive is effective only with the <<rtcfile,*rtcfile*>> directive. ++ +An example of the use of this directive is: ++ +---- +rtcautotrim 30 +---- ++ +This would set the threshold error to 30 seconds. + +[[rtcdevice]]*rtcdevice* _device_:: +The *rtcdevice* directive sets the path to the device file for accessing the +RTC. The default path is _@DEFAULT_RTC_DEVICE@_. + +[[rtcfile]]*rtcfile* _file_:: +The *rtcfile* directive defines the name of the file in which *chronyd* can +save parameters associated with tracking the accuracy of the RTC. ++ +An example of the directive is: ++ +---- +rtcfile @CHRONYVARDIR@/rtc +---- ++ +*chronyd* saves information in this file when it exits and when the *writertc* +command is issued in *chronyc*. The information saved is the RTC's error at +some epoch, that epoch (in seconds since January 1 1970), and the rate at which +the RTC gains or loses time. ++ +So far, the support for real-time clocks is limited; their code is even more +system-specific than the rest of the software. You can only use the RTC +facilities (the <<rtcfile,*rtcfile*>> directive and the *-s* command-line +option to *chronyd*) if the following three conditions apply: ++ +. You are running Linux. +. The kernel is compiled with extended real-time clock support (i.e. the + _/dev/rtc_ device is capable of doing useful things). +. You do not have other applications that need to make use of _/dev/rtc_ at all. + +[[rtconutc]]*rtconutc*:: +*chronyd* assumes by default that the RTC keeps local time (including any +daylight saving changes). This is convenient on PCs running Linux which are +dual-booted with Windows. ++ +If you keep the RTC on local time and your computer is off when daylight saving +(summer time) starts or ends, the computer's system time will be one hour in +error when you next boot and start chronyd. ++ +An alternative is for the RTC to keep Universal Coordinated Time (UTC). This +does not suffer from the 1 hour problem when daylight saving starts or ends. ++ +If the *rtconutc* directive appears, it means the RTC is required to keep UTC. +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. + +[[rtcsync]]*rtcsync*:: +The *rtcsync* directive enables a mode where the system time is periodically +copied to the RTC and *chronyd* does not try to track its drift. This directive +cannot be used with the <<rtcfile,*rtcfile*>> directive. ++ +On Linux, the RTC copy is performed by the kernel every 11 minutes. ++ +On macOS, <<chronyd,*chronyd*>> will perform the RTC copy every 60 minutes +when the system clock is in a synchronised state. ++ +On other systems this directive does nothing. + +=== Logging + +[[log]]*log* [_option_]...:: +The *log* directive indicates that certain information is to be logged. +The log files are written to the directory specified by the <<logdir,*logdir*>> +directive. A banner is periodically written to the files to indicate the +meanings of the columns. ++ +*rawmeasurements*::: +This option logs the raw NTP measurements and related information to a file +called _measurements.log_. An entry is made for each packet received from the +source. This can be useful when debugging a problem. An example line (which +actually appears as a single line in the file) from the log file is shown +below. ++ +---- +2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \ + -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 CB00717B 4B D K +---- ++ +The columns are as follows (the quantities in square brackets are the values +from the example line above): ++ +. Date [2015-10-13] +. Hour:Minute:Second. Note that the date-time pair is expressed in UTC, not the + local time zone. [05:40:50] +. IP address of server or peer from which measurement came [203.0.113.15] +. Leap status (_N_ means normal, _+_ means that the last minute of the current + month has 61 seconds, _-_ means that the last minute of the month has 59 + seconds, _?_ means the remote computer is not currently synchronised.) [N] +. Stratum of remote computer. [2] +. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111] +. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111] +. Tests for maximum delay, maximum delay ratio and maximum delay dev ratio, + against defined parameters, and a test for synchronisation loop (1=pass, + 0=fail) [1111] +. Local poll [10] +. Remote poll [10] +. '`Score`' (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 + measurements currently being used for the regression algorithm). [1.0] +. The estimated local clock error (_theta_ in RFC 5905). Positive indicates + that the local clock is slow of the remote source. [-4.966e-03] +. The peer delay (_delta_ in RFC 5905). [2.296e-01] +. The peer dispersion (_epsilon_ in RFC 5905). [1.577e-05] +. The root delay (_DELTA_ in RFC 5905). [1.615e-01] +. The root dispersion (_EPSILON_ in RFC 5905). [7.446e-03] +. Reference ID of the server's source as a hexadecimal number. [CB00717B] +. NTP mode of the received packet (_1_=active peer, _2_=passive peer, + _4_=server, _B_=basic, _I_=interleaved). [4B] +. Source of the local transmit timestamp + (_D_=daemon, _K_=kernel, _H_=hardware). [D] +. Source of the local receive timestamp + (_D_=daemon, _K_=kernel, _H_=hardware). [K] ++ +*measurements*::: +This option is identical to the *rawmeasurements* option, except it logs only +valid measurements from synchronised sources, i.e. measurements which passed +the RFC 5905 tests 1 through 7. This can be useful for producing graphs of the +source's performance. ++ +*statistics*::: +This option logs information about the regression processing to a file called +_statistics.log_. An example line (which actually appears as a single line in +the file) from the log file is shown below. ++ +---- +2016-08-10 05:40:50 203.0.113.15 6.261e-03 -3.247e-03 \ + 2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8 0.00 +---- ++ +The columns are as follows (the quantities in square brackets are the values +from the example line above): ++ +. Date [2015-07-22] +. Hour:Minute:Second. Note that the date-time pair is expressed in + UTC, not the local time zone. [05:40:50] +. IP address of server or peer from which measurement comes [203.0.113.15] +. The estimated standard deviation of the measurements from the source (in + seconds). [6.261e-03] +. The estimated offset of the source (in seconds, positive means the local + clock is estimated to be fast, in this case). [-3.247e-03] +. The estimated standard deviation of the offset estimate (in seconds). + [2.220e-03] +. 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 + is gaining). This is relative to the compensation currently being applied to + the local clock, _not_ to the local clock without any compensation. + [1.874e-06] +. The estimated error in the rate value (in seconds per second). [1.080e-06]. +. 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] +. The number of measurements currently being used for the regression + algorithm. [16] +. 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 + linear model). [0, i.e. no samples discarded this time] +. 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 + no longer represented well by a linear model and that some older samples need + to be discarded. The number of runs for the data that is being retained is + tabulated. Values of approximately half the number of samples are expected. + [8] +. 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 + between -0.5 and +0.5. A negative value means the delay of packets sent to + the source is more variable than the delay of packets sent from the source + back. [0.00, i.e. no correction for asymmetry] ++ +*tracking*::: +This option logs changes to the estimate of the system's gain or loss rate, and +any slews made, to a file called _tracking.log_. An example line (which +actually appears as a single line in the file) from the log file is shown +below. ++ +---- +2017-08-22 13:22:36 203.0.113.15 2 -3.541 0.075 -8.621e-06 N \ + 2 2.940e-03 -2.084e-04 1.534e-02 3.472e-04 8.304e-03 +---- ++ +The columns are as follows (the quantities in square brackets are the +values from the example line above) : ++ +. Date [2017-08-22] +. Hour:Minute:Second. Note that the date-time pair is expressed in UTC, not the + local time zone. [13:22:36] +. The IP address of the server or peer to which the local system is synchronised. + [203.0.113.15] +. The stratum of the local system. [2] +. The local system frequency (in ppm, positive means the local system runs fast + of UTC). [-3.541] +. The error bounds on the frequency (in ppm). [0.075] +. 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 + UTC). [-8.621e-06] +. Leap status (_N_ means normal, _+_ means that the last minute of this month + has 61 seconds, _-_ means that the last minute of the month has 59 seconds, + _?_ means the clock is not currently synchronised.) [N] +. The number of combined sources. [2] +. The estimated standard deviation of the combined offset (in seconds). + [2.940e-03] +. The remaining offset correction from the previous update (in seconds, + positive means the system clock is slow of UTC). [-2.084e-04] +. The total of the network path delays to the reference clock to which + the local clock is ultimately synchronised (in seconds). [1.534e-02] +. The total dispersion accumulated through all the servers back to the + reference clock to which the local clock is ultimately synchronised + (in seconds). [3.472e-04] +. The maximum estimated error of the system clock in the interval since the + previous update (in seconds). It includes the offset, remaining offset + correction, root delay, and dispersion from the previous update with the + dispersion which accumulated in the interval. [8.304e-03] ++ +*rtc*::: +This option logs information about the system's real-time clock. An example +line (which actually appears as a single line in the file) from the _rtc.log_ +file is shown below. ++ +---- +2015-07-22 05:40:50 -0.037360 1 -0.037434\ + -37.948 12 5 120 +---- ++ +The columns are as follows (the quantities in square brackets are the +values from the example line above): ++ +. Date [2015-07-22] +. Hour:Minute:Second. Note that the date-time pair is expressed in UTC, not the + local time zone. [05:40:50] +. 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]. +. Flag indicating whether the regression has produced valid coefficients. + (1 for yes, 0 for no). [1] +. Offset at the current time predicted by the regression process. A large + difference between this value and the measured offset tends to indicate that + the measurement is an outlier with a serious measurement error. [-0.037434] +. 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. + [-37.948] +. The number of measurements used in the regression. [12] +. 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 + and that older measurements should be discarded. [5] +. The measurement interval used prior to the measurement being made (in + seconds). [120] ++ +*refclocks*::: +This option logs the raw and filtered reference clock measurements to a file +called _refclocks.log_. An example line (which actually appears as a single +line in the file) from the log file is shown below. ++ +---- +2009-11-30 14:33:27.000000 PPS2 7 N 1 4.900000e-07 -6.741777e-07 1.000e-06 +---- ++ +The columns are as follows (the quantities in square brackets are the values +from the example line above): ++ +. Date [2009-11-30] +. Hour:Minute:Second.Microsecond. Note that the date-time pair is expressed in + UTC, not the local time zone. [14:33:27.000000] +. Reference ID of the reference clock from which the measurement came. [PPS2] +. Sequence number of driver poll within one polling interval for raw samples, + or _-_ for filtered samples. [7] +. Leap status (_N_ means normal, _+_ means that the last minute of the current + month has 61 seconds, _-_ means that the last minute of the month has 59 + seconds). [N] +. Flag indicating whether the sample comes from PPS source. (1 for yes, + 0 for no, or _-_ for filtered sample). [1] +. Local clock error measured by reference clock driver, or _-_ for filtered sample. + [4.900000e-07] +. Local clock error with applied corrections. Positive indicates that the local + clock is slow. [-6.741777e-07] +. Assumed dispersion of the sample. [1.000e-06] ++ +*tempcomp*::: +This option logs the temperature measurements and system rate compensations to +a file called _tempcomp.log_. An example line (which actually appears as a +single line in the file) from the log file is shown below. ++ +---- +2015-04-19 10:39:48 2.8000e+04 3.6600e-01 +---- ++ +The columns are as follows (the quantities in square brackets are the values +from the example line above): ++ +. Date [2015-04-19] +. Hour:Minute:Second. Note that the date-time pair is expressed in UTC, not the + local time zone. [10:39:48] +. Temperature read from the sensor. [2.8000e+04] +. Applied compensation in ppm, positive means the system clock is running + faster than it would be without the compensation. [3.6600e-01] ++ +:: +An example of the directive is: ++ +---- +log measurements statistics tracking +---- + +[[logbanner]]*logbanner* _entries_:: +A banner is periodically written to the log files enabled by the <<log,*log*>> +directive to indicate the meanings of the columns. ++ +The *logbanner* directive specifies after how many entries in the log file +should be the banner written. The default is 32, and 0 can be used to disable +it entirely. + +[[logchange]]*logchange* _threshold_:: +This directive sets the threshold for the adjustment of the system clock that +will generate a syslog message. Clock errors detected via NTP packets, +reference clocks, or timestamps entered via the +<<chronyc.adoc#settime,*settime*>> command of *chronyc* are logged. ++ +By default, the threshold is 1 second. ++ +An example of the use is: ++ +---- +logchange 0.1 +---- ++ +which would cause a syslog message to be generated if a system clock error of over +0.1 seconds starts to be compensated. + +[[logdir]]*logdir* _directory_:: +This directive allows the directory where log files are written to be +specified. ++ +An example of the use of this directive is: ++ +---- +logdir /var/log/chrony +---- + +[[mailonchange]]*mailonchange* _email_ _threshold_:: +This directive defines an email address to which mail should be sent if +*chronyd* applies a correction exceeding a particular threshold to the system +clock. ++ +An example of the use of this directive is: ++ +---- +mailonchange root@localhost 0.5 +---- ++ +This would send a mail message to root if a change of more than 0.5 seconds +were applied to the system clock. ++ +This directive cannot be used when a system call filter is enabled by the *-F* +option as the *chronyd* process will not be allowed to fork and execute the +sendmail binary. + +=== Miscellaneous + +[[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 +(NIC) uses its own clock to accurately timestamp the actual transmissions and +receptions, avoiding processing and queueing delays in the kernel, network +driver, and hardware. This can significantly improve the accuracy of the +timestamps and the measured offset, which is used for synchronisation of the +system clock. In order to get the best results, both sides receiving and +sending NTP packets (i.e. server and client, or two peers) need to use HW +timestamping. If the server or peer supports the interleaved mode, it needs to +be enabled by the *xleave* option in the <<server,*server*>> or the +<<peer,*peer*>> directive. ++ +This directive is supported on Linux 3.19 and newer. The NIC must support HW +timestamping, which can be verified with the *ethtool -T* command. The list of +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 +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. ++ +If the kernel supports software timestamping, it will be enabled for all +interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is +indicated in the _measurements.log_ file if enabled by the <<log,*log +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. ++ +The *hwtimestamp* directive has the following options: ++ +*minpoll* _poll_::: +This option specifies the minimum interval between readings of the NIC clock. +It's defined as a power of two. It should correspond to the minimum polling +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). +*txcomp* _compensation_::: +This option specifies the difference in seconds between the actual transmission +time at the physical layer and the reported transmit timestamp. This value will +be added to transmit timestamps obtained from the NIC. The default value is 0. +*rxcomp* _compensation_::: +This option specifies the difference in seconds between the reported receive +timestamp and the actual reception time at the physical layer. This value will +be subtracted from receive timestamps obtained from the NIC. The default value +is 0. +*nocrossts*::: +Some hardware can precisely cross timestamp the NIC clock with the system +clock. This option disables the use of the cross timestamping. +*rxfilter* _filter_::: +This option selects the receive timestamping filter. The _filter_ can be one of +the following: +_all_:::: +Enables timestamping of all received packets. +_ntp_:::: +Enables timestamping of received NTP 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). +:: ++ +Examples of the directive are: ++ +---- +hwtimestamp eth0 +hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9 +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. ++ +The format of the directive is shown in the example below: ++ +---- +keyfile @SYSCONFDIR@/chrony.keys +---- ++ +The argument is simply the name of the file containing the ID-key pairs. The +format of the file is shown below: ++ +---- +10 tulip +11 hyacinth +20 MD5 ASCII:crocus +25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c + ... +---- ++ +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. ++ +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*. ++ +The password can be specified as a string of 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. ++ +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. ++ +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. + +[[pidfile]]*pidfile* _file_:: +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. ++ +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. ++ +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. + +[[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. +The child process retains root privileges, but can only perform a very limited +range of privileged system calls on behalf of the parent. ++ +The compiled-in default value is _@DEFAULT_USER@_. + +[[examples]] +== EXAMPLES + +=== NTP client with permanent connection to NTP servers + +This section shows how to configure *chronyd* for computers that are connected +to the Internet (or to any network containing true NTP servers which ultimately +derive their time from a reference clock) permanently or most of the time. + +To operate in this mode, you will need to know the names of the NTP servers +you want to use. You might be able to find names of suitable servers by one of +the following methods: + +* Your institution might already operate servers on its network. + Contact your system administrator to find out. +* Your ISP probably has one or more NTP servers available for its + customers. +* 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 + 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. + +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: + +---- +server foo.example.net +server bar.example.net +server baz.example.net +---- + +However, you will probably want to include some of the other directives. The +<<driftfile,*driftfile*>>, <<makestep,*makestep*>> and <<rtcsync,*rtcsync*>> +might be particularly useful. Also, the *iburst* option of the +<<server,*server*>> directive is useful to speed up the initial +synchronisation. The smallest useful configuration file would look something +like: + +---- +server foo.example.net iburst +server bar.example.net iburst +server baz.example.net iburst +driftfile @CHRONYVARDIR@/drift +makestep 1.0 3 +rtcsync +---- + +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 <<pool,*pool*>> +directive instead of multiple *server* directives. The configuration file could +in this case look like: + +---- +pool pool.ntp.org iburst +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 +occasional connections to NTP servers. In this case, you will need some +additional configuration to tell *chronyd* when the connection goes up and +down. This saves the program from continuously trying to poll the servers when +they are inaccessible. + +Again, assuming that your NTP servers are called _foo.example.net_, +_bar.example.net_ and _baz.example.net_, your _chrony.conf_ file would now +contain: + +---- +server foo.example.net offline +server bar.example.net offline +server baz.example.net offline +driftfile @CHRONYVARDIR@/drift +makestep 1.0 3 +rtcsync +---- + +The *offline* keyword indicates that the servers start in an offline state, and +that they should not be contacted until *chronyd* receives notification from +*chronyc* that the link to the Internet is present. To tell *chronyd* when to +start and finish sampling the servers, the <<chronyc.adoc#online,*online*>> and +<<chronyc.adoc#offline,*offline*>> commands of *chronyc* need to be used. + +To give an example of their use, assuming that *pppd* is the program being +used to connect to the Internet and that *chronyc* has been installed at +_@BINDIR@/chronyc_, the script _/etc/ppp/ip-up_ would include: + +---- +@BINDIR@/chronyc online +---- + +and the script _/etc/ppp/ip-down_ would include: + +---- +@BINDIR@/chronyc offline +---- + +*chronyd*'s polling of the servers would now only occur whilst the machine is +actually connected to the Internet. + +=== 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 +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. + +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, +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 +itself to a '`majority-vote`' of selected clients' times; this allows the +clients to '`flywheel`' the master 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 +<<chronyc.adoc#settime,*settime*>> command. The smoothing process needs to be +activated by the <<chronyc.adoc#smoothtime,*smoothtime activate*>> command when +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): + +---- +initstepslew 1 client1 client3 client6 +driftfile @CHRONYVARDIR@/drift +local stratum 8 +manual +allow 192.168.165.0/24 +smoothtime 400 0.01 +rtcsync +---- + +For the clients that have to resynchronise the master when it restarts, +the configuration file might be: + +---- +server master iburst +driftfile @CHRONYVARDIR@/drift +allow 192.168.165.0/24 +makestep 1.0 3 +rtcsync +---- + +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. + +A configuration file for the first server might be (assuming there are three +servers called _master1_, _master2_, and _master3_): + +---- +initstepslew 1 master2 master3 +server master2 +server master3 +driftfile @CHRONYVARDIR@/drift +local stratum 8 orphan +manual +allow 192.168.165.0/24 +rtcsync +---- + +The other servers would be the same, except the hostnames in the *initstepslew* +and *server* directives would be modified to specify the other servers. Their +clients might be configured to poll all three servers. + +=== RTC tracking + +This section considers a computer which has occasional connections to the +Internet and is turned off between '`sessions`'. In this case, *chronyd* relies +on the computer's RTC to maintain the time between the periods when it is +powered up. It assumes that Linux is run exclusively on the computer. Dual-boot +systems might work; it depends what (if anything) the other system does to the +RTC. On 2.6 and later kernels, if your motherboard has a HPET, you will need to +enable the *HPET_EMULATE_RTC* option in your kernel configuration. Otherwise, +*chronyd* will not be able to interact with the RTC device and will give up +using it. + +When the computer is connected to the Internet, *chronyd* has access to +external NTP servers which it makes measurements from. These measurements are +saved, and straight-line fits are performed on them to provide an estimate of +the computer's time error and rate of gaining or losing time. + +When the computer is taken offline from the Internet, the best estimate of the +gain or loss rate is used to free-run the computer until it next goes online. + +Whilst the computer is running, *chronyd* makes measurements of the RTC (via +the _/dev/rtc_ interface, which must be compiled into the kernel). An estimate +is made of the RTC error at a particular RTC second, and the rate at which the +RTC gains or loses time relative to true time. + +When the computer is powered down, the measurement histories for all the NTP +servers are saved to files, and the RTC tracking information is also +saved to a file (if the <<rtcfile,*rtcfile*>> directive has been specified). +These pieces of information are also saved if the <<chronyc.adoc#dump,*dump*>> +and <<chronyc.adoc#writertc,*writertc*>> commands respectively are issued +through *chronyc*. + +When the computer is rebooted, *chronyd* reads the current RTC time and the RTC +information saved at the last shutdown. This information is used to set the +system clock to the best estimate of what its time would have been now, had it +been left running continuously. The measurement histories for the servers are +then reloaded. + +The next time the computer goes online, the previous sessions' measurements can +contribute to the line-fitting process, which gives a much better estimate of +the computer's gain or loss rate. + +One problem with saving the measurements and RTC data when the machine is shut +down is what happens if there is a power failure; the most recent data will not +be saved. Although *chronyd* is robust enough to cope with this, some +performance might be lost. (The main danger arises if the RTC has been changed +during the session, with the *trimrtc* command in *chronyc*. Because of this, +*trimrtc* will make sure that a meaningful RTC file is saved after the +change is completed). + +The easiest protection against power failure is to put the *dump* and +*writertc* commands in the same place as the *offline* command is issued to +take *chronyd* offline; because *chronyd* free-runs between online sessions, no +parameters will change significantly between going offline from the Internet +and any power failure. + +A final point regards computers which are left running for extended periods and +where it is desired to spin down the hard disc when it is not in use (e.g. when +not accessed for 15 minutes). *chronyd* has been planned so it supports such +operation; this is the reason why the RTC tracking parameters are not saved to +disc after every update, but only when the user requests such a write, or +during the shutdown sequence. The only other facility that will generate +periodic writes to the disc is the *log rtc* facility in the configuration +file; this option should not be used if you want your disc to spin down. + +To illustrate how a computer might be configured for this case, example +configuration files are shown. + +For the _chrony.conf_ file, the following can be used as an example. + +---- +server foo.example.net maxdelay 0.4 offline +server bar.example.net maxdelay 0.4 offline +server baz.example.net maxdelay 0.4 offline +logdir /var/log/chrony +log statistics measurements tracking +driftfile @CHRONYVARDIR@/drift +makestep 1.0 3 +maxupdateskew 100.0 +dumpdir @CHRONYVARDIR@ +rtcfile @CHRONYVARDIR@/rtc +---- + +*pppd* is used for connecting to the Internet. This runs two scripts +_/etc/ppp/ip-up_ and _/etc/ppp/ip-down_ when the link goes online and offline +respectively. + +The relevant part of the _/etc/ppp/ip-up_ file is: + +---- +@BINDIR@/chronyc online +---- + +and the relevant part of the _/etc/ppp/ip-down_ script is: + +---- +@BINDIR@/chronyc -m offline dump writertc +---- + +*chronyd* is started during the boot sequence with the *-r* and *-s* 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 *chronyd*'s +configuration file. + +For the system shutdown, *chronyd* should receive a SIGTERM several seconds +before the final SIGKILL; the SIGTERM causes the measurement histories and RTC +information to be saved. + +=== 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 +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 +hardware reference clock (e.g. a GPS receiver), it can be specified by the +<<refclock,*refclock*>> directive. + +The amount of memory used for logging client accesses can be increased in order +to enable clients to use the interleaved mode even when the server has a large +number of clients, and better support rate limiting if it is enabled by the +<<ratelimit,*ratelimit*>> directive. The system timezone database, if it is +kept up to date and includes the _right/UTC_ timezone, can be used as a +reliable source to determine when a leap second will be applied to UTC. The +*-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which +*chronyd* will not be able to serve time to its clients when it needs to be +restarted (e.g. after upgrading to a newer version, or a change in the +configuration). + +The configuration file could look like: + +---- +server foo.example.net iburst +server bar.example.net iburst +server baz.example.net iburst +server qux.example.net iburst +makestep 1.0 3 +rtcsync +allow +clientloglimit 100000000 +leapsectz right/UTC +driftfile @CHRONYVARDIR@/drift +dumpdir @CHRONYRUNDIR@ +---- + +== SEE ALSO + +<<chronyc.adoc#,*chronyc(1)*>>, <<chronyd.adoc#,*chronyd(8)*>> + +== BUGS + +For instructions on how to report bugs, please visit +https://chrony.tuxfamily.org/. + +== AUTHORS + +chrony was written by Richard Curnow, Miroslav Lichvar, and others. diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in new file mode 100644 index 0000000..d50e825 --- /dev/null +++ b/doc/chrony.conf.man.in @@ -0,0 +1,3981 @@ +'\" t +.\" Title: chrony.conf +.\" Author: [see the "AUTHORS" section] +.\" Generator: Asciidoctor 1.5.6.1 +.\" Date: 2018-09-19 +.\" Manual: Configuration Files +.\" Source: chrony @CHRONY_VERSION@ +.\" Language: English +.\" +.TH "CHRONY.CONF" "5" "2018-09-19" "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 +.. +.if \n[.g] .mso www.tmac +.LINKSTYLE blue R < > +.SH "NAME" +chrony.conf \- chronyd configuration file +.SH "SYNOPSIS" +.sp +\fBchrony.conf\fP +.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 +\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. +.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 +configuration file is ignored. +.sp +While the number of supported directives is large, only a few of them are +typically needed. See the \fBEXAMPLES\fP section for configuration in +typical operating scenarios. +.sp +The configuration file might contain comment lines. A comment line is any line +that starts with zero or more spaces followed by any one of the following +characters: \fB!\fP, \fB;\fP, \fB#\fP, \fB%\fP. Any line with this format will be ignored. +.SH "DIRECTIVES" +.SS "Time sources" +.sp +\fBserver\fP \fIhostname\fP [\fIoption\fP]... +.RS 4 +The \fBserver\fP directive specifies an NTP server which can be used as a time +source. The client\-server relationship is strictly hierarchical: a client might +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: +.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 \-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. 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 +.RS 4 +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 \-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 or less 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. +.RE +.sp +\fBburst\fP +.RS 4 +With this option, \fBchronyd\fP will shorten the interval between up to four +requests to 2 seconds or less 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, +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) +should \fBchronyd\fP use to authenticate requests sent to the server and verify its +responses. The server must have the same key for this number configured, +otherwise no relationship between the computers will be possible. +.sp +If the server is running \fBntpd\fP and the output size of the hash function used +by the key is longer than 160 bits (e.g. SHA256), the \fBversion\fP option needs to +be set to 4 for compatibility. +.RE +.sp +\fBmaxdelay\fP \fIdelay\fP +.RS 4 +\fBchronyd\fP uses the network round\-trip delay to the server to determine how +accurate a particular measurement is likely to be. Long round\-trip delays +indicate that the request, or the response, or both were delayed. If only one +of the messages was delayed the measurement error is likely to be substantial. +.sp +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). +.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 +option. For example, \fBmaxdelay 0.3\fP would indicate that measurements with a +round\-trip delay of 0.3 seconds or more should be ignored. The default value is +3 seconds and the maximum value is 1000 seconds. +.RE +.sp +\fBmaxdelayratio\fP \fIratio\fP +.RS 4 +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. +.RE +.sp +\fBmaxdelaydevratio\fP \fIratio\fP +.RS 4 +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 +the previous measurements that is greater than the specified ratio, it will be +rejected. The default is 10.0. +.RE +.sp +\fBmindelay\fP \fIdelay\fP +.RS 4 +This option specifies a fixed minimum round\-trip delay to be used instead of +the minimum amongst the previous measurements. This can be useful in networks +with static configuration to improve the stability of corrections for +asymmetric jitter, weighting of the measurements, and the \fBmaxdelayratio\fP and +\fBmaxdelaydevratio\fP tests. The value should be set accurately in order to have a +positive effect on the synchronisation. +.RE +.sp +\fBasymmetry\fP \fIratio\fP +.RS 4 +This option specifies the asymmetry of the network jitter on the path to the +source, which is used to correct the measured offset according to the delay. +The asymmetry can be between \-0.5 and +0.5. A negative value means the delay of +packets sent to the source is more variable than the delay of packets sent from +the source back. By default, \fBchronyd\fP estimates the asymmetry automatically. +.RE +.sp +\fBoffset\fP \fIoffset\fP +.RS 4 +This option specifies a correction (in seconds) which will be applied to +offsets measured with this source. It\(cqs particularly useful to compensate for a +known asymmetry in network delay or timestamping errors. For example, if +packets sent to the source were on average delayed by 100 microseconds more +than packets sent from the source back, the correction would be \-0.00005 (\-50 +microseconds). The default is 0.0. +.RE +.sp +\fBminsamples\fP \fIsamples\fP +.RS 4 +Set the minimum number of samples kept for this source. This overrides the +\fBminsamples\fP directive. +.RE +.sp +\fBmaxsamples\fP \fIsamples\fP +.RS 4 +Set the maximum number of samples kept for this source. This overrides the +\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 +option can be specified. \fBchronyd\fP will not try to poll the server until it is +enabled to do so (by using the \fBonline\fP command in +\fBchronyc\fP). +.RE +.sp +\fBauto_offline\fP +.RS 4 +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. (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 +.RS 4 +Prefer this source over sources without the \fBprefer\fP option. +.RE +.sp +\fBnoselect\fP +.RS 4 +Never select this source. This is particularly useful for monitoring. +.RE +.sp +\fBtrust\fP +.RS 4 +Assume time from this source is always true. It can be rejected as a +falseticker in the source selection only if another source with this option +does not agree with it. +.RE +.sp +\fBrequire\fP +.RS 4 +Require that at least one of the sources specified with this option is +selectable (i.e. recently reachable and not a falseticker) before updating the +clock. Together with the \fBtrust\fP option this might be useful to allow a trusted +authenticated source to be safely combined with unauthenticated sources in +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. +.RE +.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. +.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 +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. +\fBclientloglimit\fP is too small), or it might be overwritten +by other clients that have the same IP address (e.g. computers behind NAT or +someone sending requests with a spoofed source address). +.sp +The \fBxleave\fP option can be combined with the \fBpresend\fP option in order to +shorten the interval in which the server has to keep the state to be able to +respond in the interleaved mode. +.RE +.sp +\fBpolltarget\fP \fItarget\fP +.RS 4 +Target number of measurements to use for the regression algorithm which +\fBchronyd\fP will try to maintain by adjusting the polling interval between +\fBminpoll\fP and \fBmaxpoll\fP. A higher target makes \fBchronyd\fP prefer shorter polling +intervals. The default is 8 and a useful range is from 6 to 60. +.RE +.sp +\fBport\fP \fIport\fP +.RS 4 +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). +.RE +.sp +\fBpresend\fP \fIpoll\fP +.RS 4 +If the timing measurements being made by \fBchronyd\fP are the only network data +passing between two computers, you might find that some measurements are badly +skewed due to either the client or the server having to do an ARP lookup on the +other party prior to transmitting a packet. This is more of a problem with long +sampling intervals, which might be similar in duration to the lifetime of entries +in the ARP caches of the machines. +.sp +In order to avoid this problem, the \fBpresend\fP option can be used. It takes a +single integer argument, which is the smallest polling interval for which an +extra pair of NTP packets will be exchanged between the client and the server +prior to the actual measurement. For example, with the following option +included in a \fBserver\fP directive: +.sp +.if n \{\ +.RS 4 +.\} +.nf +presend 9 +.fi +.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. +.RE +.sp +\fBminstratum\fP \fIstratum\fP +.RS 4 +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 +known to be unreliable or inaccurate and which should be used only when other +sources are unreachable. +.RE +.sp +\fBversion\fP \fIversion\fP +.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, +the default version is 4. +.RE +.RE +.sp +\fBpool\fP \fIname\fP [\fIoption\fP]... +.RS 4 +The syntax of this directive is similar to that for the \fBserver\fP +directive, except that it is used to specify a pool of NTP servers rather than +a single NTP server. The pool name is expected to resolve to multiple addresses +which might change over time. +.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, +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. +.sp +An example of the \fBpool\fP directive is +.sp +.if n \{\ +.RS 4 +.\} +.nf +pool pool.ntp.org iburst maxsources 3 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +\fBpeer\fP \fIhostname\fP [\fIoption\fP]... +.RS 4 +The syntax of this directive is identical to that for the \fBserver\fP +directive, except that it specifies a symmetric association with an NTP peer +instead of a client/server association with an NTP server. A single symmetric +association allows the peers to be both servers and clients to each other. This +is mainly useful when the NTP implementation of the peer (e.g. \fBntpd\fP) supports +ephemeral symmetric associations and does not need to be configured with an +address of this host. \fBchronyd\fP does not support ephemeral associations. +.sp +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 +Note that the symmetric mode is less secure than the client/server mode. A +denial\-of\-service attack is possible on unauthenticated symmetric associations, +i.e. when the peer was specified without the \fBkey\fP option. An attacker who does +not see network traffic between two hosts, but knows that they are peering with +each other, can periodically send them unauthenticated packets with spoofed +source addresses in order to disrupt their NTP state and prevent them from +synchronising to each other. When the association is authenticated, an attacker +who does see the network traffic, but cannot prevent the packets from reaching +the other host, can still disrupt the state by replaying old packets. The +attacker has effectively the same power as a man\-in\-the\-middle attacker. A +partial protection against this attack is implemented in \fBchronyd\fP, which can +protect the peers if they are using the same polling interval and they never +sent an authenticated packet with a timestamp from future, but it should not be +relied on as it is difficult to ensure the conditions are met. If two hosts +should be able to synchronise to each other in both directions, it is +recommended to use two separate client/server associations (specified by the +\fBserver\fP directive on both hosts) instead. +.RE +.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. +.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 |