├── .gitignore ├── README.rst ├── combine_delta.c ├── configure ├── main.c ├── main_client.c ├── main_poll_server.c ├── main_sim_client.c ├── ntimed.h ├── ntimed_endian.h ├── ntimed_queue.h ├── ntimed_tricks.h ├── ntp.h ├── ntp_filter.c ├── ntp_packet.c ├── ntp_peer.c ├── ntp_peerset.c ├── ntp_tbl.h ├── ntp_tools.c ├── ocx_stdio.c ├── param.c ├── param_instance.h ├── param_tbl.h ├── pll_std.c ├── plotgen.py ├── suckaddr.c ├── time_sim.c ├── time_stuff.c ├── time_unix.c ├── todo.c ├── udp.c └── udp.h /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | ntimed-client 3 | .gdbinit 4 | .depend 5 | *.o 6 | _* 7 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Ntimed -- Network Time Synchronization 2 | ====================================== 3 | 4 | What is this ? 5 | ~~~~~~~~~~~~~~ 6 | 7 | This is a preview/early-acces/alpha/buzzword-of-the-times release 8 | of a new FOSS project written to gradually take over the world of 9 | networked timekeeping. 10 | 11 | The first step is a NTP protocol client daemon, 'Ntimed-client', 12 | which will synchronize a systems clock to some set of NTP servers 13 | 14 | If this catches on, support for slave servers, refclocks and other 15 | protocols, such as PTP, can be added, subject to interest, skill, 16 | time and money. 17 | 18 | The overall architectural goals are the same as every other FOSS 19 | project claims to follow: Simplicity, Quality, Security etc. etc. 20 | but I tend to think that we stick a little bit more closely to them. 21 | 22 | This work is sponsored by Linux Foundation, partly in response to 23 | the HeartBleed fiasco, and after studying the 300,000+ lines of 24 | source-code in NTPD. I concluded that while it *could* be salvaged, 25 | it would be more economical, much faster and far more efficient to 26 | start from scratch. 27 | 28 | Ntimed is the result. 29 | 30 | 31 | What should you do with this 32 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | You can take this code, compile it, run it, and it will steer your 35 | computer's clock, but I am not going to encourage you to do that in 36 | production yet -- unless you know what you are doing and why you 37 | are doing it: This is only a preview release. 38 | 39 | Soon-ish, there will be full production-ready releases and 40 | packages for your favourite operating system, but we are not 41 | there yet. 42 | 43 | But if you are willing to read C-source code to figure out what the 44 | printouts mean or if you care about quality time-keeping or quality 45 | programming, I would love to hear your feedback, reviews, and ideas. 46 | 47 | 48 | Where can I read more ? 49 | ~~~~~~~~~~~~~~~~~~~~~~~ 50 | 51 | I maintain a blog-of-sorts about this project here: 52 | 53 | http://phk.freebsd.dk/time 54 | 55 | There you will find information about theory, practice, 56 | and the thinking that tries to bridge the gap between them. 57 | 58 | Updates typically happen during weekends -- that is when I work on 59 | Ntimed. 60 | 61 | 62 | Who do I yell at and how ? 63 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 64 | 65 | Me, Poul-Henning Kamp. Please send email to phk@Ntimed.org. 66 | 67 | 68 | What happens next ? 69 | ~~~~~~~~~~~~~~~~~~~ 70 | 71 | The plan is to have the first production-ready release in Q1/2015. 72 | 73 | Hopefully OS releases will then adopt Ntimed - first as an alternative 74 | to, and later as replacement for NTPD in client applications. 75 | 76 | It is not my intent to start and manage an entirely new FOSS project 77 | around Ntimed. Harlan from The Network Time Foundation has agreed 78 | to adopt Ntimed and it will run in/with/parallel to the NTPD project. 79 | Or something. We still need to flesh out all those details. 80 | 81 | 82 | How to compile 83 | ~~~~~~~~~~~~~~ 84 | 85 | Pull the source code over to the machine you want to play on, and:: 86 | 87 | sh configure 88 | make 89 | 90 | (That was fast, wasn't it ? -- Amazing how slow things aren't 91 | when you don't go hunting for 27 different FORTRAN compilers for 92 | your C code.) 93 | 94 | 95 | How to test 96 | ~~~~~~~~~~~ 97 | 98 | Stop ntpd if it is running, and then:: 99 | 100 | ./ntimed-client -t /tmp/somefile some_ntp_server some_other_ntp_server 101 | 102 | That should synchronize your clock to those servers. 103 | 104 | Because this is a preview release, the process will not "daemonize" 105 | into the background. 106 | 107 | The '-t /tmp/somefile' arguments tells it to write a full blow-by-blow 108 | tracefile, for analysis and debugging. 109 | 110 | If something goes wrong, I'm going to ask you for a copy the tracefile. 111 | 112 | 113 | What happens when you run it ? 114 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 115 | 116 | After a few seconds, your clock will be stepped if necessary. 117 | (In the final version, this is where the process daemonizes into the 118 | background -- from which point you can trust your clock to be good.) 119 | 120 | In the next 30-60 seconds, the PLL will eliminate any residual phase 121 | error and from this point in time, your computer's clock should be 122 | good to a few milliseconds - depending on the quality of the servers. 123 | 124 | After about 5-10 minutes, the PLL will have integrated the 125 | frequency error of your computer's crystal, and the PLL will 126 | start to "stiffen" to minimize the amount of steering necessary 127 | to keep the clock aligned to the servers. 128 | 129 | If you are using distant or very distant servers, it will take longer 130 | time before the PLL stiffens. 131 | 132 | 133 | Packet traces and simulations 134 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 135 | 136 | You can also run the program two other ways:: 137 | 138 | ./ntimed-client --poll-server some_ntp_server some_other_ntp_server 139 | 140 | This will *not* steer your clock, but it will query the servers as 141 | if it should have steered your clock. (Ntpd should *not* be running 142 | at the same time.) By default it terminates after 1800 seconds, 143 | but you can control that with "-d 3600" for one hour etc. 144 | 145 | If you save the output into a file (redirect stdout or use '-t filename'), 146 | you can use it as input for a simulation run:: 147 | 148 | ./ntimed-client --sim-client -s filename -t /tmp/_ 149 | 150 | And then run:: 151 | 152 | python plotgen.py 153 | 154 | Then you can finally get some nice pictures to look at by:: 155 | 156 | gnuplot 157 | load '/tmp/_g' 158 | 159 | 160 | Tweaking parameters 161 | ~~~~~~~~~~~~~~~~~~~ 162 | 163 | Parameters can be examined and tweaked with '-p' arguments:: 164 | 165 | -p '?' 166 | 167 | Gives a list of available parameters, and you can get information about 168 | each parameter:: 169 | 170 | -p parameter_name 171 | 172 | To set the parameter to a non-default value:: 173 | 174 | -p parameter_name=new_value 175 | 176 | Not everything which should be a parameter is yet, and there are 177 | some unused dummy parameters there, just to make sure the macro-magic 178 | works. 179 | 180 | 181 | Thanks and acknowledegments 182 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 183 | 184 | First and foremost a big thanks to Professor Dave L. Mills. 185 | 186 | Thanks for being the first time-nut on the InterNETs, as we called 187 | them back then. 188 | 189 | Thanks for being an all-round pleasant fellow to work with. 190 | 191 | Thanks for adopting my 'nanokernel' and 'refclock_oncore'. 192 | 193 | But in particular thanks for lending me the most cantankerous LORAN-C 194 | receiver the world have ever seen, at a time in my life where I 195 | badly needed that a distraction to keep me sane. 196 | 197 | A big thanks to the Linux Foundation for realizing that NTPD was 198 | in dire straits after Dave Mills retired. 199 | 200 | Thanks for giving me money and free hands to do what I thought was 201 | best -- even though I am a "BSD-guy". 202 | 203 | Thanks to Harlan Stenn for keeping the NTPD flame burning, however 204 | stormy the last decade has been. 205 | 206 | I trust The Network Time Foundation will take as good care of Ntimed 207 | in the future, as it has taken care of NTPD in the past. 208 | 209 | A special wave of the hat to John R. Vig for his famous Quartz 210 | Crystal Tutorial. 211 | 212 | And finally, a shout-out and thanks to time-nuts@ in general and 213 | Tom Van Baak in particular, for being jolly and interesting company 214 | for people who happen to care about nanoseconds, leap seconds, 215 | choke-ring antennas and the finer points of SC- vs. AT-cut quartz 216 | crystals. 217 | 218 | *phk* 219 | -------------------------------------------------------------------------------- /combine_delta.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Source Combiner based on delta-pdfs 27 | * =================================== 28 | * 29 | * The basic principle here is that sources gives us four values: 30 | * - The highest low value were the probability is zero. 31 | * - The lowest high value were the probability is zero. 32 | * - The most probable value 33 | * - The relative trust in that value [0...1] 34 | * Together this defines a triangular probability density function. 35 | * 36 | * The combiner adds all these pdfs' together weighted by trust 37 | * and finds the highest probability which sports a quorum. 38 | * 39 | * See also: http://phk.freebsd.dk/time/20141107.html 40 | * 41 | * XXX: decay trust by age 42 | * XXX: auto determine quorom if param not set 43 | * XXX: param for minimum probability density 44 | */ 45 | 46 | #include 47 | #include 48 | #include 49 | 50 | #include "ntimed.h" 51 | 52 | 53 | struct cd_stat { 54 | double x; 55 | double prob; 56 | unsigned quorum; 57 | }; 58 | 59 | struct cd_source { 60 | unsigned magic; 61 | #define CD_SOURCE_MAGIC 0x2799775c 62 | TAILQ_ENTRY(cd_source) list; 63 | 64 | struct combiner combiner; 65 | 66 | struct combine_delta *cd; 67 | double trust, low, mid, high; 68 | int tb_gen; 69 | }; 70 | 71 | struct combine_delta { 72 | unsigned magic; 73 | #define COMBINE_DELTA_MAGIC 0x8dc5030c 74 | 75 | unsigned nsrc; 76 | TAILQ_HEAD(, cd_source) head; 77 | }; 78 | 79 | struct combine_delta * 80 | CD_New(void) 81 | { 82 | struct combine_delta *cd; 83 | 84 | ALLOC_OBJ(cd, COMBINE_DELTA_MAGIC); 85 | AN(cd); 86 | 87 | TAILQ_INIT(&cd->head); 88 | return (cd); 89 | } 90 | 91 | static void 92 | cd_try_peak(const struct combine_delta *cd, double *mx, double *my, double x, 93 | struct cd_stat *st) 94 | { 95 | struct cd_source *cs; 96 | 97 | // XXX: Hack to make plots with log zscale and only one 98 | // XXX: source look sensible. 99 | st->x = x; 100 | st->prob = 0.001; 101 | st->quorum = 0; 102 | 103 | TAILQ_FOREACH(cs, &cd->head, list) { 104 | if (cs->tb_gen != TB_generation) 105 | continue; 106 | if (x < cs->low) 107 | continue; 108 | if (x > cs->high) 109 | continue; 110 | if (cs->low >= cs->high) 111 | continue; 112 | 113 | st->quorum++; 114 | if (x < cs->mid) { 115 | st->prob += cs->trust * 2.0 * (x - cs->low) / 116 | ((cs->high - cs->low) * (cs->mid - cs->low)); 117 | } else { 118 | st->prob += cs->trust * 2.0 * (cs->high - x) / 119 | ((cs->high - cs->low) * (cs->high - cs->mid)); 120 | } 121 | if (isnan(st->prob)) { 122 | Fail(NULL, 0, "lo %.3e hi %.3e mid %.3e", 123 | cs->low, cs->high, cs->mid); 124 | } 125 | } 126 | if (st->prob > *my) { 127 | *my = st->prob; 128 | *mx = x; 129 | } 130 | } 131 | 132 | static int 133 | stat_cmp(const void *p1, const void *p2) 134 | { 135 | const struct cd_stat *left = p1; 136 | const struct cd_stat *right = p2; 137 | 138 | /*lint -save -e514 */ 139 | return ((left->x > right->x) - (left->x < right->x)); 140 | /*lint -restore */ 141 | } 142 | 143 | static void 144 | cd_find_peak(struct ocx *ocx, const struct combine_delta *cd) 145 | { 146 | struct cd_source *cs; 147 | double max_x = 0; 148 | double max_y = 1; 149 | struct cd_stat st[cd->nsrc * 3L]; 150 | size_t m; 151 | 152 | m = 0; 153 | TAILQ_FOREACH(cs, &cd->head, list) { 154 | if (cs->tb_gen != TB_generation) 155 | continue; 156 | cd_try_peak(cd, &max_x, &max_y, cs->low, &st[m++]); 157 | cd_try_peak(cd, &max_x, &max_y, cs->mid, &st[m++]); 158 | cd_try_peak(cd, &max_x, &max_y, cs->high, &st[m++]); 159 | } 160 | Put(ocx, OCX_TRACE, 161 | " %.3e %.3e %.3e\n", max_x, max_y, log(max_y)/log(10.)); 162 | PLL(ocx, max_x, max_y); 163 | qsort(st, cd->nsrc * 3L, sizeof st[0], stat_cmp); 164 | } 165 | 166 | static void __match_proto__(combine_f) 167 | cd_filter(struct ocx *ocx, const struct combiner *cb, 168 | double trust, double low, double mid, double high) 169 | { 170 | struct cd_source *cs; 171 | struct combine_delta *cd; 172 | 173 | CHECK_OBJ_NOTNULL(cb, COMBINER_MAGIC); 174 | CAST_OBJ_NOTNULL(cs, cb->priv, CD_SOURCE_MAGIC); 175 | cd = cs->cd; 176 | CHECK_OBJ_NOTNULL(cd, COMBINE_DELTA_MAGIC); 177 | 178 | /* Sign: local - remote -> postive is ahead */ 179 | assert(trust >= 0 && trust <= 1.0); 180 | cs->trust = trust; 181 | cs->low = low; 182 | cs->mid = mid; 183 | cs->high = high; 184 | cs->tb_gen = TB_generation; 185 | 186 | Put(ocx, OCX_TRACE, 187 | "Combine %s %s %.6f %.6f %.6f", cb->name1, cb->name2, 188 | cs->low, cs->mid, cs->high); 189 | 190 | cd_find_peak(ocx, cd); 191 | } 192 | 193 | struct combiner * 194 | CD_AddSource(struct combine_delta *cd, const char *name1, const char *name2) 195 | { 196 | struct cd_source *cs; 197 | 198 | CHECK_OBJ_NOTNULL(cd, COMBINE_DELTA_MAGIC); 199 | 200 | ALLOC_OBJ(cs, CD_SOURCE_MAGIC); 201 | AN(cs); 202 | cs->cd = cd; 203 | cs->low = cs->mid = cs->high = nan(""); 204 | TAILQ_INSERT_TAIL(&cd->head, cs, list); 205 | 206 | INIT_OBJ(&cs->combiner, COMBINER_MAGIC); 207 | cs->combiner.func = cd_filter; 208 | cs->combiner.priv = cs; 209 | cs->combiner.name1 = name1; 210 | cs->combiner.name2 = name2; 211 | 212 | cd->nsrc += 1; 213 | 214 | return (&cs->combiner); 215 | } 216 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2014 Poul-Henning Kamp 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | # SUCH DAMAGE. 26 | # 27 | # Handwritten configure script. 28 | # ============================= 29 | # 30 | # Before you suggest I use tools for this, please read: 31 | # 32 | # https://www.varnish-cache.org/docs/trunk/phk/autocrap.html 33 | # and 34 | # http://queue.acm.org/detail.cfm?id=2349257 35 | # 36 | 37 | set -e 38 | 39 | # List of .h files 40 | # NB: These SHALL always be included with #include "..." 41 | 42 | HDRS=' 43 | ntimed.h 44 | ntimed_endian.h 45 | ntimed_queue.h 46 | ntimed_tricks.h 47 | ntp.h 48 | ntp_tbl.h 49 | param_instance.h 50 | param_tbl.h 51 | udp.h 52 | ' 53 | 54 | # List of .c files 55 | 56 | SRCS=' 57 | combine_delta.c 58 | main.c 59 | main_client.c 60 | main_poll_server.c 61 | main_sim_client.c 62 | ntp_filter.c 63 | ntp_packet.c 64 | ntp_peer.c 65 | ntp_peerset.c 66 | ntp_tools.c 67 | ocx_stdio.c 68 | param.c 69 | pll_std.c 70 | suckaddr.c 71 | time_sim.c 72 | time_stuff.c 73 | time_unix.c 74 | todo.c 75 | udp.c 76 | ' 77 | 78 | if make -v 2>&1 | grep GNU > /dev/null 2>&1 ; then 79 | echo "make(1) is GNU make." 80 | BSD=false 81 | elif [ -f /usr/share/mk/bsd.prog.mk ] ; then 82 | echo "Found bsd.prog.mk, will use it." 83 | BSD=true 84 | else 85 | echo "Defaulting to plain makefile" 86 | BSD=false 87 | fi 88 | 89 | if $BSD ; then 90 | ( 91 | echo '# BSD-style Makefile generated by configure' 92 | echo 'PROG = ntimed-client' 93 | for f in ${SRCS} 94 | do 95 | echo "SRCS += ${f}" 96 | done 97 | 98 | echo 'NO_MAN = not_yet' 99 | echo 'LDADD += -lm' 100 | echo 'WARNS ?= 6' 101 | echo '.include ' 102 | ) > Makefile 103 | 104 | msg=", remember to run 'make depend'" 105 | else 106 | ( 107 | echo '# Portable Makefile generated by configure' 108 | echo '' 109 | echo 'all: ntimed-client' 110 | echo '' 111 | echo "CFLAGS += -Wall -Werror" 112 | echo '' 113 | 114 | for f in ${HDRS} 115 | do 116 | b=`basename $f .h` 117 | i=`sed -n -e '/#include.*"/{ 118 | s/"$// 119 | s/.*"// 120 | p 121 | }' $f | sort -u` 122 | if [ "x${i}" != "x" ] ; then 123 | echo "${b}.h: " ${i} 124 | echo " touch ${b}.h" 125 | echo 126 | fi 127 | done 128 | 129 | l="" 130 | for f in ${SRCS} 131 | do 132 | b=`basename $f .c` 133 | i=`sed -n -e '/#include.*"/{ 134 | s/"$// 135 | s/.*"// 136 | p 137 | }' $f | sort -u` 138 | echo "${b}.o: ${b}.c" ${i} 139 | echo 140 | l="${l} ${b}.o" 141 | done 142 | 143 | echo 144 | echo "ntimed-client: ${l}" 145 | echo " \${CC} \${CFLAGS} -o ntimed-client ${l} -lm" 146 | echo 147 | echo "clean:" 148 | echo " rm -f ${l} ntimed-client" 149 | echo 150 | echo "depend:" 151 | echo " @echo Dependencies already done" 152 | ) > Makefile 153 | fi 154 | 155 | echo "Makefile generated${msg}" 156 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Main main() functions 27 | * ===================== 28 | * 29 | */ 30 | 31 | #include 32 | 33 | #include "ntimed.h" 34 | #include "ntp.h" 35 | 36 | /*************************************************************************/ 37 | 38 | static void 39 | dummy(void) 40 | { 41 | // Reference otherwise unused "library" functions 42 | 43 | NTP_Peer_Destroy(NULL); 44 | } 45 | 46 | static int 47 | main_run_tests(int argc, char * const * argv) 48 | { 49 | 50 | (void)argc; 51 | (void)argv; 52 | 53 | Time_Unix_Passive(); 54 | 55 | TS_RunTest(NULL); 56 | 57 | return (0); 58 | } 59 | 60 | int 61 | main(int argc, char * const *argv) 62 | { 63 | if (getpid() == 0) 64 | dummy(); 65 | 66 | if (argc > 1 && !strcmp(argv[1], "--poll-server")) 67 | return (main_poll_server(argc - 1, argv + 1)); 68 | if (argc > 1 && !strcmp(argv[1], "--sim-client")) 69 | return (main_sim_client(argc - 1, argv + 1)); 70 | if (argc > 1 && !strcmp(argv[1], "--run-tests")) 71 | return (main_run_tests(argc - 1, argv + 1)); 72 | 73 | return (main_client(argc, argv)); 74 | } 75 | -------------------------------------------------------------------------------- /main_client.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Client main function 27 | * ==================== 28 | * 29 | * Steer system time based on NTP servers 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "ntimed.h" 39 | #include "ntp.h" 40 | #include "udp.h" 41 | 42 | #define PARAM_CLIENT PARAM_INSTANCE 43 | #define PARAM_TABLE_NAME client_param_table 44 | #include "param_instance.h" 45 | #undef PARAM_TABLE_NAME 46 | #undef PARAM_CLIENT 47 | 48 | static volatile sig_atomic_t restart = 1; 49 | 50 | static void __match_proto__() 51 | sig_hup(int siginfo) 52 | { 53 | (void)signal(SIGHUP, sig_hup); 54 | (void)siginfo; 55 | restart = 1; 56 | } 57 | 58 | int 59 | main_client(int argc, char *const *argv) 60 | { 61 | int ch; 62 | struct ntp_peer *np; 63 | struct ntp_peerset *nps; 64 | struct todolist *tdl; 65 | struct combine_delta *cd; 66 | struct udp_socket *usc; 67 | int npeer = 0; 68 | 69 | setbuf(stdout, NULL); 70 | setbuf(stderr, NULL); 71 | 72 | tdl = TODO_NewList(); 73 | Time_Unix(tdl); 74 | 75 | PLL_Init(); 76 | 77 | nps = NTP_PeerSet_New(NULL); 78 | 79 | Param_Register(client_param_table); 80 | NF_Init(); 81 | 82 | while ((ch = getopt(argc, argv, "p:t:")) != -1) { 83 | switch(ch) { 84 | case 'p': 85 | Param_Tweak(NULL, optarg); 86 | break; 87 | case 't': 88 | ArgTracefile(optarg); 89 | break; 90 | default: 91 | Fail(NULL, 0, 92 | "Usage %s [-p param] [-t tracefile] servers...", 93 | argv[0]); 94 | break; 95 | } 96 | } 97 | argc -= optind; 98 | argv += optind; 99 | 100 | for (ch = 0; ch < argc; ch++) 101 | npeer += NTP_PeerSet_Add(NULL, nps, argv[ch]); 102 | if (npeer == 0) 103 | Fail(NULL, 0, "No NTP peers found"); 104 | 105 | Put(NULL, OCX_TRACE, "# NTIMED Format client 1.0\n"); 106 | Put(NULL, OCX_TRACE, "# Found %d peers\n", npeer); 107 | 108 | Param_Report(NULL, OCX_TRACE); 109 | 110 | usc = UdpTimedSocket(NULL); 111 | if (usc == NULL) 112 | Fail(NULL, errno, "Could not open UDP socket"); 113 | 114 | cd = CD_New(); 115 | 116 | NTP_PeerSet_Foreach(np, nps) { 117 | NF_New(np); 118 | np->combiner = CD_AddSource(cd, np->hostname, np->ip); 119 | } 120 | 121 | do { 122 | if (restart) { 123 | Debug(NULL, "RESTART\n"); 124 | TB_generation++; 125 | NTP_PeerSet_Poll(NULL, nps, usc, tdl); 126 | restart = 0; 127 | } 128 | (void)signal(SIGHUP, sig_hup); 129 | (void)TODO_Run(NULL, tdl); 130 | } while (restart); 131 | 132 | return (0); 133 | } 134 | -------------------------------------------------------------------------------- /main_poll_server.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * poll-server 27 | * [-d duration] When to stop 28 | * [-m monitor] Poll this monitor every 32 seconds 29 | * [-t tracefile] Where to save the output (if not stdout) 30 | * server ... What servers to poll 31 | * 32 | */ 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | #include "ntimed.h" 40 | #include "ntp.h" 41 | #include "udp.h" 42 | 43 | static struct udp_socket *usc; 44 | 45 | static void 46 | mps_filter(struct ocx *ocx, const struct ntp_peer *np) 47 | { 48 | char buf[256]; 49 | 50 | NTP_Tool_Format(buf, sizeof buf, np->rx_pkt); 51 | Put(ocx, OCX_TRACE, "Poll %s %s %s\n", np->hostname, np->ip, buf); 52 | } 53 | 54 | static enum todo_e __match_proto__(todo_f) 55 | mps_mon(struct ocx *ocx, struct todolist *tdl, void *priv) 56 | { 57 | char buf[256]; 58 | struct ntp_peer *np; 59 | int i; 60 | 61 | (void)ocx; 62 | (void)tdl; 63 | CAST_OBJ_NOTNULL(np, priv, NTP_PEER_MAGIC); 64 | i = NTP_Peer_Poll(ocx, usc, np, 0.2); 65 | if (i == 1) { 66 | NTP_Tool_Format(buf, sizeof buf, np->rx_pkt); 67 | Put(ocx, OCX_TRACE, 68 | "Monitor %s %s %s\n", np->hostname, np->ip, buf); 69 | } else { 70 | Put(ocx, OCX_TRACE, 71 | "Monitor_err %s %s %d\n", np->hostname, np->ip, i); 72 | } 73 | return(TODO_OK); 74 | } 75 | 76 | static enum todo_e __match_proto__(todo_f) 77 | mps_end(struct ocx *ocx, struct todolist *tdl, void *priv) 78 | { 79 | (void)tdl; 80 | (void)priv; 81 | Put(ocx, OCX_TRACE, "# Run completed\n"); 82 | return(TODO_FAIL); 83 | } 84 | 85 | int 86 | main_poll_server(int argc, char *const *argv) 87 | { 88 | int ch; 89 | int npeer = 0; 90 | char *p; 91 | struct ntp_peerset *npl; 92 | struct ntp_peer *mon = NULL; 93 | struct ntp_peer *np; 94 | struct todolist *tdl; 95 | double duration = 1800; 96 | 97 | setbuf(stdout, NULL); 98 | setbuf(stderr, NULL); 99 | 100 | ArgTracefile("-"); 101 | 102 | tdl = TODO_NewList(); 103 | Time_Unix_Passive(); 104 | 105 | npl = NTP_PeerSet_New(NULL); 106 | AN(npl); 107 | 108 | while ((ch = getopt(argc, argv, "d:m:t:")) != -1) { 109 | switch(ch) { 110 | case 'd': 111 | duration = strtod(optarg, &p); 112 | if (*p != '\0' || duration < 1.0) 113 | Fail(NULL, 0, "Invalid -d argument"); 114 | break; 115 | case 'm': 116 | mon = NTP_Peer_NewLookup(NULL, optarg); 117 | if (mon == NULL) 118 | Fail(NULL, 0, "Monitor (-m) didn't resolve."); 119 | break; 120 | case 't': 121 | ArgTracefile(optarg); 122 | break; 123 | default: 124 | Fail(NULL, 0, 125 | "Usage %s [-d duration] [-m monitor] " 126 | "[-t tracefile] server...", argv[0]); 127 | break; 128 | } 129 | } 130 | argc -= optind; 131 | argv += optind; 132 | 133 | for (ch = 0; ch < argc; ch++) 134 | npeer += NTP_PeerSet_Add(NULL, npl, argv[ch]); 135 | Put(NULL, OCX_TRACE, "# NTIMED Format poll-server 1.0\n"); 136 | Put(NULL, OCX_TRACE, "# Found %d peers\n", npeer); 137 | if (npeer == 0) 138 | Fail(NULL, 0, "No peers found"); 139 | 140 | NTP_PeerSet_Foreach(np, npl) { 141 | Put(NULL, OCX_TRACE, "# Peer %s %s\n", np->hostname, np->ip); 142 | np->filter_func = mps_filter; 143 | } 144 | 145 | if (mon != NULL) 146 | Put(NULL, OCX_TRACE, 147 | "# Monitor %s %s\n", mon->hostname, mon->ip); 148 | 149 | usc = UdpTimedSocket(NULL); 150 | assert(usc != NULL); 151 | 152 | TODO_ScheduleRel(tdl, mps_end, NULL, duration, 0, "End task"); 153 | 154 | if (mon != NULL) 155 | TODO_ScheduleRel(tdl, mps_mon, mon, 0, 32, "Monitor"); 156 | 157 | NTP_PeerSet_Poll(NULL, npl, usc, tdl); 158 | (void)TODO_Run(NULL, tdl); 159 | return (0); 160 | } 161 | -------------------------------------------------------------------------------- /main_sim_client.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * sim_client 27 | * -s simfile Output file from poll-server 28 | * server_numbers ... 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "ntimed.h" 36 | #include "ntp.h" 37 | 38 | #define PARAM_CLIENT PARAM_INSTANCE 39 | #define PARAM_TABLE_NAME client_param_table 40 | #include "param_instance.h" 41 | #undef PARAM_TABLE_NAME 42 | #undef PARAM_CLIENT 43 | 44 | /**********************************************************************/ 45 | 46 | struct sim_file { 47 | unsigned magic; 48 | #define SIM_FILE_MAGIC 0x7f847bd0 49 | char *filename; 50 | FILE *input; 51 | unsigned n_peer; 52 | struct ntp_peerset *npl; 53 | struct timestamp when; 54 | unsigned t0; 55 | }; 56 | 57 | static void 58 | simfile_poll(struct ocx *ocx, const struct sim_file *sf, char *buf) 59 | { 60 | char *hostname; 61 | char *ip; 62 | char *pkt; 63 | struct ntp_peer *np; 64 | struct ntp_packet *rxp; 65 | struct ntp_packet *txp; 66 | 67 | CHECK_OBJ_NOTNULL(sf, SIM_FILE_MAGIC); 68 | AN(buf); 69 | 70 | if (memcmp(buf, "Poll ", 5)) 71 | Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf); 72 | hostname = buf + 5; 73 | ip = strchr(hostname, ' '); 74 | if (ip == NULL) 75 | Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf); 76 | pkt = strchr(ip + 1, ' '); 77 | if (pkt == NULL) 78 | Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf); 79 | 80 | *ip++ = '\0'; 81 | *pkt++ = '\0'; 82 | 83 | NTP_PeerSet_Foreach(np, sf->npl) 84 | if (!strcmp(np->hostname, hostname) && !strcmp(np->ip, ip)) 85 | break; 86 | if (np == NULL) 87 | Fail(ocx, 0, "Peer not found (%s, %s)\n", hostname, ip); 88 | 89 | CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); 90 | 91 | txp = np->tx_pkt; 92 | INIT_OBJ(txp, NTP_PACKET_MAGIC); 93 | 94 | rxp = np->rx_pkt; 95 | if (NTP_Tool_Scan(rxp, pkt)) 96 | Fail(ocx, 0, "Cannot parse packet (%s, %s, %s)\n", 97 | hostname, ip, pkt); 98 | 99 | TS_Add(&rxp->ntp_origin, Time_Sim_delta); 100 | TS_Add(&rxp->ts_rx, Time_Sim_delta); 101 | 102 | txp->ntp_transmit = rxp->ntp_origin; 103 | 104 | if (np->filter_func != NULL) 105 | np->filter_func(ocx, np); 106 | } 107 | 108 | static enum todo_e 109 | simfile_readline(struct ocx *ocx, struct todolist *tdl, void *priv) 110 | { 111 | struct sim_file *sf; 112 | char buf[BUFSIZ], *p; 113 | struct timestamp t0; 114 | unsigned u1, u2; 115 | double dt; 116 | 117 | AN(tdl); 118 | CAST_OBJ_NOTNULL(sf, priv, SIM_FILE_MAGIC); 119 | 120 | TB_Now(&t0); 121 | 122 | while (1) { 123 | if (fgets(buf, sizeof buf, sf->input) == NULL) { 124 | Debug(ocx, "EOF on -s file (%s)\n", sf->filename); 125 | exit(0); 126 | } 127 | p = strchr(buf, '\r'); 128 | if (p != NULL) 129 | *p = '\0'; 130 | p = strchr(buf, '\n'); 131 | if (p != NULL) 132 | *p = '\0'; 133 | 134 | if (!strncmp(buf, "Now ", 4)) { 135 | if (sscanf(buf, "Now %u.%u", &u1, &u2) != 2) 136 | Fail(ocx, 0, "Bad 'Now' line (%s)", buf); 137 | if (sf->t0 == 0) 138 | sf->t0 = u1 - t0.sec; 139 | u1 -= sf->t0; 140 | TS_Nanosec(&sf->when, u1, u2); 141 | dt = TS_Diff(&sf->when, &t0); 142 | if (dt >= 1e-3) { 143 | TODO_ScheduleAbs(tdl, simfile_readline, priv, 144 | &sf->when, 0.0, "Readline"); 145 | return (TODO_OK); 146 | } 147 | } else if (!strncmp(buf, "Poll ", 5)) { 148 | simfile_poll(ocx, sf, buf); 149 | } 150 | /* We ignore things we don't understand */ 151 | } 152 | } 153 | 154 | static struct sim_file * 155 | SimFile_Open(struct ocx *ocx, const char *fn, struct todolist *tdl, 156 | struct ntp_peerset *npl) 157 | { 158 | struct sim_file *sf; 159 | char buf[BUFSIZ]; 160 | char buf2[BUFSIZ]; 161 | char buf3[BUFSIZ]; 162 | char *e; 163 | int s; 164 | unsigned fpeer = 0; 165 | 166 | AN(fn); 167 | AN(tdl); 168 | AN(npl); 169 | 170 | ALLOC_OBJ(sf, SIM_FILE_MAGIC); 171 | AN(sf); 172 | 173 | sf->input = fopen(fn, "r"); 174 | if (sf->input == NULL) 175 | Fail(ocx, 1, "Could not open -s file (%s)", fn); 176 | sf->filename = strdup(fn); 177 | AN(sf->filename); 178 | sf->npl = npl; 179 | 180 | for (s = 0; s < 3; ) { 181 | if (fgets(buf, sizeof buf, sf->input) == NULL) 182 | Fail(ocx, 1, "Premature EOF on -s file (%s)", fn); 183 | e = strchr(buf, '\0'); 184 | AN(e); 185 | if (e == buf) 186 | continue; 187 | if (e[-1] == '\n') 188 | *--e = '\0'; 189 | Debug(ocx, ">>> %s\n", buf); 190 | switch(s) { 191 | case 0: 192 | if (strcmp(buf, "# NTIMED Format poll-server 1.0")) 193 | Fail(ocx, 0, 194 | "Wrong fileformat in -s file (%s)", fn); 195 | s++; 196 | break; 197 | case 1: 198 | if (sscanf(buf, "# Found %u peers", &sf->n_peer) != 1) 199 | Fail(ocx, 0, 200 | "Expected '# Found ... peers' line"); 201 | s++; 202 | break; 203 | case 2: 204 | if (sscanf(buf, "# Peer %s %s", buf2, buf3) != 2) 205 | Fail(ocx, 0, "Expected '# Peer' line"); 206 | 207 | NTP_PeerSet_AddSim(ocx, npl, buf2, buf3); 208 | if (++fpeer == sf->n_peer) 209 | s++; 210 | break; 211 | default: 212 | Debug(ocx, "<%s>\n", buf); 213 | Fail(ocx, 0, 214 | "XXX: Wrong state (%d) in open_sim_file", s); 215 | } 216 | } 217 | (void)simfile_readline(NULL, tdl, sf); 218 | return (sf); 219 | } 220 | 221 | int 222 | main_sim_client(int argc, char *const *argv) 223 | { 224 | int ch; 225 | const char *s_filename = NULL; 226 | struct sim_file *sf; 227 | struct ntp_peerset *npl; 228 | struct ntp_peer *np; 229 | struct todolist *tdl; 230 | struct combine_delta *cd; 231 | double a, b, c; 232 | 233 | setbuf(stdout, NULL); 234 | setbuf(stderr, NULL); 235 | 236 | tdl = TODO_NewList(); 237 | Time_Sim(tdl); 238 | 239 | PLL_Init(); 240 | 241 | npl = NTP_PeerSet_New(NULL); 242 | 243 | Param_Register(client_param_table); 244 | NF_Init(); 245 | 246 | while ((ch = getopt(argc, argv, "B:s:p:t:")) != -1) { 247 | switch(ch) { 248 | case 'B': 249 | ch = sscanf(optarg, "%lg,%lg,%lg", &a, &b, &c); 250 | if (ch != 3) 251 | Fail(NULL, 0, 252 | "bad -B argument \"when,freq,phase\""); 253 | Time_Sim_Bump(tdl, a, b, c); 254 | break; 255 | case 's': 256 | s_filename = optarg; 257 | break; 258 | case 'p': 259 | Param_Tweak(NULL, optarg); 260 | break; 261 | case 't': 262 | ArgTracefile(optarg); 263 | break; 264 | default: 265 | Fail(NULL, 0, 266 | "Usage %s [-s simfile] [-p params] [-t tracefile]" 267 | " [-B when,freq,phase]", argv[0]); 268 | break; 269 | } 270 | } 271 | // argc -= optind; 272 | // argv += optind; 273 | 274 | Param_Report(NULL, OCX_TRACE); 275 | 276 | if (s_filename == NULL) 277 | Fail(NULL, 1, "You must specify -s file."); 278 | 279 | sf = SimFile_Open(NULL, s_filename, tdl, npl); 280 | AN(sf); 281 | 282 | cd = CD_New(); 283 | 284 | NTP_PeerSet_Foreach(np, npl) { 285 | NF_New(np); 286 | np->combiner = CD_AddSource(cd, np->hostname, np->ip); 287 | } 288 | 289 | (void)TODO_Run(NULL, tdl); 290 | 291 | return (0); 292 | } 293 | -------------------------------------------------------------------------------- /ntimed.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Main include file 27 | * ================= 28 | */ 29 | 30 | #ifdef NTIMED_H_INCLUDED 31 | #error "ntimed.h included multiple times" 32 | #endif 33 | #define NTIMED_H_INCLUDED 34 | 35 | #include 36 | #include 37 | #include "ntimed_queue.h" 38 | #include "ntimed_tricks.h" 39 | 40 | struct todolist; 41 | struct udp_socket; 42 | 43 | /* ocx_*.c -- Operational Context *************************************/ 44 | 45 | struct ocx; // private 46 | 47 | enum ocx_chan { 48 | OCX_DIAG, // think: stderr 49 | OCX_TRACE, // think: /var/run/stats 50 | OCX_DEBUG, // think: stdout 51 | }; 52 | 53 | void Put(struct ocx *, enum ocx_chan, const char *, ...) 54 | __printflike(3, 4); 55 | void PutHex(struct ocx *, enum ocx_chan, const void *, ssize_t len); 56 | 57 | /* 58 | * Report "Failure: " + args + "\n" [+ errno-line] + "\n". exit(1); 59 | */ 60 | void Fail(struct ocx *, int err, const char *, ...) \ 61 | __attribute__((__noreturn__)) 62 | __printflike(3, 4); 63 | 64 | #define Debug(ocx, ...) Put(ocx, OCX_DEBUG, __VA_ARGS__) 65 | #define DebugHex(ocx, ptr, len) PutHex(ocx, OCX_DEBUG, ptr, len) 66 | 67 | void ArgTracefile(const char *fn); 68 | 69 | /* param.c -- Parameters **********************************************/ 70 | 71 | struct param_tbl { 72 | const char *name; 73 | double *val; 74 | double min; 75 | double max; 76 | double def; 77 | const char *doc; 78 | TAILQ_ENTRY(param_tbl) list; 79 | }; 80 | 81 | void Param_Register(struct param_tbl *pt); 82 | void Param_Tweak(struct ocx *, const char *arg); 83 | void Param_Report(struct ocx *ocx, enum ocx_chan); 84 | 85 | /* pll_std.c -- Standard PLL ******************************************/ 86 | 87 | typedef void pll_f(struct ocx *ocx, double offset, double weight); 88 | extern pll_f *PLL; 89 | 90 | void PLL_Init(void); 91 | 92 | /* suckaddr.c -- Sockaddr utils ***************************************/ 93 | 94 | int SA_Equal(const void *sa1, size_t sl1, const void *sa2, size_t sl2); 95 | 96 | /* time_sim.c -- Simulated timebase ***********************************/ 97 | 98 | extern double Time_Sim_delta; 99 | void Time_Sim(struct todolist *); 100 | void Time_Sim_Bump(struct todolist *, double when, double freq, double phase); 101 | 102 | /* time_unix.c -- UNIX timebase ***************************************/ 103 | 104 | void Time_Unix(struct todolist *); 105 | void Time_Unix_Passive(void); 106 | 107 | /* time_stuff.c -- Timebase infrastructure ****************************/ 108 | 109 | struct timestamp { 110 | unsigned magic; 111 | #define TIMESTAMP_MAGIC 0x344cd213 112 | uint64_t sec; // Really: time_t 113 | uint64_t frac; 114 | }; 115 | 116 | typedef int tb_sleep_f(double dur); 117 | typedef struct timestamp *tb_now_f(struct timestamp *); 118 | typedef void tb_step_f(struct ocx *, double offset); 119 | typedef void tb_adjust_f(struct ocx *, double offset, double duration, 120 | double frequency); 121 | 122 | extern int TB_generation; 123 | extern tb_sleep_f *TB_Sleep; 124 | extern tb_now_f *TB_Now; 125 | extern tb_step_f *TB_Step; 126 | extern tb_adjust_f *TB_Adjust; 127 | 128 | void TS_Add(struct timestamp *ts, double dt); 129 | struct timestamp *TS_Nanosec(struct timestamp *storage, 130 | int64_t sec, int64_t nsec); 131 | 132 | struct timestamp *TS_Double(struct timestamp *storage, double); 133 | double TS_Diff(const struct timestamp *t1, const struct timestamp *t2); 134 | int TS_SleepUntil(const struct timestamp *); 135 | void TS_Format(char *buf, size_t len, const struct timestamp *ts); 136 | 137 | void TS_RunTest(struct ocx *ocx); 138 | 139 | /* todo.c -- todo-list scheduler **************************************/ 140 | 141 | 142 | enum todo_e { 143 | TODO_INTR = -2, // Signal received 144 | TODO_FAIL = -1, // Break out of TODO_Run() 145 | TODO_OK = 0, 146 | TODO_DONE = 1, // Stop repeating me 147 | }; 148 | 149 | typedef enum todo_e todo_f(struct ocx *, struct todolist *, void *priv); 150 | 151 | struct todolist *TODO_NewList(void); 152 | 153 | uintptr_t TODO_ScheduleRel(struct todolist *, 154 | todo_f *func, void *priv, 155 | double when, double repeat, 156 | const char *fmt, ...) __printflike(6, 7); 157 | uintptr_t TODO_ScheduleAbs(struct todolist *, 158 | todo_f *func, void *priv, 159 | const struct timestamp *when, double repeat, 160 | const char *fmt, ...) __printflike(6, 7); 161 | enum todo_e TODO_Run(struct ocx *ocx, struct todolist *); 162 | void TODO_Cancel(struct todolist *tdl, uintptr_t *); 163 | 164 | /* combine_delta.c -- Source Combiner based on delta-pdfs *************/ 165 | 166 | struct combiner; 167 | 168 | typedef void combine_f(struct ocx *, const struct combiner *, 169 | double trust, double low, double mid, double high); 170 | 171 | struct combiner { 172 | unsigned magic; 173 | #define COMBINER_MAGIC 0xab2b239c 174 | 175 | combine_f *func; 176 | void *priv; 177 | const char *name1; 178 | const char *name2; 179 | }; 180 | 181 | struct combine_delta *CD_New(void); 182 | struct combiner *CD_AddSource(struct combine_delta *, 183 | const char *name1, const char *name2); 184 | 185 | /********************************************************************** 186 | * Main functions 187 | */ 188 | 189 | int main_client(int argc, char *const *argv); 190 | int main_poll_server(int argc, char *const *argv); 191 | int main_sim_client(int argc, char *const *argv); 192 | -------------------------------------------------------------------------------- /ntimed_endian.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2002 Thomas Moestl 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Part of: 27 | * $FreeBSD: head/sys/sys/endian.h 208331 2010-05-20 06:16:13Z phk $ 28 | */ 29 | 30 | #ifdef NTIMED_ENDIAN 31 | #error "ntimed_endian.h included multiple times" 32 | #endif 33 | #define NTIMED_ENDIAN 34 | 35 | /* Alignment-agnostic encode/decode bytestream to/from little/big endian. */ 36 | 37 | static __inline uint16_t 38 | Be16dec(const void *pp) 39 | { 40 | uint8_t const *p = (uint8_t const *)pp; 41 | 42 | return ((p[0] << 8) | p[1]); 43 | } 44 | 45 | static __inline uint32_t 46 | Be32dec(const void *pp) 47 | { 48 | uint8_t const *p = (uint8_t const *)pp; 49 | 50 | return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); 51 | } 52 | 53 | static __inline void 54 | Be16enc(void *pp, uint16_t u) 55 | { 56 | uint8_t *p = (uint8_t *)pp; 57 | 58 | p[0] = (u >> 8) & 0xff; 59 | p[1] = u & 0xff; 60 | } 61 | 62 | static __inline void 63 | Be32enc(void *pp, uint32_t u) 64 | { 65 | uint8_t *p = (uint8_t *)pp; 66 | 67 | p[0] = (u >> 24) & 0xff; 68 | p[1] = (u >> 16) & 0xff; 69 | p[2] = (u >> 8) & 0xff; 70 | p[3] = u & 0xff; 71 | } 72 | -------------------------------------------------------------------------------- /ntimed_queue.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1991, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 4. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * Part of: 30 | * $FreeBSD: head/sys/sys/queue.h 251887 2013-06-18 02:57:56Z lstewart $ 31 | */ 32 | 33 | #ifdef NTIMED_QUEUE_H 34 | #error "ntimed_queue.h included multiple times" 35 | #endif 36 | #define NTIMED_QUEUE_H 37 | 38 | /* 39 | * Tail queue declarations. 40 | */ 41 | #define TAILQ_HEAD(name, type) \ 42 | struct name { \ 43 | struct type *tqh_first; /* first element */ \ 44 | struct type **tqh_last; /* addr of last next element */ \ 45 | } 46 | 47 | #define TAILQ_HEAD_INITIALIZER(head) \ 48 | { NULL, &(head).tqh_first } 49 | 50 | #define TAILQ_ENTRY(type) \ 51 | struct { \ 52 | struct type *tqe_next; /* next element */ \ 53 | struct type **tqe_prev; /* address of previous next element */ \ 54 | } 55 | 56 | /* 57 | * Tail queue functions. 58 | */ 59 | 60 | #define TAILQ_CONCAT(head1, head2, field) do { \ 61 | if (!TAILQ_EMPTY(head2)) { \ 62 | *(head1)->tqh_last = (head2)->tqh_first; \ 63 | (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ 64 | (head1)->tqh_last = (head2)->tqh_last; \ 65 | TAILQ_INIT((head2)); \ 66 | } \ 67 | } while (0) 68 | 69 | #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) 70 | 71 | #define TAILQ_FIRST(head) ((head)->tqh_first) 72 | 73 | #define TAILQ_FOREACH(var, head, field) \ 74 | for ((var) = TAILQ_FIRST((head)); \ 75 | (var); \ 76 | (var) = TAILQ_NEXT((var), field)) 77 | 78 | #define TAILQ_FOREACH_FROM(var, head, field) \ 79 | for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ 80 | (var); \ 81 | (var) = TAILQ_NEXT((var), field)) 82 | 83 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 84 | for ((var) = TAILQ_FIRST((head)); \ 85 | (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ 86 | (var) = (tvar)) 87 | 88 | #define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ 89 | for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ 90 | (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ 91 | (var) = (tvar)) 92 | 93 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 94 | for ((var) = TAILQ_LAST((head), headname); \ 95 | (var); \ 96 | (var) = TAILQ_PREV((var), headname, field)) 97 | 98 | #define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ 99 | for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ 100 | (var); \ 101 | (var) = TAILQ_PREV((var), headname, field)) 102 | 103 | #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 104 | for ((var) = TAILQ_LAST((head), headname); \ 105 | (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ 106 | (var) = (tvar)) 107 | 108 | #define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ 109 | for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ 110 | (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ 111 | (var) = (tvar)) 112 | 113 | #define TAILQ_INIT(head) do { \ 114 | TAILQ_FIRST((head)) = NULL; \ 115 | (head)->tqh_last = &TAILQ_FIRST((head)); \ 116 | } while (0) 117 | 118 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 119 | if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ 120 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 121 | &TAILQ_NEXT((elm), field); \ 122 | else { \ 123 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 124 | } \ 125 | TAILQ_NEXT((listelm), field) = (elm); \ 126 | (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ 127 | } while (0) 128 | 129 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 130 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 131 | TAILQ_NEXT((elm), field) = (listelm); \ 132 | *(listelm)->field.tqe_prev = (elm); \ 133 | (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ 134 | } while (0) 135 | 136 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ 137 | if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ 138 | TAILQ_FIRST((head))->field.tqe_prev = \ 139 | &TAILQ_NEXT((elm), field); \ 140 | else \ 141 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 142 | TAILQ_FIRST((head)) = (elm); \ 143 | (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ 144 | } while (0) 145 | 146 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ 147 | TAILQ_NEXT((elm), field) = NULL; \ 148 | (elm)->field.tqe_prev = (head)->tqh_last; \ 149 | *(head)->tqh_last = (elm); \ 150 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 151 | } while (0) 152 | 153 | #define TAILQ_LAST(head, headname) \ 154 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 155 | 156 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 157 | 158 | #define TAILQ_PREV(elm, headname, field) \ 159 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 160 | 161 | #define TAILQ_REMOVE(head, elm, field) do { \ 162 | if ((TAILQ_NEXT((elm), field)) != NULL) \ 163 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 164 | (elm)->field.tqe_prev; \ 165 | else { \ 166 | (head)->tqh_last = (elm)->field.tqe_prev; \ 167 | } \ 168 | *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ 169 | } while (0) 170 | 171 | #define TAILQ_SWAP(head1, head2, type, field) do { \ 172 | struct type *swap_first = (head1)->tqh_first; \ 173 | struct type **swap_last = (head1)->tqh_last; \ 174 | (head1)->tqh_first = (head2)->tqh_first; \ 175 | (head1)->tqh_last = (head2)->tqh_last; \ 176 | (head2)->tqh_first = swap_first; \ 177 | (head2)->tqh_last = swap_last; \ 178 | if ((swap_first = (head1)->tqh_first) != NULL) \ 179 | swap_first->field.tqe_prev = &(head1)->tqh_first; \ 180 | else \ 181 | (head1)->tqh_last = &(head1)->tqh_first; \ 182 | if ((swap_first = (head2)->tqh_first) != NULL) \ 183 | swap_first->field.tqe_prev = &(head2)->tqh_first; \ 184 | else \ 185 | (head2)->tqh_last = &(head2)->tqh_first; \ 186 | } while (0) 187 | -------------------------------------------------------------------------------- /ntimed_tricks.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifdef NTIMED_TRICKS_H 28 | #error "ntimed_tricks.h included multiple times" 29 | #endif 30 | #define NTIMED_TRICKS_H 31 | 32 | #include 33 | 34 | /********************************************************************** 35 | * Assert and friends 36 | * 37 | * We always runs with asserts enabled, and we assert liberally. 38 | * 39 | * Tests which are too expensive for production use should be wrapped 40 | * in "#ifdef DIAGNOSTICS" 41 | * 42 | */ 43 | 44 | #undef NDEBUG // Asserts *always* enabled. 45 | 46 | #define AZ(foo) do { assert((foo) == 0); } while (0) 47 | #define AN(foo) do { assert((foo) != 0); } while (0) 48 | 49 | #define WRONG(foo) \ 50 | do { \ 51 | /*lint -save -e506 */ \ 52 | assert(0 == (uintptr_t)foo); \ 53 | /*lint -restore */ \ 54 | } while (0) 55 | 56 | /********************************************************************** 57 | * Safe printfs into compile-time fixed-size buffers. 58 | */ 59 | 60 | #define bprintf(buf, fmt, ...) \ 61 | do { \ 62 | assert(snprintf(buf, sizeof buf, fmt, __VA_ARGS__) \ 63 | < (int)sizeof buf); \ 64 | } while (0) 65 | 66 | #define vbprintf(buf, fmt, ap) \ 67 | do { \ 68 | assert(vsnprintf(buf, sizeof buf, fmt, ap) \ 69 | < (int)sizeof buf); \ 70 | } while (0) 71 | 72 | /********************************************************************** 73 | * FlexeLint shutuppery 74 | * 75 | * Flexelint is a commercial LINT program from gimpel.com, and a very 76 | * good one at that. We need a few special-case markups to tell it 77 | * our intentions. 78 | */ 79 | 80 | /* 81 | * In OO-light situations, functions have to match their prototype 82 | * even if that means not const'ing a const'able argument. 83 | * The typedef should be specified as argument to the macro. 84 | * 85 | */ 86 | #define __match_proto__(xxx) /*lint -e{818} */ 87 | 88 | /* 89 | * Some functions never return, and there's nothing Turing can do about it. 90 | * Some compilers think you should still include a final return(bla) in 91 | * such functions, while other tools complain about unreachable code. 92 | * Wrapping in a macro means we can shut the tools up. 93 | */ 94 | 95 | #define NEEDLESS_RETURN(foo) return(foo) 96 | 97 | /********************************************************************** 98 | * Compiler tricks 99 | */ 100 | 101 | #ifndef __printflike 102 | #define __printflike(a, b) 103 | #endif 104 | 105 | /********************************************************************** 106 | * Mini object type checking 107 | * 108 | * This is a trivial struct type checking framework I have used with 109 | * a lot of success in a number of high-rel software projects over the 110 | * years. It is particularly valuable when you pass things trough void 111 | * pointers or opaque APIs. 112 | * 113 | * Define your struct like this: 114 | * struct foobar { 115 | * unsigned magic; 116 | * #define FOOBAR_MAGIC 0x23923092 117 | * ... 118 | * } 119 | * 120 | * The "magic" element SHALL be the first element of the struct. 121 | * 122 | * The MAGIC number you get from "od -x < /dev/random | head -1" or 123 | * similar. It is important that each structure has its own distinct 124 | * value. 125 | * 126 | */ 127 | 128 | #define INIT_OBJ(to, type_magic) \ 129 | do { \ 130 | (void)memset(to, 0, sizeof *to); \ 131 | (to)->magic = (type_magic); \ 132 | } while (0) 133 | 134 | #define ALLOC_OBJ(to, type_magic) \ 135 | do { \ 136 | (to) = calloc(1L, sizeof *(to)); \ 137 | if ((to) != NULL) \ 138 | (to)->magic = (type_magic); \ 139 | } while (0) 140 | 141 | #define FREE_OBJ(to) \ 142 | do { \ 143 | (to)->magic = (0); \ 144 | free(to); \ 145 | } while (0) 146 | 147 | #define VALID_OBJ(ptr, type_magic) \ 148 | ((ptr) != NULL && (ptr)->magic == (type_magic)) 149 | 150 | #define CHECK_OBJ(ptr, type_magic) \ 151 | do { \ 152 | assert((ptr)->magic == type_magic); \ 153 | } while (0) 154 | 155 | #define CHECK_OBJ_NOTNULL(ptr, type_magic) \ 156 | do { \ 157 | assert((ptr) != NULL); \ 158 | assert((ptr)->magic == type_magic); \ 159 | } while (0) 160 | 161 | #define CHECK_OBJ_ORNULL(ptr, type_magic) \ 162 | do { \ 163 | if ((ptr) != NULL) \ 164 | assert((ptr)->magic == type_magic); \ 165 | } while (0) 166 | 167 | #define CAST_OBJ(to, from, type_magic) \ 168 | do { \ 169 | (to) = (from); \ 170 | if ((to) != NULL) \ 171 | CHECK_OBJ((to), (type_magic)); \ 172 | } while (0) 173 | 174 | #define CAST_OBJ_NOTNULL(to, from, type_magic) \ 175 | do { \ 176 | (to) = (from); \ 177 | assert((to) != NULL); \ 178 | CHECK_OBJ((to), (type_magic)); \ 179 | } while (0) 180 | -------------------------------------------------------------------------------- /ntp.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * NTP protocol stuff 27 | * ================== 28 | * 29 | */ 30 | 31 | struct ntp_peer; 32 | 33 | #ifdef NTP_H_INCLUDED 34 | #error "ntp.h included multiple times" 35 | #endif 36 | #define NTP_H_INCLUDED 37 | 38 | enum ntp_mode { 39 | #define NTP_MODE(n, l, u) NTP_MODE_##u = n, 40 | #include "ntp_tbl.h" 41 | #undef NTP_MODE 42 | }; 43 | 44 | enum ntp_leap { 45 | #define NTP_LEAP(n, l, u) NTP_LEAP_##u = n, 46 | #include "ntp_tbl.h" 47 | #undef NTP_LEAP 48 | }; 49 | 50 | enum ntp_state { 51 | #define NTP_STATE(n, l, u, d) NTP_STATE_##u = n, 52 | #include "ntp_tbl.h" 53 | #undef NTP_STATE 54 | }; 55 | 56 | /* ntp_packet.c -- [De]Serialisation **********************************/ 57 | 58 | struct ntp_packet { 59 | unsigned magic; 60 | #define NTP_PACKET_MAGIC 0x78b7f0be 61 | 62 | enum ntp_leap ntp_leap; 63 | uint8_t ntp_version; 64 | enum ntp_mode ntp_mode; 65 | uint8_t ntp_stratum; 66 | uint8_t ntp_poll; 67 | int8_t ntp_precision; 68 | struct timestamp ntp_delay; 69 | struct timestamp ntp_dispersion; 70 | uint8_t ntp_refid[4]; 71 | struct timestamp ntp_reference; 72 | struct timestamp ntp_origin; 73 | struct timestamp ntp_receive; 74 | struct timestamp ntp_transmit; 75 | 76 | struct timestamp ts_rx; 77 | }; 78 | 79 | struct ntp_packet *NTP_Packet_Unpack(struct ntp_packet *dst, void *ptr, 80 | ssize_t len); 81 | size_t NTP_Packet_Pack(void *ptr, ssize_t len, struct ntp_packet *); 82 | 83 | /* ntp_tools.c -- Handy tools *****************************************/ 84 | 85 | void NTP_Tool_Client_Req(struct ntp_packet *); 86 | void NTP_Tool_Format(char *p, ssize_t len, const struct ntp_packet *pkt); 87 | int NTP_Tool_Scan(struct ntp_packet *pkt, const char *buf); 88 | 89 | /* ntp_filter.c -- NTP sanity checking ********************************/ 90 | 91 | typedef void ntp_filter_f(struct ocx *, const struct ntp_peer *); 92 | 93 | void NF_New(struct ntp_peer *); 94 | void NF_Init(void); 95 | 96 | /* ntp_peer.c -- State management *************************************/ 97 | 98 | 99 | struct ntp_peer { 100 | unsigned magic; 101 | #define NTP_PEER_MAGIC 0xbf0740a0 102 | char *hostname; 103 | char *ip; 104 | struct sockaddr *sa; 105 | unsigned sa_len; 106 | struct ntp_packet *tx_pkt; 107 | struct ntp_packet *rx_pkt; 108 | 109 | ntp_filter_f *filter_func; 110 | void *filter_priv; 111 | 112 | struct combiner *combiner; 113 | 114 | // For ntp_peerset.c 115 | TAILQ_ENTRY(ntp_peer) list; 116 | struct ntp_group *group; 117 | enum ntp_state state; 118 | const struct ntp_peer *other; 119 | }; 120 | 121 | struct ntp_peer *NTP_Peer_New(const char *name, const void *, unsigned); 122 | struct ntp_peer *NTP_Peer_NewLookup(struct ocx *ocx, const char *name); 123 | void NTP_Peer_Destroy(struct ntp_peer *np); 124 | int NTP_Peer_Poll(struct ocx *, const struct udp_socket *, 125 | const struct ntp_peer *, double tmo); 126 | 127 | /* ntp_peerset.c -- Peer set management ****************************/ 128 | 129 | struct ntp_peerset *NTP_PeerSet_New(struct ocx *); 130 | void NTP_PeerSet_AddSim(struct ocx *, struct ntp_peerset *, 131 | const char *hostname, const char *ip); 132 | int NTP_PeerSet_Add(struct ocx *, struct ntp_peerset *, const char *hostname); 133 | void NTP_PeerSet_Poll(struct ocx *, struct ntp_peerset *, struct udp_socket *, 134 | struct todolist *); 135 | 136 | struct ntp_peer *NTP_PeerSet_Iter0(const struct ntp_peerset *); 137 | struct ntp_peer *NTP_PeerSet_IterN(const struct ntp_peerset *, 138 | const struct ntp_peer *); 139 | 140 | #define NTP_PeerSet_Foreach(var, nps) \ 141 | for(var = NTP_PeerSet_Iter0(nps); \ 142 | var != NULL; \ 143 | var = NTP_PeerSet_IterN(nps, var)) 144 | -------------------------------------------------------------------------------- /ntp_filter.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Filter incoming NTP packets 27 | * =========================== 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include "ntimed.h" 34 | 35 | #include "ntp.h" 36 | 37 | #define PARAM_NTP_FILTER PARAM_INSTANCE 38 | #define PARAM_TABLE_NAME ntp_filter_param_table 39 | #include "param_instance.h" 40 | #undef PARAM_TABLE_NAME 41 | #undef PARAM_NTP_FILTER 42 | 43 | struct ntp_filter { 44 | unsigned magic; 45 | #define NTP_FILTER_MAGIC 0xf7b7032d 46 | 47 | double lo, mid, hi; 48 | double alo, amid, ahi; 49 | double alolo, ahihi; 50 | double navg; 51 | double trust; 52 | 53 | int generation; 54 | }; 55 | 56 | static void __match_proto__(ntp_filter_f) 57 | nf_filter(struct ocx *ocx, const struct ntp_peer *np) 58 | { 59 | struct ntp_filter *nf; 60 | struct ntp_packet *rxp; 61 | int branch, fail_hi, fail_lo; 62 | double lo_noise, hi_noise; 63 | double lo_lim, hi_lim; 64 | double r; 65 | char buf[256]; 66 | 67 | CAST_OBJ_NOTNULL(nf, np->filter_priv, NTP_FILTER_MAGIC); 68 | 69 | if (nf->generation != TB_generation) { 70 | nf->navg = 0; 71 | nf->alo = nf->amid = nf->ahi = 0.0; 72 | nf->alolo = nf->ahihi = 0.0; 73 | } 74 | 75 | rxp = np->rx_pkt; 76 | CHECK_OBJ_NOTNULL(rxp, NTP_PACKET_MAGIC); 77 | 78 | NTP_Tool_Format(buf, sizeof buf, rxp); 79 | 80 | Put(NULL, OCX_TRACE, "NTP_Packet %s %s %s\n", 81 | np->hostname, np->ip, buf); 82 | 83 | if (rxp->ntp_leap == NTP_LEAP_UNKNOWN) 84 | return; // XXX diags 85 | 86 | // XXX: Check leap warnings in wrong months 87 | // XXX: Check leap warnings against other sources 88 | 89 | if (rxp->ntp_version < 3 || rxp->ntp_version > 4) { 90 | Put(ocx, OCX_TRACE, "NF Bad version %d\n", rxp->ntp_version); 91 | return; 92 | } 93 | 94 | if (rxp->ntp_mode != NTP_MODE_SERVER) { 95 | Put(ocx, OCX_TRACE, "NF Bad mode %d\n", rxp->ntp_mode); 96 | return; 97 | } 98 | 99 | if (rxp->ntp_stratum == 0 || rxp->ntp_stratum > 15) { 100 | Put(ocx, OCX_TRACE, "NF Bad stratum %d\n", rxp->ntp_stratum); 101 | return; 102 | } 103 | 104 | r = TS_Diff(&rxp->ntp_transmit, &rxp->ntp_receive); 105 | if (r <= 0.0) { 106 | Put(ocx, OCX_TRACE, "NF rx after tx %.3e\n", r); 107 | return; 108 | } 109 | 110 | r = TS_Diff(&rxp->ntp_transmit, &rxp->ntp_reference); 111 | if (r < -2e-9) { 112 | /* two nanoseconds to Finagle rounding errors */ 113 | Put(ocx, OCX_TRACE, "NF ref after tx %.3e\n", r); 114 | return; // XXX diags 115 | } 116 | 117 | // This is almost never a good sign. 118 | if (r > 2048) { 119 | /* XXX: 2048 -> param */ 120 | Put(ocx, OCX_TRACE, "NF ancient ref %.3e\n", r); 121 | return; 122 | } 123 | 124 | if (nf->navg < param_ntp_filter_average) 125 | nf->navg += 1; 126 | 127 | nf->lo = TS_Diff(&rxp->ntp_origin, &rxp->ntp_receive); 128 | nf->hi = TS_Diff(&rxp->ts_rx, &rxp->ntp_transmit); 129 | nf->mid = .5 * (nf->lo + nf->hi); 130 | 131 | if (nf->navg > 2) { 132 | lo_noise = sqrt(nf->alolo - nf->alo * nf->alo); 133 | hi_noise = sqrt(nf->ahihi - nf->ahi * nf->ahi); 134 | } else { 135 | lo_noise = 0.0; 136 | hi_noise = 0.0; 137 | } 138 | 139 | lo_lim = nf->alo - lo_noise * param_ntp_filter_threshold; 140 | hi_lim = nf->ahi + hi_noise * param_ntp_filter_threshold; 141 | 142 | fail_lo = nf->lo < lo_lim; 143 | fail_hi = nf->hi > hi_lim; 144 | 145 | if (fail_lo && fail_hi) { 146 | branch = 1; 147 | } else if (nf->navg > 3 && fail_lo) { 148 | nf->mid = nf->amid + (nf->hi - nf->ahi); 149 | branch = 2; 150 | } else if (nf->navg > 3 && fail_hi) { 151 | nf->mid = nf->amid + nf->lo - nf->alo; 152 | branch = 3; 153 | } else { 154 | branch = 4; 155 | } 156 | 157 | r = nf->navg; 158 | if (nf->navg > 2 && branch != 4) 159 | r *= r; 160 | 161 | nf->alo += (nf->lo - nf->alo) / r; 162 | nf->amid += (nf->mid - nf->amid) / r; 163 | nf->ahi += (nf->hi - nf->ahi) / r; 164 | nf->alolo += (nf->lo*nf->lo - nf->alolo) / r; 165 | nf->ahihi += (nf->hi*nf->hi - nf->ahihi) / r; 166 | 167 | if (rxp->ntp_stratum == 0) 168 | nf->trust = 0.0; 169 | else if (rxp->ntp_stratum == 15) 170 | nf->trust = 0.0; 171 | else 172 | nf->trust = 1.0 / rxp->ntp_stratum; 173 | 174 | Put(ocx, OCX_TRACE, 175 | "NTP_Filter %s %s %d %.3e %.3e %.3e %.3e %.3e %.3e\n", 176 | np->hostname, np->ip, branch, 177 | nf->lo, nf->mid, nf->hi, 178 | lo_lim, nf->amid, hi_lim); 179 | 180 | if (np->combiner->func != NULL) 181 | np->combiner->func(ocx, np->combiner, 182 | nf->trust, nf->lo, nf->mid, nf->hi); 183 | } 184 | 185 | void 186 | NF_New(struct ntp_peer *np) 187 | { 188 | struct ntp_filter *nf; 189 | 190 | ALLOC_OBJ(nf, NTP_FILTER_MAGIC); 191 | AN(nf); 192 | np->filter_func = nf_filter; 193 | np->filter_priv = nf; 194 | } 195 | 196 | void 197 | NF_Init(void) 198 | { 199 | Param_Register(ntp_filter_param_table); 200 | } 201 | -------------------------------------------------------------------------------- /ntp_packet.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * NTP packet (de)serialization 27 | * ============================ 28 | * 29 | * 0 1 2 3 30 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 31 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | * 0 |LI | VN |Mode | Stratum | Poll | Precision | 33 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | * 4 | Root Delay | 35 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | * 8 | Root Dispersion | 37 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | * 12 | Reference ID | 39 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | * 16 | | 41 | * + Reference Timestamp (64) + 42 | * | | 43 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 44 | * 24 | | 45 | * + Origin Timestamp (64) + 46 | * | | 47 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 | * 32 | | 49 | * + Receive Timestamp (64) + 50 | * | | 51 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 52 | * 40 | | 53 | * + Transmit Timestamp (64) + 54 | * | | 55 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 56 | * 57 | */ 58 | 59 | #include 60 | #include 61 | 62 | #include "ntimed.h" 63 | #include "ntp.h" 64 | #include "ntimed_endian.h" 65 | 66 | /* 67 | * Seconds between 1900 (NTP epoch) and 1970 (UNIX epoch). 68 | * 17 is the number of leapdays. 69 | */ 70 | #define NTP_UNIX (((1970U - 1900U) * 365U + 17U) * 24U * 60U * 60U) 71 | 72 | /********************************************************************** 73 | * Picking a NTP packet apart in a safe, byte-order agnostic manner 74 | */ 75 | 76 | static void 77 | ntp64_2ts(struct timestamp *ts, const uint8_t *ptr) 78 | { 79 | 80 | INIT_OBJ(ts, TIMESTAMP_MAGIC); 81 | ts->sec = Be32dec(ptr) - NTP_UNIX; 82 | ts->frac = (uint64_t)Be32dec(ptr + 4) << 32ULL; 83 | } 84 | 85 | static void 86 | ntp32_2ts(struct timestamp *ts, const uint8_t *ptr) 87 | { 88 | 89 | INIT_OBJ(ts, TIMESTAMP_MAGIC); 90 | ts->sec = Be16dec(ptr); 91 | ts->frac = (uint64_t)Be16dec(ptr + 2) << 48ULL; 92 | } 93 | 94 | 95 | struct ntp_packet * 96 | NTP_Packet_Unpack(struct ntp_packet *np, void *ptr, ssize_t len) 97 | { 98 | uint8_t *p = ptr; 99 | 100 | AN(ptr); 101 | if (len != 48) { 102 | /* XXX: Diagnostic */ 103 | return (NULL); 104 | } 105 | 106 | if (np == NULL) { 107 | ALLOC_OBJ(np, NTP_PACKET_MAGIC); 108 | AN(np); 109 | } else { 110 | INIT_OBJ(np, NTP_PACKET_MAGIC); 111 | } 112 | 113 | np->ntp_leap = (enum ntp_leap)(p[0] >> 6); 114 | np->ntp_version = (p[0] >> 3) & 0x7; 115 | np->ntp_mode = (enum ntp_mode)(p[0] & 0x07); 116 | np->ntp_stratum = p[1]; 117 | np->ntp_poll = p[2]; 118 | np->ntp_precision = (int8_t)p[3]; 119 | ntp32_2ts(&np->ntp_delay, p + 4); 120 | ntp32_2ts(&np->ntp_dispersion, p + 8); 121 | memcpy(np->ntp_refid, p + 12, 4L); 122 | ntp64_2ts(&np->ntp_reference, p + 16); 123 | ntp64_2ts(&np->ntp_origin, p + 24); 124 | ntp64_2ts(&np->ntp_receive, p + 32); 125 | ntp64_2ts(&np->ntp_transmit, p + 40); 126 | return (np); 127 | } 128 | 129 | /********************************************************************** 130 | * Putting a NTP packet apart in a safe, byte-order agnostic manner 131 | */ 132 | 133 | static void 134 | ts_2ntp32(uint8_t *dst, const struct timestamp *ts) 135 | { 136 | 137 | CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC); 138 | assert(ts->sec < 65536); 139 | Be16enc(dst, (uint16_t)ts->sec); 140 | Be16enc(dst + 2, ts->frac >> 48ULL); 141 | } 142 | 143 | static void 144 | ts_2ntp64(uint8_t *dst, const struct timestamp *ts) 145 | { 146 | 147 | CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC); 148 | Be32enc(dst, ts->sec + NTP_UNIX); 149 | Be32enc(dst + 4, ts->frac >> 32ULL); 150 | } 151 | 152 | size_t 153 | NTP_Packet_Pack(void *ptr, ssize_t len, struct ntp_packet *np) 154 | { 155 | uint8_t *pbuf = ptr; 156 | 157 | AN(ptr); 158 | assert(len >= 48); 159 | CHECK_OBJ_NOTNULL(np, NTP_PACKET_MAGIC); 160 | assert(np->ntp_version < 8); 161 | assert(np->ntp_stratum < 15); 162 | 163 | pbuf[0] = (uint8_t)np->ntp_leap; 164 | pbuf[0] <<= 3; 165 | pbuf[0] |= np->ntp_version; 166 | pbuf[0] <<= 3; 167 | pbuf[0] |= (uint8_t)np->ntp_mode; 168 | pbuf[1] = np->ntp_stratum; 169 | pbuf[2] = np->ntp_poll; 170 | pbuf[3] = (uint8_t)np->ntp_precision; 171 | ts_2ntp32(pbuf + 4, &np->ntp_delay); 172 | ts_2ntp32(pbuf + 8, &np->ntp_dispersion); 173 | memcpy(pbuf + 12, np->ntp_refid, 4L); 174 | ts_2ntp64(pbuf + 16, &np->ntp_reference); 175 | ts_2ntp64(pbuf + 24, &np->ntp_origin); 176 | ts_2ntp64(pbuf + 32, &np->ntp_receive); 177 | 178 | TB_Now(&np->ntp_transmit); 179 | ts_2ntp64(pbuf + 40, &np->ntp_transmit); 180 | 181 | /* Reverse again, to avoid subsequent trouble from rounding. */ 182 | ntp64_2ts(&np->ntp_transmit, pbuf + 40); 183 | 184 | return (48); 185 | } 186 | -------------------------------------------------------------------------------- /ntp_peer.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "ntimed.h" 35 | #include "udp.h" 36 | #include "ntp.h" 37 | 38 | struct ntp_peer * 39 | NTP_Peer_New(const char *hostname, const void *sa, unsigned salen) 40 | { 41 | struct ntp_peer *np; 42 | char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 43 | 44 | AZ(getnameinfo(sa, salen, hbuf, sizeof(hbuf), sbuf, 45 | sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)); 46 | 47 | ALLOC_OBJ(np, NTP_PEER_MAGIC); 48 | AN(np); 49 | 50 | np->sa_len = salen; 51 | np->sa = calloc(1, np->sa_len); 52 | AN(np->sa); 53 | memcpy(np->sa, sa, np->sa_len); 54 | 55 | np->hostname = strdup(hostname); 56 | AN(np->hostname); 57 | np->ip = strdup(hbuf); 58 | AN(np->ip); 59 | 60 | ALLOC_OBJ(np->tx_pkt, NTP_PACKET_MAGIC); 61 | AN(np->tx_pkt); 62 | 63 | ALLOC_OBJ(np->rx_pkt, NTP_PACKET_MAGIC); 64 | AN(np->rx_pkt); 65 | 66 | NTP_Tool_Client_Req(np->tx_pkt); 67 | 68 | return (np); 69 | } 70 | 71 | struct ntp_peer * 72 | NTP_Peer_NewLookup(struct ocx *ocx, const char *hostname) 73 | { 74 | struct addrinfo hints, *res0; 75 | int error; 76 | struct ntp_peer *np; 77 | 78 | memset(&hints, 0, sizeof hints); 79 | hints.ai_family = PF_UNSPEC; 80 | hints.ai_socktype = SOCK_DGRAM; 81 | error = getaddrinfo(hostname, "ntp", &hints, &res0); 82 | if (error) 83 | Fail(ocx, 0, "hostname '%s', port 'ntp': %s\n", 84 | hostname, gai_strerror(error)); 85 | 86 | np = NTP_Peer_New(hostname, res0->ai_addr, res0->ai_addrlen); 87 | freeaddrinfo(res0); 88 | return (np); 89 | } 90 | 91 | void 92 | NTP_Peer_Destroy(struct ntp_peer *np) 93 | { 94 | 95 | CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); 96 | free(np->sa); 97 | free(np->hostname); 98 | free(np->ip); 99 | free(np->tx_pkt); 100 | free(np->rx_pkt); 101 | FREE_OBJ(np); 102 | } 103 | 104 | int 105 | NTP_Peer_Poll(struct ocx *ocx, const struct udp_socket *usc, 106 | const struct ntp_peer *np, double tmo) 107 | { 108 | char buf[100]; 109 | size_t len; 110 | struct sockaddr_storage rss; 111 | socklen_t rssl; 112 | ssize_t l; 113 | int i; 114 | struct timestamp t0, t1, t2; 115 | double d; 116 | 117 | AN(usc); 118 | CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); 119 | assert(tmo > 0.0 && tmo <= 1.0); 120 | 121 | len = NTP_Packet_Pack(buf, sizeof buf, np->tx_pkt); 122 | 123 | l = Udp_Send(ocx, usc, np->sa, np->sa_len, buf, len); 124 | if (l != (ssize_t)len) { 125 | Debug(ocx, "Tx peer %s %s got %zd (%s)\n", 126 | np->hostname, np->ip, l, strerror(errno)); 127 | return (0); 128 | } 129 | 130 | (void)TB_Now(&t0); 131 | 132 | while (1) { 133 | (void)TB_Now(&t1); 134 | d = TS_Diff(&t1, &t0); 135 | 136 | i = UdpTimedRx(ocx, usc, np->sa->sa_family, &rss, &rssl, &t2, 137 | buf, sizeof buf, tmo - d); 138 | 139 | if (i == 0) 140 | return (0); 141 | 142 | if (i < 0) 143 | Fail(ocx, 1, "Rx failed\n"); 144 | 145 | if (i != 48) { 146 | Debug(ocx, "Rx peer %s %s got len=%d\n", 147 | np->hostname, np->ip, i); 148 | continue; 149 | } 150 | 151 | /* Ignore packets from other hosts */ 152 | if (!SA_Equal(np->sa, np->sa_len, &rss, rssl)) 153 | continue; 154 | 155 | AN(NTP_Packet_Unpack(np->rx_pkt, buf, i)); 156 | np->rx_pkt->ts_rx = t2; 157 | 158 | /* Ignore packets which are not replies to our packet */ 159 | if (TS_Diff(&np->tx_pkt->ntp_transmit, 160 | &np->rx_pkt->ntp_origin) != 0.0) { 161 | continue; 162 | } 163 | 164 | return (1); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /ntp_peerset.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * A peer-set is the total set of NTP servers we keep track of. 27 | * 28 | * The set is composed of groups, each of which is all the IP# we 29 | * get from resolving a single argument. 30 | * 31 | * Within each group we poll a maximum of one ${param} servers, 32 | * picking the best. 33 | * 34 | * Across the set we keep an hairy eyeball for servers appearing 35 | * more than once, because it would deceive our quorum. Spotting 36 | * the same IP# is trivial, multihomed servers can be spotted on 37 | * {stratum,refid,reftime} triplet. 38 | * 39 | */ 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "ntimed.h" 48 | #include "ntp.h" 49 | 50 | struct ntp_group { 51 | unsigned magic; 52 | #define NTP_GROUP_MAGIC 0xdd5f58de 53 | TAILQ_ENTRY(ntp_group) list; 54 | 55 | char *hostname; 56 | int npeer; 57 | }; 58 | 59 | struct ntp_peerset { 60 | unsigned magic; 61 | #define NTP_PEERSET_MAGIC 0x0bf873d0 62 | 63 | TAILQ_HEAD(,ntp_peer) head; 64 | int npeer; 65 | 66 | TAILQ_HEAD(,ntp_group) group; 67 | int ngroup; 68 | 69 | struct udp_socket *usc; 70 | double t0; 71 | double init_duration; 72 | double poll_period; 73 | double init_packets; 74 | }; 75 | 76 | /**********************************************************************/ 77 | 78 | struct ntp_peerset * 79 | NTP_PeerSet_New(struct ocx *ocx) 80 | { 81 | struct ntp_peerset *nps; 82 | 83 | (void)ocx; 84 | ALLOC_OBJ(nps, NTP_PEERSET_MAGIC); 85 | AN(nps); 86 | 87 | TAILQ_INIT(&nps->head); 88 | TAILQ_INIT(&nps->group); 89 | return (nps); 90 | } 91 | 92 | /********************************************************************** 93 | * Iterator helpers 94 | */ 95 | 96 | struct ntp_peer * 97 | NTP_PeerSet_Iter0(const struct ntp_peerset *nps) 98 | { 99 | 100 | CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); 101 | return (TAILQ_FIRST(&nps->head)); 102 | } 103 | 104 | struct ntp_peer * 105 | NTP_PeerSet_IterN(const struct ntp_peerset *nps, const struct ntp_peer *np) 106 | { 107 | 108 | CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); 109 | CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); 110 | return (TAILQ_NEXT(np, list)); 111 | } 112 | 113 | /**********************************************************************/ 114 | 115 | static int 116 | ntp_peerset_fillgroup(struct ocx *ocx, struct ntp_peerset *nps, 117 | struct ntp_group *ng, const char *lookup) 118 | { 119 | struct addrinfo hints, *res, *res0; 120 | int error, n = 0; 121 | struct ntp_peer *np, *np2; 122 | 123 | CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); 124 | CHECK_OBJ_NOTNULL(ng, NTP_GROUP_MAGIC); 125 | 126 | memset(&hints, 0, sizeof hints); 127 | hints.ai_family = PF_UNSPEC; 128 | hints.ai_socktype = SOCK_DGRAM; 129 | error = getaddrinfo(lookup, "ntp", &hints, &res0); 130 | if (error) 131 | Fail(ocx, 1, "hostname '%s', port 'ntp': %s\n", 132 | lookup, gai_strerror(error)); 133 | for (res = res0; res; res = res->ai_next) { 134 | if (res->ai_family != AF_INET && res->ai_family != AF_INET6) 135 | continue; 136 | np = NTP_Peer_New(ng->hostname, res->ai_addr, res->ai_addrlen); 137 | AN(np); 138 | TAILQ_FOREACH(np2, &nps->head, list) 139 | if (SA_Equal(np->sa, np->sa_len, np2->sa, np2->sa_len)) 140 | break; 141 | if (np2 != NULL) { 142 | /* All duplicates point to the same "master" */ 143 | np->state = NTP_STATE_DUPLICATE; 144 | np->other = np2->other; 145 | if (np->other == NULL) 146 | np->other = np2; 147 | TAILQ_INSERT_TAIL(&nps->head, np, list); 148 | Debug(ocx, "Peer {%s %s} is duplicate of {%s %s}\n", 149 | np->hostname, np->ip, np2->hostname, np2->ip); 150 | } else { 151 | np->state = NTP_STATE_NEW; 152 | TAILQ_INSERT_HEAD(&nps->head, np, list); 153 | } 154 | nps->npeer++; 155 | np->group = ng; 156 | ng->npeer++; 157 | n++; 158 | } 159 | freeaddrinfo(res0); 160 | return (n); 161 | } 162 | 163 | /**********************************************************************/ 164 | 165 | static struct ntp_group * 166 | ntp_peerset_add_group(struct ntp_peerset *nps, const char *name) 167 | { 168 | struct ntp_group *ng; 169 | 170 | ALLOC_OBJ(ng, NTP_GROUP_MAGIC); 171 | AN(ng); 172 | ng->hostname = strdup(name); 173 | AN(ng->hostname); 174 | TAILQ_INSERT_TAIL(&nps->group, ng, list); 175 | nps->ngroup++; 176 | return (ng); 177 | } 178 | 179 | /********************************************************************** 180 | * Add a peer with a specific hostname+ip combination without actually 181 | * resolving the hostname. 182 | */ 183 | 184 | void 185 | NTP_PeerSet_AddSim(struct ocx *ocx, struct ntp_peerset *nps, 186 | const char *hostname, const char *ip) 187 | { 188 | struct ntp_group *ng; 189 | 190 | CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); 191 | TAILQ_FOREACH(ng, &nps->group, list) 192 | if (!strcasecmp(ng->hostname, hostname)) 193 | break; 194 | if (ng == NULL) 195 | ng = ntp_peerset_add_group(nps, hostname); 196 | assert(ntp_peerset_fillgroup(ocx, nps, ng, ip) == 1); 197 | } 198 | 199 | /********************************************************************** 200 | * Create a new group and add whatever peers its hostname resolves to 201 | */ 202 | 203 | int 204 | NTP_PeerSet_Add(struct ocx *ocx, struct ntp_peerset *nps, const char *hostname) 205 | { 206 | struct ntp_group *ng; 207 | 208 | CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); 209 | 210 | TAILQ_FOREACH(ng, &nps->group, list) 211 | if (!strcasecmp(ng->hostname, hostname)) 212 | Fail(ocx, 0, "hostname %s is duplicated\n", hostname); 213 | 214 | ng = ntp_peerset_add_group(nps, hostname); 215 | 216 | if (ntp_peerset_fillgroup(ocx, nps, ng, hostname) == 0) 217 | Fail(ocx, 0, "hostname %s no IP# found.\n", hostname); 218 | 219 | return (ng->npeer); 220 | } 221 | 222 | /********************************************************************** 223 | * This function is responsible for polling the peers in the set. 224 | */ 225 | 226 | static enum todo_e __match_proto__(todo_f) 227 | ntp_peerset_poll(struct ocx *ocx, struct todolist *tdl, void *priv) 228 | { 229 | struct ntp_peerset *nps; 230 | struct ntp_peer *np; 231 | double d, dt; 232 | 233 | (void)ocx; 234 | CAST_OBJ_NOTNULL(nps, priv, NTP_PEERSET_MAGIC); 235 | AN(tdl); 236 | 237 | np = TAILQ_FIRST(&nps->head); 238 | if (np == NULL) 239 | return(TODO_DONE); 240 | 241 | CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); 242 | TAILQ_REMOVE(&nps->head, np, list); 243 | TAILQ_INSERT_TAIL(&nps->head, np, list); 244 | 245 | d = nps->poll_period / nps->npeer; 246 | if (nps->t0 < nps->init_duration) { 247 | dt = exp( 248 | log(nps->init_duration) / (nps->init_packets * nps->npeer)); 249 | if (nps->t0 * dt < nps->init_duration) 250 | d = nps->t0 * dt - nps->t0; 251 | } 252 | nps->t0 += d; 253 | TODO_ScheduleRel(tdl, ntp_peerset_poll, nps, d, 0.0, "NTP_PeerSet"); 254 | if (NTP_Peer_Poll(ocx, nps->usc, np, 0.8)) { 255 | if (np->filter_func != NULL) 256 | np->filter_func(ocx, np); 257 | } 258 | 259 | return (TODO_OK); 260 | } 261 | 262 | /********************************************************************** 263 | * This function will be responsible for maintaining the peers in the set 264 | * 265 | * XXX: Pick only the best N (3?) servers from any hostname as active. 266 | * XXX: Re-lookup hostnames on periodic basis to keep the set current 267 | * XXX: Implement (a future) pool.ntp.org load-balancing protocol 268 | */ 269 | 270 | static enum todo_e 271 | ntp_peerset_herd(struct ocx *ocx, struct todolist *tdl, void *priv) 272 | { 273 | (void)ocx; 274 | (void)tdl; 275 | (void)priv; 276 | return (TODO_DONE); 277 | } 278 | 279 | /**********************************************************************/ 280 | 281 | static uintptr_t poll_hdl; 282 | static uintptr_t herd_hdl; 283 | 284 | void 285 | NTP_PeerSet_Poll(struct ocx *ocx, struct ntp_peerset *nps, 286 | struct udp_socket *usc, 287 | struct todolist *tdl) 288 | { 289 | struct ntp_peer *np; 290 | 291 | (void)ocx; 292 | CHECK_OBJ_NOTNULL(nps, NTP_PEERSET_MAGIC); 293 | AN(usc); 294 | AN(tdl); 295 | 296 | TAILQ_FOREACH(np, &nps->head, list) 297 | np->state = NTP_STATE_NEW; 298 | nps->usc = usc; 299 | nps->t0 = 1.0; 300 | nps->init_duration = 64.; 301 | nps->init_packets = 6.; 302 | nps->poll_period = 64.; 303 | 304 | if (poll_hdl != 0) 305 | TODO_Cancel(tdl, &poll_hdl); 306 | poll_hdl = TODO_ScheduleRel(tdl, ntp_peerset_poll, nps, 0.0, 0.0, 307 | "NTP_PeerSet Poll"); 308 | 309 | if (herd_hdl != 0) 310 | TODO_Cancel(tdl, &herd_hdl); 311 | herd_hdl = TODO_ScheduleRel(tdl, ntp_peerset_herd, nps, 312 | 15. * 60. / nps->ngroup, 0.0, "NTP_PeerSet Herd"); 313 | 314 | } 315 | -------------------------------------------------------------------------------- /ntp_tbl.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Table generator file for tables related to the NTP protocol. 27 | */ 28 | 29 | /*lint -save -e525 -e539 */ 30 | #ifdef NTP_MODE 31 | NTP_MODE(0, mode0, MODE0) 32 | NTP_MODE(1, symact, SYMACT) 33 | NTP_MODE(2, sympas, SYMPAS) 34 | NTP_MODE(3, client, CLIENT) 35 | NTP_MODE(4, server, SERVER) 36 | NTP_MODE(5, bcast, BCAST) 37 | NTP_MODE(6, ctrl, CTRL) 38 | NTP_MODE(7, mode7, MODE7) 39 | #endif 40 | 41 | #ifdef NTP_LEAP 42 | NTP_LEAP(0, none, NONE) 43 | NTP_LEAP(1, ins, INS) 44 | NTP_LEAP(2, del, DEL) 45 | NTP_LEAP(3, unknown, UNKNOWN) 46 | #endif 47 | 48 | #ifdef NTP_STATE 49 | NTP_STATE(0, new, NEW, "Newly configured peer.") 50 | NTP_STATE(1, active, ACTIVE, "Good (and used) peer.") 51 | NTP_STATE(2, available, AVAILABLE, "Not good enough peer.") 52 | NTP_STATE(3, unsynchronized, UNSYNCHRONIZED, "Bad peer.") 53 | NTP_STATE(4, unresponsive, UNRESPONSIVE, "Peer does not respond.") 54 | NTP_STATE(5, unreachable, UNREACHABLE, "Peer cannot be reached.") 55 | NTP_STATE(6, multihome, MULTIHOME, "Copy of multihomed peer.") 56 | NTP_STATE(7, duplicate, DUPLICATE, "Duplicate peer.") 57 | #endif 58 | 59 | /*lint -restore */ 60 | -------------------------------------------------------------------------------- /ntp_tools.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * NTP tools 27 | * ========= 28 | * 29 | * 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "ntimed.h" 38 | #include "ntp.h" 39 | #include "ntimed_endian.h" 40 | 41 | /********************************************************************** 42 | * Build a standard client query packet 43 | */ 44 | 45 | void 46 | NTP_Tool_Client_Req(struct ntp_packet *np) 47 | { 48 | AN(np); 49 | INIT_OBJ(np, NTP_PACKET_MAGIC); 50 | 51 | np->ntp_leap = NTP_LEAP_UNKNOWN; 52 | np->ntp_version = 4; 53 | np->ntp_mode = NTP_MODE_CLIENT; 54 | np->ntp_stratum = 0; 55 | np->ntp_poll = 4; 56 | np->ntp_precision = -6; 57 | INIT_OBJ(&np->ntp_delay, TIMESTAMP_MAGIC); 58 | np->ntp_delay.sec = 1; 59 | INIT_OBJ(&np->ntp_dispersion, TIMESTAMP_MAGIC); 60 | np->ntp_dispersion.sec = 1; 61 | INIT_OBJ(&np->ntp_reference, TIMESTAMP_MAGIC); 62 | INIT_OBJ(&np->ntp_origin, TIMESTAMP_MAGIC); 63 | INIT_OBJ(&np->ntp_receive, TIMESTAMP_MAGIC); 64 | } 65 | 66 | /********************************************************************** 67 | * Format a NTP packet in a standardized layout for subsequent parsing. 68 | * 69 | * We dump absolute timestamps relative to the origin timestamp. 70 | * 71 | * XXX: Nanosecond precision is enough for everybody. 72 | */ 73 | 74 | static void __printflike(3, 4) 75 | bxprintf(char **bp, const char *e, const char *fmt, ...) 76 | { 77 | va_list ap; 78 | 79 | assert(*bp < e); 80 | va_start(ap, fmt); 81 | *bp += vsnprintf(*bp, (unsigned)(e - *bp), fmt, ap); 82 | va_end(ap); 83 | } 84 | 85 | void 86 | NTP_Tool_Format(char *p, ssize_t len, const struct ntp_packet *pkt) 87 | { 88 | char *e; 89 | char buf[40]; 90 | 91 | AN(p); 92 | assert(len > 0); 93 | CHECK_OBJ_NOTNULL(pkt, NTP_PACKET_MAGIC); 94 | 95 | e = p + len; 96 | 97 | bxprintf(&p, e, "[%d", pkt->ntp_leap); 98 | bxprintf(&p, e, " %u", pkt->ntp_version); 99 | 100 | bxprintf(&p, e, " %d", pkt->ntp_mode); 101 | 102 | bxprintf(&p, e, " %3u", pkt->ntp_stratum); 103 | 104 | bxprintf(&p, e, " %3u", pkt->ntp_poll); 105 | 106 | bxprintf(&p, e, " %4d", pkt->ntp_precision); 107 | 108 | TS_Format(buf, sizeof buf, &pkt->ntp_delay); 109 | bxprintf(&p, e, " %s", buf); assert(p < e); 110 | 111 | TS_Format(buf, sizeof buf, &pkt->ntp_dispersion); 112 | bxprintf(&p, e, " %s", buf); assert(p < e); 113 | 114 | bxprintf(&p, e, " 0x%02x%02x%02x%02x", 115 | pkt->ntp_refid[0], pkt->ntp_refid[1], 116 | pkt->ntp_refid[2], pkt->ntp_refid[3]); 117 | 118 | bxprintf(&p, e, " %.9f", 119 | TS_Diff(&pkt->ntp_reference, &pkt->ntp_origin)); 120 | 121 | TS_Format(buf, sizeof buf, &pkt->ntp_origin); 122 | bxprintf(&p, e, " %s", buf); assert(p < e); 123 | 124 | bxprintf(&p, e, " %.9f", 125 | TS_Diff(&pkt->ntp_receive, &pkt->ntp_origin)); 126 | 127 | bxprintf(&p, e, " %.9f", 128 | TS_Diff(&pkt->ntp_transmit, &pkt->ntp_receive)); 129 | 130 | if (pkt->ts_rx.sec && pkt->ts_rx.frac) { 131 | bxprintf(&p, e, " %.9f]", 132 | TS_Diff(&pkt->ts_rx, &pkt->ntp_transmit)); 133 | } else { 134 | bxprintf(&p, e, " %.9f]", 0.0); 135 | } 136 | assert(p < e); 137 | } 138 | 139 | /********************************************************************** 140 | * Scan a packet in NTP_Tool_Format layout. 141 | */ 142 | 143 | int 144 | NTP_Tool_Scan(struct ntp_packet *pkt, const char *buf) 145 | { 146 | unsigned u_fields[8]; 147 | double d_fields[7]; 148 | char cc; 149 | int i; 150 | 151 | i = sscanf(buf, 152 | "[%u %u %u %u %u %lf %lf %lf %x %lf %u.%u %lf %lf %lf%c", 153 | u_fields + 0, /* NTP_leap */ 154 | u_fields + 1, /* NTP_version */ 155 | u_fields + 2, /* NTP_mode */ 156 | u_fields + 3, /* NTP_stratum */ 157 | u_fields + 4, /* NTP_poll */ 158 | d_fields + 0, /* NTP_precision */ 159 | d_fields + 1, /* NTP_delay */ 160 | d_fields + 2, /* NTP_dispersion */ 161 | u_fields + 5, /* NTP_refid */ 162 | d_fields + 3, /* NTP_reference - NTP_origin */ 163 | u_fields + 6, /* NTP_origin:sec */ 164 | u_fields + 7, /* NTP_origin:nsec */ 165 | d_fields + 4, /* NTP_receive - NTP_origin */ 166 | d_fields + 5, /* NTP_transmit - NTP_receive */ 167 | d_fields + 6, /* ts_rx - NTP_transmit*/ 168 | &cc); 169 | if (i != 16 || cc != ']') 170 | return (-1); 171 | 172 | INIT_OBJ(pkt, NTP_PACKET_MAGIC); 173 | pkt->ntp_leap = (enum ntp_leap)u_fields[0]; 174 | pkt->ntp_version = (uint8_t)u_fields[1]; 175 | pkt->ntp_mode = (enum ntp_mode)u_fields[2]; 176 | pkt->ntp_stratum = (uint8_t)u_fields[3]; 177 | pkt->ntp_poll = (uint8_t)u_fields[4]; 178 | pkt->ntp_precision = (int8_t)floor(d_fields[0]); 179 | TS_Double(&pkt->ntp_delay, d_fields[1]); 180 | TS_Double(&pkt->ntp_dispersion, d_fields[2]); 181 | Be32enc(pkt->ntp_refid, u_fields[5]); 182 | 183 | TS_Nanosec(&pkt->ntp_origin, u_fields[6], u_fields[7]); 184 | 185 | pkt->ntp_reference = pkt->ntp_origin; 186 | TS_Add(&pkt->ntp_reference, d_fields[3]); 187 | 188 | pkt->ntp_receive = pkt->ntp_origin; 189 | TS_Add(&pkt->ntp_receive, d_fields[4]); 190 | 191 | pkt->ntp_transmit = pkt->ntp_receive; 192 | TS_Add(&pkt->ntp_transmit, d_fields[5]); 193 | 194 | if (d_fields[6] != 0.0) { 195 | pkt->ts_rx = pkt->ntp_transmit; 196 | TS_Add(&pkt->ts_rx, d_fields[6]); 197 | } else 198 | INIT_OBJ(&pkt->ts_rx, TIMESTAMP_MAGIC); 199 | return (0); 200 | } 201 | -------------------------------------------------------------------------------- /ocx_stdio.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Operational Context STDIO 27 | * ========================= 28 | * 29 | * "The most effective debugging tool is still careful thought, 30 | * coupled with judiciously placed print statements." 31 | * -- Brian Kernighan, "Unix for Beginners" (1979) 32 | * 33 | * The problem with print statements is where they end up. For instance 34 | * in a server with a CLI interface, you want the print statements to go 35 | * to the CLI session which called the code. 36 | * 37 | * An "Operational Context" is a back-pointer to where the print statement 38 | * should end up. 39 | * 40 | * We operate with three "channels", DIAG, TRACE and DEBUG. 41 | * 42 | * DIAG is mandatory output, error messages, diagnostics etc. 43 | * This should always end up where the action was initiated. 44 | * 45 | * DEBUG is optional output which may be supressed. 46 | * This should go where DIAG goes, unless specifically redirected 47 | * by the operator. 48 | * 49 | * TRACE is data collection, statistics etc. 50 | * In general this goes nowhere unless configured to end up 51 | * somewhere. 52 | * 53 | * About this implementation: 54 | * 55 | * This is a very naive implementation spitting things out to stdout/stderr, 56 | * knowing that we are a single threaded program. 57 | * 58 | * XXX: Pull in sbufs to do it right. 59 | */ 60 | 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | 68 | #include "ntimed.h" 69 | 70 | static FILE *tracefile; 71 | 72 | static FILE * 73 | getdst(enum ocx_chan chan) 74 | { 75 | if (chan == OCX_DIAG) 76 | return (stderr); 77 | if (chan == OCX_TRACE) 78 | return (tracefile); 79 | if (chan == OCX_DEBUG) 80 | return (stdout); 81 | WRONG("Wrong ocx_chan"); 82 | NEEDLESS_RETURN(NULL); 83 | } 84 | 85 | static void __match_proto__() 86 | putv(struct ocx *ocx, enum ocx_chan chan, const char *fmt, va_list ap) 87 | { 88 | FILE *dst = getdst(chan); 89 | va_list ap2; 90 | 91 | va_copy(ap2, ap); 92 | AZ(ocx); 93 | if (dst != NULL) 94 | (void)vfprintf(dst, fmt, ap); 95 | if (chan == OCX_DIAG) 96 | vsyslog(LOG_ERR, fmt, ap2); 97 | va_end(ap2); 98 | } 99 | 100 | /********************************************************************** 101 | * XXX: take strftime format string to chop tracefiles in time. 102 | */ 103 | 104 | void 105 | ArgTracefile(const char *fn) 106 | { 107 | 108 | if (tracefile != NULL && tracefile != stdout) { 109 | AZ(fclose(tracefile)); 110 | tracefile = NULL; 111 | } 112 | 113 | if (fn == NULL) 114 | return; 115 | 116 | if (!strcmp(fn, "-")) { 117 | tracefile = stdout; 118 | return; 119 | } 120 | 121 | tracefile = fopen(fn, "w"); 122 | if (tracefile == NULL) 123 | Fail(NULL, 1, "Could not open '%s' for writing", fn); 124 | setbuf(tracefile, NULL); 125 | } 126 | 127 | /********************************************************************** 128 | * XXX: The stuff below is generic and really ought to be in ocx.c on 129 | * XXX: its own. 130 | */ 131 | 132 | void 133 | Put(struct ocx *ocx, enum ocx_chan chan, const char *fmt, ...) 134 | { 135 | va_list ap; 136 | 137 | AZ(ocx); 138 | va_start(ap, fmt); 139 | putv(ocx, chan, fmt, ap); 140 | va_end(ap); 141 | } 142 | 143 | void 144 | PutHex(struct ocx *ocx, enum ocx_chan chan, const void *ptr, ssize_t len) 145 | { 146 | const uint8_t *p = ptr; 147 | const char *s = ""; 148 | 149 | AN(ptr); 150 | assert(len >= 0); 151 | 152 | while(len--) { 153 | Put(ocx, chan, "%s%02x", s, *p++); 154 | s = " "; 155 | } 156 | } 157 | 158 | void 159 | Fail(struct ocx *ocx, int err, const char *fmt, ...) 160 | { 161 | va_list ap; 162 | 163 | if (err) 164 | err = errno; 165 | Put(ocx, OCX_DIAG, "Failure: "); 166 | va_start(ap, fmt); 167 | putv(ocx, OCX_DIAG, fmt, ap); 168 | va_end(ap); 169 | Put(ocx, OCX_DIAG, "\n"); 170 | if (err) 171 | Put(ocx, OCX_DIAG, "errno = %d (%s)\n", err, strerror(err)); 172 | exit(1); 173 | } 174 | -------------------------------------------------------------------------------- /param.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Routines to deal with parameters 27 | * 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include "ntimed.h" 35 | 36 | static TAILQ_HEAD(, param_tbl) param_tbl = TAILQ_HEAD_INITIALIZER(param_tbl); 37 | 38 | void 39 | Param_Register(struct param_tbl *pt) 40 | { 41 | 42 | for (;pt->name != NULL; pt++) 43 | TAILQ_INSERT_TAIL(¶m_tbl, pt, list); 44 | } 45 | 46 | static void 47 | param_wrapline(struct ocx *ocx, const char *b) 48 | { 49 | int n = 0; 50 | const char *e, *w; 51 | const int tabs = 8; 52 | const int wrap_at = 64; 53 | 54 | AN(b); 55 | Put(ocx, OCX_DIAG, "\t"); 56 | e = strchr(b, '\0'); 57 | while (b < e) { 58 | if (!isspace((int)*b)) { 59 | Put(ocx, OCX_DIAG, "%c", *b); 60 | b++; 61 | n++; 62 | } else if (*b == '\t') { 63 | do { 64 | Put(ocx, OCX_DIAG, " "); 65 | n++; 66 | } while ((n % tabs) != 1); 67 | b++; 68 | } else if (*b == '\n') { 69 | Put(ocx, OCX_DIAG, "\n"); 70 | param_wrapline(ocx, b + 1); 71 | return; 72 | } else { 73 | assert (*b == ' '); 74 | for (w = b + 1; w < e; w++) 75 | if (isspace((int)*w)) 76 | break; 77 | if (n + (w - b) < wrap_at) { 78 | Put(ocx, OCX_DIAG, "%.*s", (int)(w - b), b); 79 | n += (w - b); 80 | b = w; 81 | } else { 82 | Put(ocx, OCX_DIAG, "\n"); 83 | param_wrapline(ocx, b + 1); 84 | return; 85 | } 86 | } 87 | } 88 | } 89 | 90 | void 91 | Param_Tweak(struct ocx *ocx, const char *arg) 92 | { 93 | struct param_tbl *pt; 94 | const char *q; 95 | char *r; 96 | double d; 97 | size_t l; 98 | 99 | if (!strcmp(arg, "?")) { 100 | Put(ocx, OCX_DIAG, "List of available parameters:\n"); 101 | TAILQ_FOREACH(pt, ¶m_tbl, list) 102 | Put(ocx, OCX_DIAG, "\t%s\n", pt->name); 103 | Fail(ocx, 0, "Stopping after parameter query.\n"); 104 | } 105 | 106 | q = strchr(arg, '='); 107 | if (q == NULL) { 108 | TAILQ_FOREACH(pt, ¶m_tbl, list) 109 | if (!strcmp(pt->name, arg)) 110 | break; 111 | if (pt == NULL) 112 | Fail(ocx, 0, "-p unknown parameter '%s' (try -p '?')", 113 | arg); 114 | Put(ocx, OCX_DIAG, "Parameter:\n\t%s\n", pt->name); 115 | Put(ocx, OCX_DIAG, "Minimum:\n\t%.3e\n", pt->min); 116 | Put(ocx, OCX_DIAG, "Maximum:\n\t%.3e\n", pt->max); 117 | Put(ocx, OCX_DIAG, "Default:\n\t%.3e\n", pt->def); 118 | Put(ocx, OCX_DIAG, "Description:\n"); 119 | param_wrapline(ocx, pt->doc); 120 | Put(ocx, OCX_DIAG, "\n\n"); 121 | Fail(ocx, 0, "Stopping after parameter query.\n"); 122 | } 123 | 124 | assert (q >= arg); 125 | l = (unsigned)(q - arg); 126 | 127 | TAILQ_FOREACH(pt, ¶m_tbl, list) { 128 | if (strlen(pt->name) != l) 129 | continue; 130 | if (!strncmp(pt->name, arg, l)) 131 | break; 132 | } 133 | if (pt == NULL) 134 | Fail(ocx, 0, "-p unknown parameter '%.*s' (try -p '?')", 135 | (int)(q - arg), arg); 136 | 137 | r = NULL; 138 | d = strtod(q + 1, &r); 139 | if (*r != '\0') 140 | Fail(ocx, 0, "-p '%.*s' bad value '%s'\n", 141 | (int)(q - arg), arg, q + 1); 142 | if (d < pt->min) 143 | Fail(ocx, 0, "-p '%.*s' below min value (%g)\n", 144 | (int)(q - arg), arg, pt->min); 145 | if (d > pt->max) 146 | Fail(ocx, 0, "-p '%.*s' above max value (%g)\n", 147 | (int)(q - arg), arg, pt->max); 148 | Put(ocx, OCX_DIAG, "# Tweak(%s -> %.3e)\n", arg, d); 149 | *(pt->val) = d; 150 | return; 151 | } 152 | 153 | void 154 | Param_Report(struct ocx *ocx, enum ocx_chan chan) 155 | { 156 | struct param_tbl *pt; 157 | 158 | TAILQ_FOREACH(pt, ¶m_tbl, list) 159 | Put(ocx, chan, "# param %s %g # min %g, max %g, default %g\n", 160 | pt->name, *pt->val, pt->min, pt->max, pt->def); 161 | } 162 | -------------------------------------------------------------------------------- /param_instance.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Instantiate a set of parameters 27 | * =============================== 28 | * 29 | * The #including file must #define the desired parameters from param_tbl.h 30 | * into existence (PARAM_INSTANCE), and #define the desired name 31 | * of the local parameter table (PARAM_TABLE_NAME), then include this 32 | * file. 33 | */ 34 | 35 | #define PARAM_INSTANCE(nam, vmin, vmax, vdef, docs) \ 36 | static double param_##nam = vdef; 37 | #include "param_tbl.h" 38 | #undef PARAM_INSTANCE 39 | 40 | /*lint -save -e785 */ 41 | static struct param_tbl PARAM_TABLE_NAME[] = { 42 | #define PARAM_INSTANCE(nam, vmin, vmax, vdef, docs) \ 43 | { .name = #nam, .val = ¶m_##nam, \ 44 | .min = vmin, .max = vmax, .def = vdef, .doc = docs }, 45 | #include "param_tbl.h" 46 | #undef PARAM_INSTANCE 47 | { .name = 0 } 48 | }; 49 | /*lint -restore */ 50 | -------------------------------------------------------------------------------- /param_tbl.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Parameter Tables 27 | * ================ 28 | * 29 | * All parameters tweakable with a -p argument is defined in this file 30 | * including their minimum, maximum and default values and a not too 31 | * terse documentation string. 32 | * 33 | * Things get instantiated from here by #include param_instance.h 34 | */ 35 | 36 | /*lint -save -e525 -e539 */ 37 | 38 | /* name, min, max, default, docs */ 39 | 40 | #ifdef PARAM_CLIENT 41 | PARAM_CLIENT(poll_rate, 16.0, 4096.0, 64.0, "") 42 | PARAM_CLIENT(foo, 16.0, 4096.0, 64.0, "") 43 | #endif 44 | 45 | #ifdef PARAM_NTP_FILTER 46 | 47 | PARAM_NTP_FILTER(ntp_filter_average, 48 | 3, 1e3, 20, 49 | "Exponential average divisor for average packet delays." 50 | " The value chosen is a compromise between gliding through congestion" 51 | "of common durations and reacting to large-scale routing changes" 52 | "in a timely manner." 53 | " In reality there is no way to tell the two apart." 54 | ) 55 | 56 | PARAM_NTP_FILTER(ntp_filter_threshold, 57 | 0.01, 10.0, 3.00, 58 | "Packet delays exceeding the average by this factor are untrustworthy." 59 | " Setting this too high increases noise from (mild) congestion." 60 | " Setting it too low throws away adequate timestamps." 61 | ) 62 | 63 | #endif 64 | 65 | /********************************************************************** 66 | * Parameters for pll_std.c 67 | */ 68 | 69 | #ifdef PARAM_PLL_STD 70 | 71 | PARAM_PLL_STD(pll_std_p_init, 72 | 1e-3, 0.50, 0.33, 73 | "Proportional term when PLL starts.\n\n" 74 | "Reducing this will make the PLL more resistant to measurement" 75 | " noise and jitter, but also makes it converge slower." 76 | " Increasing this will almost certainly cause oscillation." 77 | ) 78 | 79 | PARAM_PLL_STD(pll_std_i_init, 80 | 10, 1000, 60, 81 | "Initial P/I ratio when PLL starts.\n\n" 82 | "Reducing this speed up convergence, but risk overshoot." 83 | " Increasing this will slow convergence and reduce impact of noise." 84 | ) 85 | 86 | PARAM_PLL_STD(pll_std_capture_time, 87 | 20, 1e6, 300, 88 | "Capture time before stiffening PLL.\n\n" 89 | "After this many seconds, the PLL will start to stiffen the" 90 | " P and I terms to gain noise immunity." 91 | " Decreasing risks that initial frequency capture is not finished," 92 | " which will increase the offset-excursion." 93 | " Increasing just delays this stiffening." 94 | ) 95 | 96 | PARAM_PLL_STD(pll_std_stiffen_rate, 97 | 0.5, 1.0, 0.999, 98 | "Rate of PLL P/I term stiffening.\n\n" 99 | "The exponential stiffening per second of D and I terms." 100 | " Decreasing makes stiffening faster." 101 | " Increasing makes stiffening slower." 102 | ) 103 | 104 | PARAM_PLL_STD(pll_std_p_limit, 105 | 1e-6, 0.50, 3e-2, 106 | "Lower limit for Proportional term.\n\n" 107 | "Reducing this will make the PLL more resistent to noise," 108 | " but going too far it will not be able to steer the clock fast enough." 109 | " Increasing this makes the PLL more agile and prone to noise." 110 | ) 111 | 112 | #endif 113 | 114 | 115 | /*lint -restore */ 116 | -------------------------------------------------------------------------------- /pll_std.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Standard PLL 27 | * ============ 28 | * 29 | * (And the function pointer for accessing any PLL) 30 | */ 31 | 32 | #include 33 | 34 | #include "ntimed.h" 35 | 36 | #define PARAM_PLL_STD PARAM_INSTANCE 37 | #define PARAM_TABLE_NAME pll_std_param_table 38 | #include "param_instance.h" 39 | #undef PARAM_TABLE_NAME 40 | #undef PARAM_PLL_STD 41 | 42 | static double pll_integrator; 43 | static struct timestamp pll_last_time; 44 | static int pll_mode; 45 | static double pll_a, pll_b; 46 | static struct timestamp pll_t0; 47 | static int pll_generation; 48 | 49 | static void __match_proto__(pll_f) 50 | pll_std(struct ocx *ocx, double offset, double weight) 51 | { 52 | double p_term, dur, dt, rt; 53 | double used_a, used_b; 54 | struct timestamp t0; 55 | 56 | TB_Now(&t0); 57 | p_term = 0.0; 58 | dur = .0; 59 | dt = 0; 60 | used_a = used_b = 0; 61 | 62 | if (pll_generation != TB_generation) { 63 | pll_mode = 0; 64 | pll_generation = TB_generation; 65 | } 66 | 67 | switch (pll_mode) { 68 | 69 | case 0: /* Startup */ 70 | 71 | pll_t0 = t0; 72 | pll_mode = 1; 73 | pll_a = param_pll_std_p_init; 74 | pll_b = 0.0; 75 | break; 76 | 77 | case 1: /* Wait until we have a good estimate, then step */ 78 | 79 | rt = TS_Diff(&t0, &pll_t0); 80 | if (rt > 2.0 && weight > 3) { // XXX param 81 | if (fabs(offset) > 1e-3) // XXX param 82 | TB_Step(ocx, -offset); 83 | pll_mode = 2; 84 | pll_t0 = t0; 85 | } 86 | break; 87 | 88 | case 2: /* Wait for another good estimate, then PLL */ 89 | 90 | rt = TS_Diff(&t0, &pll_t0); 91 | if (rt > 6.0) { 92 | pll_b = pll_a / param_pll_std_i_init; 93 | pll_t0 = t0; 94 | pll_mode = 3; 95 | } 96 | break; 97 | 98 | case 3: /* track mode */ 99 | rt = TS_Diff(&t0, &pll_t0); 100 | assert(rt > 0); 101 | 102 | dt = TS_Diff(&t0, &pll_last_time); 103 | assert(dt > 0); 104 | 105 | /* 106 | * XXX: Brute-force exploitation of the weight. 107 | * 108 | * Ideally, we should scale the pll_[ab] terms and the 109 | * stiffening of them based on the weight. That is harder 110 | * than it sounds -- or at least I have not found a good 111 | * candidate function yet. 112 | * In the meantime this is a simple threshold based 113 | * prevention of horribly distant servers injecting too 114 | * much noise into the very reactive default PLL. 115 | * Some averaging of the weight may be required. 116 | */ 117 | if (weight < 50) { 118 | used_a = 3e-2; 119 | used_b = 5e-4; 120 | } else if (weight < 150) { 121 | used_a = 6e-2; 122 | used_b = 1e-3; 123 | } else { 124 | 125 | if (rt > param_pll_std_capture_time && 126 | pll_a > param_pll_std_p_limit) { 127 | pll_a *= pow(param_pll_std_stiffen_rate, dt); 128 | pll_b *= pow(param_pll_std_stiffen_rate, dt); 129 | } 130 | used_a = pll_a; 131 | used_b = pll_b; 132 | } 133 | p_term = -offset * used_a; 134 | pll_integrator += p_term * used_b; 135 | dur = dt; 136 | break; 137 | default: 138 | WRONG("Wrong PLL state"); 139 | } 140 | 141 | dur = ceil(dur); 142 | 143 | /* Clamp (XXX: leave to timebase to do this ?) */ 144 | if (p_term > dur * 500e-6) 145 | p_term = dur * 500e-6; 146 | if (p_term < dur * -500e-6) 147 | p_term = dur * -500e-6; 148 | 149 | pll_last_time = t0; 150 | Put(ocx, OCX_TRACE, 151 | "PLL %d %.3e %.3e %.3e -> %.3e %.3e %.3e %.3e %.3e\n", 152 | pll_mode, dt, offset, weight, 153 | p_term, dur, pll_integrator, 154 | used_a, used_b); 155 | if (dur > 0.0) 156 | TB_Adjust(ocx, p_term, dur, pll_integrator); 157 | } 158 | 159 | pll_f *PLL = NULL; 160 | 161 | void 162 | PLL_Init(void) 163 | { 164 | Param_Register(pll_std_param_table); 165 | PLL = pll_std; 166 | } 167 | -------------------------------------------------------------------------------- /plotgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2014 Poul-Henning Kamp 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | # SUCH DAMAGE. 26 | # 27 | # Postprocess simulation output for gnuplot 28 | # ========================================= 29 | # 30 | # python plotgen.py 31 | # then 32 | # gnuplot 33 | # load '/tmp/_g' 34 | # 35 | 36 | from __future__ import print_function 37 | 38 | fi = open("/tmp/_") 39 | fg = open("/tmp/_g", "w") 40 | 41 | fg.write("#\n") 42 | 43 | plots = [] 44 | 45 | class plot(object): 46 | def __init__(self, title = "-"): 47 | self.s = [] 48 | self.title = title 49 | plots.append(self) 50 | 51 | def write(self, l): 52 | self.s.append(l) 53 | 54 | def commit(self, fg, title): 55 | if title: 56 | fg.write("set y2label '%s'\n" % self.title) 57 | else: 58 | fg.write("unset y2label\n") 59 | 60 | for i in self.s: 61 | fg.write(i + "\n") 62 | 63 | now = 0.0 64 | 65 | class simpll(object): 66 | def __init__(self): 67 | self.fo = open("/tmp/_simpll", "w") 68 | self.pl1 = plot('p/freq') 69 | self.pl1.write( 70 | "plot '/tmp/_simpll' using 1:($2*1e6) with line notitle") 71 | self.pl2 = plot('p/off') 72 | self.pl2.write( 73 | "plot '/tmp/_simpll' using 1:($3*1e6/($4 == 0 ? 1 : $4)) with impulse notitle") 74 | if False: 75 | self.pl3 = plot('p/dur') 76 | self.pl3.write( 77 | "plot '/tmp/_simpll' using 1:4 with imp notitle") 78 | 79 | def data(self, ll): 80 | self.fo.write("%f " % now + " " + " ".join(ll[1:]) + "\n") 81 | 82 | class combine(object): 83 | def __init__(self): 84 | self.fo = open("/tmp/_combine", "w") 85 | self.pl1 = plot("c/in") 86 | self.pl1.write( 87 | "plot '/tmp/_combine' using 1:2 notitle" 88 | ", '/tmp/_combine' using 1:4 notitle" 89 | ", '/tmp/_combine' using 1:3 notitle" 90 | ) 91 | self.pl2 = plot('c/peak') 92 | self.pl2.write( 93 | "plot '/tmp/_combine' using 1:5 with line notitle" 94 | ) 95 | self.pl3 = plot("c/weight") 96 | self.pl3.write("set yrange[0:]") 97 | self.pl3.write( 98 | "plot '/tmp/_combine' using 1:6 axis x1y2 with line notitle" 99 | ) 100 | self.pl3.write("set autoscale y") 101 | 102 | def data(self, ll): 103 | self.fo.write("%f " % now + " " + " ".join(ll[3:]) + "\n") 104 | 105 | p_simpll = simpll() 106 | 107 | p_combine = combine() 108 | 109 | n = 0 110 | for l in fi: 111 | n += 1 112 | if len(l) == 0 or l[0] == "#": 113 | continue 114 | ll = l.split() 115 | if ll[0] == "Now": 116 | now = float(ll[1]) - 1e6 117 | continue 118 | if ll[0] == "SIMPLL": 119 | p_simpll.data(ll) 120 | continue 121 | if ll[0] == "Combine": 122 | p_combine.data(ll) 123 | continue 124 | 125 | def plotset(xlo, xhi, lbl): 126 | fg.write("set autoscale x\n") 127 | fg.write("set xrange [%g:%g]\n" % (xlo, xhi)) 128 | fg.write("set bmargin 0\n") 129 | fg.write("set xtics format ''\n") 130 | fg.write("set xtics (%g, %g)\n" % (xlo, xhi)) 131 | for i in plots[:-1]: 132 | i.commit(fg, lbl) 133 | fg.write("set bmargin 2\n") 134 | fg.write("set xtics format '%g'\n") 135 | fg.write("set xtics (%g, %g)\n" % (xlo, xhi)) 136 | plots[-1].commit(fg, lbl) 137 | 138 | fg.write("set grid\n") 139 | fg.write("set pointsize .5\n") 140 | fg.write("set size .8,.8\n") 141 | fg.write("set tmargin 1\n") 142 | fg.write("set lmargin 8\n") 143 | 144 | fg.write("set multiplot layout %d,3 columnsfirst\n" % len(plots)) 145 | 146 | fg.write("set style data point\n") 147 | plotset(0, 64, False) 148 | 149 | if now > 10000: 150 | fg.write("set pointsize .2\n") 151 | plotset(60, 3600, False) 152 | fg.write("set pointsize .1\n") 153 | plotset(3600, now, True) 154 | else: 155 | fg.write("set pointsize .5\n") 156 | plotset(60, 600, False) 157 | fg.write("set pointsize .2\n") 158 | plotset(5, now, True) 159 | 160 | fg.write("unset multiplot\n") 161 | fg.write("set autoscale\n") 162 | fg.write("set xtics auto\n") 163 | fg.write("set ytics auto\n") 164 | -------------------------------------------------------------------------------- /suckaddr.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * The world has not enough cuss-words to precisely convey how broken the 27 | * the struct sockaddr concept is. 28 | */ 29 | 30 | #include 31 | 32 | #include /* Compat for OpenBSD */ 33 | #include 34 | 35 | #include 36 | 37 | #include "ntimed.h" 38 | 39 | int 40 | SA_Equal(const void *sa1, size_t sl1, const void *sa2, size_t sl2) 41 | { 42 | const struct sockaddr *s1, *s2; 43 | const struct sockaddr_in *s41, *s42; 44 | const struct sockaddr_in6 *s61, *s62; 45 | 46 | AN(sa1); 47 | AN(sa2); 48 | assert(sl1 >= sizeof(struct sockaddr)); 49 | assert(sl2 >= sizeof(struct sockaddr)); 50 | 51 | s1 = sa1; 52 | s2 = sa2; 53 | if (s1->sa_family != s2->sa_family) 54 | return (0); 55 | 56 | if (s1->sa_family == AF_INET) { 57 | assert(sl1 >= sizeof(struct sockaddr_in)); 58 | assert(sl2 >= sizeof(struct sockaddr_in)); 59 | s41 = sa1; 60 | s42 = sa2; 61 | if (s41->sin_port != s42->sin_port) 62 | return (0); 63 | if (memcmp(&s41->sin_addr, &s42->sin_addr, 64 | sizeof s41->sin_addr)) 65 | return (0); 66 | return (1); 67 | } 68 | 69 | if (s1->sa_family == AF_INET6) { 70 | assert(sl1 >= sizeof(struct sockaddr_in6)); 71 | assert(sl2 >= sizeof(struct sockaddr_in6)); 72 | s61 = sa1; 73 | s62 = sa2; 74 | if (s61->sin6_port != s62->sin6_port) 75 | return (0); 76 | if (s61->sin6_scope_id != s62->sin6_scope_id) 77 | return (0); 78 | if (memcmp(&s61->sin6_addr, &s62->sin6_addr, 79 | sizeof s61->sin6_addr)) 80 | return (0); 81 | return (1); 82 | } 83 | return (0); 84 | } 85 | -------------------------------------------------------------------------------- /time_sim.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Simulated timebase 27 | * ================== 28 | * 29 | * Very simple minded: Time advances when TB_Sleep() is called only. 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #include "ntimed.h" 38 | 39 | static struct timestamp st_now; 40 | 41 | static double freq = 0; 42 | static double freq0 = 0; 43 | 44 | static double adj_offset = 0; 45 | static double adj_duration = 0; 46 | static double adj_freq = 0; 47 | 48 | /* 49 | * This variable is public and represent the amount of time the simulated 50 | * clock has been tweaked by the TB_Step() and TB_Adjust() functions. 51 | * 52 | * This can be used to "(re-)model" previously recorded event series 53 | * onto the ST timebase. 54 | */ 55 | 56 | double Time_Sim_delta = 0; 57 | 58 | /**********************************************************************/ 59 | 60 | static struct timestamp * 61 | st_Now(struct timestamp *storage) 62 | { 63 | if (storage == NULL) 64 | ALLOC_OBJ(storage, TIMESTAMP_MAGIC); 65 | AN(storage); 66 | *storage = st_now; 67 | return (storage); 68 | } 69 | 70 | /**********************************************************************/ 71 | 72 | static int 73 | st_Sleep(double dur) 74 | { 75 | 76 | TS_Add(&st_now, dur); 77 | Time_Sim_delta += dur * freq; 78 | return (0); 79 | } 80 | 81 | /**********************************************************************/ 82 | 83 | static void __match_proto__(tb_step_f) 84 | st_Step(struct ocx *ocx, double offset) 85 | { 86 | 87 | Debug(ocx, "SIMSTEP %.3e\n", offset); 88 | Time_Sim_delta += offset; 89 | TB_generation++; 90 | } 91 | 92 | /**********************************************************************/ 93 | 94 | static void __match_proto__(tb_adjust_f) 95 | st_Adjust(struct ocx *ocx, double offset, double duration, double frequency) 96 | { 97 | 98 | (void)ocx; 99 | adj_offset = offset; 100 | adj_duration = floor(duration); 101 | if (adj_offset > 0.0 && adj_duration == 0.0) 102 | adj_duration = 1.0; 103 | adj_freq = frequency; 104 | } 105 | 106 | /**********************************************************************/ 107 | 108 | static enum todo_e __match_proto__(todo_f) 109 | st_kern_pll(struct ocx *ocx, struct todolist *tdl, void *priv) 110 | { 111 | double d; 112 | 113 | (void)ocx; 114 | AN(tdl); 115 | AZ(priv); 116 | freq = freq0 + adj_freq; 117 | if (adj_duration > 0.0) { 118 | d = adj_offset / adj_duration; 119 | freq += d; 120 | adj_offset -= d; 121 | adj_duration -= 1.0; 122 | } 123 | Put(ocx, OCX_TRACE, "SIMPLL %.3e %.3e %.3e\n", 124 | adj_freq, adj_offset, adj_duration); 125 | return (TODO_OK); 126 | } 127 | 128 | /********************************************************************** 129 | * Mechanism to artificially bump simulated clock around. 130 | */ 131 | 132 | struct st_bump { 133 | unsigned magic; 134 | #define ST_BUMP_MAGIC 0xc8981be3 135 | double bfreq; 136 | double bphase; 137 | }; 138 | 139 | static enum todo_e __match_proto__(todo_f) 140 | st_kern_bump(struct ocx *ocx, struct todolist *tdl, void *priv) 141 | { 142 | struct st_bump *stb; 143 | 144 | (void)ocx; 145 | (void)tdl; 146 | CAST_OBJ_NOTNULL(stb, priv, ST_BUMP_MAGIC); 147 | 148 | freq0 += stb->bfreq; 149 | Time_Sim_delta += stb->bphase; 150 | FREE_OBJ(stb); 151 | return (TODO_OK); 152 | } 153 | 154 | void 155 | Time_Sim_Bump(struct todolist *tdl, double when, double bfreq, double bphase) 156 | { 157 | struct st_bump *stb; 158 | 159 | ALLOC_OBJ(stb, ST_BUMP_MAGIC); 160 | AN(stb); 161 | stb->bfreq = bfreq; 162 | stb->bphase = bphase; 163 | (void)TODO_ScheduleRel(tdl, st_kern_bump, stb, when, 0.0, "BUMP"); 164 | } 165 | 166 | /**********************************************************************/ 167 | 168 | void 169 | Time_Sim(struct todolist *tdl) 170 | { 171 | 172 | INIT_OBJ(&st_now, TIMESTAMP_MAGIC); 173 | TS_Add(&st_now, 1e6); 174 | TB_Now = st_Now; 175 | TB_Sleep = st_Sleep; 176 | TB_Step = st_Step; 177 | TB_Adjust = st_Adjust; 178 | (void)TODO_ScheduleRel(tdl, st_kern_pll, NULL, 0.0, 1.0, "SIMPLL"); 179 | } 180 | -------------------------------------------------------------------------------- /time_stuff.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Timebase infrastructure 27 | * ======================= 28 | * 29 | * This file implements the generic timebase stuff, calling out to a specific 30 | * implementation through the function pointers as required. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "ntimed.h" 39 | 40 | #define NANO_FRAC 18446744074ULL // 2^64 / 1e9 41 | 42 | /* 43 | * Whenever the clock is stepped, we increment this generation number. 44 | * 45 | * XXX: Add support for stepping externally via a signal (SIGRESUME ?) 46 | */ 47 | int TB_generation = 41; 48 | 49 | /**********************************************************************/ 50 | 51 | static struct timestamp * 52 | ts_fixstorage(struct timestamp *storage) 53 | { 54 | if (storage == NULL) { 55 | ALLOC_OBJ(storage, TIMESTAMP_MAGIC); 56 | AN(storage); 57 | } else { 58 | AN(storage); 59 | memset(storage, 0, sizeof *storage); 60 | storage->magic = TIMESTAMP_MAGIC; 61 | } 62 | return (storage); 63 | } 64 | 65 | /**********************************************************************/ 66 | 67 | struct timestamp * 68 | TS_Nanosec(struct timestamp *storage, int64_t sec, int64_t nsec) 69 | { 70 | 71 | storage = ts_fixstorage(storage); 72 | 73 | assert(sec >= 0); 74 | assert(nsec >= 0); 75 | assert(nsec < 1000000000); 76 | storage->sec = (uint64_t)sec; 77 | storage->frac = (uint32_t)nsec * NANO_FRAC; 78 | return (storage); 79 | } 80 | 81 | /**********************************************************************/ 82 | 83 | struct timestamp * 84 | TS_Double(struct timestamp *storage, double d) 85 | { 86 | 87 | assert(d >= 0.0); 88 | storage = ts_fixstorage(storage); 89 | 90 | storage->sec += (uint64_t)floor(d); 91 | d -= floor(d); 92 | storage->frac = (uint64_t)ldexp(d, 64); 93 | return (storage); 94 | } 95 | 96 | /**********************************************************************/ 97 | 98 | void 99 | TS_Add(struct timestamp *ts, double dt) 100 | { 101 | double di; 102 | 103 | CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC); 104 | dt += ldexp(ts->frac, -64); 105 | di = floor(dt); 106 | ts->sec += (uint64_t)di; 107 | ts->frac = (uint64_t)ldexp(dt - di, 64); 108 | } 109 | 110 | /**********************************************************************/ 111 | 112 | double 113 | TS_Diff(const struct timestamp *t1, const struct timestamp *t2) 114 | { 115 | double d; 116 | 117 | CHECK_OBJ_NOTNULL(t1, TIMESTAMP_MAGIC); 118 | CHECK_OBJ_NOTNULL(t2, TIMESTAMP_MAGIC); 119 | d = ldexp((double)t1->frac - (double)t2->frac, -64); 120 | d += ((double)t1->sec - (double)t2->sec); 121 | 122 | return (d); 123 | } 124 | 125 | /**********************************************************************/ 126 | 127 | int 128 | TS_SleepUntil(const struct timestamp *t) 129 | { 130 | struct timestamp now; 131 | double dt; 132 | 133 | TB_Now(&now); 134 | dt = TS_Diff(t, &now); 135 | if (dt <= 0.) 136 | return (0); 137 | return (TB_Sleep(dt)); 138 | } 139 | 140 | /**********************************************************************/ 141 | 142 | void 143 | TS_Format(char *buf, size_t len, const struct timestamp *ts) 144 | { 145 | CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC); 146 | uint64_t x, y; 147 | int i; 148 | 149 | /* XXX: Nanosecond precision is enough for everybody. */ 150 | x = ts->sec; 151 | y = (ts->frac + NANO_FRAC / 2ULL) / NANO_FRAC; 152 | if (y >= 1000000000ULL) { 153 | y -= 1000000000ULL; 154 | x += 1; 155 | } 156 | i = snprintf(buf, len, "%jd.%09jd", (intmax_t)x, (intmax_t)y); 157 | assert(i < (int)len); 158 | } 159 | 160 | /********************************************************************** 161 | * DUMMY TimeBase functions 162 | */ 163 | 164 | static struct timestamp * 165 | tb_Now(struct timestamp *storage) 166 | { 167 | 168 | (void)storage; 169 | WRONG("No TB_Now"); 170 | NEEDLESS_RETURN(NULL); 171 | } 172 | 173 | tb_now_f *TB_Now = tb_Now; 174 | 175 | /**********************************************************************/ 176 | 177 | static int 178 | tb_Sleep(double dur) 179 | { 180 | (void)dur; 181 | WRONG("No TB_Sleep"); 182 | NEEDLESS_RETURN(-1); 183 | } 184 | 185 | tb_sleep_f *TB_Sleep = tb_Sleep; 186 | 187 | /**********************************************************************/ 188 | 189 | static void __match_proto__(tb_step_f) 190 | tb_Step(struct ocx *ocx, double offset) 191 | { 192 | (void)ocx; 193 | (void)offset; 194 | WRONG("No TB_Step"); 195 | } 196 | 197 | tb_step_f *TB_Step = tb_Step; 198 | 199 | /**********************************************************************/ 200 | 201 | static void __match_proto__(tb_adjust_f) 202 | tb_Adjust(struct ocx *ocx, double offset, double duration, double frequency) 203 | { 204 | (void)ocx; 205 | (void)offset; 206 | (void)duration; 207 | (void)frequency; 208 | WRONG("No TB_Adjust"); 209 | } 210 | 211 | tb_adjust_f *TB_Adjust = tb_Adjust; 212 | 213 | /********************************************************************** 214 | * Timebase test functions. 215 | */ 216 | 217 | static int 218 | ts_onetest(struct ocx *ocx, const struct timestamp *ts, double off) 219 | { 220 | struct timestamp ts2; 221 | double dt; 222 | char buf[40]; 223 | 224 | TS_Format(buf, sizeof buf, ts); 225 | ts2 = *ts; 226 | TS_Add(&ts2, off); 227 | Debug(ocx, "%s + %12.9f = ", buf, off); 228 | TS_Format(buf, sizeof buf, &ts2); 229 | dt = TS_Diff(&ts2, ts) - off; 230 | Debug(ocx, "%s %8.1e", buf, dt); 231 | if (fabs(dt) > 5e-10) { 232 | Debug(ocx, " ERR\n"); 233 | return (1); 234 | } 235 | Debug(ocx, " OK\n"); 236 | return (0); 237 | } 238 | 239 | void 240 | TS_RunTest(struct ocx *ocx) 241 | { 242 | struct timestamp ts; 243 | int nf = 0; 244 | 245 | TB_Now(&ts); 246 | nf += ts_onetest(ocx, &ts, 1e-9); 247 | nf += ts_onetest(ocx, &ts, 1e-8); 248 | nf += ts_onetest(ocx, &ts, 1e-6); 249 | nf += ts_onetest(ocx, &ts, 1e-3); 250 | nf += ts_onetest(ocx, &ts, 1e-1); 251 | nf += ts_onetest(ocx, &ts, 0.999); 252 | nf += ts_onetest(ocx, &ts, 1.001); 253 | nf += ts_onetest(ocx, &ts, 1.999); 254 | nf += ts_onetest(ocx, &ts, -2.000); 255 | nf += ts_onetest(ocx, &ts, -1.999); 256 | nf += ts_onetest(ocx, &ts, -1.000); 257 | nf += ts_onetest(ocx, &ts, -0.999); 258 | nf += ts_onetest(ocx, &ts, -1e-3); 259 | nf += ts_onetest(ocx, &ts, -1e-6); 260 | nf += ts_onetest(ocx, &ts, -1e-9); 261 | Debug(ocx, "TS_RunTest: %d failures\n", nf); 262 | AZ(nf); 263 | } 264 | 265 | 266 | -------------------------------------------------------------------------------- /time_unix.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * UNIX timebase 27 | * ============= 28 | * 29 | * Implement the timebase functions on top of a modern UNIX kernel which 30 | * has the some version of the Mills/Kamp kernel PLL code and either 31 | * [gs]ettimeofday(2) or better: clock_[gs]ettime(2) API. 32 | * 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | #include "ntimed.h" 44 | 45 | static double adj_offset = 0; 46 | static double adj_duration = 0; 47 | static double adj_freq = 0; 48 | 49 | static uintptr_t ticker; 50 | static struct todolist *kt_tdl; 51 | 52 | // #undef CLOCK_REALTIME /* Test old unix code */ 53 | 54 | /********************************************************************** 55 | * The NTP-pll in UNIX kernels apply the offset correction in an 56 | * exponential-decay fashion for historical and wrong reasons. 57 | * 58 | * The short explanation is that this ends up confusing all PLLs I have 59 | * ever seen, by introducing mainly odd harmonics of the PLL update period 60 | * into all time-measurements in the system. 61 | * 62 | * A much more sane mode would be to tell the kernel "I want this much 63 | * offset accumulated over this many seconds", giving a constant frequency 64 | * over the PLL update period while still falling back to the frequency 65 | * estimate should the time-steering userland process fail. 66 | * 67 | * I will add such a mode to the FreeBSD kernel as a reference implementation 68 | * at a later date, in the mean time this code implements it by updating the 69 | * kernel frequency from userland as needed. 70 | * 71 | * XXX: Optimise to only wake up when truly needed, rather than every second. 72 | * XXX: Requires TODO cancellation. 73 | */ 74 | 75 | static void 76 | kt_setfreq(struct ocx *ocx, double frequency) 77 | { 78 | struct timex tx; 79 | int i; 80 | 81 | assert(isfinite(frequency)); 82 | 83 | memset(&tx, 0, sizeof tx); 84 | tx.modes = MOD_STATUS; 85 | #if defined(MOD_NANO) 86 | tx.modes |= MOD_NANO; 87 | #elif defined(MOD_MICRO) 88 | tx.modes |= MOD_MICRO; 89 | #endif 90 | 91 | tx.status = STA_PLL | STA_FREQHOLD; 92 | tx.modes = MOD_FREQUENCY; 93 | tx.freq = (long)floor(frequency * (65536 * 1e6)); 94 | errno = 0; 95 | i = ntp_adjtime(&tx); 96 | Put(ocx, OCX_TRACE, "KERNPLL %.6e %d\n", frequency, i); 97 | /* XXX: what is the correct error test here ? */ 98 | assert(i >= 0); 99 | } 100 | 101 | static enum todo_e __match_proto__(todo_f) 102 | kt_ticker(struct ocx *ocx, struct todolist *tdl, void *priv) 103 | { 104 | 105 | (void)ocx; 106 | AN(tdl); 107 | AZ(priv); 108 | kt_setfreq(ocx, adj_freq); 109 | ticker = 0; 110 | return (TODO_OK); 111 | } 112 | 113 | static void __match_proto__(tb_adjust_f) 114 | kt_adjust(struct ocx *ocx, double offset, double duration, double frequency) 115 | { 116 | double freq; 117 | 118 | (void)ocx; 119 | assert(duration >= 0.0); 120 | 121 | if (ticker) 122 | TODO_Cancel(kt_tdl, &ticker); 123 | 124 | adj_offset = offset; 125 | adj_duration = floor(duration); 126 | if (adj_offset > 0.0 && adj_duration == 0.0) 127 | adj_duration = 1.0; 128 | adj_freq = frequency; 129 | 130 | freq = adj_freq; 131 | if (adj_duration > 0.0) 132 | freq += adj_offset / adj_duration; 133 | kt_setfreq(ocx, freq); 134 | if (adj_duration > 0.0) 135 | ticker = TODO_ScheduleRel(kt_tdl, kt_ticker, NULL, 136 | adj_duration, 0.0, "KT_TICK"); 137 | } 138 | 139 | /**********************************************************************/ 140 | 141 | #ifdef CLOCK_REALTIME 142 | 143 | static void __match_proto__(tb_step_f) 144 | kt_step(struct ocx *ocx, double offset) 145 | { 146 | double d; 147 | struct timespec ts; 148 | 149 | Put(ocx, OCX_TRACE, "KERNTIME_STEP %.3e\n", offset); 150 | d = floor(offset); 151 | offset -= d; 152 | 153 | AZ(clock_gettime(CLOCK_REALTIME, &ts)); 154 | ts.tv_sec += (long)d; 155 | ts.tv_nsec += (long)floor(offset * 1e9); 156 | if (ts.tv_nsec < 0) { 157 | ts.tv_sec -= 1; 158 | ts.tv_nsec += 1000000000; 159 | } else if (ts.tv_nsec >= 1000000000) { 160 | ts.tv_sec += 1; 161 | ts.tv_nsec -= 1000000000; 162 | } 163 | AZ(clock_settime(CLOCK_REALTIME, &ts)); 164 | TB_generation++; 165 | } 166 | 167 | #else 168 | 169 | static void __match_proto__(tb_step_f) 170 | kt_step(struct ocx *ocx, double offset) 171 | { 172 | double d; 173 | struct timeval tv; 174 | 175 | Put(ocx, OCX_TRACE, "KERNTIME_STEP %.3e\n", offset); 176 | d = floor(offset); 177 | offset -= d; 178 | 179 | AZ(gettimeofday(&tv, NULL)); 180 | tv.tv_sec += (long)d; 181 | tv.tv_usec += (long)floor(offset * 1e6); 182 | if (tv.tv_usec < 0) { 183 | tv.tv_sec -= 1; 184 | tv.tv_usec += 1000000; 185 | } else if (tv.tv_usec >= 1000000) { 186 | tv.tv_sec += 1; 187 | tv.tv_usec -= 1000000; 188 | } 189 | AZ(settimeofday(&tv, NULL)); 190 | TB_generation++; 191 | } 192 | 193 | #endif 194 | 195 | /**********************************************************************/ 196 | 197 | #if defined (CLOCK_REALTIME) 198 | 199 | static struct timestamp * __match_proto__(tb_now_f) 200 | kt_now(struct timestamp *storage) 201 | { 202 | struct timespec ts; 203 | 204 | AZ(clock_gettime(CLOCK_REALTIME, &ts)); 205 | return (TS_Nanosec(storage, ts.tv_sec, ts.tv_nsec)); 206 | } 207 | 208 | #else 209 | 210 | static struct timestamp * __match_proto__(tb_now_f) 211 | kt_now(struct timestamp *storage) 212 | { 213 | struct timeval tv; 214 | 215 | AZ(gettimeofday(&tv, NULL)); 216 | return (TS_Nanosec(storage, tv.tv_sec, tv.tv_usec * 1000LL)); 217 | } 218 | 219 | #endif 220 | 221 | /**********************************************************************/ 222 | 223 | static int __match_proto__(tb_sleep_f) 224 | kt_sleep(double dur) 225 | { 226 | struct pollfd fds[1]; 227 | int i; 228 | 229 | i = poll(fds, 0, (int)floor(dur * 1e3)); 230 | if (i < 0 && errno == EINTR) 231 | return (1); 232 | AZ(i); 233 | return (0); 234 | } 235 | 236 | /**********************************************************************/ 237 | 238 | void 239 | Time_Unix(struct todolist *tdl) 240 | { 241 | 242 | AN(tdl); 243 | TB_Step = kt_step; 244 | TB_Adjust = kt_adjust; 245 | TB_Sleep = kt_sleep; 246 | TB_Now = kt_now; 247 | kt_tdl = tdl; 248 | 249 | /* XXX: test if we have perms */ 250 | } 251 | 252 | /********************************************************************** 253 | * Non-tweaking subset. 254 | */ 255 | 256 | void 257 | Time_Unix_Passive(void) 258 | { 259 | 260 | TB_Sleep = kt_sleep; 261 | TB_Now = kt_now; 262 | } 263 | -------------------------------------------------------------------------------- /todo.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * todo-list scheduler 27 | * =================== 28 | * 29 | * This is a simple "TODO-list" scheduler for calling things at certain 30 | * times. Jobs can be one-shot or repeated and repeated jobs can abort. 31 | * 32 | * For ease of debugging, TODO jobs have a name. 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "ntimed.h" 40 | 41 | struct todo { 42 | unsigned magic; 43 | #define TODO_MAGIC 0x5279009a 44 | TAILQ_ENTRY(todo) list; 45 | 46 | todo_f *func; 47 | void *priv; 48 | struct timestamp when; 49 | double repeat; 50 | 51 | char what[40]; 52 | }; 53 | 54 | struct todolist { 55 | unsigned magic; 56 | #define TODOLIST_MAGIC 0x7db66255 57 | TAILQ_HEAD(,todo) todolist; 58 | }; 59 | 60 | struct todolist * 61 | TODO_NewList(void) 62 | { 63 | struct todolist *tdl; 64 | 65 | ALLOC_OBJ(tdl, TODOLIST_MAGIC); 66 | AN(tdl); 67 | TAILQ_INIT(&tdl->todolist); 68 | return (tdl); 69 | } 70 | 71 | static void 72 | todo_insert(struct todolist *tdl, struct todo *tp) 73 | { 74 | struct todo *tp1; 75 | 76 | TAILQ_FOREACH(tp1, &tdl->todolist, list) { 77 | if (TS_Diff(&tp1->when, &tp->when) > 0.0) { 78 | TAILQ_INSERT_BEFORE(tp1, tp, list); 79 | return; 80 | } 81 | } 82 | TAILQ_INSERT_TAIL(&tdl->todolist, tp, list); 83 | } 84 | 85 | /********************************************************************** 86 | */ 87 | 88 | void 89 | TODO_Cancel(struct todolist *tdl, uintptr_t *tp) 90 | { 91 | struct todo *tp2; 92 | 93 | CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC); 94 | AN(tp); 95 | AN(*tp); 96 | 97 | TAILQ_FOREACH(tp2, &tdl->todolist, list) 98 | if ((uintptr_t)tp2 == *tp) 99 | break; 100 | CHECK_OBJ_NOTNULL(tp2, TODO_MAGIC); 101 | TAILQ_REMOVE(&tdl->todolist, tp2, list); 102 | FREE_OBJ(tp2); 103 | *tp = 0; 104 | } 105 | 106 | /********************************************************************** 107 | */ 108 | 109 | uintptr_t 110 | TODO_ScheduleAbs(struct todolist *tdl, todo_f *func, void *priv, 111 | const struct timestamp *when, double repeat, const char *fmt, ...) 112 | { 113 | struct todo *tp; 114 | va_list ap; 115 | 116 | CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC); 117 | AN(func); 118 | CHECK_OBJ_NOTNULL(when, TIMESTAMP_MAGIC); 119 | assert(repeat >= 0.0); 120 | AN(fmt); 121 | 122 | ALLOC_OBJ(tp, TODO_MAGIC); 123 | AN(tp); 124 | tp->func = func; 125 | tp->priv = priv; 126 | tp->when = *when; 127 | tp->repeat = repeat; 128 | va_start(ap, fmt); 129 | (void)vsnprintf(tp->what, sizeof tp->what, fmt, ap); 130 | va_end(ap); 131 | todo_insert(tdl, tp); 132 | return ((uintptr_t)tp); 133 | } 134 | 135 | uintptr_t 136 | TODO_ScheduleRel(struct todolist *tdl, todo_f *func, void *priv, 137 | double when, double repeat, const char *fmt, ...) 138 | { 139 | struct todo *tp; 140 | va_list ap; 141 | 142 | CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC); 143 | AN(func); 144 | assert(when >= 0.0); 145 | assert(repeat >= 0.0); 146 | AN(fmt); 147 | 148 | ALLOC_OBJ(tp, TODO_MAGIC); 149 | AN(tp); 150 | tp->func = func; 151 | tp->priv = priv; 152 | TB_Now(&tp->when); 153 | TS_Add(&tp->when, when); 154 | tp->repeat = repeat; 155 | va_start(ap, fmt); 156 | (void)vsnprintf(tp->what, sizeof tp->what, fmt, ap); 157 | va_end(ap); 158 | todo_insert(tdl, tp); 159 | return ((uintptr_t)tp); 160 | } 161 | 162 | /********************************************************************** 163 | * Schedule TODO list until failure or empty 164 | */ 165 | 166 | enum todo_e 167 | TODO_Run(struct ocx *ocx, struct todolist *tdl) 168 | { 169 | struct todo *tp; 170 | enum todo_e ret = TODO_OK; 171 | char buf[40]; 172 | int i; 173 | 174 | CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC); 175 | while(!TAILQ_EMPTY(&tdl->todolist)) { 176 | tp = TAILQ_FIRST(&tdl->todolist); 177 | i = TS_SleepUntil(&tp->when); 178 | if (i == 1) 179 | return (TODO_INTR); 180 | AZ(i); 181 | TS_Format(buf, sizeof buf, &tp->when); 182 | Put(ocx, OCX_TRACE, "Now %s %s\n", buf, tp->what); 183 | ret = tp->func(ocx, tdl, tp->priv); 184 | if (ret == TODO_FAIL) 185 | break; 186 | if (ret == TODO_DONE || tp->repeat == 0.0) { 187 | TAILQ_REMOVE(&tdl->todolist, tp, list); 188 | FREE_OBJ(tp); 189 | } else if (ret == TODO_OK) { 190 | TS_Add(&tp->when, tp->repeat); 191 | TAILQ_REMOVE(&tdl->todolist, tp, list); 192 | todo_insert(tdl, tp); 193 | } else { 194 | WRONG("Invalid Return from todo->func"); 195 | } 196 | } 197 | return (ret); 198 | } 199 | -------------------------------------------------------------------------------- /udp.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include /* Compat for NetBSD */ 32 | #include /* Compat for OpenBSD */ 33 | #include 34 | 35 | #include "ntimed.h" 36 | #include "udp.h" 37 | 38 | struct udp_socket { 39 | unsigned magic; 40 | #define UDP_SOCKET_MAGIC 0x302a563f 41 | 42 | int fd4; 43 | int fd6; 44 | }; 45 | 46 | static int 47 | udp_sock(int fam) 48 | { 49 | int fd; 50 | int i; 51 | 52 | fd = socket(fam, SOCK_DGRAM, 0); 53 | if (fd < 0) 54 | return (fd); 55 | 56 | #ifdef SO_TIMESTAMPNS 57 | i = 1; 58 | (void)setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &i, sizeof i); 59 | #elif defined(SO_TIMESTAMP) 60 | i = 1; 61 | (void)setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &i, sizeof i); 62 | #endif 63 | return (fd); 64 | } 65 | 66 | struct udp_socket * 67 | UdpTimedSocket(struct ocx *ocx) 68 | { 69 | struct udp_socket *usc; 70 | 71 | ALLOC_OBJ(usc, UDP_SOCKET_MAGIC); 72 | AN(usc); 73 | usc->fd4 = udp_sock(AF_INET); 74 | usc->fd6 = udp_sock(AF_INET6); 75 | if (usc->fd4 < 0 && usc->fd6 < 0) 76 | Fail(ocx, 1, "socket(2) failed"); 77 | return (usc); 78 | } 79 | 80 | ssize_t 81 | UdpTimedRx(struct ocx *ocx, const struct udp_socket *usc, 82 | sa_family_t fam, 83 | struct sockaddr_storage *ss, socklen_t *sl, 84 | struct timestamp *ts, void *buf, ssize_t len, double tmo) 85 | { 86 | struct msghdr msg; 87 | struct iovec iov; 88 | struct cmsghdr *cmsg; 89 | u_char ctrl[1024]; 90 | ssize_t rl; 91 | int i; 92 | int tmo_msec; 93 | struct pollfd pfd[1]; 94 | 95 | CHECK_OBJ_NOTNULL(usc, UDP_SOCKET_MAGIC); 96 | AN(ss); 97 | AN(sl); 98 | AN(ts); 99 | AN(buf); 100 | assert(len > 0); 101 | 102 | if (fam == AF_INET) 103 | pfd[0].fd = usc->fd4; 104 | else if (fam == AF_INET6) 105 | pfd[0].fd = usc->fd6; 106 | else 107 | WRONG("Wrong family in UdpTimedRx"); 108 | 109 | pfd[0].events = POLLIN; 110 | pfd[0].revents = 0; 111 | 112 | if (tmo == 0.0) { 113 | tmo_msec = -1; 114 | } else { 115 | tmo_msec = lround(1e3 * tmo); 116 | if (tmo_msec <= 0) 117 | tmo_msec = 0; 118 | } 119 | i = poll(pfd, 1, tmo_msec); 120 | 121 | if (i < 0) 122 | Fail(ocx, 1, "poll(2) failed\n"); 123 | 124 | if (i == 0) 125 | return (0); 126 | 127 | /* Grab a timestamp in case none of the SCM_TIMESTAMP* works */ 128 | TB_Now(ts); 129 | 130 | memset(&msg, 0, sizeof msg); 131 | msg.msg_name = (void*)ss; 132 | msg.msg_namelen = sizeof *ss; 133 | msg.msg_iov = &iov; 134 | msg.msg_iovlen = 1; 135 | msg.msg_control = ctrl; 136 | msg.msg_controllen = sizeof ctrl; 137 | iov.iov_base = buf; 138 | iov.iov_len = (size_t)len; 139 | memset(ctrl, 0, sizeof ctrl); 140 | cmsg = (void*)ctrl; 141 | 142 | rl = recvmsg(pfd[0].fd, &msg, 0); 143 | if (rl <= 0) 144 | return (rl); 145 | 146 | *sl = msg.msg_namelen; 147 | 148 | if (msg.msg_flags != 0) { 149 | Debug(ocx, "msg_flags = 0x%x", msg.msg_flags); 150 | return (-1); 151 | } 152 | 153 | for(;cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 154 | #ifdef SCM_TIMESTAMPNS 155 | if (cmsg->cmsg_level == SOL_SOCKET && 156 | cmsg->cmsg_type == SCM_TIMESTAMPNS && 157 | cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) { 158 | struct timespec tsc; 159 | memcpy(&tsc, CMSG_DATA(cmsg), sizeof tsc); 160 | (void)TS_Nanosec(ts, tsc.tv_sec, tsc.tv_nsec); 161 | continue; 162 | } 163 | #endif 164 | #ifdef SCM_TIMESTAMP 165 | if (cmsg->cmsg_level == SOL_SOCKET && 166 | cmsg->cmsg_type == SCM_TIMESTAMP && 167 | cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) { 168 | struct timeval tv; 169 | memcpy(&tv, CMSG_DATA(cmsg), sizeof tv); 170 | (void)TS_Nanosec(ts, tv.tv_sec, tv.tv_usec * 1000LL); 171 | continue; 172 | } 173 | #endif 174 | Debug(ocx, "RX-msg: %d %d %u ", 175 | cmsg->cmsg_level, cmsg->cmsg_type, cmsg->cmsg_len); 176 | DebugHex(ocx, CMSG_DATA(cmsg), cmsg->cmsg_len); 177 | Debug(ocx, "\n"); 178 | 179 | } 180 | return (rl); 181 | } 182 | 183 | ssize_t 184 | Udp_Send(struct ocx *ocx, const struct udp_socket *usc, 185 | const void *ss, socklen_t sl, const void *buf, size_t len) 186 | { 187 | const struct sockaddr *sa; 188 | 189 | (void)ocx; 190 | CHECK_OBJ_NOTNULL(usc, UDP_SOCKET_MAGIC); 191 | AN(ss); 192 | AN(sl); 193 | AN(buf); 194 | AN(len); 195 | sa = ss; 196 | if (sa->sa_family == AF_INET) 197 | return (sendto(usc->fd4, buf, len, 0, ss, sl)); 198 | if (sa->sa_family == AF_INET6) 199 | return (sendto(usc->fd6, buf, len, 0, ss, sl)); 200 | 201 | WRONG("Wrong AF_"); 202 | NEEDLESS_RETURN(0); 203 | } 204 | -------------------------------------------------------------------------------- /udp.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2014 Poul-Henning Kamp 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifdef UDP_H_INCLUDED 28 | #error "udp.h included multiple times" 29 | #endif 30 | #define UDP_H_INCLUDED 31 | 32 | /********************************************************************** 33 | * UDP sockets 34 | */ 35 | 36 | 37 | struct udp_socket *UdpTimedSocket(struct ocx *ocx); 38 | ssize_t UdpTimedRx(struct ocx *, const struct udp_socket *, 39 | sa_family_t fam, 40 | struct sockaddr_storage *, socklen_t *, 41 | struct timestamp *, 42 | void *, ssize_t len, 43 | double tmo); 44 | ssize_t Udp_Send(struct ocx *, const struct udp_socket *, 45 | const void *sa, socklen_t, const void *ptr, size_t); 46 | 47 | --------------------------------------------------------------------------------