1implement Sntp; 2 3# 4# rfc1361 (simple network time protocol) 5# 6 7include "sys.m"; 8 sys: Sys; 9 10include "draw.m"; 11 12include "ip.m"; 13 ip: IP; 14 IPaddr: import ip; 15 16include "dial.m"; 17 dial: Dial; 18 19include "timers.m"; 20 timers: Timers; 21 Timer: import timers; 22 23include "arg.m"; 24 25Sntp: module 26{ 27 init: fn(nil: ref Draw->Context, nil: list of string); 28}; 29 30debug := 0; 31 32Retries: con 4; 33Delay: con 3*1000; # milliseconds 34 35SNTP: adt { 36 li: int; 37 vn: int; 38 mode: int; 39 stratum: int; # level of local clock 40 poll: int; # log2(maximum interval in seconds between successive messages) 41 precision: int; # log2(seconds precision of local clock) [eg, -6 for mains, -18 for microsec] 42 rootdelay: int; # round trip delay in seconds to reference (16:16 fraction) 43 dispersion: int; # maximum error relative to primary reference 44 clockid: string; # reference clock identifier 45 reftime: big; # local time at which clock last set/corrected 46 orgtime: big; # local time at which client transmitted request 47 rcvtime: big; # time at which request arrived at server 48 xmttime: big; # time server transmitted reply 49 auth: array of byte; # auth field (ignored by this implementation) 50 51 new: fn(vn, mode: int): ref SNTP; 52 pack: fn(s: self ref SNTP): array of byte; 53 unpack: fn(a: array of byte): ref SNTP; 54}; 55SNTPlen: con 4+3*4+4*8; 56 57Version: con 1; # accepted by version 2 and version 3 servers 58Stratum: con 0; 59Poll: con 0; 60LI: con 0; 61Symmetric: con 2; 62ClientMode: con 3; 63ServerMode: con 4; 64Epoch: con big 86400*big (365*70 + 17); # seconds between 1 Jan 1900 and 1 Jan 1970 65 66Microsec: con big 1000000; 67 68server := "$ntp"; 69stderr: ref Sys->FD; 70 71init(nil: ref Draw->Context, args: list of string) 72{ 73 sys = load Sys Sys->PATH; 74 ip = load IP IP->PATH; 75 timers = load Timers Timers->PATH; 76 dial = load Dial Dial->PATH; 77 78 ip->init(); 79 arg := load Arg Arg->PATH; 80 arg->init(args); 81 arg->setusage("sntp [-d] [server]"); 82 83 doset := 1; 84 while((o := arg->opt()) != 0) 85 case o { 86 'd' => debug++; 87 'i' => doset = 0; 88 * => arg->usage(); 89 } 90 args = arg->argv(); 91 if(len args > 1) 92 arg->usage(); 93 arg = nil; 94 95 if(args != nil) 96 server = hd args; 97 98 sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil); 99 stderr = sys->fildes(2); 100 timers->init(100); 101 102 conn := dial->dial(dial->netmkaddr(server, "udp", "ntp"), nil); 103 if(conn == nil){ 104 sys->fprint(stderr, "sntp: can't dial %s: %r\n", server); 105 raise "fail:dial"; 106 } 107 108 replies := chan of ref SNTP; 109 spawn reader(conn.dfd, replies); 110 111 for(i:=0; i<Retries; i++){ 112 request := SNTP.new(Version, ClientMode); 113 request.poll = 6; 114 request.orgtime = (big time() + Epoch)<<32; 115 b := request.pack(); 116 if(sys->write(conn.dfd, b, len b) != len b){ 117 sys->fprint(stderr, "sntp: UDP write failed: %r\n"); 118 continue; 119 } 120 t := Timer.start(Delay); 121 alt{ 122 reply := <-replies => 123 t.stop(); 124 if(reply == nil) 125 quit("read error"); 126 if(debug){ 127 sys->fprint(stderr, "LI = %d, version = %d, mode = %d\n", reply.li, reply.vn, reply.mode); 128 if(reply.stratum == 1) 129 sys->fprint(stderr, "stratum = 1 (%s), ", reply.clockid); 130 else 131 sys->fprint(stderr, "stratum = %d, ", reply.stratum); 132 sys->fprint(stderr, "poll = %d, prec = %d\n", reply.poll, reply.precision); 133 sys->fprint(stderr, "rootdelay = %d, dispersion = %d\n", reply.rootdelay, reply.dispersion); 134 } 135 if(reply.vn == 0 || reply.vn > 3) 136 continue; # unsupported version, ignored 137 if(reply.mode >= 6 || reply.mode == ClientMode) 138 continue; 139 now := ((reply.xmttime>>32)&16rFFFFFFFF) - Epoch; 140 if(now <= big 1120000000) 141 continue; 142 if(reply.li == 3 || reply.stratum == 0) # unsynchronised 143 sys->fprint(stderr, "sntp: time server not synchronised to reference time\n"); 144 if(debug) 145 sys->print("%bd\n", now); 146 if(doset){ 147 settime("#r/rtc", now); 148 settime("/dev/time", now*Microsec); 149 } 150 quit(nil); 151 <-t.timeout => 152 continue; 153 } 154 } 155 sys->fprint(sys->fildes(2), "sntp: no response from server %s\n", server); 156 quit("timeout"); 157} 158 159reader(fd: ref Sys->FD, replies: chan of ref SNTP) 160{ 161 for(;;){ 162 buf := array[512] of byte; 163 nb := sys->read(fd, buf, len buf); 164 if(nb <= 0) 165 break; 166 reply := SNTP.unpack(buf[0:nb]); 167 if(reply == nil){ 168 # ignore bad replies 169 if(debug) 170 sys->fprint(stderr, "sntp: invalid reply (len %d)\n", nb); 171 continue; 172 } 173 replies <-= reply; 174 } 175 if(debug) 176 sys->fprint(stderr, "sntp: UDP read failed: %r\n"); 177 replies <-= nil; 178} 179 180quit(s: string) 181{ 182 pid := sys->pctl(0, nil); 183 timers->shutdown(); 184 fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); 185 if(fd != nil) 186 sys->fprint(fd, "killgrp"); 187 if(s != nil) 188 raise "fail:"+s; 189 exit; 190} 191 192time(): int 193{ 194 n := rdn("#r/rtc"); 195 if(n > big 300) # ie, possibly set 196 return int n; 197 n = rdn("/dev/time"); 198 if(n <= big 0) 199 return 0; 200 return int(n/big Microsec); 201} 202 203rdn(f: string): big 204{ 205 fd := sys->open(f, Sys->OREAD); 206 if(fd == nil) 207 return big -1; 208 b := array[128] of byte; 209 n := sys->read(fd, b, len b); 210 if(n <= 0) 211 return big 0; 212 return big string b[0:n]; 213} 214 215settime(f: string, t: big) 216{ 217 fd := sys->open(f, Sys->OWRITE); 218 if(fd != nil) 219 sys->fprint(fd, "%bd", t); 220} 221 222get8(a: array of byte, i: int): big 223{ 224 b := big ip->get4(a, i+4) & 16rFFFFFFFF; 225 return (big ip->get4(a, i) << 32) | b; 226} 227 228put8(a: array of byte, o: int, v: big) 229{ 230 ip->put4(a, o, int (v>>32)); 231 ip->put4(a, o+4, int v); 232} 233 234SNTP.unpack(a: array of byte): ref SNTP 235{ 236 if(len a < SNTPlen) 237 return nil; 238 s := ref SNTP; 239 mode := int a[0]; 240 s.li = mode>>6; 241 s.vn = (mode>>3); 242 s.mode = mode & 3; 243 s.stratum = int a[1]; 244 s.poll = int a[2]; 245 if(s.poll & 16r80) 246 s.poll |= ~0 << 8; 247 s.precision = int a[3]; 248 if(s.precision & 16r80) 249 s.precision |= ~0 << 8; 250 s.rootdelay = ip->get4(a, 4); 251 s.dispersion = ip->get4(a, 8); 252 if(s.stratum <= 1){ 253 for(i := 12; i < 16; i++) 254 if(a[i] == byte 0) 255 break; 256 s.clockid = string a[12:i]; 257 }else 258 s.clockid = sys->sprint("%d.%d.%d.%d", int a[12], int a[13], int a[14], int a[15]); 259 s.reftime = get8(a, 16); 260 s.orgtime = get8(a, 24); 261 s.rcvtime = get8(a, 32); 262 s.xmttime = get8(a, 40); 263 if(len a > SNTPlen) 264 s.auth = a[48:]; 265 return s; 266} 267 268SNTP.pack(s: self ref SNTP): array of byte 269{ 270 a := array[SNTPlen + len s.auth] of byte; 271 a[0] = byte ((s.li<<6) | (s.vn<<3) | s.mode); 272 a[1] = byte s.stratum; 273 a[2] = byte s.poll; 274 a[3] = byte s.precision; 275 ip->put4(a, 4, s.rootdelay); 276 ip->put4(a, 8, s.dispersion); 277 ip->put4(a, 12, 0); # clockid field 278 if(s.clockid != nil){ 279 if(s.stratum <= 1){ 280 b := array of byte s.clockid; 281 for(i := 0; i < len b && i < 4; i++) 282 a[12+i] = b[i]; 283 }else 284 a[12:] = IPaddr.parse(s.clockid).t1.v4(); 285 } 286 put8(a, 16, s.reftime); 287 put8(a, 24, s.orgtime); 288 put8(a, 32, s.rcvtime); 289 put8(a, 40, s.xmttime); 290 if(s.auth != nil) 291 a[48:] = s.auth; 292 return a; 293} 294 295SNTP.new(vn, mode: int): ref SNTP 296{ 297 s := ref SNTP; 298 s.vn = vn; 299 s.mode = mode; 300 s.li = 0; 301 s.stratum = 0; 302 s.poll = 0; 303 s.precision = 0; 304 s.clockid = nil; 305 s.reftime = big 0; 306 s.orgtime = big 0; 307 s.rcvtime = big 0; 308 s.xmttime = big 0; 309 return s; 310} 311