summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordzwdz2024-05-04 18:22:23 +0200
committerdzwdz2024-05-04 18:22:23 +0200
commit8c7b8d9e4ec40c38c657851354fc74428f0df1ac (patch)
tree29588306bd4453de86fc5a4ba230537f8f47bea5 /src
parent32d7e504169351ce6a5542b4f559c7e4d3834b45 (diff)
user/ntpfs: implement a basic ntp client
time() will probably end up doing io. That sounded bad at first, but Plan 9 does that too (see /sys/src/libc/9sys/nsec.c), so it's probably fine. I might need better service management soon. Also, dunno what it should return before it makes contact with NTP. I could implement RTC support, but eh. Doesn't feel that necessary. I'll also need to remember how the hell threading works, so it can talk with the ntp daemon on another thread.
Diffstat (limited to 'src')
-rw-r--r--src/cmd/init/init.c7
-rw-r--r--src/cmd/netstack/udp.c11
-rw-r--r--src/cmd/netstack/util.h12
-rw-r--r--src/cmd/ntpfs.c102
4 files changed, 128 insertions, 4 deletions
diff --git a/src/cmd/init/init.c b/src/cmd/init/init.c
index d8cb1f4..68268bf 100644
--- a/src/cmd/init/init.c
+++ b/src/cmd/init/init.c
@@ -129,6 +129,13 @@ int main(void) {
MOUNT_AT("/") { fs_whitelist(allow); }
execv(argv[0], (void*)argv);
}
+ MOUNT_AT("/dev/ntp") {
+ const char *allow[] = {"/bin/ntpfs", "/net/connect/", NULL};
+ const char *argv[] = {"/bin/ntpfs", "/net/connect/0/10.69.0.1/udp/123", NULL};
+ MOUNT_AT("/") { fs_whitelist(allow); }
+ _sys_sleep(1000); /* hack, waits until the network goes up */
+ execv(argv[0], (void*)argv);
+ }
if (!fork()) {
redirect("/bin/shell", "/dev/com1", "/dev/com1");
diff --git a/src/cmd/netstack/udp.c b/src/cmd/netstack/udp.c
index 285af41..3b53b47 100644
--- a/src/cmd/netstack/udp.c
+++ b/src/cmd/netstack/udp.c
@@ -55,10 +55,13 @@ struct udp_conn *udpc_new(
c->lport = u.src ? u.src : 50000; // TODO randomize source ports
c->rport = u.dst;
if (arpcache_get(c->rip, &c->rmac) < 0) {
- // TODO make arp request, wait for reply
- warnx("IP not in ARP cache, unimplemented");
- free(c);
- return NULL;
+ // TODO wait for ARP reply
+ arp_request(c->rip);
+ if (arpcache_get(state.gateway, &c->rmac) < 0) {
+ warnx("neither target nor gateway not in ARP cache, dropping");
+ free(c);
+ return NULL;
+ }
}
c->on_recv = on_recv;
c->carg = carg;
diff --git a/src/cmd/netstack/util.h b/src/cmd/netstack/util.h
index c1032a2..1cd22fb 100644
--- a/src/cmd/netstack/util.h
+++ b/src/cmd/netstack/util.h
@@ -40,3 +40,15 @@ static inline uint32_t nget32(const void *vbuf) {
| (b[2] << 8)
| (b[3] << 0);
}
+
+static inline uint64_t nget64(const void *vbuf) {
+ const uint8_t *b = vbuf;
+ return ((uint64_t)b[0] << 56)
+ | ((uint64_t)b[1] << 48)
+ | ((uint64_t)b[2] << 40)
+ | ((uint64_t)b[3] << 32)
+ | ((uint64_t)b[4] << 24)
+ | ((uint64_t)b[5] << 16)
+ | ((uint64_t)b[6] << 8)
+ | ((uint64_t)b[7] << 0);
+}
diff --git a/src/cmd/ntpfs.c b/src/cmd/ntpfs.c
new file mode 100644
index 0000000..783b56e
--- /dev/null
+++ b/src/cmd/ntpfs.c
@@ -0,0 +1,102 @@
+#include <camellia.h>
+#include <camellia/flags.h>
+#include <camellia/fs/misc.h>
+#include <camellia/fsutil.h>
+#include <camellia/syscalls.h>
+#include <err.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+// TODO put that in the libc somewhere
+#include "netstack/util.h"
+
+enum {
+ PacketSize = 12 * 4, /* 12 words */
+ ClientMode = 3,
+ NtpV4 = 4 << 3,
+ TxTimestamp = 40,
+};
+
+uint64_t offset = 0;
+
+uint64_t
+getntptime(const char *addr)
+{
+ char packet[PacketSize] = {0};
+ packet[0] |= ClientMode;
+ packet[0] |= NtpV4;
+
+ hid_t h = camellia_open(addr, OPEN_READ | OPEN_WRITE);
+ if (h < 0) {
+ warn("open %s", addr);
+ return 0;
+ }
+ if (write(h, packet, PacketSize) != PacketSize) {
+ warnx("partial write");
+ close(h);
+ return 0;
+ }
+ ssize_t ret = read(h, packet, PacketSize);
+ if (ret != PacketSize) {
+ warnx("partial read");
+ close(h);
+ return 0;
+ }
+
+ return nget64(packet + TxTimestamp);
+}
+
+/* Converts an NTP timestamp to the amount of nanoseconds (10^-9) since the
+ * Unix epoch. */
+uint64_t
+ntp2unix(uint64_t ntp)
+{
+ uint64_t seconds = (ntp >> 32) - 2208988800;
+ uint64_t frac = ntp & 0xFFFFFFFF;
+ uint64_t prec = 1000000000;
+ return seconds * prec + ((frac * prec) >> 32);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ if (argc != 2) {
+ errx(1, "bad usage");
+ }
+
+ // TODO: error checking, once i actually implement timeouts
+ uint64_t ntpt = getntptime(argv[1]);
+ uint64_t unixt = ntp2unix(ntpt);
+ offset = unixt - _sys_time(0);
+
+ char buf[256];
+ int len;
+
+ struct ufs_request req;
+ hid_t reqh;
+ while ((reqh = ufs_wait(buf, sizeof(buf), &req))) {
+ switch (req.op) {
+ case VFSOP_OPEN:
+ if (req.len == 0 && !OPEN_WRITEABLE(req.flags)) {
+ _sys_fs_respond(reqh, NULL, 0, 0);
+ } else {
+ _sys_fs_respond(reqh, NULL, -EGENERIC, 0);
+ }
+ break;
+ case VFSOP_READ:
+ len = snprintf(buf, sizeof(buf), "%lu\n", _sys_time(0) + offset);
+ if (0 <= len && len < (int)sizeof(buf)) {
+ fs_normslice(&req.offset, &req.len, len, false);
+ _sys_fs_respond(reqh, buf + req.offset, req.len, 0);
+ } else {
+ _sys_fs_respond(reqh, NULL, -EGENERIC, 0);
+ }
+ break;
+ default:
+ _sys_fs_respond(reqh, NULL, -ENOSYS, 0);
+ break;
+ }
+ }
+}