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

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: 問題点を指摘します:

  1. データのエンディアン: `struct timeval` のメンバーはエンディアンに依存する場合があります。そのため、`hostA` と `hostB` のエンディアンが異なる場合、データが正しく転送されない可能性があります。この問題を回避するために、エンディアン変換が必要です。
  2. エラーチェック: `read()` や `write()` 関数はエラーを返す可能性があります。特に、ソケット通信ではこれらのエラーを処理することが重要です。
  3. セキュリティ上の考慮: ソケット通信はセキュリティ上のリスクを伴う場合があります。特に、不正なデータを送信することでネットワークに対する攻撃が可能です。このため、適切なセキュリティ対策が必要です。

修正したコードは以下のようになります:

// 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世紀のフロンティアとアメリカの再興

訳・解説 山形浩生, 高須正和。 山形さんは、歳食ったので、助け 船をお願いされたそうだ。


This year's Index

Home