diff --git a/chrony.conf b/chrony.conf new file mode 100644 index 0000000..e9876c4 --- /dev/null +++ b/chrony.conf @@ -0,0 +1,38 @@ +# Use public servers from the pool.ntp.org project. +# Please consider joining the pool (http://www.pool.ntp.org/join.html). +pool 2.centos.pool.ntp.org iburst + +# Record the rate at which the system clock gains/losses time. +driftfile /var/lib/chrony/drift + +# Allow the system clock to be stepped in the first three updates +# if its offset is larger than 1 second. +makestep 1.0 3 + +# Enable kernel synchronization of the real-time clock (RTC). +rtcsync + +# Enable hardware timestamping on all interfaces that support it. +#hwtimestamp * + +# Increase the minimum number of selectable sources required to adjust +# the system clock. +#minsources 2 + +# Allow NTP client access from local network. +#allow 192.168.0.0/16 + +# Serve time even if not synchronized to a time source. +#local stratum 10 + +# Specify file containing keys for NTP authentication. +keyfile /etc/chrony.keys + +# Get TAI-UTC offset and leap seconds from the system tz database. +leapsectz right/UTC + +# Specify directory for log files. +logdir /var/log/chrony + +# Select which information is logged. +#log measurements statistics tracking diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/.gitignore b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/.gitignore deleted file mode 100644 index f87df3c..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/*.o -/.deps -/clknetsim -/clknetsim.so -/tests* diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/COPYING b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/COPYING deleted file mode 100644 index d511905..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - 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. - - - Copyright (C) - - 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. - - , 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. diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/Makefile b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/Makefile deleted file mode 100644 index 23c77d2..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -CC ?= gcc -CXX ?= g++ -CFLAGS += -O2 -Wall -g -fPIC -CXXFLAGS += $(CFLAGS) - -all: clknetsim.so clknetsim - -clientobjs = client.o -serverobjs = $(patsubst %.cc,%.o,$(wildcard *.cc)) - -clknetsim.so: $(clientobjs) - $(CC) $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) -ldl -lm - -clknetsim: $(serverobjs) - $(CXX) $(CFLAGS) -o $@ $^ $(LDFLAGS) - -clean: - rm -rf server *.so *.o core.* .deps - -.deps: - @mkdir .deps - -.deps/%.d: %.c .deps - @$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@ - -.deps/%.D: %.cc .deps - @$(CXX) -MM $(CPPFLAGS) -MT '$(<:%.cc=%.o) $@' $< -o $@ - --include $(clientobjs:%.o=.deps/%.d) $(serverobjs:%.o=.deps/%.D) diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/README b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/README deleted file mode 100644 index 0b22b5a..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/README +++ /dev/null @@ -1,217 +0,0 @@ -Clock and Network Simulator (clknetsim) -======================================= - -clknetsim is a tool designed to test programs which synchronize the system -clock, either over network or from a hardware reference clock. It simulates a -system or a number of systems connected to each other in a network and the -tested programs discipline the simulated system clocks. It can be used to -quickly test how well the programs control the system clocks in various -conditions or to test the network protocols. - -The tested programs are not modified in order to be included in the simulation, -but they have some system calls redirected by a clknetsim library, which is -loaded by the LD_PRELOAD feature of the dynamic linker, to a clknetsim server, -which runs the simulation and collects several statistics about each client. -The server and the clients run on a single host, they communicate via a UNIX -domain socket. The simulation runs as fast as the host system is capable of, -with two simulated systems it is usually three or four orders of magnitude -faster than real time. - -Supported programs: -- chronyd and chronyc from chrony (http://chrony.tuxfamily.org/) -- ntpd, ntpdate, sntp and ntpq from ntp (http://www.ntp.org/) -- ntpd from busybox (http://www.busybox.net/) -- ptp4l, phc2sys, pmc and nsm from linuxptp (http://linuxptp.sourceforge.net/) - -Limitations: -- only Linux is supported -- the fake system calls implement only a minimal functionality required to - keep the supported clients working -- the simulated system clock advances only on select(), poll() or usleep() - calls, this means the client sees the CPU as infinitely fast -- adjtimex() frequency and tick changes happen immediately, the kernel has - infinite HZ -- adjtime() and PLL updates happen in one second intervals in the simulated - time instead of the uncorrected simulated system time, all clocks are updated - at the same time - - -Usage ------ - -The clknetsim server is started with two required arguments, the first one is -path to a configuration file describing the network and clocks and the second -argument is the number of simulated nodes. The simulation is started when all -clients are connected. - -The clients are started under a non-root user, with preloaded clknetsim.so and -the environment variable CLKNETSIM_NODE set to the number of the client. -Optionally, the environment variable CLKNETSIM_SOCKET can be set to the path of -the UNIX domain socket which is used to connect to the server, clknetsim.sock -in current directory is used by default. The CLKNETSIM_START_DATE variable can -be used to specify in seconds since 1970 when should the simulated time start, -1262304000 by default (2010-01-01 0:00 UTC). The CLKNETSIM_CONNECT_TIMEOUT -variable sets the server connection timeout, 10 seconds by default. - -The simulated network is available to the clients as one or more Ethernet -networks with IPv4 addressing. All nodes have interfaces to all networks. -Their addresses are 192.168.122+s.n, where n is the number of the node -(starting at 1) and s is the number of the network (starting at 1). The -broadcast addresses are 192.168.122+s.255. - -At the end of the simulation clock and network statistics are printed. -clknetsim has options which can be used to control for how long the -simulation should run, or if the frequency, offset or network log should be -written. clknetsim -h prints a complete list of available options. - -A minimal example how to start a simulation: - -$ LD_PRELOAD=./clknetsim.so CLKNETSIM_NODE=1 chronyd -d -f chrony.conf & -$ LD_PRELOAD=./clknetsim.so CLKNETSIM_NODE=2 ntpd -n -c ntp.conf & -$ ./clknetsim -o log.offset -l 100000 clknetsim.conf 2 - -clknetsim.conf: -node2_freq = (sum (* 1e-8 (normal))) -node1_delay2 = (+ 1e-1 (* 1e-3 (exponential))) -node2_delay1 = (+ 1e-1 (* 1e-3 (exponential))) - -chrony.conf: -pidfile chronyd.pid -local stratum 1 -allow - -ntp.conf: -pidfile ntpd.pid -server 192.168.123.1 - -The clknetsim.bash file contains bash functions which can create the -configuration in several network settings, start the simulation, stop the -clients and process the results. The examples subdirectory contains an example -script for each supported client. The above example can be written in a bash -script as: - -CLKNETSIM_PATH=. -. ./clknetsim.bash - -generate_config1 2 0.0 "(sum (* 1e-8 (normal)))" "(+ 1e-1 (* 1e-3 (exponential)))" -start_client 1 chrony "local stratum 1" -start_client 2 ntp "server 192.168.123.1" -start_server 2 -o log.offset -l 100000 - -cat tmp/stats - - -Configuration file ------------------- - -The configuration file is a text file containing a list of assignments, each -specified on a separate line, and comments using # as delimiter. Each node has -several variables, which configure the system clock, the reference clock and -the network delays to other nodes in the network. They can be set either to an -integer value, a floating-point value or a number generating expression written -in a Lisp-style syntax. - -Variables: -- nodeX_freq = float | expr - the system clock frequency error in terms of gained seconds per second of - simulated time, if an expression is specified, the expression is evaluated and - frequency updated once per simulated second (or at the rate specified with - the -R option), the allowed range is (-0.2, 0.2), the default is 0 -- nodeX_delayY = expr - the network delay for packets sent from node X to node Y in seconds, the - expression is evaluated for each sent packet, a negative value means the - packet will be dropped, there is no default (packets are dropped) -- nodeX_offset = float - the initial time error of the system clock in seconds, the default is 0 -- nodeX_start = float - the time in seconds when will be the node started, the default is 0 -- nodeX_refclock = expr - the reference clock time error in seconds, the clock can be accessed by the - client via shared memory (NTP SHM protocol) or as a PTP hardware clock (PHC) - via the clock_gettime() function, there is no default (the clock is disabled) -- nodeX_step = expr - the extra time step applied once per second (or at the rate specified with - the -R option) in seconds, there is no default (no extra steps are applied) -- nodeX_shift_pll = integer - kernel PLL parameter, the default is 2 -- nodeX_pll_clamp = 1 | 0 - kernel PLL parameter, the default is 0 -- nodeX_fll_mode2 = 1 | 0 - kernel FLL parameter, the default is 0 - -Functions and their parameters supported in the expressions: - (* [expr | float] ...) - multiplication - (+ [expr | float] ...) - addition - (% [expr | float] ...) - modulo - (sum [expr | float] ...) - - summation over consecutive evaluation of parameters - (uniform) - random number generator with standard uniform - distribution - (normal) - random number generator with standard normal - distribution - (exponential) - random number generator with exponential distribution - (lambda = 1) - (poisson lambda) - random number generator with poisson distribution - (file "datafile") - number generator reading floating-point values from - the specified file in an inifinite loop - (pulse high low) - pulse wave generator - (sine period) - sine wave generator - (cosine period) - cosine wave generator - (triangle period) - triangle wave generator - (equal epsilon [expr | float] ...) - - returns 1.0 if the values of all parameters are - equal within epsilon, 0.0 otherwise - (max [expr | float] ...) - - returns maximum value - (min [expr | float] ...) - - returns minimum value - -Variables available in network delay expressions: - time - current network time - from - number of the sending node - to - number of the receiving node - port - receiving port number - length - length of the packet - subnet - number of the Ethernet network in which - the packet was sent - -An example: - -# node1 is an NTP server, it has an accurate and absolutely stable clock -node1_offset = 0 -node1_freq = 0 - -# node2 is an NTP client, it starts with 0.1s offset and has -# 0.01ppm/s frequency wander -node2_offset = 0.1 -node2_freq = (sum (* 1e-8 (normal))) - -# network delays between the two nodes have 10ms mean and 100us -# jitter in both directions -node1_delay2 = (+ 9.9e-3 (* 100e-6 (exponential))) -node2_delay1 = (+ 9.9e-3 (* 100e-6 (exponential))) - - -Author ------- - -Miroslav Lichvar - - -License -------- - -Copyright (C) 2010, 2011, 2012 Miroslav Lichvar - -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, see . diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/client.c b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/client.c deleted file mode 100644 index 2652b52..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/client.c +++ /dev/null @@ -1,1981 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef SO_TIMESTAMPING -#include -#include -#endif - -#include "protocol.h" - -#include "client_fuzz.c" - -/* first node in first subnet is 192.168.123.1 */ -#define BASE_ADDR 0xc0a87b00 -#define NETMASK 0xffffff00 -#define NODE_ADDR(subnet, node) (BASE_ADDR + 0x100 * (subnet) + (node) + 1) -#define BROADCAST_ADDR(subnet) (NODE_ADDR(subnet, 0) | 0xff) -#define NODE_FROM_ADDR(addr) (((addr) & ~NETMASK) - 1) -#define SUBNET_FROM_ADDR(addr) ((((addr) & NETMASK) - BASE_ADDR) / 0x100) - -#define PTP_PRIMARY_MCAST_ADDR 0xe0000181 /* 224.0.1.129 */ -#define PTP_PDELAY_MCAST_ADDR 0xe000006b /* 224.0.0.107 */ - -#define REFCLK_FD 1000 -#define REFCLK_ID ((~(clockid_t)REFCLK_FD << 3) | 3) -#define REFCLK_PHC_INDEX 0 -#define SYSCLK_FD 1001 -#define SYSCLK_CLOCKID ((~(clockid_t)SYSCLK_FD << 3) | 3) -#define SYSCLK_PHC_INDEX 1 - -#define MAX_SOCKETS 20 -#define BASE_SOCKET_FD 100 -#define BASE_SOCKET_DEFAULT_PORT 60000 - -#define MAX_TIMERS 40 -#define BASE_TIMER_ID 0xC1230123 -#define BASE_TIMER_FD 200 - -#define URANDOM_FILE (void *)0xD1230123 - -static FILE *(*_fopen)(const char *path, const char *mode); -static size_t (*_fread)(void *ptr, size_t size, size_t nmemb, FILE *stream); -static int (*_fileno)(FILE *stream); -static int (*_fclose)(FILE *fp); -static int (*_open)(const char *pathname, int flags); -static int (*_close)(int fd); -static int (*_socket)(int domain, int type, int protocol); -static int (*_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); -static ssize_t (*_recvmsg)(int sockfd, struct msghdr *msg, int flags); -static ssize_t (*_send)(int sockfd, const void *buf, size_t len, int flags); -static int (*_usleep)(useconds_t usec); -static void (*_srandom)(unsigned int seed); -static int (*_shmget)(key_t key, size_t size, int shmflg); -static void *(*_shmat)(int shmid, const void *shmaddr, int shmflg); - -static unsigned int node; -static int initialized = 0; -static int clknetsim_fd; -static int precision_hack = 1; -static unsigned int random_seed = 0; -static int recv_multiply = 1; -static int timestamping = 1; - -enum { - IFACE_NONE = 0, - IFACE_LO, - IFACE_ALL, - IFACE_ETH0, -}; - -struct ts_message { - char data[MAX_PACKET_SIZE]; - unsigned int len; - unsigned int subnet; - unsigned int to; - unsigned int port; -}; - -struct socket { - int used; - int type; - int port; - int iface; - int remote_node; - int remote_port; - int broadcast; - int pkt_info; - int time_stamping; - struct ts_message last_ts_msg; -}; - -static struct socket sockets[MAX_SOCKETS]; -static int subnets; - -static double real_time = 0.0; -static double monotonic_time = 0.0; -static double network_time = 0.0; -static int local_time_valid = 0; - -static time_t system_time_offset = 1262304000; /* 2010-01-01 0:00 UTC */ - -#define TIMER_TYPE_SIGNAL 1 -#define TIMER_TYPE_FD 2 - -struct timer { - int used; - int armed; - int type; - clockid_t clock_id; - double timeout; - double interval; -}; - -static struct timer timers[MAX_TIMERS]; - -static timer_t itimer_real_id; - -#define SHM_KEY 0x4e545030 -#define SHM_REFCLOCKS 4 - -static struct shmTime { - int mode; - int count; - time_t clockTimeStampSec; - int clockTimeStampUSec; - time_t receiveTimeStampSec; - int receiveTimeStampUSec; - int leap; - int precision; - int nsamples; - int valid; - int clockTimeStampNSec; - int receiveTimeStampNSec; - int dummy[8]; -} shm_time[SHM_REFCLOCKS]; - -static int shm_refclocks = 0; -static double shm_refclock_time = 0.0; -static struct Reply_getrefoffsets refclock_offsets; -static int refclock_offsets_used = REPLY_GETREFOFFSETS_SIZE; - -static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen); - -__attribute__((constructor)) -static void init(void) { - struct Request_register req; - struct Reply_register rep; - struct sockaddr_un s = {AF_UNIX, "clknetsim.sock"}; - const char *env; - unsigned int connect_retries = 100; /* 10 seconds */ - - if (initialized) - return; - - _fopen = (FILE *(*)(const char *path, const char *mode))dlsym(RTLD_NEXT, "fopen"); - _fread = (size_t (*)(void *ptr, size_t size, size_t nmemb, FILE *stream))dlsym(RTLD_NEXT, "fread"); - _fileno = (int (*)(FILE *stream))dlsym(RTLD_NEXT, "fileno"); - _fclose = (int (*)(FILE *fp))dlsym(RTLD_NEXT, "fclose"); - _open = (int (*)(const char *pathname, int flags))dlsym(RTLD_NEXT, "open"); - _close = (int (*)(int fd))dlsym(RTLD_NEXT, "close"); - _socket = (int (*)(int domain, int type, int protocol))dlsym(RTLD_NEXT, "socket"); - _connect = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen))dlsym(RTLD_NEXT, "connect"); - _recvmsg = (ssize_t (*)(int sockfd, struct msghdr *msg, int flags))dlsym(RTLD_NEXT, "recvmsg"); - _send = (ssize_t (*)(int sockfd, const void *buf, size_t len, int flags))dlsym(RTLD_NEXT, "send"); - _usleep = (int (*)(useconds_t usec))dlsym(RTLD_NEXT, "usleep"); - _srandom = (void (*)(unsigned int seed))dlsym(RTLD_NEXT, "srandom"); - _shmget = (int (*)(key_t key, size_t size, int shmflg))dlsym(RTLD_NEXT, "shmget"); - _shmat = (void *(*)(int shmid, const void *shmaddr, int shmflg))dlsym(RTLD_NEXT, "shmat"); - - env = getenv("CLKNETSIM_START_DATE"); - if (env) - system_time_offset = atol(env); - - env = getenv("CLKNETSIM_RANDOM_SEED"); - if (env) - random_seed = atoi(env); - - env = getenv("CLKNETSIM_RECV_MULTIPLY"); - if (env) - recv_multiply = atoi(env); - - env = getenv("CLKNETSIM_TIMESTAMPING"); - if (env) - timestamping = atoi(env); - - if (fuzz_init()) { - node = 0; - subnets = 1; - initialized = 1; - return; - } - - env = getenv("CLKNETSIM_NODE"); - if (!env) { - fprintf(stderr, "clknetsim: CLKNETSIM_NODE variable not set.\n"); - exit(1); - } - node = atoi(env) - 1; - - env = getenv("CLKNETSIM_SOCKET"); - if (env) - snprintf(s.sun_path, sizeof (s.sun_path), "%s", env); - - env = getenv("CLKNETSIM_CONNECT_TIMEOUT"); - if (env) - connect_retries = 10 * atoi(env); - - clknetsim_fd = _socket(AF_UNIX, SOCK_SEQPACKET, 0); - - assert(clknetsim_fd >= 0); - - while (_connect(clknetsim_fd, (struct sockaddr *)&s, sizeof (s)) < 0) { - if (!--connect_retries) { - fprintf(stderr, "clknetsim: could not connect to server.\n"); - exit(1); - } - _usleep(100000); - } - - /* this requires the node variable to be already set */ - srandom(0); - - initialized = 1; - - req.node = node; - make_request(REQ_REGISTER, &req, sizeof (req), &rep, sizeof (rep)); - - subnets = rep.subnets; -} - -__attribute__((destructor)) -static void fini(void) { - if (initialized) - make_request(REQ_DEREGISTER, NULL, 0, NULL, 0); -} - -static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen) { - struct Request_packet request; - int sent, received = 0; - - assert(initialized); - - if (fuzz_mode) { - fuzz_process_reply(request_id, request_data, reply, replylen); - return; - } - - request.header.request = request_id; - request.header._pad = 0; - - assert(offsetof(struct Request_packet, data) + reqlen <= sizeof (request)); - - if (request_data) - memcpy(&request.data, request_data, reqlen); - reqlen += offsetof(struct Request_packet, data); - - if ((sent = _send(clknetsim_fd, &request, reqlen, 0)) <= 0 || - (reply && (received = recv(clknetsim_fd, reply, replylen, 0)) <= 0)) { - fprintf(stderr, "clknetsim: server connection closed.\n"); - initialized = 0; - exit(1); - } - - assert(sent == reqlen); - - if (!reply) - return; - - /* check reply length */ - switch (request_id) { - case REQ_RECV: - /* reply with variable length */ - assert(received >= offsetof(struct Reply_recv, data)); - assert(offsetof(struct Reply_recv, data) + - ((struct Reply_recv *)reply)->len <= received); - break; - default: - assert(received == replylen); - } -} - -static void fetch_time(void) { - struct Reply_gettime r; - - if (!local_time_valid) { - make_request(REQ_GETTIME, NULL, 0, &r, sizeof (r)); - real_time = r.real_time; - monotonic_time = r.monotonic_time; - network_time = r.network_time; - local_time_valid = 1; - } -} - -static double get_real_time(void) { - fetch_time(); - return real_time; -} - -static double get_monotonic_time(void) { - fetch_time(); - return monotonic_time; -} - -static double get_refclock_offset(void) { - if (refclock_offsets_used >= REPLY_GETREFOFFSETS_SIZE) { - make_request(REQ_GETREFOFFSETS, NULL, 0, &refclock_offsets, sizeof (refclock_offsets)); - refclock_offsets_used = 0; - } - return refclock_offsets.offsets[refclock_offsets_used++]; -} - -static double get_refclock_time(void) { - fetch_time(); - return network_time - get_refclock_offset(); -} - -static void settime(double time) { - struct Request_settime req; - - req.time = time; - make_request(REQ_SETTIME, &req, sizeof (req), NULL, 0); - - local_time_valid = 0; -} - -static void fill_refclock_sample(void) { - struct Reply_getrefsample r; - double clock_time, receive_time, round_corr; - int i; - - if (!shm_refclocks) - return; - - make_request(REQ_GETREFSAMPLE, NULL, 0, &r, sizeof (r)); - - if (r.time == shm_refclock_time || !r.valid) - return; - shm_refclock_time = r.time; - - for (i = 0; i < shm_refclocks; i++) { - if (shm_refclocks == 1) { - clock_time = r.time - r.offset; - receive_time = r.time; - } else { - clock_time = get_refclock_time(); - receive_time = get_real_time(); - } - - round_corr = (clock_time * 1e6 - floor(clock_time * 1e6) + 0.5) / 1e6; - clock_time -= round_corr; - receive_time -= round_corr; - - shm_time[i].count++; - shm_time[i].clockTimeStampSec = floor(clock_time); - shm_time[i].clockTimeStampUSec = (clock_time - shm_time[i].clockTimeStampSec) * 1e6; - shm_time[i].clockTimeStampNSec = (clock_time - shm_time[i].clockTimeStampSec) * 1e9; - shm_time[i].clockTimeStampSec += system_time_offset; - shm_time[i].receiveTimeStampSec = floor(receive_time); - shm_time[i].receiveTimeStampUSec = (receive_time - shm_time[i].receiveTimeStampSec) * 1e6; - shm_time[i].receiveTimeStampNSec = (receive_time - shm_time[i].receiveTimeStampSec) * 1e9; - shm_time[i].receiveTimeStampSec += system_time_offset; - shm_time[i].leap = 0; - shm_time[i].valid = 1; - } -} - -static int socket_in_subnet(int socket, int subnet) { - switch (sockets[socket].iface) { - case IFACE_LO: - return 0; - case IFACE_NONE: - case IFACE_ALL: - return 1; - default: - return sockets[socket].iface - IFACE_ETH0 == subnet; - } -} - -static void get_target(int socket, uint32_t addr, unsigned int *subnet, unsigned int *node) { - if (addr == PTP_PRIMARY_MCAST_ADDR || addr == PTP_PDELAY_MCAST_ADDR) { - assert(sockets[socket].iface >= IFACE_ETH0); - *subnet = sockets[socket].iface - IFACE_ETH0; - *node = -1; /* multicast as broadcast */ - } else { - *subnet = SUBNET_FROM_ADDR(addr); - assert(*subnet >= 0 && *subnet < subnets); - assert(socket_in_subnet(socket, *subnet)); - - if (addr == BROADCAST_ADDR(*subnet)) - *node = -1; /* broadcast */ - else - *node = NODE_FROM_ADDR(addr); - } -} - -static int get_network_from_iface(const char *iface) { - if (strncmp(iface, "eth", 3)) - return -1; - return atoi(iface + 3); -} - -static int get_free_socket(void) { - int i; - - for (i = 0; i < MAX_SOCKETS; i++) { - if (!sockets[i].used) - return i; - } - - return -1; -} - -static int get_socket_from_fd(int fd) { - int s = fd - BASE_SOCKET_FD; - - if (s >= 0 && s < MAX_SOCKETS && sockets[s].used) - return s; - return -1; -} - -static int get_socket_fd(int s) { - return s + BASE_SOCKET_FD; -} - -static int find_recv_socket(int subnet, int port, int broadcast) { - int i, s = -1; - - for (i = 0; i < MAX_SOCKETS; i++) { - if (!sockets[i].used || - !socket_in_subnet(i, subnet) || - sockets[i].type != SOCK_DGRAM || - (port && sockets[i].port != port)) - continue; - if (s < 0 || sockets[s].iface < sockets[i].iface || - (broadcast && sockets[i].broadcast) || - (!broadcast && sockets[s].broadcast && - !sockets[i].broadcast)) - s = i; - } - - return s; -} - -static int get_free_timer(void) { - int i; - - for (i = 0; i < MAX_TIMERS; i++) { - if (!timers[i].used) - return i; - } - - return -1; -} - -static timer_t get_timerid(int timer) { - return (timer_t)((long)timer + BASE_TIMER_ID); -} - -static int get_timer_from_id(timer_t timerid) { - int t = (long)timerid - BASE_TIMER_ID; - - if (t >= 0 && t < MAX_TIMERS && timers[t].used) - return t; - return -1; -} - -static int get_timerfd(int timer) { - return timer + BASE_TIMER_FD; -} - -static int get_timer_from_fd(int fd) { - int t = fd - BASE_TIMER_FD; - - if (t >= 0 && t < MAX_TIMERS && timers[t].used) - return t; - return -1; -} - -static int get_first_timer(fd_set *timerfds) { - int i, r = -1; - - for (i = 0; i < MAX_TIMERS; i++) { - if (!timers[i].used || !timers[i].armed) - continue; - if (timers[i].type == TIMER_TYPE_FD && - !(timerfds && FD_ISSET(get_timerfd(i), timerfds))) - continue; - if (r < 0 || timers[r].timeout > timers[i].timeout) - r = i; - } - - return r; -} - -static void rearm_timer(int timer) -{ - assert(timers[timer].armed); - if (timers[timer].interval > 0.0) - timers[timer].timeout += timers[timer].interval; - else - timers[timer].armed = 0; -} - -static void time_to_timeval(double d, struct timeval *tv) { - tv->tv_sec = floor(d); - tv->tv_usec = (d - tv->tv_sec) * 1e6; -} - -static void time_to_timespec(double d, struct timespec *tp) { - tp->tv_sec = floor(d); - tp->tv_nsec = (d - tp->tv_sec) * 1e9; -} - -static double timeval_to_time(const struct timeval *tv, time_t offset) { - return tv->tv_sec + offset + tv->tv_usec / 1e6; -} - -static double timespec_to_time(const struct timespec *tp, time_t offset) { - return tp->tv_sec + offset + tp->tv_nsec / 1e9; -} - -int gettimeofday(struct timeval *tv, struct timezone *tz) { - double time; - - time = get_real_time() + 0.5e-6; - - time_to_timeval(time, tv); - tv->tv_sec += system_time_offset; - - /* chrony clock precision routine hack */ - if (precision_hack) - tv->tv_usec += random() % 2; - - return 0; -} - -int clock_gettime(clockid_t which_clock, struct timespec *tp) { - double time; - - switch (which_clock) { - case CLOCK_REALTIME: - case SYSCLK_CLOCKID: - time = get_real_time(); - break; - case CLOCK_MONOTONIC: - time = get_monotonic_time(); - break; - case REFCLK_ID: - time = get_refclock_time(); - break; - default: - assert(0); - } - - time += 0.5e-9; - time_to_timespec(time, tp); - - if (which_clock == CLOCK_REALTIME || which_clock == REFCLK_ID) - tp->tv_sec += system_time_offset; - - /* ntpd clock precision routine hack */ - if (precision_hack) { - static int x = 0; - tp->tv_nsec += x++ * 101; - } - - return 0; -} - -time_t time(time_t *t) { - time_t time; - - time = floor(get_real_time()); - time += system_time_offset; - if (t) - *t = time; - return time; -} - -int settimeofday(const struct timeval *tv, const struct timezone *tz) { - assert(tv); - settime(timeval_to_time(tv, -system_time_offset)); - return 0; -} - -int clock_settime(clockid_t which_clock, const struct timespec *tp) { - assert(tp && which_clock == CLOCK_REALTIME); - settime(timespec_to_time(tp, -system_time_offset)); - return 0; -} - -int adjtimex(struct timex *buf) { - struct Request_adjtimex req; - struct Reply_adjtimex rep; - - if (buf->modes & ADJ_SETOFFSET) - local_time_valid = 0; - - req.timex = *buf; - make_request(REQ_ADJTIMEX, &req, sizeof (req), &rep, sizeof (rep)); - *buf = rep.timex; - - if (rep.ret < 0) - errno = EINVAL; - - return rep.ret; -} - -int ntp_adjtime(struct timex *buf) { - return adjtimex(buf); -} - -int clock_adjtime(clockid_t id, struct timex *tx) { - assert(id == CLOCK_REALTIME || id == SYSCLK_CLOCKID || id == REFCLK_ID); - - if (id == SYSCLK_CLOCKID) { - /* allow large frequency adjustment by setting ticks */ - - long hz, base_tick, scaled_ppm_per_tick; - int r; - - hz = sysconf(_SC_CLK_TCK); - assert(hz > 0); - base_tick = (1000000 + hz / 2) / hz; - scaled_ppm_per_tick = 65536 * hz; - - if (tx->modes & ADJ_FREQUENCY && !(tx->modes & ADJ_TICK)) - tx->tick = base_tick, tx->modes |= ADJ_TICK; - - tx->tick += tx->freq / scaled_ppm_per_tick; - tx->freq = tx->freq % scaled_ppm_per_tick; - - r = adjtimex(tx); - - tx->freq += (tx->tick - base_tick) * scaled_ppm_per_tick; - tx->tick = base_tick; - - return r; - } else if (id == REFCLK_ID) { - if (tx->modes) { - errno = EINVAL; - return -1; - } - - memset(tx, 0, sizeof (*tx)); - return 0; - } - - return adjtimex(tx); -} - -int adjtime(const struct timeval *delta, struct timeval *olddelta) { - struct Request_adjtime req; - struct Reply_adjtime rep; - - if (delta) - req.tv = *delta; - else - time_to_timeval(0.0, &req.tv); - - make_request(REQ_ADJTIME, &req, sizeof (req), &rep, sizeof (rep)); - if (olddelta) - *olddelta = rep.tv; - - if (!delta) { - req.tv = rep.tv; - make_request(REQ_ADJTIME, &req, sizeof (req), &rep, sizeof (rep)); - } - - return 0; -} - -int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { - struct Request_select req; - struct Reply_select rep; - int i, timer, s, recv_fd = -1; - double elapsed = 0.0; - - if (writefds) - FD_ZERO(writefds); - - if (exceptfds) { - /* chronyd waiting for TX timestamp from the error queue */ - for (i = 0; i < nfds; i++) { - if (!FD_ISSET(i, exceptfds) || get_socket_from_fd(i) < 0 || - !sockets[get_socket_from_fd(i)].last_ts_msg.len) - continue; - if (readfds) - FD_ZERO(readfds); - FD_ZERO(exceptfds); - FD_SET(i, exceptfds); - return 1; - } - - FD_ZERO(exceptfds); - } - - req.read = 0; - req._pad = 0; - - /* unknown reading fds are always ready (e.g. chronyd waiting - for name resolving notification, or OpenSSL waiting for - /dev/urandom) */ - if (readfds) { - for (i = 0; i < nfds; i++) { - if (!FD_ISSET(i, readfds)) - continue; - if (get_socket_from_fd(i) < 0 && - get_timer_from_fd(i) < 0) { - FD_ZERO(readfds); - FD_SET(i, readfds); - return 1; - } - req.read = 1; - } - } - - timer = get_first_timer(readfds); - - assert((timeout && (timeout->tv_sec > 0 || timeout->tv_usec > 0)) || - timer >= 0 || find_recv_socket(0, 0, 0) >= 0); - - fetch_time(); - - if (timeout) - req.timeout = timeout->tv_sec + (timeout->tv_usec + 1) / 1e6; - else - req.timeout = 1e20; - -try_again: - if (timer >= 0 && timers[timer].timeout <= monotonic_time) { - /* avoid unnecessary requests */ - rep.ret = REPLY_SELECT_TIMEOUT; - } else { - if (timer >= 0 && monotonic_time + req.timeout > timers[timer].timeout) - req.timeout = timers[timer].timeout - monotonic_time; - - make_request(REQ_SELECT, &req, sizeof (req), &rep, sizeof (rep)); - - elapsed += rep.time.monotonic_time - monotonic_time; - req.timeout -= rep.time.monotonic_time - monotonic_time; - - real_time = rep.time.real_time; - monotonic_time = rep.time.monotonic_time; - network_time = rep.time.network_time; - local_time_valid = 1; - - fill_refclock_sample(); - - if (monotonic_time >= 0.1 || timer >= 0 || rep.ret != REPLY_SELECT_TIMEOUT) - precision_hack = 0; - } - - switch (rep.ret) { - case REPLY_SELECT_TERMINATE: - kill(getpid(), SIGTERM); - errno = EINTR; - return -1; - - case REPLY_SELECT_TIMEOUT: - if (timer >= 0 && monotonic_time >= timers[timer].timeout) { - rearm_timer(timer); - switch (timers[timer].type) { - case TIMER_TYPE_SIGNAL: - kill(getpid(), SIGALRM); - errno = EINTR; - return -1; - case TIMER_TYPE_FD: - recv_fd = get_timerfd(timer); - break; - default: - assert(0); - } - } else - recv_fd = 0; - break; - - case REPLY_SELECT_NORMAL: - case REPLY_SELECT_BROADCAST: - s = find_recv_socket(rep.subnet, rep.dst_port, - rep.ret == REPLY_SELECT_BROADCAST); - recv_fd = s >= 0 ? get_socket_fd(s) : 0; - - /* fetch and drop the packet if no fd is waiting for it */ - if (!readfds || !recv_fd || !FD_ISSET(recv_fd, readfds)) { - struct Reply_recv recv_rep; - - make_request(REQ_RECV, NULL, 0, &recv_rep, sizeof (recv_rep)); - if (rep.ret != REPLY_SELECT_BROADCAST) - fprintf(stderr, "clknetsim: dropped packet from " - "node %d on port %d in subnet %d\n", - recv_rep.from + 1, recv_rep.dst_port, - recv_rep.subnet + 1); - - goto try_again; - } - break; - - default: - assert(0); - return 0; - } - - assert(!recv_fd || (readfds && FD_ISSET(recv_fd, readfds))); - assert(!recv_fd || (recv_fd >= BASE_SOCKET_FD && recv_fd < BASE_SOCKET_FD + MAX_SOCKETS) || - (recv_fd >= BASE_TIMER_FD && recv_fd < BASE_TIMER_FD + MAX_TIMERS)); - - if (readfds) { - FD_ZERO(readfds); - if (recv_fd) - FD_SET(recv_fd, readfds); - } - - if (timeout) { - time_to_timeval(timeval_to_time(timeout, 0) - elapsed, timeout); - if (timeout->tv_sec < 0) { - timeout->tv_sec = 0; - timeout->tv_usec = 0; - } - } - - return recv_fd ? 1 : 0; -} - -#ifndef CLKNETSIM_DISABLE_POLL -int poll(struct pollfd *fds, nfds_t nfds, int timeout) { - struct timeval tv, *ptv = NULL; - int r, maxfd = 0; - nfds_t i; - fd_set rfds; - - /* ptp4l waiting for tx SO_TIMESTAMPING */ - if (nfds == 1 && fds[0].events != POLLOUT && get_socket_from_fd(fds[0].fd) >= 0 && - sockets[get_socket_from_fd(fds[0].fd)].time_stamping & - (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE)) { - if (!fds[0].events) { - fds[0].revents = POLLERR; - return 1; - } else if (fds[0].events == POLLPRI) { - /* SO_SELECT_ERR_QUEUE option enabled */ - fds[0].revents = POLLPRI; - return 1; - } - } - - /* pmc waiting to send packet */ - if (nfds == 2 && (fds[1].events & POLLOUT) && get_socket_from_fd(fds[1].fd) >= 0) { - fds[0].revents = 0; - fds[1].revents = POLLOUT; - return 1; - } - - FD_ZERO(&rfds); - - for (i = 0; i < nfds; i++) - if (fds[i].fd >= 0 && fds[i].events & POLLIN) { - FD_SET(fds[i].fd, &rfds); - if (maxfd < fds[i].fd) - maxfd = fds[i].fd; - } - - if (timeout >= 0) { - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - ptv = &tv; - } - - r = select(maxfd + 1, &rfds, NULL, NULL, ptv); - - for (i = 0; i < nfds; i++) - fds[i].revents = r > 0 && fds[i].fd >= 0 && - FD_ISSET(fds[i].fd, &rfds) ? POLLIN : 0; - - return r; -} - -int __poll_chk(struct pollfd *fds, nfds_t nfds, int timeout, size_t fdslen) { - return poll(fds, nfds, timeout); -} - -#endif - -int usleep(useconds_t usec) { - struct timeval tv; - int r; - - tv.tv_sec = usec / 1000000; - tv.tv_usec = usec % 1000000; - - r = select(0, NULL, NULL, NULL, &tv); - assert(r == 0); - - return 0; -} - -int nanosleep(const struct timespec *req, struct timespec *rem) { - struct timeval tv; - int r; - - tv.tv_sec = req->tv_sec; - tv.tv_usec = req->tv_nsec / 1000 + 1; - - r = select(0, NULL, NULL, NULL, &tv); - assert(r <= 0); - - if (r < 0) { - assert(!rem); - return r; - } - - if (rem) - rem->tv_sec = rem->tv_nsec = 0; - - return 0; -} - -int clock_nanosleep(clockid_t clock_id, int flags, - const struct timespec *request, - struct timespec *remain) { - assert(clock_id == CLOCK_MONOTONIC || clock_id == CLOCK_REALTIME); - return nanosleep(request, remain); -} - -FILE *fopen(const char *path, const char *mode) { - if (!strcmp(path, "/proc/net/if_inet6")) { - errno = ENOENT; - return NULL; - } else if (!strcmp(path, "/dev/urandom")) { - return URANDOM_FILE; - } - - /* make sure _fopen is initialized in case it is called from another - constructor (e.g. OpenSSL's libcrypto) */ - init(); - - return _fopen(path, mode); -} - -size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { - if (stream == URANDOM_FILE) { - size_t i, l = size * nmemb; - long r; - - assert(RAND_MAX >= 0xffffff); - for (i = r = 0; i < l; i++) { - if (i % 3) - r >>= 8; - else - r = random(); - ((unsigned char *)ptr)[i] = r; - } - - return nmemb; - } - - return _fread(ptr, size, nmemb, stream); -} - -int fileno(FILE *stream) { - if (stream == URANDOM_FILE) - return -1; - - return _fileno(stream); -} - -int fclose(FILE *fp) { - if (fp == URANDOM_FILE) - return 0; - return _fclose(fp); -} - -int open(const char *pathname, int flags) { - int r; - - assert(REFCLK_PHC_INDEX == 0 && SYSCLK_PHC_INDEX == 1); - if (!strcmp(pathname, "/dev/ptp0")) - return REFCLK_FD; - else if (!strcmp(pathname, "/dev/ptp1")) - return SYSCLK_FD; - - r = _open(pathname, flags); - assert(r < 0 || (r < BASE_SOCKET_FD && r < BASE_TIMER_FD)); - - return r; -} - -int close(int fd) { - int t, s; - - if (fd == REFCLK_FD || fd == SYSCLK_FD) { - return 0; - } else if ((t = get_timer_from_fd(fd)) >= 0) { - return timer_delete(get_timerid(t)); - } else if ((s = get_socket_from_fd(fd)) >= 0) { - sockets[s].used = 0; - return 0; - } - - return _close(fd); -} - -int socket(int domain, int type, int protocol) { - int s; - - if (domain != AF_INET || (type != SOCK_DGRAM && type != SOCK_STREAM)) { - errno = EINVAL; - return -1; - } - - s = get_free_socket(); - if (s < 0) { - assert(0); - errno = ENOMEM; - return -1; - } - - memset(sockets + s, 0, sizeof (struct socket)); - sockets[s].used = 1; - sockets[s].type = type; - sockets[s].port = BASE_SOCKET_DEFAULT_PORT + s; - sockets[s].remote_node = -1; - - return get_socket_fd(s); -} - -int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - /* ntpd uses connect() and getsockname() to find the interface - which will be used to send packets to an address */ - int s = get_socket_from_fd(sockfd), port; - unsigned int node, subnet; - uint32_t a; - - if (s < 0 || addr->sa_family != AF_INET) { - errno = EINVAL; - return -1; - } - - port = ntohs(((const struct sockaddr_in *)addr)->sin_port); - a = ntohl(((const struct sockaddr_in *)addr)->sin_addr.s_addr); - - get_target(s, a, &subnet, &node); - - sockets[s].iface = IFACE_ETH0 + subnet; - sockets[s].remote_node = node; - sockets[s].remote_port = port; - - return 0; -} - -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - int s = get_socket_from_fd(sockfd), port; - uint32_t a; - - if (s < 0 || addr->sa_family != AF_INET) { - errno = EINVAL; - return -1; - } - - port = ntohs(((struct sockaddr_in *)addr)->sin_port); - a = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); - - if (port) - sockets[s].port = port; - - if (a == INADDR_ANY) - sockets[s].iface = IFACE_ALL; - else if (a == INADDR_LOOPBACK) - sockets[s].iface = IFACE_LO; - else { - int subnet = SUBNET_FROM_ADDR(a); - assert(subnet >= 0 && subnet < subnets); - if (a == NODE_ADDR(subnet, node)) - sockets[s].iface = IFACE_ETH0 + subnet; - else if (a == BROADCAST_ADDR(subnet)) { - sockets[s].iface = IFACE_ETH0 + subnet; - sockets[s].broadcast = 1; - } else - assert(0); - } - - return 0; -} - -int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { - int s = get_socket_from_fd(sockfd); - uint32_t a; - - if (s < 0) { - errno = EINVAL; - return -1; - } - - struct sockaddr_in *in; - in = (struct sockaddr_in *)addr; - assert(*addrlen >= sizeof (*in)); - *addrlen = sizeof (*in); - in->sin_family = AF_INET; - in->sin_port = htons(sockets[s].port); - - switch (sockets[s].iface) { - case IFACE_NONE: - case IFACE_ALL: - a = INADDR_ANY; - break; - case IFACE_LO: - a = INADDR_LOOPBACK; - break; - default: - assert(sockets[s].iface - IFACE_ETH0 < subnets); - a = sockets[s].broadcast ? - BROADCAST_ADDR(sockets[s].iface - IFACE_ETH0) : - NODE_ADDR(sockets[s].iface - IFACE_ETH0, node); - } - - in->sin_addr.s_addr = htonl(a); - - return 0; -} - -int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { - int subnet, s = get_socket_from_fd(sockfd); - - if (s < 0) { - errno = EINVAL; - return -1; - } - - if (level == SOL_SOCKET && optname == SO_BINDTODEVICE) { - if (!strcmp(optval, "lo")) - sockets[s].iface = IFACE_LO; - else if ((subnet = get_network_from_iface(optval)) >= 0) - sockets[s].iface = IFACE_ETH0 + subnet; - else { - errno = EINVAL; - return -1; - } - } - else if (level == IPPROTO_IP && optname == IP_PKTINFO && optlen == sizeof (int)) - sockets[s].pkt_info = !!(int *)optval; -#ifdef SO_TIMESTAMPING - else if (level == SOL_SOCKET && optname == SO_TIMESTAMPING && optlen == sizeof (int)) { - if (!timestamping) { - errno = EINVAL; - return -1; - } - sockets[s].time_stamping = *(int *)optval; - } -#endif - - /* unhandled options succeed too */ - return 0; -} - -int fcntl(int fd, int cmd, ...) { - return 0; -} - -int ioctl(int fd, unsigned long request, ...) { - va_list ap; - struct ifconf *conf; - struct ifreq *req; - int i, subnet, ret = 0, s = get_socket_from_fd(fd); - - va_start(ap, request); - - if (request == SIOCGIFCONF) { - conf = va_arg(ap, struct ifconf *); - assert(conf->ifc_len >= sizeof (struct ifreq) * (1 + subnets)); - conf->ifc_len = sizeof (struct ifreq) * (1 + subnets); - sprintf(conf->ifc_req[0].ifr_name, "lo"); - ((struct sockaddr_in*)&conf->ifc_req[0].ifr_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - conf->ifc_req[0].ifr_addr.sa_family = AF_INET; - - for (i = 0; i < subnets; i++) { - sprintf(conf->ifc_req[i + 1].ifr_name, "eth%d", i); - ((struct sockaddr_in*)&conf->ifc_req[i + 1].ifr_addr)->sin_addr.s_addr = htonl(NODE_ADDR(i, node)); - conf->ifc_req[i + 1].ifr_addr.sa_family = AF_INET; - } - } else if (request == SIOCGIFINDEX) { - req = va_arg(ap, struct ifreq *); - if (!strcmp(req->ifr_name, "lo")) - req->ifr_ifindex = 0; - else if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) - req->ifr_ifindex = subnet + 1; - else - ret = -1, errno = EINVAL; - } else if (request == SIOCGIFFLAGS) { - req = va_arg(ap, struct ifreq *); - if (!strcmp(req->ifr_name, "lo")) - req->ifr_flags = IFF_UP | IFF_LOOPBACK; - else if (get_network_from_iface(req->ifr_name) >= 0) - req->ifr_flags = IFF_UP | IFF_BROADCAST; - else - ret = -1, errno = EINVAL; - } else if (request == SIOCGIFBRDADDR) { - req = va_arg(ap, struct ifreq *); - if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) - ((struct sockaddr_in*)&req->ifr_broadaddr)->sin_addr.s_addr = htonl(BROADCAST_ADDR(subnet)); - else - ret = -1, errno = EINVAL; - req->ifr_broadaddr.sa_family = AF_INET; - } else if (request == SIOCGIFNETMASK) { - req = va_arg(ap, struct ifreq *); - if (!strcmp(req->ifr_name, "lo")) - ((struct sockaddr_in*)&req->ifr_netmask)->sin_addr.s_addr = htonl(0xff000000); - else if (get_network_from_iface(req->ifr_name) >= 0) - ((struct sockaddr_in*)&req->ifr_netmask)->sin_addr.s_addr = htonl(NETMASK); - else - ret = -1, errno = EINVAL; - req->ifr_netmask.sa_family = AF_INET; - } else if (request == SIOCGIFHWADDR) { - req = va_arg(ap, struct ifreq *); - if (!strcmp(req->ifr_name, "lo")) - memset((&req->ifr_hwaddr)->sa_data, 0, IFHWADDRLEN); - else if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) { - char mac[IFHWADDRLEN] = {0x12, 0x34, 0x56, 0x78, subnet + 1, node + 1}; - memcpy((&req->ifr_hwaddr)->sa_data, mac, sizeof (mac)); - } else - ret = -1, errno = EINVAL; - req->ifr_netmask.sa_family = AF_UNSPEC; -#ifdef ETHTOOL_GET_TS_INFO - } else if (request == SIOCETHTOOL) { - struct ethtool_ts_info *info; - req = va_arg(ap, struct ifreq *); - info = (struct ethtool_ts_info *)req->ifr_data; - memset(info, 0, sizeof (*info)); - if (get_network_from_iface(req->ifr_name) >= 0) { - info->phc_index = timestamping > 1 ? REFCLK_PHC_INDEX : SYSCLK_PHC_INDEX; - info->so_timestamping = SOF_TIMESTAMPING_SOFTWARE | - SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_RAW_HARDWARE | - SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE; - info->tx_types = HWTSTAMP_TX_ON; - info->rx_filters = 1 << HWTSTAMP_FILTER_NONE | 1 << HWTSTAMP_FILTER_ALL; - } else - ret = -1, errno = EINVAL; -#endif -#ifdef PTP_CLOCK_GETCAPS - } else if (request == PTP_CLOCK_GETCAPS && (fd == REFCLK_FD || fd == SYSCLK_FD)) { - struct ptp_clock_caps *caps = va_arg(ap, struct ptp_clock_caps *); - memset(caps, 0, sizeof (*caps)); - /* maximum frequency in 32-bit timex.freq */ - caps->max_adj = 32767999; -#endif -#ifdef PTP_SYS_OFFSET - } else if (request == PTP_SYS_OFFSET && fd == REFCLK_FD) { - struct ptp_sys_offset *sys_off = va_arg(ap, struct ptp_sys_offset *); - struct timespec ts; - int i; - - if (sys_off->n_samples > PTP_MAX_SAMPLES) - sys_off->n_samples = PTP_MAX_SAMPLES; - - clock_gettime(REFCLK_ID, &ts); - for (i = 0; i < sys_off->n_samples; i++) { - sys_off->ts[2 * i + 1].sec = ts.tv_sec; - sys_off->ts[2 * i + 1].nsec = ts.tv_nsec; - } - - clock_gettime(CLOCK_REALTIME, &ts); - for (i = 0; i < sys_off->n_samples + 1; i++) { - sys_off->ts[2 * i].sec = ts.tv_sec; - sys_off->ts[2 * i].nsec = ts.tv_nsec; - } -#endif -#ifdef PTP_SYS_OFFSET_PRECISE - } else if (request == PTP_SYS_OFFSET_PRECISE && fd == REFCLK_FD) { - struct ptp_sys_offset_precise *sys_off = va_arg(ap, struct ptp_sys_offset_precise *); - struct timespec ts; - - clock_gettime(REFCLK_ID, &ts); - sys_off->device.sec = ts.tv_sec; - sys_off->device.nsec = ts.tv_nsec; - - clock_gettime(CLOCK_REALTIME, &ts); - sys_off->sys_realtime.sec = ts.tv_sec; - sys_off->sys_realtime.nsec = ts.tv_nsec; -#endif -#ifdef SIOCSHWTSTAMP - } else if (request == SIOCSHWTSTAMP && s >= 0) { -#endif -#ifdef SIOCGHWTSTAMP - } else if (request == SIOCGHWTSTAMP && s >= 0) { - struct hwtstamp_config *ts_config; - - req = va_arg(ap, struct ifreq *); - ts_config = (struct hwtstamp_config *)req->ifr_data; - - ts_config->flags = 0; - ts_config->tx_type = HWTSTAMP_TX_ON; - ts_config->rx_filter = HWTSTAMP_FILTER_ALL; -#endif - } else { - ret = -1; - errno = EINVAL; - } - - va_end(ap); - return ret; -} - -int getifaddrs(struct ifaddrs **ifap) { - struct iface { - struct ifaddrs ifaddrs; - struct sockaddr_in addr, netmask, broadaddr; - char name[11]; - } *ifaces; - int i; - - ifaces = malloc(sizeof (struct iface) * (1 + subnets)); - - ifaces[0].ifaddrs = (struct ifaddrs){ - .ifa_next = &ifaces[1].ifaddrs, - .ifa_name = "lo", - .ifa_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING, - .ifa_addr = (struct sockaddr *)&ifaces[0].addr, - .ifa_netmask = (struct sockaddr *)&ifaces[0].netmask, - .ifa_broadaddr = (struct sockaddr *)&ifaces[0].broadaddr - }; - ifaces[0].addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - ifaces[0].netmask.sin_addr.s_addr = htonl(0xff000000); - ifaces[0].broadaddr.sin_addr.s_addr = 0; - - for (i = 0; i < subnets; i++) { - ifaces[i + 1].ifaddrs = (struct ifaddrs){ - .ifa_next = &ifaces[i + 2].ifaddrs, - .ifa_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING, - .ifa_addr = (struct sockaddr *)&ifaces[i + 1].addr, - .ifa_netmask = (struct sockaddr *)&ifaces[i + 1].netmask, - .ifa_broadaddr = (struct sockaddr *)&ifaces[i + 1].broadaddr - }; - ifaces[i + 1].ifaddrs.ifa_name = ifaces[i + 1].name; - snprintf(ifaces[i + 1].name, sizeof (ifaces[i + 1].name), "eth%d", i); - ifaces[i + 1].addr.sin_addr.s_addr = htonl(NODE_ADDR(i, node)); - ifaces[i + 1].netmask.sin_addr.s_addr = htonl(NETMASK); - ifaces[i + 1].broadaddr.sin_addr.s_addr = htonl(BROADCAST_ADDR(i)); - } - - ifaces[i].ifaddrs.ifa_next = NULL; - - for (i = 0; i < 1 + subnets; i++) { - ifaces[i].addr.sin_family = AF_INET; - ifaces[i].netmask.sin_family = AF_INET; - ifaces[i].broadaddr.sin_family = AF_INET; - } - - *ifap = (struct ifaddrs *)ifaces; - return 0; -} - -void freeifaddrs(struct ifaddrs *ifa) { - free(ifa); -} - -ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { - struct Request_send req; - struct sockaddr_in connected_sa, *sa; - struct cmsghdr *cmsg; - int s = get_socket_from_fd(sockfd), timestamping; - - if (s < 0 || sockets[s].type != SOCK_DGRAM) { - assert(0); - errno = EINVAL; - return -1; - } - - if (sockets[s].remote_node >= 0) { - if (msg->msg_name) { - errno = EISCONN; - return -1; - } - sa = &connected_sa; - sa->sin_family = AF_INET; - sa->sin_port = htons(sockets[s].remote_port); - sa->sin_addr.s_addr = htonl(NODE_ADDR(sockets[s].iface - IFACE_ETH0, - sockets[s].remote_node)); - } else { - sa = msg->msg_name; - assert(sa && msg->msg_namelen >= sizeof (struct sockaddr_in)); - assert(sa->sin_family == AF_INET); - } - - assert(msg->msg_iovlen == 1); - assert(msg->msg_iov[0].iov_len <= sizeof (req.data)); - - get_target(s, ntohl(sa->sin_addr.s_addr), &req.subnet, &req.to); - req.src_port = sockets[s].port; - req.dst_port = ntohs(sa->sin_port); - assert(req.src_port && req.dst_port); - - req.len = msg->msg_iov[0].iov_len; - memcpy(req.data, msg->msg_iov[0].iov_base, req.len); - - make_request(REQ_SEND, &req, offsetof(struct Request_send, data) + req.len, NULL, 0); - - timestamping = sockets[s].time_stamping; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR((struct msghdr *)msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING) - memcpy(×tamping, CMSG_DATA(cmsg), sizeof (timestamping)); - } - - if (timestamping & (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE)) { - struct ts_message *last_ts_msg = &sockets[s].last_ts_msg; - - assert(req.len <= sizeof (last_ts_msg->data)); - memcpy(last_ts_msg->data, req.data, req.len); - last_ts_msg->len = req.len; - last_ts_msg->subnet = req.subnet; - last_ts_msg->to = req.to; - last_ts_msg->port = req.dst_port; - } - - return req.len; -} - -ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { - struct msghdr msg; - struct iovec iov; - - iov.iov_base = (void *)buf; - iov.iov_len = len; - - msg.msg_name = (void *)dest_addr; - msg.msg_namelen = addrlen; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - return sendmsg(sockfd, &msg, flags); -} - -ssize_t send(int sockfd, const void *buf, size_t len, int flags) { - return sendto(sockfd, buf, len, flags, NULL, 0); -} - -int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, -#if !defined(__GLIBC_PREREQ) || !(__GLIBC_PREREQ(2, 20)) - const -#endif - struct timespec *timeout) { - ssize_t len; - int i, n; - - assert(vlen > 0); - len = recvmsg(sockfd, &msgvec[0].msg_hdr, flags); - if (len < 0) - return -1; - msgvec[0].msg_len = len; - - if (recv_multiply <= 1 || vlen <= 1) - return 1; - - n = random() % recv_multiply + 1; - if (n > vlen) - n = vlen; - - for (i = 1; i < n; i++) { - struct msghdr *src = &msgvec[0].msg_hdr, *dst = &msgvec[i].msg_hdr; - if (dst->msg_name) { - memcpy(dst->msg_name, src->msg_name, src->msg_namelen); - dst->msg_namelen = src->msg_namelen; - } - assert(dst->msg_iovlen == 1 && dst->msg_iov[0].iov_len >= len); - memcpy(dst->msg_iov[0].iov_base, src->msg_iov[0].iov_base, len); - if (dst->msg_control) { - assert(dst->msg_controllen >= src->msg_controllen); - memcpy(dst->msg_control, src->msg_control, src->msg_controllen); - dst->msg_controllen = src->msg_controllen; - } - dst->msg_flags = src->msg_flags; - msgvec[i].msg_len = msgvec[0].msg_len; - } - - return n; -} - -ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { - struct ts_message *last_ts_msg = NULL; - struct Reply_recv rep; - struct sockaddr_in *sa; - struct cmsghdr *cmsg; - int msglen, cmsglen, s = get_socket_from_fd(sockfd); - - if (sockfd == clknetsim_fd) - return _recvmsg(sockfd, msg, flags); - - assert(s >= 0 && sockets[s].type == SOCK_DGRAM); - - if (sockets[s].last_ts_msg.len && flags & MSG_ERRQUEUE) { - uint32_t addr; - uint16_t port; - - /* last message looped back to the error queue */ - - last_ts_msg = &sockets[s].last_ts_msg; - - msg->msg_flags = MSG_ERRQUEUE; - - rep.subnet = last_ts_msg->subnet; - rep.from = last_ts_msg->to; - rep.src_port = last_ts_msg->port; - rep.dst_port = sockets[s].port; - - addr = htonl(NODE_ADDR(rep.subnet, rep.from)); - port = htons(rep.src_port); - - /* put the message in an Ethernet frame */ - memset(rep.data, 0, 42); - rep.data[12] = 0x08; - rep.data[14] = 0x45; - rep.data[23] = 17; - memcpy(rep.data + 30, &addr, sizeof (addr)); - memcpy(rep.data + 36, &port, sizeof (port)); - - assert(last_ts_msg->len + 42 <= sizeof (rep.data)); - memcpy(rep.data + 42, last_ts_msg->data, last_ts_msg->len); - - rep.len = 42 + last_ts_msg->len; - - last_ts_msg->len = 0; - } else - make_request(REQ_RECV, NULL, 0, &rep, sizeof (rep)); - - if (rep.len == 0 && rep.from == -1) { - errno = EWOULDBLOCK; - return -1; - } - - assert(socket_in_subnet(s, rep.subnet)); - assert(sockets[s].port == rep.dst_port); - assert(!sockets[s].remote_port || sockets[s].remote_port == rep.src_port); - - if (msg->msg_name) { - assert(msg->msg_namelen >= sizeof (struct sockaddr_in)); - - sa = msg->msg_name; - sa->sin_family = AF_INET; - sa->sin_port = htons(rep.src_port); - sa->sin_addr.s_addr = htonl(NODE_ADDR(rep.subnet, rep.from)); - msg->msg_namelen = sizeof (struct sockaddr_in); - } - - assert(msg->msg_iovlen == 1); - msglen = msg->msg_iov[0].iov_len < rep.len ? msg->msg_iov[0].iov_len : rep.len; - memcpy(msg->msg_iov[0].iov_base, rep.data, msglen); - - cmsglen = 0; - - if (sockets[s].pkt_info) { - struct in_pktinfo ipi; - - cmsglen = CMSG_SPACE(sizeof (ipi)); - assert(msg->msg_control && msg->msg_controllen >= cmsglen); - - cmsg = CMSG_FIRSTHDR(msg); - memset(cmsg, 0, sizeof (*cmsg)); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof (ipi)); - - memset(&ipi, 0, sizeof (ipi)); - ipi.ipi_spec_dst.s_addr = htonl(NODE_ADDR(rep.subnet, node)); - ipi.ipi_addr.s_addr = ipi.ipi_spec_dst.s_addr; - ipi.ipi_ifindex = rep.subnet + 1; - - memcpy(CMSG_DATA(cmsg), &ipi, sizeof (ipi)); - } - -#ifdef SO_TIMESTAMPING - if (last_ts_msg || - (sockets[s].time_stamping & (SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE) && - !(flags & MSG_ERRQUEUE))) { - struct timespec ts; - - /* don't use CMSG_NXTHDR as it's buggy in glibc */ - cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen); - cmsglen += CMSG_SPACE(3 * sizeof (ts)); - assert(msg->msg_control && msg->msg_controllen >= cmsglen); - - memset(cmsg, 0, CMSG_SPACE(3 * sizeof (ts))); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SO_TIMESTAMPING; - cmsg->cmsg_len = CMSG_LEN(3 * sizeof (ts)); - - if (sockets[s].time_stamping & SOF_TIMESTAMPING_SOFTWARE) { - clock_gettime(CLOCK_REALTIME, &ts); - memcpy((struct timespec *)CMSG_DATA(cmsg), &ts, sizeof (ts)); - } - if (sockets[s].time_stamping & SOF_TIMESTAMPING_RAW_HARDWARE) { - clock_gettime(timestamping > 1 ? REFCLK_ID : CLOCK_REALTIME, &ts); - memcpy((struct timespec *)CMSG_DATA(cmsg) + 2, &ts, sizeof (ts)); - } - } -#endif - msg->msg_controllen = cmsglen; - - return msglen; -} - -ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { - ssize_t ret; - struct msghdr msg; - struct iovec iov; - - iov.iov_base = (void *)buf; - iov.iov_len = len; - - /* needed for compatibility with old glibc recvmsg() */ - memset(&msg, 0, sizeof (msg)); - - msg.msg_name = (void *)src_addr; - msg.msg_namelen = *addrlen; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - ret = recvmsg(sockfd, &msg, flags); - *addrlen = msg.msg_namelen; - - return ret; -} - -ssize_t recv(int sockfd, void *buf, size_t len, int flags) { - struct sockaddr_in sa; - socklen_t addrlen = sizeof (sa); - - return recvfrom(sockfd, buf, len, flags, (struct sockaddr *)&sa, &addrlen); -} - -int timer_create(clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id) { - int t; - - assert(which_clock == CLOCK_REALTIME && timer_event_spec == NULL); - - t = get_free_timer(); - if (t < 0) { - assert(0); - errno = ENOMEM; - return -1; - } - - timers[t].used = 1; - timers[t].armed = 0; - timers[t].type = TIMER_TYPE_SIGNAL; - timers[t].clock_id = which_clock; - *created_timer_id = get_timerid(t); - - return 0; -} - -int timer_delete(timer_t timerid) { - int t = get_timer_from_id(timerid); - - if (t < 0) { - errno = EINVAL; - return -1; - } - - timers[t].used = 0; - - return 0; -} - -int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { - int t = get_timer_from_id(timerid); - - if (t < 0) { - errno = EINVAL; - return -1; - } - - assert(value && ovalue == NULL && - (flags == 0 || (flags == TIMER_ABSTIME && timers[t].clock_id == CLOCK_MONOTONIC))); - - if (value->it_value.tv_sec || value->it_value.tv_nsec) { - timers[t].armed = 1; - timers[t].timeout = timespec_to_time(&value->it_value, 0); - if (!(flags & TIMER_ABSTIME)) - timers[t].timeout += get_monotonic_time(); - timers[t].interval = timespec_to_time(&value->it_interval, 0); - } else { - timers[t].armed = 0; - } - - return 0; -} - -int timer_gettime(timer_t timerid, struct itimerspec *value) { - double timeout; - int t = get_timer_from_id(timerid); - - if (t < 0) { - errno = EINVAL; - return -1; - } - - if (timers[t].armed) { - timeout = timers[t].timeout - get_monotonic_time(); - time_to_timespec(timeout, &value->it_value); - } else { - value->it_value.tv_sec = 0; - value->it_value.tv_nsec = 0; - } - time_to_timespec(timers[t].interval, &value->it_interval); - - return 0; -} - -#ifndef CLKNETSIM_DISABLE_ITIMER -int setitimer(__itimer_which_t which, const struct itimerval *new_value, struct itimerval *old_value) { - struct itimerspec timerspec; - - assert(which == ITIMER_REAL && old_value == NULL); - - if (get_timer_from_id(itimer_real_id) < 0) - timer_create(CLOCK_REALTIME, NULL, &itimer_real_id); - - timerspec.it_interval.tv_sec = new_value->it_interval.tv_sec; - timerspec.it_interval.tv_nsec = new_value->it_interval.tv_usec * 1000; - timerspec.it_value.tv_sec = new_value->it_value.tv_sec; - timerspec.it_value.tv_nsec = new_value->it_value.tv_usec * 1000; - - return timer_settime(itimer_real_id, 0, &timerspec, NULL); -} - -int getitimer(__itimer_which_t which, struct itimerval *curr_value) { - struct itimerspec timerspec; - - assert(which == ITIMER_REAL); - - if (timer_gettime(itimer_real_id, &timerspec)) - return -1; - - curr_value->it_interval.tv_sec = timerspec.it_interval.tv_sec; - curr_value->it_interval.tv_usec = timerspec.it_interval.tv_nsec / 1000; - curr_value->it_value.tv_sec = timerspec.it_value.tv_sec; - curr_value->it_value.tv_usec = timerspec.it_value.tv_nsec / 1000; - - return 0; -} -#endif - -int timerfd_create(int clockid, int flags) { - int t; - - assert((clockid == CLOCK_REALTIME || clockid == CLOCK_MONOTONIC) && !flags); - - t = get_free_timer(); - if (t < 0) { - assert(0); - errno = ENOMEM; - return -1; - } - - timers[t].used = 1; - timers[t].armed = 0; - timers[t].type = TIMER_TYPE_FD; - timers[t].clock_id = clockid; - - return get_timerfd(t); -} - -int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) { - if (flags == TFD_TIMER_ABSTIME) - flags = TIMER_ABSTIME; - else - assert(!flags); - - return timer_settime(get_timerid(get_timer_from_fd(fd)), flags, new_value, old_value); -} - -int timerfd_gettime(int fd, struct itimerspec *curr_value) { - return timer_gettime(get_timerid(get_timer_from_fd(fd)), curr_value); -} - -int shmget(key_t key, size_t size, int shmflg) { - if (fuzz_mode) - return _shmget(key, size, shmflg); - - if (key >= SHM_KEY && key < SHM_KEY + SHM_REFCLOCKS) - return key; - - return -1; -} - -void *shmat(int shmid, const void *shmaddr, int shmflg) { - if (fuzz_mode) - return _shmat(shmid, shmaddr, shmflg); - - assert(shmid >= SHM_KEY && shmid < SHM_KEY + SHM_REFCLOCKS); - - if (shm_refclocks < shmid - SHM_KEY + 1) - shm_refclocks = shmid - SHM_KEY + 1; - memset(&shm_time[shmid - SHM_KEY], 0, sizeof (shm_time[0])); - shm_time[shmid - SHM_KEY].mode = 1; - shm_time[shmid - SHM_KEY].precision = -20; - - /* don't wait for select() with starting of the refclock generator */ - fill_refclock_sample(); - - return &shm_time[shmid - SHM_KEY]; -} - -int shmdt(const void *shmaddr) { - assert(shmaddr >= (void *)&shm_time[0] && shmaddr < (void *)&shm_time[SHM_REFCLOCKS]); - return 0; -} - -uid_t getuid(void) { - return 0; -} - -int uname(struct utsname *buf) { - memset(buf, 0, sizeof (*buf)); - sprintf(buf->sysname, "Linux (clknetsim)"); - sprintf(buf->release, "4.19"); - return 0; -} - -int gethostname(char *name, size_t len) { - snprintf(name, len, "clknetsim-node%d", node + 1); - return 0; -} - -void openlog(const char *ident, int option, int facility) { -} - -void __syslog_chk(int priority, int flag, const char *format, ...) { - va_list ap; - - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - fprintf(stderr, "\n"); -} - -void syslog(int priority, const char *format, ...) { - va_list ap; - - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - fprintf(stderr, "\n"); -} - -void closelog(void) { -} - -#ifndef CLKNETSIM_DISABLE_SYSCALL -long syscall(long number, ...) { - va_list ap; - long r; - struct timex *timex; - clockid_t clock_id; - - va_start(ap, number); - switch (number) { -#ifdef __NR_clock_adjtime - case __NR_clock_adjtime: - clock_id = va_arg(ap, clockid_t); - timex = va_arg(ap, struct timex *); - r = clock_adjtime(clock_id, timex); - break; -#endif - default: - assert(0); - } - va_end(ap); - - return r; -} -#endif - -ssize_t getrandom(void *buf, size_t length, unsigned int flags) { - errno = ENOTSUP; - return -1; -} - -void srandom(unsigned int seed) { - FILE *f; - - /* override the seed to the fixed seed if set or make it truly - random in case it's based on the simulated time */ - if (random_seed) { - seed = random_seed + node; - } else if ((f = _fopen("/dev/urandom", "r"))) { - if (fread(&seed, sizeof (seed), 1, f) != 1) - ; - fclose(f); - } - _srandom(seed); -} - -struct passwd *getpwnam(const char *name) { - static struct passwd pw = { - .pw_name = "", - .pw_passwd = "", - .pw_uid = 0, - .pw_gid = 0, - .pw_gecos = "", - .pw_dir = "", - .pw_shell = "" - }; - - return &pw; -} - -int initgroups(const char *user, gid_t group) { - return 0; -} - -int setgroups(size_t size, const gid_t *list) { - return 0; -} - -int setegid(gid_t gid) { - return 0; -} - -int setgid(gid_t gid) { - return 0; -} - -int seteuid(uid_t uid) { - return 0; -} - -int setuid(uid_t uid) { - return 0; -} - -int cap_set_proc() { - return 0; -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/client_fuzz.c b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/client_fuzz.c deleted file mode 100644 index 87ce98f..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/client_fuzz.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2015 Miroslav Lichvar - * - * 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, see . - */ - -/* This is a minimal replacement for the clknetsim server to allow fuzz - testing. There is no clock control or networking. When the time reaches - fuzz_start, a packet read from stdin is forwarded to the fuzz_port port of - the client, and the client is terminated. Packets sent by the client from - the port are written to stdout. */ - -enum { - FUZZ_MODE_DISABLED = 0, - FUZZ_MODE_ONESHOT = 1, - FUZZ_MODE_BURST = 2, - FUZZ_MODE_REPLY = 3, - FUZZ_MODE_NONE = 4, -}; - -#define FUZZ_FLAG_TIMEOUT 1024 - -#define MAX_FUZZ_PORTS 16 - -static int fuzz_mode; -static int fuzz_ports[MAX_FUZZ_PORTS]; -static int fuzz_port_index, fuzz_ports_n; -static int fuzz_timeout; -static double fuzz_start; - -static int fuzz_init(void) { - const char *env; - - env = getenv("CLKNETSIM_FUZZ_MODE"); - if (!env) - return 0; - - fuzz_mode = atoi(env); - - if (fuzz_mode & FUZZ_FLAG_TIMEOUT) { - fuzz_timeout = 1; - fuzz_mode &= ~FUZZ_FLAG_TIMEOUT; - } - - if (fuzz_mode == FUZZ_MODE_DISABLED) - return 0; - - if (fuzz_mode < FUZZ_MODE_ONESHOT || fuzz_mode > FUZZ_MODE_NONE) { - fprintf(stderr, "clknetsim: unknown fuzz mode.\n"); - exit(1); - } - - env = getenv("CLKNETSIM_FUZZ_PORT"); - - for (fuzz_ports_n = 0; env && fuzz_ports_n < MAX_FUZZ_PORTS; fuzz_ports_n++) { - fuzz_ports[fuzz_ports_n] = atoi(env); - if (!fuzz_ports[fuzz_ports_n]) - break; - env = strchr(env, ','); - if (env) - env++; - } - - if (!fuzz_ports_n) { - fprintf(stderr, "clknetsim: CLKNETSIM_FUZZ_PORT variable not set or invalid.\n"); - exit(1); - } - fuzz_port_index = 0; - - env = getenv("CLKNETSIM_FUZZ_START"); - fuzz_start = env ? atof(env) : 0.1; - - return 1; -} - -static int fuzz_is_fuzz_port(int port) { - int i; - - for (i = 0; i < fuzz_ports_n; i++) - if (fuzz_ports[i] == port) - return 1; - return 0; -} - -static int fuzz_get_fuzz_port(void) { - return fuzz_ports[fuzz_port_index]; -} - -static void fuzz_switch_fuzz_port(void) { - fuzz_port_index = (fuzz_port_index + 1) % fuzz_ports_n; -} - -static int fuzz_read_packet(char *data, int maxlen, int *rlen) { - int len; - uint16_t slen; - - if (fuzz_mode > FUZZ_MODE_ONESHOT) { - if (fread(&slen, 1, sizeof (slen), stdin) != sizeof (slen)) - return 0; - len = ntohs(slen); - if (len > maxlen) - len = maxlen; - } else { - len = maxlen; - } - - *rlen = fread(data, 1, len, stdin); - - return !len || rlen; -} - -static void fuzz_write_packet(const char *data, int len) { - uint16_t slen; - - if (fuzz_mode > FUZZ_MODE_ONESHOT) { - slen = htons(len); - fwrite(&slen, 1, sizeof (slen), stdout); - } - - fwrite(data, 1, len, stdout); -} - -static void fuzz_process_reply(int request_id, const union Request_data *request, union Reply_data *reply, int replylen) { - static double network_time = 0.0; - static int received = 0; - static int sent = 0; - static int dst_port = 0; - static int packet_len = 0; - static int valid_packet = 0; - static char packet[MAX_PACKET_SIZE]; - - if (reply) - memset(reply, 0, replylen); - - switch (request_id) { - case REQ_GETTIME: - reply->gettime.real_time = network_time; - reply->gettime.monotonic_time = network_time; - reply->gettime.network_time = network_time; - break; - case REQ_SELECT: - if (fuzz_mode == FUZZ_MODE_NONE) { - reply->select.ret = REPLY_SELECT_TIMEOUT; - return; - } - - if (!valid_packet && (!received || fuzz_mode != FUZZ_MODE_ONESHOT)) - valid_packet = fuzz_read_packet(packet, sizeof (packet), &packet_len); - - if (!valid_packet) { - reply->select.ret = REPLY_SELECT_TERMINATE; - } else if (!packet_len && fuzz_timeout) { - network_time += request->select.timeout; - reply->select.ret = REPLY_SELECT_TIMEOUT; - valid_packet = 0; - } else { - if (fuzz_mode == FUZZ_MODE_REPLY) { - if (sent > received) { - reply->select.ret = REPLY_SELECT_NORMAL; - } else { - network_time += request->select.timeout; - reply->select.ret = REPLY_SELECT_TIMEOUT; - } - } else { - if (network_time < fuzz_start && !sent) { - network_time += request->select.timeout; - if (network_time >= fuzz_start) { - network_time = fuzz_start; - reply->select.ret = REPLY_SELECT_NORMAL; - } else { - reply->select.ret = REPLY_SELECT_TIMEOUT; - } - } else { - reply->select.ret = REPLY_SELECT_NORMAL; - } - } - } - - reply->select.subnet = 0; - reply->select.dst_port = dst_port ? dst_port : fuzz_get_fuzz_port(); - reply->select.time.real_time = network_time; - reply->select.time.monotonic_time = network_time; - reply->select.time.network_time = network_time; - break; - case REQ_SEND: - if (request->send.to != 1 && request->send.to != -1) - break; - - if (fuzz_mode == FUZZ_MODE_REPLY) { - if (!fuzz_is_fuzz_port(request->send.dst_port)) - break; - dst_port = request->send.src_port; - } else if (!fuzz_is_fuzz_port(request->send.src_port)) - break; - - fuzz_write_packet(request->send.data, request->send.len); - sent++; - break; - case REQ_RECV: - network_time += 1e-5; - reply->recv.subnet = 0; - reply->recv.from = valid_packet ? 1 : -1; - reply->recv.src_port = fuzz_get_fuzz_port(); - reply->recv.dst_port = dst_port ? dst_port : fuzz_get_fuzz_port(); - memcpy(reply->recv.data, packet, packet_len); - reply->recv.len = packet_len; - received++; - valid_packet = 0; - packet_len = 0; - fuzz_switch_fuzz_port(); - break; - case REQ_SETTIME: - network_time = request->settime.time; - break; - case REQ_ADJTIME: - case REQ_GETREFSAMPLE: - case REQ_GETREFOFFSETS: - case REQ_DEREGISTER: - break; - case REQ_ADJTIMEX: - reply->adjtimex.timex.tick = 10000; - break; - case REQ_REGISTER: - default: - assert(0); - } -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clknetsim.bash b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clknetsim.bash deleted file mode 100644 index fe9449c..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clknetsim.bash +++ /dev/null @@ -1,260 +0,0 @@ -# Copyright (C) 2010, 2011 Miroslav Lichvar -# -# 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, see . - -[ -n "$CLKNETSIM_TMPDIR" ] || CLKNETSIM_TMPDIR=tmp - -client_pids="" - -start_client() { - local node=$1 client=$2 config=$3 suffix=$4 opts=$5 - local args=() line lastpid - - rm -f $CLKNETSIM_TMPDIR/log.$node $CLKNETSIM_TMPDIR/conf.$node - - [ $client = chrony ] && client=chronyd - [ $client = ntp ] && client=ntpd - - if ! which $client$suffix &> /dev/null; then - echo "can't find $client$suffix in PATH" - return 1 - fi - - case $client in - chronyd) - cat > $CLKNETSIM_TMPDIR/conf.$node <<-EOF - pidfile $CLKNETSIM_TMPDIR/pidfile.$node - allow - cmdallow - bindcmdaddress 0.0.0.0 - $config - EOF - args=(-d -f $CLKNETSIM_TMPDIR/conf.$node $opts) - ;; - ntpd) - cat > $CLKNETSIM_TMPDIR/conf.$node <<-EOF - pidfile $CLKNETSIM_TMPDIR/pidfile.$node - restrict default - $config - EOF - args=(-n -c $CLKNETSIM_TMPDIR/conf.$node $opts) - ;; - ptp4l) - cat > $CLKNETSIM_TMPDIR/conf.$node <<-EOF - [global] - $config - EOF - args=(-f $CLKNETSIM_TMPDIR/conf.$node $opts) - ;; - chronyc) - args=($opts -m) - while read line; do args+=("$line"); done <<< "$config" - ;; - pmc) - args=($opts) - while read line; do args+=("$line"); done <<< "$config" - ;; - ntpq) - while read line; do args+=(-c "$line"); done <<< "$config" - args+=($opts) - ;; - sntp) - args=(-K /dev/null $opts $config) - ;; - ntpdate) - args=($opts $config) - ;; - busybox) - args=(ntpd -ddd -n) - while read line; do args+=(-p "$line"); done <<< "$config" - args+=($opts) - ;; - phc2sys) - args=(-s /dev/ptp0 -O 0 $opts $config) - ;; - nsm) - args=($opts) - while read line; do args+=("$line"); done <<< "$config" - ;; - *) - echo "unknown client $client" - exit 1 - ;; - esac - - LD_PRELOAD=$CLKNETSIM_PATH/clknetsim.so \ - CLKNETSIM_NODE=$node CLKNETSIM_SOCKET=$CLKNETSIM_TMPDIR/sock \ - $client_wrapper $client$suffix "${args[@]}" &> $CLKNETSIM_TMPDIR/log.$node & - lastpid=$! - disown $lastpid - - client_pids="$client_pids $lastpid" -} - -start_server() { - local nodes=$1 ret=0 - shift - $server_wrapper $CLKNETSIM_PATH/clknetsim "$@" -s $CLKNETSIM_TMPDIR/sock \ - $CLKNETSIM_TMPDIR/conf $nodes > $CLKNETSIM_TMPDIR/stats 2> $CLKNETSIM_TMPDIR/log - if [ $? -ne 0 ]; then - echo clknetsim failed 1>&2 - ret=1 - fi - kill $client_pids &> /dev/null - client_pids=" " - return $ret -} - -generate_seq() { - $CLKNETSIM_PATH/clknetsim -G "$@" -} - -generate_config1() { - local nodes=$1 offset=$2 freqexpr=$3 delayexprup=$4 delayexprdown=$5 refclockexpr=$6 i - - for i in `seq 2 $nodes`; do - echo "node${i}_offset = $offset" - echo "node${i}_freq = $freqexpr" - echo "node${i}_delay1 = $delayexprup" - if [ -n "$delayexprdown" ]; then - echo "node1_delay${i} = $delayexprdown" - else - echo "node1_delay${i} = $delayexprup" - fi - [ -n "$refclockexpr" ] && echo "node${i}_refclock = $refclockexpr" - done > $CLKNETSIM_TMPDIR/conf -} - -generate_config2() { - local nodes=$1 offset=$2 freqexpr=$3 delayexpr=$4 i j - - for i in `seq 2 $nodes`; do - echo "node${i}_offset = $offset" - echo "node${i}_freq = $freqexpr" - for j in `seq 1 $nodes`; do - [ $i -eq $j ] && continue - echo "node${i}_delay${j} = $delayexpr" - echo "node${j}_delay${i} = $delayexpr" - done - done > $CLKNETSIM_TMPDIR/conf -} - -generate_config3() { - local topnodes=$1 nodes=$2 offset=$3 freqexpr=$4 delayexprup=$5 delayexprdown=$6 i j - - for i in `seq $[$topnodes + 1] $nodes`; do - echo "node${i}_offset = $offset" - echo "node${i}_freq = $freqexpr" - for j in `seq 1 $topnodes`; do - [ $i -eq $j ] && continue - echo "node${i}_delay${j} = $delayexprup" - if [ -n "$delayexprdown" ]; then - echo "node${j}_delay${i} = $delayexprdown" - else - echo "node${j}_delay${i} = $delayexprup" - fi - done - done > $CLKNETSIM_TMPDIR/conf -} - -generate_config4() { - local stablenodes=$1 subnets=$2 offset=$3 freqexpr=$4 delayexpr=$5 - local subnet i j added - - echo "$subnets" | tr '|' '\n' | while read subnet; do - for i in $subnet; do - if ! [[ " $stablenodes $added " =~ [^0-9]$i[^0-9] ]]; then - echo "node${i}_offset = $offset" - echo "node${i}_freq = $freqexpr" - fi - for j in $subnet; do - [ $i -eq $j ] && continue - echo "node${i}_delay${j} = $delayexpr" - done - added="$added $i" - done - done > $CLKNETSIM_TMPDIR/conf -} - -find_sync() { - local offlog=$1 freqlog=$2 index=$3 offsync=$4 freqsync=$5 smooth=$6 - - [ -z "$smooth" ] && smooth=0.05 - - paste <(cut -f $index $1) <(cut -f $index $2) | awk ' - BEGIN { - lastnonsync = -1 - time = 0 - } - { - off = $1 < 0 ? -$1 : $1 - freq = $2 < 0 ? -$2 : $2 - - if (avgoff == 0.0 && avgfreq == 0.0) { - avgoff = off - avgfreq = freq - } else { - avgoff += '$smooth' * (off - avgoff) - avgfreq += '$smooth' * (freq - avgfreq) - } - - if (avgoff > '$offsync' || avgfreq > '$freqsync') { - lastnonsync = time - } - time++ - } END { - if (lastnonsync < time) { - print lastnonsync + 1 - } else { - print -1 - } - }' -} - -get_stat() { - local statname=$1 index=$2 - - if [ -z "$index" ]; then - echo $(cat $CLKNETSIM_TMPDIR/stats | grep "^$statname:" | cut -f 2) - else - cat $CLKNETSIM_TMPDIR/stats | grep "^$statname:" | cut -f 2 | - head -n $index | tail -n 1 - fi -} - -check_stat() { - local value=$1 min=$2 max=$3 tolerance=$4 - [ -z "$tolerance" ] && tolerance=0.0 - awk " - BEGIN { - eq = (\"$value\" == \"inf\" || - $value + $value / 1e6 + $tolerance >= $min) && - (\"$max\" == \"inf\" || - (\"$value\" != \"inf\" && - $value - $value / 1e6 - $tolerance <= $max)) - exit !eq - }" -} - -if [ -z "$CLKNETSIM_PATH" ]; then - echo CLKNETSIM_PATH not set 2>&1 - exit 1 -fi - -if [ ! -x "$CLKNETSIM_PATH/clknetsim" -o ! -e "$CLKNETSIM_PATH/clknetsim.so" ]; then - echo "can't find clknetsim or clknetsim.so in $CLKNETSIM_PATH" - exit 1 -fi - -[ -d $CLKNETSIM_TMPDIR ] || mkdir $CLKNETSIM_TMPDIR diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clock.cc b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clock.cc deleted file mode 100644 index 1e01fed..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clock.cc +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#include "clock.h" - -#define MINSEC 256 -#define MAXSEC 2048 -#define MAXTIMECONST 10 -#define MAXMAXERROR 16000000 -#define SHIFT_FLL 2 -#define SCALE_FREQ 65536.0e6 -#define MAXFREQ_SCALED 32768000 -#define MAX_SLEWRATE 500 -#define MAX_TICK(base_tick) ((base_tick) * 11 / 10) -#define MIN_TICK(base_tick) ((base_tick) * 9 / 10) - -#define MIN_FREQ 0.8 -#define MAX_FREQ 1.2 - -Clock::Clock() { - time = 0.0; - mono_time = 0.0; - freq = 1.0; - - freq_generator = NULL; - step_generator = NULL; - - base_tick = sysconf(_SC_CLK_TCK); - assert(base_tick > 0); - base_tick = (1000000 + base_tick / 2) / base_tick; - - memset(&ntp_timex, 0, sizeof(ntp_timex)); - ntp_timex.tick = base_tick; - ntp_timex.tolerance = MAXFREQ_SCALED; - ntp_timex.precision = 1; - - ntp_state = TIME_OK; - - /* in Linux kernel SHIFT_PLL is 2 since 2.6.31 */ - ntp_shift_pll = 2; - ntp_flags = 0; - ntp_update_interval = 0; - ntp_offset = 0.0; - ntp_slew = 0.0; - - ss_offset = 0; - ss_slew = 0; -} - -Clock::~Clock() { - if (freq_generator) - delete freq_generator; - if (step_generator) - delete step_generator; -} - -double Clock::get_real_time() const { - return time; -} - -double Clock::get_monotonic_time() const { - return mono_time; -} - -double Clock::get_total_freq() const { - double timex_freq, adjtime_freq; - - timex_freq = (double)ntp_timex.tick / base_tick + ntp_timex.freq / SCALE_FREQ + ntp_slew; - adjtime_freq = ss_slew / 1e6; - return freq * (timex_freq + adjtime_freq); -} - -double Clock::get_raw_freq() const { - double timex_freq; - - timex_freq = (double)ntp_timex.tick / base_tick + ntp_timex.freq / SCALE_FREQ; - return freq * timex_freq; -} - -double Clock::get_true_interval(double local_interval) const { - return local_interval / get_total_freq(); -} - -double Clock::get_local_interval(double true_interval) const { - return true_interval * get_total_freq(); -} - -void Clock::set_freq_generator(Generator *gen) { - if (freq_generator) - delete freq_generator; - freq_generator = gen; -} - -void Clock::set_step_generator(Generator *gen) { - if (step_generator) - delete step_generator; - step_generator = gen; -} - -void Clock::set_freq(double freq) { - this->freq = freq + 1.0; - if (!(this->freq > MIN_FREQ && this->freq < MAX_FREQ)) { - fprintf(stderr, "frequency %e outside allowed range (%.2f, %.2f)\n", this->freq - 1.0, MIN_FREQ - 1.0, MAX_FREQ - 1.0); - exit(1); - } -} - -void Clock::set_time(double time) { - this->time = time; -} - -void Clock::step_time(double step) { - this->time += step; -} - -void Clock::set_ntp_shift_pll(int shift) { - ntp_shift_pll = shift; -} - -void Clock::set_ntp_flag(int enable, int flag) { - ntp_flags &= ~flag; - if (enable) - ntp_flags |= flag; -} - -void Clock::advance(double real_interval) { - double local_interval = get_local_interval(real_interval); - - time += local_interval; - mono_time += local_interval; -} - -void Clock::update(bool second) { - if (freq_generator) - set_freq(freq_generator->generate(NULL)); - if (step_generator) - step_time(step_generator->generate(NULL)); - - if (!second) - return; - - if (ntp_timex.status & STA_PLL) { - ntp_update_interval++; - ntp_slew = ntp_offset / (1 << (ntp_shift_pll + - ntp_timex.constant)); - -#if 0 - if (ntp_slew > MAX_SLEWRATE / 1e6) - ntp_slew = MAX_SLEWRATE / 1e6; - else if (ntp_slew < -MAX_SLEWRATE / 1e6) - ntp_slew = -MAX_SLEWRATE / 1e6; -#endif - - ntp_offset -= ntp_slew; - - if (ntp_timex.status & STA_NANO) - ntp_timex.offset = ntp_offset * 1e9; - else - ntp_timex.offset = ntp_offset * 1e6; - } - - if (ss_offset) { - if (ss_offset > 0) { - if (ss_offset > MAX_SLEWRATE) { - ss_slew = MAX_SLEWRATE; - ss_offset -= MAX_SLEWRATE; - } else { - ss_slew = ss_offset; - ss_offset = 0; - } - } else { - if (ss_offset < -MAX_SLEWRATE) { - ss_slew = -MAX_SLEWRATE; - ss_offset -= -MAX_SLEWRATE; - } else { - ss_slew = ss_offset; - ss_offset = 0; - } - } - } else - ss_slew = 0; - - switch (ntp_state) { - case TIME_OK: - if (ntp_timex.status & STA_INS) - ntp_state = TIME_INS; - else if (ntp_timex.status & STA_DEL) - ntp_state = TIME_DEL; - break; - case TIME_INS: - if ((time_t)(time + 0.5) % (24 * 3600) <= 1) { - time -= 1.0; - ntp_timex.tai += 1.0; - ntp_state = TIME_OOP; - } else if (!(ntp_timex.status & STA_INS)) { - ntp_state = TIME_OK; - } - break; - case TIME_DEL: - if ((time_t)(time + 1.0 + 0.5) % (24 * 3600) <= 1) { - time += 1.0; - ntp_timex.tai -= 1.0; - ntp_state = TIME_WAIT; - } else if (!(ntp_timex.status & STA_DEL)) { - ntp_state = TIME_OK; - } - break; - case TIME_OOP: - ntp_state = TIME_WAIT; - break; - case TIME_WAIT: - if (!(ntp_timex.status & (STA_INS | STA_DEL))) - ntp_state = TIME_OK; - break; - default: - assert(0); - } -} - -void Clock::update_ntp_offset(long offset) { - double fll_adj, pll_adj, new_offset, old_offset, tc, t; - - if (ntp_timex.status & STA_FREQHOLD) - ntp_update_interval = 0; - - if (ntp_timex.status & STA_NANO) - new_offset = offset / 1e9; - else - new_offset = offset / 1e6; - - tc = 1 << ntp_timex.constant; - ntp_timex.offset = offset; - old_offset = ntp_offset; - ntp_offset = new_offset; - - if (!(ntp_timex.status & STA_PLL)) - return; - - if (old_offset && ntp_update_interval >= MINSEC && - (ntp_timex.status & STA_FLL || ntp_update_interval > MAXSEC)) { - ntp_timex.status |= STA_MODE; - if (ntp_flags & CLOCK_NTP_FLL_MODE2) - fll_adj = (new_offset - old_offset) / (ntp_update_interval * (1 << SHIFT_FLL)); - else - fll_adj = new_offset / (ntp_update_interval * (1 << SHIFT_FLL)); - } else { - ntp_timex.status &= ~STA_MODE; - fll_adj = 0.0; - } - - if (ntp_flags & CLOCK_NTP_PLL_CLAMP) { - if (ntp_update_interval > MAXSEC) - ntp_update_interval = MAXSEC; - if (ntp_update_interval > tc * (1 << (ntp_shift_pll + 1))) - ntp_update_interval = tc * (1 << (ntp_shift_pll + 1)); - } - - t = 4 * (1 << ntp_shift_pll) * tc; - pll_adj = new_offset * ntp_update_interval / (t * t); - - ntp_timex.freq += (fll_adj + pll_adj) * SCALE_FREQ; - - if (ntp_timex.freq > MAXFREQ_SCALED) - ntp_timex.freq = MAXFREQ_SCALED; - else if (ntp_timex.freq < -MAXFREQ_SCALED) - ntp_timex.freq = -MAXFREQ_SCALED; - - ntp_update_interval = 0; -} - -int Clock::adjtimex(struct timex *buf) { - int r = ntp_state; - struct timex t; - - if (buf->modes & ADJ_FREQUENCY) { - ntp_timex.freq = buf->freq; - if (ntp_timex.freq > MAXFREQ_SCALED) - ntp_timex.freq = MAXFREQ_SCALED; - else if (ntp_timex.freq < -MAXFREQ_SCALED) - ntp_timex.freq = -MAXFREQ_SCALED; - } - if (buf->modes & ADJ_MAXERROR) - ntp_timex.maxerror = buf->maxerror; - if (buf->modes & ADJ_STATUS) { - if ((buf->status & STA_PLL) && !(ntp_timex.status & STA_PLL)) - ntp_update_interval = 0; - ntp_timex.status = buf->status & 0xff; - } - if (buf->modes & ADJ_MICRO) - ntp_timex.status &= ~STA_NANO; - if (buf->modes & ADJ_NANO) - ntp_timex.status |= STA_NANO; - if (buf->modes & ADJ_TIMECONST) { - ntp_timex.constant = buf->constant; - if (!(ntp_timex.status & STA_NANO)) - ntp_timex.constant += 4; - if (ntp_timex.constant > MAXTIMECONST) - ntp_timex.constant = MAXTIMECONST; - if (ntp_timex.constant < 0) - ntp_timex.constant = 0; - } - if (buf->modes & ADJ_TICK) { - if (buf->tick > MAX_TICK(base_tick) || buf->tick < MIN_TICK(base_tick)) { - r = -1; - } else - ntp_timex.tick = buf->tick; - } - if ((buf->modes & ADJ_OFFSET_SINGLESHOT) != ADJ_OFFSET_SINGLESHOT) { - if (buf->modes & ADJ_OFFSET) { - update_ntp_offset(buf->offset); - } - } - if (buf->modes & ADJ_SETOFFSET) { - if (ntp_timex.status & STA_NANO) - time += buf->time.tv_sec + buf->time.tv_usec * 1e-9; - else - time += buf->time.tv_sec + buf->time.tv_usec * 1e-6; - ntp_timex.maxerror = MAXMAXERROR; - } - if (buf->modes & ADJ_TAI) { - ntp_timex.tai = buf->constant; - } - - t = ntp_timex; - - if ((buf->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) { - if ((buf->modes & ADJ_OFFSET_SS_READ) == ADJ_OFFSET_SINGLESHOT) { - t.offset = ss_offset; - ss_offset = buf->offset; - } else { - t.offset = ss_offset; - } - } - - *buf = t; - - return r; -} - -int Clock::adjtime(const struct timeval *delta, struct timeval *olddelta) { - if (olddelta) { - olddelta->tv_sec = ss_offset / 1000000; - olddelta->tv_usec = ss_offset % 1000000; - } - if (delta) - ss_offset = delta->tv_sec * 1000000 + delta->tv_usec; - return 0; -} - -Refclock::Refclock() { - time = 0.0; - offset = 0.0; - generate = false; - valid = false; - offset_generator = NULL; -} - -Refclock::~Refclock() { - if (offset_generator) - delete offset_generator; -} - -void Refclock::set_offset_generator(Generator *gen) { - if (offset_generator) - delete offset_generator; - offset_generator = gen; -} - -void Refclock::set_generation(bool enable) { - generate = enable; -} - -void Refclock::update(double time, const Clock *clock) { - if (!generate || !offset_generator) - return; - - this->time = clock->get_real_time(); - offset = this->time - time + offset_generator->generate(NULL); - valid = true; -} - -bool Refclock::get_sample(double *time, double *offset) const { - *time = this->time; - *offset = this->offset; - return valid; -} - -void Refclock::get_offsets(double *offsets, int size) { - int i; - - for (i = 0; i < size; i++) - offsets[i] = offset_generator->generate(NULL); -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clock.h b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clock.h deleted file mode 100644 index cb3ead1..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/clock.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#ifndef CLOCK_H -#define CLOCK_H - -#include "sysheaders.h" - -#include "generator.h" - -#define CLOCK_NTP_FLL_MODE2 0x1 -#define CLOCK_NTP_PLL_CLAMP 0x2 - -class Clock { - double time; - double mono_time; - double freq; - - Generator *freq_generator; - Generator *step_generator; - - long base_tick; - - struct timex ntp_timex; - int ntp_state; - int ntp_shift_pll; - int ntp_flags; - long ntp_update_interval; - double ntp_offset; - double ntp_slew; - - long ss_offset; - long ss_slew; - -public: - Clock(); - ~Clock(); - double get_real_time() const; - double get_monotonic_time() const; - double get_total_freq() const; - double get_raw_freq() const; - double get_true_interval(double local_interval) const; - double get_local_interval(double true_interval) const; - - void set_freq_generator(Generator *gen); - void set_step_generator(Generator *gen); - void set_freq(double freq); - void set_time(double time); - void step_time(double step); - void set_ntp_shift_pll(int shift); - void set_ntp_flag(int enable, int flag); - - void advance(double real_interval); - void update(bool second); - - void update_ntp_offset(long offset); - int adjtimex(struct timex *buf); - int adjtime(const struct timeval *delta, struct timeval *olddelta); -}; - -class Refclock { - double time; - double offset; - bool generate; - bool valid; - - Generator *offset_generator; -public: - Refclock(); - ~Refclock(); - void set_offset_generator(Generator *gen); - void update(double time, const Clock *clock); - void set_generation(bool enable); - bool get_sample(double *time, double *offset) const; - void get_offsets(double *offsets, int size); -}; - -#endif diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/busybox.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/busybox.test deleted file mode 100755 index d0430b3..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/busybox.test +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config1 2 0.01 "(+ 1e-6 (sum (* 1e-9 (normal))))" "(+ 1e-3 (* 1e-3 (exponential)))" - -start_client 1 chrony "local stratum 1" -start_client 2 busybox "192.168.123.1" - -start_server 2 -v 2 -o log.offset -f log.freq -g log.rawfreq -p log.packets -r 2000 -l 400000 - -cat tmp/stats diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/chronyc.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/chronyc.test deleted file mode 100755 index 116bfee..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/chronyc.test +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config4 "1" "1 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" - -echo "node3_start = 10000" >> tmp/conf -start_client 1 chronyd "local stratum 1" -start_client 2 chronyd "server 192.168.123.1" -start_client 3 chronyc "tracking -sources -n -sourcestats" "" "-h 192.168.123.2" - -start_server 3 -v 2 -l 10001 - -cat tmp/log.3 diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/chronyd.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/chronyd.test deleted file mode 100755 index 0e2de1f..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/chronyd.test +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config1 2 0.01 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" "" "(* 1e-4 (normal))" - -start_client 1 chrony "local stratum 1" -start_client 2 chrony "server 192.168.123.1 minpoll 6 maxpoll 6 -refclock SHM 0" - -start_server 2 -v 2 -o log.offset -f log.freq -g log.rawfreq -p log.packets -r 2000 -l 40000 - -cat tmp/stats diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/nsm.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/nsm.test deleted file mode 100755 index b55120c..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/nsm.test +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config4 "1 3" "1 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" - -echo 'node3_start = 50' >> tmp/conf - -start_client 1 ptp4l "hybrid_e2e 1 -net_sync_monitor 1" "" "-i eth0" -start_client 2 ptp4l "hybrid_e2e 1 -net_sync_monitor 1" "" "-i eth0" -start_client 3 nsm "NSM 192.168.123.1 -NSM 192.168.123.2" "" "-i eth0" - -start_server 3 -l 110 - -cat tmp/log.3 diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpd.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpd.test deleted file mode 100755 index 5609b12..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpd.test +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config1 3 0.01 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" -echo "node2_shift_pll = 2" >> tmp/conf - -start_client 1 ntp "server 127.127.1.0" -start_client 2 ntp "server 192.168.123.1 minpoll 6 maxpoll 6" -start_client 3 ntp "server 192.168.123.1 minpoll 6 maxpoll 6" - -start_server 3 -v 2 -o log.offset -r 2000 -l 40000 - -cat tmp/stats - -echo -get_stat 'RMS offset' -get_stat 'RMS frequency' diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpdate.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpdate.test deleted file mode 100755 index 9b49180..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpdate.test +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config1 3 10.0 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" - -echo "node2_start = 100" >> tmp/conf -echo "node3_start = 100" >> tmp/conf - -start_client 1 ntpd "server 127.127.1.0" -start_client 2 ntpdate "-B 192.168.123.1" -start_client 3 ntpdate "-b 192.168.123.1" - -start_server 3 -v 2 -o log.offset -r 110 -l 200 - -cat tmp/stats diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpq.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpq.test deleted file mode 100755 index b2b8763..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ntpq.test +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config4 "1" "1 2 | 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" - -echo 'node3_start = 1000' >> tmp/conf - -start_client 1 ntpd "server 127.127.1.0" -start_client 2 ntpd "server 192.168.123.1 minpoll 6 maxpoll 6" -start_client 3 ntpq "rv 0 -peers" "" "192.168.123.2" - -start_server 3 -l 1010 - -cat tmp/log.3 diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/phc2sys.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/phc2sys.test deleted file mode 100755 index b948819..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/phc2sys.test +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -echo "node1_freq = (sum (* 1e-9 (normal)))" > tmp/conf -echo "node1_refclock = (* 1e-6 (normal))" >> tmp/conf - -start_client 1 phc2sys "" - -start_server 1 -v 2 -o log.offset -f log.freq -r 1000 -l 4000 - -cat tmp/stats diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/pmc.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/pmc.test deleted file mode 100755 index 5c6e44d..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/pmc.test +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config4 "1" "1 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" - -echo 'node3_start = 100' >> tmp/conf - -start_client 1 ptp4l "" "" "-i eth0" -start_client 2 ptp4l "" "" "-i eth0" -start_client 3 pmc " -GET TIME_STATUS_NP -GET TIME_PROPERTIES_DATA_SET -GET PORT_DATA_SET" - -start_server 3 -l 110 - -cat tmp/log.3 diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ptp4l.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ptp4l.test deleted file mode 100755 index be58af7..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/ptp4l.test +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config4 "1" "1 2 3 | 3 4" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" - -start_client 1 ptp4l "clockClass 6" "" "-i eth0" -start_client 2 ptp4l "time_stamping software" "" "-i eth0" -start_client 3 ptp4l "" "" "-i eth0 -i eth1" -start_client 4 ptp4l "" "" "-i eth1" - -start_server 4 -n 2 -o log.offset -p log.packets -r 100 -l 1000 - -cat tmp/stats diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/sntp.test b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/sntp.test deleted file mode 100755 index 28e8bfa..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/examples/sntp.test +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -CLKNETSIM_PATH=.. -. ../clknetsim.bash - -generate_config1 3 10.0 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" - -echo "node2_start = 100" >> tmp/conf -echo "node3_start = 100" >> tmp/conf - -start_client 1 ntpd "server 127.127.1.0" -start_client 2 sntp "-j 192.168.123.1" -start_client 3 sntp "-s 192.168.123.1" - -start_server 3 -v 2 -o log.offset -r 110 -l 200 - -cat tmp/stats diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/generator.cc b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/generator.cc deleted file mode 100644 index 8ca1bc0..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/generator.cc +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#include "generator.h" - -static void syntax_assert(bool condition) { - if (!condition) { - fprintf(stderr, "syntax error\n"); - exit(1); - } -} - -Generator::Generator(const vector *input) { - if (input) - this->input = *input; - constant = false; -} - -Generator::~Generator() { - while (!input.empty()) { - delete input.back(); - input.pop_back(); - } -} - -bool Generator::is_constant() const { - return constant; -} - -Generator_float::Generator_float(double f): Generator(NULL) { - this->f = f; - constant = true; -} - -double Generator_float::generate(const Generator_variables *variables) { - return f; -} - -Generator_variable::Generator_variable(string name): Generator(NULL) { - this->name = name; -} - -double Generator_variable::generate(const Generator_variables *variables) { - Generator_variables::const_iterator iter; - - syntax_assert(variables); - iter = variables->find(name); - syntax_assert(iter != variables->end()); - - return iter->second; -} - -Generator_random_uniform::Generator_random_uniform(const vector *input): - Generator(NULL) { - syntax_assert(!input || input->size() == 0); -} - -double Generator_random_uniform::generate(const Generator_variables *variables) { - double x; - - x = ((random() & 0x7fffffff) + 1) / 2147483649.0; - x = ((random() & 0x7fffffff) + x) / 2147483648.0; - - return x; -} - -Generator_random_normal::Generator_random_normal(const vector *input): - Generator(NULL), uniform(NULL) { - syntax_assert(!input || input->size() == 0); -} - -double Generator_random_normal::generate(const Generator_variables *variables) { - /* Marsaglia polar method */ - - double x, y, s; - - do { - x = 2.0 * uniform.generate(variables) - 1.0; - y = 2.0 * uniform.generate(variables) - 1.0; - s = x * x + y * y; - } while (s >= 1.0); - - x *= sqrt(-2.0 * log(s) / s); - - return x; -} - -Generator_random_exponential::Generator_random_exponential(const vector *input): - Generator(NULL), uniform(NULL) { - syntax_assert(!input || input->size() == 0); -} - -double Generator_random_exponential::generate(const Generator_variables *variables) { - return -log(uniform.generate(variables)); -} - -Generator_random_poisson::Generator_random_poisson(const vector *input): - Generator(NULL), uniform(NULL) { - double lambda; - - syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); - - lambda = (*input)[0]->generate(NULL); - syntax_assert(lambda >= 1 && lambda <= 20); - L = exp(-lambda); -} - -double Generator_random_poisson::generate(const Generator_variables *variables) { - double p; - int k; - - for (p = 1.0, k = 0; k < 100; k++) { - p *= uniform.generate(variables); - if (p <= L) - break; - } - - return k; -} - -Generator_file::Generator_file(const char *file): Generator(NULL) { - input = fopen(file, "r"); - if (!input) { - fprintf(stderr, "can't open %s\n", file); - exit(1); - } -} - -Generator_file::~Generator_file() { - fclose(input); -} - -double Generator_file::generate(const Generator_variables *variables) { - double x; - - while (1) { - if (fscanf(input, "%lf", &x) != 1) { - if (feof(input)) { - fseek(input, 0, SEEK_SET); - continue; - } - assert(0); - } - break; - } - return x; -} - -Generator_wave_pulse::Generator_wave_pulse(const vector *input): - Generator(NULL) { - syntax_assert(input && input->size() == 2 && - (*input)[0]->is_constant() && (*input)[1]->is_constant()); - high = (*input)[0]->generate(NULL); - low = (*input)[1]->generate(NULL); - counter = 0; -} - -double Generator_wave_pulse::generate(const Generator_variables *variables) { - counter++; - if (counter > high + low) - counter = 1; - if (counter <= high) - return 1.0; - return -1.0; -} - -Generator_wave_sine::Generator_wave_sine(const vector *input): - Generator(NULL) { - syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); - length = (*input)[0]->generate(NULL); - counter = 0; -} - -double Generator_wave_sine::generate(const Generator_variables *variables) { - return sin(counter++ / length * 2 * M_PI); -} - -Generator_wave_cosine::Generator_wave_cosine(const vector *input): - Generator(NULL) { - syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); - length = (*input)[0]->generate(NULL); - counter = 0; -} - -double Generator_wave_cosine::generate(const Generator_variables *variables) { - return cos(counter++ / length * 2 * M_PI); -} - -Generator_wave_triangle::Generator_wave_triangle(const vector *input): - Generator(NULL) { - syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); - length = (*input)[0]->generate(NULL); - counter = 0; -} - -double Generator_wave_triangle::generate(const Generator_variables *variables) { - double phase; - phase = counter / length - floor(counter / length); - counter++; - return -4.0 * (fabs(phase - 0.5) - 0.25); - -} - -Generator_sum::Generator_sum(const vector *input): - Generator(input) { - sum = 0.0; -} - -double Generator_sum::generate(const Generator_variables *variables) { - unsigned int i; - - for (i = 0; i < input.size(); i++) - sum += input[i]->generate(variables); - return sum; -} - -Generator_multiply::Generator_multiply(const vector *input): - Generator(input) { -} - -double Generator_multiply::generate(const Generator_variables *variables) { - unsigned int i; - double x = 1.0; - - for (i = 0; i < input.size(); i++) - x *= input[i]->generate(variables); - return x; -} - -Generator_add::Generator_add(const vector *input): - Generator(input) { -} - -double Generator_add::generate(const Generator_variables *variables) { - unsigned int i; - double x = 0.0; - - for (i = 0; i < input.size(); i++) - x += input[i]->generate(variables); - return x; -} - -Generator_modulo::Generator_modulo(const vector *input): - Generator(input) { - syntax_assert(input && input->size() > 0); -} - -double Generator_modulo::generate(const Generator_variables *variables) { - unsigned int i; - double x = input[0]->generate(variables); - - for (i = 1; i < input.size(); i++) - x = fmod(x, input[i]->generate(variables)); - - return x; -} - -Generator_equal::Generator_equal(const vector *input): - Generator(input) { - syntax_assert(input && input->size() > 0); -} - -double Generator_equal::generate(const Generator_variables *variables) { - unsigned int i; - double x, min = 0.0, max = 0.0, epsilon = input[0]->generate(variables); - - for (i = 1; i < input.size(); i++) { - x = input[i]->generate(variables); - if (i == 1 || min > x) - min = x; - if (i == 1 || max < x) - max = x; - } - - return max - min <= epsilon ? 1.0 : 0.0; -} - -Generator_max::Generator_max(const vector *input): - Generator(input) { - syntax_assert(input && input->size() > 0); -} - -double Generator_max::generate(const Generator_variables *variables) { - unsigned int i; - double x, max = 0.0; - - for (i = 0; i < input.size(); i++) { - x = input[i]->generate(variables); - if (!i || max < x) - max = x; - } - - return max; -} - -Generator_min::Generator_min(const vector *input): - Generator(input) { - syntax_assert(input && input->size() > 0); -} - -double Generator_min::generate(const Generator_variables *variables) { - unsigned int i; - double x, min = 0.0; - - for (i = 0; i < input.size(); i++) { - x = input[i]->generate(variables); - if (!i || min > x) - min = x; - } - - return min; -} - -Generator_generator::Generator_generator() { -} - -Generator_generator::~Generator_generator() { -} - -Generator *Generator_generator::generate(char *code) const { - const char *ws = " \t\n\r", *wsp = " \t\n\r()"; - int len, paren; - Generator *ret; - vector generators; - char *arg, *name, *end, *string = NULL; - - //printf("code: |%s|\n", code); - len = strlen(code); - end = code + len; - - if (code[0] == '(') { - syntax_assert(len > 2 && code[len - 1] == ')'); - code[len - 1] = '\0'; - code++; - end = code + len - 2; - } - - code += strspn(code, ws); - - name = code; - - code += strcspn(code, wsp); - code[0] = '\0'; - code++; - - code += strspn(code, ws); - - while (code < end) { - arg = code; - - if (arg[0] == '(') { - code = ++arg; - for (paren = 1; code < end; code++) { - if (code[0] == '(') - paren++; - else if (code[0] == ')') - paren--; - if (paren == 0) - break; - } - - syntax_assert(paren == 0 && code[0] == ')'); - code[0] = '\0'; - code++; - - //printf("generator: %s\n", arg); - generators.push_back(generate(arg)); - syntax_assert(generators.back()); - } else if (arg[0] == '"') { - string = code = ++arg; - code += strcspn(code, "\""); - syntax_assert(code[0] == '"'); - code[0] = '\0'; - code++; - //printf("string: |%s|\n", string); - } else { - code += strcspn(code, wsp); - syntax_assert(code[0] != ')' && code[0] != '('); - code[0] = '\0'; - code++; - if (isalpha(arg[0])) { - generators.push_back(new Generator_variable(arg)); - //printf("variable: %s\n", arg); - } else { - generators.push_back(new Generator_float(atof(arg))); - //printf("float: %f\n", generators.back()->generate()); - } - } - - code += strspn(code, ws); - } - - if (strcmp(name, "*") == 0) - ret = new Generator_multiply(&generators); - else if (strcmp(name, "+") == 0) - ret = new Generator_add(&generators); - else if (strcmp(name, "%") == 0) - ret = new Generator_modulo(&generators); - else if (strcmp(name, "sum") == 0) - ret = new Generator_sum(&generators); - else if (strcmp(name, "uniform") == 0) - ret = new Generator_random_uniform(&generators); - else if (strcmp(name, "normal") == 0) - ret = new Generator_random_normal(&generators); - else if (strcmp(name, "exponential") == 0) - ret = new Generator_random_exponential(&generators); - else if (strcmp(name, "poisson") == 0) - ret = new Generator_random_poisson(&generators); - else if (strcmp(name, "file") == 0) - ret = new Generator_file(string); - else if (strcmp(name, "pulse") == 0) - ret = new Generator_wave_pulse(&generators); - else if (strcmp(name, "sine") == 0) - ret = new Generator_wave_sine(&generators); - else if (strcmp(name, "cosine") == 0) - ret = new Generator_wave_cosine(&generators); - else if (strcmp(name, "triangle") == 0) - ret = new Generator_wave_triangle(&generators); - else if (strcmp(name, "equal") == 0) - ret = new Generator_equal(&generators); - else if (strcmp(name, "max") == 0) - ret = new Generator_max(&generators); - else if (strcmp(name, "min") == 0) - ret = new Generator_min(&generators); - else { - ret = NULL; - syntax_assert(0); - } - - return ret; -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/generator.h b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/generator.h deleted file mode 100644 index e8a6fe2..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/generator.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#ifndef GENERATOR_H -#define GENERATOR_H - -#include "sysheaders.h" -#include -#include -#include - -using namespace std; - -typedef map Generator_variables; - -class Generator { - protected: - vector input; - bool constant; - - public: - Generator(const vector *input); - virtual ~Generator(); - virtual double generate(const Generator_variables *variables) = 0; - bool is_constant() const; -}; - -class Generator_float: public Generator { - double f; - - public: - Generator_float(double f); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_variable: public Generator { - string name; - - public: - Generator_variable(string name); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_random_uniform: public Generator { - public: - Generator_random_uniform(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_random_normal: public Generator { - Generator_random_uniform uniform; - - public: - Generator_random_normal(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_random_exponential: public Generator { - Generator_random_uniform uniform; - - public: - Generator_random_exponential(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_random_poisson: public Generator { - Generator_random_uniform uniform; - double L; - - public: - Generator_random_poisson(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_file: public Generator { - FILE *input; - - public: - Generator_file(const char *file); - virtual ~Generator_file(); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_wave_pulse: public Generator { - int high; - int low; - int counter; - - public: - Generator_wave_pulse(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_wave_sine: public Generator { - double length; - int counter; - - public: - Generator_wave_sine(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_wave_cosine: public Generator { - double length; - int counter; - - public: - Generator_wave_cosine(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_wave_triangle: public Generator { - double length; - int counter; - - public: - Generator_wave_triangle(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_sum: public Generator { - double sum; - public: - Generator_sum(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_multiply: public Generator { - public: - Generator_multiply(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_add: public Generator { - public: - Generator_add(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_modulo: public Generator { - public: - Generator_modulo(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_equal: public Generator { - public: - Generator_equal(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_max: public Generator { - public: - Generator_max(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_min: public Generator { - public: - Generator_min(const vector *input); - virtual double generate(const Generator_variables *variables); -}; - -class Generator_generator { - public: - Generator_generator(); - ~Generator_generator(); - Generator *generate(char *code) const; -}; - -#endif diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/network.cc b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/network.cc deleted file mode 100644 index 7b2fcbe..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/network.cc +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#include "sysheaders.h" -#include "network.h" - -Packet_queue::Packet_queue() { -} - -Packet_queue::~Packet_queue() { - while (!queue.empty()) { - delete queue.back(); - queue.pop_back(); - } -} - -void Packet_queue::insert(struct Packet *packet) { - deque::iterator i; - - for (i = queue.begin(); i < queue.end(); i++) - if (packet->receive_time < (*i)->receive_time) - break; - queue.insert(i, packet); -} - -struct Packet *Packet_queue::dequeue() { - struct Packet *ret; - - assert(!queue.empty()); - ret = queue.front(); - queue.pop_front(); - - return ret; -} - -double Packet_queue::get_timeout(double time) const { - if (!queue.empty()) { - return queue[0]->receive_time - time; - } - return 1e20; -} - -Network::Network(const char *socket, unsigned int n, unsigned int subnets, unsigned int rate) { - time = 0.0; - this->subnets = subnets; - socket_name = socket; - update_rate = rate; - update_count = 0; - offset_log = NULL; - freq_log = NULL; - rawfreq_log = NULL; - packet_log = NULL; - - assert(n > 0); - - while (nodes.size() < n) - nodes.push_back(new Node(nodes.size(), this)); - - stats.resize(n); - link_delays.resize(n * n); -} - -Network::~Network() { - while (!nodes.empty()) { - delete nodes.back(); - nodes.pop_back(); - } - - while (!link_delays.empty()) { - delete link_delays.back(); - link_delays.pop_back(); - } - - unlink(socket_name); - - if (offset_log) - fclose(offset_log); - if (freq_log) - fclose(freq_log); - if (rawfreq_log) - fclose(rawfreq_log); - if (packet_log) - fclose(packet_log); -} - - -bool Network::prepare_clients() { - struct sockaddr_un s; - int sockfd, fd; - unsigned int i; - - s.sun_family = AF_UNIX; - snprintf(s.sun_path, sizeof (s.sun_path), "%s", socket_name); - - sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (sockfd < 0) { - fprintf(stderr, "socket() failed\n"); - return false; - } - - unlink(socket_name); - if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) { - fprintf(stderr, "bind() failed\n"); - return false; - } - - if (listen(sockfd, nodes.size()) < 0) { - fprintf(stderr, "listen() failed\n"); - return false; - } - - for (i = 0; i < nodes.size(); i++) { - Request_packet req; - unsigned int node; - - fprintf(stderr, "\rWaiting for %u clients...", (unsigned int)nodes.size() - i); - fd = accept(sockfd, NULL, NULL); - if (fd < 0) { - fprintf(stderr, "accept() failed\n"); - return false; - } - - if (recv(fd, &req, sizeof (req), 0) != offsetof(Request_packet, data) + - sizeof (Request_register) || req.header.request != REQ_REGISTER) { - fprintf(stderr, "client didn't register correctly.\n"); - return false; - } - node = req.data._register.node; - assert(node < nodes.size() && nodes[node]->get_fd() < 0); - nodes[node]->set_fd(fd); - } - fprintf(stderr, "done\n"); - - close(sockfd); - - update(); - - return true; -} - -Node *Network::get_node(unsigned int node) { - assert(node < nodes.size()); - return nodes[node]; -} - -void Network::set_link_delay_generator(unsigned int from, unsigned int to, Generator *generator) { - unsigned int i; - - assert(from < nodes.size() && to < nodes.size()); - - i = from * nodes.size() + to; - if (link_delays[i]) - delete link_delays[i]; - link_delays[i] = generator; -} - -bool Network::run(double time_limit) { - int i, n = nodes.size(), waiting; - bool pending_update; - double min_timeout, timeout, next_update; - - while (time < time_limit) { - for (i = 0, waiting = 0; i < n; i++) - if (nodes[i]->waiting()) - waiting++; - else - stats[i].update_wakeup_stats(); - - while (waiting < n) { - for (i = 0; i < n; i++) { - if (nodes[i]->waiting()) - continue; - if (!nodes[i]->process_fd()) { - fprintf(stderr, "client %d failed.\n", i + 1); - return false; - } - if (nodes[i]->waiting()) - waiting++; - } - } - - do { - min_timeout = nodes[0]->get_timeout(); - for (i = 1; i < n; i++) { - timeout = nodes[i]->get_timeout(); - if (min_timeout > timeout) - min_timeout = timeout; - } - - timeout = packet_queue.get_timeout(time); - if (timeout <= min_timeout) - min_timeout = timeout; - - next_update = floor(time) + (double)(update_count + 1) / update_rate; - timeout = next_update - time; - if (timeout <= min_timeout) { - min_timeout = timeout; - pending_update = true; - } else - pending_update = false; - - //min_timeout += 1e-12; - assert(min_timeout >= 0.0); - - if (pending_update) - time = next_update; - else - time += min_timeout; - - for (i = 0; i < n; i++) - nodes[i]->get_clock()->advance(min_timeout); - - if (pending_update) - update(); - } while (pending_update && time < time_limit); - - for (i = 0; i < n; i++) - nodes[i]->resume(); - - while (packet_queue.get_timeout(time) <= 0) { - assert(packet_queue.get_timeout(time) > -1e-10); - struct Packet *packet = packet_queue.dequeue(); - stats[packet->to].update_packet_stats(true, time, packet->delay); - nodes[packet->to]->receive(packet); - } - } - - return true; -} - -void Network::update() { - int i, n = nodes.size(); - - update_count++; - update_count %= update_rate; - - for (i = 0; i < n; i++) { - nodes[i]->get_clock()->update(update_count == 0); - nodes[i]->get_refclock()->update(time, nodes[i]->get_clock()); - } - - update_clock_stats(); -} - -void Network::update_clock_stats() { - int i, n = nodes.size(); - - if (offset_log) { - for (i = 0; i < n; i++) - fprintf(offset_log, "%.9f%c", nodes[i]->get_clock()->get_real_time() - time, i + 1 < n ? '\t' : '\n'); - } - if (freq_log) { - for (i = 0; i < n; i++) - fprintf(freq_log, "%e%c", nodes[i]->get_clock()->get_total_freq() - 1.0, i + 1 < n ? '\t' : '\n'); - } - if (rawfreq_log) { - for (i = 0; i < n; i++) - fprintf(rawfreq_log, "%e%c", nodes[i]->get_clock()->get_raw_freq() - 1.0, i + 1 < n ? '\t' : '\n'); - } - - for (i = 0; i < n; i++) - stats[i].update_clock_stats(nodes[i]->get_clock()->get_real_time() - time, - nodes[i]->get_clock()->get_total_freq() - 1.0, - nodes[i]->get_clock()->get_raw_freq() - 1.0); -} - -void Network::open_offset_log(const char *log) { - offset_log = fopen(log, "w"); -} - -void Network::open_freq_log(const char *log) { - freq_log = fopen(log, "w"); -} - -void Network::open_rawfreq_log(const char *log) { - rawfreq_log = fopen(log, "w"); -} - -void Network::open_packet_log(const char *log) { - packet_log = fopen(log, "w"); -} - -void Network::print_stats(int verbosity) const { - int i, n = nodes.size(); - - if (verbosity <= 0) - return; - - for (i = 0; i < n; i++) { - if (verbosity > 1) - printf("\n---------------------- Node %d ----------------------\n\n", i + 1); - stats[i].print(verbosity); - } - if (verbosity == 1) - printf("\n"); -} - -void Network::reset_stats() { - int i, n = nodes.size(); - - for (i = 0; i < n; i++) - stats[i].reset(); -} - -void Network::reset_clock_stats() { - int i, n = nodes.size(); - - for (i = 0; i < n; i++) - stats[i].reset_clock_stats(); -} - -void Network::send(struct Packet *packet) { - double delay = -1.0; - unsigned int i; - - /* broadcast */ - if (packet->to == (unsigned int)-1) { - for (i = 0; i < nodes.size(); i++) { - struct Packet *p; - - if (i == packet->from) - continue; - - p = new struct Packet; - memcpy(p, packet, sizeof (struct Packet)); - p->to = i; - - send(p); - } - - delete packet; - return; - } - - assert(packet->to < nodes.size() && packet->from < nodes.size() && - packet->subnet < subnets); - - i = packet->from * nodes.size() + packet->to; - - if (link_delays[i]) { - link_delay_variables["time"] = time; - link_delay_variables["from"] = packet->from + 1; - link_delay_variables["to"] = packet->to + 1; - link_delay_variables["subnet"] = packet->subnet + 1; - link_delay_variables["port"] = packet->dst_port; - link_delay_variables["length"] = packet->len; - - delay = link_delays[i]->generate(&link_delay_variables); - } - - stats[packet->from].update_packet_stats(false, time, delay); - - if (packet_log) - fprintf(packet_log, "%e\t%d\t%d\t%e\t%d\t%d\t%d\n", time, - packet->from + 1, packet->to + 1, delay, - packet->src_port, packet->dst_port, - packet->subnet + 1); - - if (delay > 0.0) { - packet->receive_time = time + delay; - packet->delay = delay; - packet_queue.insert(packet); -#ifdef DEBUG - printf("sending packet from %d to %d:%d:%d at %f delay %f \n", - packet->from, packet->subnet, packet->to, - packet->dst_port, time, delay); -#endif - } else { -#ifdef DEBUG - printf("dropping packet from %d to %d:%d:%d at %f\n", - packet->from, packet->subnet, packet->to, - packet->dst_port, time); -#endif - delete packet; - } -} - -double Network::get_time() const { - return time; -} - -unsigned int Network::get_subnets() const { - return subnets; -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/network.h b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/network.h deleted file mode 100644 index 4270365..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/network.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#ifndef NETWORK_H -#define NETWORK_H - -#include -#include - -using namespace std; - -#include "node.h" -#include "stats.h" - -struct Packet { - double receive_time; - double delay; - int broadcast; - unsigned int subnet; - unsigned int from; - unsigned int to; - unsigned int src_port; - unsigned int dst_port; - unsigned int len; - char data[MAX_PACKET_SIZE]; -}; - -class Packet_queue { - deque queue; - public: - Packet_queue(); - ~Packet_queue(); - void insert(struct Packet *packet); - struct Packet *dequeue(); - double get_timeout(double time) const; -}; - -class Network { - double time; - unsigned int subnets; - unsigned int update_rate; - unsigned int update_count; - - const char *socket_name; - vector nodes; - vector link_delays; - vector stats; - - Generator_variables link_delay_variables; - - Packet_queue packet_queue; - - FILE *offset_log; - FILE *freq_log; - FILE *rawfreq_log; - FILE *packet_log; - - void update(); - void update_clock_stats(); - - public: - Network(const char *socket, unsigned int n, unsigned int s, unsigned int rate); - ~Network(); - bool prepare_clients(); - Node *get_node(unsigned int node); - void set_link_delay_generator(unsigned int from, unsigned int to, Generator *generator); - bool run(double time_limit); - void open_offset_log(const char *log); - void open_freq_log(const char *log); - void open_rawfreq_log(const char *log); - void open_packet_log(const char *log); - void print_stats(int verbosity) const; - void reset_stats(); - void reset_clock_stats(); - - void send(struct Packet *packet); - double get_time() const; - unsigned int get_subnets() const; -}; - -#endif diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/node.cc b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/node.cc deleted file mode 100644 index ae8d53e..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/node.cc +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#include "node.h" -#include "network.h" -#include "protocol.h" -#include "sysheaders.h" - -Node::Node(int index, Network *network) { - this->network = network; - this->index = index; - fd = -1; - pending_request = REQ_REGISTER; - start_time = 0.0; - terminate = false; -} - -Node::~Node() { - while (!incoming_packets.empty()) { - delete incoming_packets.back(); - incoming_packets.pop_back(); - } - - terminate = true; - - do { - if (waiting()) - resume(); - } while (process_fd()); - - if (fd >= 0) - close(fd); -} - -void Node::set_fd(int fd) { - this->fd = fd; -} - -int Node::get_fd() const { - return fd; -} - -void Node::set_start_time(double time) { - start_time = time; -} - -bool Node::process_fd() { - Request_packet request; - int received, reqlen; - - received = recv(fd, &request, sizeof (request), 0); - if (received < (int)sizeof (request.header)) - return false; - - reqlen = received - (int)offsetof(Request_packet, data); - - assert(pending_request == 0); - pending_request = request.header.request; - -#ifdef DEBUG - printf("received request %ld in node %d at %f\n", - pending_request, index, clock.get_real_time()); -#endif - - switch (pending_request) { - case REQ_GETTIME: - assert(reqlen == 0); - process_gettime(); - break; - case REQ_SETTIME: - assert(reqlen == sizeof (Request_settime)); - process_settime(&request.data.settime); - break; - case REQ_ADJTIMEX: - assert(reqlen == sizeof (Request_adjtimex)); - process_adjtimex(&request.data.adjtimex); - break; - case REQ_ADJTIME: - assert(reqlen == sizeof (Request_adjtime)); - process_adjtime(&request.data.adjtime); - break; - case REQ_SELECT: - assert(reqlen == sizeof (Request_select)); - process_select(&request.data.select); - break; - case REQ_SEND: - /* request with variable length */ - assert(reqlen >= (int)offsetof(Request_send, data) && - reqlen <= (int)sizeof (Request_send)); - assert(request.data.send.len <= sizeof (request.data.send.data)); - assert((int)(request.data.send.len + offsetof(Request_send, data)) <= reqlen); - process_send(&request.data.send); - break; - case REQ_RECV: - assert(reqlen == 0); - process_recv(); - break; - case REQ_GETREFSAMPLE: - assert(reqlen == 0); - process_getrefsample(); - break; - case REQ_GETREFOFFSETS: - assert(reqlen == 0); - process_getrefoffsets(); - break; - case REQ_DEREGISTER: - assert(reqlen == 0); - break; - default: - assert(0); - } - - return true; -} - -void Node::reply(void *data, int len, int request) { - int sent; - - assert(request == pending_request); - pending_request = 0; - - if (data) { - sent = send(fd, data, len, 0); - assert(sent == len); - } -} - - -void Node::process_gettime() { - Reply_gettime r; - - r.real_time = clock.get_real_time(); - r.monotonic_time = clock.get_monotonic_time(); - r.network_time = network->get_time(); - reply(&r, sizeof (r), REQ_GETTIME); -} - -void Node::process_settime(Request_settime *req) { - clock.set_time(req->time); - reply(NULL, 0, REQ_SETTIME); -} - -void Node::process_adjtimex(Request_adjtimex *req) { - Reply_adjtimex rep; - struct timex *buf = &req->timex; - - rep.ret = clock.adjtimex(buf); - rep.timex = *buf; - rep._pad = 0; - reply(&rep, sizeof (rep), REQ_ADJTIMEX); -} - -void Node::process_adjtime(Request_adjtime *req) { - Reply_adjtime rep; - - clock.adjtime(&req->tv, &rep.tv); - reply(&rep, sizeof (rep), REQ_ADJTIME); -} - -void Node::try_select() { - Reply_select rep = {-1, 0, 0}; - - if (terminate) { - rep.ret = REPLY_SELECT_TERMINATE; -#ifdef DEBUG - printf("select returned on termination in %d at %f\n", - index, clock.get_real_time()); -#endif - } else if (select_timeout - clock.get_monotonic_time() <= 0.0) { - assert(select_timeout - clock.get_monotonic_time() > -1e-10); - rep.ret = REPLY_SELECT_TIMEOUT; -#ifdef DEBUG - printf("select returned on timeout in %d at %f\n", index, clock.get_real_time()); -#endif - } else if (select_read && incoming_packets.size() > 0) { - rep.ret = incoming_packets.back()->broadcast ? - REPLY_SELECT_BROADCAST : - REPLY_SELECT_NORMAL; - rep.subnet = incoming_packets.back()->subnet; - rep.dst_port = incoming_packets.back()->dst_port; -#ifdef DEBUG - printf("select returned for packet in %d at %f\n", index, clock.get_real_time()); -#endif - } - - if (rep.ret >= 0) { - rep.time.real_time = clock.get_real_time(); - rep.time.monotonic_time = clock.get_monotonic_time(); - rep.time.network_time = network->get_time(); - reply(&rep, sizeof (rep), REQ_SELECT); - } -} - -void Node::process_select(Request_select *req) { - if (req->timeout < 0.0) - req->timeout = 0.0; - select_timeout = clock.get_monotonic_time() + req->timeout; - select_read = req->read; -#ifdef DEBUG - printf("select called with timeout %f read %d in %d at %f\n", - req->timeout, req->read, index, clock.get_real_time()); -#endif - try_select(); -} - -void Node::process_send(Request_send *req) { - struct Packet *packet; - - if (!terminate) { - packet = new struct Packet; - packet->broadcast = req->to == (unsigned int)-1; - packet->subnet = req->subnet; - packet->from = index; - packet->to = req->to; - packet->src_port = req->src_port; - packet->dst_port = req->dst_port; - packet->len = req->len; - memcpy(packet->data, req->data, req->len); - network->send(packet); - } - - reply(NULL, 0, REQ_SEND); -} - -void Node::process_recv() { - Reply_recv rep; - struct Packet *packet; - - if (incoming_packets.empty()) { - rep.subnet = 0; - rep.from = -1; - rep.src_port = 0; - rep.dst_port = 0; - rep.len = 0; - reply(&rep, offsetof (Reply_recv, data), REQ_RECV); - - return; - } - - packet = incoming_packets.back(); - - rep.subnet = packet->subnet; - rep.from = packet->from; - rep.src_port = packet->src_port; - rep.dst_port = packet->dst_port; - rep.len = packet->len; - - assert(packet->len <= sizeof (rep.data)); - memcpy(rep.data, packet->data, packet->len); - - delete packet; - - reply(&rep, offsetof (Reply_recv, data) + rep.len, REQ_RECV); - - incoming_packets.pop_back(); -#ifdef DEBUG - printf("received packet in %d at %f\n", index, clock.get_real_time()); -#endif -} - -void Node::receive(struct Packet *packet) { - if (pending_request == REQ_REGISTER || pending_request == REQ_DEREGISTER) { - delete packet; - return; - } - - incoming_packets.insert(incoming_packets.begin(), packet); - - if (pending_request == REQ_SELECT) - try_select(); -} - -void Node::process_getrefsample() { - Reply_getrefsample r; - - refclock.set_generation(true); - r.valid = refclock.get_sample(&r.time, &r.offset); - r._pad = 0; - reply(&r, sizeof (r), REQ_GETREFSAMPLE); -} - -void Node::process_getrefoffsets() { - Reply_getrefoffsets r; - - refclock.get_offsets(r.offsets, REPLY_GETREFOFFSETS_SIZE); - reply(&r, sizeof (r), REQ_GETREFOFFSETS); -} - -void Node::resume() { - switch (pending_request) { - case REQ_SELECT: - try_select(); - break; - case REQ_REGISTER: - if (start_time - network->get_time() <= 0.0 || terminate) { - Reply_register rep; - rep.subnets = network->get_subnets(); - reply(&rep, sizeof (rep), REQ_REGISTER); -#ifdef DEBUG - printf("starting %d at %f\n", index, network->get_time()); -#endif - } - break; - case REQ_DEREGISTER: - break; - default: - assert(0); - - } -} - -bool Node::waiting() const { - return pending_request == REQ_SELECT || - pending_request == REQ_REGISTER || - pending_request == REQ_DEREGISTER; -} - -bool Node::finished() const { - return pending_request == REQ_DEREGISTER; -} - -double Node::get_timeout() const { - switch (pending_request) { - case REQ_SELECT: - return clock.get_true_interval(select_timeout - clock.get_monotonic_time()); - case REQ_REGISTER: - return start_time - network->get_time(); - case REQ_DEREGISTER: - return 10.0; - default: - assert(0); - } -} - -Clock *Node::get_clock() { - return &clock; -} - -Refclock *Node::get_refclock() { - return &refclock; -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/node.h b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/node.h deleted file mode 100644 index 11b2e12..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/node.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#ifndef NODE_H -#define NODE_H - -#include "protocol.h" -#include "clock.h" - -#include - -using namespace std; - -class Network; - -class Node { - Clock clock; - Refclock refclock; - Network *network; - int index; - int fd; - int pending_request; - double start_time; - double select_timeout; - bool select_read; - bool terminate; - - vector incoming_packets; - - public: - Node(int index, Network *network); - ~Node(); - void set_fd(int fd); - int get_fd() const; - void set_start_time(double time); - bool process_fd(); - void reply(void *data, int len, int request); - void process_gettime(); - void process_settime(Request_settime *req); - void process_adjtimex(Request_adjtimex *req); - void process_adjtime(Request_adjtime *req); - void try_select(); - void process_select(Request_select *req); - void process_send(Request_send *req); - void process_recv(); - void process_getrefsample(); - void process_getrefoffsets(); - - void receive(struct Packet *packet); - void resume(); - bool waiting() const; - bool finished() const; - - double get_timeout() const; - Clock *get_clock(); - Refclock *get_refclock(); -}; - -#endif diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/protocol.h b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/protocol.h deleted file mode 100644 index 590cae6..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/protocol.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#ifndef PROTOCOL_H -#define PROTOCOL_H - -#include "sysheaders.h" - -#define REQ_REGISTER 1 -#define REQ_GETTIME 2 -#define REQ_SETTIME 3 -#define REQ_ADJTIMEX 4 -#define REQ_ADJTIME 5 -#define REQ_SELECT 6 -#define REQ_SEND 7 -#define REQ_RECV 8 -#define REQ_GETREFSAMPLE 9 -#define REQ_GETREFOFFSETS 10 -#define REQ_DEREGISTER 11 - -struct Request_header { - int request; - int _pad; -}; - -struct Request_register { - unsigned int node; -}; - -struct Reply_register { - unsigned int subnets; -}; - -struct Reply_gettime { - double real_time; - double monotonic_time; - double network_time; -}; - -struct Request_settime { - double time; -}; - -struct Request_adjtimex { - struct timex timex; -}; - -struct Reply_adjtimex { - int ret; - int _pad; - struct timex timex; -}; - -struct Request_adjtime { - struct timeval tv; -}; - -struct Reply_adjtime { - struct timeval tv; -}; - -struct Request_select { - double timeout; - int read; - int _pad; -}; - -#define REPLY_SELECT_TIMEOUT 0 -#define REPLY_SELECT_NORMAL 1 -#define REPLY_SELECT_BROADCAST 2 -#define REPLY_SELECT_TERMINATE 3 - -struct Reply_select { - int ret; - unsigned int subnet; /* for NORMAL or BROADCAST */ - unsigned int dst_port; /* for NORMAL or BROADCAST */ - struct Reply_gettime time; -}; - -#define MAX_PACKET_SIZE 4000 - -struct Request_send { - unsigned int subnet; - unsigned int to; - unsigned int src_port; - unsigned int dst_port; - unsigned int len; - char data[MAX_PACKET_SIZE]; -}; - -struct Reply_recv { - unsigned int subnet; - unsigned int from; - unsigned int src_port; - unsigned int dst_port; - unsigned int len; - char data[MAX_PACKET_SIZE]; -}; - -struct Reply_getrefsample { - double time; - double offset; - int valid; - int _pad; -}; - -#define REPLY_GETREFOFFSETS_SIZE 1024 - -struct Reply_getrefoffsets { - double offsets[REPLY_GETREFOFFSETS_SIZE]; -}; - -union Request_data { - struct Request_register _register; - struct Request_settime settime; - struct Request_adjtimex adjtimex; - struct Request_adjtime adjtime; - struct Request_select select; - struct Request_send send; -}; - -union Reply_data { - struct Reply_register _register; - struct Reply_gettime gettime; - struct Reply_adjtimex adjtimex; - struct Reply_adjtime adjtime; - struct Reply_select select; - struct Reply_recv recv; - struct Reply_getrefsample getrefsample; - struct Reply_getrefoffsets getrefoffsets; -}; - -struct Request_packet { - struct Request_header header; - union Request_data data; -}; - -struct Reply_packet { - union Reply_data data; -}; - -#endif diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/server.cc b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/server.cc deleted file mode 100644 index a7aaa8a..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/server.cc +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#include "sysheaders.h" -#include "network.h" - -bool load_config(const char *file, Network *network, unsigned int nodes) { - Generator_generator generator; - FILE *f; - const char *ws = " \t\n\r"; - char line[1000], *var, *arg, *end; - unsigned int node, node2; - - f = fopen(file, "r"); - if (!f) - return false; - - while (fgets(line, sizeof (line), f)) { - end = line + strlen(line); - var = line + strspn(line, ws); - arg = line + strcspn(line, "="); - *arg++ = '\0'; - - if (var >= end || *var == '#') - continue; - - if (arg >= end) - return false; - - while (end > line && (end[-1] == '\r' || end[-1] == '\n' || end[-1] == '\t' || end[-1] == ' ')) - *--end = '\0'; - - arg += strspn(arg, ws); - - if (strncmp(var, "node", 4)) - return false; - - var += 4; - node = atoi(var) - 1; - if (node >= nodes) - continue; - - var += strcspn(var, "_") + 1; - if (var >= end) - return false; - - if (strncmp(var, "offset", 6) == 0) - network->get_node(node)->get_clock()->set_time(atof(arg)); - else if (strncmp(var, "start", 5) == 0) - network->get_node(node)->set_start_time(atof(arg)); - else if (strncmp(var, "freq", 4) == 0) { - if (arg[0] == '(') - network->get_node(node)->get_clock()->set_freq_generator(generator.generate(arg)); - else - network->get_node(node)->get_clock()->set_freq(atof(arg)); - } else if (strncmp(var, "step", 4) == 0) - network->get_node(node)->get_clock()->set_step_generator(generator.generate(arg)); - else if (strncmp(var, "shift_pll", 9) == 0) - network->get_node(node)->get_clock()->set_ntp_shift_pll(atoi(arg)); - else if (strncmp(var, "fll_mode2", 9) == 0) - network->get_node(node)->get_clock()->set_ntp_flag(atoi(arg), CLOCK_NTP_FLL_MODE2); - else if (strncmp(var, "pll_clamp", 9) == 0) - network->get_node(node)->get_clock()->set_ntp_flag(atoi(arg), CLOCK_NTP_PLL_CLAMP); - else if (strncmp(var, "delay", 5) == 0) { - var += 5; - node2 = atoi(var) - 1; - if (node2 >= nodes) - continue; - network->set_link_delay_generator(node, node2, generator.generate(arg)); - } else if (strncmp(var, "refclock", 8) == 0) - network->get_node(node)->get_refclock()->set_offset_generator(generator.generate(arg)); - else - return false; - } - - fclose(f); - - return true; -} - -void run_generator(char *expr, int num) { - Generator_generator gen_generator; - Generator *generator; - - generator = gen_generator.generate(expr); - while (num--) - printf("%.9e\n", generator->generate(NULL)); - delete generator; -} - -int main(int argc, char **argv) { - int nodes, subnets = 1, help = 0, verbosity = 2, generate_only = 0, rate = 1; - double limit = 10000.0, reset = 0.0; - const char *offset_log = NULL, *freq_log = NULL, *rawfreq_log = NULL, - *packet_log = NULL, *config, *socket = "clknetsim.sock", *env; - struct timeval tv; - - int r, opt; - Network *network; - - while ((opt = getopt(argc, argv, "l:r:R:n:o:f:Gg:p:s:v:h")) != -1) { - switch (opt) { - case 'l': - limit = atof(optarg); - break; - case 'r': - reset = atof(optarg); - break; - case 'R': - rate = atoi(optarg); - break; - case 'n': - subnets = atoi(optarg); - break; - case 'o': - offset_log = optarg; - break; - case 'f': - freq_log = optarg; - break; - case 'g': - rawfreq_log = optarg; - break; - case 'p': - packet_log = optarg; - break; - case 's': - socket = optarg; - break; - case 'G': - generate_only = 1; - break; - case 'v': - verbosity = atoi(optarg); - break; - case 'h': - default: - help = 1; - } - } - - if (optind + 2 != argc || help) { - printf("usage: clknetsim [options] config nodes\n"); - printf(" or: clknetsim -G expr num\n"); - printf(" -l secs set time limit to secs (default 10000)\n"); - printf(" -r secs reset clock stats after secs (default 0)\n"); - printf(" -R rate set freq/log/stats update rate (default 1 per second)\n"); - printf(" -n subnets set number of subnetworks (default 1)\n"); - printf(" -o file log time offsets to file\n"); - printf(" -f file log frequency offsets to file\n"); - printf(" -g file log raw (w/o slew) frequency offsets to file\n"); - printf(" -p file log packet delays to file\n"); - printf(" -s socket set server socket name (default clknetsim.sock)\n"); - printf(" -v level set verbosity level (default 2)\n"); - printf(" -G print num numbers generated by expr\n"); - printf(" -h print usage\n"); - return 1; - } - - config = argv[optind]; - nodes = atoi(argv[optind + 1]); - - env = getenv("CLKNETSIM_RANDOM_SEED"); - if (env) { - srandom(atoi(env)); - } else { - gettimeofday(&tv, NULL); - srandom(tv.tv_sec ^ tv.tv_usec); - } - - if (generate_only) { - run_generator(argv[optind], nodes); - return 0; - } - - network = new Network(socket, nodes, subnets, rate); - - if (offset_log) - network->open_offset_log(offset_log); - if (freq_log) - network->open_freq_log(freq_log); - if (rawfreq_log) - network->open_rawfreq_log(rawfreq_log); - if (packet_log) - network->open_packet_log(packet_log); - - if (!load_config(config, network, nodes)) { - fprintf(stderr, "Couldn't parse config %s\n", config); - return 1; - } - - if (!network->prepare_clients()) - return 1; - - fprintf(stderr, "Running simulation..."); - - if (reset && reset < limit) { - r = network->run(reset); - network->reset_clock_stats(); - } else - r = true; - - if (r) - r = network->run(limit); - - if (r) { - fprintf(stderr, "done\n\n"); - network->print_stats(verbosity); - } else - fprintf(stderr, "failed\n"); - - delete network; - - return !r; -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/stats.cc b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/stats.cc deleted file mode 100644 index 33d28b5..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/stats.cc +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#include "stats.h" - -#include "sysheaders.h" - -Stats::Stats() { - reset(); -} - -Stats::~Stats() { -} - -void Stats::reset() { - reset_clock_stats(); - - packets_in_sum2 = 0.0; - packets_out_sum2 = 0.0; - packets_in = 0; - packets_out = 0; - packets_in_time_last = 0.0; - packets_out_time_last = 0.0; - packets_in_int_sum = 0.0; - packets_out_int_sum = 0.0; - packets_in_int_min = 0.0; - packets_out_int_min = 0.0; - - wakeups_int_sum = 0; - wakeups = 0; -} - -void Stats::reset_clock_stats() { - offset_sum2 = 0.0; - offset_abs_sum = 0.0; - offset_sum = 0.0; - offset_abs_max = 0.0; - freq_sum2 = 0.0; - freq_abs_sum = 0.0; - freq_sum = 0.0; - freq_abs_max = 0.0; - rawfreq_sum2 = 0.0; - rawfreq_abs_sum = 0.0; - rawfreq_sum = 0.0; - rawfreq_abs_max = 0.0; - samples = 0; -} - -void Stats::update_clock_stats(double offset, double freq, double rawfreq) { - offset_sum2 += offset * offset; - offset_abs_sum += fabs(offset); - offset_sum += offset; - if (offset_abs_max < fabs(offset)) - offset_abs_max = fabs(offset); - - freq_sum2 += freq * freq; - freq_abs_sum += fabs(freq); - freq_sum += freq; - if (freq_abs_max < fabs(freq)) - freq_abs_max = fabs(freq); - - rawfreq_sum2 += rawfreq * rawfreq; - rawfreq_abs_sum += fabs(rawfreq); - rawfreq_sum += rawfreq; - if (rawfreq_abs_max < fabs(rawfreq)) - rawfreq_abs_max = fabs(rawfreq); - - samples++; - wakeups_int_sum++; -} - -void Stats::update_packet_stats(bool incoming, double time, double delay) { - if (delay < 0.0) - delay = 0.0; - if (incoming) { - packets_in++; - packets_in_sum2 += delay * delay; - if (packets_in >= 2) { - packets_in_int_sum += time - packets_in_time_last; - if (packets_in == 2 || packets_in_int_min > time - packets_in_time_last) - packets_in_int_min = time - packets_in_time_last; - } - packets_in_time_last = time; - } else { - packets_out++; - packets_out_sum2 += delay * delay; - if (packets_out >= 2) { - packets_out_int_sum += time - packets_out_time_last; - if (packets_out == 2 || packets_out_int_min > time - packets_out_time_last) - packets_out_int_min = time - packets_out_time_last; - } - packets_out_time_last = time; - } -} - -void Stats::update_wakeup_stats() { - wakeups++; -} - -void Stats::print(int verbosity) const { - if (verbosity <= 0) - return; - if (verbosity <= 1) { - printf("%e ", sqrt(offset_sum2 / samples)); - return; - } - - printf("RMS offset: \t%e\n", sqrt(offset_sum2 / samples)); - printf("Maximum absolute offset: \t%e\n", offset_abs_max); - printf("Mean absolute offset: \t%e\n", offset_abs_sum / samples); - printf("Mean offset: \t%e\n", offset_sum / samples); - printf("RMS frequency: \t%e\n", sqrt(freq_sum2 / samples)); - printf("Maximum absolute frequency: \t%e\n", freq_abs_max); - printf("Mean absolute frequency: \t%e\n", freq_abs_sum / samples); - printf("Mean frequency: \t%e\n", freq_sum / samples); - printf("RMS raw frequency: \t%e\n", sqrt(rawfreq_sum2 / samples)); - printf("Maximum absolute raw frequency: \t%e\n", rawfreq_abs_max); - printf("Mean absolute raw frequency: \t%e\n", rawfreq_abs_sum / samples); - printf("Mean raw frequency: \t%e\n", rawfreq_sum / samples); - if (packets_in) { - printf("RMS incoming packet delay: \t%e\n", (double)sqrt(packets_in_sum2 / packets_in)); - } else { - printf("RMS incoming packet delay: \tinf\n"); - } - if (packets_in >= 2) { - printf("Mean incoming packet interval: \t%e\n", packets_in_int_sum / (packets_in - 1)); - printf("Minimum incoming packet interval: \t%e\n", packets_in_int_min); - } else { - printf("Mean incoming packet interval: \tinf\n"); - printf("Minimum incoming packet interval: \tinf\n"); - } - if (packets_out) { - printf("RMS outgoing packet delay: \t%e\n", (double)sqrt(packets_out_sum2 / packets_out)); - } else { - printf("RMS outgoing packet delay: \tinf\n"); - } - if (packets_out >= 2) { - printf("Mean outgoing packet interval: \t%e\n", packets_out_int_sum / (packets_out - 1)); - printf("Minimum outgoing packet interval: \t%e\n", packets_out_int_min); - } else { - printf("Mean outgoing packet interval: \tinf\n"); - printf("Minimum outgoing packet interval: \tinf\n"); - } - if (wakeups) - printf("Mean wakeup interval: \t%e\n", (double)wakeups_int_sum / wakeups); - else - printf("Mean wakeup interval: \tinf\n"); -} diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/stats.h b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/stats.h deleted file mode 100644 index 667cfa6..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/stats.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 Miroslav Lichvar - * - * 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, see . - */ - -#ifndef STATS_H -#define STATS_H - -#include "clock.h" - -class Stats { - double offset_sum2; - double offset_abs_sum; - double offset_sum; - double offset_abs_max; - double freq_sum2; - double freq_abs_sum; - double freq_sum; - double freq_abs_max; - double rawfreq_sum2; - double rawfreq_abs_sum; - double rawfreq_sum; - double rawfreq_abs_max; - unsigned long samples; - - double packets_in_sum2; - double packets_out_sum2; - unsigned long packets_in; - unsigned long packets_out; - double packets_in_time_last; - double packets_out_time_last; - double packets_in_int_sum; - double packets_out_int_sum; - double packets_in_int_min; - double packets_out_int_min; - - unsigned long wakeups_int_sum; - unsigned long wakeups; - - public: - Stats(); - ~Stats(); - void reset(); - void reset_clock_stats(); - void update_clock_stats(double offset, double freq, double rawfreq); - void update_packet_stats(bool incoming, double time, double delay); - void update_wakeup_stats(); - void print(int verbosity) const; -}; - -#endif diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/sysheaders.h b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/sysheaders.h deleted file mode 100644 index f8b8132..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/sysheaders.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef SYSTEM_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -#ifndef ADJ_SETOFFSET -#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ -#endif -#ifndef ADJ_MICRO -#define ADJ_MICRO 0x1000 /* select microsecond resolution */ -#endif -#ifndef ADJ_NANO -#define ADJ_NANO 0x2000 /* select nanosecond resolution */ -#endif -#ifndef ADJ_OFFSET_SS_READ -#define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ -#endif -#ifndef STA_NANO -#define STA_NANO 0x2000 /* resolution (0 = us, 1 = ns) (ro) */ -#endif -#ifndef STA_MODE -#define STA_MODE 0x4000 /* mode (0 = PLL, 1 = FLL) (ro) */ -#endif -#endif - -#endif diff --git a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/visclocks.py b/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/visclocks.py deleted file mode 100755 index 637bef9..0000000 --- a/clknetsim-3f5ef9eb72d2182fab4ea06ad42f6f3e9dc802f5/visclocks.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2012 Miroslav Lichvar -# -# 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, see . - -import sys, pygame -import collections -import math - -freq_file = open(sys.argv[1], 'r') -offset_file = open(sys.argv[2], 'r') -delay_file = open(sys.argv[3], 'r') - -(maxx, maxy) = (640, 480) - -pygame.init() -window = pygame.display.set_mode((maxx, maxy)) -font = pygame.font.SysFont("monospace", 12) - -pygame.time.set_timer(pygame.USEREVENT, 1000 / 30) - -white = (255, 255, 255) -black = (0, 0, 0) -blue = (50, 50, 255) -lightblue = (150, 150, 255) -red = (255, 0, 0) -green = (0, 255, 0) - -freqs = collections.deque() -offsets = collections.deque() -delays = [] - -freq_avg = 0.0 -time = -1 -xscale = 2e-1 -yscale = 1e6 -frame_skip = 10 - -offset_rms = [ 0, 0, 0, 0 ] -offset_lock = 0 -delays_shown = 3 -eof = False -paused = False -game_mode = False - -delay_lines = [] -while True: - line = delay_file.readline() - if line == "": - break - line = line.split() - if line[2] == "1": - idx = int(line[1]) - elif line[1] == "1": - idx = int(line[2]) - else: - continue - while len(delay_lines) < idx + 1: - delay_lines.append([]) - delay_lines[idx].append(line) - -delay_file.close() - -for i in range(len(delay_lines)): - delays.append([]) - - last_line = [] - for line in delay_lines[i]: - if len(last_line) == 4 and len(line) == 4 and last_line[2] == "1" and line[1] == "1": - delay1 = float(last_line[3]) - delay2 = float(line[3]) - delays[i].append((int(float(line[0])), (delay1 - delay2) / 2, (delay1 + delay2) / 2)) - last_line = line - -while True: - event = pygame.event.wait() - if event.type == pygame.QUIT: - break - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_SPACE or event.key == pygame.K_p: - paused = not paused - elif event.key == pygame.K_q: - break - elif event.key == pygame.K_g: - game_mode = not game_mode - pygame.event.set_grab(game_mode) - pygame.mouse.set_visible(not game_mode) - elif event.key == pygame.K_l: - offset_lock += 1 - offset_lock %= len(offset_rms) - delays_shown = offset_lock + 1 - if delays_shown == 1: - delays_shown = 3 - elif event.key == pygame.K_PAGEUP: - frame_skip *= 2 - elif event.key == pygame.K_PAGEDOWN: - frame_skip /= 2 - if frame_skip <= 5: - frame_skip = 5 - elif event.key == pygame.K_UP: - yscale *= 2 - elif event.key == pygame.K_DOWN: - yscale /= 2 - elif event.key == pygame.K_LEFT: - xscale *= 2 - elif event.key == pygame.K_RIGHT: - xscale /= 2 - elif event.type == pygame.MOUSEMOTION: - rel = pygame.mouse.get_rel() - if game_mode and rel != (0, 0): - freq_avg += rel[1] / 1e9 - - pygame.event.clear(pygame.USEREVENT) - - if event.type != pygame.USEREVENT or paused: - continue - - while not eof: - histsize = maxx / xscale - - freq = freq_file.readline() - if freq == "": - eof = True - break - freqs.appendleft(float(freq)) - while len(freqs) > histsize: - freqs.pop() - - offset = offset_file.readline() - if offset == "": - eof = True - break - offsets.appendleft(map(float, offset.split())) - while len(offsets) > histsize: - offsets.pop() - - if not game_mode: - freq_avg += 0.001 * (freqs[0] - freq_avg) - else: - buttons = pygame.mouse.get_pressed() - if buttons == (1, 0, 0): - slew = 1e-6 - elif buttons == (0, 0, 1): - slew = -1e-6 - else: - slew = 0.0 - offsets[0][0] = offsets[1][0] - (freq_avg - freqs[0] + slew) - - offset_rms = [r + 0.001 * (o * o - r) for r, o in zip(offset_rms, offsets[0])] - - time += 1 - if time % frame_skip == 0: - break - - if len(offsets) == 0: - continue - - window.fill(black) - last_off = [] - x = maxx - y = maxy / 2 + offsets[0][offset_lock] * yscale - - def get_delays(time): - d = delays[delays_shown] - idx = len(d) - 1 - while time >= 0 and idx >= 0: - while d[idx][0] > time and idx > 0: - idx -= 1 - if d[idx][0] != time: - yield (False, 0, 0) - else: - yield (True, d[idx][1], d[idx][2]) - time -= 1 - - for freq, offset, (delay_valid, delay_center, delay_size) in zip(freqs, offsets, get_delays(time)): - x -= xscale - y -= (freq - freq_avg) * yscale - if int(x + xscale) != int(x): - for i, (off, col) in enumerate(zip(offset, [white, red, green, blue])): - oy = y - off * yscale - if len(last_off) > i: - pygame.draw.aaline(window, col, last_off[i], (x, oy)) - else: - last_off.append(()) - last_off[i] = (x, oy) - if game_mode: - break - - if delay_valid: - pygame.draw.line(window, blue, (x, y - (delay_center - delay_size) * yscale), (x, y - (delay_center + delay_size) * yscale)) - pygame.draw.line(window, lightblue, (x - 3, y - delay_center * yscale), (x + 3, y - delay_center * yscale)) - - window.blit(font.render("time = %d rms = %s xscale = %.1e yscale = %.1e" % (time, ["%1.6f" % math.sqrt(o) for o in offset_rms], xscale, yscale), False, white, black), (5, 0)) - window.blit(font.render("q:Quit p:Pause PgDn:Slow down PgUp:Speed up g:Game mode l:Switch lock Arrows:Scale", False, white, black), (5, maxy - 15)) - pygame.display.flip() - - #pygame.image.save(window, "visclocks%06d.png" % time) - -freq_file.close() -offset_file.close() diff --git a/getdate.c b/getdate.c deleted file mode 100644 index c0c502a..0000000 --- a/getdate.c +++ /dev/null @@ -1,2713 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.0.4. */ - -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output. */ -#define YYBISON 1 - -/* Bison version. */ -#define YYBISON_VERSION "3.0.4" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 0 - -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - - - - -/* Copy the first part of user declarations. */ -#line 1 "getdate.y" /* yacc.c:339 */ - -/* -** Originally written by Steven M. Bellovin while -** at the University of North Carolina at Chapel Hill. Later tweaked by -** a couple of people on Usenet. Completely overhauled by Rich $alz -** and Jim Berets in August, 1990. -** -** This code is in the public domain and has no copyright. -*/ - -#include "config.h" - -/* Since the code of getdate.y is not included in the Emacs executable - itself, there is no need to #define static in this file. Even if - the code were included in the Emacs executable, it probably - wouldn't do any harm to #undef it here; this will only cause - problems if we try to write to a static variable, which I don't - think this code needs to do. */ -#ifdef emacs -# undef static -#endif - -#include -#include - -#if HAVE_STDLIB_H -# include /* for `free'; used by Bison 1.27 */ -#endif - -#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) -# define IN_CTYPE_DOMAIN(c) 1 -#else -# define IN_CTYPE_DOMAIN(c) isascii(c) -#endif - -#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) -#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) -#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) -#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) - -/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: - - Its arg may be any int or unsigned int; it need not be an unsigned char. - - It's guaranteed to evaluate its argument exactly once. - - It's typically faster. - Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that - only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless - it's important to use the locale's definition of `digit' even when the - host does not conform to Posix. */ -#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) - -#if defined (STDC_HEADERS) || defined (USG) -# include -#endif - -#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) -# define __attribute__(x) -#endif - -#ifndef ATTRIBUTE_UNUSED -# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -#endif - -/* Some old versions of bison generate parsers that use bcopy. - That loses on systems that don't provide the function, so we have - to redefine it here. */ -#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) -# define bcopy(from, to, len) memcpy ((to), (from), (len)) -#endif - -/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), - as well as gratuitiously global symbol names, so we can have multiple - yacc generated parsers in the same program. Note that these are only - the variables produced by yacc. If other parser generators (bison, - byacc, etc) produce additional global names that conflict at link time, - then those parser generators need to be fixed instead of adding those - names to this list. */ - -#define yymaxdepth gd_maxdepth -#define yyparse gd_parse -#define yylex gd_lex -#define yyerror gd_error -#define yylval gd_lval -#define yychar gd_char -#define yydebug gd_debug -#define yypact gd_pact -#define yyr1 gd_r1 -#define yyr2 gd_r2 -#define yydef gd_def -#define yychk gd_chk -#define yypgo gd_pgo -#define yyact gd_act -#define yyexca gd_exca -#define yyerrflag gd_errflag -#define yynerrs gd_nerrs -#define yyps gd_ps -#define yypv gd_pv -#define yys gd_s -#define yy_yys gd_yys -#define yystate gd_state -#define yytmp gd_tmp -#define yyv gd_v -#define yy_yyv gd_yyv -#define yyval gd_val -#define yylloc gd_lloc -#define yyreds gd_reds /* With YYDEBUG defined */ -#define yytoks gd_toks /* With YYDEBUG defined */ -#define yylhs gd_yylhs -#define yylen gd_yylen -#define yydefred gd_yydefred -#define yydgoto gd_yydgoto -#define yysindex gd_yysindex -#define yyrindex gd_yyrindex -#define yygindex gd_yygindex -#define yytable gd_yytable -#define yycheck gd_yycheck - -static int yylex (void); -static int yyerror (char *s); - -#define EPOCH 1970 -#define HOUR(x) ((x) * 60) - -#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ - -/* -** An entry in the lexical lookup table. -*/ -typedef struct _TABLE { - const char *name; - int type; - int value; -} TABLE; - - -/* -** Meridian: am, pm, or 24-hour style. -*/ -typedef enum _MERIDIAN { - MERam, MERpm, MER24 -} MERIDIAN; - - -/* -** Global variables. We could get rid of most of these by using a good -** union as the yacc stack. (This routine was originally written before -** yacc had the %union construct.) Maybe someday; right now we only use -** the %union very rarely. -*/ -static const char *yyInput; -static int yyDayOrdinal; -static int yyDayNumber; -static int yyHaveDate; -static int yyHaveDay; -static int yyHaveRel; -static int yyHaveTime; -static int yyHaveZone; -static int yyTimezone; -static int yyDay; -static int yyHour; -static int yyMinutes; -static int yyMonth; -static int yySeconds; -static int yyYear; -static MERIDIAN yyMeridian; -static int yyRelDay; -static int yyRelHour; -static int yyRelMinutes; -static int yyRelMonth; -static int yyRelSeconds; -static int yyRelYear; - - -#line 239 "getdate.c" /* yacc.c:339 */ - -# ifndef YY_NULLPTR -# if defined __cplusplus && 201103L <= __cplusplus -# define YY_NULLPTR nullptr -# else -# define YY_NULLPTR 0 -# endif -# endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - - -/* Debug traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -#if YYDEBUG -extern int yydebug; -#endif - -/* Token type. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - enum yytokentype - { - tAGO = 258, - tDAY = 259, - tDAY_UNIT = 260, - tDAYZONE = 261, - tDST = 262, - tHOUR_UNIT = 263, - tID = 264, - tMERIDIAN = 265, - tMINUTE_UNIT = 266, - tMONTH = 267, - tMONTH_UNIT = 268, - tSEC_UNIT = 269, - tSNUMBER = 270, - tUNUMBER = 271, - tYEAR_UNIT = 272, - tZONE = 273 - }; -#endif - -/* Value type. */ -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED - -union YYSTYPE -{ -#line 177 "getdate.y" /* yacc.c:355 */ - - int Number; - enum _MERIDIAN Meridian; - -#line 300 "getdate.c" /* yacc.c:355 */ -}; - -typedef union YYSTYPE YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 -# define YYSTYPE_IS_DECLARED 1 -#endif - - -extern YYSTYPE yylval; - -int yyparse (void); - - - -/* Copy the second part of user declarations. */ - -#line 317 "getdate.c" /* yacc.c:358 */ - -#ifdef short -# undef short -#endif - -#ifdef YYTYPE_UINT8 -typedef YYTYPE_UINT8 yytype_uint8; -#else -typedef unsigned char yytype_uint8; -#endif - -#ifdef YYTYPE_INT8 -typedef YYTYPE_INT8 yytype_int8; -#else -typedef signed char yytype_int8; -#endif - -#ifdef YYTYPE_UINT16 -typedef YYTYPE_UINT16 yytype_uint16; -#else -typedef unsigned short int yytype_uint16; -#endif - -#ifdef YYTYPE_INT16 -typedef YYTYPE_INT16 yytype_int16; -#else -typedef short int yytype_int16; -#endif - -#ifndef YYSIZE_T -# ifdef __SIZE_TYPE__ -# define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif ! defined YYSIZE_T -# include /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# else -# define YYSIZE_T unsigned int -# endif -#endif - -#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) - -#ifndef YY_ -# if defined YYENABLE_NLS && YYENABLE_NLS -# if ENABLE_NLS -# include /* INFRINGES ON USER NAME SPACE */ -# define YY_(Msgid) dgettext ("bison-runtime", Msgid) -# endif -# endif -# ifndef YY_ -# define YY_(Msgid) Msgid -# endif -#endif - -#ifndef YY_ATTRIBUTE -# if (defined __GNUC__ \ - && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ - || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C -# define YY_ATTRIBUTE(Spec) __attribute__(Spec) -# else -# define YY_ATTRIBUTE(Spec) /* empty */ -# endif -#endif - -#ifndef YY_ATTRIBUTE_PURE -# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) -#endif - -#ifndef YY_ATTRIBUTE_UNUSED -# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) -#endif - -#if !defined _Noreturn \ - && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) -# if defined _MSC_VER && 1200 <= _MSC_VER -# define _Noreturn __declspec (noreturn) -# else -# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) -# endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if ! defined lint || defined __GNUC__ -# define YYUSE(E) ((void) (E)) -#else -# define YYUSE(E) /* empty */ -#endif - -#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ -/* Suppress an incorrect diagnostic about yylval being uninitialized. */ -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ - _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") -# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ - _Pragma ("GCC diagnostic pop") -#else -# define YY_INITIAL_VALUE(Value) Value -#endif -#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_END -#endif -#ifndef YY_INITIAL_VALUE -# define YY_INITIAL_VALUE(Value) /* Nothing. */ -#endif - - -#if ! defined yyoverflow || YYERROR_VERBOSE - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# elif defined __BUILTIN_VA_ARG_INCR -# include /* INFRINGES ON USER NAME SPACE */ -# elif defined _AIX -# define YYSTACK_ALLOC __alloca -# elif defined _MSC_VER -# include /* INFRINGES ON USER NAME SPACE */ -# define alloca _alloca -# else -# define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS -# include /* INFRINGES ON USER NAME SPACE */ - /* Use EXIT_SUCCESS as a witness for stdlib.h. */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's 'empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -# endif -# else -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# ifndef YYSTACK_ALLOC_MAXIMUM -# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -# endif -# if (defined __cplusplus && ! defined EXIT_SUCCESS \ - && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) -# include /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# if ! defined malloc && ! defined EXIT_SUCCESS -void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# ifndef YYFREE -# define YYFREE free -# if ! defined free && ! defined EXIT_SUCCESS -void free (void *); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# endif -#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ - - -#if (! defined yyoverflow \ - && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - yytype_int16 yyss_alloc; - YYSTYPE yyvs_alloc; -}; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -# define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (0) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED -/* Copy COUNT objects from SRC to DST. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(Dst, Src, Count) \ - __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) -# else -# define YYCOPY(Dst, Src, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (Dst)[yyi] = (Src)[yyi]; \ - } \ - while (0) -# endif -# endif -#endif /* !YYCOPY_NEEDED */ - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 2 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 50 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 22 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 11 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 51 -/* YYNSTATES -- Number of states. */ -#define YYNSTATES 61 - -/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned - by yylex, with out-of-bounds checking. */ -#define YYUNDEFTOK 2 -#define YYMAXUTOK 273 - -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) - -/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM - as returned by yylex, without out-of-bounds checking. */ -static const yytype_uint8 yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 20, 2, 2, 21, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18 -}; - -#if YYDEBUG - /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_uint16 yyrline[] = -{ - 0, 193, 193, 194, 197, 200, 203, 206, 209, 212, - 215, 221, 227, 236, 242, 254, 257, 261, 266, 270, - 274, 280, 284, 302, 308, 314, 318, 323, 327, 334, - 342, 345, 348, 351, 354, 357, 360, 363, 366, 369, - 372, 375, 378, 381, 384, 387, 390, 393, 396, 401, - 435, 438 -}; -#endif - -#if YYDEBUG || YYERROR_VERBOSE || 0 -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = -{ - "$end", "error", "$undefined", "tAGO", "tDAY", "tDAY_UNIT", "tDAYZONE", - "tDST", "tHOUR_UNIT", "tID", "tMERIDIAN", "tMINUTE_UNIT", "tMONTH", - "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tYEAR_UNIT", - "tZONE", "':'", "','", "'/'", "$accept", "spec", "item", "time", "zone", - "day", "date", "rel", "relunit", "number", "o_merid", YY_NULLPTR -}; -#endif - -# ifdef YYPRINT -/* YYTOKNUM[NUM] -- (External) token number corresponding to the - (internal) symbol number NUM (which must be that of a token). */ -static const yytype_uint16 yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 58, - 44, 47 -}; -# endif - -#define YYPACT_NINF -20 - -#define yypact_value_is_default(Yystate) \ - (!!((Yystate) == (-20))) - -#define YYTABLE_NINF -1 - -#define yytable_value_is_error(Yytable_value) \ - 0 - - /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -static const yytype_int8 yypact[] = -{ - -20, 0, -20, -19, -20, -20, -20, -20, -13, -20, - -20, 30, 15, -20, 14, -20, -20, -20, -20, -20, - -20, 19, -20, -20, 4, -20, -20, -20, -20, -20, - -20, -20, -20, -20, -20, -20, -6, -20, -20, 16, - -20, 17, 23, -20, -20, 24, -20, -20, -20, 27, - 28, -20, -20, -20, 29, -20, 32, -8, -20, -20, - -20 -}; - - /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ -static const yytype_uint8 yydefact[] = -{ - 2, 0, 1, 18, 39, 16, 42, 45, 0, 36, - 48, 0, 49, 33, 15, 3, 4, 5, 7, 6, - 8, 30, 9, 19, 25, 38, 41, 44, 35, 47, - 32, 20, 37, 40, 10, 43, 27, 34, 46, 0, - 31, 0, 0, 17, 29, 0, 24, 28, 23, 50, - 21, 26, 51, 12, 0, 11, 0, 50, 22, 14, - 13 -}; - - /* YYPGOTO[NTERM-NUM]. */ -static const yytype_int8 yypgoto[] = -{ - -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, - -7 -}; - - /* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int8 yydefgoto[] = -{ - -1, 1, 15, 16, 17, 18, 19, 20, 21, 22, - 55 -}; - - /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ -static const yytype_uint8 yytable[] = -{ - 2, 23, 52, 24, 3, 4, 5, 59, 6, 46, - 47, 7, 8, 9, 10, 11, 12, 13, 14, 31, - 32, 43, 44, 33, 45, 34, 35, 36, 37, 38, - 39, 48, 40, 49, 41, 25, 42, 52, 26, 50, - 51, 27, 53, 28, 29, 57, 54, 30, 58, 56, - 60 -}; - -static const yytype_uint8 yycheck[] = -{ - 0, 20, 10, 16, 4, 5, 6, 15, 8, 15, - 16, 11, 12, 13, 14, 15, 16, 17, 18, 4, - 5, 7, 3, 8, 20, 10, 11, 12, 13, 14, - 15, 15, 17, 16, 19, 5, 21, 10, 8, 16, - 16, 11, 15, 13, 14, 16, 19, 17, 16, 21, - 57 -}; - - /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const yytype_uint8 yystos[] = -{ - 0, 23, 0, 4, 5, 6, 8, 11, 12, 13, - 14, 15, 16, 17, 18, 24, 25, 26, 27, 28, - 29, 30, 31, 20, 16, 5, 8, 11, 13, 14, - 17, 4, 5, 8, 10, 11, 12, 13, 14, 15, - 17, 19, 21, 7, 3, 20, 15, 16, 15, 16, - 16, 16, 10, 15, 19, 32, 21, 16, 16, 15, - 32 -}; - - /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const yytype_uint8 yyr1[] = -{ - 0, 22, 23, 23, 24, 24, 24, 24, 24, 24, - 25, 25, 25, 25, 25, 26, 26, 26, 27, 27, - 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, - 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, - 32, 32 -}; - - /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ -static const yytype_uint8 yyr2[] = -{ - 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, - 2, 4, 4, 6, 6, 1, 1, 2, 1, 2, - 2, 3, 5, 3, 3, 2, 4, 2, 3, 2, - 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, - 2, 2, 1, 2, 2, 1, 2, 2, 1, 1, - 0, 1 -}; - - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab - - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK (yylen); \ - yystate = *yyssp; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror (YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (0) - -/* Error token number */ -#define YYTERROR 1 -#define YYERRCODE 256 - - - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (0) - -/* This macro is provided for backward compatibility. */ -#ifndef YY_LOCATION_PRINT -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -#endif - - -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Type, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (0) - - -/*----------------------------------------. -| Print this symbol's value on YYOUTPUT. | -`----------------------------------------*/ - -static void -yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -{ - FILE *yyo = yyoutput; - YYUSE (yyo); - if (!yyvaluep) - return; -# ifdef YYPRINT - if (yytype < YYNTOKENS) - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# endif - YYUSE (yytype); -} - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -static void -yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -{ - YYFPRINTF (yyoutput, "%s %s (", - yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); - - yy_symbol_value_print (yyoutput, yytype, yyvaluep); - YYFPRINTF (yyoutput, ")"); -} - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -static void -yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) -{ - YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (0) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -static void -yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) -{ - unsigned long int yylno = yyrline[yyrule]; - int yynrhs = yyr2[yyrule]; - int yyi; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", - yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) - { - YYFPRINTF (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, - yystos[yyssp[yyi + 1 - yynrhs]], - &(yyvsp[(yyi + 1) - (yynrhs)]) - ); - YYFPRINTF (stderr, "\n"); - } -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyssp, yyvsp, Rule); \ -} while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - -#if YYERROR_VERBOSE - -# ifndef yystrlen -# if defined __GLIBC__ && defined _STRING_H -# define yystrlen strlen -# else -/* Return the length of YYSTR. */ -static YYSIZE_T -yystrlen (const char *yystr) -{ - YYSIZE_T yylen; - for (yylen = 0; yystr[yylen]; yylen++) - continue; - return yylen; -} -# endif -# endif - -# ifndef yystpcpy -# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -static char * -yystpcpy (char *yydest, const char *yysrc) -{ - char *yyd = yydest; - const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -# endif - -# ifndef yytnamerr -/* Copy to YYRES the contents of YYSTR after stripping away unnecessary - quotes and backslashes, so that it's suitable for yyerror. The - heuristic is that double-quoting is unnecessary unless the string - contains an apostrophe, a comma, or backslash (other than - backslash-backslash). YYSTR is taken from yytname. If YYRES is - null, do not copy; instead, return the length of what the result - would have been. */ -static YYSIZE_T -yytnamerr (char *yyres, const char *yystr) -{ - if (*yystr == '"') - { - YYSIZE_T yyn = 0; - char const *yyp = yystr; - - for (;;) - switch (*++yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; - - case '\\': - if (*++yyp != '\\') - goto do_not_strip_quotes; - /* Fall through. */ - default: - if (yyres) - yyres[yyn] = *yyp; - yyn++; - break; - - case '"': - if (yyres) - yyres[yyn] = '\0'; - return yyn; - } - do_not_strip_quotes: ; - } - - if (! yyres) - return yystrlen (yystr); - - return yystpcpy (yyres, yystr) - yyres; -} -# endif - -/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message - about the unexpected token YYTOKEN for the state stack whose top is - YYSSP. - - Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is - not large enough to hold the message. In that case, also set - *YYMSG_ALLOC to the required number of bytes. Return 2 if the - required number of bytes is too large to store. */ -static int -yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, - yytype_int16 *yyssp, int yytoken) -{ - YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); - YYSIZE_T yysize = yysize0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - /* Internationalized format string. */ - const char *yyformat = YY_NULLPTR; - /* Arguments of yyformat. */ - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - /* Number of reported tokens (one for the "unexpected", one per - "expected"). */ - int yycount = 0; - - /* There are many possibilities here to consider: - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected - tokens because there are none. - - The only way there can be no lookahead present (in yychar) is if - this state is a consistent state with a default action. Thus, - detecting the absence of a lookahead is sufficient to determine - that there is no unexpected or expected token to report. In that - case, just report a simple "syntax error". - - Don't assume there isn't a lookahead just because this state is a - consistent state with a default action. There might have been a - previous inconsistent state, consistent state with a non-default - action, or user semantic action that manipulated yychar. - - Of course, the expected token list depends on states to have - correct lookahead information, and it depends on the parser not - to perform extra reductions after fetching a lookahead from the - scanner and before detecting a syntax error. Thus, state merging - (from LALR or IELR) and default reductions corrupt the expected - token list. However, the list is correct for canonical LR with - one exception: it will still contain any token that will not be - accepted due to an error action in a later state. - */ - if (yytoken != YYEMPTY) - { - int yyn = yypact[*yyssp]; - yyarg[yycount++] = yytname[yytoken]; - if (!yypact_value_is_default (yyn)) - { - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. In other words, skip the first -YYN actions for - this state because they are default actions. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yyx; - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR - && !yytable_value_is_error (yytable[yyx + yyn])) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - break; - } - yyarg[yycount++] = yytname[yyx]; - { - YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); - if (! (yysize <= yysize1 - && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; - } - } - } - } - - switch (yycount) - { -# define YYCASE_(N, S) \ - case N: \ - yyformat = S; \ - break - YYCASE_(0, YY_("syntax error")); - YYCASE_(1, YY_("syntax error, unexpected %s")); - YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); - YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); - YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); - YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); -# undef YYCASE_ - } - - { - YYSIZE_T yysize1 = yysize + yystrlen (yyformat); - if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; - } - - if (*yymsg_alloc < yysize) - { - *yymsg_alloc = 2 * yysize; - if (! (yysize <= *yymsg_alloc - && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) - *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; - return 1; - } - - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - { - char *yyp = *yymsg; - int yyi = 0; - while ((*yyp = *yyformat) != '\0') - if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyformat += 2; - } - else - { - yyp++; - yyformat++; - } - } - return 0; -} -#endif /* YYERROR_VERBOSE */ - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -static void -yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) -{ - YYUSE (yyvaluep); - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YYUSE (yytype); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - - - - -/* The lookahead symbol. */ -int yychar; - -/* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; -/* Number of syntax errors so far. */ -int yynerrs; - - -/*----------. -| yyparse. | -`----------*/ - -int -yyparse (void) -{ - int yystate; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - - /* The stacks and their tools: - 'yyss': related to states. - 'yyvs': related to semantic values. - - Refer to the stacks through separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs; - YYSTYPE *yyvsp; - - YYSIZE_T yystacksize; - - int yyn; - int yyresult; - /* Lookahead token as an internal (translated) token number. */ - int yytoken = 0; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; - - yyssp = yyss = yyssa; - yyvsp = yyvs = yyvsa; - yystacksize = YYINITDEPTH; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yystate = 0; - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | -`------------------------------------------------------------*/ - yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - - yysetstate: - *yyssp = yystate; - - if (yyss + yystacksize - 1 <= yyssp) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; - -#ifdef yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - yytype_int16 *yyss1 = yyss; - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); - - yyss = yyss1; - yyvs = yyvs1; - } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyexhaustedlab; -# else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yytype_int16 *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif -#endif /* no yyoverflow */ - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - - if (yystate == YYFINAL) - YYACCEPT; - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - - /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to lookahead token. */ - yyn = yypact[yystate]; - if (yypact_value_is_default (yyn)) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - yychar = yylex (); - } - - if (yychar <= YYEOF) - { - yychar = yytoken = YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - yytoken = YYTRANSLATE (yychar); - YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yytable_value_is_error (yyn)) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - /* Shift the lookahead token. */ - YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - - /* Discard the shifted token. */ - yychar = YYEMPTY; - - yystate = yyn; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- Do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - '$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - - - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 4: -#line 197 "getdate.y" /* yacc.c:1646 */ - { - yyHaveTime++; - } -#line 1436 "getdate.c" /* yacc.c:1646 */ - break; - - case 5: -#line 200 "getdate.y" /* yacc.c:1646 */ - { - yyHaveZone++; - } -#line 1444 "getdate.c" /* yacc.c:1646 */ - break; - - case 6: -#line 203 "getdate.y" /* yacc.c:1646 */ - { - yyHaveDate++; - } -#line 1452 "getdate.c" /* yacc.c:1646 */ - break; - - case 7: -#line 206 "getdate.y" /* yacc.c:1646 */ - { - yyHaveDay++; - } -#line 1460 "getdate.c" /* yacc.c:1646 */ - break; - - case 8: -#line 209 "getdate.y" /* yacc.c:1646 */ - { - yyHaveRel++; - } -#line 1468 "getdate.c" /* yacc.c:1646 */ - break; - - case 10: -#line 215 "getdate.y" /* yacc.c:1646 */ - { - yyHour = (yyvsp[-1].Number); - yyMinutes = 0; - yySeconds = 0; - yyMeridian = (yyvsp[0].Meridian); - } -#line 1479 "getdate.c" /* yacc.c:1646 */ - break; - - case 11: -#line 221 "getdate.y" /* yacc.c:1646 */ - { - yyHour = (yyvsp[-3].Number); - yyMinutes = (yyvsp[-1].Number); - yySeconds = 0; - yyMeridian = (yyvsp[0].Meridian); - } -#line 1490 "getdate.c" /* yacc.c:1646 */ - break; - - case 12: -#line 227 "getdate.y" /* yacc.c:1646 */ - { - yyHour = (yyvsp[-3].Number); - yyMinutes = (yyvsp[-1].Number); - yyMeridian = MER24; - yyHaveZone++; - yyTimezone = ((yyvsp[0].Number) < 0 - ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60 - : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60)); - } -#line 1504 "getdate.c" /* yacc.c:1646 */ - break; - - case 13: -#line 236 "getdate.y" /* yacc.c:1646 */ - { - yyHour = (yyvsp[-5].Number); - yyMinutes = (yyvsp[-3].Number); - yySeconds = (yyvsp[-1].Number); - yyMeridian = (yyvsp[0].Meridian); - } -#line 1515 "getdate.c" /* yacc.c:1646 */ - break; - - case 14: -#line 242 "getdate.y" /* yacc.c:1646 */ - { - yyHour = (yyvsp[-5].Number); - yyMinutes = (yyvsp[-3].Number); - yySeconds = (yyvsp[-1].Number); - yyMeridian = MER24; - yyHaveZone++; - yyTimezone = ((yyvsp[0].Number) < 0 - ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60 - : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60)); - } -#line 1530 "getdate.c" /* yacc.c:1646 */ - break; - - case 15: -#line 254 "getdate.y" /* yacc.c:1646 */ - { - yyTimezone = (yyvsp[0].Number); - } -#line 1538 "getdate.c" /* yacc.c:1646 */ - break; - - case 16: -#line 257 "getdate.y" /* yacc.c:1646 */ - { - yyTimezone = (yyvsp[0].Number) - 60; - } -#line 1546 "getdate.c" /* yacc.c:1646 */ - break; - - case 17: -#line 261 "getdate.y" /* yacc.c:1646 */ - { - yyTimezone = (yyvsp[-1].Number) - 60; - } -#line 1554 "getdate.c" /* yacc.c:1646 */ - break; - - case 18: -#line 266 "getdate.y" /* yacc.c:1646 */ - { - yyDayOrdinal = 1; - yyDayNumber = (yyvsp[0].Number); - } -#line 1563 "getdate.c" /* yacc.c:1646 */ - break; - - case 19: -#line 270 "getdate.y" /* yacc.c:1646 */ - { - yyDayOrdinal = 1; - yyDayNumber = (yyvsp[-1].Number); - } -#line 1572 "getdate.c" /* yacc.c:1646 */ - break; - - case 20: -#line 274 "getdate.y" /* yacc.c:1646 */ - { - yyDayOrdinal = (yyvsp[-1].Number); - yyDayNumber = (yyvsp[0].Number); - } -#line 1581 "getdate.c" /* yacc.c:1646 */ - break; - - case 21: -#line 280 "getdate.y" /* yacc.c:1646 */ - { - yyMonth = (yyvsp[-2].Number); - yyDay = (yyvsp[0].Number); - } -#line 1590 "getdate.c" /* yacc.c:1646 */ - break; - - case 22: -#line 284 "getdate.y" /* yacc.c:1646 */ - { - /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. - The goal in recognizing YYYY/MM/DD is solely to support legacy - machine-generated dates like those in an RCS log listing. If - you want portability, use the ISO 8601 format. */ - if ((yyvsp[-4].Number) >= 1000) - { - yyYear = (yyvsp[-4].Number); - yyMonth = (yyvsp[-2].Number); - yyDay = (yyvsp[0].Number); - } - else - { - yyMonth = (yyvsp[-4].Number); - yyDay = (yyvsp[-2].Number); - yyYear = (yyvsp[0].Number); - } - } -#line 1613 "getdate.c" /* yacc.c:1646 */ - break; - - case 23: -#line 302 "getdate.y" /* yacc.c:1646 */ - { - /* ISO 8601 format. yyyy-mm-dd. */ - yyYear = (yyvsp[-2].Number); - yyMonth = -(yyvsp[-1].Number); - yyDay = -(yyvsp[0].Number); - } -#line 1624 "getdate.c" /* yacc.c:1646 */ - break; - - case 24: -#line 308 "getdate.y" /* yacc.c:1646 */ - { - /* e.g. 17-JUN-1992. */ - yyDay = (yyvsp[-2].Number); - yyMonth = (yyvsp[-1].Number); - yyYear = -(yyvsp[0].Number); - } -#line 1635 "getdate.c" /* yacc.c:1646 */ - break; - - case 25: -#line 314 "getdate.y" /* yacc.c:1646 */ - { - yyMonth = (yyvsp[-1].Number); - yyDay = (yyvsp[0].Number); - } -#line 1644 "getdate.c" /* yacc.c:1646 */ - break; - - case 26: -#line 318 "getdate.y" /* yacc.c:1646 */ - { - yyMonth = (yyvsp[-3].Number); - yyDay = (yyvsp[-2].Number); - yyYear = (yyvsp[0].Number); - } -#line 1654 "getdate.c" /* yacc.c:1646 */ - break; - - case 27: -#line 323 "getdate.y" /* yacc.c:1646 */ - { - yyMonth = (yyvsp[0].Number); - yyDay = (yyvsp[-1].Number); - } -#line 1663 "getdate.c" /* yacc.c:1646 */ - break; - - case 28: -#line 327 "getdate.y" /* yacc.c:1646 */ - { - yyMonth = (yyvsp[-1].Number); - yyDay = (yyvsp[-2].Number); - yyYear = (yyvsp[0].Number); - } -#line 1673 "getdate.c" /* yacc.c:1646 */ - break; - - case 29: -#line 334 "getdate.y" /* yacc.c:1646 */ - { - yyRelSeconds = -yyRelSeconds; - yyRelMinutes = -yyRelMinutes; - yyRelHour = -yyRelHour; - yyRelDay = -yyRelDay; - yyRelMonth = -yyRelMonth; - yyRelYear = -yyRelYear; - } -#line 1686 "getdate.c" /* yacc.c:1646 */ - break; - - case 31: -#line 345 "getdate.y" /* yacc.c:1646 */ - { - yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1694 "getdate.c" /* yacc.c:1646 */ - break; - - case 32: -#line 348 "getdate.y" /* yacc.c:1646 */ - { - yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1702 "getdate.c" /* yacc.c:1646 */ - break; - - case 33: -#line 351 "getdate.y" /* yacc.c:1646 */ - { - yyRelYear += (yyvsp[0].Number); - } -#line 1710 "getdate.c" /* yacc.c:1646 */ - break; - - case 34: -#line 354 "getdate.y" /* yacc.c:1646 */ - { - yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1718 "getdate.c" /* yacc.c:1646 */ - break; - - case 35: -#line 357 "getdate.y" /* yacc.c:1646 */ - { - yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1726 "getdate.c" /* yacc.c:1646 */ - break; - - case 36: -#line 360 "getdate.y" /* yacc.c:1646 */ - { - yyRelMonth += (yyvsp[0].Number); - } -#line 1734 "getdate.c" /* yacc.c:1646 */ - break; - - case 37: -#line 363 "getdate.y" /* yacc.c:1646 */ - { - yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1742 "getdate.c" /* yacc.c:1646 */ - break; - - case 38: -#line 366 "getdate.y" /* yacc.c:1646 */ - { - yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1750 "getdate.c" /* yacc.c:1646 */ - break; - - case 39: -#line 369 "getdate.y" /* yacc.c:1646 */ - { - yyRelDay += (yyvsp[0].Number); - } -#line 1758 "getdate.c" /* yacc.c:1646 */ - break; - - case 40: -#line 372 "getdate.y" /* yacc.c:1646 */ - { - yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1766 "getdate.c" /* yacc.c:1646 */ - break; - - case 41: -#line 375 "getdate.y" /* yacc.c:1646 */ - { - yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1774 "getdate.c" /* yacc.c:1646 */ - break; - - case 42: -#line 378 "getdate.y" /* yacc.c:1646 */ - { - yyRelHour += (yyvsp[0].Number); - } -#line 1782 "getdate.c" /* yacc.c:1646 */ - break; - - case 43: -#line 381 "getdate.y" /* yacc.c:1646 */ - { - yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1790 "getdate.c" /* yacc.c:1646 */ - break; - - case 44: -#line 384 "getdate.y" /* yacc.c:1646 */ - { - yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1798 "getdate.c" /* yacc.c:1646 */ - break; - - case 45: -#line 387 "getdate.y" /* yacc.c:1646 */ - { - yyRelMinutes += (yyvsp[0].Number); - } -#line 1806 "getdate.c" /* yacc.c:1646 */ - break; - - case 46: -#line 390 "getdate.y" /* yacc.c:1646 */ - { - yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1814 "getdate.c" /* yacc.c:1646 */ - break; - - case 47: -#line 393 "getdate.y" /* yacc.c:1646 */ - { - yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number); - } -#line 1822 "getdate.c" /* yacc.c:1646 */ - break; - - case 48: -#line 396 "getdate.y" /* yacc.c:1646 */ - { - yyRelSeconds += (yyvsp[0].Number); - } -#line 1830 "getdate.c" /* yacc.c:1646 */ - break; - - case 49: -#line 402 "getdate.y" /* yacc.c:1646 */ - { - if (yyHaveTime && yyHaveDate && !yyHaveRel) - yyYear = (yyvsp[0].Number); - else - { - if ((yyvsp[0].Number)>10000) - { - yyHaveDate++; - yyDay= ((yyvsp[0].Number))%100; - yyMonth= ((yyvsp[0].Number)/100)%100; - yyYear = (yyvsp[0].Number)/10000; - } - else - { - yyHaveTime++; - if ((yyvsp[0].Number) < 100) - { - yyHour = (yyvsp[0].Number); - yyMinutes = 0; - } - else - { - yyHour = (yyvsp[0].Number) / 100; - yyMinutes = (yyvsp[0].Number) % 100; - } - yySeconds = 0; - yyMeridian = MER24; - } - } - } -#line 1865 "getdate.c" /* yacc.c:1646 */ - break; - - case 50: -#line 435 "getdate.y" /* yacc.c:1646 */ - { - (yyval.Meridian) = MER24; - } -#line 1873 "getdate.c" /* yacc.c:1646 */ - break; - - case 51: -#line 439 "getdate.y" /* yacc.c:1646 */ - { - (yyval.Meridian) = (yyvsp[0].Meridian); - } -#line 1881 "getdate.c" /* yacc.c:1646 */ - break; - - -#line 1885 "getdate.c" /* yacc.c:1646 */ - default: break; - } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ - YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); - - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - - *++yyvsp = yyval; - - /* Now 'shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; - - goto yynewstate; - - -/*--------------------------------------. -| yyerrlab -- here on detecting error. | -`--------------------------------------*/ -yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); - - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; -#if ! YYERROR_VERBOSE - yyerror (YY_("syntax error")); -#else -# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ - yyssp, yytoken) - { - char const *yymsgp = YY_("syntax error"); - int yysyntax_error_status; - yysyntax_error_status = YYSYNTAX_ERROR; - if (yysyntax_error_status == 0) - yymsgp = yymsg; - else if (yysyntax_error_status == 1) - { - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); - if (!yymsg) - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - yysyntax_error_status = 2; - } - else - { - yysyntax_error_status = YYSYNTAX_ERROR; - yymsgp = yymsg; - } - } - yyerror (yymsgp); - if (yysyntax_error_status == 2) - goto yyexhaustedlab; - } -# undef YYSYNTAX_ERROR -#endif - } - - - - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } - else - { - yydestruct ("Error: discarding", - yytoken, &yylval); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - - /* Pacify compilers like GCC when the user code never invokes - YYERROR and the label yyerrorlab therefore never appears in user - code. */ - if (/*CONSTCOND*/ 0) - goto yyerrorlab; - - /* Do not reclaim the symbols of the rule whose action triggered - this YYERROR. */ - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - - yydestruct ("Error: popping", - yystos[yystate], yyvsp); - YYPOPSTACK (1); - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - - /* Shift the error token. */ - YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturn; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturn; - -#if !defined yyoverflow || YYERROR_VERBOSE -/*-------------------------------------------------. -| yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ -yyexhaustedlab: - yyerror (YY_("memory exhausted")); - yyresult = 2; - /* Fall through. */ -#endif - -yyreturn: - if (yychar != YYEMPTY) - { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE (yychar); - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - } - /* Do not reclaim the symbols of the rule whose action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (yylen); - YY_STACK_PRINT (yyss, yyssp); - while (yyssp != yyss) - { - yydestruct ("Cleanup: popping", - yystos[*yyssp], yyvsp); - YYPOPSTACK (1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif -#if YYERROR_VERBOSE - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); -#endif - return yyresult; -} -#line 444 "getdate.y" /* yacc.c:1906 */ - - -/* Include this file down here because bison inserts code above which - may define-away `const'. We want the prototype for get_date to have - the same signature as the function definition does. */ -#include "getdate.h" - -extern struct tm *gmtime (); -extern struct tm *localtime (); -extern time_t mktime (); - -/* Month and day table. */ -static TABLE const MonthDayTable[] = { - { "january", tMONTH, 1 }, - { "february", tMONTH, 2 }, - { "march", tMONTH, 3 }, - { "april", tMONTH, 4 }, - { "may", tMONTH, 5 }, - { "june", tMONTH, 6 }, - { "july", tMONTH, 7 }, - { "august", tMONTH, 8 }, - { "september", tMONTH, 9 }, - { "sept", tMONTH, 9 }, - { "october", tMONTH, 10 }, - { "november", tMONTH, 11 }, - { "december", tMONTH, 12 }, - { "sunday", tDAY, 0 }, - { "monday", tDAY, 1 }, - { "tuesday", tDAY, 2 }, - { "tues", tDAY, 2 }, - { "wednesday", tDAY, 3 }, - { "wednes", tDAY, 3 }, - { "thursday", tDAY, 4 }, - { "thur", tDAY, 4 }, - { "thurs", tDAY, 4 }, - { "friday", tDAY, 5 }, - { "saturday", tDAY, 6 }, - { NULL, 0, 0 } -}; - -/* Time units table. */ -static TABLE const UnitsTable[] = { - { "year", tYEAR_UNIT, 1 }, - { "month", tMONTH_UNIT, 1 }, - { "fortnight", tDAY_UNIT, 14 }, - { "week", tDAY_UNIT, 7 }, - { "day", tDAY_UNIT, 1 }, - { "hour", tHOUR_UNIT, 1 }, - { "minute", tMINUTE_UNIT, 1 }, - { "min", tMINUTE_UNIT, 1 }, - { "second", tSEC_UNIT, 1 }, - { "sec", tSEC_UNIT, 1 }, - { NULL, 0, 0 } -}; - -/* Assorted relative-time words. */ -static TABLE const OtherTable[] = { - { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, - { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, - { "today", tMINUTE_UNIT, 0 }, - { "now", tMINUTE_UNIT, 0 }, - { "last", tUNUMBER, -1 }, - { "this", tMINUTE_UNIT, 0 }, - { "next", tUNUMBER, 1 }, - { "first", tUNUMBER, 1 }, -/* { "second", tUNUMBER, 2 }, */ - { "third", tUNUMBER, 3 }, - { "fourth", tUNUMBER, 4 }, - { "fifth", tUNUMBER, 5 }, - { "sixth", tUNUMBER, 6 }, - { "seventh", tUNUMBER, 7 }, - { "eighth", tUNUMBER, 8 }, - { "ninth", tUNUMBER, 9 }, - { "tenth", tUNUMBER, 10 }, - { "eleventh", tUNUMBER, 11 }, - { "twelfth", tUNUMBER, 12 }, - { "ago", tAGO, 1 }, - { NULL, 0, 0 } -}; - -/* The timezone table. */ -static TABLE const TimezoneTable[] = { - { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ - { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ - { "utc", tZONE, HOUR ( 0) }, - { "wet", tZONE, HOUR ( 0) }, /* Western European */ - { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ - { "wat", tZONE, HOUR ( 1) }, /* West Africa */ - { "at", tZONE, HOUR ( 2) }, /* Azores */ -#if 0 - /* For completeness. BST is also British Summer, and GST is - * also Guam Standard. */ - { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */ - { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */ -#endif -#if 0 - { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */ - { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */ - { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */ -#endif - { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ - { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ - { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ - { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ - { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ - { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ - { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ - { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ - { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ - { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ - { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ - { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ - { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ - { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ - { "cat", tZONE, HOUR (10) }, /* Central Alaska */ - { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ - { "nt", tZONE, HOUR (11) }, /* Nome */ - { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ - { "cet", tZONE, -HOUR (1) }, /* Central European */ - { "met", tZONE, -HOUR (1) }, /* Middle European */ - { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ - { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ - { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ - { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ - { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ - { "fwt", tZONE, -HOUR (1) }, /* French Winter */ - { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ - { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ - { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ -#if 0 - { "it", tZONE, -HOUR (3.5) },/* Iran */ -#endif - { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ - { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ -#if 0 - { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */ -#endif - { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ -#if 0 - /* For completeness. NST is also Newfoundland Standard, and SST is - * also Swedish Summer. */ - { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */ - { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */ -#endif /* 0 */ - { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ - { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ -#if 0 - { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */ -#endif - { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ - { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ -#if 0 - { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */ - { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */ -#endif - { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ - { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ - { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ - { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ - { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ - { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ - { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ - { NULL, 0, 0 } -}; - -/* Military timezone table. */ -static TABLE const MilitaryTable[] = { - { "a", tZONE, HOUR ( 1) }, - { "b", tZONE, HOUR ( 2) }, - { "c", tZONE, HOUR ( 3) }, - { "d", tZONE, HOUR ( 4) }, - { "e", tZONE, HOUR ( 5) }, - { "f", tZONE, HOUR ( 6) }, - { "g", tZONE, HOUR ( 7) }, - { "h", tZONE, HOUR ( 8) }, - { "i", tZONE, HOUR ( 9) }, - { "k", tZONE, HOUR ( 10) }, - { "l", tZONE, HOUR ( 11) }, - { "m", tZONE, HOUR ( 12) }, - { "n", tZONE, HOUR (- 1) }, - { "o", tZONE, HOUR (- 2) }, - { "p", tZONE, HOUR (- 3) }, - { "q", tZONE, HOUR (- 4) }, - { "r", tZONE, HOUR (- 5) }, - { "s", tZONE, HOUR (- 6) }, - { "t", tZONE, HOUR (- 7) }, - { "u", tZONE, HOUR (- 8) }, - { "v", tZONE, HOUR (- 9) }, - { "w", tZONE, HOUR (-10) }, - { "x", tZONE, HOUR (-11) }, - { "y", tZONE, HOUR (-12) }, - { "z", tZONE, HOUR ( 0) }, - { NULL, 0, 0 } -}; - - - - -/* ARGSUSED */ -static int -yyerror (s) - char *s ATTRIBUTE_UNUSED; -{ - return 0; -} - -static int -ToHour (Hours, Meridian) - int Hours; - MERIDIAN Meridian; -{ - switch (Meridian) - { - case MER24: - if (Hours < 0 || Hours > 23) - return -1; - return Hours; - case MERam: - if (Hours < 1 || Hours > 12) - return -1; - if (Hours == 12) - Hours = 0; - return Hours; - case MERpm: - if (Hours < 1 || Hours > 12) - return -1; - if (Hours == 12) - Hours = 0; - return Hours + 12; - default: - abort (); - } - /* NOTREACHED */ -} - -static int -ToYear (Year) - int Year; -{ - if (Year < 0) - Year = -Year; - - /* XPG4 suggests that years 00-68 map to 2000-2068, and - years 69-99 map to 1969-1999. */ - if (Year < 69) - Year += 2000; - else if (Year < 100) - Year += 1900; - - return Year; -} - -static int -LookupWord (buff) - char *buff; -{ - register char *p; - register char *q; - register const TABLE *tp; - int i; - int abbrev; - - /* Make it lowercase. */ - for (p = buff; *p; p++) - if (ISUPPER ((unsigned char) *p)) - *p = tolower ((unsigned char) *p); - - if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) - { - yylval.Meridian = MERam; - return tMERIDIAN; - } - if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) - { - yylval.Meridian = MERpm; - return tMERIDIAN; - } - - /* See if we have an abbreviation for a month. */ - if (strlen (buff) == 3) - abbrev = 1; - else if (strlen (buff) == 4 && buff[3] == '.') - { - abbrev = 1; - buff[3] = '\0'; - } - else - abbrev = 0; - - for (tp = MonthDayTable; tp->name; tp++) - { - if (abbrev) - { - if (strncmp (buff, tp->name, 3) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - } - else if (strcmp (buff, tp->name) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - } - - for (tp = TimezoneTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - - if (strcmp (buff, "dst") == 0) - return tDST; - - for (tp = UnitsTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - - /* Strip off any plural and try the units table again. */ - i = strlen (buff) - 1; - if (buff[i] == 's') - { - buff[i] = '\0'; - for (tp = UnitsTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - buff[i] = 's'; /* Put back for "this" in OtherTable. */ - } - - for (tp = OtherTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - - /* Military timezones. */ - if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff)) - { - for (tp = MilitaryTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - } - - /* Drop out any periods and try the timezone table again. */ - for (i = 0, p = q = buff; *q; q++) - if (*q != '.') - *p++ = *q; - else - i++; - *p = '\0'; - if (i) - for (tp = TimezoneTable; tp->name; tp++) - if (strcmp (buff, tp->name) == 0) - { - yylval.Number = tp->value; - return tp->type; - } - - return tID; -} - -static int -yylex () -{ - register unsigned char c; - register char *p; - char buff[20]; - int Count; - int sign; - - for (;;) - { - while (ISSPACE ((unsigned char) *yyInput)) - yyInput++; - - if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') - { - if (c == '-' || c == '+') - { - sign = c == '-' ? -1 : 1; - if (!ISDIGIT (*++yyInput)) - /* skip the '-' sign */ - continue; - } - else - sign = 0; - for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) - yylval.Number = 10 * yylval.Number + c - '0'; - yyInput--; - if (sign < 0) - yylval.Number = -yylval.Number; - return sign ? tSNUMBER : tUNUMBER; - } - if (ISALPHA (c)) - { - for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) - if (p < &buff[sizeof buff - 1]) - *p++ = c; - *p = '\0'; - yyInput--; - return LookupWord (buff); - } - if (c != '(') - return *yyInput++; - Count = 0; - do - { - c = *yyInput++; - if (c == '\0') - return c; - if (c == '(') - Count++; - else if (c == ')') - Count--; - } - while (Count > 0); - } -} - -#define TM_YEAR_ORIGIN 1900 - -/* Yield A - B, measured in seconds. */ -static long -difftm (struct tm *a, struct tm *b) -{ - int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); - int by = b->tm_year + (TM_YEAR_ORIGIN - 1); - long days = ( - /* difference in day of year */ - a->tm_yday - b->tm_yday - /* + intervening leap days */ - + ((ay >> 2) - (by >> 2)) - - (ay / 100 - by / 100) - + ((ay / 100 >> 2) - (by / 100 >> 2)) - /* + difference in years * 365 */ - + (long) (ay - by) * 365 - ); - return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) - + (a->tm_min - b->tm_min)) - + (a->tm_sec - b->tm_sec)); -} - -time_t -get_date (const char *p, const time_t *now) -{ - struct tm tm, tm0, *tmp; - time_t Start; - - yyInput = p; - Start = now ? *now : time ((time_t *) NULL); - tmp = localtime (&Start); - if (!tmp) - return -1; - yyYear = tmp->tm_year + TM_YEAR_ORIGIN; - yyMonth = tmp->tm_mon + 1; - yyDay = tmp->tm_mday; - yyHour = tmp->tm_hour; - yyMinutes = tmp->tm_min; - yySeconds = tmp->tm_sec; - tm.tm_isdst = tmp->tm_isdst; - yyMeridian = MER24; - yyRelSeconds = 0; - yyRelMinutes = 0; - yyRelHour = 0; - yyRelDay = 0; - yyRelMonth = 0; - yyRelYear = 0; - yyHaveDate = 0; - yyHaveDay = 0; - yyHaveRel = 0; - yyHaveTime = 0; - yyHaveZone = 0; - - if (yyparse () - || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) - return -1; - - tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; - tm.tm_mon = yyMonth - 1 + yyRelMonth; - tm.tm_mday = yyDay + yyRelDay; - if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay)) - { - tm.tm_hour = ToHour (yyHour, yyMeridian); - if (tm.tm_hour < 0) - return -1; - tm.tm_min = yyMinutes; - tm.tm_sec = yySeconds; - } - else - { - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - } - tm.tm_hour += yyRelHour; - tm.tm_min += yyRelMinutes; - tm.tm_sec += yyRelSeconds; - - /* Let mktime deduce tm_isdst if we have an absolute timestamp, - or if the relative timestamp mentions days, months, or years. */ - if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear) - tm.tm_isdst = -1; - - tm0 = tm; - - Start = mktime (&tm); - - if (Start == (time_t) -1) - { - - /* Guard against falsely reporting errors near the time_t boundaries - when parsing times in other time zones. For example, if the min - time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead - of UTC, then the min localtime value is 1970-01-01 08:00:00; if - we apply mktime to 1970-01-01 00:00:00 we will get an error, so - we apply mktime to 1970-01-02 08:00:00 instead and adjust the time - zone by 24 hours to compensate. This algorithm assumes that - there is no DST transition within a day of the time_t boundaries. */ - if (yyHaveZone) - { - tm = tm0; - if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) - { - tm.tm_mday++; - yyTimezone -= 24 * 60; - } - else - { - tm.tm_mday--; - yyTimezone += 24 * 60; - } - Start = mktime (&tm); - } - - if (Start == (time_t) -1) - return Start; - } - - if (yyHaveDay && !yyHaveDate) - { - tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 - + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); - Start = mktime (&tm); - if (Start == (time_t) -1) - return Start; - } - - if (yyHaveZone) - { - long delta; - struct tm *gmt = gmtime (&Start); - if (!gmt) - return -1; - delta = yyTimezone * 60L + difftm (&tm, gmt); - if ((Start + delta < Start) != (delta < 0)) - return -1; /* time_t overflow */ - Start += delta; - } - - return Start; -} - -#if defined (TEST) - -/* ARGSUSED */ -int -main (ac, av) - int ac; - char *av[]; -{ - char buff[MAX_BUFF_LEN + 1]; - time_t d; - - (void) printf ("Enter date, or blank line to exit.\n\t> "); - (void) fflush (stdout); - - buff[MAX_BUFF_LEN] = 0; - while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) - { - d = get_date (buff, (time_t *) NULL); - if (d == -1) - (void) printf ("Bad format - couldn't convert.\n"); - else - (void) printf ("%s", ctime (&d)); - (void) printf ("\t> "); - (void) fflush (stdout); - } - exit (0); - /* NOTREACHED */ -} -#endif /* defined (TEST) */ diff --git a/ntp2chrony.py b/ntp2chrony.py new file mode 100644 index 0000000..b840225 --- /dev/null +++ b/ntp2chrony.py @@ -0,0 +1,671 @@ +#!/usr/bin/python +# +# Convert ntp configuration to chrony +# +# Copyright (C) 2018-2019 Miroslav Lichvar +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +import argparse +import ipaddress +import logging +import os +import os.path +import re +import subprocess +import sys + +# python2 compatibility hacks +if sys.version_info[0] < 3: + from io import open + reload(sys) + sys.setdefaultencoding("utf-8") + +class NtpConfiguration(object): + def __init__(self, root_dir, ntp_conf, step_tickers): + self.root_dir = root_dir if root_dir != "/" else "" + self.ntp_conf_path = ntp_conf + self.step_tickers_path = step_tickers + + # Read and write files using an 8-bit transparent encoding + self.file_encoding = "latin-1" + self.enabled_services = set() + self.step_tickers = [] + self.time_sources = [] + self.fudges = {} + self.restrictions = { + # Built-in defaults + ipaddress.ip_network(u"0.0.0.0/0"): set(), + ipaddress.ip_network(u"::/0"): set(), + } + self.keyfile = "" + self.keys = [] + self.trusted_keys = [] + self.driftfile = "" + self.statistics = [] + self.leapfile = "" + self.tos_options = {} + self.ignored_directives = set() + self.ignored_lines = [] + + #self.detect_enabled_services() + self.parse_step_tickers() + self.parse_ntp_conf() + + def detect_enabled_services(self): + for service in ["ntpdate", "ntpd", "ntp-wait"]: + if os.path.islink("{}/etc/systemd/system/multi-user.target.wants/{}.service" + .format(self.root_dir, service)): + self.enabled_services.add(service) + logging.info("Enabled services found in /etc/systemd/system: %s", + " ".join(self.enabled_services)) + + def parse_step_tickers(self): + if not self.step_tickers_path: + return + + path = os.path.join(self.root_dir, self.step_tickers_path) + if not os.path.isfile(path): + logging.info("Missing %s", path) + return + + with open(path, encoding=self.file_encoding) as f: + for line in f: + line = line[:line.find('#')] + + words = line.split() + + if not words: + continue + + self.step_tickers.extend(words) + + def parse_ntp_conf(self, path=None): + if path is None: + path = os.path.join(self.root_dir, self.ntp_conf_path) + + with open(path, encoding=self.file_encoding) as f: + logging.info("Reading %s", path) + + for line in f: + line = line[:line.find('#')] + + words = line.split() + + if not words: + continue + + if not self.parse_directive(words): + self.ignored_lines.append(line) + + def parse_directive(self, words): + name = words.pop(0) + if name.startswith("logconfig"): + name = "logconfig" + + if words: + if name in ["server", "peer", "pool"]: + return self.parse_source(name, words) + elif name == "fudge": + return self.parse_fudge(words) + elif name == "restrict": + return self.parse_restrict(words) + elif name == "tos": + return self.parse_tos(words) + elif name == "includefile": + return self.parse_includefile(words) + elif name == "keys": + return self.parse_keys(words) + elif name == "trustedkey": + return self.parse_trustedkey(words) + elif name == "driftfile": + self.driftfile = words[0] + elif name == "statistics": + self.statistics = words + elif name == "leapfile": + self.leapfile = words[0] + else: + self.ignored_directives.add(name) + return False + else: + self.ignored_directives.add(name) + return False + + return True + + def parse_source(self, source_type, words): + ipv4_only = False + ipv6_only = False + source = { + "type": source_type, + "options": [] + } + + if words[0] == "-4": + ipv4_only = True + words.pop(0) + elif words[0] == "-6": + ipv6_only = True + words.pop(0) + + if not words: + return False + + source["address"] = words.pop(0) + + # Check if -4/-6 corresponds to the address and ignore hostnames + if ipv4_only or ipv6_only: + try: + version = ipaddress.ip_address(source["address"]).version + if (ipv4_only and version != 4) or (ipv6_only and version != 6): + return False + except ValueError: + return False + + if source["address"].startswith("127.127."): + if not source["address"].startswith("127.127.1."): + # Ignore non-LOCAL refclocks + return False + + while words: + if len(words) >= 2 and words[0] in ["minpoll", "maxpoll", "version", "key"]: + source["options"].append((words[0], words[1])) + words = words[2:] + elif words[0] in ["burst", "iburst", "noselect", "prefer", "true", "xleave"]: + source["options"].append((words[0],)) + words.pop(0) + else: + return False + + self.time_sources.append(source) + return True + + def parse_fudge(self, words): + address = words.pop(0) + options = {} + + while words: + if len(words) >= 2 and words[0] in ["stratum"]: + if not words[1].isdigit(): + return False + options[words[0]] = int(words[1]) + words = words[2:] + elif len(words) >= 2: + words = words[2:] + else: + return False + + self.fudges[address] = options + return True + + def parse_restrict(self, words): + ipv4_only = False + ipv6_only = False + flags = set() + mask = "" + + if words[0] == "-4": + ipv4_only = True + words.pop(0) + elif words[0] == "-6": + ipv6_only = True + words.pop(0) + + if not words: + return False + + address = words.pop(0) + + while words: + if len(words) >= 2 and words[0] == "mask": + mask = words[1] + words = words[2:] + else: + if words[0] not in ["kod", "nomodify", "notrap", "nopeer", "noquery", + "limited", "ignore", "noserve"]: + return False + flags.add(words[0]) + words.pop(0) + + # Convert to IP network(s), ignoring restrictions with hostnames + networks = [] + if address == "default" and not mask: + if not ipv6_only: + networks.append(ipaddress.ip_network(u"0.0.0.0/0")) + if not ipv4_only: + networks.append(ipaddress.ip_network(u"::/0")) + else: + try: + if mask: + networks.append(ipaddress.ip_network(u"{}/{}".format(address, mask))) + else: + networks.append(ipaddress.ip_network(address)) + except ValueError: + return False + + if (ipv4_only and networks[-1].version != 4) or \ + (ipv6_only and networks[-1].version != 6): + return False + + for network in networks: + self.restrictions[network] = flags + + return True + + def parse_tos(self, words): + options = {} + while words: + if len(words) >= 2 and words[0] in ["minsane", "orphan"]: + if not words[1].isdigit(): + return False + options[words[0]] = int(words[1]) + words = words[2:] + elif len(words) >= 2 and words[0] in ["maxdist"]: + # Check if it is a float value + if not words[1].replace('.', '', 1).isdigit(): + return False + options[words[0]] = float(words[1]) + words = words[2:] + else: + return False + + self.tos_options.update(options) + + return True + + def parse_includefile(self, words): + path = os.path.join(self.root_dir, words[0]) + if not os.path.isfile(path): + return False + + self.parse_ntp_conf(path) + return True + + def parse_keys(self, words): + keyfile = words[0] + path = os.path.join(self.root_dir, keyfile) + if not os.path.isfile(path): + logging.info("Missing %s", path) + return False + + with open(path, encoding=self.file_encoding) as f: + logging.info("Reading %s", path) + keys = [] + for line in f: + words = line.split() + if len(words) < 3 or not words[0].isdigit(): + continue + keys.append((int(words[0]), words[1], words[2])) + + self.keyfile = keyfile + self.keys = keys + + return True + + def parse_trustedkey(self, words): + key_ranges = [] + for word in words: + if word.isdigit(): + key_ranges.append((int(word), int(word))) + elif re.match("^[0-9]+-[0-9]+$", word): + first, last = word.split("-") + key_ranges.append((int(first), int(last))) + else: + return False + + self.trusted_keys = key_ranges + return True + + def write_chrony_configuration(self, chrony_conf_path, chrony_keys_path, + dry_run=False, backup=False): + chrony_conf = self.get_chrony_conf(chrony_keys_path) + logging.debug("Generated %s:\n%s", chrony_conf_path, chrony_conf) + + if not dry_run: + self.write_file(chrony_conf_path, 0o644, chrony_conf, backup) + + chrony_keys = self.get_chrony_keys() + if chrony_keys: + logging.debug("Generated %s:\n%s", chrony_keys_path, chrony_keys) + + if not dry_run: + self.write_file(chrony_keys_path, 0o640, chrony_keys, backup) + + def get_processed_time_sources(self): + # Convert {0,1,2,3}.*pool.ntp.org servers to 2.*pool.ntp.org pools + + # Make shallow copies of all sources (only type will be modified) + time_sources = [s.copy() for s in self.time_sources] + + pools = {} + for source in time_sources: + if source["type"] != "server": + continue + m = re.match("^([0123])(\\.\\w+)?\\.pool\\.ntp\\.org$", source["address"]) + if m is None: + continue + number = m.group(1) + zone = m.group(2) + if zone not in pools: + pools[zone] = [] + pools[zone].append((int(number), source)) + + remove_servers = set() + for zone, pool in pools.items(): + # sort and skip all pools not in [0, 3] range + pool.sort() + if [number for number, source in pool] != [0, 1, 2, 3]: + # only exact group of 4 servers can be converted, nothing to do here + continue + # verify that parameters are the same for all servers in the pool + if not all([p[1]["options"] == pool[0][1]["options"] for p in pool]): + break + remove_servers.update([pool[i][1]["address"] for i in [0, 1, 3]]) + pool[2][1]["type"] = "pool" + + processed_sources = [] + for source in time_sources: + if source["type"] == "server" and source["address"] in remove_servers: + continue + processed_sources.append(source) + return processed_sources + + def get_chrony_conf_sources(self): + conf = "" + + if self.step_tickers: + conf += "# Specify NTP servers used for initial correction.\n" + conf += "initstepslew 0.1 {}\n".format(" ".join(self.step_tickers)) + conf += "\n" + + conf += "# Specify time sources.\n" + + for source in self.get_processed_time_sources(): + address = source["address"] + if address.startswith("127.127."): + if address.startswith("127.127.1."): + continue + # No other refclocks are expected from the parser + assert False + else: + conf += "{} {}".format(source["type"], address) + for option in source["options"]: + if option[0] in ["minpoll", "maxpoll", "version", "key", + "iburst", "noselect", "prefer", "xleave"]: + conf += " {}".format(" ".join(option)) + elif option[0] == "burst": + conf += " presend 6" + elif option[0] == "true": + conf += " trust" + else: + # No other options are expected from the parser + assert False + conf += "\n" + conf += "\n" + + return conf + + def get_chrony_conf_allows(self): + allowed_networks = filter(lambda n: "ignore" not in self.restrictions[n] and + "noserve" not in self.restrictions[n], + self.restrictions.keys()) + + conf = "" + for network in sorted(allowed_networks, key=lambda n: (n.version, n)): + if network.num_addresses > 1: + conf += "allow {}\n".format(network) + else: + conf += "allow {}\n".format(network.network_address) + + if conf: + conf = "# Allow NTP client access.\n" + conf + conf += "\n" + + return conf + + def get_chrony_conf_cmdallows(self): + allowed_networks = filter(lambda n: "ignore" not in self.restrictions[n] and + "noquery" not in self.restrictions[n] and + n != ipaddress.ip_network(u"127.0.0.1/32") and + n != ipaddress.ip_network(u"::1/128"), + self.restrictions.keys()) + + ip_versions = set() + conf = "" + for network in sorted(allowed_networks, key=lambda n: (n.version, n)): + ip_versions.add(network.version) + if network.num_addresses > 1: + conf += "cmdallow {}\n".format(network) + else: + conf += "cmdallow {}\n".format(network.network_address) + + if conf: + conf = "# Allow remote monitoring.\n" + conf + if 4 in ip_versions: + conf += "bindcmdaddress 0.0.0.0\n" + if 6 in ip_versions: + conf += "bindcmdaddress ::\n" + conf += "\n" + + return conf + + def get_chrony_conf(self, chrony_keys_path): + local_stratum = 0 + maxdistance = 0.0 + minsources = 1 + orphan_stratum = 0 + logs = [] + + for source in self.time_sources: + address = source["address"] + if address.startswith("127.127.1."): + if address in self.fudges and "stratum" in self.fudges[address]: + local_stratum = self.fudges[address]["stratum"] + else: + local_stratum = 5 + + if "maxdist" in self.tos_options: + maxdistance = self.tos_options["maxdist"] + if "minsane" in self.tos_options: + minsources = self.tos_options["minsane"] + if "orphan" in self.tos_options: + orphan_stratum = self.tos_options["orphan"] + + if "clockstats" in self.statistics: + logs.append("refclocks"); + if "loopstats" in self.statistics: + logs.append("tracking") + if "peerstats" in self.statistics: + logs.append("statistics"); + if "rawstats" in self.statistics: + logs.append("measurements") + + conf = "# This file was converted from {}{}.\n".format( + self.ntp_conf_path, + " and " + self.step_tickers_path if self.step_tickers_path else "") + conf += "\n" + + if self.ignored_lines: + conf += "# The following directives were ignored in the conversion:\n" + + for line in self.ignored_lines: + # Remove sensitive information + line = re.sub(r"\s+pw\s+\S+", " pw XXX", line.rstrip()) + conf += "# " + line + "\n" + conf += "\n" + + conf += self.get_chrony_conf_sources() + + conf += "# Record the rate at which the system clock gains/losses time.\n" + if not self.driftfile: + conf += "#" + conf += "driftfile /var/lib/chrony/drift\n" + conf += "\n" + + conf += "# Allow the system clock to be stepped in the first three updates\n" + conf += "# if its offset is larger than 1 second.\n" + conf += "makestep 1.0 3\n" + conf += "\n" + + conf += "# Enable kernel synchronization of the real-time clock (RTC).\n" + conf += "rtcsync\n" + conf += "\n" + + conf += "# Enable hardware timestamping on all interfaces that support it.\n" + conf += "#hwtimestamp *\n" + conf += "\n" + + if maxdistance > 0.0: + conf += "# Specify the maximum distance of sources to be selectable.\n" + conf += "maxdistance {}\n".format(maxdistance) + conf += "\n" + + conf += "# Increase the minimum number of selectable sources required to adjust\n" + conf += "# the system clock.\n" + if minsources > 1: + conf += "minsources {}\n".format(minsources) + else: + conf += "#minsources 2\n" + conf += "\n" + + conf += self.get_chrony_conf_allows() + + conf += self.get_chrony_conf_cmdallows() + + conf += "# Serve time even if not synchronized to a time source.\n" + if orphan_stratum > 0 and orphan_stratum < 16: + conf += "local stratum {} orphan\n".format(orphan_stratum) + elif local_stratum > 0 and local_stratum < 16: + conf += "local stratum {}\n".format(local_stratum) + else: + conf += "#local stratum 10\n" + conf += "\n" + + conf += "# Specify file containing keys for NTP authentication.\n" + conf += "keyfile {}\n".format(chrony_keys_path) + conf += "\n" + + conf += "# Get TAI-UTC offset and leap seconds from the system tz database.\n" + conf += "leapsectz right/UTC\n" + conf += "\n" + + conf += "# Specify directory for log files.\n" + conf += "logdir /var/log/chrony\n" + conf += "\n" + + conf += "# Select which information is logged.\n" + if logs: + conf += "log {}\n".format(" ".join(logs)) + else: + conf += "#log measurements statistics tracking\n" + + return conf + + def get_chrony_keys(self): + if not self.keyfile: + return "" + + keys = "# This file was converted from {}.\n".format(self.keyfile) + keys += "\n" + + for key in self.keys: + key_id = key[0] + key_type = key[1] + password = key[2] + + if key_type in ["m", "M"]: + key_type = "MD5" + elif key_type not in ["MD5", "SHA1", "SHA256", "SHA384", "SHA512"]: + continue + + prefix = "ASCII" if len(password) <= 20 else "HEX" + + for first, last in self.trusted_keys: + if first <= key_id <= last: + trusted = True + break + else: + trusted = False + + # Disable keys that were not marked as trusted + if not trusted: + keys += "#" + + keys += "{} {} {}:{}\n".format(key_id, key_type, prefix, password) + + return keys + + def write_file(self, path, mode, content, backup): + path = self.root_dir + path + if backup and os.path.isfile(path): + os.rename(path, path + ".old") + + with open(os.open(path, os.O_CREAT | os.O_WRONLY | os.O_EXCL, mode), "w", + encoding=self.file_encoding) as f: + logging.info("Writing %s", path) + f.write(u"" + content) + + # Fix SELinux context if restorecon is installed + try: + subprocess.call(["restorecon", path]) + except OSError: + pass + + +def main(): + parser = argparse.ArgumentParser(description="Convert ntp configuration to chrony.") + parser.add_argument("-r", "--root", dest="roots", default=["/"], nargs="+", + metavar="DIR", help="specify root directory (default /)") + parser.add_argument("--ntp-conf", action="store", default="/etc/ntp.conf", + metavar="FILE", help="specify ntp config (default /etc/ntp.conf)") + parser.add_argument("--step-tickers", action="store", default="", + metavar="FILE", help="specify ntpdate step-tickers config (no default)") + parser.add_argument("--chrony-conf", action="store", default="/etc/chrony.conf", + metavar="FILE", help="specify chrony config (default /etc/chrony.conf)") + parser.add_argument("--chrony-keys", action="store", default="/etc/chrony.keys", + metavar="FILE", help="specify chrony keyfile (default /etc/chrony.keys)") + parser.add_argument("-b", "--backup", action="store_true", help="backup existing configs before writing") + parser.add_argument("-L", "--ignored-lines", action="store_true", help="print ignored lines") + parser.add_argument("-D", "--ignored-directives", action="store_true", + help="print names of ignored directives") + parser.add_argument("-n", "--dry-run", action="store_true", help="don't make any changes") + parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity") + + args = parser.parse_args() + + logging.basicConfig(format="%(message)s", + level=[logging.ERROR, logging.INFO, logging.DEBUG][min(args.verbose, 2)]) + + for root in args.roots: + conf = NtpConfiguration(root, args.ntp_conf, args.step_tickers) + + if args.ignored_lines: + for line in conf.ignored_lines: + print(line) + + if args.ignored_directives: + for directive in conf.ignored_directives: + print(directive) + + conf.write_chrony_configuration(args.chrony_conf, args.chrony_keys, args.dry_run, args.backup) + +if __name__ == "__main__": + main() diff --git a/test/simulation/clknetsim/.gitignore b/test/simulation/clknetsim/.gitignore new file mode 100644 index 0000000..f87df3c --- /dev/null +++ b/test/simulation/clknetsim/.gitignore @@ -0,0 +1,5 @@ +/*.o +/.deps +/clknetsim +/clknetsim.so +/tests* diff --git a/test/simulation/clknetsim/COPYING b/test/simulation/clknetsim/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/test/simulation/clknetsim/COPYING @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/test/simulation/clknetsim/Makefile b/test/simulation/clknetsim/Makefile new file mode 100644 index 0000000..23c77d2 --- /dev/null +++ b/test/simulation/clknetsim/Makefile @@ -0,0 +1,29 @@ +CC ?= gcc +CXX ?= g++ +CFLAGS += -O2 -Wall -g -fPIC +CXXFLAGS += $(CFLAGS) + +all: clknetsim.so clknetsim + +clientobjs = client.o +serverobjs = $(patsubst %.cc,%.o,$(wildcard *.cc)) + +clknetsim.so: $(clientobjs) + $(CC) $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) -ldl -lm + +clknetsim: $(serverobjs) + $(CXX) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +clean: + rm -rf server *.so *.o core.* .deps + +.deps: + @mkdir .deps + +.deps/%.d: %.c .deps + @$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@ + +.deps/%.D: %.cc .deps + @$(CXX) -MM $(CPPFLAGS) -MT '$(<:%.cc=%.o) $@' $< -o $@ + +-include $(clientobjs:%.o=.deps/%.d) $(serverobjs:%.o=.deps/%.D) diff --git a/test/simulation/clknetsim/README b/test/simulation/clknetsim/README new file mode 100644 index 0000000..0b22b5a --- /dev/null +++ b/test/simulation/clknetsim/README @@ -0,0 +1,217 @@ +Clock and Network Simulator (clknetsim) +======================================= + +clknetsim is a tool designed to test programs which synchronize the system +clock, either over network or from a hardware reference clock. It simulates a +system or a number of systems connected to each other in a network and the +tested programs discipline the simulated system clocks. It can be used to +quickly test how well the programs control the system clocks in various +conditions or to test the network protocols. + +The tested programs are not modified in order to be included in the simulation, +but they have some system calls redirected by a clknetsim library, which is +loaded by the LD_PRELOAD feature of the dynamic linker, to a clknetsim server, +which runs the simulation and collects several statistics about each client. +The server and the clients run on a single host, they communicate via a UNIX +domain socket. The simulation runs as fast as the host system is capable of, +with two simulated systems it is usually three or four orders of magnitude +faster than real time. + +Supported programs: +- chronyd and chronyc from chrony (http://chrony.tuxfamily.org/) +- ntpd, ntpdate, sntp and ntpq from ntp (http://www.ntp.org/) +- ntpd from busybox (http://www.busybox.net/) +- ptp4l, phc2sys, pmc and nsm from linuxptp (http://linuxptp.sourceforge.net/) + +Limitations: +- only Linux is supported +- the fake system calls implement only a minimal functionality required to + keep the supported clients working +- the simulated system clock advances only on select(), poll() or usleep() + calls, this means the client sees the CPU as infinitely fast +- adjtimex() frequency and tick changes happen immediately, the kernel has + infinite HZ +- adjtime() and PLL updates happen in one second intervals in the simulated + time instead of the uncorrected simulated system time, all clocks are updated + at the same time + + +Usage +----- + +The clknetsim server is started with two required arguments, the first one is +path to a configuration file describing the network and clocks and the second +argument is the number of simulated nodes. The simulation is started when all +clients are connected. + +The clients are started under a non-root user, with preloaded clknetsim.so and +the environment variable CLKNETSIM_NODE set to the number of the client. +Optionally, the environment variable CLKNETSIM_SOCKET can be set to the path of +the UNIX domain socket which is used to connect to the server, clknetsim.sock +in current directory is used by default. The CLKNETSIM_START_DATE variable can +be used to specify in seconds since 1970 when should the simulated time start, +1262304000 by default (2010-01-01 0:00 UTC). The CLKNETSIM_CONNECT_TIMEOUT +variable sets the server connection timeout, 10 seconds by default. + +The simulated network is available to the clients as one or more Ethernet +networks with IPv4 addressing. All nodes have interfaces to all networks. +Their addresses are 192.168.122+s.n, where n is the number of the node +(starting at 1) and s is the number of the network (starting at 1). The +broadcast addresses are 192.168.122+s.255. + +At the end of the simulation clock and network statistics are printed. +clknetsim has options which can be used to control for how long the +simulation should run, or if the frequency, offset or network log should be +written. clknetsim -h prints a complete list of available options. + +A minimal example how to start a simulation: + +$ LD_PRELOAD=./clknetsim.so CLKNETSIM_NODE=1 chronyd -d -f chrony.conf & +$ LD_PRELOAD=./clknetsim.so CLKNETSIM_NODE=2 ntpd -n -c ntp.conf & +$ ./clknetsim -o log.offset -l 100000 clknetsim.conf 2 + +clknetsim.conf: +node2_freq = (sum (* 1e-8 (normal))) +node1_delay2 = (+ 1e-1 (* 1e-3 (exponential))) +node2_delay1 = (+ 1e-1 (* 1e-3 (exponential))) + +chrony.conf: +pidfile chronyd.pid +local stratum 1 +allow + +ntp.conf: +pidfile ntpd.pid +server 192.168.123.1 + +The clknetsim.bash file contains bash functions which can create the +configuration in several network settings, start the simulation, stop the +clients and process the results. The examples subdirectory contains an example +script for each supported client. The above example can be written in a bash +script as: + +CLKNETSIM_PATH=. +. ./clknetsim.bash + +generate_config1 2 0.0 "(sum (* 1e-8 (normal)))" "(+ 1e-1 (* 1e-3 (exponential)))" +start_client 1 chrony "local stratum 1" +start_client 2 ntp "server 192.168.123.1" +start_server 2 -o log.offset -l 100000 + +cat tmp/stats + + +Configuration file +------------------ + +The configuration file is a text file containing a list of assignments, each +specified on a separate line, and comments using # as delimiter. Each node has +several variables, which configure the system clock, the reference clock and +the network delays to other nodes in the network. They can be set either to an +integer value, a floating-point value or a number generating expression written +in a Lisp-style syntax. + +Variables: +- nodeX_freq = float | expr + the system clock frequency error in terms of gained seconds per second of + simulated time, if an expression is specified, the expression is evaluated and + frequency updated once per simulated second (or at the rate specified with + the -R option), the allowed range is (-0.2, 0.2), the default is 0 +- nodeX_delayY = expr + the network delay for packets sent from node X to node Y in seconds, the + expression is evaluated for each sent packet, a negative value means the + packet will be dropped, there is no default (packets are dropped) +- nodeX_offset = float + the initial time error of the system clock in seconds, the default is 0 +- nodeX_start = float + the time in seconds when will be the node started, the default is 0 +- nodeX_refclock = expr + the reference clock time error in seconds, the clock can be accessed by the + client via shared memory (NTP SHM protocol) or as a PTP hardware clock (PHC) + via the clock_gettime() function, there is no default (the clock is disabled) +- nodeX_step = expr + the extra time step applied once per second (or at the rate specified with + the -R option) in seconds, there is no default (no extra steps are applied) +- nodeX_shift_pll = integer + kernel PLL parameter, the default is 2 +- nodeX_pll_clamp = 1 | 0 + kernel PLL parameter, the default is 0 +- nodeX_fll_mode2 = 1 | 0 + kernel FLL parameter, the default is 0 + +Functions and their parameters supported in the expressions: + (* [expr | float] ...) - multiplication + (+ [expr | float] ...) - addition + (% [expr | float] ...) - modulo + (sum [expr | float] ...) + - summation over consecutive evaluation of parameters + (uniform) - random number generator with standard uniform + distribution + (normal) - random number generator with standard normal + distribution + (exponential) - random number generator with exponential distribution + (lambda = 1) + (poisson lambda) - random number generator with poisson distribution + (file "datafile") - number generator reading floating-point values from + the specified file in an inifinite loop + (pulse high low) - pulse wave generator + (sine period) - sine wave generator + (cosine period) - cosine wave generator + (triangle period) - triangle wave generator + (equal epsilon [expr | float] ...) + - returns 1.0 if the values of all parameters are + equal within epsilon, 0.0 otherwise + (max [expr | float] ...) + - returns maximum value + (min [expr | float] ...) + - returns minimum value + +Variables available in network delay expressions: + time - current network time + from - number of the sending node + to - number of the receiving node + port - receiving port number + length - length of the packet + subnet - number of the Ethernet network in which + the packet was sent + +An example: + +# node1 is an NTP server, it has an accurate and absolutely stable clock +node1_offset = 0 +node1_freq = 0 + +# node2 is an NTP client, it starts with 0.1s offset and has +# 0.01ppm/s frequency wander +node2_offset = 0.1 +node2_freq = (sum (* 1e-8 (normal))) + +# network delays between the two nodes have 10ms mean and 100us +# jitter in both directions +node1_delay2 = (+ 9.9e-3 (* 100e-6 (exponential))) +node2_delay1 = (+ 9.9e-3 (* 100e-6 (exponential))) + + +Author +------ + +Miroslav Lichvar + + +License +------- + +Copyright (C) 2010, 2011, 2012 Miroslav Lichvar + +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, see . diff --git a/test/simulation/clknetsim/client.c b/test/simulation/clknetsim/client.c new file mode 100644 index 0000000..2652b52 --- /dev/null +++ b/test/simulation/clknetsim/client.c @@ -0,0 +1,1981 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SO_TIMESTAMPING +#include +#include +#endif + +#include "protocol.h" + +#include "client_fuzz.c" + +/* first node in first subnet is 192.168.123.1 */ +#define BASE_ADDR 0xc0a87b00 +#define NETMASK 0xffffff00 +#define NODE_ADDR(subnet, node) (BASE_ADDR + 0x100 * (subnet) + (node) + 1) +#define BROADCAST_ADDR(subnet) (NODE_ADDR(subnet, 0) | 0xff) +#define NODE_FROM_ADDR(addr) (((addr) & ~NETMASK) - 1) +#define SUBNET_FROM_ADDR(addr) ((((addr) & NETMASK) - BASE_ADDR) / 0x100) + +#define PTP_PRIMARY_MCAST_ADDR 0xe0000181 /* 224.0.1.129 */ +#define PTP_PDELAY_MCAST_ADDR 0xe000006b /* 224.0.0.107 */ + +#define REFCLK_FD 1000 +#define REFCLK_ID ((~(clockid_t)REFCLK_FD << 3) | 3) +#define REFCLK_PHC_INDEX 0 +#define SYSCLK_FD 1001 +#define SYSCLK_CLOCKID ((~(clockid_t)SYSCLK_FD << 3) | 3) +#define SYSCLK_PHC_INDEX 1 + +#define MAX_SOCKETS 20 +#define BASE_SOCKET_FD 100 +#define BASE_SOCKET_DEFAULT_PORT 60000 + +#define MAX_TIMERS 40 +#define BASE_TIMER_ID 0xC1230123 +#define BASE_TIMER_FD 200 + +#define URANDOM_FILE (void *)0xD1230123 + +static FILE *(*_fopen)(const char *path, const char *mode); +static size_t (*_fread)(void *ptr, size_t size, size_t nmemb, FILE *stream); +static int (*_fileno)(FILE *stream); +static int (*_fclose)(FILE *fp); +static int (*_open)(const char *pathname, int flags); +static int (*_close)(int fd); +static int (*_socket)(int domain, int type, int protocol); +static int (*_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +static ssize_t (*_recvmsg)(int sockfd, struct msghdr *msg, int flags); +static ssize_t (*_send)(int sockfd, const void *buf, size_t len, int flags); +static int (*_usleep)(useconds_t usec); +static void (*_srandom)(unsigned int seed); +static int (*_shmget)(key_t key, size_t size, int shmflg); +static void *(*_shmat)(int shmid, const void *shmaddr, int shmflg); + +static unsigned int node; +static int initialized = 0; +static int clknetsim_fd; +static int precision_hack = 1; +static unsigned int random_seed = 0; +static int recv_multiply = 1; +static int timestamping = 1; + +enum { + IFACE_NONE = 0, + IFACE_LO, + IFACE_ALL, + IFACE_ETH0, +}; + +struct ts_message { + char data[MAX_PACKET_SIZE]; + unsigned int len; + unsigned int subnet; + unsigned int to; + unsigned int port; +}; + +struct socket { + int used; + int type; + int port; + int iface; + int remote_node; + int remote_port; + int broadcast; + int pkt_info; + int time_stamping; + struct ts_message last_ts_msg; +}; + +static struct socket sockets[MAX_SOCKETS]; +static int subnets; + +static double real_time = 0.0; +static double monotonic_time = 0.0; +static double network_time = 0.0; +static int local_time_valid = 0; + +static time_t system_time_offset = 1262304000; /* 2010-01-01 0:00 UTC */ + +#define TIMER_TYPE_SIGNAL 1 +#define TIMER_TYPE_FD 2 + +struct timer { + int used; + int armed; + int type; + clockid_t clock_id; + double timeout; + double interval; +}; + +static struct timer timers[MAX_TIMERS]; + +static timer_t itimer_real_id; + +#define SHM_KEY 0x4e545030 +#define SHM_REFCLOCKS 4 + +static struct shmTime { + int mode; + int count; + time_t clockTimeStampSec; + int clockTimeStampUSec; + time_t receiveTimeStampSec; + int receiveTimeStampUSec; + int leap; + int precision; + int nsamples; + int valid; + int clockTimeStampNSec; + int receiveTimeStampNSec; + int dummy[8]; +} shm_time[SHM_REFCLOCKS]; + +static int shm_refclocks = 0; +static double shm_refclock_time = 0.0; +static struct Reply_getrefoffsets refclock_offsets; +static int refclock_offsets_used = REPLY_GETREFOFFSETS_SIZE; + +static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen); + +__attribute__((constructor)) +static void init(void) { + struct Request_register req; + struct Reply_register rep; + struct sockaddr_un s = {AF_UNIX, "clknetsim.sock"}; + const char *env; + unsigned int connect_retries = 100; /* 10 seconds */ + + if (initialized) + return; + + _fopen = (FILE *(*)(const char *path, const char *mode))dlsym(RTLD_NEXT, "fopen"); + _fread = (size_t (*)(void *ptr, size_t size, size_t nmemb, FILE *stream))dlsym(RTLD_NEXT, "fread"); + _fileno = (int (*)(FILE *stream))dlsym(RTLD_NEXT, "fileno"); + _fclose = (int (*)(FILE *fp))dlsym(RTLD_NEXT, "fclose"); + _open = (int (*)(const char *pathname, int flags))dlsym(RTLD_NEXT, "open"); + _close = (int (*)(int fd))dlsym(RTLD_NEXT, "close"); + _socket = (int (*)(int domain, int type, int protocol))dlsym(RTLD_NEXT, "socket"); + _connect = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen))dlsym(RTLD_NEXT, "connect"); + _recvmsg = (ssize_t (*)(int sockfd, struct msghdr *msg, int flags))dlsym(RTLD_NEXT, "recvmsg"); + _send = (ssize_t (*)(int sockfd, const void *buf, size_t len, int flags))dlsym(RTLD_NEXT, "send"); + _usleep = (int (*)(useconds_t usec))dlsym(RTLD_NEXT, "usleep"); + _srandom = (void (*)(unsigned int seed))dlsym(RTLD_NEXT, "srandom"); + _shmget = (int (*)(key_t key, size_t size, int shmflg))dlsym(RTLD_NEXT, "shmget"); + _shmat = (void *(*)(int shmid, const void *shmaddr, int shmflg))dlsym(RTLD_NEXT, "shmat"); + + env = getenv("CLKNETSIM_START_DATE"); + if (env) + system_time_offset = atol(env); + + env = getenv("CLKNETSIM_RANDOM_SEED"); + if (env) + random_seed = atoi(env); + + env = getenv("CLKNETSIM_RECV_MULTIPLY"); + if (env) + recv_multiply = atoi(env); + + env = getenv("CLKNETSIM_TIMESTAMPING"); + if (env) + timestamping = atoi(env); + + if (fuzz_init()) { + node = 0; + subnets = 1; + initialized = 1; + return; + } + + env = getenv("CLKNETSIM_NODE"); + if (!env) { + fprintf(stderr, "clknetsim: CLKNETSIM_NODE variable not set.\n"); + exit(1); + } + node = atoi(env) - 1; + + env = getenv("CLKNETSIM_SOCKET"); + if (env) + snprintf(s.sun_path, sizeof (s.sun_path), "%s", env); + + env = getenv("CLKNETSIM_CONNECT_TIMEOUT"); + if (env) + connect_retries = 10 * atoi(env); + + clknetsim_fd = _socket(AF_UNIX, SOCK_SEQPACKET, 0); + + assert(clknetsim_fd >= 0); + + while (_connect(clknetsim_fd, (struct sockaddr *)&s, sizeof (s)) < 0) { + if (!--connect_retries) { + fprintf(stderr, "clknetsim: could not connect to server.\n"); + exit(1); + } + _usleep(100000); + } + + /* this requires the node variable to be already set */ + srandom(0); + + initialized = 1; + + req.node = node; + make_request(REQ_REGISTER, &req, sizeof (req), &rep, sizeof (rep)); + + subnets = rep.subnets; +} + +__attribute__((destructor)) +static void fini(void) { + if (initialized) + make_request(REQ_DEREGISTER, NULL, 0, NULL, 0); +} + +static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen) { + struct Request_packet request; + int sent, received = 0; + + assert(initialized); + + if (fuzz_mode) { + fuzz_process_reply(request_id, request_data, reply, replylen); + return; + } + + request.header.request = request_id; + request.header._pad = 0; + + assert(offsetof(struct Request_packet, data) + reqlen <= sizeof (request)); + + if (request_data) + memcpy(&request.data, request_data, reqlen); + reqlen += offsetof(struct Request_packet, data); + + if ((sent = _send(clknetsim_fd, &request, reqlen, 0)) <= 0 || + (reply && (received = recv(clknetsim_fd, reply, replylen, 0)) <= 0)) { + fprintf(stderr, "clknetsim: server connection closed.\n"); + initialized = 0; + exit(1); + } + + assert(sent == reqlen); + + if (!reply) + return; + + /* check reply length */ + switch (request_id) { + case REQ_RECV: + /* reply with variable length */ + assert(received >= offsetof(struct Reply_recv, data)); + assert(offsetof(struct Reply_recv, data) + + ((struct Reply_recv *)reply)->len <= received); + break; + default: + assert(received == replylen); + } +} + +static void fetch_time(void) { + struct Reply_gettime r; + + if (!local_time_valid) { + make_request(REQ_GETTIME, NULL, 0, &r, sizeof (r)); + real_time = r.real_time; + monotonic_time = r.monotonic_time; + network_time = r.network_time; + local_time_valid = 1; + } +} + +static double get_real_time(void) { + fetch_time(); + return real_time; +} + +static double get_monotonic_time(void) { + fetch_time(); + return monotonic_time; +} + +static double get_refclock_offset(void) { + if (refclock_offsets_used >= REPLY_GETREFOFFSETS_SIZE) { + make_request(REQ_GETREFOFFSETS, NULL, 0, &refclock_offsets, sizeof (refclock_offsets)); + refclock_offsets_used = 0; + } + return refclock_offsets.offsets[refclock_offsets_used++]; +} + +static double get_refclock_time(void) { + fetch_time(); + return network_time - get_refclock_offset(); +} + +static void settime(double time) { + struct Request_settime req; + + req.time = time; + make_request(REQ_SETTIME, &req, sizeof (req), NULL, 0); + + local_time_valid = 0; +} + +static void fill_refclock_sample(void) { + struct Reply_getrefsample r; + double clock_time, receive_time, round_corr; + int i; + + if (!shm_refclocks) + return; + + make_request(REQ_GETREFSAMPLE, NULL, 0, &r, sizeof (r)); + + if (r.time == shm_refclock_time || !r.valid) + return; + shm_refclock_time = r.time; + + for (i = 0; i < shm_refclocks; i++) { + if (shm_refclocks == 1) { + clock_time = r.time - r.offset; + receive_time = r.time; + } else { + clock_time = get_refclock_time(); + receive_time = get_real_time(); + } + + round_corr = (clock_time * 1e6 - floor(clock_time * 1e6) + 0.5) / 1e6; + clock_time -= round_corr; + receive_time -= round_corr; + + shm_time[i].count++; + shm_time[i].clockTimeStampSec = floor(clock_time); + shm_time[i].clockTimeStampUSec = (clock_time - shm_time[i].clockTimeStampSec) * 1e6; + shm_time[i].clockTimeStampNSec = (clock_time - shm_time[i].clockTimeStampSec) * 1e9; + shm_time[i].clockTimeStampSec += system_time_offset; + shm_time[i].receiveTimeStampSec = floor(receive_time); + shm_time[i].receiveTimeStampUSec = (receive_time - shm_time[i].receiveTimeStampSec) * 1e6; + shm_time[i].receiveTimeStampNSec = (receive_time - shm_time[i].receiveTimeStampSec) * 1e9; + shm_time[i].receiveTimeStampSec += system_time_offset; + shm_time[i].leap = 0; + shm_time[i].valid = 1; + } +} + +static int socket_in_subnet(int socket, int subnet) { + switch (sockets[socket].iface) { + case IFACE_LO: + return 0; + case IFACE_NONE: + case IFACE_ALL: + return 1; + default: + return sockets[socket].iface - IFACE_ETH0 == subnet; + } +} + +static void get_target(int socket, uint32_t addr, unsigned int *subnet, unsigned int *node) { + if (addr == PTP_PRIMARY_MCAST_ADDR || addr == PTP_PDELAY_MCAST_ADDR) { + assert(sockets[socket].iface >= IFACE_ETH0); + *subnet = sockets[socket].iface - IFACE_ETH0; + *node = -1; /* multicast as broadcast */ + } else { + *subnet = SUBNET_FROM_ADDR(addr); + assert(*subnet >= 0 && *subnet < subnets); + assert(socket_in_subnet(socket, *subnet)); + + if (addr == BROADCAST_ADDR(*subnet)) + *node = -1; /* broadcast */ + else + *node = NODE_FROM_ADDR(addr); + } +} + +static int get_network_from_iface(const char *iface) { + if (strncmp(iface, "eth", 3)) + return -1; + return atoi(iface + 3); +} + +static int get_free_socket(void) { + int i; + + for (i = 0; i < MAX_SOCKETS; i++) { + if (!sockets[i].used) + return i; + } + + return -1; +} + +static int get_socket_from_fd(int fd) { + int s = fd - BASE_SOCKET_FD; + + if (s >= 0 && s < MAX_SOCKETS && sockets[s].used) + return s; + return -1; +} + +static int get_socket_fd(int s) { + return s + BASE_SOCKET_FD; +} + +static int find_recv_socket(int subnet, int port, int broadcast) { + int i, s = -1; + + for (i = 0; i < MAX_SOCKETS; i++) { + if (!sockets[i].used || + !socket_in_subnet(i, subnet) || + sockets[i].type != SOCK_DGRAM || + (port && sockets[i].port != port)) + continue; + if (s < 0 || sockets[s].iface < sockets[i].iface || + (broadcast && sockets[i].broadcast) || + (!broadcast && sockets[s].broadcast && + !sockets[i].broadcast)) + s = i; + } + + return s; +} + +static int get_free_timer(void) { + int i; + + for (i = 0; i < MAX_TIMERS; i++) { + if (!timers[i].used) + return i; + } + + return -1; +} + +static timer_t get_timerid(int timer) { + return (timer_t)((long)timer + BASE_TIMER_ID); +} + +static int get_timer_from_id(timer_t timerid) { + int t = (long)timerid - BASE_TIMER_ID; + + if (t >= 0 && t < MAX_TIMERS && timers[t].used) + return t; + return -1; +} + +static int get_timerfd(int timer) { + return timer + BASE_TIMER_FD; +} + +static int get_timer_from_fd(int fd) { + int t = fd - BASE_TIMER_FD; + + if (t >= 0 && t < MAX_TIMERS && timers[t].used) + return t; + return -1; +} + +static int get_first_timer(fd_set *timerfds) { + int i, r = -1; + + for (i = 0; i < MAX_TIMERS; i++) { + if (!timers[i].used || !timers[i].armed) + continue; + if (timers[i].type == TIMER_TYPE_FD && + !(timerfds && FD_ISSET(get_timerfd(i), timerfds))) + continue; + if (r < 0 || timers[r].timeout > timers[i].timeout) + r = i; + } + + return r; +} + +static void rearm_timer(int timer) +{ + assert(timers[timer].armed); + if (timers[timer].interval > 0.0) + timers[timer].timeout += timers[timer].interval; + else + timers[timer].armed = 0; +} + +static void time_to_timeval(double d, struct timeval *tv) { + tv->tv_sec = floor(d); + tv->tv_usec = (d - tv->tv_sec) * 1e6; +} + +static void time_to_timespec(double d, struct timespec *tp) { + tp->tv_sec = floor(d); + tp->tv_nsec = (d - tp->tv_sec) * 1e9; +} + +static double timeval_to_time(const struct timeval *tv, time_t offset) { + return tv->tv_sec + offset + tv->tv_usec / 1e6; +} + +static double timespec_to_time(const struct timespec *tp, time_t offset) { + return tp->tv_sec + offset + tp->tv_nsec / 1e9; +} + +int gettimeofday(struct timeval *tv, struct timezone *tz) { + double time; + + time = get_real_time() + 0.5e-6; + + time_to_timeval(time, tv); + tv->tv_sec += system_time_offset; + + /* chrony clock precision routine hack */ + if (precision_hack) + tv->tv_usec += random() % 2; + + return 0; +} + +int clock_gettime(clockid_t which_clock, struct timespec *tp) { + double time; + + switch (which_clock) { + case CLOCK_REALTIME: + case SYSCLK_CLOCKID: + time = get_real_time(); + break; + case CLOCK_MONOTONIC: + time = get_monotonic_time(); + break; + case REFCLK_ID: + time = get_refclock_time(); + break; + default: + assert(0); + } + + time += 0.5e-9; + time_to_timespec(time, tp); + + if (which_clock == CLOCK_REALTIME || which_clock == REFCLK_ID) + tp->tv_sec += system_time_offset; + + /* ntpd clock precision routine hack */ + if (precision_hack) { + static int x = 0; + tp->tv_nsec += x++ * 101; + } + + return 0; +} + +time_t time(time_t *t) { + time_t time; + + time = floor(get_real_time()); + time += system_time_offset; + if (t) + *t = time; + return time; +} + +int settimeofday(const struct timeval *tv, const struct timezone *tz) { + assert(tv); + settime(timeval_to_time(tv, -system_time_offset)); + return 0; +} + +int clock_settime(clockid_t which_clock, const struct timespec *tp) { + assert(tp && which_clock == CLOCK_REALTIME); + settime(timespec_to_time(tp, -system_time_offset)); + return 0; +} + +int adjtimex(struct timex *buf) { + struct Request_adjtimex req; + struct Reply_adjtimex rep; + + if (buf->modes & ADJ_SETOFFSET) + local_time_valid = 0; + + req.timex = *buf; + make_request(REQ_ADJTIMEX, &req, sizeof (req), &rep, sizeof (rep)); + *buf = rep.timex; + + if (rep.ret < 0) + errno = EINVAL; + + return rep.ret; +} + +int ntp_adjtime(struct timex *buf) { + return adjtimex(buf); +} + +int clock_adjtime(clockid_t id, struct timex *tx) { + assert(id == CLOCK_REALTIME || id == SYSCLK_CLOCKID || id == REFCLK_ID); + + if (id == SYSCLK_CLOCKID) { + /* allow large frequency adjustment by setting ticks */ + + long hz, base_tick, scaled_ppm_per_tick; + int r; + + hz = sysconf(_SC_CLK_TCK); + assert(hz > 0); + base_tick = (1000000 + hz / 2) / hz; + scaled_ppm_per_tick = 65536 * hz; + + if (tx->modes & ADJ_FREQUENCY && !(tx->modes & ADJ_TICK)) + tx->tick = base_tick, tx->modes |= ADJ_TICK; + + tx->tick += tx->freq / scaled_ppm_per_tick; + tx->freq = tx->freq % scaled_ppm_per_tick; + + r = adjtimex(tx); + + tx->freq += (tx->tick - base_tick) * scaled_ppm_per_tick; + tx->tick = base_tick; + + return r; + } else if (id == REFCLK_ID) { + if (tx->modes) { + errno = EINVAL; + return -1; + } + + memset(tx, 0, sizeof (*tx)); + return 0; + } + + return adjtimex(tx); +} + +int adjtime(const struct timeval *delta, struct timeval *olddelta) { + struct Request_adjtime req; + struct Reply_adjtime rep; + + if (delta) + req.tv = *delta; + else + time_to_timeval(0.0, &req.tv); + + make_request(REQ_ADJTIME, &req, sizeof (req), &rep, sizeof (rep)); + if (olddelta) + *olddelta = rep.tv; + + if (!delta) { + req.tv = rep.tv; + make_request(REQ_ADJTIME, &req, sizeof (req), &rep, sizeof (rep)); + } + + return 0; +} + +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { + struct Request_select req; + struct Reply_select rep; + int i, timer, s, recv_fd = -1; + double elapsed = 0.0; + + if (writefds) + FD_ZERO(writefds); + + if (exceptfds) { + /* chronyd waiting for TX timestamp from the error queue */ + for (i = 0; i < nfds; i++) { + if (!FD_ISSET(i, exceptfds) || get_socket_from_fd(i) < 0 || + !sockets[get_socket_from_fd(i)].last_ts_msg.len) + continue; + if (readfds) + FD_ZERO(readfds); + FD_ZERO(exceptfds); + FD_SET(i, exceptfds); + return 1; + } + + FD_ZERO(exceptfds); + } + + req.read = 0; + req._pad = 0; + + /* unknown reading fds are always ready (e.g. chronyd waiting + for name resolving notification, or OpenSSL waiting for + /dev/urandom) */ + if (readfds) { + for (i = 0; i < nfds; i++) { + if (!FD_ISSET(i, readfds)) + continue; + if (get_socket_from_fd(i) < 0 && + get_timer_from_fd(i) < 0) { + FD_ZERO(readfds); + FD_SET(i, readfds); + return 1; + } + req.read = 1; + } + } + + timer = get_first_timer(readfds); + + assert((timeout && (timeout->tv_sec > 0 || timeout->tv_usec > 0)) || + timer >= 0 || find_recv_socket(0, 0, 0) >= 0); + + fetch_time(); + + if (timeout) + req.timeout = timeout->tv_sec + (timeout->tv_usec + 1) / 1e6; + else + req.timeout = 1e20; + +try_again: + if (timer >= 0 && timers[timer].timeout <= monotonic_time) { + /* avoid unnecessary requests */ + rep.ret = REPLY_SELECT_TIMEOUT; + } else { + if (timer >= 0 && monotonic_time + req.timeout > timers[timer].timeout) + req.timeout = timers[timer].timeout - monotonic_time; + + make_request(REQ_SELECT, &req, sizeof (req), &rep, sizeof (rep)); + + elapsed += rep.time.monotonic_time - monotonic_time; + req.timeout -= rep.time.monotonic_time - monotonic_time; + + real_time = rep.time.real_time; + monotonic_time = rep.time.monotonic_time; + network_time = rep.time.network_time; + local_time_valid = 1; + + fill_refclock_sample(); + + if (monotonic_time >= 0.1 || timer >= 0 || rep.ret != REPLY_SELECT_TIMEOUT) + precision_hack = 0; + } + + switch (rep.ret) { + case REPLY_SELECT_TERMINATE: + kill(getpid(), SIGTERM); + errno = EINTR; + return -1; + + case REPLY_SELECT_TIMEOUT: + if (timer >= 0 && monotonic_time >= timers[timer].timeout) { + rearm_timer(timer); + switch (timers[timer].type) { + case TIMER_TYPE_SIGNAL: + kill(getpid(), SIGALRM); + errno = EINTR; + return -1; + case TIMER_TYPE_FD: + recv_fd = get_timerfd(timer); + break; + default: + assert(0); + } + } else + recv_fd = 0; + break; + + case REPLY_SELECT_NORMAL: + case REPLY_SELECT_BROADCAST: + s = find_recv_socket(rep.subnet, rep.dst_port, + rep.ret == REPLY_SELECT_BROADCAST); + recv_fd = s >= 0 ? get_socket_fd(s) : 0; + + /* fetch and drop the packet if no fd is waiting for it */ + if (!readfds || !recv_fd || !FD_ISSET(recv_fd, readfds)) { + struct Reply_recv recv_rep; + + make_request(REQ_RECV, NULL, 0, &recv_rep, sizeof (recv_rep)); + if (rep.ret != REPLY_SELECT_BROADCAST) + fprintf(stderr, "clknetsim: dropped packet from " + "node %d on port %d in subnet %d\n", + recv_rep.from + 1, recv_rep.dst_port, + recv_rep.subnet + 1); + + goto try_again; + } + break; + + default: + assert(0); + return 0; + } + + assert(!recv_fd || (readfds && FD_ISSET(recv_fd, readfds))); + assert(!recv_fd || (recv_fd >= BASE_SOCKET_FD && recv_fd < BASE_SOCKET_FD + MAX_SOCKETS) || + (recv_fd >= BASE_TIMER_FD && recv_fd < BASE_TIMER_FD + MAX_TIMERS)); + + if (readfds) { + FD_ZERO(readfds); + if (recv_fd) + FD_SET(recv_fd, readfds); + } + + if (timeout) { + time_to_timeval(timeval_to_time(timeout, 0) - elapsed, timeout); + if (timeout->tv_sec < 0) { + timeout->tv_sec = 0; + timeout->tv_usec = 0; + } + } + + return recv_fd ? 1 : 0; +} + +#ifndef CLKNETSIM_DISABLE_POLL +int poll(struct pollfd *fds, nfds_t nfds, int timeout) { + struct timeval tv, *ptv = NULL; + int r, maxfd = 0; + nfds_t i; + fd_set rfds; + + /* ptp4l waiting for tx SO_TIMESTAMPING */ + if (nfds == 1 && fds[0].events != POLLOUT && get_socket_from_fd(fds[0].fd) >= 0 && + sockets[get_socket_from_fd(fds[0].fd)].time_stamping & + (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE)) { + if (!fds[0].events) { + fds[0].revents = POLLERR; + return 1; + } else if (fds[0].events == POLLPRI) { + /* SO_SELECT_ERR_QUEUE option enabled */ + fds[0].revents = POLLPRI; + return 1; + } + } + + /* pmc waiting to send packet */ + if (nfds == 2 && (fds[1].events & POLLOUT) && get_socket_from_fd(fds[1].fd) >= 0) { + fds[0].revents = 0; + fds[1].revents = POLLOUT; + return 1; + } + + FD_ZERO(&rfds); + + for (i = 0; i < nfds; i++) + if (fds[i].fd >= 0 && fds[i].events & POLLIN) { + FD_SET(fds[i].fd, &rfds); + if (maxfd < fds[i].fd) + maxfd = fds[i].fd; + } + + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ptv = &tv; + } + + r = select(maxfd + 1, &rfds, NULL, NULL, ptv); + + for (i = 0; i < nfds; i++) + fds[i].revents = r > 0 && fds[i].fd >= 0 && + FD_ISSET(fds[i].fd, &rfds) ? POLLIN : 0; + + return r; +} + +int __poll_chk(struct pollfd *fds, nfds_t nfds, int timeout, size_t fdslen) { + return poll(fds, nfds, timeout); +} + +#endif + +int usleep(useconds_t usec) { + struct timeval tv; + int r; + + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + + r = select(0, NULL, NULL, NULL, &tv); + assert(r == 0); + + return 0; +} + +int nanosleep(const struct timespec *req, struct timespec *rem) { + struct timeval tv; + int r; + + tv.tv_sec = req->tv_sec; + tv.tv_usec = req->tv_nsec / 1000 + 1; + + r = select(0, NULL, NULL, NULL, &tv); + assert(r <= 0); + + if (r < 0) { + assert(!rem); + return r; + } + + if (rem) + rem->tv_sec = rem->tv_nsec = 0; + + return 0; +} + +int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain) { + assert(clock_id == CLOCK_MONOTONIC || clock_id == CLOCK_REALTIME); + return nanosleep(request, remain); +} + +FILE *fopen(const char *path, const char *mode) { + if (!strcmp(path, "/proc/net/if_inet6")) { + errno = ENOENT; + return NULL; + } else if (!strcmp(path, "/dev/urandom")) { + return URANDOM_FILE; + } + + /* make sure _fopen is initialized in case it is called from another + constructor (e.g. OpenSSL's libcrypto) */ + init(); + + return _fopen(path, mode); +} + +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { + if (stream == URANDOM_FILE) { + size_t i, l = size * nmemb; + long r; + + assert(RAND_MAX >= 0xffffff); + for (i = r = 0; i < l; i++) { + if (i % 3) + r >>= 8; + else + r = random(); + ((unsigned char *)ptr)[i] = r; + } + + return nmemb; + } + + return _fread(ptr, size, nmemb, stream); +} + +int fileno(FILE *stream) { + if (stream == URANDOM_FILE) + return -1; + + return _fileno(stream); +} + +int fclose(FILE *fp) { + if (fp == URANDOM_FILE) + return 0; + return _fclose(fp); +} + +int open(const char *pathname, int flags) { + int r; + + assert(REFCLK_PHC_INDEX == 0 && SYSCLK_PHC_INDEX == 1); + if (!strcmp(pathname, "/dev/ptp0")) + return REFCLK_FD; + else if (!strcmp(pathname, "/dev/ptp1")) + return SYSCLK_FD; + + r = _open(pathname, flags); + assert(r < 0 || (r < BASE_SOCKET_FD && r < BASE_TIMER_FD)); + + return r; +} + +int close(int fd) { + int t, s; + + if (fd == REFCLK_FD || fd == SYSCLK_FD) { + return 0; + } else if ((t = get_timer_from_fd(fd)) >= 0) { + return timer_delete(get_timerid(t)); + } else if ((s = get_socket_from_fd(fd)) >= 0) { + sockets[s].used = 0; + return 0; + } + + return _close(fd); +} + +int socket(int domain, int type, int protocol) { + int s; + + if (domain != AF_INET || (type != SOCK_DGRAM && type != SOCK_STREAM)) { + errno = EINVAL; + return -1; + } + + s = get_free_socket(); + if (s < 0) { + assert(0); + errno = ENOMEM; + return -1; + } + + memset(sockets + s, 0, sizeof (struct socket)); + sockets[s].used = 1; + sockets[s].type = type; + sockets[s].port = BASE_SOCKET_DEFAULT_PORT + s; + sockets[s].remote_node = -1; + + return get_socket_fd(s); +} + +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + /* ntpd uses connect() and getsockname() to find the interface + which will be used to send packets to an address */ + int s = get_socket_from_fd(sockfd), port; + unsigned int node, subnet; + uint32_t a; + + if (s < 0 || addr->sa_family != AF_INET) { + errno = EINVAL; + return -1; + } + + port = ntohs(((const struct sockaddr_in *)addr)->sin_port); + a = ntohl(((const struct sockaddr_in *)addr)->sin_addr.s_addr); + + get_target(s, a, &subnet, &node); + + sockets[s].iface = IFACE_ETH0 + subnet; + sockets[s].remote_node = node; + sockets[s].remote_port = port; + + return 0; +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + int s = get_socket_from_fd(sockfd), port; + uint32_t a; + + if (s < 0 || addr->sa_family != AF_INET) { + errno = EINVAL; + return -1; + } + + port = ntohs(((struct sockaddr_in *)addr)->sin_port); + a = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); + + if (port) + sockets[s].port = port; + + if (a == INADDR_ANY) + sockets[s].iface = IFACE_ALL; + else if (a == INADDR_LOOPBACK) + sockets[s].iface = IFACE_LO; + else { + int subnet = SUBNET_FROM_ADDR(a); + assert(subnet >= 0 && subnet < subnets); + if (a == NODE_ADDR(subnet, node)) + sockets[s].iface = IFACE_ETH0 + subnet; + else if (a == BROADCAST_ADDR(subnet)) { + sockets[s].iface = IFACE_ETH0 + subnet; + sockets[s].broadcast = 1; + } else + assert(0); + } + + return 0; +} + +int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + int s = get_socket_from_fd(sockfd); + uint32_t a; + + if (s < 0) { + errno = EINVAL; + return -1; + } + + struct sockaddr_in *in; + in = (struct sockaddr_in *)addr; + assert(*addrlen >= sizeof (*in)); + *addrlen = sizeof (*in); + in->sin_family = AF_INET; + in->sin_port = htons(sockets[s].port); + + switch (sockets[s].iface) { + case IFACE_NONE: + case IFACE_ALL: + a = INADDR_ANY; + break; + case IFACE_LO: + a = INADDR_LOOPBACK; + break; + default: + assert(sockets[s].iface - IFACE_ETH0 < subnets); + a = sockets[s].broadcast ? + BROADCAST_ADDR(sockets[s].iface - IFACE_ETH0) : + NODE_ADDR(sockets[s].iface - IFACE_ETH0, node); + } + + in->sin_addr.s_addr = htonl(a); + + return 0; +} + +int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + int subnet, s = get_socket_from_fd(sockfd); + + if (s < 0) { + errno = EINVAL; + return -1; + } + + if (level == SOL_SOCKET && optname == SO_BINDTODEVICE) { + if (!strcmp(optval, "lo")) + sockets[s].iface = IFACE_LO; + else if ((subnet = get_network_from_iface(optval)) >= 0) + sockets[s].iface = IFACE_ETH0 + subnet; + else { + errno = EINVAL; + return -1; + } + } + else if (level == IPPROTO_IP && optname == IP_PKTINFO && optlen == sizeof (int)) + sockets[s].pkt_info = !!(int *)optval; +#ifdef SO_TIMESTAMPING + else if (level == SOL_SOCKET && optname == SO_TIMESTAMPING && optlen == sizeof (int)) { + if (!timestamping) { + errno = EINVAL; + return -1; + } + sockets[s].time_stamping = *(int *)optval; + } +#endif + + /* unhandled options succeed too */ + return 0; +} + +int fcntl(int fd, int cmd, ...) { + return 0; +} + +int ioctl(int fd, unsigned long request, ...) { + va_list ap; + struct ifconf *conf; + struct ifreq *req; + int i, subnet, ret = 0, s = get_socket_from_fd(fd); + + va_start(ap, request); + + if (request == SIOCGIFCONF) { + conf = va_arg(ap, struct ifconf *); + assert(conf->ifc_len >= sizeof (struct ifreq) * (1 + subnets)); + conf->ifc_len = sizeof (struct ifreq) * (1 + subnets); + sprintf(conf->ifc_req[0].ifr_name, "lo"); + ((struct sockaddr_in*)&conf->ifc_req[0].ifr_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + conf->ifc_req[0].ifr_addr.sa_family = AF_INET; + + for (i = 0; i < subnets; i++) { + sprintf(conf->ifc_req[i + 1].ifr_name, "eth%d", i); + ((struct sockaddr_in*)&conf->ifc_req[i + 1].ifr_addr)->sin_addr.s_addr = htonl(NODE_ADDR(i, node)); + conf->ifc_req[i + 1].ifr_addr.sa_family = AF_INET; + } + } else if (request == SIOCGIFINDEX) { + req = va_arg(ap, struct ifreq *); + if (!strcmp(req->ifr_name, "lo")) + req->ifr_ifindex = 0; + else if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) + req->ifr_ifindex = subnet + 1; + else + ret = -1, errno = EINVAL; + } else if (request == SIOCGIFFLAGS) { + req = va_arg(ap, struct ifreq *); + if (!strcmp(req->ifr_name, "lo")) + req->ifr_flags = IFF_UP | IFF_LOOPBACK; + else if (get_network_from_iface(req->ifr_name) >= 0) + req->ifr_flags = IFF_UP | IFF_BROADCAST; + else + ret = -1, errno = EINVAL; + } else if (request == SIOCGIFBRDADDR) { + req = va_arg(ap, struct ifreq *); + if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) + ((struct sockaddr_in*)&req->ifr_broadaddr)->sin_addr.s_addr = htonl(BROADCAST_ADDR(subnet)); + else + ret = -1, errno = EINVAL; + req->ifr_broadaddr.sa_family = AF_INET; + } else if (request == SIOCGIFNETMASK) { + req = va_arg(ap, struct ifreq *); + if (!strcmp(req->ifr_name, "lo")) + ((struct sockaddr_in*)&req->ifr_netmask)->sin_addr.s_addr = htonl(0xff000000); + else if (get_network_from_iface(req->ifr_name) >= 0) + ((struct sockaddr_in*)&req->ifr_netmask)->sin_addr.s_addr = htonl(NETMASK); + else + ret = -1, errno = EINVAL; + req->ifr_netmask.sa_family = AF_INET; + } else if (request == SIOCGIFHWADDR) { + req = va_arg(ap, struct ifreq *); + if (!strcmp(req->ifr_name, "lo")) + memset((&req->ifr_hwaddr)->sa_data, 0, IFHWADDRLEN); + else if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) { + char mac[IFHWADDRLEN] = {0x12, 0x34, 0x56, 0x78, subnet + 1, node + 1}; + memcpy((&req->ifr_hwaddr)->sa_data, mac, sizeof (mac)); + } else + ret = -1, errno = EINVAL; + req->ifr_netmask.sa_family = AF_UNSPEC; +#ifdef ETHTOOL_GET_TS_INFO + } else if (request == SIOCETHTOOL) { + struct ethtool_ts_info *info; + req = va_arg(ap, struct ifreq *); + info = (struct ethtool_ts_info *)req->ifr_data; + memset(info, 0, sizeof (*info)); + if (get_network_from_iface(req->ifr_name) >= 0) { + info->phc_index = timestamping > 1 ? REFCLK_PHC_INDEX : SYSCLK_PHC_INDEX; + info->so_timestamping = SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_RAW_HARDWARE | + SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE; + info->tx_types = HWTSTAMP_TX_ON; + info->rx_filters = 1 << HWTSTAMP_FILTER_NONE | 1 << HWTSTAMP_FILTER_ALL; + } else + ret = -1, errno = EINVAL; +#endif +#ifdef PTP_CLOCK_GETCAPS + } else if (request == PTP_CLOCK_GETCAPS && (fd == REFCLK_FD || fd == SYSCLK_FD)) { + struct ptp_clock_caps *caps = va_arg(ap, struct ptp_clock_caps *); + memset(caps, 0, sizeof (*caps)); + /* maximum frequency in 32-bit timex.freq */ + caps->max_adj = 32767999; +#endif +#ifdef PTP_SYS_OFFSET + } else if (request == PTP_SYS_OFFSET && fd == REFCLK_FD) { + struct ptp_sys_offset *sys_off = va_arg(ap, struct ptp_sys_offset *); + struct timespec ts; + int i; + + if (sys_off->n_samples > PTP_MAX_SAMPLES) + sys_off->n_samples = PTP_MAX_SAMPLES; + + clock_gettime(REFCLK_ID, &ts); + for (i = 0; i < sys_off->n_samples; i++) { + sys_off->ts[2 * i + 1].sec = ts.tv_sec; + sys_off->ts[2 * i + 1].nsec = ts.tv_nsec; + } + + clock_gettime(CLOCK_REALTIME, &ts); + for (i = 0; i < sys_off->n_samples + 1; i++) { + sys_off->ts[2 * i].sec = ts.tv_sec; + sys_off->ts[2 * i].nsec = ts.tv_nsec; + } +#endif +#ifdef PTP_SYS_OFFSET_PRECISE + } else if (request == PTP_SYS_OFFSET_PRECISE && fd == REFCLK_FD) { + struct ptp_sys_offset_precise *sys_off = va_arg(ap, struct ptp_sys_offset_precise *); + struct timespec ts; + + clock_gettime(REFCLK_ID, &ts); + sys_off->device.sec = ts.tv_sec; + sys_off->device.nsec = ts.tv_nsec; + + clock_gettime(CLOCK_REALTIME, &ts); + sys_off->sys_realtime.sec = ts.tv_sec; + sys_off->sys_realtime.nsec = ts.tv_nsec; +#endif +#ifdef SIOCSHWTSTAMP + } else if (request == SIOCSHWTSTAMP && s >= 0) { +#endif +#ifdef SIOCGHWTSTAMP + } else if (request == SIOCGHWTSTAMP && s >= 0) { + struct hwtstamp_config *ts_config; + + req = va_arg(ap, struct ifreq *); + ts_config = (struct hwtstamp_config *)req->ifr_data; + + ts_config->flags = 0; + ts_config->tx_type = HWTSTAMP_TX_ON; + ts_config->rx_filter = HWTSTAMP_FILTER_ALL; +#endif + } else { + ret = -1; + errno = EINVAL; + } + + va_end(ap); + return ret; +} + +int getifaddrs(struct ifaddrs **ifap) { + struct iface { + struct ifaddrs ifaddrs; + struct sockaddr_in addr, netmask, broadaddr; + char name[11]; + } *ifaces; + int i; + + ifaces = malloc(sizeof (struct iface) * (1 + subnets)); + + ifaces[0].ifaddrs = (struct ifaddrs){ + .ifa_next = &ifaces[1].ifaddrs, + .ifa_name = "lo", + .ifa_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING, + .ifa_addr = (struct sockaddr *)&ifaces[0].addr, + .ifa_netmask = (struct sockaddr *)&ifaces[0].netmask, + .ifa_broadaddr = (struct sockaddr *)&ifaces[0].broadaddr + }; + ifaces[0].addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ifaces[0].netmask.sin_addr.s_addr = htonl(0xff000000); + ifaces[0].broadaddr.sin_addr.s_addr = 0; + + for (i = 0; i < subnets; i++) { + ifaces[i + 1].ifaddrs = (struct ifaddrs){ + .ifa_next = &ifaces[i + 2].ifaddrs, + .ifa_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING, + .ifa_addr = (struct sockaddr *)&ifaces[i + 1].addr, + .ifa_netmask = (struct sockaddr *)&ifaces[i + 1].netmask, + .ifa_broadaddr = (struct sockaddr *)&ifaces[i + 1].broadaddr + }; + ifaces[i + 1].ifaddrs.ifa_name = ifaces[i + 1].name; + snprintf(ifaces[i + 1].name, sizeof (ifaces[i + 1].name), "eth%d", i); + ifaces[i + 1].addr.sin_addr.s_addr = htonl(NODE_ADDR(i, node)); + ifaces[i + 1].netmask.sin_addr.s_addr = htonl(NETMASK); + ifaces[i + 1].broadaddr.sin_addr.s_addr = htonl(BROADCAST_ADDR(i)); + } + + ifaces[i].ifaddrs.ifa_next = NULL; + + for (i = 0; i < 1 + subnets; i++) { + ifaces[i].addr.sin_family = AF_INET; + ifaces[i].netmask.sin_family = AF_INET; + ifaces[i].broadaddr.sin_family = AF_INET; + } + + *ifap = (struct ifaddrs *)ifaces; + return 0; +} + +void freeifaddrs(struct ifaddrs *ifa) { + free(ifa); +} + +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { + struct Request_send req; + struct sockaddr_in connected_sa, *sa; + struct cmsghdr *cmsg; + int s = get_socket_from_fd(sockfd), timestamping; + + if (s < 0 || sockets[s].type != SOCK_DGRAM) { + assert(0); + errno = EINVAL; + return -1; + } + + if (sockets[s].remote_node >= 0) { + if (msg->msg_name) { + errno = EISCONN; + return -1; + } + sa = &connected_sa; + sa->sin_family = AF_INET; + sa->sin_port = htons(sockets[s].remote_port); + sa->sin_addr.s_addr = htonl(NODE_ADDR(sockets[s].iface - IFACE_ETH0, + sockets[s].remote_node)); + } else { + sa = msg->msg_name; + assert(sa && msg->msg_namelen >= sizeof (struct sockaddr_in)); + assert(sa->sin_family == AF_INET); + } + + assert(msg->msg_iovlen == 1); + assert(msg->msg_iov[0].iov_len <= sizeof (req.data)); + + get_target(s, ntohl(sa->sin_addr.s_addr), &req.subnet, &req.to); + req.src_port = sockets[s].port; + req.dst_port = ntohs(sa->sin_port); + assert(req.src_port && req.dst_port); + + req.len = msg->msg_iov[0].iov_len; + memcpy(req.data, msg->msg_iov[0].iov_base, req.len); + + make_request(REQ_SEND, &req, offsetof(struct Request_send, data) + req.len, NULL, 0); + + timestamping = sockets[s].time_stamping; + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR((struct msghdr *)msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING) + memcpy(×tamping, CMSG_DATA(cmsg), sizeof (timestamping)); + } + + if (timestamping & (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE)) { + struct ts_message *last_ts_msg = &sockets[s].last_ts_msg; + + assert(req.len <= sizeof (last_ts_msg->data)); + memcpy(last_ts_msg->data, req.data, req.len); + last_ts_msg->len = req.len; + last_ts_msg->subnet = req.subnet; + last_ts_msg->to = req.to; + last_ts_msg->port = req.dst_port; + } + + return req.len; +} + +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { + struct msghdr msg; + struct iovec iov; + + iov.iov_base = (void *)buf; + iov.iov_len = len; + + msg.msg_name = (void *)dest_addr; + msg.msg_namelen = addrlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + return sendmsg(sockfd, &msg, flags); +} + +ssize_t send(int sockfd, const void *buf, size_t len, int flags) { + return sendto(sockfd, buf, len, flags, NULL, 0); +} + +int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, +#if !defined(__GLIBC_PREREQ) || !(__GLIBC_PREREQ(2, 20)) + const +#endif + struct timespec *timeout) { + ssize_t len; + int i, n; + + assert(vlen > 0); + len = recvmsg(sockfd, &msgvec[0].msg_hdr, flags); + if (len < 0) + return -1; + msgvec[0].msg_len = len; + + if (recv_multiply <= 1 || vlen <= 1) + return 1; + + n = random() % recv_multiply + 1; + if (n > vlen) + n = vlen; + + for (i = 1; i < n; i++) { + struct msghdr *src = &msgvec[0].msg_hdr, *dst = &msgvec[i].msg_hdr; + if (dst->msg_name) { + memcpy(dst->msg_name, src->msg_name, src->msg_namelen); + dst->msg_namelen = src->msg_namelen; + } + assert(dst->msg_iovlen == 1 && dst->msg_iov[0].iov_len >= len); + memcpy(dst->msg_iov[0].iov_base, src->msg_iov[0].iov_base, len); + if (dst->msg_control) { + assert(dst->msg_controllen >= src->msg_controllen); + memcpy(dst->msg_control, src->msg_control, src->msg_controllen); + dst->msg_controllen = src->msg_controllen; + } + dst->msg_flags = src->msg_flags; + msgvec[i].msg_len = msgvec[0].msg_len; + } + + return n; +} + +ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { + struct ts_message *last_ts_msg = NULL; + struct Reply_recv rep; + struct sockaddr_in *sa; + struct cmsghdr *cmsg; + int msglen, cmsglen, s = get_socket_from_fd(sockfd); + + if (sockfd == clknetsim_fd) + return _recvmsg(sockfd, msg, flags); + + assert(s >= 0 && sockets[s].type == SOCK_DGRAM); + + if (sockets[s].last_ts_msg.len && flags & MSG_ERRQUEUE) { + uint32_t addr; + uint16_t port; + + /* last message looped back to the error queue */ + + last_ts_msg = &sockets[s].last_ts_msg; + + msg->msg_flags = MSG_ERRQUEUE; + + rep.subnet = last_ts_msg->subnet; + rep.from = last_ts_msg->to; + rep.src_port = last_ts_msg->port; + rep.dst_port = sockets[s].port; + + addr = htonl(NODE_ADDR(rep.subnet, rep.from)); + port = htons(rep.src_port); + + /* put the message in an Ethernet frame */ + memset(rep.data, 0, 42); + rep.data[12] = 0x08; + rep.data[14] = 0x45; + rep.data[23] = 17; + memcpy(rep.data + 30, &addr, sizeof (addr)); + memcpy(rep.data + 36, &port, sizeof (port)); + + assert(last_ts_msg->len + 42 <= sizeof (rep.data)); + memcpy(rep.data + 42, last_ts_msg->data, last_ts_msg->len); + + rep.len = 42 + last_ts_msg->len; + + last_ts_msg->len = 0; + } else + make_request(REQ_RECV, NULL, 0, &rep, sizeof (rep)); + + if (rep.len == 0 && rep.from == -1) { + errno = EWOULDBLOCK; + return -1; + } + + assert(socket_in_subnet(s, rep.subnet)); + assert(sockets[s].port == rep.dst_port); + assert(!sockets[s].remote_port || sockets[s].remote_port == rep.src_port); + + if (msg->msg_name) { + assert(msg->msg_namelen >= sizeof (struct sockaddr_in)); + + sa = msg->msg_name; + sa->sin_family = AF_INET; + sa->sin_port = htons(rep.src_port); + sa->sin_addr.s_addr = htonl(NODE_ADDR(rep.subnet, rep.from)); + msg->msg_namelen = sizeof (struct sockaddr_in); + } + + assert(msg->msg_iovlen == 1); + msglen = msg->msg_iov[0].iov_len < rep.len ? msg->msg_iov[0].iov_len : rep.len; + memcpy(msg->msg_iov[0].iov_base, rep.data, msglen); + + cmsglen = 0; + + if (sockets[s].pkt_info) { + struct in_pktinfo ipi; + + cmsglen = CMSG_SPACE(sizeof (ipi)); + assert(msg->msg_control && msg->msg_controllen >= cmsglen); + + cmsg = CMSG_FIRSTHDR(msg); + memset(cmsg, 0, sizeof (*cmsg)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof (ipi)); + + memset(&ipi, 0, sizeof (ipi)); + ipi.ipi_spec_dst.s_addr = htonl(NODE_ADDR(rep.subnet, node)); + ipi.ipi_addr.s_addr = ipi.ipi_spec_dst.s_addr; + ipi.ipi_ifindex = rep.subnet + 1; + + memcpy(CMSG_DATA(cmsg), &ipi, sizeof (ipi)); + } + +#ifdef SO_TIMESTAMPING + if (last_ts_msg || + (sockets[s].time_stamping & (SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE) && + !(flags & MSG_ERRQUEUE))) { + struct timespec ts; + + /* don't use CMSG_NXTHDR as it's buggy in glibc */ + cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen); + cmsglen += CMSG_SPACE(3 * sizeof (ts)); + assert(msg->msg_control && msg->msg_controllen >= cmsglen); + + memset(cmsg, 0, CMSG_SPACE(3 * sizeof (ts))); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SO_TIMESTAMPING; + cmsg->cmsg_len = CMSG_LEN(3 * sizeof (ts)); + + if (sockets[s].time_stamping & SOF_TIMESTAMPING_SOFTWARE) { + clock_gettime(CLOCK_REALTIME, &ts); + memcpy((struct timespec *)CMSG_DATA(cmsg), &ts, sizeof (ts)); + } + if (sockets[s].time_stamping & SOF_TIMESTAMPING_RAW_HARDWARE) { + clock_gettime(timestamping > 1 ? REFCLK_ID : CLOCK_REALTIME, &ts); + memcpy((struct timespec *)CMSG_DATA(cmsg) + 2, &ts, sizeof (ts)); + } + } +#endif + msg->msg_controllen = cmsglen; + + return msglen; +} + +ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { + ssize_t ret; + struct msghdr msg; + struct iovec iov; + + iov.iov_base = (void *)buf; + iov.iov_len = len; + + /* needed for compatibility with old glibc recvmsg() */ + memset(&msg, 0, sizeof (msg)); + + msg.msg_name = (void *)src_addr; + msg.msg_namelen = *addrlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + ret = recvmsg(sockfd, &msg, flags); + *addrlen = msg.msg_namelen; + + return ret; +} + +ssize_t recv(int sockfd, void *buf, size_t len, int flags) { + struct sockaddr_in sa; + socklen_t addrlen = sizeof (sa); + + return recvfrom(sockfd, buf, len, flags, (struct sockaddr *)&sa, &addrlen); +} + +int timer_create(clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id) { + int t; + + assert(which_clock == CLOCK_REALTIME && timer_event_spec == NULL); + + t = get_free_timer(); + if (t < 0) { + assert(0); + errno = ENOMEM; + return -1; + } + + timers[t].used = 1; + timers[t].armed = 0; + timers[t].type = TIMER_TYPE_SIGNAL; + timers[t].clock_id = which_clock; + *created_timer_id = get_timerid(t); + + return 0; +} + +int timer_delete(timer_t timerid) { + int t = get_timer_from_id(timerid); + + if (t < 0) { + errno = EINVAL; + return -1; + } + + timers[t].used = 0; + + return 0; +} + +int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { + int t = get_timer_from_id(timerid); + + if (t < 0) { + errno = EINVAL; + return -1; + } + + assert(value && ovalue == NULL && + (flags == 0 || (flags == TIMER_ABSTIME && timers[t].clock_id == CLOCK_MONOTONIC))); + + if (value->it_value.tv_sec || value->it_value.tv_nsec) { + timers[t].armed = 1; + timers[t].timeout = timespec_to_time(&value->it_value, 0); + if (!(flags & TIMER_ABSTIME)) + timers[t].timeout += get_monotonic_time(); + timers[t].interval = timespec_to_time(&value->it_interval, 0); + } else { + timers[t].armed = 0; + } + + return 0; +} + +int timer_gettime(timer_t timerid, struct itimerspec *value) { + double timeout; + int t = get_timer_from_id(timerid); + + if (t < 0) { + errno = EINVAL; + return -1; + } + + if (timers[t].armed) { + timeout = timers[t].timeout - get_monotonic_time(); + time_to_timespec(timeout, &value->it_value); + } else { + value->it_value.tv_sec = 0; + value->it_value.tv_nsec = 0; + } + time_to_timespec(timers[t].interval, &value->it_interval); + + return 0; +} + +#ifndef CLKNETSIM_DISABLE_ITIMER +int setitimer(__itimer_which_t which, const struct itimerval *new_value, struct itimerval *old_value) { + struct itimerspec timerspec; + + assert(which == ITIMER_REAL && old_value == NULL); + + if (get_timer_from_id(itimer_real_id) < 0) + timer_create(CLOCK_REALTIME, NULL, &itimer_real_id); + + timerspec.it_interval.tv_sec = new_value->it_interval.tv_sec; + timerspec.it_interval.tv_nsec = new_value->it_interval.tv_usec * 1000; + timerspec.it_value.tv_sec = new_value->it_value.tv_sec; + timerspec.it_value.tv_nsec = new_value->it_value.tv_usec * 1000; + + return timer_settime(itimer_real_id, 0, &timerspec, NULL); +} + +int getitimer(__itimer_which_t which, struct itimerval *curr_value) { + struct itimerspec timerspec; + + assert(which == ITIMER_REAL); + + if (timer_gettime(itimer_real_id, &timerspec)) + return -1; + + curr_value->it_interval.tv_sec = timerspec.it_interval.tv_sec; + curr_value->it_interval.tv_usec = timerspec.it_interval.tv_nsec / 1000; + curr_value->it_value.tv_sec = timerspec.it_value.tv_sec; + curr_value->it_value.tv_usec = timerspec.it_value.tv_nsec / 1000; + + return 0; +} +#endif + +int timerfd_create(int clockid, int flags) { + int t; + + assert((clockid == CLOCK_REALTIME || clockid == CLOCK_MONOTONIC) && !flags); + + t = get_free_timer(); + if (t < 0) { + assert(0); + errno = ENOMEM; + return -1; + } + + timers[t].used = 1; + timers[t].armed = 0; + timers[t].type = TIMER_TYPE_FD; + timers[t].clock_id = clockid; + + return get_timerfd(t); +} + +int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) { + if (flags == TFD_TIMER_ABSTIME) + flags = TIMER_ABSTIME; + else + assert(!flags); + + return timer_settime(get_timerid(get_timer_from_fd(fd)), flags, new_value, old_value); +} + +int timerfd_gettime(int fd, struct itimerspec *curr_value) { + return timer_gettime(get_timerid(get_timer_from_fd(fd)), curr_value); +} + +int shmget(key_t key, size_t size, int shmflg) { + if (fuzz_mode) + return _shmget(key, size, shmflg); + + if (key >= SHM_KEY && key < SHM_KEY + SHM_REFCLOCKS) + return key; + + return -1; +} + +void *shmat(int shmid, const void *shmaddr, int shmflg) { + if (fuzz_mode) + return _shmat(shmid, shmaddr, shmflg); + + assert(shmid >= SHM_KEY && shmid < SHM_KEY + SHM_REFCLOCKS); + + if (shm_refclocks < shmid - SHM_KEY + 1) + shm_refclocks = shmid - SHM_KEY + 1; + memset(&shm_time[shmid - SHM_KEY], 0, sizeof (shm_time[0])); + shm_time[shmid - SHM_KEY].mode = 1; + shm_time[shmid - SHM_KEY].precision = -20; + + /* don't wait for select() with starting of the refclock generator */ + fill_refclock_sample(); + + return &shm_time[shmid - SHM_KEY]; +} + +int shmdt(const void *shmaddr) { + assert(shmaddr >= (void *)&shm_time[0] && shmaddr < (void *)&shm_time[SHM_REFCLOCKS]); + return 0; +} + +uid_t getuid(void) { + return 0; +} + +int uname(struct utsname *buf) { + memset(buf, 0, sizeof (*buf)); + sprintf(buf->sysname, "Linux (clknetsim)"); + sprintf(buf->release, "4.19"); + return 0; +} + +int gethostname(char *name, size_t len) { + snprintf(name, len, "clknetsim-node%d", node + 1); + return 0; +} + +void openlog(const char *ident, int option, int facility) { +} + +void __syslog_chk(int priority, int flag, const char *format, ...) { + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +void syslog(int priority, const char *format, ...) { + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +void closelog(void) { +} + +#ifndef CLKNETSIM_DISABLE_SYSCALL +long syscall(long number, ...) { + va_list ap; + long r; + struct timex *timex; + clockid_t clock_id; + + va_start(ap, number); + switch (number) { +#ifdef __NR_clock_adjtime + case __NR_clock_adjtime: + clock_id = va_arg(ap, clockid_t); + timex = va_arg(ap, struct timex *); + r = clock_adjtime(clock_id, timex); + break; +#endif + default: + assert(0); + } + va_end(ap); + + return r; +} +#endif + +ssize_t getrandom(void *buf, size_t length, unsigned int flags) { + errno = ENOTSUP; + return -1; +} + +void srandom(unsigned int seed) { + FILE *f; + + /* override the seed to the fixed seed if set or make it truly + random in case it's based on the simulated time */ + if (random_seed) { + seed = random_seed + node; + } else if ((f = _fopen("/dev/urandom", "r"))) { + if (fread(&seed, sizeof (seed), 1, f) != 1) + ; + fclose(f); + } + _srandom(seed); +} + +struct passwd *getpwnam(const char *name) { + static struct passwd pw = { + .pw_name = "", + .pw_passwd = "", + .pw_uid = 0, + .pw_gid = 0, + .pw_gecos = "", + .pw_dir = "", + .pw_shell = "" + }; + + return &pw; +} + +int initgroups(const char *user, gid_t group) { + return 0; +} + +int setgroups(size_t size, const gid_t *list) { + return 0; +} + +int setegid(gid_t gid) { + return 0; +} + +int setgid(gid_t gid) { + return 0; +} + +int seteuid(uid_t uid) { + return 0; +} + +int setuid(uid_t uid) { + return 0; +} + +int cap_set_proc() { + return 0; +} diff --git a/test/simulation/clknetsim/client_fuzz.c b/test/simulation/clknetsim/client_fuzz.c new file mode 100644 index 0000000..87ce98f --- /dev/null +++ b/test/simulation/clknetsim/client_fuzz.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2015 Miroslav Lichvar + * + * 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, see . + */ + +/* This is a minimal replacement for the clknetsim server to allow fuzz + testing. There is no clock control or networking. When the time reaches + fuzz_start, a packet read from stdin is forwarded to the fuzz_port port of + the client, and the client is terminated. Packets sent by the client from + the port are written to stdout. */ + +enum { + FUZZ_MODE_DISABLED = 0, + FUZZ_MODE_ONESHOT = 1, + FUZZ_MODE_BURST = 2, + FUZZ_MODE_REPLY = 3, + FUZZ_MODE_NONE = 4, +}; + +#define FUZZ_FLAG_TIMEOUT 1024 + +#define MAX_FUZZ_PORTS 16 + +static int fuzz_mode; +static int fuzz_ports[MAX_FUZZ_PORTS]; +static int fuzz_port_index, fuzz_ports_n; +static int fuzz_timeout; +static double fuzz_start; + +static int fuzz_init(void) { + const char *env; + + env = getenv("CLKNETSIM_FUZZ_MODE"); + if (!env) + return 0; + + fuzz_mode = atoi(env); + + if (fuzz_mode & FUZZ_FLAG_TIMEOUT) { + fuzz_timeout = 1; + fuzz_mode &= ~FUZZ_FLAG_TIMEOUT; + } + + if (fuzz_mode == FUZZ_MODE_DISABLED) + return 0; + + if (fuzz_mode < FUZZ_MODE_ONESHOT || fuzz_mode > FUZZ_MODE_NONE) { + fprintf(stderr, "clknetsim: unknown fuzz mode.\n"); + exit(1); + } + + env = getenv("CLKNETSIM_FUZZ_PORT"); + + for (fuzz_ports_n = 0; env && fuzz_ports_n < MAX_FUZZ_PORTS; fuzz_ports_n++) { + fuzz_ports[fuzz_ports_n] = atoi(env); + if (!fuzz_ports[fuzz_ports_n]) + break; + env = strchr(env, ','); + if (env) + env++; + } + + if (!fuzz_ports_n) { + fprintf(stderr, "clknetsim: CLKNETSIM_FUZZ_PORT variable not set or invalid.\n"); + exit(1); + } + fuzz_port_index = 0; + + env = getenv("CLKNETSIM_FUZZ_START"); + fuzz_start = env ? atof(env) : 0.1; + + return 1; +} + +static int fuzz_is_fuzz_port(int port) { + int i; + + for (i = 0; i < fuzz_ports_n; i++) + if (fuzz_ports[i] == port) + return 1; + return 0; +} + +static int fuzz_get_fuzz_port(void) { + return fuzz_ports[fuzz_port_index]; +} + +static void fuzz_switch_fuzz_port(void) { + fuzz_port_index = (fuzz_port_index + 1) % fuzz_ports_n; +} + +static int fuzz_read_packet(char *data, int maxlen, int *rlen) { + int len; + uint16_t slen; + + if (fuzz_mode > FUZZ_MODE_ONESHOT) { + if (fread(&slen, 1, sizeof (slen), stdin) != sizeof (slen)) + return 0; + len = ntohs(slen); + if (len > maxlen) + len = maxlen; + } else { + len = maxlen; + } + + *rlen = fread(data, 1, len, stdin); + + return !len || rlen; +} + +static void fuzz_write_packet(const char *data, int len) { + uint16_t slen; + + if (fuzz_mode > FUZZ_MODE_ONESHOT) { + slen = htons(len); + fwrite(&slen, 1, sizeof (slen), stdout); + } + + fwrite(data, 1, len, stdout); +} + +static void fuzz_process_reply(int request_id, const union Request_data *request, union Reply_data *reply, int replylen) { + static double network_time = 0.0; + static int received = 0; + static int sent = 0; + static int dst_port = 0; + static int packet_len = 0; + static int valid_packet = 0; + static char packet[MAX_PACKET_SIZE]; + + if (reply) + memset(reply, 0, replylen); + + switch (request_id) { + case REQ_GETTIME: + reply->gettime.real_time = network_time; + reply->gettime.monotonic_time = network_time; + reply->gettime.network_time = network_time; + break; + case REQ_SELECT: + if (fuzz_mode == FUZZ_MODE_NONE) { + reply->select.ret = REPLY_SELECT_TIMEOUT; + return; + } + + if (!valid_packet && (!received || fuzz_mode != FUZZ_MODE_ONESHOT)) + valid_packet = fuzz_read_packet(packet, sizeof (packet), &packet_len); + + if (!valid_packet) { + reply->select.ret = REPLY_SELECT_TERMINATE; + } else if (!packet_len && fuzz_timeout) { + network_time += request->select.timeout; + reply->select.ret = REPLY_SELECT_TIMEOUT; + valid_packet = 0; + } else { + if (fuzz_mode == FUZZ_MODE_REPLY) { + if (sent > received) { + reply->select.ret = REPLY_SELECT_NORMAL; + } else { + network_time += request->select.timeout; + reply->select.ret = REPLY_SELECT_TIMEOUT; + } + } else { + if (network_time < fuzz_start && !sent) { + network_time += request->select.timeout; + if (network_time >= fuzz_start) { + network_time = fuzz_start; + reply->select.ret = REPLY_SELECT_NORMAL; + } else { + reply->select.ret = REPLY_SELECT_TIMEOUT; + } + } else { + reply->select.ret = REPLY_SELECT_NORMAL; + } + } + } + + reply->select.subnet = 0; + reply->select.dst_port = dst_port ? dst_port : fuzz_get_fuzz_port(); + reply->select.time.real_time = network_time; + reply->select.time.monotonic_time = network_time; + reply->select.time.network_time = network_time; + break; + case REQ_SEND: + if (request->send.to != 1 && request->send.to != -1) + break; + + if (fuzz_mode == FUZZ_MODE_REPLY) { + if (!fuzz_is_fuzz_port(request->send.dst_port)) + break; + dst_port = request->send.src_port; + } else if (!fuzz_is_fuzz_port(request->send.src_port)) + break; + + fuzz_write_packet(request->send.data, request->send.len); + sent++; + break; + case REQ_RECV: + network_time += 1e-5; + reply->recv.subnet = 0; + reply->recv.from = valid_packet ? 1 : -1; + reply->recv.src_port = fuzz_get_fuzz_port(); + reply->recv.dst_port = dst_port ? dst_port : fuzz_get_fuzz_port(); + memcpy(reply->recv.data, packet, packet_len); + reply->recv.len = packet_len; + received++; + valid_packet = 0; + packet_len = 0; + fuzz_switch_fuzz_port(); + break; + case REQ_SETTIME: + network_time = request->settime.time; + break; + case REQ_ADJTIME: + case REQ_GETREFSAMPLE: + case REQ_GETREFOFFSETS: + case REQ_DEREGISTER: + break; + case REQ_ADJTIMEX: + reply->adjtimex.timex.tick = 10000; + break; + case REQ_REGISTER: + default: + assert(0); + } +} diff --git a/test/simulation/clknetsim/clknetsim.bash b/test/simulation/clknetsim/clknetsim.bash new file mode 100644 index 0000000..fe9449c --- /dev/null +++ b/test/simulation/clknetsim/clknetsim.bash @@ -0,0 +1,260 @@ +# Copyright (C) 2010, 2011 Miroslav Lichvar +# +# 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, see . + +[ -n "$CLKNETSIM_TMPDIR" ] || CLKNETSIM_TMPDIR=tmp + +client_pids="" + +start_client() { + local node=$1 client=$2 config=$3 suffix=$4 opts=$5 + local args=() line lastpid + + rm -f $CLKNETSIM_TMPDIR/log.$node $CLKNETSIM_TMPDIR/conf.$node + + [ $client = chrony ] && client=chronyd + [ $client = ntp ] && client=ntpd + + if ! which $client$suffix &> /dev/null; then + echo "can't find $client$suffix in PATH" + return 1 + fi + + case $client in + chronyd) + cat > $CLKNETSIM_TMPDIR/conf.$node <<-EOF + pidfile $CLKNETSIM_TMPDIR/pidfile.$node + allow + cmdallow + bindcmdaddress 0.0.0.0 + $config + EOF + args=(-d -f $CLKNETSIM_TMPDIR/conf.$node $opts) + ;; + ntpd) + cat > $CLKNETSIM_TMPDIR/conf.$node <<-EOF + pidfile $CLKNETSIM_TMPDIR/pidfile.$node + restrict default + $config + EOF + args=(-n -c $CLKNETSIM_TMPDIR/conf.$node $opts) + ;; + ptp4l) + cat > $CLKNETSIM_TMPDIR/conf.$node <<-EOF + [global] + $config + EOF + args=(-f $CLKNETSIM_TMPDIR/conf.$node $opts) + ;; + chronyc) + args=($opts -m) + while read line; do args+=("$line"); done <<< "$config" + ;; + pmc) + args=($opts) + while read line; do args+=("$line"); done <<< "$config" + ;; + ntpq) + while read line; do args+=(-c "$line"); done <<< "$config" + args+=($opts) + ;; + sntp) + args=(-K /dev/null $opts $config) + ;; + ntpdate) + args=($opts $config) + ;; + busybox) + args=(ntpd -ddd -n) + while read line; do args+=(-p "$line"); done <<< "$config" + args+=($opts) + ;; + phc2sys) + args=(-s /dev/ptp0 -O 0 $opts $config) + ;; + nsm) + args=($opts) + while read line; do args+=("$line"); done <<< "$config" + ;; + *) + echo "unknown client $client" + exit 1 + ;; + esac + + LD_PRELOAD=$CLKNETSIM_PATH/clknetsim.so \ + CLKNETSIM_NODE=$node CLKNETSIM_SOCKET=$CLKNETSIM_TMPDIR/sock \ + $client_wrapper $client$suffix "${args[@]}" &> $CLKNETSIM_TMPDIR/log.$node & + lastpid=$! + disown $lastpid + + client_pids="$client_pids $lastpid" +} + +start_server() { + local nodes=$1 ret=0 + shift + $server_wrapper $CLKNETSIM_PATH/clknetsim "$@" -s $CLKNETSIM_TMPDIR/sock \ + $CLKNETSIM_TMPDIR/conf $nodes > $CLKNETSIM_TMPDIR/stats 2> $CLKNETSIM_TMPDIR/log + if [ $? -ne 0 ]; then + echo clknetsim failed 1>&2 + ret=1 + fi + kill $client_pids &> /dev/null + client_pids=" " + return $ret +} + +generate_seq() { + $CLKNETSIM_PATH/clknetsim -G "$@" +} + +generate_config1() { + local nodes=$1 offset=$2 freqexpr=$3 delayexprup=$4 delayexprdown=$5 refclockexpr=$6 i + + for i in `seq 2 $nodes`; do + echo "node${i}_offset = $offset" + echo "node${i}_freq = $freqexpr" + echo "node${i}_delay1 = $delayexprup" + if [ -n "$delayexprdown" ]; then + echo "node1_delay${i} = $delayexprdown" + else + echo "node1_delay${i} = $delayexprup" + fi + [ -n "$refclockexpr" ] && echo "node${i}_refclock = $refclockexpr" + done > $CLKNETSIM_TMPDIR/conf +} + +generate_config2() { + local nodes=$1 offset=$2 freqexpr=$3 delayexpr=$4 i j + + for i in `seq 2 $nodes`; do + echo "node${i}_offset = $offset" + echo "node${i}_freq = $freqexpr" + for j in `seq 1 $nodes`; do + [ $i -eq $j ] && continue + echo "node${i}_delay${j} = $delayexpr" + echo "node${j}_delay${i} = $delayexpr" + done + done > $CLKNETSIM_TMPDIR/conf +} + +generate_config3() { + local topnodes=$1 nodes=$2 offset=$3 freqexpr=$4 delayexprup=$5 delayexprdown=$6 i j + + for i in `seq $[$topnodes + 1] $nodes`; do + echo "node${i}_offset = $offset" + echo "node${i}_freq = $freqexpr" + for j in `seq 1 $topnodes`; do + [ $i -eq $j ] && continue + echo "node${i}_delay${j} = $delayexprup" + if [ -n "$delayexprdown" ]; then + echo "node${j}_delay${i} = $delayexprdown" + else + echo "node${j}_delay${i} = $delayexprup" + fi + done + done > $CLKNETSIM_TMPDIR/conf +} + +generate_config4() { + local stablenodes=$1 subnets=$2 offset=$3 freqexpr=$4 delayexpr=$5 + local subnet i j added + + echo "$subnets" | tr '|' '\n' | while read subnet; do + for i in $subnet; do + if ! [[ " $stablenodes $added " =~ [^0-9]$i[^0-9] ]]; then + echo "node${i}_offset = $offset" + echo "node${i}_freq = $freqexpr" + fi + for j in $subnet; do + [ $i -eq $j ] && continue + echo "node${i}_delay${j} = $delayexpr" + done + added="$added $i" + done + done > $CLKNETSIM_TMPDIR/conf +} + +find_sync() { + local offlog=$1 freqlog=$2 index=$3 offsync=$4 freqsync=$5 smooth=$6 + + [ -z "$smooth" ] && smooth=0.05 + + paste <(cut -f $index $1) <(cut -f $index $2) | awk ' + BEGIN { + lastnonsync = -1 + time = 0 + } + { + off = $1 < 0 ? -$1 : $1 + freq = $2 < 0 ? -$2 : $2 + + if (avgoff == 0.0 && avgfreq == 0.0) { + avgoff = off + avgfreq = freq + } else { + avgoff += '$smooth' * (off - avgoff) + avgfreq += '$smooth' * (freq - avgfreq) + } + + if (avgoff > '$offsync' || avgfreq > '$freqsync') { + lastnonsync = time + } + time++ + } END { + if (lastnonsync < time) { + print lastnonsync + 1 + } else { + print -1 + } + }' +} + +get_stat() { + local statname=$1 index=$2 + + if [ -z "$index" ]; then + echo $(cat $CLKNETSIM_TMPDIR/stats | grep "^$statname:" | cut -f 2) + else + cat $CLKNETSIM_TMPDIR/stats | grep "^$statname:" | cut -f 2 | + head -n $index | tail -n 1 + fi +} + +check_stat() { + local value=$1 min=$2 max=$3 tolerance=$4 + [ -z "$tolerance" ] && tolerance=0.0 + awk " + BEGIN { + eq = (\"$value\" == \"inf\" || + $value + $value / 1e6 + $tolerance >= $min) && + (\"$max\" == \"inf\" || + (\"$value\" != \"inf\" && + $value - $value / 1e6 - $tolerance <= $max)) + exit !eq + }" +} + +if [ -z "$CLKNETSIM_PATH" ]; then + echo CLKNETSIM_PATH not set 2>&1 + exit 1 +fi + +if [ ! -x "$CLKNETSIM_PATH/clknetsim" -o ! -e "$CLKNETSIM_PATH/clknetsim.so" ]; then + echo "can't find clknetsim or clknetsim.so in $CLKNETSIM_PATH" + exit 1 +fi + +[ -d $CLKNETSIM_TMPDIR ] || mkdir $CLKNETSIM_TMPDIR diff --git a/test/simulation/clknetsim/clock.cc b/test/simulation/clknetsim/clock.cc new file mode 100644 index 0000000..1e01fed --- /dev/null +++ b/test/simulation/clknetsim/clock.cc @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#include "clock.h" + +#define MINSEC 256 +#define MAXSEC 2048 +#define MAXTIMECONST 10 +#define MAXMAXERROR 16000000 +#define SHIFT_FLL 2 +#define SCALE_FREQ 65536.0e6 +#define MAXFREQ_SCALED 32768000 +#define MAX_SLEWRATE 500 +#define MAX_TICK(base_tick) ((base_tick) * 11 / 10) +#define MIN_TICK(base_tick) ((base_tick) * 9 / 10) + +#define MIN_FREQ 0.8 +#define MAX_FREQ 1.2 + +Clock::Clock() { + time = 0.0; + mono_time = 0.0; + freq = 1.0; + + freq_generator = NULL; + step_generator = NULL; + + base_tick = sysconf(_SC_CLK_TCK); + assert(base_tick > 0); + base_tick = (1000000 + base_tick / 2) / base_tick; + + memset(&ntp_timex, 0, sizeof(ntp_timex)); + ntp_timex.tick = base_tick; + ntp_timex.tolerance = MAXFREQ_SCALED; + ntp_timex.precision = 1; + + ntp_state = TIME_OK; + + /* in Linux kernel SHIFT_PLL is 2 since 2.6.31 */ + ntp_shift_pll = 2; + ntp_flags = 0; + ntp_update_interval = 0; + ntp_offset = 0.0; + ntp_slew = 0.0; + + ss_offset = 0; + ss_slew = 0; +} + +Clock::~Clock() { + if (freq_generator) + delete freq_generator; + if (step_generator) + delete step_generator; +} + +double Clock::get_real_time() const { + return time; +} + +double Clock::get_monotonic_time() const { + return mono_time; +} + +double Clock::get_total_freq() const { + double timex_freq, adjtime_freq; + + timex_freq = (double)ntp_timex.tick / base_tick + ntp_timex.freq / SCALE_FREQ + ntp_slew; + adjtime_freq = ss_slew / 1e6; + return freq * (timex_freq + adjtime_freq); +} + +double Clock::get_raw_freq() const { + double timex_freq; + + timex_freq = (double)ntp_timex.tick / base_tick + ntp_timex.freq / SCALE_FREQ; + return freq * timex_freq; +} + +double Clock::get_true_interval(double local_interval) const { + return local_interval / get_total_freq(); +} + +double Clock::get_local_interval(double true_interval) const { + return true_interval * get_total_freq(); +} + +void Clock::set_freq_generator(Generator *gen) { + if (freq_generator) + delete freq_generator; + freq_generator = gen; +} + +void Clock::set_step_generator(Generator *gen) { + if (step_generator) + delete step_generator; + step_generator = gen; +} + +void Clock::set_freq(double freq) { + this->freq = freq + 1.0; + if (!(this->freq > MIN_FREQ && this->freq < MAX_FREQ)) { + fprintf(stderr, "frequency %e outside allowed range (%.2f, %.2f)\n", this->freq - 1.0, MIN_FREQ - 1.0, MAX_FREQ - 1.0); + exit(1); + } +} + +void Clock::set_time(double time) { + this->time = time; +} + +void Clock::step_time(double step) { + this->time += step; +} + +void Clock::set_ntp_shift_pll(int shift) { + ntp_shift_pll = shift; +} + +void Clock::set_ntp_flag(int enable, int flag) { + ntp_flags &= ~flag; + if (enable) + ntp_flags |= flag; +} + +void Clock::advance(double real_interval) { + double local_interval = get_local_interval(real_interval); + + time += local_interval; + mono_time += local_interval; +} + +void Clock::update(bool second) { + if (freq_generator) + set_freq(freq_generator->generate(NULL)); + if (step_generator) + step_time(step_generator->generate(NULL)); + + if (!second) + return; + + if (ntp_timex.status & STA_PLL) { + ntp_update_interval++; + ntp_slew = ntp_offset / (1 << (ntp_shift_pll + + ntp_timex.constant)); + +#if 0 + if (ntp_slew > MAX_SLEWRATE / 1e6) + ntp_slew = MAX_SLEWRATE / 1e6; + else if (ntp_slew < -MAX_SLEWRATE / 1e6) + ntp_slew = -MAX_SLEWRATE / 1e6; +#endif + + ntp_offset -= ntp_slew; + + if (ntp_timex.status & STA_NANO) + ntp_timex.offset = ntp_offset * 1e9; + else + ntp_timex.offset = ntp_offset * 1e6; + } + + if (ss_offset) { + if (ss_offset > 0) { + if (ss_offset > MAX_SLEWRATE) { + ss_slew = MAX_SLEWRATE; + ss_offset -= MAX_SLEWRATE; + } else { + ss_slew = ss_offset; + ss_offset = 0; + } + } else { + if (ss_offset < -MAX_SLEWRATE) { + ss_slew = -MAX_SLEWRATE; + ss_offset -= -MAX_SLEWRATE; + } else { + ss_slew = ss_offset; + ss_offset = 0; + } + } + } else + ss_slew = 0; + + switch (ntp_state) { + case TIME_OK: + if (ntp_timex.status & STA_INS) + ntp_state = TIME_INS; + else if (ntp_timex.status & STA_DEL) + ntp_state = TIME_DEL; + break; + case TIME_INS: + if ((time_t)(time + 0.5) % (24 * 3600) <= 1) { + time -= 1.0; + ntp_timex.tai += 1.0; + ntp_state = TIME_OOP; + } else if (!(ntp_timex.status & STA_INS)) { + ntp_state = TIME_OK; + } + break; + case TIME_DEL: + if ((time_t)(time + 1.0 + 0.5) % (24 * 3600) <= 1) { + time += 1.0; + ntp_timex.tai -= 1.0; + ntp_state = TIME_WAIT; + } else if (!(ntp_timex.status & STA_DEL)) { + ntp_state = TIME_OK; + } + break; + case TIME_OOP: + ntp_state = TIME_WAIT; + break; + case TIME_WAIT: + if (!(ntp_timex.status & (STA_INS | STA_DEL))) + ntp_state = TIME_OK; + break; + default: + assert(0); + } +} + +void Clock::update_ntp_offset(long offset) { + double fll_adj, pll_adj, new_offset, old_offset, tc, t; + + if (ntp_timex.status & STA_FREQHOLD) + ntp_update_interval = 0; + + if (ntp_timex.status & STA_NANO) + new_offset = offset / 1e9; + else + new_offset = offset / 1e6; + + tc = 1 << ntp_timex.constant; + ntp_timex.offset = offset; + old_offset = ntp_offset; + ntp_offset = new_offset; + + if (!(ntp_timex.status & STA_PLL)) + return; + + if (old_offset && ntp_update_interval >= MINSEC && + (ntp_timex.status & STA_FLL || ntp_update_interval > MAXSEC)) { + ntp_timex.status |= STA_MODE; + if (ntp_flags & CLOCK_NTP_FLL_MODE2) + fll_adj = (new_offset - old_offset) / (ntp_update_interval * (1 << SHIFT_FLL)); + else + fll_adj = new_offset / (ntp_update_interval * (1 << SHIFT_FLL)); + } else { + ntp_timex.status &= ~STA_MODE; + fll_adj = 0.0; + } + + if (ntp_flags & CLOCK_NTP_PLL_CLAMP) { + if (ntp_update_interval > MAXSEC) + ntp_update_interval = MAXSEC; + if (ntp_update_interval > tc * (1 << (ntp_shift_pll + 1))) + ntp_update_interval = tc * (1 << (ntp_shift_pll + 1)); + } + + t = 4 * (1 << ntp_shift_pll) * tc; + pll_adj = new_offset * ntp_update_interval / (t * t); + + ntp_timex.freq += (fll_adj + pll_adj) * SCALE_FREQ; + + if (ntp_timex.freq > MAXFREQ_SCALED) + ntp_timex.freq = MAXFREQ_SCALED; + else if (ntp_timex.freq < -MAXFREQ_SCALED) + ntp_timex.freq = -MAXFREQ_SCALED; + + ntp_update_interval = 0; +} + +int Clock::adjtimex(struct timex *buf) { + int r = ntp_state; + struct timex t; + + if (buf->modes & ADJ_FREQUENCY) { + ntp_timex.freq = buf->freq; + if (ntp_timex.freq > MAXFREQ_SCALED) + ntp_timex.freq = MAXFREQ_SCALED; + else if (ntp_timex.freq < -MAXFREQ_SCALED) + ntp_timex.freq = -MAXFREQ_SCALED; + } + if (buf->modes & ADJ_MAXERROR) + ntp_timex.maxerror = buf->maxerror; + if (buf->modes & ADJ_STATUS) { + if ((buf->status & STA_PLL) && !(ntp_timex.status & STA_PLL)) + ntp_update_interval = 0; + ntp_timex.status = buf->status & 0xff; + } + if (buf->modes & ADJ_MICRO) + ntp_timex.status &= ~STA_NANO; + if (buf->modes & ADJ_NANO) + ntp_timex.status |= STA_NANO; + if (buf->modes & ADJ_TIMECONST) { + ntp_timex.constant = buf->constant; + if (!(ntp_timex.status & STA_NANO)) + ntp_timex.constant += 4; + if (ntp_timex.constant > MAXTIMECONST) + ntp_timex.constant = MAXTIMECONST; + if (ntp_timex.constant < 0) + ntp_timex.constant = 0; + } + if (buf->modes & ADJ_TICK) { + if (buf->tick > MAX_TICK(base_tick) || buf->tick < MIN_TICK(base_tick)) { + r = -1; + } else + ntp_timex.tick = buf->tick; + } + if ((buf->modes & ADJ_OFFSET_SINGLESHOT) != ADJ_OFFSET_SINGLESHOT) { + if (buf->modes & ADJ_OFFSET) { + update_ntp_offset(buf->offset); + } + } + if (buf->modes & ADJ_SETOFFSET) { + if (ntp_timex.status & STA_NANO) + time += buf->time.tv_sec + buf->time.tv_usec * 1e-9; + else + time += buf->time.tv_sec + buf->time.tv_usec * 1e-6; + ntp_timex.maxerror = MAXMAXERROR; + } + if (buf->modes & ADJ_TAI) { + ntp_timex.tai = buf->constant; + } + + t = ntp_timex; + + if ((buf->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) { + if ((buf->modes & ADJ_OFFSET_SS_READ) == ADJ_OFFSET_SINGLESHOT) { + t.offset = ss_offset; + ss_offset = buf->offset; + } else { + t.offset = ss_offset; + } + } + + *buf = t; + + return r; +} + +int Clock::adjtime(const struct timeval *delta, struct timeval *olddelta) { + if (olddelta) { + olddelta->tv_sec = ss_offset / 1000000; + olddelta->tv_usec = ss_offset % 1000000; + } + if (delta) + ss_offset = delta->tv_sec * 1000000 + delta->tv_usec; + return 0; +} + +Refclock::Refclock() { + time = 0.0; + offset = 0.0; + generate = false; + valid = false; + offset_generator = NULL; +} + +Refclock::~Refclock() { + if (offset_generator) + delete offset_generator; +} + +void Refclock::set_offset_generator(Generator *gen) { + if (offset_generator) + delete offset_generator; + offset_generator = gen; +} + +void Refclock::set_generation(bool enable) { + generate = enable; +} + +void Refclock::update(double time, const Clock *clock) { + if (!generate || !offset_generator) + return; + + this->time = clock->get_real_time(); + offset = this->time - time + offset_generator->generate(NULL); + valid = true; +} + +bool Refclock::get_sample(double *time, double *offset) const { + *time = this->time; + *offset = this->offset; + return valid; +} + +void Refclock::get_offsets(double *offsets, int size) { + int i; + + for (i = 0; i < size; i++) + offsets[i] = offset_generator->generate(NULL); +} diff --git a/test/simulation/clknetsim/clock.h b/test/simulation/clknetsim/clock.h new file mode 100644 index 0000000..cb3ead1 --- /dev/null +++ b/test/simulation/clknetsim/clock.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#ifndef CLOCK_H +#define CLOCK_H + +#include "sysheaders.h" + +#include "generator.h" + +#define CLOCK_NTP_FLL_MODE2 0x1 +#define CLOCK_NTP_PLL_CLAMP 0x2 + +class Clock { + double time; + double mono_time; + double freq; + + Generator *freq_generator; + Generator *step_generator; + + long base_tick; + + struct timex ntp_timex; + int ntp_state; + int ntp_shift_pll; + int ntp_flags; + long ntp_update_interval; + double ntp_offset; + double ntp_slew; + + long ss_offset; + long ss_slew; + +public: + Clock(); + ~Clock(); + double get_real_time() const; + double get_monotonic_time() const; + double get_total_freq() const; + double get_raw_freq() const; + double get_true_interval(double local_interval) const; + double get_local_interval(double true_interval) const; + + void set_freq_generator(Generator *gen); + void set_step_generator(Generator *gen); + void set_freq(double freq); + void set_time(double time); + void step_time(double step); + void set_ntp_shift_pll(int shift); + void set_ntp_flag(int enable, int flag); + + void advance(double real_interval); + void update(bool second); + + void update_ntp_offset(long offset); + int adjtimex(struct timex *buf); + int adjtime(const struct timeval *delta, struct timeval *olddelta); +}; + +class Refclock { + double time; + double offset; + bool generate; + bool valid; + + Generator *offset_generator; +public: + Refclock(); + ~Refclock(); + void set_offset_generator(Generator *gen); + void update(double time, const Clock *clock); + void set_generation(bool enable); + bool get_sample(double *time, double *offset) const; + void get_offsets(double *offsets, int size); +}; + +#endif diff --git a/test/simulation/clknetsim/examples/busybox.test b/test/simulation/clknetsim/examples/busybox.test new file mode 100755 index 0000000..d0430b3 --- /dev/null +++ b/test/simulation/clknetsim/examples/busybox.test @@ -0,0 +1,13 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config1 2 0.01 "(+ 1e-6 (sum (* 1e-9 (normal))))" "(+ 1e-3 (* 1e-3 (exponential)))" + +start_client 1 chrony "local stratum 1" +start_client 2 busybox "192.168.123.1" + +start_server 2 -v 2 -o log.offset -f log.freq -g log.rawfreq -p log.packets -r 2000 -l 400000 + +cat tmp/stats diff --git a/test/simulation/clknetsim/examples/chronyc.test b/test/simulation/clknetsim/examples/chronyc.test new file mode 100755 index 0000000..116bfee --- /dev/null +++ b/test/simulation/clknetsim/examples/chronyc.test @@ -0,0 +1,17 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config4 "1" "1 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" + +echo "node3_start = 10000" >> tmp/conf +start_client 1 chronyd "local stratum 1" +start_client 2 chronyd "server 192.168.123.1" +start_client 3 chronyc "tracking +sources -n +sourcestats" "" "-h 192.168.123.2" + +start_server 3 -v 2 -l 10001 + +cat tmp/log.3 diff --git a/test/simulation/clknetsim/examples/chronyd.test b/test/simulation/clknetsim/examples/chronyd.test new file mode 100755 index 0000000..0e2de1f --- /dev/null +++ b/test/simulation/clknetsim/examples/chronyd.test @@ -0,0 +1,14 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config1 2 0.01 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" "" "(* 1e-4 (normal))" + +start_client 1 chrony "local stratum 1" +start_client 2 chrony "server 192.168.123.1 minpoll 6 maxpoll 6 +refclock SHM 0" + +start_server 2 -v 2 -o log.offset -f log.freq -g log.rawfreq -p log.packets -r 2000 -l 40000 + +cat tmp/stats diff --git a/test/simulation/clknetsim/examples/nsm.test b/test/simulation/clknetsim/examples/nsm.test new file mode 100755 index 0000000..b55120c --- /dev/null +++ b/test/simulation/clknetsim/examples/nsm.test @@ -0,0 +1,19 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config4 "1 3" "1 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" + +echo 'node3_start = 50' >> tmp/conf + +start_client 1 ptp4l "hybrid_e2e 1 +net_sync_monitor 1" "" "-i eth0" +start_client 2 ptp4l "hybrid_e2e 1 +net_sync_monitor 1" "" "-i eth0" +start_client 3 nsm "NSM 192.168.123.1 +NSM 192.168.123.2" "" "-i eth0" + +start_server 3 -l 110 + +cat tmp/log.3 diff --git a/test/simulation/clknetsim/examples/ntpd.test b/test/simulation/clknetsim/examples/ntpd.test new file mode 100755 index 0000000..5609b12 --- /dev/null +++ b/test/simulation/clknetsim/examples/ntpd.test @@ -0,0 +1,19 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config1 3 0.01 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" +echo "node2_shift_pll = 2" >> tmp/conf + +start_client 1 ntp "server 127.127.1.0" +start_client 2 ntp "server 192.168.123.1 minpoll 6 maxpoll 6" +start_client 3 ntp "server 192.168.123.1 minpoll 6 maxpoll 6" + +start_server 3 -v 2 -o log.offset -r 2000 -l 40000 + +cat tmp/stats + +echo +get_stat 'RMS offset' +get_stat 'RMS frequency' diff --git a/test/simulation/clknetsim/examples/ntpdate.test b/test/simulation/clknetsim/examples/ntpdate.test new file mode 100755 index 0000000..9b49180 --- /dev/null +++ b/test/simulation/clknetsim/examples/ntpdate.test @@ -0,0 +1,17 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config1 3 10.0 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" + +echo "node2_start = 100" >> tmp/conf +echo "node3_start = 100" >> tmp/conf + +start_client 1 ntpd "server 127.127.1.0" +start_client 2 ntpdate "-B 192.168.123.1" +start_client 3 ntpdate "-b 192.168.123.1" + +start_server 3 -v 2 -o log.offset -r 110 -l 200 + +cat tmp/stats diff --git a/test/simulation/clknetsim/examples/ntpq.test b/test/simulation/clknetsim/examples/ntpq.test new file mode 100755 index 0000000..b2b8763 --- /dev/null +++ b/test/simulation/clknetsim/examples/ntpq.test @@ -0,0 +1,17 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config4 "1" "1 2 | 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" + +echo 'node3_start = 1000' >> tmp/conf + +start_client 1 ntpd "server 127.127.1.0" +start_client 2 ntpd "server 192.168.123.1 minpoll 6 maxpoll 6" +start_client 3 ntpq "rv 0 +peers" "" "192.168.123.2" + +start_server 3 -l 1010 + +cat tmp/log.3 diff --git a/test/simulation/clknetsim/examples/phc2sys.test b/test/simulation/clknetsim/examples/phc2sys.test new file mode 100755 index 0000000..b948819 --- /dev/null +++ b/test/simulation/clknetsim/examples/phc2sys.test @@ -0,0 +1,13 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +echo "node1_freq = (sum (* 1e-9 (normal)))" > tmp/conf +echo "node1_refclock = (* 1e-6 (normal))" >> tmp/conf + +start_client 1 phc2sys "" + +start_server 1 -v 2 -o log.offset -f log.freq -r 1000 -l 4000 + +cat tmp/stats diff --git a/test/simulation/clknetsim/examples/pmc.test b/test/simulation/clknetsim/examples/pmc.test new file mode 100755 index 0000000..5c6e44d --- /dev/null +++ b/test/simulation/clknetsim/examples/pmc.test @@ -0,0 +1,19 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config4 "1" "1 2 3" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" + +echo 'node3_start = 100' >> tmp/conf + +start_client 1 ptp4l "" "" "-i eth0" +start_client 2 ptp4l "" "" "-i eth0" +start_client 3 pmc " +GET TIME_STATUS_NP +GET TIME_PROPERTIES_DATA_SET +GET PORT_DATA_SET" + +start_server 3 -l 110 + +cat tmp/log.3 diff --git a/test/simulation/clknetsim/examples/ptp4l.test b/test/simulation/clknetsim/examples/ptp4l.test new file mode 100755 index 0000000..be58af7 --- /dev/null +++ b/test/simulation/clknetsim/examples/ptp4l.test @@ -0,0 +1,15 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config4 "1" "1 2 3 | 3 4" 0.01 "(sum (* 1e-9 (normal)))" "(* 1e-8 (exponential))" + +start_client 1 ptp4l "clockClass 6" "" "-i eth0" +start_client 2 ptp4l "time_stamping software" "" "-i eth0" +start_client 3 ptp4l "" "" "-i eth0 -i eth1" +start_client 4 ptp4l "" "" "-i eth1" + +start_server 4 -n 2 -o log.offset -p log.packets -r 100 -l 1000 + +cat tmp/stats diff --git a/test/simulation/clknetsim/examples/sntp.test b/test/simulation/clknetsim/examples/sntp.test new file mode 100755 index 0000000..28e8bfa --- /dev/null +++ b/test/simulation/clknetsim/examples/sntp.test @@ -0,0 +1,17 @@ +#!/bin/bash + +CLKNETSIM_PATH=.. +. ../clknetsim.bash + +generate_config1 3 10.0 "(sum (* 1e-9 (normal)))" "(+ 1e-3 (* 1e-3 (exponential)))" + +echo "node2_start = 100" >> tmp/conf +echo "node3_start = 100" >> tmp/conf + +start_client 1 ntpd "server 127.127.1.0" +start_client 2 sntp "-j 192.168.123.1" +start_client 3 sntp "-s 192.168.123.1" + +start_server 3 -v 2 -o log.offset -r 110 -l 200 + +cat tmp/stats diff --git a/test/simulation/clknetsim/generator.cc b/test/simulation/clknetsim/generator.cc new file mode 100644 index 0000000..8ca1bc0 --- /dev/null +++ b/test/simulation/clknetsim/generator.cc @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#include "generator.h" + +static void syntax_assert(bool condition) { + if (!condition) { + fprintf(stderr, "syntax error\n"); + exit(1); + } +} + +Generator::Generator(const vector *input) { + if (input) + this->input = *input; + constant = false; +} + +Generator::~Generator() { + while (!input.empty()) { + delete input.back(); + input.pop_back(); + } +} + +bool Generator::is_constant() const { + return constant; +} + +Generator_float::Generator_float(double f): Generator(NULL) { + this->f = f; + constant = true; +} + +double Generator_float::generate(const Generator_variables *variables) { + return f; +} + +Generator_variable::Generator_variable(string name): Generator(NULL) { + this->name = name; +} + +double Generator_variable::generate(const Generator_variables *variables) { + Generator_variables::const_iterator iter; + + syntax_assert(variables); + iter = variables->find(name); + syntax_assert(iter != variables->end()); + + return iter->second; +} + +Generator_random_uniform::Generator_random_uniform(const vector *input): + Generator(NULL) { + syntax_assert(!input || input->size() == 0); +} + +double Generator_random_uniform::generate(const Generator_variables *variables) { + double x; + + x = ((random() & 0x7fffffff) + 1) / 2147483649.0; + x = ((random() & 0x7fffffff) + x) / 2147483648.0; + + return x; +} + +Generator_random_normal::Generator_random_normal(const vector *input): + Generator(NULL), uniform(NULL) { + syntax_assert(!input || input->size() == 0); +} + +double Generator_random_normal::generate(const Generator_variables *variables) { + /* Marsaglia polar method */ + + double x, y, s; + + do { + x = 2.0 * uniform.generate(variables) - 1.0; + y = 2.0 * uniform.generate(variables) - 1.0; + s = x * x + y * y; + } while (s >= 1.0); + + x *= sqrt(-2.0 * log(s) / s); + + return x; +} + +Generator_random_exponential::Generator_random_exponential(const vector *input): + Generator(NULL), uniform(NULL) { + syntax_assert(!input || input->size() == 0); +} + +double Generator_random_exponential::generate(const Generator_variables *variables) { + return -log(uniform.generate(variables)); +} + +Generator_random_poisson::Generator_random_poisson(const vector *input): + Generator(NULL), uniform(NULL) { + double lambda; + + syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); + + lambda = (*input)[0]->generate(NULL); + syntax_assert(lambda >= 1 && lambda <= 20); + L = exp(-lambda); +} + +double Generator_random_poisson::generate(const Generator_variables *variables) { + double p; + int k; + + for (p = 1.0, k = 0; k < 100; k++) { + p *= uniform.generate(variables); + if (p <= L) + break; + } + + return k; +} + +Generator_file::Generator_file(const char *file): Generator(NULL) { + input = fopen(file, "r"); + if (!input) { + fprintf(stderr, "can't open %s\n", file); + exit(1); + } +} + +Generator_file::~Generator_file() { + fclose(input); +} + +double Generator_file::generate(const Generator_variables *variables) { + double x; + + while (1) { + if (fscanf(input, "%lf", &x) != 1) { + if (feof(input)) { + fseek(input, 0, SEEK_SET); + continue; + } + assert(0); + } + break; + } + return x; +} + +Generator_wave_pulse::Generator_wave_pulse(const vector *input): + Generator(NULL) { + syntax_assert(input && input->size() == 2 && + (*input)[0]->is_constant() && (*input)[1]->is_constant()); + high = (*input)[0]->generate(NULL); + low = (*input)[1]->generate(NULL); + counter = 0; +} + +double Generator_wave_pulse::generate(const Generator_variables *variables) { + counter++; + if (counter > high + low) + counter = 1; + if (counter <= high) + return 1.0; + return -1.0; +} + +Generator_wave_sine::Generator_wave_sine(const vector *input): + Generator(NULL) { + syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); + length = (*input)[0]->generate(NULL); + counter = 0; +} + +double Generator_wave_sine::generate(const Generator_variables *variables) { + return sin(counter++ / length * 2 * M_PI); +} + +Generator_wave_cosine::Generator_wave_cosine(const vector *input): + Generator(NULL) { + syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); + length = (*input)[0]->generate(NULL); + counter = 0; +} + +double Generator_wave_cosine::generate(const Generator_variables *variables) { + return cos(counter++ / length * 2 * M_PI); +} + +Generator_wave_triangle::Generator_wave_triangle(const vector *input): + Generator(NULL) { + syntax_assert(input && input->size() == 1 && (*input)[0]->is_constant()); + length = (*input)[0]->generate(NULL); + counter = 0; +} + +double Generator_wave_triangle::generate(const Generator_variables *variables) { + double phase; + phase = counter / length - floor(counter / length); + counter++; + return -4.0 * (fabs(phase - 0.5) - 0.25); + +} + +Generator_sum::Generator_sum(const vector *input): + Generator(input) { + sum = 0.0; +} + +double Generator_sum::generate(const Generator_variables *variables) { + unsigned int i; + + for (i = 0; i < input.size(); i++) + sum += input[i]->generate(variables); + return sum; +} + +Generator_multiply::Generator_multiply(const vector *input): + Generator(input) { +} + +double Generator_multiply::generate(const Generator_variables *variables) { + unsigned int i; + double x = 1.0; + + for (i = 0; i < input.size(); i++) + x *= input[i]->generate(variables); + return x; +} + +Generator_add::Generator_add(const vector *input): + Generator(input) { +} + +double Generator_add::generate(const Generator_variables *variables) { + unsigned int i; + double x = 0.0; + + for (i = 0; i < input.size(); i++) + x += input[i]->generate(variables); + return x; +} + +Generator_modulo::Generator_modulo(const vector *input): + Generator(input) { + syntax_assert(input && input->size() > 0); +} + +double Generator_modulo::generate(const Generator_variables *variables) { + unsigned int i; + double x = input[0]->generate(variables); + + for (i = 1; i < input.size(); i++) + x = fmod(x, input[i]->generate(variables)); + + return x; +} + +Generator_equal::Generator_equal(const vector *input): + Generator(input) { + syntax_assert(input && input->size() > 0); +} + +double Generator_equal::generate(const Generator_variables *variables) { + unsigned int i; + double x, min = 0.0, max = 0.0, epsilon = input[0]->generate(variables); + + for (i = 1; i < input.size(); i++) { + x = input[i]->generate(variables); + if (i == 1 || min > x) + min = x; + if (i == 1 || max < x) + max = x; + } + + return max - min <= epsilon ? 1.0 : 0.0; +} + +Generator_max::Generator_max(const vector *input): + Generator(input) { + syntax_assert(input && input->size() > 0); +} + +double Generator_max::generate(const Generator_variables *variables) { + unsigned int i; + double x, max = 0.0; + + for (i = 0; i < input.size(); i++) { + x = input[i]->generate(variables); + if (!i || max < x) + max = x; + } + + return max; +} + +Generator_min::Generator_min(const vector *input): + Generator(input) { + syntax_assert(input && input->size() > 0); +} + +double Generator_min::generate(const Generator_variables *variables) { + unsigned int i; + double x, min = 0.0; + + for (i = 0; i < input.size(); i++) { + x = input[i]->generate(variables); + if (!i || min > x) + min = x; + } + + return min; +} + +Generator_generator::Generator_generator() { +} + +Generator_generator::~Generator_generator() { +} + +Generator *Generator_generator::generate(char *code) const { + const char *ws = " \t\n\r", *wsp = " \t\n\r()"; + int len, paren; + Generator *ret; + vector generators; + char *arg, *name, *end, *string = NULL; + + //printf("code: |%s|\n", code); + len = strlen(code); + end = code + len; + + if (code[0] == '(') { + syntax_assert(len > 2 && code[len - 1] == ')'); + code[len - 1] = '\0'; + code++; + end = code + len - 2; + } + + code += strspn(code, ws); + + name = code; + + code += strcspn(code, wsp); + code[0] = '\0'; + code++; + + code += strspn(code, ws); + + while (code < end) { + arg = code; + + if (arg[0] == '(') { + code = ++arg; + for (paren = 1; code < end; code++) { + if (code[0] == '(') + paren++; + else if (code[0] == ')') + paren--; + if (paren == 0) + break; + } + + syntax_assert(paren == 0 && code[0] == ')'); + code[0] = '\0'; + code++; + + //printf("generator: %s\n", arg); + generators.push_back(generate(arg)); + syntax_assert(generators.back()); + } else if (arg[0] == '"') { + string = code = ++arg; + code += strcspn(code, "\""); + syntax_assert(code[0] == '"'); + code[0] = '\0'; + code++; + //printf("string: |%s|\n", string); + } else { + code += strcspn(code, wsp); + syntax_assert(code[0] != ')' && code[0] != '('); + code[0] = '\0'; + code++; + if (isalpha(arg[0])) { + generators.push_back(new Generator_variable(arg)); + //printf("variable: %s\n", arg); + } else { + generators.push_back(new Generator_float(atof(arg))); + //printf("float: %f\n", generators.back()->generate()); + } + } + + code += strspn(code, ws); + } + + if (strcmp(name, "*") == 0) + ret = new Generator_multiply(&generators); + else if (strcmp(name, "+") == 0) + ret = new Generator_add(&generators); + else if (strcmp(name, "%") == 0) + ret = new Generator_modulo(&generators); + else if (strcmp(name, "sum") == 0) + ret = new Generator_sum(&generators); + else if (strcmp(name, "uniform") == 0) + ret = new Generator_random_uniform(&generators); + else if (strcmp(name, "normal") == 0) + ret = new Generator_random_normal(&generators); + else if (strcmp(name, "exponential") == 0) + ret = new Generator_random_exponential(&generators); + else if (strcmp(name, "poisson") == 0) + ret = new Generator_random_poisson(&generators); + else if (strcmp(name, "file") == 0) + ret = new Generator_file(string); + else if (strcmp(name, "pulse") == 0) + ret = new Generator_wave_pulse(&generators); + else if (strcmp(name, "sine") == 0) + ret = new Generator_wave_sine(&generators); + else if (strcmp(name, "cosine") == 0) + ret = new Generator_wave_cosine(&generators); + else if (strcmp(name, "triangle") == 0) + ret = new Generator_wave_triangle(&generators); + else if (strcmp(name, "equal") == 0) + ret = new Generator_equal(&generators); + else if (strcmp(name, "max") == 0) + ret = new Generator_max(&generators); + else if (strcmp(name, "min") == 0) + ret = new Generator_min(&generators); + else { + ret = NULL; + syntax_assert(0); + } + + return ret; +} diff --git a/test/simulation/clknetsim/generator.h b/test/simulation/clknetsim/generator.h new file mode 100644 index 0000000..e8a6fe2 --- /dev/null +++ b/test/simulation/clknetsim/generator.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include "sysheaders.h" +#include +#include +#include + +using namespace std; + +typedef map Generator_variables; + +class Generator { + protected: + vector input; + bool constant; + + public: + Generator(const vector *input); + virtual ~Generator(); + virtual double generate(const Generator_variables *variables) = 0; + bool is_constant() const; +}; + +class Generator_float: public Generator { + double f; + + public: + Generator_float(double f); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_variable: public Generator { + string name; + + public: + Generator_variable(string name); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_random_uniform: public Generator { + public: + Generator_random_uniform(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_random_normal: public Generator { + Generator_random_uniform uniform; + + public: + Generator_random_normal(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_random_exponential: public Generator { + Generator_random_uniform uniform; + + public: + Generator_random_exponential(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_random_poisson: public Generator { + Generator_random_uniform uniform; + double L; + + public: + Generator_random_poisson(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_file: public Generator { + FILE *input; + + public: + Generator_file(const char *file); + virtual ~Generator_file(); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_wave_pulse: public Generator { + int high; + int low; + int counter; + + public: + Generator_wave_pulse(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_wave_sine: public Generator { + double length; + int counter; + + public: + Generator_wave_sine(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_wave_cosine: public Generator { + double length; + int counter; + + public: + Generator_wave_cosine(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_wave_triangle: public Generator { + double length; + int counter; + + public: + Generator_wave_triangle(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_sum: public Generator { + double sum; + public: + Generator_sum(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_multiply: public Generator { + public: + Generator_multiply(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_add: public Generator { + public: + Generator_add(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_modulo: public Generator { + public: + Generator_modulo(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_equal: public Generator { + public: + Generator_equal(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_max: public Generator { + public: + Generator_max(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_min: public Generator { + public: + Generator_min(const vector *input); + virtual double generate(const Generator_variables *variables); +}; + +class Generator_generator { + public: + Generator_generator(); + ~Generator_generator(); + Generator *generate(char *code) const; +}; + +#endif diff --git a/test/simulation/clknetsim/network.cc b/test/simulation/clknetsim/network.cc new file mode 100644 index 0000000..7b2fcbe --- /dev/null +++ b/test/simulation/clknetsim/network.cc @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#include "sysheaders.h" +#include "network.h" + +Packet_queue::Packet_queue() { +} + +Packet_queue::~Packet_queue() { + while (!queue.empty()) { + delete queue.back(); + queue.pop_back(); + } +} + +void Packet_queue::insert(struct Packet *packet) { + deque::iterator i; + + for (i = queue.begin(); i < queue.end(); i++) + if (packet->receive_time < (*i)->receive_time) + break; + queue.insert(i, packet); +} + +struct Packet *Packet_queue::dequeue() { + struct Packet *ret; + + assert(!queue.empty()); + ret = queue.front(); + queue.pop_front(); + + return ret; +} + +double Packet_queue::get_timeout(double time) const { + if (!queue.empty()) { + return queue[0]->receive_time - time; + } + return 1e20; +} + +Network::Network(const char *socket, unsigned int n, unsigned int subnets, unsigned int rate) { + time = 0.0; + this->subnets = subnets; + socket_name = socket; + update_rate = rate; + update_count = 0; + offset_log = NULL; + freq_log = NULL; + rawfreq_log = NULL; + packet_log = NULL; + + assert(n > 0); + + while (nodes.size() < n) + nodes.push_back(new Node(nodes.size(), this)); + + stats.resize(n); + link_delays.resize(n * n); +} + +Network::~Network() { + while (!nodes.empty()) { + delete nodes.back(); + nodes.pop_back(); + } + + while (!link_delays.empty()) { + delete link_delays.back(); + link_delays.pop_back(); + } + + unlink(socket_name); + + if (offset_log) + fclose(offset_log); + if (freq_log) + fclose(freq_log); + if (rawfreq_log) + fclose(rawfreq_log); + if (packet_log) + fclose(packet_log); +} + + +bool Network::prepare_clients() { + struct sockaddr_un s; + int sockfd, fd; + unsigned int i; + + s.sun_family = AF_UNIX; + snprintf(s.sun_path, sizeof (s.sun_path), "%s", socket_name); + + sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sockfd < 0) { + fprintf(stderr, "socket() failed\n"); + return false; + } + + unlink(socket_name); + if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) { + fprintf(stderr, "bind() failed\n"); + return false; + } + + if (listen(sockfd, nodes.size()) < 0) { + fprintf(stderr, "listen() failed\n"); + return false; + } + + for (i = 0; i < nodes.size(); i++) { + Request_packet req; + unsigned int node; + + fprintf(stderr, "\rWaiting for %u clients...", (unsigned int)nodes.size() - i); + fd = accept(sockfd, NULL, NULL); + if (fd < 0) { + fprintf(stderr, "accept() failed\n"); + return false; + } + + if (recv(fd, &req, sizeof (req), 0) != offsetof(Request_packet, data) + + sizeof (Request_register) || req.header.request != REQ_REGISTER) { + fprintf(stderr, "client didn't register correctly.\n"); + return false; + } + node = req.data._register.node; + assert(node < nodes.size() && nodes[node]->get_fd() < 0); + nodes[node]->set_fd(fd); + } + fprintf(stderr, "done\n"); + + close(sockfd); + + update(); + + return true; +} + +Node *Network::get_node(unsigned int node) { + assert(node < nodes.size()); + return nodes[node]; +} + +void Network::set_link_delay_generator(unsigned int from, unsigned int to, Generator *generator) { + unsigned int i; + + assert(from < nodes.size() && to < nodes.size()); + + i = from * nodes.size() + to; + if (link_delays[i]) + delete link_delays[i]; + link_delays[i] = generator; +} + +bool Network::run(double time_limit) { + int i, n = nodes.size(), waiting; + bool pending_update; + double min_timeout, timeout, next_update; + + while (time < time_limit) { + for (i = 0, waiting = 0; i < n; i++) + if (nodes[i]->waiting()) + waiting++; + else + stats[i].update_wakeup_stats(); + + while (waiting < n) { + for (i = 0; i < n; i++) { + if (nodes[i]->waiting()) + continue; + if (!nodes[i]->process_fd()) { + fprintf(stderr, "client %d failed.\n", i + 1); + return false; + } + if (nodes[i]->waiting()) + waiting++; + } + } + + do { + min_timeout = nodes[0]->get_timeout(); + for (i = 1; i < n; i++) { + timeout = nodes[i]->get_timeout(); + if (min_timeout > timeout) + min_timeout = timeout; + } + + timeout = packet_queue.get_timeout(time); + if (timeout <= min_timeout) + min_timeout = timeout; + + next_update = floor(time) + (double)(update_count + 1) / update_rate; + timeout = next_update - time; + if (timeout <= min_timeout) { + min_timeout = timeout; + pending_update = true; + } else + pending_update = false; + + //min_timeout += 1e-12; + assert(min_timeout >= 0.0); + + if (pending_update) + time = next_update; + else + time += min_timeout; + + for (i = 0; i < n; i++) + nodes[i]->get_clock()->advance(min_timeout); + + if (pending_update) + update(); + } while (pending_update && time < time_limit); + + for (i = 0; i < n; i++) + nodes[i]->resume(); + + while (packet_queue.get_timeout(time) <= 0) { + assert(packet_queue.get_timeout(time) > -1e-10); + struct Packet *packet = packet_queue.dequeue(); + stats[packet->to].update_packet_stats(true, time, packet->delay); + nodes[packet->to]->receive(packet); + } + } + + return true; +} + +void Network::update() { + int i, n = nodes.size(); + + update_count++; + update_count %= update_rate; + + for (i = 0; i < n; i++) { + nodes[i]->get_clock()->update(update_count == 0); + nodes[i]->get_refclock()->update(time, nodes[i]->get_clock()); + } + + update_clock_stats(); +} + +void Network::update_clock_stats() { + int i, n = nodes.size(); + + if (offset_log) { + for (i = 0; i < n; i++) + fprintf(offset_log, "%.9f%c", nodes[i]->get_clock()->get_real_time() - time, i + 1 < n ? '\t' : '\n'); + } + if (freq_log) { + for (i = 0; i < n; i++) + fprintf(freq_log, "%e%c", nodes[i]->get_clock()->get_total_freq() - 1.0, i + 1 < n ? '\t' : '\n'); + } + if (rawfreq_log) { + for (i = 0; i < n; i++) + fprintf(rawfreq_log, "%e%c", nodes[i]->get_clock()->get_raw_freq() - 1.0, i + 1 < n ? '\t' : '\n'); + } + + for (i = 0; i < n; i++) + stats[i].update_clock_stats(nodes[i]->get_clock()->get_real_time() - time, + nodes[i]->get_clock()->get_total_freq() - 1.0, + nodes[i]->get_clock()->get_raw_freq() - 1.0); +} + +void Network::open_offset_log(const char *log) { + offset_log = fopen(log, "w"); +} + +void Network::open_freq_log(const char *log) { + freq_log = fopen(log, "w"); +} + +void Network::open_rawfreq_log(const char *log) { + rawfreq_log = fopen(log, "w"); +} + +void Network::open_packet_log(const char *log) { + packet_log = fopen(log, "w"); +} + +void Network::print_stats(int verbosity) const { + int i, n = nodes.size(); + + if (verbosity <= 0) + return; + + for (i = 0; i < n; i++) { + if (verbosity > 1) + printf("\n---------------------- Node %d ----------------------\n\n", i + 1); + stats[i].print(verbosity); + } + if (verbosity == 1) + printf("\n"); +} + +void Network::reset_stats() { + int i, n = nodes.size(); + + for (i = 0; i < n; i++) + stats[i].reset(); +} + +void Network::reset_clock_stats() { + int i, n = nodes.size(); + + for (i = 0; i < n; i++) + stats[i].reset_clock_stats(); +} + +void Network::send(struct Packet *packet) { + double delay = -1.0; + unsigned int i; + + /* broadcast */ + if (packet->to == (unsigned int)-1) { + for (i = 0; i < nodes.size(); i++) { + struct Packet *p; + + if (i == packet->from) + continue; + + p = new struct Packet; + memcpy(p, packet, sizeof (struct Packet)); + p->to = i; + + send(p); + } + + delete packet; + return; + } + + assert(packet->to < nodes.size() && packet->from < nodes.size() && + packet->subnet < subnets); + + i = packet->from * nodes.size() + packet->to; + + if (link_delays[i]) { + link_delay_variables["time"] = time; + link_delay_variables["from"] = packet->from + 1; + link_delay_variables["to"] = packet->to + 1; + link_delay_variables["subnet"] = packet->subnet + 1; + link_delay_variables["port"] = packet->dst_port; + link_delay_variables["length"] = packet->len; + + delay = link_delays[i]->generate(&link_delay_variables); + } + + stats[packet->from].update_packet_stats(false, time, delay); + + if (packet_log) + fprintf(packet_log, "%e\t%d\t%d\t%e\t%d\t%d\t%d\n", time, + packet->from + 1, packet->to + 1, delay, + packet->src_port, packet->dst_port, + packet->subnet + 1); + + if (delay > 0.0) { + packet->receive_time = time + delay; + packet->delay = delay; + packet_queue.insert(packet); +#ifdef DEBUG + printf("sending packet from %d to %d:%d:%d at %f delay %f \n", + packet->from, packet->subnet, packet->to, + packet->dst_port, time, delay); +#endif + } else { +#ifdef DEBUG + printf("dropping packet from %d to %d:%d:%d at %f\n", + packet->from, packet->subnet, packet->to, + packet->dst_port, time); +#endif + delete packet; + } +} + +double Network::get_time() const { + return time; +} + +unsigned int Network::get_subnets() const { + return subnets; +} diff --git a/test/simulation/clknetsim/network.h b/test/simulation/clknetsim/network.h new file mode 100644 index 0000000..4270365 --- /dev/null +++ b/test/simulation/clknetsim/network.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#ifndef NETWORK_H +#define NETWORK_H + +#include +#include + +using namespace std; + +#include "node.h" +#include "stats.h" + +struct Packet { + double receive_time; + double delay; + int broadcast; + unsigned int subnet; + unsigned int from; + unsigned int to; + unsigned int src_port; + unsigned int dst_port; + unsigned int len; + char data[MAX_PACKET_SIZE]; +}; + +class Packet_queue { + deque queue; + public: + Packet_queue(); + ~Packet_queue(); + void insert(struct Packet *packet); + struct Packet *dequeue(); + double get_timeout(double time) const; +}; + +class Network { + double time; + unsigned int subnets; + unsigned int update_rate; + unsigned int update_count; + + const char *socket_name; + vector nodes; + vector link_delays; + vector stats; + + Generator_variables link_delay_variables; + + Packet_queue packet_queue; + + FILE *offset_log; + FILE *freq_log; + FILE *rawfreq_log; + FILE *packet_log; + + void update(); + void update_clock_stats(); + + public: + Network(const char *socket, unsigned int n, unsigned int s, unsigned int rate); + ~Network(); + bool prepare_clients(); + Node *get_node(unsigned int node); + void set_link_delay_generator(unsigned int from, unsigned int to, Generator *generator); + bool run(double time_limit); + void open_offset_log(const char *log); + void open_freq_log(const char *log); + void open_rawfreq_log(const char *log); + void open_packet_log(const char *log); + void print_stats(int verbosity) const; + void reset_stats(); + void reset_clock_stats(); + + void send(struct Packet *packet); + double get_time() const; + unsigned int get_subnets() const; +}; + +#endif diff --git a/test/simulation/clknetsim/node.cc b/test/simulation/clknetsim/node.cc new file mode 100644 index 0000000..ae8d53e --- /dev/null +++ b/test/simulation/clknetsim/node.cc @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#include "node.h" +#include "network.h" +#include "protocol.h" +#include "sysheaders.h" + +Node::Node(int index, Network *network) { + this->network = network; + this->index = index; + fd = -1; + pending_request = REQ_REGISTER; + start_time = 0.0; + terminate = false; +} + +Node::~Node() { + while (!incoming_packets.empty()) { + delete incoming_packets.back(); + incoming_packets.pop_back(); + } + + terminate = true; + + do { + if (waiting()) + resume(); + } while (process_fd()); + + if (fd >= 0) + close(fd); +} + +void Node::set_fd(int fd) { + this->fd = fd; +} + +int Node::get_fd() const { + return fd; +} + +void Node::set_start_time(double time) { + start_time = time; +} + +bool Node::process_fd() { + Request_packet request; + int received, reqlen; + + received = recv(fd, &request, sizeof (request), 0); + if (received < (int)sizeof (request.header)) + return false; + + reqlen = received - (int)offsetof(Request_packet, data); + + assert(pending_request == 0); + pending_request = request.header.request; + +#ifdef DEBUG + printf("received request %ld in node %d at %f\n", + pending_request, index, clock.get_real_time()); +#endif + + switch (pending_request) { + case REQ_GETTIME: + assert(reqlen == 0); + process_gettime(); + break; + case REQ_SETTIME: + assert(reqlen == sizeof (Request_settime)); + process_settime(&request.data.settime); + break; + case REQ_ADJTIMEX: + assert(reqlen == sizeof (Request_adjtimex)); + process_adjtimex(&request.data.adjtimex); + break; + case REQ_ADJTIME: + assert(reqlen == sizeof (Request_adjtime)); + process_adjtime(&request.data.adjtime); + break; + case REQ_SELECT: + assert(reqlen == sizeof (Request_select)); + process_select(&request.data.select); + break; + case REQ_SEND: + /* request with variable length */ + assert(reqlen >= (int)offsetof(Request_send, data) && + reqlen <= (int)sizeof (Request_send)); + assert(request.data.send.len <= sizeof (request.data.send.data)); + assert((int)(request.data.send.len + offsetof(Request_send, data)) <= reqlen); + process_send(&request.data.send); + break; + case REQ_RECV: + assert(reqlen == 0); + process_recv(); + break; + case REQ_GETREFSAMPLE: + assert(reqlen == 0); + process_getrefsample(); + break; + case REQ_GETREFOFFSETS: + assert(reqlen == 0); + process_getrefoffsets(); + break; + case REQ_DEREGISTER: + assert(reqlen == 0); + break; + default: + assert(0); + } + + return true; +} + +void Node::reply(void *data, int len, int request) { + int sent; + + assert(request == pending_request); + pending_request = 0; + + if (data) { + sent = send(fd, data, len, 0); + assert(sent == len); + } +} + + +void Node::process_gettime() { + Reply_gettime r; + + r.real_time = clock.get_real_time(); + r.monotonic_time = clock.get_monotonic_time(); + r.network_time = network->get_time(); + reply(&r, sizeof (r), REQ_GETTIME); +} + +void Node::process_settime(Request_settime *req) { + clock.set_time(req->time); + reply(NULL, 0, REQ_SETTIME); +} + +void Node::process_adjtimex(Request_adjtimex *req) { + Reply_adjtimex rep; + struct timex *buf = &req->timex; + + rep.ret = clock.adjtimex(buf); + rep.timex = *buf; + rep._pad = 0; + reply(&rep, sizeof (rep), REQ_ADJTIMEX); +} + +void Node::process_adjtime(Request_adjtime *req) { + Reply_adjtime rep; + + clock.adjtime(&req->tv, &rep.tv); + reply(&rep, sizeof (rep), REQ_ADJTIME); +} + +void Node::try_select() { + Reply_select rep = {-1, 0, 0}; + + if (terminate) { + rep.ret = REPLY_SELECT_TERMINATE; +#ifdef DEBUG + printf("select returned on termination in %d at %f\n", + index, clock.get_real_time()); +#endif + } else if (select_timeout - clock.get_monotonic_time() <= 0.0) { + assert(select_timeout - clock.get_monotonic_time() > -1e-10); + rep.ret = REPLY_SELECT_TIMEOUT; +#ifdef DEBUG + printf("select returned on timeout in %d at %f\n", index, clock.get_real_time()); +#endif + } else if (select_read && incoming_packets.size() > 0) { + rep.ret = incoming_packets.back()->broadcast ? + REPLY_SELECT_BROADCAST : + REPLY_SELECT_NORMAL; + rep.subnet = incoming_packets.back()->subnet; + rep.dst_port = incoming_packets.back()->dst_port; +#ifdef DEBUG + printf("select returned for packet in %d at %f\n", index, clock.get_real_time()); +#endif + } + + if (rep.ret >= 0) { + rep.time.real_time = clock.get_real_time(); + rep.time.monotonic_time = clock.get_monotonic_time(); + rep.time.network_time = network->get_time(); + reply(&rep, sizeof (rep), REQ_SELECT); + } +} + +void Node::process_select(Request_select *req) { + if (req->timeout < 0.0) + req->timeout = 0.0; + select_timeout = clock.get_monotonic_time() + req->timeout; + select_read = req->read; +#ifdef DEBUG + printf("select called with timeout %f read %d in %d at %f\n", + req->timeout, req->read, index, clock.get_real_time()); +#endif + try_select(); +} + +void Node::process_send(Request_send *req) { + struct Packet *packet; + + if (!terminate) { + packet = new struct Packet; + packet->broadcast = req->to == (unsigned int)-1; + packet->subnet = req->subnet; + packet->from = index; + packet->to = req->to; + packet->src_port = req->src_port; + packet->dst_port = req->dst_port; + packet->len = req->len; + memcpy(packet->data, req->data, req->len); + network->send(packet); + } + + reply(NULL, 0, REQ_SEND); +} + +void Node::process_recv() { + Reply_recv rep; + struct Packet *packet; + + if (incoming_packets.empty()) { + rep.subnet = 0; + rep.from = -1; + rep.src_port = 0; + rep.dst_port = 0; + rep.len = 0; + reply(&rep, offsetof (Reply_recv, data), REQ_RECV); + + return; + } + + packet = incoming_packets.back(); + + rep.subnet = packet->subnet; + rep.from = packet->from; + rep.src_port = packet->src_port; + rep.dst_port = packet->dst_port; + rep.len = packet->len; + + assert(packet->len <= sizeof (rep.data)); + memcpy(rep.data, packet->data, packet->len); + + delete packet; + + reply(&rep, offsetof (Reply_recv, data) + rep.len, REQ_RECV); + + incoming_packets.pop_back(); +#ifdef DEBUG + printf("received packet in %d at %f\n", index, clock.get_real_time()); +#endif +} + +void Node::receive(struct Packet *packet) { + if (pending_request == REQ_REGISTER || pending_request == REQ_DEREGISTER) { + delete packet; + return; + } + + incoming_packets.insert(incoming_packets.begin(), packet); + + if (pending_request == REQ_SELECT) + try_select(); +} + +void Node::process_getrefsample() { + Reply_getrefsample r; + + refclock.set_generation(true); + r.valid = refclock.get_sample(&r.time, &r.offset); + r._pad = 0; + reply(&r, sizeof (r), REQ_GETREFSAMPLE); +} + +void Node::process_getrefoffsets() { + Reply_getrefoffsets r; + + refclock.get_offsets(r.offsets, REPLY_GETREFOFFSETS_SIZE); + reply(&r, sizeof (r), REQ_GETREFOFFSETS); +} + +void Node::resume() { + switch (pending_request) { + case REQ_SELECT: + try_select(); + break; + case REQ_REGISTER: + if (start_time - network->get_time() <= 0.0 || terminate) { + Reply_register rep; + rep.subnets = network->get_subnets(); + reply(&rep, sizeof (rep), REQ_REGISTER); +#ifdef DEBUG + printf("starting %d at %f\n", index, network->get_time()); +#endif + } + break; + case REQ_DEREGISTER: + break; + default: + assert(0); + + } +} + +bool Node::waiting() const { + return pending_request == REQ_SELECT || + pending_request == REQ_REGISTER || + pending_request == REQ_DEREGISTER; +} + +bool Node::finished() const { + return pending_request == REQ_DEREGISTER; +} + +double Node::get_timeout() const { + switch (pending_request) { + case REQ_SELECT: + return clock.get_true_interval(select_timeout - clock.get_monotonic_time()); + case REQ_REGISTER: + return start_time - network->get_time(); + case REQ_DEREGISTER: + return 10.0; + default: + assert(0); + } +} + +Clock *Node::get_clock() { + return &clock; +} + +Refclock *Node::get_refclock() { + return &refclock; +} diff --git a/test/simulation/clknetsim/node.h b/test/simulation/clknetsim/node.h new file mode 100644 index 0000000..11b2e12 --- /dev/null +++ b/test/simulation/clknetsim/node.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#ifndef NODE_H +#define NODE_H + +#include "protocol.h" +#include "clock.h" + +#include + +using namespace std; + +class Network; + +class Node { + Clock clock; + Refclock refclock; + Network *network; + int index; + int fd; + int pending_request; + double start_time; + double select_timeout; + bool select_read; + bool terminate; + + vector incoming_packets; + + public: + Node(int index, Network *network); + ~Node(); + void set_fd(int fd); + int get_fd() const; + void set_start_time(double time); + bool process_fd(); + void reply(void *data, int len, int request); + void process_gettime(); + void process_settime(Request_settime *req); + void process_adjtimex(Request_adjtimex *req); + void process_adjtime(Request_adjtime *req); + void try_select(); + void process_select(Request_select *req); + void process_send(Request_send *req); + void process_recv(); + void process_getrefsample(); + void process_getrefoffsets(); + + void receive(struct Packet *packet); + void resume(); + bool waiting() const; + bool finished() const; + + double get_timeout() const; + Clock *get_clock(); + Refclock *get_refclock(); +}; + +#endif diff --git a/test/simulation/clknetsim/protocol.h b/test/simulation/clknetsim/protocol.h new file mode 100644 index 0000000..590cae6 --- /dev/null +++ b/test/simulation/clknetsim/protocol.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#include "sysheaders.h" + +#define REQ_REGISTER 1 +#define REQ_GETTIME 2 +#define REQ_SETTIME 3 +#define REQ_ADJTIMEX 4 +#define REQ_ADJTIME 5 +#define REQ_SELECT 6 +#define REQ_SEND 7 +#define REQ_RECV 8 +#define REQ_GETREFSAMPLE 9 +#define REQ_GETREFOFFSETS 10 +#define REQ_DEREGISTER 11 + +struct Request_header { + int request; + int _pad; +}; + +struct Request_register { + unsigned int node; +}; + +struct Reply_register { + unsigned int subnets; +}; + +struct Reply_gettime { + double real_time; + double monotonic_time; + double network_time; +}; + +struct Request_settime { + double time; +}; + +struct Request_adjtimex { + struct timex timex; +}; + +struct Reply_adjtimex { + int ret; + int _pad; + struct timex timex; +}; + +struct Request_adjtime { + struct timeval tv; +}; + +struct Reply_adjtime { + struct timeval tv; +}; + +struct Request_select { + double timeout; + int read; + int _pad; +}; + +#define REPLY_SELECT_TIMEOUT 0 +#define REPLY_SELECT_NORMAL 1 +#define REPLY_SELECT_BROADCAST 2 +#define REPLY_SELECT_TERMINATE 3 + +struct Reply_select { + int ret; + unsigned int subnet; /* for NORMAL or BROADCAST */ + unsigned int dst_port; /* for NORMAL or BROADCAST */ + struct Reply_gettime time; +}; + +#define MAX_PACKET_SIZE 4000 + +struct Request_send { + unsigned int subnet; + unsigned int to; + unsigned int src_port; + unsigned int dst_port; + unsigned int len; + char data[MAX_PACKET_SIZE]; +}; + +struct Reply_recv { + unsigned int subnet; + unsigned int from; + unsigned int src_port; + unsigned int dst_port; + unsigned int len; + char data[MAX_PACKET_SIZE]; +}; + +struct Reply_getrefsample { + double time; + double offset; + int valid; + int _pad; +}; + +#define REPLY_GETREFOFFSETS_SIZE 1024 + +struct Reply_getrefoffsets { + double offsets[REPLY_GETREFOFFSETS_SIZE]; +}; + +union Request_data { + struct Request_register _register; + struct Request_settime settime; + struct Request_adjtimex adjtimex; + struct Request_adjtime adjtime; + struct Request_select select; + struct Request_send send; +}; + +union Reply_data { + struct Reply_register _register; + struct Reply_gettime gettime; + struct Reply_adjtimex adjtimex; + struct Reply_adjtime adjtime; + struct Reply_select select; + struct Reply_recv recv; + struct Reply_getrefsample getrefsample; + struct Reply_getrefoffsets getrefoffsets; +}; + +struct Request_packet { + struct Request_header header; + union Request_data data; +}; + +struct Reply_packet { + union Reply_data data; +}; + +#endif diff --git a/test/simulation/clknetsim/server.cc b/test/simulation/clknetsim/server.cc new file mode 100644 index 0000000..a7aaa8a --- /dev/null +++ b/test/simulation/clknetsim/server.cc @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#include "sysheaders.h" +#include "network.h" + +bool load_config(const char *file, Network *network, unsigned int nodes) { + Generator_generator generator; + FILE *f; + const char *ws = " \t\n\r"; + char line[1000], *var, *arg, *end; + unsigned int node, node2; + + f = fopen(file, "r"); + if (!f) + return false; + + while (fgets(line, sizeof (line), f)) { + end = line + strlen(line); + var = line + strspn(line, ws); + arg = line + strcspn(line, "="); + *arg++ = '\0'; + + if (var >= end || *var == '#') + continue; + + if (arg >= end) + return false; + + while (end > line && (end[-1] == '\r' || end[-1] == '\n' || end[-1] == '\t' || end[-1] == ' ')) + *--end = '\0'; + + arg += strspn(arg, ws); + + if (strncmp(var, "node", 4)) + return false; + + var += 4; + node = atoi(var) - 1; + if (node >= nodes) + continue; + + var += strcspn(var, "_") + 1; + if (var >= end) + return false; + + if (strncmp(var, "offset", 6) == 0) + network->get_node(node)->get_clock()->set_time(atof(arg)); + else if (strncmp(var, "start", 5) == 0) + network->get_node(node)->set_start_time(atof(arg)); + else if (strncmp(var, "freq", 4) == 0) { + if (arg[0] == '(') + network->get_node(node)->get_clock()->set_freq_generator(generator.generate(arg)); + else + network->get_node(node)->get_clock()->set_freq(atof(arg)); + } else if (strncmp(var, "step", 4) == 0) + network->get_node(node)->get_clock()->set_step_generator(generator.generate(arg)); + else if (strncmp(var, "shift_pll", 9) == 0) + network->get_node(node)->get_clock()->set_ntp_shift_pll(atoi(arg)); + else if (strncmp(var, "fll_mode2", 9) == 0) + network->get_node(node)->get_clock()->set_ntp_flag(atoi(arg), CLOCK_NTP_FLL_MODE2); + else if (strncmp(var, "pll_clamp", 9) == 0) + network->get_node(node)->get_clock()->set_ntp_flag(atoi(arg), CLOCK_NTP_PLL_CLAMP); + else if (strncmp(var, "delay", 5) == 0) { + var += 5; + node2 = atoi(var) - 1; + if (node2 >= nodes) + continue; + network->set_link_delay_generator(node, node2, generator.generate(arg)); + } else if (strncmp(var, "refclock", 8) == 0) + network->get_node(node)->get_refclock()->set_offset_generator(generator.generate(arg)); + else + return false; + } + + fclose(f); + + return true; +} + +void run_generator(char *expr, int num) { + Generator_generator gen_generator; + Generator *generator; + + generator = gen_generator.generate(expr); + while (num--) + printf("%.9e\n", generator->generate(NULL)); + delete generator; +} + +int main(int argc, char **argv) { + int nodes, subnets = 1, help = 0, verbosity = 2, generate_only = 0, rate = 1; + double limit = 10000.0, reset = 0.0; + const char *offset_log = NULL, *freq_log = NULL, *rawfreq_log = NULL, + *packet_log = NULL, *config, *socket = "clknetsim.sock", *env; + struct timeval tv; + + int r, opt; + Network *network; + + while ((opt = getopt(argc, argv, "l:r:R:n:o:f:Gg:p:s:v:h")) != -1) { + switch (opt) { + case 'l': + limit = atof(optarg); + break; + case 'r': + reset = atof(optarg); + break; + case 'R': + rate = atoi(optarg); + break; + case 'n': + subnets = atoi(optarg); + break; + case 'o': + offset_log = optarg; + break; + case 'f': + freq_log = optarg; + break; + case 'g': + rawfreq_log = optarg; + break; + case 'p': + packet_log = optarg; + break; + case 's': + socket = optarg; + break; + case 'G': + generate_only = 1; + break; + case 'v': + verbosity = atoi(optarg); + break; + case 'h': + default: + help = 1; + } + } + + if (optind + 2 != argc || help) { + printf("usage: clknetsim [options] config nodes\n"); + printf(" or: clknetsim -G expr num\n"); + printf(" -l secs set time limit to secs (default 10000)\n"); + printf(" -r secs reset clock stats after secs (default 0)\n"); + printf(" -R rate set freq/log/stats update rate (default 1 per second)\n"); + printf(" -n subnets set number of subnetworks (default 1)\n"); + printf(" -o file log time offsets to file\n"); + printf(" -f file log frequency offsets to file\n"); + printf(" -g file log raw (w/o slew) frequency offsets to file\n"); + printf(" -p file log packet delays to file\n"); + printf(" -s socket set server socket name (default clknetsim.sock)\n"); + printf(" -v level set verbosity level (default 2)\n"); + printf(" -G print num numbers generated by expr\n"); + printf(" -h print usage\n"); + return 1; + } + + config = argv[optind]; + nodes = atoi(argv[optind + 1]); + + env = getenv("CLKNETSIM_RANDOM_SEED"); + if (env) { + srandom(atoi(env)); + } else { + gettimeofday(&tv, NULL); + srandom(tv.tv_sec ^ tv.tv_usec); + } + + if (generate_only) { + run_generator(argv[optind], nodes); + return 0; + } + + network = new Network(socket, nodes, subnets, rate); + + if (offset_log) + network->open_offset_log(offset_log); + if (freq_log) + network->open_freq_log(freq_log); + if (rawfreq_log) + network->open_rawfreq_log(rawfreq_log); + if (packet_log) + network->open_packet_log(packet_log); + + if (!load_config(config, network, nodes)) { + fprintf(stderr, "Couldn't parse config %s\n", config); + return 1; + } + + if (!network->prepare_clients()) + return 1; + + fprintf(stderr, "Running simulation..."); + + if (reset && reset < limit) { + r = network->run(reset); + network->reset_clock_stats(); + } else + r = true; + + if (r) + r = network->run(limit); + + if (r) { + fprintf(stderr, "done\n\n"); + network->print_stats(verbosity); + } else + fprintf(stderr, "failed\n"); + + delete network; + + return !r; +} diff --git a/test/simulation/clknetsim/stats.cc b/test/simulation/clknetsim/stats.cc new file mode 100644 index 0000000..33d28b5 --- /dev/null +++ b/test/simulation/clknetsim/stats.cc @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#include "stats.h" + +#include "sysheaders.h" + +Stats::Stats() { + reset(); +} + +Stats::~Stats() { +} + +void Stats::reset() { + reset_clock_stats(); + + packets_in_sum2 = 0.0; + packets_out_sum2 = 0.0; + packets_in = 0; + packets_out = 0; + packets_in_time_last = 0.0; + packets_out_time_last = 0.0; + packets_in_int_sum = 0.0; + packets_out_int_sum = 0.0; + packets_in_int_min = 0.0; + packets_out_int_min = 0.0; + + wakeups_int_sum = 0; + wakeups = 0; +} + +void Stats::reset_clock_stats() { + offset_sum2 = 0.0; + offset_abs_sum = 0.0; + offset_sum = 0.0; + offset_abs_max = 0.0; + freq_sum2 = 0.0; + freq_abs_sum = 0.0; + freq_sum = 0.0; + freq_abs_max = 0.0; + rawfreq_sum2 = 0.0; + rawfreq_abs_sum = 0.0; + rawfreq_sum = 0.0; + rawfreq_abs_max = 0.0; + samples = 0; +} + +void Stats::update_clock_stats(double offset, double freq, double rawfreq) { + offset_sum2 += offset * offset; + offset_abs_sum += fabs(offset); + offset_sum += offset; + if (offset_abs_max < fabs(offset)) + offset_abs_max = fabs(offset); + + freq_sum2 += freq * freq; + freq_abs_sum += fabs(freq); + freq_sum += freq; + if (freq_abs_max < fabs(freq)) + freq_abs_max = fabs(freq); + + rawfreq_sum2 += rawfreq * rawfreq; + rawfreq_abs_sum += fabs(rawfreq); + rawfreq_sum += rawfreq; + if (rawfreq_abs_max < fabs(rawfreq)) + rawfreq_abs_max = fabs(rawfreq); + + samples++; + wakeups_int_sum++; +} + +void Stats::update_packet_stats(bool incoming, double time, double delay) { + if (delay < 0.0) + delay = 0.0; + if (incoming) { + packets_in++; + packets_in_sum2 += delay * delay; + if (packets_in >= 2) { + packets_in_int_sum += time - packets_in_time_last; + if (packets_in == 2 || packets_in_int_min > time - packets_in_time_last) + packets_in_int_min = time - packets_in_time_last; + } + packets_in_time_last = time; + } else { + packets_out++; + packets_out_sum2 += delay * delay; + if (packets_out >= 2) { + packets_out_int_sum += time - packets_out_time_last; + if (packets_out == 2 || packets_out_int_min > time - packets_out_time_last) + packets_out_int_min = time - packets_out_time_last; + } + packets_out_time_last = time; + } +} + +void Stats::update_wakeup_stats() { + wakeups++; +} + +void Stats::print(int verbosity) const { + if (verbosity <= 0) + return; + if (verbosity <= 1) { + printf("%e ", sqrt(offset_sum2 / samples)); + return; + } + + printf("RMS offset: \t%e\n", sqrt(offset_sum2 / samples)); + printf("Maximum absolute offset: \t%e\n", offset_abs_max); + printf("Mean absolute offset: \t%e\n", offset_abs_sum / samples); + printf("Mean offset: \t%e\n", offset_sum / samples); + printf("RMS frequency: \t%e\n", sqrt(freq_sum2 / samples)); + printf("Maximum absolute frequency: \t%e\n", freq_abs_max); + printf("Mean absolute frequency: \t%e\n", freq_abs_sum / samples); + printf("Mean frequency: \t%e\n", freq_sum / samples); + printf("RMS raw frequency: \t%e\n", sqrt(rawfreq_sum2 / samples)); + printf("Maximum absolute raw frequency: \t%e\n", rawfreq_abs_max); + printf("Mean absolute raw frequency: \t%e\n", rawfreq_abs_sum / samples); + printf("Mean raw frequency: \t%e\n", rawfreq_sum / samples); + if (packets_in) { + printf("RMS incoming packet delay: \t%e\n", (double)sqrt(packets_in_sum2 / packets_in)); + } else { + printf("RMS incoming packet delay: \tinf\n"); + } + if (packets_in >= 2) { + printf("Mean incoming packet interval: \t%e\n", packets_in_int_sum / (packets_in - 1)); + printf("Minimum incoming packet interval: \t%e\n", packets_in_int_min); + } else { + printf("Mean incoming packet interval: \tinf\n"); + printf("Minimum incoming packet interval: \tinf\n"); + } + if (packets_out) { + printf("RMS outgoing packet delay: \t%e\n", (double)sqrt(packets_out_sum2 / packets_out)); + } else { + printf("RMS outgoing packet delay: \tinf\n"); + } + if (packets_out >= 2) { + printf("Mean outgoing packet interval: \t%e\n", packets_out_int_sum / (packets_out - 1)); + printf("Minimum outgoing packet interval: \t%e\n", packets_out_int_min); + } else { + printf("Mean outgoing packet interval: \tinf\n"); + printf("Minimum outgoing packet interval: \tinf\n"); + } + if (wakeups) + printf("Mean wakeup interval: \t%e\n", (double)wakeups_int_sum / wakeups); + else + printf("Mean wakeup interval: \tinf\n"); +} diff --git a/test/simulation/clknetsim/stats.h b/test/simulation/clknetsim/stats.h new file mode 100644 index 0000000..667cfa6 --- /dev/null +++ b/test/simulation/clknetsim/stats.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Miroslav Lichvar + * + * 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, see . + */ + +#ifndef STATS_H +#define STATS_H + +#include "clock.h" + +class Stats { + double offset_sum2; + double offset_abs_sum; + double offset_sum; + double offset_abs_max; + double freq_sum2; + double freq_abs_sum; + double freq_sum; + double freq_abs_max; + double rawfreq_sum2; + double rawfreq_abs_sum; + double rawfreq_sum; + double rawfreq_abs_max; + unsigned long samples; + + double packets_in_sum2; + double packets_out_sum2; + unsigned long packets_in; + unsigned long packets_out; + double packets_in_time_last; + double packets_out_time_last; + double packets_in_int_sum; + double packets_out_int_sum; + double packets_in_int_min; + double packets_out_int_min; + + unsigned long wakeups_int_sum; + unsigned long wakeups; + + public: + Stats(); + ~Stats(); + void reset(); + void reset_clock_stats(); + void update_clock_stats(double offset, double freq, double rawfreq); + void update_packet_stats(bool incoming, double time, double delay); + void update_wakeup_stats(); + void print(int verbosity) const; +}; + +#endif diff --git a/test/simulation/clknetsim/sysheaders.h b/test/simulation/clknetsim/sysheaders.h new file mode 100644 index 0000000..f8b8132 --- /dev/null +++ b/test/simulation/clknetsim/sysheaders.h @@ -0,0 +1,38 @@ +#ifndef SYSTEM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#ifndef ADJ_SETOFFSET +#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ +#endif +#ifndef ADJ_MICRO +#define ADJ_MICRO 0x1000 /* select microsecond resolution */ +#endif +#ifndef ADJ_NANO +#define ADJ_NANO 0x2000 /* select nanosecond resolution */ +#endif +#ifndef ADJ_OFFSET_SS_READ +#define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ +#endif +#ifndef STA_NANO +#define STA_NANO 0x2000 /* resolution (0 = us, 1 = ns) (ro) */ +#endif +#ifndef STA_MODE +#define STA_MODE 0x4000 /* mode (0 = PLL, 1 = FLL) (ro) */ +#endif +#endif + +#endif diff --git a/test/simulation/clknetsim/visclocks.py b/test/simulation/clknetsim/visclocks.py new file mode 100755 index 0000000..637bef9 --- /dev/null +++ b/test/simulation/clknetsim/visclocks.py @@ -0,0 +1,212 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Miroslav Lichvar +# +# 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, see . + +import sys, pygame +import collections +import math + +freq_file = open(sys.argv[1], 'r') +offset_file = open(sys.argv[2], 'r') +delay_file = open(sys.argv[3], 'r') + +(maxx, maxy) = (640, 480) + +pygame.init() +window = pygame.display.set_mode((maxx, maxy)) +font = pygame.font.SysFont("monospace", 12) + +pygame.time.set_timer(pygame.USEREVENT, 1000 / 30) + +white = (255, 255, 255) +black = (0, 0, 0) +blue = (50, 50, 255) +lightblue = (150, 150, 255) +red = (255, 0, 0) +green = (0, 255, 0) + +freqs = collections.deque() +offsets = collections.deque() +delays = [] + +freq_avg = 0.0 +time = -1 +xscale = 2e-1 +yscale = 1e6 +frame_skip = 10 + +offset_rms = [ 0, 0, 0, 0 ] +offset_lock = 0 +delays_shown = 3 +eof = False +paused = False +game_mode = False + +delay_lines = [] +while True: + line = delay_file.readline() + if line == "": + break + line = line.split() + if line[2] == "1": + idx = int(line[1]) + elif line[1] == "1": + idx = int(line[2]) + else: + continue + while len(delay_lines) < idx + 1: + delay_lines.append([]) + delay_lines[idx].append(line) + +delay_file.close() + +for i in range(len(delay_lines)): + delays.append([]) + + last_line = [] + for line in delay_lines[i]: + if len(last_line) == 4 and len(line) == 4 and last_line[2] == "1" and line[1] == "1": + delay1 = float(last_line[3]) + delay2 = float(line[3]) + delays[i].append((int(float(line[0])), (delay1 - delay2) / 2, (delay1 + delay2) / 2)) + last_line = line + +while True: + event = pygame.event.wait() + if event.type == pygame.QUIT: + break + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_SPACE or event.key == pygame.K_p: + paused = not paused + elif event.key == pygame.K_q: + break + elif event.key == pygame.K_g: + game_mode = not game_mode + pygame.event.set_grab(game_mode) + pygame.mouse.set_visible(not game_mode) + elif event.key == pygame.K_l: + offset_lock += 1 + offset_lock %= len(offset_rms) + delays_shown = offset_lock + 1 + if delays_shown == 1: + delays_shown = 3 + elif event.key == pygame.K_PAGEUP: + frame_skip *= 2 + elif event.key == pygame.K_PAGEDOWN: + frame_skip /= 2 + if frame_skip <= 5: + frame_skip = 5 + elif event.key == pygame.K_UP: + yscale *= 2 + elif event.key == pygame.K_DOWN: + yscale /= 2 + elif event.key == pygame.K_LEFT: + xscale *= 2 + elif event.key == pygame.K_RIGHT: + xscale /= 2 + elif event.type == pygame.MOUSEMOTION: + rel = pygame.mouse.get_rel() + if game_mode and rel != (0, 0): + freq_avg += rel[1] / 1e9 + + pygame.event.clear(pygame.USEREVENT) + + if event.type != pygame.USEREVENT or paused: + continue + + while not eof: + histsize = maxx / xscale + + freq = freq_file.readline() + if freq == "": + eof = True + break + freqs.appendleft(float(freq)) + while len(freqs) > histsize: + freqs.pop() + + offset = offset_file.readline() + if offset == "": + eof = True + break + offsets.appendleft(map(float, offset.split())) + while len(offsets) > histsize: + offsets.pop() + + if not game_mode: + freq_avg += 0.001 * (freqs[0] - freq_avg) + else: + buttons = pygame.mouse.get_pressed() + if buttons == (1, 0, 0): + slew = 1e-6 + elif buttons == (0, 0, 1): + slew = -1e-6 + else: + slew = 0.0 + offsets[0][0] = offsets[1][0] - (freq_avg - freqs[0] + slew) + + offset_rms = [r + 0.001 * (o * o - r) for r, o in zip(offset_rms, offsets[0])] + + time += 1 + if time % frame_skip == 0: + break + + if len(offsets) == 0: + continue + + window.fill(black) + last_off = [] + x = maxx + y = maxy / 2 + offsets[0][offset_lock] * yscale + + def get_delays(time): + d = delays[delays_shown] + idx = len(d) - 1 + while time >= 0 and idx >= 0: + while d[idx][0] > time and idx > 0: + idx -= 1 + if d[idx][0] != time: + yield (False, 0, 0) + else: + yield (True, d[idx][1], d[idx][2]) + time -= 1 + + for freq, offset, (delay_valid, delay_center, delay_size) in zip(freqs, offsets, get_delays(time)): + x -= xscale + y -= (freq - freq_avg) * yscale + if int(x + xscale) != int(x): + for i, (off, col) in enumerate(zip(offset, [white, red, green, blue])): + oy = y - off * yscale + if len(last_off) > i: + pygame.draw.aaline(window, col, last_off[i], (x, oy)) + else: + last_off.append(()) + last_off[i] = (x, oy) + if game_mode: + break + + if delay_valid: + pygame.draw.line(window, blue, (x, y - (delay_center - delay_size) * yscale), (x, y - (delay_center + delay_size) * yscale)) + pygame.draw.line(window, lightblue, (x - 3, y - delay_center * yscale), (x + 3, y - delay_center * yscale)) + + window.blit(font.render("time = %d rms = %s xscale = %.1e yscale = %.1e" % (time, ["%1.6f" % math.sqrt(o) for o in offset_rms], xscale, yscale), False, white, black), (5, 0)) + window.blit(font.render("q:Quit p:Pause PgDn:Slow down PgUp:Speed up g:Game mode l:Switch lock Arrows:Scale", False, white, black), (5, maxy - 15)) + pygame.display.flip() + + #pygame.image.save(window, "visclocks%06d.png" % time) + +freq_file.close() +offset_file.close()