パケット受信タイムスタンプ

Linuxでパケット受信契機のタイムスタンプ採取方法。

ユーザーアプリからはsetsockoptSO_TIMESTAMPを設定しておき、recvmsgで受信する都度せっせと**CMSG_DATA(cmsg)**経由で取得する形での仕組みが用意されている。具体的なコーディングサンプルだと

char    inbuf[BUFSIZ];
    char    cmsgbuf[CMSG_SPACE(sizeof(struct timeval))];
    struct  cmsghdr *cmsg;
    struct  msghdr  msghdr;
    struct  iovec   msg_iov;
    struct timeval  *pTime, tv;
    const int on = 1;
    **setsockopt**(sock, SOL_SOCKET, **SO_TIMESTAMP**, &on, sizeof(on));
    msg_iov.iov_base = inbuf;
    msg_iov.iov_len = sizeof(inbuf);
    msghdr.msg_name = NULL;
    msghdr.msg_namelen = 0;
    msghdr.msg_iov = &msg_iov;
    msghdr.msg_iovlen = 1;
    msghdr.msg_control = cmsgbuf;
    msghdr.msg_controllen = sizeof(cmsgbuf);
    while ( **recvmsg**(sock, &msghdr, ZERO) ){
        /* Receive auxiliary data in msgh */
        for (cmsg = CMSG_FIRSTHDR(&msghdr);
            cmsg != NULL;
                cmsg = CMSG_NXTHDR(&msghdr,cmsg)) {
            if (cmsg->cmsg_level == SOL_SOCKET
                && cmsg->cmsg_type == **SO_TIMESTAMP**) {
                pTime = (struct timeval *) **CMSG_DATA(cmsg);**
                printf("cmsg time=%ld,%ld\n", pTime->tv_sec, pTime->tv_usec);
                break;
            }
        }
    }

な感じだ。

が、実際にタイムスタンプを採取してみると微妙にタイムラグが気になったので調べてみたところ、sock_recv_timestampを呼んでタイムスタンプの契機を与えているのはrecvmsg。て事は、受信データがバッファがコピーされた時間であり、実際にパケットが届いた時間とは言い切れない。

というわけで、取り敢えず今回のターゲットはUDP限定なので、カーネルのnet/ipv4/udp.c

*** udp.c.orig  2007-07-07 13:52:58.000000000 +0900
--- udp.c       2007-09-05 09:47:58.000000000 +0900
***************
*** 1231,1236 ****
--- 1231,1243 ----
  
        if (sk != NULL) {
                int ret = udp_queue_rcv_skb(sk, skb);
+ // for UDP TIMESTAMP TEST 2007/09/05
+               if (sock_flag(sk, SOCK_RCVTSTAMP)) {
+                       struct timeval stamp;
+                       do_gettimeofday(&stamp);
+                       skb_set_timestamp(skb, &stamp);
+               }
+ // for UDP TIMESTAMP TEST 2007/09/05
                sock_put(sk);
  
                /* a return value > 0 means to resubmit the input, but

を追加。あとはリビルドすればめでたしめでたし。

なお、上記パッチはカーネル版数2.6.20.xあたりで確認してあるが、言うまでもなく無保証なのでそこんとこよろしく。

参照

JM Project (Japanese) http://www.linux.or.jp/JM/