mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
Merge pull request #447 from DrDoof/feat/ntp
ntp: keep the time from an NTP server
This commit is contained in:
11 files changed
+618
-8
No files matched your search
@@ -71,6 +71,8 @@ SRCS = \
|
|||||||
sfp.c \
|
sfp.c \
|
||||||
dns.c \
|
dns.c \
|
||||||
dns_cli.c \
|
dns_cli.c \
|
||||||
|
ntp.c \
|
||||||
|
ntp_cli.c \
|
||||||
syslog.c \
|
syslog.c \
|
||||||
udp_apps.c
|
udp_apps.c
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -22,6 +22,7 @@
|
|||||||
#include "dhcp.h"
|
#include "dhcp.h"
|
||||||
#include "syslog.h"
|
#include "syslog.h"
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
|
#include "ntp.h"
|
||||||
#include "uip/uip.h"
|
#include "uip/uip.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
@@ -1869,6 +1870,8 @@ void cmd_parser(void) __banked
|
|||||||
parse_syslog();
|
parse_syslog();
|
||||||
} else if (cmd_compare(0, "dns")) {
|
} else if (cmd_compare(0, "dns")) {
|
||||||
dns_parse();
|
dns_parse();
|
||||||
|
} else if (cmd_compare(0, "ntp") || cmd_compare(0, "time")) {
|
||||||
|
ntp_parse();
|
||||||
} else if (cmd_compare(0, "ip")) {
|
} else if (cmd_compare(0, "ip")) {
|
||||||
if (cmd_compare(1, "dhcp")) {
|
if (cmd_compare(1, "dhcp")) {
|
||||||
dhcp_start();
|
dhcp_start();
|
||||||
@@ -2019,11 +2022,6 @@ void cmd_parser(void) __banked
|
|||||||
parse_storm();
|
parse_storm();
|
||||||
} else if (cmd_compare(0, "version")) {
|
} else if (cmd_compare(0, "version")) {
|
||||||
print_sw_version();
|
print_sw_version();
|
||||||
} else if (cmd_compare(0, "time")) {
|
|
||||||
print_string(" Tick counter: "); print_long(ticks); print_string(" Sec Counter: ");
|
|
||||||
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
|
||||||
print_sfr_data();
|
|
||||||
write_char('\n');
|
|
||||||
} else if (cmd_compare(0, "history")) {
|
} else if (cmd_compare(0, "history")) {
|
||||||
__xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK;
|
__xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK;
|
||||||
__xdata uint8_t found_begin = 0;
|
__xdata uint8_t found_begin = 0;
|
||||||
|
|||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
# Time
|
||||||
|
|
||||||
|
The switch has no battery backed clock. It counts seconds from power on, and
|
||||||
|
it can take the date and time from an NTP server once the network is up. The
|
||||||
|
time is lost at every restart and set again at the first synchronisation.
|
||||||
|
|
||||||
|
```
|
||||||
|
ntp server <name|ip> # default pool.ntp.org
|
||||||
|
ntp interval <1-1440> # minutes between two synchronisations, default 60
|
||||||
|
ntp timezone <+hh:mm> # offset from UTC, -12:00 to +14:00
|
||||||
|
ntp dst off|eu|us # daylight saving rule
|
||||||
|
ntp on
|
||||||
|
ntp off
|
||||||
|
ntp # settings and time
|
||||||
|
time # local date and time
|
||||||
|
```
|
||||||
|
|
||||||
|
The client speaks SNTP version 4 and synchronises at once when turned on,
|
||||||
|
then after every interval. A server given by name is resolved through the DNS
|
||||||
|
resolver before every synchronisation. An answer from an unsynchronised
|
||||||
|
server is ignored, and a failed attempt is repeated after 30 seconds.
|
||||||
|
|
||||||
|
The local time is UTC plus the time zone offset, plus one hour while daylight
|
||||||
|
saving time applies:
|
||||||
|
|
||||||
|
* `eu`: from 01:00 UTC on the last Sunday of March to 01:00 UTC on the last
|
||||||
|
Sunday of October;
|
||||||
|
* `us`: from 02:00 local time on the second Sunday of March to 02:00 local
|
||||||
|
time on the first Sunday of November.
|
||||||
|
|
||||||
|
The same settings, with the current local time and the last synchronisation,
|
||||||
|
are in the Time card of the System page. Settings made there or on the
|
||||||
|
console are kept only once the configuration is saved.
|
||||||
+59
-3
@@ -90,7 +90,14 @@ sy_ip_err:"Invalid IP / netmask / gateway",sy_host_err:"Hostname: 1-23 printable
|
|||||||
sy_net_q:"Apply network settings?",sy_net_d:"The management IP changes to {ip}: this page will need to be reopened there.",
|
sy_net_q:"Apply network settings?",sy_net_d:"The management IP changes to {ip}: this page will need to be reopened there.",
|
||||||
sy_ip_changed:"IP changed, reconnect at http://{ip}/",sy_dhcp_q:"Switch to DHCP?",
|
sy_ip_changed:"IP changed, reconnect at http://{ip}/",sy_dhcp_q:"Switch to DHCP?",
|
||||||
sy_dhcp_d:"The switch requests an address via DHCP. You must find its new IP to reconnect.",
|
sy_dhcp_d:"The switch requests an address via DHCP. You must find its new IP to reconnect.",
|
||||||
sy_sysip_err:"Invalid syslog server IP",sy_dns1:"DNS server",sy_dns2:"Second DNS",sy_dns_err:"Invalid DNS server IP",sy_dns_dhcp:"from DHCP: {ip}",sy_sysport_err:"Syslog port must be 1-65535",
|
sy_sysip_err:"Invalid syslog server IP",sy_dns1:"DNS server",sy_dns2:"Second DNS",sy_dns_err:"Invalid DNS server IP",sy_dns_dhcp:"from DHCP: {ip}",
|
||||||
|
sy_time:"Time",sy_ntp:"NTP",sy_ntpsrv:"NTP server",sy_ntpsrv_p:"host name or IPv4 address",sy_ntpint:"Poll interval [min]",
|
||||||
|
sy_tz:"Time zone",sy_dst:"Daylight saving",sy_dst_eu:"Europe",
|
||||||
|
sy_dst_us:"United States",sy_now:"Local time",
|
||||||
|
sy_sync:"Last synchronisation",sy_ntpaddr:"Server address",sy_nosync:"not synchronised",sy_waiting:"waiting for the server",
|
||||||
|
sy_ago:"{n} min ago",sy_ntp_err:"Enter the host name or IPv4 address of the NTP server",sy_ntpint_err:"Poll interval must be 1-1440",
|
||||||
|
sy_time_note:"The switch has no battery backed clock: the time is lost at every restart and set again from the NTP server once the network is up. Save to flash to keep the settings.",
|
||||||
|
sy_sysport_err:"Syslog port must be 1-65535",
|
||||||
sy_pw_len:"Password: 1-20 characters",sy_pw_space:"Password cannot contain spaces",sy_pw_match:"Passwords do not match",
|
sy_pw_len:"Password: 1-20 characters",sy_pw_space:"Password cannot contain spaces",sy_pw_match:"Passwords do not match",
|
||||||
sy_pw_q:"Change admin password?",sy_pw_d:"Takes effect immediately for new logins. Save to flash to persist.",
|
sy_pw_q:"Change admin password?",sy_pw_d:"Takes effect immediately for new logins. Save to flash to persist.",
|
||||||
sy_reboot_q:"Reboot the switch?",sy_reboot_d:"There are UNSAVED changes and they will be lost. Save to flash first if you want to keep them.",
|
sy_reboot_q:"Reboot the switch?",sy_reboot_d:"There are UNSAVED changes and they will be lost. Save to flash first if you want to keep them.",
|
||||||
@@ -785,6 +792,8 @@ var CONF_CMDS=[
|
|||||||
/^syslog\s+(on|off)$/,/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^syslog\s+port\s+\d{1,5}$/,
|
/^syslog\s+(on|off)$/,/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^syslog\s+port\s+\d{1,5}$/,
|
||||||
/^session\s+\d{1,5}$/,
|
/^session\s+\d{1,5}$/,
|
||||||
/^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/,
|
/^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/,
|
||||||
|
/^ntp\s+(on|off)$/,/^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,/^ntp\s+interval\s+\d{1,4}$/,
|
||||||
|
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,/^ntp\s+dst\s+(off|eu|us)$/,
|
||||||
/^passwd\s+\S+$/,/^hostname\s+\S{1,23}$/,
|
/^passwd\s+\S+$/,/^hostname\s+\S{1,23}$/,
|
||||||
/^vlan\s+\d{1,4}\s+d$/,/^vlan\s+\d{1,4}\s+mgmt$/,
|
/^vlan\s+\d{1,4}\s+d$/,/^vlan\s+\d{1,4}\s+mgmt$/,
|
||||||
/^vlan\s+\d{1,4}(\s+[a-zA-Z]\w*)?(\s+\d{1,2}t?)+$/,
|
/^vlan\s+\d{1,4}(\s+[a-zA-Z]\w*)?(\s+\d{1,2}t?)+$/,
|
||||||
@@ -2155,11 +2164,58 @@ $("cfgreload").addEventListener("click",cfgReload);
|
|||||||
$("cfgwrite").addEventListener("click",function(){
|
$("cfgwrite").addEventListener("click",function(){
|
||||||
writeConfig($("cfgedit").value,t("cw_title"));
|
writeConfig($("cfgedit").value,t("cw_title"));
|
||||||
});
|
});
|
||||||
tabHooks.system={enter:sysLoad};
|
var ntpBase=null,ntpTimer=null;
|
||||||
|
function tzText(m){var a=Math.abs(m);return(m<0?"-":"+")+String(Math.floor(a/60)).padStart(2,"0")+":"+String(a%60).padStart(2,"0")}
|
||||||
|
function ntpTick(){
|
||||||
|
if(!ntpBase)return;
|
||||||
|
var d=new Date(ntpBase.ms+(Date.now()-ntpBase.at));
|
||||||
|
function p(n){return String(n).padStart(2,"0")}
|
||||||
|
$("sy-sync").textContent=t("sy_ago",{n:ntpBase.sync+Math.floor((Date.now()-ntpBase.at)/60000)});
|
||||||
|
$("sy-now").textContent=d.getUTCFullYear()+"-"+p(d.getUTCMonth()+1)+"-"+p(d.getUTCDate())+" "
|
||||||
|
+p(d.getUTCHours())+":"+p(d.getUTCMinutes())+":"+p(d.getUTCSeconds())+" UTC"+ntpBase.tz;
|
||||||
|
}
|
||||||
|
function ntpLoad(){
|
||||||
|
return api("/cmd",{method:"POST",body:"ntp"}).then(function(r){
|
||||||
|
var m=r.body.match(/NTP (on|off), server (\S*), address ([\d.]+), interval (\d+) min, timezone ([+-]\d\d:\d\d), dst (\w+)/);
|
||||||
|
if(!m)return;
|
||||||
|
var x=r.body.match(/Time (\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d) UTC([+-]\d\d:\d\d)( DST)?, synchronised (\d+) min ago/);
|
||||||
|
$("sy-ntpaddr").textContent=m[3]==="0.0.0.0"?"-":m[3];
|
||||||
|
if(x){
|
||||||
|
ntpBase={at:Date.now(),ms:Date.UTC(+x[1],+x[2]-1,+x[3],+x[4],+x[5],+x[6]),tz:x[7]+(x[8]?" DST":""),sync:+x[9]};
|
||||||
|
ntpTick();
|
||||||
|
}else{
|
||||||
|
ntpBase=null;
|
||||||
|
$("sy-now").textContent=t("sy_nosync");
|
||||||
|
$("sy-sync").textContent=m[1]==="on"?t("sy_waiting"):"-";
|
||||||
|
}
|
||||||
|
if($("sy-ntpcard").dataset.dirty)return;
|
||||||
|
$("sy-ntp").checked=m[1]==="on";$("sy-ntpsrv").value=m[2];$("sy-ntpint").value=m[4];
|
||||||
|
$("sy-tz").value=m[5];$("sy-dst").value=m[6];
|
||||||
|
}).catch(function(){});
|
||||||
|
}
|
||||||
|
[-720,-660,-600,-570,-540,-480,-420,-360,-300,-240,-210,-180,-120,-60,0,60,120,180,210,240,270,300,330,345,
|
||||||
|
360,390,420,480,525,540,570,600,630,660,720,765,780,840].forEach(function(z){
|
||||||
|
$("sy-tz").appendChild(h("option",{value:tzText(z),text:"UTC"+tzText(z)}));
|
||||||
|
});
|
||||||
|
["change","input"].forEach(function(ev){$("sy-ntpcard").addEventListener(ev,function(){this.dataset.dirty="1"})});
|
||||||
|
$("sy-ntpapply").addEventListener("click",function(){
|
||||||
|
var srv=$("sy-ntpsrv").value.trim(),iv=$("sy-ntpint").value.trim();
|
||||||
|
if(!/^[A-Za-z0-9.-]{1,47}$/.test(srv)){toast(t("sy_ntp_err"),"err");return;}
|
||||||
|
if(!(+iv>=1&&+iv<=1440)){toast(t("sy_ntpint_err"),"err");return;}
|
||||||
|
delete $("sy-ntpcard").dataset.dirty;
|
||||||
|
postCmds(["ntp server "+srv,"ntp interval "+iv,"ntp timezone "+$("sy-tz").value,"ntp dst "+$("sy-dst").value,
|
||||||
|
"ntp "+($("sy-ntp").checked?"on":"off")]).then(ntpLoad).catch(function(){});
|
||||||
|
});
|
||||||
|
$("sy-ntprefresh").addEventListener("click",ntpLoad);
|
||||||
|
tabHooks.system={
|
||||||
|
enter:function(){sysLoad();ntpLoad();clearInterval(ntpTimer);ntpTimer=setInterval(ntpTick,1000)},
|
||||||
|
leave:function(){clearInterval(ntpTimer)},
|
||||||
|
};
|
||||||
|
|
||||||
var CONF_OVERWRITE=[
|
var CONF_OVERWRITE=[
|
||||||
/^ip\b/,/^gw\b/,/^netmask\b/,/^hostname\b/,
|
/^ip\b/,/^gw\b/,/^netmask\b/,/^hostname\b/,
|
||||||
/^syslog\s+ip\b/,/^syslog\s+port\b/,/^dns\s+server\b/,/^passwd\b/,/^session\b/,
|
/^syslog\s+ip\b/,/^syslog\s+port\b/,/^dns\s+server\b/,/^passwd\b/,/^session\b/,
|
||||||
|
/^ntp\s+server\b/,/^ntp\s+interval\b/,/^ntp\s+timezone\b/,/^ntp\s+dst\b/,
|
||||||
/^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
|
/^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
|
||||||
/^pvid\s+\d{1,2}\b/,
|
/^pvid\s+\d{1,2}\b/,
|
||||||
/^port\s+\d{1,2}(?!\s+name\b)/,/^port\s+\d{1,2}\s+name\b/,
|
/^port\s+\d{1,2}(?!\s+name\b)/,/^port\s+\d{1,2}\s+name\b/,
|
||||||
@@ -2169,7 +2225,7 @@ var CONF_OVERWRITE=[
|
|||||||
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/,
|
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/,
|
||||||
/^igmp\b/,/^mtu\s+\d{1,2}\b/,/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\b/,
|
/^igmp\b/,/^mtu\s+\d{1,2}\b/,/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\b/,
|
||||||
];
|
];
|
||||||
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
|
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
|
||||||
function mergeConf(base,texts){
|
function mergeConf(base,texts){
|
||||||
var conf=base.slice();
|
var conf=base.slice();
|
||||||
function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})}
|
function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})}
|
||||||
|
|||||||
@@ -411,6 +411,22 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
|||||||
<input class="in" id="sy-sesstmo" style="width:80px" type="number" min="1" max="65535"></label>
|
<input class="in" id="sy-sesstmo" style="width:80px" type="number" min="1" max="65535"></label>
|
||||||
<p class="small mut" data-i18n="sy_services_note">Service state reflects the startup config; runtime state is not readable.</p>
|
<p class="small mut" data-i18n="sy_services_note">Service state reflects the startup config; runtime state is not readable.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card" id="sy-ntpcard"><h2 data-i18n="sy_time">Time</h2>
|
||||||
|
<label class="row" style="justify-content:space-between"><span data-i18n="sy_ntp">NTP</span>
|
||||||
|
<span class="switch"><input type="checkbox" id="sy-ntp"><i></i></span></label>
|
||||||
|
<label class="row"><span class="lb" data-i18n="sy_ntpsrv">NTP server</span><input class="in" id="sy-ntpsrv" maxlength="47" data-i18n-p="sy_ntpsrv_p"></label>
|
||||||
|
<label class="row"><span class="lb" data-i18n="sy_ntpint">Poll interval [min]</span><input class="in sm" id="sy-ntpint" type="number" min="1" max="1440"></label>
|
||||||
|
<label class="row"><span class="lb" data-i18n="sy_tz">Time zone</span><select class="in" id="sy-tz"></select></label>
|
||||||
|
<label class="row"><span class="lb" data-i18n="sy_dst">Daylight saving</span><select class="in" id="sy-dst"><option value="off" data-i18n="c_offc">Off</option><option value="eu" data-i18n="sy_dst_eu">Europe</option><option value="us" data-i18n="sy_dst_us">United States</option></select></label>
|
||||||
|
<div style="display:flex;gap:10px;margin-top:8px">
|
||||||
|
<button class="ctl pri" id="sy-ntpapply" data-i18n="c_apply">Apply</button>
|
||||||
|
<button class="ctl" id="sy-ntprefresh" data-i18n="c_refresh">Refresh</button>
|
||||||
|
</div>
|
||||||
|
<label class="row" style="margin-top:10px"><span class="lb" data-i18n="sy_now">Local time</span><span class="mono" id="sy-now">-</span></label>
|
||||||
|
<label class="row"><span class="lb" data-i18n="sy_sync">Last synchronisation</span><span id="sy-sync">-</span></label>
|
||||||
|
<label class="row"><span class="lb" data-i18n="sy_ntpaddr">Server address</span><span class="mono" id="sy-ntpaddr">-</span></label>
|
||||||
|
<p class="small mut" style="margin-top:8px" data-i18n="sy_time_note">The switch has no battery backed clock.</p>
|
||||||
|
</div>
|
||||||
<div class="card"><h2 data-i18n="sy_password">Admin password</h2>
|
<div class="card"><h2 data-i18n="sy_password">Admin password</h2>
|
||||||
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" maxlength="20"></label>
|
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" maxlength="20"></label>
|
||||||
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" maxlength="20"></label>
|
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" maxlength="20"></label>
|
||||||
|
|||||||
@@ -0,0 +1,341 @@
|
|||||||
|
/*
|
||||||
|
* SNTP client and local time for the RTL837x platform
|
||||||
|
* This code is in the Public Domain
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "rtl837x_common.h"
|
||||||
|
#include "rtl837x_regs.h"
|
||||||
|
#include "cmd_parser.h"
|
||||||
|
#include "uip/uip.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "ntp.h"
|
||||||
|
|
||||||
|
#pragma codeseg BANK3
|
||||||
|
#pragma constseg BANK3
|
||||||
|
|
||||||
|
#define NTP_PORT 123
|
||||||
|
#define NTP_TRIES 3
|
||||||
|
#define NTP_TIMEOUT (3 * SYS_TICK_HZ)
|
||||||
|
#define NTP_RETRY 30
|
||||||
|
#define NTP_UNIX_EPOCH 2208988800UL
|
||||||
|
|
||||||
|
#define NTP_IDLE 0
|
||||||
|
#define NTP_RESOLVE 1
|
||||||
|
#define NTP_SEND 2
|
||||||
|
#define NTP_WAIT 3
|
||||||
|
|
||||||
|
extern __xdata uint8_t sfr_data[4];
|
||||||
|
extern volatile __xdata uint32_t ticks;
|
||||||
|
|
||||||
|
__xdata struct ntp_state ntp_state;
|
||||||
|
__xdata uip_ipaddr_t ntp_ip;
|
||||||
|
__xdata uint32_t ntp_now;
|
||||||
|
__xdata uint32_t ntp_t;
|
||||||
|
__xdata uint16_t ntp_year;
|
||||||
|
__xdata uint8_t ntp_mon, ntp_mday, ntp_hour, ntp_min, ntp_sec, ntp_wday, ntp_mlen;
|
||||||
|
|
||||||
|
/* days in each month of a common year */
|
||||||
|
static __code const uint8_t ntp_mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||||
|
|
||||||
|
static __code const char ntp_default_server[] = "pool.ntp.org";
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t ntp_uptime(void) __reentrant
|
||||||
|
{
|
||||||
|
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
||||||
|
((__xdata uint8_t *)&ntp_now)[0] = sfr_data[3];
|
||||||
|
((__xdata uint8_t *)&ntp_now)[1] = sfr_data[2];
|
||||||
|
((__xdata uint8_t *)&ntp_now)[2] = sfr_data[1];
|
||||||
|
((__xdata uint8_t *)&ntp_now)[3] = sfr_data[0];
|
||||||
|
return ntp_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t ntp_divmod(uint8_t d) __reentrant
|
||||||
|
{
|
||||||
|
uint8_t i, r = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
r = (r << 1) | (((uint8_t *)&ntp_t)[3] >> 7);
|
||||||
|
ntp_t <<= 1;
|
||||||
|
if (r >= d) {
|
||||||
|
r -= d;
|
||||||
|
ntp_t |= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t ntp_leap(uint16_t y) __reentrant
|
||||||
|
{
|
||||||
|
return !(y % 4) && ((y % 100) || !(y % 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ntp_split(uint32_t t) __reentrant
|
||||||
|
{
|
||||||
|
uint16_t days;
|
||||||
|
|
||||||
|
ntp_t = t;
|
||||||
|
ntp_sec = ntp_divmod(60);
|
||||||
|
ntp_min = ntp_divmod(60);
|
||||||
|
ntp_hour = ntp_divmod(24);
|
||||||
|
days = ntp_t;
|
||||||
|
ntp_wday = (days + 4) % 7;
|
||||||
|
for (ntp_year = 1970; days >= 365 + ntp_leap(ntp_year); ntp_year++)
|
||||||
|
days -= 365 + ntp_leap(ntp_year);
|
||||||
|
for (ntp_mon = 1; ; ntp_mon++) {
|
||||||
|
ntp_mlen = ntp_mdays[ntp_mon - 1] + (ntp_mon == 2 && ntp_leap(ntp_year));
|
||||||
|
if (days < ntp_mlen)
|
||||||
|
break;
|
||||||
|
days -= ntp_mlen;
|
||||||
|
}
|
||||||
|
ntp_mday = days + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t ntp_sunday(uint8_t n) __reentrant
|
||||||
|
{
|
||||||
|
uint8_t first = (uint16_t)(ntp_wday + 36 - ntp_mday) % 7U;
|
||||||
|
|
||||||
|
first = 1 + (uint16_t)(7 - first) % 7U;
|
||||||
|
if (n)
|
||||||
|
return first + 7 * (n - 1);
|
||||||
|
return first + 7 * ((uint16_t)(ntp_mlen - first) / 7U);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t ntp_in_dst(uint8_t sm, uint8_t sn, uint8_t sh, uint8_t em, uint8_t en, uint8_t eh) __reentrant
|
||||||
|
{
|
||||||
|
uint8_t day;
|
||||||
|
|
||||||
|
if (ntp_mon < sm || ntp_mon > em)
|
||||||
|
return 0;
|
||||||
|
if (ntp_mon > sm && ntp_mon < em)
|
||||||
|
return 1;
|
||||||
|
if (ntp_mon == sm) {
|
||||||
|
day = ntp_sunday(sn);
|
||||||
|
return ntp_mday > day || (ntp_mday == day && ntp_hour >= sh);
|
||||||
|
}
|
||||||
|
day = ntp_sunday(en);
|
||||||
|
return ntp_mday < day || (ntp_mday == day && ntp_hour < eh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ntp_print2(uint8_t v) __reentrant
|
||||||
|
{
|
||||||
|
write_char('0' + v / 10);
|
||||||
|
write_char('0' + v % 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ntp_print_offset(int16_t off) __reentrant
|
||||||
|
{
|
||||||
|
uint16_t a = off < 0 ? -off : off;
|
||||||
|
|
||||||
|
write_char(off < 0 ? '-' : '+');
|
||||||
|
ntp_print2(a / 60);
|
||||||
|
write_char(':');
|
||||||
|
ntp_print2(a % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ntp_connect(__xdata uint8_t *a) __reentrant
|
||||||
|
{
|
||||||
|
if (ntp_state.conn)
|
||||||
|
uip_udp_remove(ntp_state.conn);
|
||||||
|
uip_ipaddr(ntp_ip, a[0], a[1], a[2], a[3]);
|
||||||
|
ntp_state.conn = uip_udp_new(&ntp_ip, HTONS(NTP_PORT));
|
||||||
|
if (!ntp_state.conn)
|
||||||
|
print_string("NTP: no free UDP connection\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ntp_retry(void) __reentrant
|
||||||
|
{
|
||||||
|
ntp_state.phase = NTP_IDLE;
|
||||||
|
ntp_state.next = ntp_uptime() + NTP_RETRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ntp_request(void) __reentrant
|
||||||
|
{
|
||||||
|
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
for (i = 1; i < 48; i++)
|
||||||
|
p[i] = 0;
|
||||||
|
p[0] = 0x23;
|
||||||
|
uip_udp_send(48);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ntp_receive(void) __reentrant
|
||||||
|
{
|
||||||
|
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
|
||||||
|
|
||||||
|
if (ntp_state.phase != NTP_WAIT || uip_datalen() < 48)
|
||||||
|
return;
|
||||||
|
if ((p[0] & 7) != 4 || (p[0] & 0xc0) == 0xc0 || !p[1] || p[1] > 15) {
|
||||||
|
ntp_retry();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
((__xdata uint8_t *)&ntp_state.utc)[0] = p[43];
|
||||||
|
((__xdata uint8_t *)&ntp_state.utc)[1] = p[42];
|
||||||
|
((__xdata uint8_t *)&ntp_state.utc)[2] = p[41];
|
||||||
|
((__xdata uint8_t *)&ntp_state.utc)[3] = p[40];
|
||||||
|
ntp_state.utc -= NTP_UNIX_EPOCH;
|
||||||
|
ntp_state.at = ntp_uptime();
|
||||||
|
ntp_state.stratum = p[1];
|
||||||
|
if (!ntp_state.synced) {
|
||||||
|
ntp_state.synced = 1;
|
||||||
|
print_string("NTP: time set from ");
|
||||||
|
print_ip(ntp_state.addr);
|
||||||
|
write_char('\n');
|
||||||
|
}
|
||||||
|
ntp_state.phase = NTP_IDLE;
|
||||||
|
ntp_state.next = ntp_state.at + (uint32_t)ntp_state.interval * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ntp_init(void) __banked
|
||||||
|
{
|
||||||
|
for (ntp_mon = 0; ntp_mon < sizeof(ntp_default_server); ntp_mon++)
|
||||||
|
ntp_state.server[ntp_mon] = ntp_default_server[ntp_mon];
|
||||||
|
ntp_state.enabled = 0;
|
||||||
|
ntp_state.interval = 60;
|
||||||
|
ntp_state.offset = 0;
|
||||||
|
ntp_state.dst = NTP_DST_OFF;
|
||||||
|
ntp_state.phase = NTP_IDLE;
|
||||||
|
ntp_state.synced = 0;
|
||||||
|
ntp_state.conn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ntp_start(void) __banked
|
||||||
|
{
|
||||||
|
ntp_state.enabled = 1;
|
||||||
|
ntp_state.phase = NTP_IDLE;
|
||||||
|
ntp_state.next = 0;
|
||||||
|
ntp_state.addr[0] = ntp_state.addr[1] = ntp_state.addr[2] = ntp_state.addr[3] = 0;
|
||||||
|
ntp_connect(ntp_state.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ntp_stop(void) __banked
|
||||||
|
{
|
||||||
|
ntp_state.enabled = 0;
|
||||||
|
if (ntp_state.conn) {
|
||||||
|
uip_udp_remove(ntp_state.conn);
|
||||||
|
ntp_state.conn = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ntp_show_time(void) __banked __reentrant
|
||||||
|
{
|
||||||
|
int16_t off = ntp_state.offset;
|
||||||
|
uint8_t dst = 0;
|
||||||
|
uint32_t t;
|
||||||
|
|
||||||
|
if (!ntp_state.synced) {
|
||||||
|
print_string("Time not synchronised\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
t = ntp_state.utc + (ntp_uptime() - ntp_state.at);
|
||||||
|
if (ntp_state.dst == NTP_DST_EU) {
|
||||||
|
ntp_split(t);
|
||||||
|
dst = ntp_in_dst(3, 0, 1, 10, 0, 1);
|
||||||
|
} else if (ntp_state.dst == NTP_DST_US) {
|
||||||
|
ntp_split(t + (int32_t)off * 60);
|
||||||
|
dst = ntp_in_dst(3, 2, 2, 11, 1, 1);
|
||||||
|
}
|
||||||
|
if (dst)
|
||||||
|
off += 60;
|
||||||
|
ntp_split(t + (int32_t)off * 60);
|
||||||
|
print_string("Time ");
|
||||||
|
itoa_short(ntp_year);
|
||||||
|
write_char('-');
|
||||||
|
ntp_print2(ntp_mon);
|
||||||
|
write_char('-');
|
||||||
|
ntp_print2(ntp_mday);
|
||||||
|
write_char(' ');
|
||||||
|
ntp_print2(ntp_hour);
|
||||||
|
write_char(':');
|
||||||
|
ntp_print2(ntp_min);
|
||||||
|
write_char(':');
|
||||||
|
ntp_print2(ntp_sec);
|
||||||
|
print_string(" UTC");
|
||||||
|
ntp_print_offset(off);
|
||||||
|
print_string(dst ? " DST, synchronised " : ", synchronised ");
|
||||||
|
ntp_t = ntp_uptime() - ntp_state.at;
|
||||||
|
ntp_divmod(60);
|
||||||
|
itoa_short(ntp_t);
|
||||||
|
print_string(" min ago, stratum ");
|
||||||
|
itoa_short(ntp_state.stratum);
|
||||||
|
write_char('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ntp_show(void) __banked __reentrant
|
||||||
|
{
|
||||||
|
int16_t off = ntp_state.offset;
|
||||||
|
|
||||||
|
print_string(ntp_state.enabled ? "NTP on, server " : "NTP off, server ");
|
||||||
|
print_string_x(ntp_state.server);
|
||||||
|
print_string(", address ");
|
||||||
|
print_ip(ntp_state.addr);
|
||||||
|
print_string(", interval ");
|
||||||
|
itoa_short(ntp_state.interval);
|
||||||
|
print_string(" min, timezone ");
|
||||||
|
ntp_print_offset(off);
|
||||||
|
print_string(ntp_state.dst == NTP_DST_EU ? ", dst eu\n" : ntp_state.dst == NTP_DST_US ? ", dst us\n" : ", dst off\n");
|
||||||
|
ntp_show_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ntp_callback(uint16_t lport) __banked __reentrant
|
||||||
|
{
|
||||||
|
if (!ntp_state.conn || lport != ntp_state.conn->lport)
|
||||||
|
return;
|
||||||
|
if (uip_newdata()) {
|
||||||
|
ntp_receive();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (ntp_state.phase) {
|
||||||
|
case NTP_IDLE:
|
||||||
|
if (ntp_uptime() < ntp_state.next || dns_state.status == DNS_PENDING)
|
||||||
|
return;
|
||||||
|
memcpy(dns_state.name, ntp_state.server, DNS_NAME_LEN);
|
||||||
|
dns_state.verbose = 0;
|
||||||
|
dns_lookup();
|
||||||
|
ntp_state.phase = NTP_RESOLVE;
|
||||||
|
return;
|
||||||
|
case NTP_RESOLVE:
|
||||||
|
if (dns_state.status == DNS_PENDING)
|
||||||
|
return;
|
||||||
|
if (dns_state.status != DNS_DONE) {
|
||||||
|
ntp_retry();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(ntp_state.addr, dns_state.addr, 4);
|
||||||
|
ntp_state.tries = 0;
|
||||||
|
ntp_state.phase = NTP_SEND;
|
||||||
|
ntp_connect(ntp_state.addr);
|
||||||
|
return;
|
||||||
|
case NTP_SEND:
|
||||||
|
ntp_state.sent = ticks;
|
||||||
|
ntp_state.phase = NTP_WAIT;
|
||||||
|
ntp_request();
|
||||||
|
return;
|
||||||
|
case NTP_WAIT:
|
||||||
|
if (ticks - ntp_state.sent < NTP_TIMEOUT)
|
||||||
|
return;
|
||||||
|
if (++ntp_state.tries < NTP_TRIES)
|
||||||
|
ntp_state.phase = NTP_SEND;
|
||||||
|
else
|
||||||
|
ntp_retry();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef _NTP_H_
|
||||||
|
#define _NTP_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "dns.h"
|
||||||
|
|
||||||
|
#define NTP_DST_OFF 0
|
||||||
|
#define NTP_DST_EU 1
|
||||||
|
#define NTP_DST_US 2
|
||||||
|
|
||||||
|
struct uip_udp_conn;
|
||||||
|
|
||||||
|
struct ntp_state {
|
||||||
|
uint8_t enabled;
|
||||||
|
char server[DNS_NAME_LEN]; /* host name or dotted address */
|
||||||
|
uint8_t addr[4]; /* address of the server last used */
|
||||||
|
uint16_t interval; /* minutes between two synchronisations */
|
||||||
|
int16_t offset; /* time zone, minutes east of UTC */
|
||||||
|
uint8_t dst; /* NTP_DST_* */
|
||||||
|
uint8_t phase;
|
||||||
|
uint8_t tries;
|
||||||
|
uint8_t synced;
|
||||||
|
uint8_t stratum;
|
||||||
|
uint32_t utc; /* seconds since 1970 UTC at the last synchronisation */
|
||||||
|
uint32_t at; /* uptime seconds at the last synchronisation */
|
||||||
|
uint32_t next; /* uptime seconds of the next synchronisation */
|
||||||
|
uint32_t sent; /* ticks when the request went out */
|
||||||
|
struct uip_udp_conn *conn;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern __xdata struct ntp_state ntp_state;
|
||||||
|
|
||||||
|
void ntp_init(void) __banked;
|
||||||
|
void ntp_start(void) __banked;
|
||||||
|
void ntp_stop(void) __banked;
|
||||||
|
void ntp_show(void) __banked __reentrant;
|
||||||
|
void ntp_show_time(void) __banked __reentrant;
|
||||||
|
void ntp_callback(uint16_t lport) __banked __reentrant;
|
||||||
|
void ntp_parse(void) __banked __reentrant;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Command line interface of the NTP client for the RTL837x platform
|
||||||
|
* This code is in the Public Domain
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "rtl837x_common.h"
|
||||||
|
#include "cmd_parser.h"
|
||||||
|
#include "ntp.h"
|
||||||
|
|
||||||
|
#pragma codeseg BANK2
|
||||||
|
#pragma constseg BANK2
|
||||||
|
|
||||||
|
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||||
|
extern __xdata uint8_t cmd_words_len;
|
||||||
|
extern __xdata uint8_t cmd_words_b[15];
|
||||||
|
extern __xdata uint16_t atoi_results_short;
|
||||||
|
uint8_t cmd_compare(uint8_t start, __code const uint8_t * cmd);
|
||||||
|
uint8_t atoi_short(uint8_t idx);
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t ntp_cli_server(void) __reentrant
|
||||||
|
{
|
||||||
|
__xdata uint8_t *s = &cmd_buffer[cmd_words_b[2]];
|
||||||
|
uint8_t n;
|
||||||
|
|
||||||
|
for (n = 0; s[n] && s[n] != ' '; n++)
|
||||||
|
if (n == DNS_NAME_LEN - 1)
|
||||||
|
return 0;
|
||||||
|
memcpy(ntp_state.server, s, n);
|
||||||
|
ntp_state.server[n] = 0;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t ntp_cli_timezone(void) __reentrant
|
||||||
|
{
|
||||||
|
__xdata uint8_t *s = &cmd_buffer[cmd_words_b[2]];
|
||||||
|
uint8_t neg = 0, h = 0, m = 0, n = 0;
|
||||||
|
uint16_t v;
|
||||||
|
|
||||||
|
if (*s == '+' || *s == '-')
|
||||||
|
neg = *s++ == '-';
|
||||||
|
while (*s >= '0' && *s <= '9') {
|
||||||
|
h = h * 10 + (*s++ - '0');
|
||||||
|
if (++n > 2)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!n)
|
||||||
|
return 0;
|
||||||
|
if (*s == ':') {
|
||||||
|
s++;
|
||||||
|
if (s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9')
|
||||||
|
return 0;
|
||||||
|
m = (s[0] - '0') * 10 + s[1] - '0';
|
||||||
|
s += 2;
|
||||||
|
}
|
||||||
|
if ((*s && *s != ' ') || (m && m != 30 && m != 45))
|
||||||
|
return 0;
|
||||||
|
v = (uint16_t)h * 60 + m;
|
||||||
|
if (v > (neg ? 720 : 840))
|
||||||
|
return 0;
|
||||||
|
ntp_state.offset = neg ? -(int16_t)v : (int16_t)v;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ntp_parse(void) __banked __reentrant
|
||||||
|
{
|
||||||
|
if (cmd_compare(0, "time")) {
|
||||||
|
ntp_show_time();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_words_len < 2) {
|
||||||
|
ntp_show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "on")) {
|
||||||
|
ntp_start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "off")) {
|
||||||
|
ntp_stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_words_len < 3)
|
||||||
|
goto err;
|
||||||
|
if (cmd_compare(1, "server")) {
|
||||||
|
if (!ntp_cli_server())
|
||||||
|
goto err;
|
||||||
|
if (ntp_state.enabled)
|
||||||
|
ntp_start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "interval")) {
|
||||||
|
if (!atoi_short(cmd_words_b[2]) || !atoi_results_short || atoi_results_short > 1440)
|
||||||
|
goto err;
|
||||||
|
ntp_state.interval = atoi_results_short;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "timezone")) {
|
||||||
|
if (!ntp_cli_timezone())
|
||||||
|
goto err;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd_compare(1, "dst")) {
|
||||||
|
if (cmd_compare(2, "off"))
|
||||||
|
ntp_state.dst = NTP_DST_OFF;
|
||||||
|
else if (cmd_compare(2, "eu"))
|
||||||
|
ntp_state.dst = NTP_DST_EU;
|
||||||
|
else if (cmd_compare(2, "us"))
|
||||||
|
ntp_state.dst = NTP_DST_US;
|
||||||
|
else
|
||||||
|
goto err;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
err:
|
||||||
|
print_string("Error: ntp [on|off|server <name>|interval <1-1440>|timezone <+hh:mm>|dst off|eu|us]\n");
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "phy.h"
|
#include "phy.h"
|
||||||
#include "syslog.h"
|
#include "syslog.h"
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
|
#include "ntp.h"
|
||||||
#include "httpd/page_impl.h"
|
#include "httpd/page_impl.h"
|
||||||
#include "boot.h"
|
#include "boot.h"
|
||||||
#include "sfp.h"
|
#include "sfp.h"
|
||||||
@@ -1769,6 +1770,7 @@ void main(void)
|
|||||||
|
|
||||||
syslog_init();
|
syslog_init();
|
||||||
dns_init();
|
dns_init();
|
||||||
|
ntp_init();
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// This register seems to work on the RTL8373 only if also the SDS
|
// This register seems to work on the RTL8373 only if also the SDS
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ void udp_callbacks(void)
|
|||||||
dhcp_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
|
dhcp_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
|
||||||
syslog_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
|
syslog_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
|
||||||
dns_callback(uip_udp_conn->lport);
|
dns_callback(uip_udp_conn->lport);
|
||||||
|
ntp_callback(uip_udp_conn->lport);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "dhcp.h"
|
#include "dhcp.h"
|
||||||
#include "syslog.h"
|
#include "syslog.h"
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
|
#include "ntp.h"
|
||||||
|
|
||||||
void udp_callbacks(void);
|
void udp_callbacks(void);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user