ntpdate, rdate
Table of Contents
talkd log
パケットのin,outが分かり易くなるように、表示を変更してみた。初めての OpenBSD改造です。
Mar 29 07:18:50 ob talkd[11230]: ==>>: look_up: id 0, l_user joy, r_user sakae, r_tty Mar 29 07:18:50 ob talkd[11230]: find_match: look_up: id 0, l_user joy, r_user sakae, r_tty Mar 29 07:18:50 ob talkd[11230]: find_match: not found Mar 29 07:18:50 ob talkd[11230]: <<==: look_up: not_here, id 0 Mar 29 07:18:50 ob talkd[11230]: ==>>: announce: id -1, l_user joy, r_user sakae, r_tty Mar 29 07:18:50 ob talkd[11230]: find_request: announce: id -1, l_user joy, r_user sakae, r_tty ttyp0 Mar 29 07:18:50 ob talkd[11230]: insert_table: announce: id -1, l_user joy, r_user sakae, r_tty ttyp0 Mar 29 07:18:50 ob talkd[11230]: <<==: announce: success, id 1 Mar 29 07:19:08 ob talkd[11230]: ==>>: delete: id 1, l_user joy, r_user sakae, r_tty Mar 29 07:19:08 ob talkd[11230]: delete_invite(1) Mar 29 07:19:08 ob talkd[11230]: delete: announce: id 1, l_user joy, r_user sakae, r_tty ttyp0 Mar 29 07:19:08 ob talkd[11230]: <<==: delete: success, id 0
この、'=>>' '<<=
' は、某Lispのtrace表示からの借用です。視認性の向上
に貢献してます。
ntpdate
前回、嘆いたVMWareのブリッジモードでのネットワーク不具合。これを避ける には、FreeBSDを起動したままにしたらどうだろとアイデアが湧いてきた。
Windowsは、reboot <-> shutdown じゃなくて、sleep <-> wakeup の繰り返し で、SDDに負担をかけない様にしてる。ならば、その延長で、FreeBSDも起動し たままにすれば、SDDの負担も減り、かつ上記の効果も期待できるかも。
大体、VMWareの挙動も、バーチャルマシンを起動すると、許可したメモリーサ イズ分の隠れファイルが作成される。最初これが信じられなかったんだけど、Windows の、プロパティで仮想マシンの一切が格納されてるdirサイズを確認すると、 そうなっている。unixで言う所の、du -sh 相当と理解した。
だから、boot/shutdownの回数を減らせば、SDDの負担は減る事になる。一石二 鳥のアイデア。
でも、これをやると一つ問題が有る。Windowsが目覚めても、FreeBSDが保持す る時刻は更新されないのだ(いわゆる、浦島太郎状態)。どうする? そんなの、OpenBSDが2次とか3次の時 刻標準になってるから、そこから時刻を貰ってくればいいんでないかい。
FXや株のトレードをやってる訳ではないので、SEIKOあたりから、わざわざ時 刻を購入する必要は無いだろう。目につかない範囲で大体時刻が合っていれば OK。
現実世界では、風呂場の給湯器に付属してる時計は、15分ずれているし、炊飯 器の時計に至っては、50分ぐらいずれているぞ。そこで、オイラーが炊飯ボー イ(いや、飯炊爺さんだな)になって、電波時計にシンクロして、10時47分に炊飯をスタートさせるのさ。これ で、11時半からの昼食にピッタリ間に合う。なんとも超アナログの世界に生き ております。
sakae@fb:~ $ date; echo | nc -w 2 -u ob 13 Sat Mar 30 16:37:18 JST 2024 Sat Mar 30 16:37:17 2024 sakae@fb:~ $ date; echo | nc -w 2 -u ob 13 Sat Mar 30 16:37:28 JST 2024 Sat Mar 30 16:37:27 2024
これ、OpenBSDから時刻をお取り寄せです。echoをパイプに流しているのは、 コネクトした後、空行入力で、時刻が返却させる為。一度コネクトすると、接 続されたままになるので、-w 2 で、タイムアウト切断させている。 結果を見ると、1秒のズレですねぇ。これぐらいなら十分に許容できる。 でも、これが、Windowsと共にFreeBSDも1日寝てましたとなると、さすがのオ イラーも堪忍袋の緒が切れますよ。
時刻合わせのサーバーは提供されていないかな?
sakae@fb:/usr/ports $ make search key=ntpd Port: ntpd-rs-1.1.2_1 Path: /usr/ports/net/ntpd-rs Info: Full-featured implementation of the Network Time Protocol Maint: mikael@FreeBSD.org B-deps: curl-8.6.0 indexinfo-0.3.1 libiconv-1.17_1 libidn2-2.3.7 libnghttp2-1.60.0 libpsl-0.21.5 libssh2-1.11.0_1,3 libunistring-1.2 rust-1.77.0 R-deps: WWW: https://tweedegolf.nl/en/pendulum Port: openntpd-6.8p1_9,2 Path: /usr/ports/net/openntpd Info: Network Time Protocol (NTP) daemon Maint: naddy@FreeBSD.org B-deps: libretls-3.8.1_1 R-deps: ca_root_nss-3.93_2 libretls-3.8.1_1 WWW: https://www.openntpd.org/
ntpd-rsはrustで作成されてるとな。openntpdの方は、名前が示す通り、 OpenBSDの成果を利用してる。残念ながら、両者共、ntpdate ntp.nict.jp み たいに、一発で時間合わせするコマンドは提供してないみたい。
そんな言う前に調べてみろ。なんだ有るじゃん。寄贈品なので、 /usr/src/contribに本体が置いてあって、/usr/src/usr.sbin/ntpの方は、 Makefileしかなかったぞ。寄贈品が多数だな。Makefileしかツリーにないもの は、寄贈品と看破しても良さそう。
root@fb:~ # ntpdate ntp.nict.jp 30 Mar 16:42:06 ntpdate[819]: step time server 133.243.238.243 offset -0.704655 sec root@fb:~ # ntpdate ntp.nict.jp 30 Mar 16:42:22 ntpdate[820]: adjust time server 133.243.238.243 offset +0.000738 sec
0.7秒ずれてた訳ね。
sakae@fb:~ $ date; echo | nc -w 2 -u ob 13 Sat Mar 30 16:44:29 JST 2024 Sat Mar 30 16:44:29 2024
私設の簡易版でも、時刻が一致した。これで、FreeBSDを起動したままにして、 Windowsをスリープしても大丈夫かな。ntpdate一発で時刻合わせが出来るから ね。
networkに接続できひん
上で書いた思惑から、Windowsをスリープさせ(== FreddBSDも当然スリープに なる)、それから、Windowsを叩き起こした。したら、FreeBSDがネットワーク に接続できなくなってしまった。何でかな? 前回検討しておいたTODOに従っ て調査しよう。
ping localhostは問題無し。ping obは応答無し。使われるNICにlo0/em0と言 う違いがある? それともネットワークがFreeBSD内に閉じているか、外部のネッ トワーク利用って差と捉えるかだな。
tcpdump -i em0 しといて、ping ob
08:23:11.203889 IP xx.xx.16.1.57957 > 239.255.255.250.ssdp: UDP, length 175 08:23:11.982344 ARP, Request who-has ob tell fb, length 28 08:23:13.027603 ARP, Request who-has ob tell fb, length 28 08:23:13.560674 IP xx.xx.16.1.59001 > 239.255.255.250.ssdp: UDP, length 274 08:23:14.040549 ARP, Request who-has ob tell fb, length 28 08:23:15.050666 ARP, Request who-has ob tell fb, length 28 08:23:16.110429 ARP, Request who-has ob tell fb, length 28 08:23:17.133771 ARP, Request who-has ob tell fb, length 28 08:23:18.151791 ARP, Request who-has ob tell fb, length 28 08:23:18.540869 IP xx.xx.16.1.59001 > 239.255.255.250.ssdp: UDP, length 274
ARPリクエストを発出するものの、返答がob側から無い。それより、 xx.xx.16.1ってパケットは何よ。ひょっとして、ネットワークが別の系統に、 すげかわっていないか?
別のってのは、WSLの内蔵ネットね。それを検証する為、Debian/WSL2から、 ping fb してみた。
09:01:58.543172 IP xx.xx.16.1.59001 > 239.255.255.250.ssdp: UDP, length 274 09:01:59.476911 IP xx.xx.27.122 > fb: ICMP echo request, id 59847, seq 1, length 64 09:02:00.515410 IP xx.xx.27.122 > fb: ICMP echo request, id 59847, seq 2, length 64 09:02:00.542793 IP win > xx.xx.27.122: ICMP host fb unreachable, length 92
どうやら、知らぬ間に、WSL2のネットワークに参加させられているよ。
VMWareのネットワークアダプタで、管理者権限アイコンのついた、設定ボタン を押す。出てきた、ブリッジの自動設定から、Hyper-V Virtual Ethernet Adapter のチェックを外す。自動なんでWSLが選ばれても文句を言えない。だから、候 補から除外してしまおうと言う訳だ。これで、使ってみるか。
正解なパケットは、こうなっている。
15:56:21.375149 ARP, Request who-has fb tell win, length 46 15:56:21.375185 ARP, Reply fb is-at 00:11:22:33:44:55 (oui Unknown), length 28 15:56:21.375564 IP win > fb: ICMP echo request, id 1000, seq 1, length 64 15:56:21.375602 IP fb > win: ICMP echo reply, id 1000, seq 1, length 64
windows wsl bridge
当然、こういう要求も有る訳ですよ。
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
WSL2 で外部からアクセス可能にするために bridge mode を有効にする
Windows10 Home版では、どうも無理っぽい。
妄想
nt なんていう alias を設定した。これ、FreeBSDが冬眠から目覚めた時に、 時刻を現実なそれに合わせるもの。
nt (ntpdate pool.ntp.org)
rootはcshellなんで、ちょっと見慣れない形式だ(設定する時には、括弧は不 要)。
sakae@fb:~ $ date; echo | nc -w 1 -u ob 13 Sun Mar 31 17:50:40 JST 2024 Mon Apr 1 06:18:19 2024 root@fb:~ # nt 1 Apr 06:18:51 ntpdate[1175]: step time server 129.250.35.250 offset +44858.874267 sec sakae@fb:~ $ date; echo | nc -w 1 -u ob 13 Mon Apr 1 06:19:22 JST 2024 Mon Apr 1 06:19:22 2024
朝起きたらFreeBSD時刻は、昨日だった。で、時刻を校正したら、やっと正し い時刻を刻むようになった。
これ、自動でやってくれないかなあ。
それには、Windowsが眠りから覚めたって事を感知して、FreeBSDに通知する仕 組みが必要だな。そんな都合の良い信号が得られるのか?
VMWareは起床を検出して、FreeBSDのネットワークを何処に参加させるかやっ てる訳なんで、頑張れば何とかなりそうな雰囲気ではあるんですが。。。
散歩してたらアイデアが湧いてきた。Windows側からcron相当を利用して、定 期的に時刻情報をFreeBSDに投げる。5分間隔ぐらいで後腐れないUDP転送ね。 FreeBSD側では、そのパケットに載ってる時刻と、FreeBSD側の時刻を比較。大 幅にずれていたら、ntpdateして時刻合せ。この機構は、inetdの中に入れる。
問題はWindows上で動かすアプリだな。たまにはgaucheとかでも使ってみたい んだけど、Windows特有の制限とかが隠れていそうで怖い。その点は、golangあ たりを使っておけば心配無いだろう。*BSD上で開発しておいて、最後に Windows用のバイナリーを生成するって事が出来るからね。
ダエモン君になる方法
こちらは、普通にダエモン君になる方法。daemon(3)
DESCRIPTION The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons.
ダエモン君になるのは大変だ。軽く説明されてるけど、物の本によると、2回 の脱皮(fork)を繰り返し、やっとダエモン君に昇格できるみたい。1回目は、 バックグラウンド化。2回目は、起動したshellからの離脱。これでいいかと思 うと甘い。最後の詰めは、cd / して、umountの妨げにならないようにする。 そして、もう一つ、標準入出力/エラーのディスクリプタをクリアする事。
作法は厳しいけど、手順は明確なので、一発で変身できる様に関数が提供され ているのさ。
FreeBSD特製の、何でもダエモン君にしちゃうユーティリティ
DAEMON(8) FreeBSD System Manager's Manual DAEMON(8) NAME daemon – run detached from the controlling terminal SYNOPSIS daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile] [-t title] [-u user] [-m output_mask] [-o output_file] [-s syslog_priority] [-T syslog_tag] [-l syslog_facility] [-R restart_delay_seconds] command arguments ... DESCRIPTION The daemon utility detaches itself from the controlling terminal and executes the program specified by its arguments. Privileges may be lowered to the specified user. The output of the daemonized process may be redirected to syslog and to a log file.
ちょっと実験。まずは普通にサーバー化
sakae@fb:~ $ nc -l 51200
ずっと待ちかまえているので、次のshellコマンドは受け付けない。まあ、自 前でバックグラウンドには出来るけど。
sakae@fb:~ $ ps awx : 1416 2 I+ 0:00.00 nc -l 51200
その時のps。2ってのは、端末番号。
sakae@fb:~ $ daemon nc -l 51200 sakae@fb:~ $
こちらは、ダエモン化したnc。次のコマンドを受け付けるよう、直ぐにプロンプ トが返ってきた。
sakae@fb:~ $ ps awx : 1454 - Ss 0:00.00 nc -l 51200
担当の端末が無い。Ctl-Cで停止って出来ない相談だ。jobsなんかで、プロセ スを検出しようにも、既にshellの手が届かない高見に移動しちゃってるのさ。 これぞ影の働き者です。こういう彼らにunixは支えられているんです。
inetd
ネットワークを影で支える立役者。今となっては、往年の名選手なんですけ ど。。昔を懐かしんで、機能を増強してみる。そう、inetdの内蔵サービスを 追加してみます。
追加機能は、正確な時刻(秒以下の端数も含めて)を、問い合わせ先に回答する 機能です。
/etc/services
puret 52525/tcp # pure time
サービス名と、ポート番号、tcp/udpの区別を登録します。
/etc/inetd.conf
puret stream tcp nowait root internal
次に、それをどの様にサポートするか、confファイルに定義します。
/usr/src/usr.sbin/inetd
ob$ diff inetd.c.org inetd.c 220a221 > void puret_stream(int, struct servtab *); 255a257,259 > /* Return timeval */ > { "puret", SOCK_STREAM, 0, 0, puret_stream }, > 1588a1593,1603 > void > puret_stream(int s, struct servtab *sep) > { > struct timeval tv; > > if (gettimeofday(&tv, NULL) == -1) > tv.tv_sec = 0L; > > (void) write(s, &tv, sizeof(tv)); > }
そして、その機能を実装します。もう笑ってしまうぐらい、単純な実装。これ でOpenBSDの第2回めの改造です。
この機能の利用プログラム(クライアント)は、最後に掲載。
最新式のrdate
OpenBSDでもrdateが提供されてた(FreeBSDは未対応)。
ob$ rdate -pov ob Thu Apr 4 06:21:35 JST 2024 rdate: adjust local clock by 0 seconds ob$ rdate -pnv ntp.nict.jp Thu Apr 4 06:22:48 JST 2024 rdate: adjust local clock by 0.001912 seconds
時刻の提供を、ntpdなサーバーから受けるか、それともinetdが提供してる、 timeプロトコルにするか、スイッチで指定します。デフォでは、ntpdが利用さ れるんで、これはもう、すっかりntpdateです。
ChatGPT
Q: hostAの時刻をgettimeofday(&tv, NULL)で取得し、それをhostBに tcpで転送し、settimeofday(&tv, NULL)を利用して、時刻を再現したい。 構造体をどうやってネットワーク転送しますか? 下記は問題ありと思ってます。
### やりたい事の抜粋 hostA struct timeval tv; gettimeofday(&tv, NULL); write(soc, &tv, sizeof(tv)); hostB struct timeval tv; read(soc, &tv, sizeof(tv); settimeofday(&tv, NULL);
A: 問題点を指摘します:
- データのエンディアン: `struct timeval` のメンバーはエンディアンに依存する場合があります。そのため、`hostA` と `hostB` のエンディアンが異なる場合、データが正しく転送されない可能性があります。この問題を回避するために、エンディアン変換が必要です。
- エラーチェック: `read()` や `write()` 関数はエラーを返す可能性があります。特に、ソケット通信ではこれらのエラーを処理することが重要です。
- セキュリティ上の考慮: ソケット通信はセキュリティ上のリスクを伴う場合があります。特に、不正なデータを送信することでネットワークに対する攻撃が可能です。このため、適切なセキュリティ対策が必要です。
修正したコードは以下のようになります:
// hostA struct timeval tv; gettimeofday(&tv, NULL); // エンディアン変換 tv.tv_sec = htonl(tv.tv_sec); tv.tv_usec = htonl(tv.tv_usec); if (write(soc, &tv, sizeof(tv)) != sizeof(tv)) { // エラー処理 } // hostB struct timeval tv; if (read(soc, &tv, sizeof(tv)) != sizeof(tv)) { // エラー処理 } // エンディアン変換を元に戻す tv.tv_sec = ntohl(tv.tv_sec); tv.tv_usec = ntohl(tv.tv_usec); settimeofday(&tv, NULL);
この修正では、`htonl()` および `ntohl()` 関数を使用してエンディアン変換を行っています。また、`write()` および `read()` 関数のエラーチェックも追加しています。
同一なエンディアンで使用する事が確定(いわゆるintel入ってるなマシンね)してるんでサボちゃったけど、大目に 見てくれないかなあ。
src
inetdの追加サービスに対応するクライアント・プログラム。実行はrootで。 OpenBSDなinetdから時刻を取り寄せて、自ホストの時刻を更新。元ネタは、rfc868time.c
/* $OpenBSD: rfc868time.c,v 1.13 2023/01/04 13:00:11 jsg Exp $ */ /* $NetBSD: rdate.c,v 1.4 1996/03/16 12:37:45 pk Exp $ */ /* * Copyright (c) 1994 Christos Zoulas * All rights reserved. * <snip> */ #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <stdio.h> #include <ctype.h> #include <err.h> #include <string.h> #include <netdb.h> #include <unistd.h> #include <time.h> #include <stdio.h> char *hostname = "ob"; /* inetd server */ int main() { struct addrinfo hints, *res0, *res; struct timeval tim; int s; int error; u_int64_t td; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(hostname, "puret", &hints, &res0); if (error) { errx(1, "%s: %s", hostname, gai_strerror(error)); /*NOTREACHED*/ } s = -1; for (res = res0; res; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) continue; if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { close(s); s = -1; continue; } break; } if (s == -1) err(1, "Could not connect socket"); freeaddrinfo(res0); if (read(s, &tim, sizeof(tim)) != sizeof(tim)) err(1, "Could not read data"); if (settimeofday(&tim, NULL) == -1) err(1, "Could not do settimeofday"); printf("%d %ld\n", tim.tv_sec, tim.tv_usec); (void) close(s); }
sizeof(tim)
上記をコンパイルした時、printfの書式指定で、とやかく言われたので、調査 してみた。
at OpenBSD(32Bit)
(gdb) p tim $1 = {tv_sec = 1712088484, tv_usec = 914749} (gdb) p sizeof(tim.tv_sec) $2 = 8 (gdb) p sizeof(tim.tv_usec) $3 = 4
at FreeBSD(32Bit)
$1 = {tv_sec = 1712092977, tv_usec = 405648} (gdb) p sizeof(tim.tv_sec) $2 = 4 (gdb) p sizeof(tim.tv_usec) $3 = 4
at Debian(64Bit)
(gdb) p tim $1 = {tv_sec = 1712040562, tv_usec = 265915} (gdb) p sizeof(tim.tv_sec) $2 = 8 (gdb) p sizeof(tim.tv_usec) $3 = 8
at ArchLinux(64Bit)
(gdb) p tim $1 = {tv_sec = 1712091316, tv_usec = 964519} (gdb) p sizeof(tim.tv_sec) $2 = 8 (gdb) p sizeof(tim.tv_usec) $3 = 8
みんなちがって、みんないい(by 金子みすず)状態なんだけど、それでいいのか?
time_t
sys/time.hって事は、機種依存性大だろう。
at FreeBSD
sakae@fb:~ $ cd /usr/include/ sakae@fb:/usr/include $ grep time_t -r . | grep typedef ./time.h:typedef __time_t time_t; ./x86/_types.h:typedef __int32_t __time_t;
at OpenBSD
./sys/_time.h:typedef __time_t time_t; ./sys/_types.h:typedef __int64_t __time_t; /* epoch time */
at Debian
sakae@atom:~$ cc -E t.c | grep time_t typedef long int __time_t; typedef __time_t time_t;
やっぱりね。世間は今トラック野郎やお医者様(言葉尻を捉えて、職業差別と 言わないで下さい)の2024年問題が話題になってるけど、epochがオーバーフ ローする2036年問題をかかえているな(旧世代の石上のOS)。
Y2K問題で、シリコンバレーでは大儲けした企業があったそうだけど、2036年
問題で、潤う企業は有るのか。そんなの許しませんって、 time_spec
系へ
の移行を絶賛展開中なんだな。
儲けの口は有るぞ。人類史上、誰も経験した事が無い、恐怖の負のうるう秒が 取りざたされているんだ。今迄は、地球の自転が遅くなる方向だったから、そ れに合わせて、正のうるう秒だった。所が地球内部のドロドロ状態が変化して、 自転が速くなってるらしい。それに合わせると、負のうるう秒を検討しないと ならないそうだ。Y2Kの時は、コンピュータの前で、お祈りした覚えがあるけ ど、ひょっとしたら、それが再現されるかも知れないな。
そう言えば、IPv4 -> IPv6 も、同じ穴のむじな感が、プンプンするぞ。
The CODE シリコンバレー全史 20世紀のフロンティアとアメリカの再興
訳・解説 山形浩生, 高須正和。 山形さんは、歳食ったので、助け 船をお願いされたそうだ。