鱒身(Masu_mi)のブログ

知った事をメモする場所。

ZeroMQ: EPGMの利用失敗

PGMやEPGM上でZeroMQを利用するためにVagrantをなんやかんや頑張っていた。 しかし(E)PGMの利用は以下の様に失敗した。EPGMでの実行内容をメモしているがPGMも同様に失敗した。 仕方ないけど、これまでに調べた事と実施した対応や検証をメモする。

結果

  • 今回、ZeroMQ over EPGM (in CentOS 7.1)の稼働は失敗した
  • ZeroMQのsignaler_t::waitpollを利用している
  • pollでブロックされて返ってこない
  • PGMは実験的なマルチキャストを提供するプロトコル
  • EPGMはPGMUDB_に埋め込んだプロトコル
  • マルチキャストを扱うプロトコルは思ったより沢山ある
  • UDPのマルチキャストでは配送先IPアドレスの配信元デバイスを指定できる
  • マルチキャストに使うIPアドレスは予約されている(クラスD)
  • PGMを扱う際にプロセスはcap_net_raw=epケイパビリティが要求される
  • 上記ケイパビリティを与えた実行ファイルはsudo呼び出し時の様な環境変数で起動して動的リンクで失敗する
  • openpgmを利用するにはlibzmqでビルドオプションが必要(./configureする時に与える)
  • libzmqのビルドでopenpgmpkgconfigが要求される(openpgm-devel.x86_64を入れる)
  • 共有ライブラリを利用するのにldconfigが必要になる
  • GoのZeroMQバインド(zmq2,3,4)go get時にメジャーバージョンを確認する

色々と悩んだ今回の環境

redhat-release CentOS Linux release 7.1.1503 (Core)
kernel.x86_64 3.10.0-229.20.1.el7
libzmq.so libzmq.so.5.1.0
go go1.5.1 linux/amd64
openpgm.x86_64 5.2.122-2.el7
openpgm-devel.x86_64 5.2.122-2.el7

マルチキャストの資料

マルチキャスト勉強したことがなかった。 とりあえず下の資料を拾い読みした。良さそうだったので一通り目を通そうと思う。

ケイパビリティについての資料

第3回 権限を最小化するLinuxカーネルケーパビリティがLIDSの連載の一部として説明している。

やったこと(PGM, EPGM対応)

OpenPGM インストール

sudo yum install openpgm.x86_64 openpgm-devel.x86_64

libzmq のインストールし直し

$ PKG_CONFIG_PATH=/usr/lib64/pkgconfig/ ./configure --with-pgm=libpgm-5.2.122-2.el7
$ make -j 4
$ make check && make install && sudo ldconfig

ケイパビリティの設定 for PGM

$ go build ./zmqgo/sub.go
$ sudo setcap 'cap_net_raw=ep' ./sub
$ getcap ./sub
./sub = cap_net_raw+ep

設定しないと要求される

go run ではビルドした一時ファイルを実行するため`go run`時点で`execcap`を行っても意味がない。 sudoを使うかsetcapでバイナリに権限を付与するかが迫られる。 そこでsetcapコマンドを利用してケイパビリティをビルド後のバイナリに付与することにした。

$ go run ./zmqgo/sub.go -endpoint 'pgm://enp0s8;239.192.1.1:15555'
[ENDPOINT]pgm://enp0s8;239.192.1.1:15555
Error: PGM protocol requires CAP_NET_RAW capability, e.g. sudo execcap 'cap_net_raw=ep'
Warn: Close on IP Router Alert (RFC 2113) send socket failed: Bad file descriptor
Invalid argument (src/session_base.cpp:639)
signal: aborted

実行時にsudo envを使用する

ケイパビリティを付与した実行バイナリはlddでは解決出来るにも関わらず動的リンクに失敗した。 解決できないライブラリパスからLD_LIBRARY_PATHが見えていないことに気づいた。sudo lddと比較したがケイパビリティの付与により環境変数がrootユーザに切り替わるのかも知れないと推測した。これによりsudo envで対応したところ稼働できた。 sudo使うならケイパビリティの付与は不要じゃんってなった。

# ldd は成功
$ ldd ./sub
        linux-vdso.so.1 =>  (0x00007fff60dfe000)
        libzmq.so.5 => /usr/local/lib/libzmq.so.5 (0x00007f94dfe4b000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f94dfc24000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f94df863000)
        libpgm-5.2.so.0 => /lib64/libpgm-5.2.so.0 (0x00007f94df616000)
        librt.so.1 => /lib64/librt.so.1 (0x00007f94df40d000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f94df209000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f94def02000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f94debff000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f94de9e9000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f94e00d6000)
# リンク失敗
$ ./sub
./sub: error while loading shared libraries: libzmq.so.5: cannot open shared object file: No such file or directory

# sudo した時と同じlibが失敗する
$ sudo ldd ./sub
        linux-vdso.so.1 =>  (0x00007fffc4984000)
        libzmq.so.5 => not found
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fbea117f000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fbea0dbe000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fbea13a7000)

$ env | grep LD_LIBRARY_PATH
LD_LIBRARY_PATH=/home/vagrant/.linuxbrew/lib:/usr/local/lib

# 環境変数を明示的に与えても無意味だった
$ LD_LIBRARY_PATH=/home/vagrant/.linuxbrew/lib:/usr/local/lib; ./sub -endpoint 'pgm://enp0s8;239.192.1.1:15555'
./sub: error while loading shared libraries: libzmq.so.5: cannot open shared object file:
No such file or directory

# sudo envを軽油するとなぜか上手く行く
$ sudo env LD_LIBRARY_PATH=/home/vagrant/.linuxbrew/lib:/usr/local/lib ./sub -endpoint 'pgm://enp0s8;239.192.1.1:15555'
[ENDPOINT]pgm://enp0s8;239.192.1.1:15555

まずソースコードを説明しておく。

ソースコード

pub.go は下みたいなコードで実験した。

# use zmq "github.com/pebbe/zmq4"
publisher, _ := zmq.NewSocket(zmq.PUB)
defer publisher.Close()
// publisher.Bind(endpoint)
if err := publisher.Connect(endpoint); err != nil {
    fmt.Println(err)
    os.Exit(1)
}
rand.Seed(time.Now().UnixNano())
// loop for a while apparently
for {
    msg := util.CreateMessage()
    publisher.Send(msg, 0)
    if verbose {
        fmt.Println("SEND(zmq.PUB):" + msg)
    }
}

sub.goは下みたいなコードで実験した。

# use zmq "github.com/pebbe/zmq4"
fmt.Println("[ENDPOINT]" + endpoint)
subscriber, _ := zmq.NewSocket(zmq.SUB)
defer subscriber.Close()
if err := subscriber.Connect(endpoint); err != nil {
    fmt.Println(err)
    os.Exit(1)
}
subscriber.SetSubscribe(filter)
for {
    msg, _ := subscriber.Recv(0)
    fmt.Println("Recv(zmq.SUB):" + msg)
}

tcpdumpでパケット到達確認を行った

このコードとtcpdumpで利用して動作確認を行った結果、以下のことがわかった。

  • パケットは届いている
  • zmq::signaler_t::waitpollで待たされている
(in publisher)$ go run ./zmqgo/pub.go -endpoint 'epgm://enp0s8;239.192.1.1:15555'
Collecting updates from weather server...
[ENDPOINT]pgm://enp0s8;239.192.1.1:15555
# 何も反応なし
# tcpdumpで対応デバイスの対応ポートを覗くと通信は到達している様子がわかる
(in subscriber)[vagrant@for(10.0.2.2) playground]$ sudo tcpdump -Zvagrant -X -i enp0s8 port 15555 | head -n 20
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 65535 bytes
07:48:56.251851 IP 192.168.33.10.55715 > 239.192.1.1.cisco-snat: UDP, length 36
        0x0000:  45b8 0040 9678 4000 0111 1009 c0a8 210a  E..@.x@.......!.
        0x0010:  efc0 0101 d9a3 3cc3 002c 16bb d861 3cc3  ......<..,...a<.
        0x0020:  0000 5584 b7c6 d1bc 038a 0000 0000 0cea  ..U.............
        0x0030:  0000 0cae 0000 0cfd 0001 0000 c0a8 210a  ..............!.
07:48:56.270188 IP 192.168.33.10.38338 > 239.192.1.1.cisco-snat: UDP, length 1452
        0x0000:  45b8 05c8 9679 4000 0111 0a80 c0a8 210a  E....y@.......!.
        0x0010:  efc0 0101 95c2 3cc3 05b4 4f8c d861 3cc3  ......<...O..a<.
        0x0020:  0400 ad71 b7c6 d1bc 038a 0594 0000 0cfd  ...q............
        0x0030:  0000 0cad 0005 3736 2031 370d 0037 3635  ......76.17..765
        0x0040:  3736 202d 3235 2034 370c 0039 3233 3231  76.-25.47..92321
        0x0050:  2039 3320 3236 0d00 3638 3536 3820 2d34  .93.26..68568.-4
        0x0060:  3220 3239 0d00 3539 3836 3820 2d33 3320  2.29..59868.-33.
        0x0070:  3231 0d00 3933 3636 3320 2d36 3920 3134  21..93663.-69.14
        0x0080:  0c00 3030 3336 3420 3938 2034 380d 0038  ..00364.98.48..8
        0x0090:  3133 3032 2031 3332 2034 360d 0030 3838  1302.132.46..088
        0x00a0:  3434 2031 3233 2034 380c 0030 3535 3930  44.123.48..05590
        0x00b0:  2039 3020 3333 0d00 3938 3530 3120 2d32  .90.33..98501.-2
        0x00c0:  3920 3239 0d00 3634 3430 3020 3131 3320  9.29..64400.113.
        0x00d0:  3136 0d00 3239 3230 3920 3132 3820 3535  16..29209.128.55
4 packets captured
4 packets received by filter
0 packets dropped by kernel

gdbでブロック箇所を探す

tcpdumpのプログラムが進む部分の進捗確認

以下の様に動作中のtcpdumpにアタッチして状況を確認を行った。またnext 5などによりプログラムが進むを確認した。

[vagrant@for(10.0.2.2) playground]$ sudo tcpdump -Zvagrant -X -i enp0s8
# ....
# ....

# 別ターミナル
[vagrant@for(10.0.2.2) playground]$ sudo gdb -p $(pidof tcpdump)
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-64.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 9318
Reading symbols from /usr/sbin/tcpdump...Reading symbols from /usr/sbin/tcpdump...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Reading symbols from /lib64/libcrypto.so.10...Reading symbols from /lib64/libcrypto.so.10...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libcrypto.so.10
Reading symbols from /lib64/libpcap.so.1...Reading symbols from /lib64/libpcap.so.1...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libpcap.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
Reading symbols from /lib64/libz.so.1...Reading symbols from /lib64/libz.so.1...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libz.so.1
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib64/libnss_files.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_files.so.2
Reading symbols from /lib64/libnss_dns.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_dns.so.2
Reading symbols from /lib64/libresolv.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libresolv.so.2
Reading symbols from /lib64/libnss_myhostname.so.2...Reading symbols from /lib64/libnss_myhostname.so.2...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_myhostname.so.2
0x00007f56c73d4b60 in __poll_nocancel () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install tcpdump-4.5.1-2.el7.x86_64
(gdb) bt
#0  0x00007f56c73d4b60 in __poll_nocancel () from /lib64/libc.so.6
#1  0x00007f56c76b4518 in pcap_wait_for_frames_mmap.part.9 () from /lib64/libpcap.so.1
#2  0x00007f56c76b69b9 in pcap_read_linux_mmap_v2 () from /lib64/libpcap.so.1
#3  0x00007f56c76bafad in pcap_loop () from /lib64/libpcap.so.1
#4  0x00000000004044c8 in main ()
(gdb) n 1
Single stepping until exit from function __poll_nocancel,
which has no line number information.
0x00007f56c76b4518 in pcap_wait_for_frames_mmap.part.9 () from /lib64/libpcap.so.1

subサンプルコマンドが稼働しな確認 以下の様にpollから帰ってこない。

[vagrant@for(10.0.2.2) playground]$ sudo env LD_LIBRARY_PATH=/home/vagrant/.linuxbrew/lib::/usr/local/lib ./sub -endpoint 'epgm://enp0s8;239.192.1.1:15555'
[ENDPOINT]epgm://enp0s8;239.192.1.1:15555

# 別ターミナル
[vagrant@for(10.0.2.2) playground]$ sudo gdb -p $(pidof sub)
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-64.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 9304
Reading symbols from /home/vagrant/works/playground/sub...done.
Reading symbols from /usr/local/lib/libzmq.so.5...done.
Loaded symbols for /usr/local/lib/libzmq.so.5
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
[New LWP 9308]
[New LWP 9307]
[New LWP 9306]
[New LWP 9305]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/libpgm-5.2.so.0...Reading symbols from /lib64/libpgm-5.2.so.0...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libpgm-5.2.so.0
Reading symbols from /lib64/librt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/librt.so.1
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
Reading symbols from /lib64/libstdc++.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib64/libnss_files.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_files.so.2
Reading symbols from /lib64/libnss_dns.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_dns.so.2
Reading symbols from /lib64/libresolv.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libresolv.so.2
Reading symbols from /lib64/libnss_myhostname.so.2...Reading symbols from /lib64/libnss_myhostname.so.2...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_myhostname.so.2
0x00007f02f06a8b7d in poll () from /lib64/libc.so.6
warning: File "/usr/local/go/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load:/usr/bin/mono-gdb.py".
To enable execution of this file add
        add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/root/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
Missing separate debuginfos, use: debuginfo-install glibc-2.17-78.el7.x86_64 libgcc-4.8.3-9.el7.x86_64 libstdc++-4.8.3-9.el7.x86_64 openpgm-5.2.122-2.el7.x86_64 systemd-libs-208-20.el7_1.6.x86_64
(gdb) bt
#0  0x00007fa55bcc8b7d in poll () from /lib64/libc.so.6
#1  0x00007fa55c20166a in zmq::signaler_t::wait (this=this@entry=0x2792de8, timeout_=timeout_@entry=-1) at src/signaler.cpp:222
#2  0x00007fa55c1e3155 in zmq::mailbox_t::recv (this=0x2792d80, cmd_=0x7fff8215dc40, timeout_=-1) at src/mailbox.cpp:81
#3  0x00007fa55c202d6d in zmq::socket_base_t::process_commands (this=this@entry=0x27927e0, timeout_=<optimized out>, throttle_=throttle_@entry=false) at src/socket_base.cpp:1328
#4  0x00007fa55c20381f in zmq::socket_base_t::recv (this=0x27927e0, msg_=msg_@entry=0xc820018280, flags_=<optimized out>) at src/socket_base.cpp:1234
#5  0x00007fa55c21f509 in s_recvmsg (s_=<optimized out>, msg_=0xc820018280, flags_=<optimized out>) at src/zmq.cpp:497
#6  0x00007fa55c21f77a in zmq_msg_recv (msg_=<optimized out>, s_=<optimized out>, flags_=<optimized out>) at src/zmq.cpp:653
#7  0x00000000004033a2 in _cgo_4659bb846bec_C2func_zmq_msg_recv (v=0xc820049ca0) at /home/vagrant/go/src/github.com/pebbe/zmq4/zmq4.go:286
#8  0x00000000004589da in runtime.asmcgocall () at /usr/local/go/src/runtime/asm_amd64.s:690
#9  0x000000c820049c10 in ?? ()
#10 0x0000000000405e4a in runtime.cgocall (fn=0x82ff20 <runtime.g0+64>, arg=0x7fff8215ddc0, ~r2=8584928) at /usr/local/go/src/runtime/cgocall.go:107
#11 0x000000000042fc30 in runtime.startTheWorldWithSema () at /usr/local/go/src/runtime/proc1.go:609
#12 0x00007fff8215ded8 in ?? ()
#13 0x0000000000000003 in ?? ()
#14 0x00007fff8215ded8 in ?? ()
#15 0x0000000000000000 in ?? ()
(gdb) s 1
Single stepping until exit from function poll,
which has no line number information.