#include "all.h" #include "../ip/ip.h" typedef struct Sntppkt { uchar d[Easize]; /* ether header */ uchar s[Easize]; uchar type[2]; uchar vihl; /* ip header */ uchar tos; uchar length[2]; uchar id[2]; uchar frag[2]; uchar ttl; uchar proto; uchar cksum[2]; uchar src[Pasize]; uchar dst[Pasize]; uchar udpsrc[2]; /* Src port */ uchar udpdst[2]; /* Dst port */ uchar udplen[2]; /* Packet length */ uchar udpsum[2]; /* Checksum including header */ uchar mode; /* li:2, vn:3, mode:3 */ uchar stratum; /* level of local clock */ char poll; /* log2(max interval between polls) */ char precision; /* log2(clock precision) -6 => mains, -18 => us */ uchar rootdelay[4]; /* round trip delay to reference 16.16 fraction */ uchar dispersion[4]; /* max error to reference */ uchar clockid[4]; /* reference clock identifier */ uchar reftime[8]; /* local time when clock set */ uchar orgtime[8]; /* local time when client sent request */ uchar rcvtime[8]; /* time request arrived */ uchar xmttime[8]; /* time reply sent */ } Sntppkt; enum { Sntpsize = 4 + 3 * 4 + 4 * 8, Version = 1, Stratum = 0, Poll = 0, LI = 0, Symmetric = 2, ClientMode = 3, ServerMode = 4, Epoch = 86400 * (365 * 70 + 17), /* 1900 to 1970 in seconds */ }; #define dprint(...) if(cons.flags&sntp.flag)print(__VA_ARGS__); static struct { Lock; int flag; int gotreply; int kicked; int active; Rendez r; Rendez doze; } sntp; static int done(void*) { return sntp.gotreply != 0; } static int kicked(void*) { return sntp.kicked != 0; } void sntprecv(Msgbuf *mb, Ifc*) { int v, li, m; Sntppkt *sh; Timet now, dt; Udppkt *uh; uh = (Udppkt*)mb->data; dprint("sntp: receive from %I\n", uh->src); if(memcmp(uh->src, sntpip, 4) != 0){ dprint("sntp: wrong IP\n"); goto overandout; } if(nhgets(uh->udplen) < Sntpsize){ dprint("sntp: packet too small\n"); goto overandout; } sh = (Sntppkt *)mb->data; v = (sh->mode >> 3) & 7; li = (sh->mode >> 6); m = sh->mode & 7; /* * if reply from right place and contains time set gotreply * and wakeup r */ dprint( "sntp: LI %d version %d mode %d stratum %d\n" "sntp: poll %d precision %d rootdelay %ld dispersion %ld\n", li, v, m, sh->stratum, sh->poll, sh->precision, nhgetl(sh->rootdelay), nhgetl(sh->dispersion)); if(v == 0 || v > 3){ dprint("sntp: unsupported version\n"); goto overandout; } if(m >= 6 || m == ClientMode){ dprint("sntp: wrong mode\n"); goto overandout; } now = nhgetl(sh->xmttime) - Epoch; if(li == 3 || now == 0 || sh->stratum == 0){ print("sntp: time server not synchronized\n"); goto overandout; } if(dt = now-time()){ settime(now); setrtc(now); if(dt < 0) dt = -dt; if(dt > 1 || (cons.flags&sntp.flag)) print("sntp: %T [adjust %+ld]\n", now, (long)dt); } sntp.gotreply = 1; wakeup(&sntp.r); overandout: mbfree(mb); } void sntpsend(void) { uchar tmp[Pasize]; ulong ip; ushort sum; Ifc *ifc; Msgbuf *mb; Sntppkt *s; /* find an interface on the same subnet as sntpip, if any */ ip = nhgetl(sntpip); for(ifc = enets; ifc; ifc = ifc->next){ if(isvalidip(ifc->ipa) && (nhgetl(ifc->ipa)&ifc->mask) == (ip&ifc->mask)) break; } /* if none, find an interface with a default gateway */ if(ifc == nil) for(ifc = enets; ifc; ifc = ifc->next) if(isvalidip(ifc->ipa) && isvalidip(ifc->netgate)) break; if(ifc == nil){ dprint("sntp: can't send to %I; no route\n", sntpip); return; } /* compose a UDP sntp request */ dprint("sntp: sending to %I on ifc %I\n", sntpip, ifc->ipa); mb = mballoc(Ensize+Ipsize+Udpsize+Sntpsize, 0, Mbsntp); s = (Sntppkt*)mb->data; /* IP fields */ memmove(s->src, ifc->ipa, Pasize); memmove(s->dst, sntpip, Pasize); s->proto = Udpproto; s->ttl = 0; /* Udp fields */ hnputs(s->udpsrc, SNTP_LOCAL); hnputs(s->udpdst, SNTP); hnputs(s->udplen, Sntpsize + Udpsize); /* Sntp fields */ memset(mb->data + Ensize+Ipsize+Udpsize, 0, Sntpsize); s->mode = 010 | ClientMode; s->poll = 6; hnputl(s->orgtime, rtctime() + Epoch); /* leave 0 fraction */ /* Compute the UDP sum - form psuedo header */ hnputs(s->cksum, Udpsize + Sntpsize); hnputs(s->udpsum, 0); sum = ptclcsum((uchar *)mb->data + Ensize + Ipsize - Udpphsize, Udpsize + Udpphsize + Sntpsize); hnputs(s->udpsum, sum); /* * now try to send it - cribbed from icmp.c */ memmove(tmp, s->dst, Pasize); if((nhgetl(ifc->ipa)&ifc->mask) != (nhgetl(s->dst)&ifc->mask)){ dprint("sntp: route via %I\n", ifc->netgate); iproute(tmp, s->dst, ifc->netgate); } ipsend1(mb, ifc, tmp); } void sntptask(void) { int i; dprint("sntp: running\n"); tsleep(&sntp.doze, kicked, 0, 2 * 60 * 1000); for(;;){ sntp.kicked = 0; dprint("sntp: poll time\n"); if(sntp.active && isvalidip(sntpip)){ sntp.gotreply = 0; for (i = 0; i < 3; i++){ sntpsend(); tsleep(&sntp.r, done, 0, 1000); if(sntp.gotreply) break; } /* clock has been set */ } tsleep(&sntp.doze, kicked, 0, 60 * 60 * 1000); } } void cmd_sntp(int argc, char *argv[]) { int i; uchar tip[Pasize]; if(argc <= 1){ print("sntp kick -- check time now\n"); print("sntp active -- toggle active status\n"); print("sntp ip -- set sntp ip\n"); return; } for(i=1; i