BIOS.WebNetworkTimeProtocol.Mod 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. MODULE WebNetworkTimeProtocol; (** AUTHOR "Patrick Hunziker"; PURPOSE "SimpleNetworkTimeProtocol RFC4330 and NetworkTimeProtocol RFC5905 "; *)
  2. (**
  3. NTP operates over the User Datagram Protocol (UDP). An NTP server listens for client NTP packets on port 123.
  4. The NTP server is stateless and responds to each received client NTP packet in a simple transactional manner
  5. by adding fields to the received packet and passing the packet back to the original sender, without reference to preceding NTP transactions.
  6. Upon receipt of a client NTP packet, the receiver time-stamps receipt of the packet as soon as possible within the packet assembly logic of the server.
  7. The packet is then passed to the NTP server process.
  8. This process interchanges the IP Header Address and Port fields in the packet, overwrites numerous fields in the NTP packet with local clock values,
  9. time-stamps the egress of the packet, recalculates the checksum, and sends the packet back to the client.
  10. Time is given as 64-bit time-stamp value.
  11. This value is an unsigned 32-bit seconds value, and a 32-bit fractional part.
  12. The unit of time is in seconds, and the epoch is 1 January 1900, meaning that the NTP time will cycle in the year 2036 (two years before the 32-bit Unix time cycle event in 2038).
  13. for best NTP server pools for a given region see wikipedia: NTP-Pool
  14. Implementation currently limited to the simple network time protocol
  15. ToDo: full NTP synchronization algorithm.
  16. Note the comments on leap seconds: http://www.eecis.udel.edu/~mills/leap.html
  17. *)
  18. IMPORT IP,UDP, Streams, Commands, Configuration, DNS, Machine, Dates;
  19. CONST NTPPort*=123;
  20. TYPE Packet=RECORD
  21. Header*:SHORTINT; (* LeapIndicator: 2 MSB bits; Version: 3 bits; mode: 3 LSB bits *)
  22. Stratum*: SHORTINT;
  23. Poll*:SHORTINT;
  24. Precision*:SHORTINT;
  25. RootDelay*:LONGINT;
  26. RootDispersion*:LONGINT;
  27. ReferenceIdentifier*:LONGINT;
  28. ReferenceTimestamp*:HUGEINT;
  29. OriginTimestamp*:HUGEINT;
  30. ReceiveTimestamp*:HUGEINT;
  31. TransmitTimestamp*:HUGEINT;
  32. Extensions: ANY;
  33. END;
  34. (** get a single time record from an NTP time server. *)
  35. PROCEDURE Get*(fip:IP.Adr; fport:LONGINT; VAR p:Packet; VAR res:WORD);
  36. VAR socket: UDP.Socket; len, high, low:LONGINT;
  37. buf: ARRAY 1024 OF CHAR;
  38. sw:Streams.StringWriter;
  39. sr: Streams.StringReader;
  40. packet:Packet;
  41. fip1:IP.Adr; fport1:LONGINT;
  42. BEGIN
  43. NEW(socket, UDP.NilPort, res);
  44. IF res=UDP.Ok THEN (* send a NTP client record *)
  45. NEW(sw, 1024);
  46. sw.Net8(0*64 + 3*8 +3); (*leap indicator currently not set; packet version=3; packet.Header = client; *)
  47. sw.Net8(packet.Stratum);
  48. sw.Net8(packet.Poll);
  49. sw.Net8(packet.Precision);
  50. sw.Net32(packet.RootDelay);
  51. sw.Net32(packet.RootDispersion);
  52. sw.Net32(packet.ReferenceIdentifier);
  53. sw.Net32(LONGINT(packet.ReferenceTimestamp DIV 100000000H)); sw.Net32(LONGINT(packet.ReferenceTimestamp MOD 100000000H));
  54. sw.Net32(LONGINT(packet.OriginTimestamp DIV 100000000H)); sw.Net32(LONGINT(packet.OriginTimestamp MOD 100000000H));
  55. sw.Net32(LONGINT(packet.ReceiveTimestamp DIV 100000000H)); sw.Net32(LONGINT(packet.ReceiveTimestamp MOD 100000000H));
  56. sw.Net32(LONGINT(packet.TransmitTimestamp DIV 100000000H)); sw.Net32(LONGINT(packet.TransmitTimestamp MOD 100000000H));
  57. sw.Update;
  58. sw.GetRaw(buf, len);
  59. socket.Send( fip, fport, buf, 0, 48, res );
  60. socket.Receive(buf,0,48, 1000, fip1, fport1, len, res);
  61. IF (res=UDP.Ok)&(len>=48) THEN (* receive the modified NTP record *)
  62. NEW(sr,1024);
  63. sr.SetRaw(buf,0,len);
  64. p.Header:=SHORT(SHORT(sr.Net8()));
  65. p.Stratum:=SHORT(SHORT(sr.Net8()));
  66. p.Poll:=SHORT(SHORT(sr.Net8()));
  67. p.Precision:=SHORT(SHORT(sr.Net8()));
  68. p.RootDelay:=sr.Net32();
  69. p.RootDispersion:=sr.Net32();
  70. p.ReferenceIdentifier:=sr.Net32();
  71. high:=sr.Net32(); low:=sr.Net32(); p.ReferenceTimestamp := 100000000H*high + low MOD 100000000H;
  72. high:=sr.Net32(); low:=sr.Net32(); p.OriginTimestamp := 100000000H*high + low MOD 100000000H;
  73. high:=sr.Net32(); low:=sr.Net32(); p.ReceiveTimestamp := 100000000H*high + low MOD 100000000H;
  74. high:=sr.Net32(); low:=sr.Net32(); p.TransmitTimestamp := 100000000H*high + low MOD 100000000H;
  75. (* currently no handling of optional extensions*)
  76. END;
  77. END;
  78. END Get;
  79. (* timeZone and daylightSaving in minutes is difference to UTC in minutes *)
  80. PROCEDURE SetSystemTime* (time: HUGEINT; timeZone, daylightSaving: LONGINT);
  81. VAR dt: Dates.DateTime; frac:HUGEINT;
  82. BEGIN {EXCLUSIVE}
  83. frac:=time MOD 100000000H;
  84. time:=time DIV 100000000H MOD 100000000H;
  85. dt:=Dates.ZeroDateNTP;
  86. Dates.AddSeconds(dt, LONGINT(time MOD (60*60*24)));
  87. Dates.AddMinutes(dt, timeZone+daylightSaving);
  88. time:=time DIV (60*60*24);
  89. Dates.AddDays(dt, LONGINT(time));
  90. Machine.PutNVByte(0BH, 82X); (* disable clock & interrupt *)
  91. Machine.PutNVByte(0, CHR(dt.seconds0));
  92. Machine.PutNVByte(2, CHR(dt.minutes));
  93. Machine.PutNVByte(4, CHR(dt.hours));
  94. Machine.PutNVByte(7, CHR(dt.day));
  95. Machine.PutNVByte(8, CHR(dt.month));
  96. Machine.PutNVByte(9, CHR(dt.year));
  97. Machine.PutNVByte(0BH, 12X) (* 24 hour mode & 1 second interrupt *)
  98. END SetSystemTime;
  99. (* to be done: full NTP synchronization algorithm from RFC5905 to set machine clock *)
  100. PROCEDURE SynchronizeNTP;
  101. END SynchronizeNTP;
  102. (* to be done: SNTP synchronization algo from RFC 4330; parametrization of timeZone and daylightSaving*)
  103. PROCEDURE SynchronizeSNTP*(context:Commands.Context);
  104. VAR fip: IP.Adr; port, res:LONGINT; packet:Packet;
  105. (* SNTP algorithm:
  106. The roundtrip delay d and system clock offset t are defined as:
  107. d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2.
  108. This can be used to set the system clock, if a high accuracy clock is retrievable & settable on this hardware/OS
  109. the following minimal implementation just juses TransmitTimestamp
  110. *)
  111. BEGIN
  112. DNS.HostByName("0.ch.pool.ntp.org", fip, port);
  113. Get(fip, NTPPort, packet, res);
  114. IF res=UDP.Ok THEN
  115. SetSystemTime(packet.TransmitTimestamp, 60, 60);
  116. context.out.String("Set system time to ");
  117. context.out.Int(packet.TransmitTimestamp MOD 100000000H, 16);
  118. context.out.Int(packet.TransmitTimestamp DIV 100000000H MOD 100000000H, 16);
  119. context.out.Ln; context.out.Update;
  120. END;
  121. END SynchronizeSNTP;
  122. PROCEDURE GetSimpleTime*(context:Commands.Context);
  123. VAR ipstr: ARRAY 64 OF CHAR; port, res, i:LONGINT;machineTimer1,machineTimer2: HUGEINT; fip: IP.Adr; packet:Packet;
  124. BEGIN
  125. IF ~context.arg.GetString(ipstr) THEN Configuration.Get("NTP.Server0",ipstr,res); END;
  126. (* note that DNS.HostByName might also deliver a default NTP server when an empty string and an NTP port number is given*)
  127. DNS.HostByName(ipstr, fip, port);
  128. IF ~context.arg.GetInteger(port,FALSE) THEN port:=NTPPort END;
  129. Get(fip, port, packet, res);
  130. context.out.String("checking server "); context.out.String(ipstr);context.out.Char(":"); context.out.Int(port,0); context.out.Ln; context.out.Update;
  131. context.out.String("SNTP result, transmit time [seconds.fraction]: "); context.out.Ln;
  132. context.out.Int(res,5); context.out.Char(":");
  133. context.out.Int(packet.TransmitTimestamp DIV 100000000H MOD 100000000H,0);
  134. context.out.Char(".");
  135. context.out.Int(packet.TransmitTimestamp MOD 100000000H,0); context.out.Ln;
  136. context.out.Update;
  137. END GetSimpleTime;
  138. END WebNetworkTimeProtocol.
  139. (*each county/region has its own ntp server pools: *)
  140. WebNetworkTimeProtocol.GetSimpleTime 0.ch.pool.ntp.org~
  141. WebNetworkTimeProtocol.GetSimpleTime 1.ch.pool.ntp.org 123~
  142. WebNetworkTimeProtocol.GetSimpleTime 2.ch.pool.ntp.org 124~
  143. WebNetworkTimeProtocol.GetSimpleTime 3.ch.pool.ntp.org~
  144. WebNetworkTimeProtocol.GetSimpleTime~
  145. WebNetworkTimeProtocol.SynchronizeSNTP ~
  146. System.Free WebNetworkTimeProtocol~
  147. System.Free Dates ~