Armadilloフォーラム

3G使用時のsocket動作の不具合

hidaka.hiroshi

2016年7月7日 15時41分

いつもお世話になります。日高です。

度々質問ばかりで申し訳ありません。
またまた、不可解な動作に悩まされています。

というのも、eth0に対してブロードキャストパケットを発行し、そのレスポンスを受信するというプログラムを作成して
動作確認を行っているのですが、相手からのパケットが受信出来る時と出来ない時があります。

【接続環境】
 以下の様な接続環境とします。

UNIT   IP-Address     NetMask
-----  -------------  -------------
IoTG2  192.168.10.10  255.255.255.0
U1     192.168.10.20  255.255.255.0
U2     192.168.12.34  255.255.255.0
※各ユニット間はHUBを介して有線接続

【動作】
 1.IoTG2からUDPのブロードキャストで問合せパケットを発行します。
 2.U1/U2はそれぞれブロードキャストで応答レスポンスを発行します。

【コード】
 ・selectを用いて、socketからパケットが来た事を知りrecvfromでパケットを読み込む
 ・使用しているソケットは setsockoptで "SO_BINDTODEVICE"に"eth0"をセットしている。
 ・その為、プログラムはroot権限で実行している。

【不可解な動作】
 ・IoTG2と同じネットワークアドレスを持つU1からパケットの応答が来ない間、U2が応答パケットを返しても
  selectからソケットレディにならない。
 ・U1からパケットの応答が来ると、ソケットレディになる。
 ・一度、U1からのパケットが来ると、それ以降はU2のパケットもソケットレディになる。
 ・一旦プログラムを終了させて、再度起動させると、またU2のみだとソケットレディにならない。
 ・この現象は3G(umts0)が接続中にのみ発生する。
 ・3GをOFF(ifdown)にした時は、U2のパケットは常にソケットレディになる。

という事で、3Gの有効・無効で動作が変わってしまいます。

一体、何からチェックしたら良いのでしょうか?

ちなみに、routeコマンドでは以下の様になっていました。

(3G-OFF)
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
    192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
(3G-ON)
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    default         180.41.63.203   0.0.0.0         UG    0      0        0 umts0
    default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
    169.254.0.0     *               255.255.0.0     U     99     0        0 umts0
    180.41.63.0     *               255.255.255.0   U     0      0        0 umts0
    192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
コメント

株式会社 ACCESS 三原と申します。

1つ気になることがあります。

> 【接続環境】
>  以下の様な接続環境とします。
>

> UNIT   IP-Address     NetMask
> -----  -------------  -------------
> IoTG2  192.168.10.10  255.255.255.0
> U1     192.168.10.20  255.255.255.0
> U2     192.168.12.34  255.255.255.0
> ※各ユニット間はHUBを介して有線接続
> 

> ちなみに、routeコマンドでは以下の様になっていました。
>

> (3G-OFF)
>     Kernel IP routing table
>     Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
>     default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
>     192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
> (3G-ON)
>     Kernel IP routing table
>     Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
>     default         180.41.63.203   0.0.0.0         UG    0      0        0 umts0
>     default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
>     169.254.0.0     *               255.255.0.0     U     99     0        0 umts0
>     180.41.63.0     *               255.255.255.0   U     0      0        0 umts0
>     192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
> 

eth0 の IP アドレスが 192.168.10.10 で、route コマンドの default 以外のローカルネットワークのアドレスが 192.168.1.0/255.255.255.0 とすると、U2 192.168.12.34 へのパケットは default ゲートウェイに送信されると思います。

3G が無効の場合は default ゲートウェイが1つなので必ず eth0 から送信されますが、3G が有効になると default ゲートウェイが2つできるので、3G に送信されるかもしれません。TCP で ACK が別の経路に送信されたら接続できません。

ローカルアドレス 192.168.12.0/255.255.255.0 へ eth0 から送信するローカルのルーティングを設定するとどうなりますでしょうか?

日高です。

> 1つ気になることがあります。
>
> > 【接続環境】
> >  以下の様な接続環境とします。
> >

> > UNIT   IP-Address     NetMask
> > -----  -------------  -------------
> > IoTG2  192.168.10.10  255.255.255.0
> > U1     192.168.10.20  255.255.255.0
> > U2     192.168.12.34  255.255.255.0
> > ※各ユニット間はHUBを介して有線接続
> > 

>
> > ちなみに、routeコマンドでは以下の様になっていました。
> >

> > (3G-OFF)
> >     Kernel IP routing table
> >     Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
> >     default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
> >     192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
> > (3G-ON)
> >     Kernel IP routing table
> >     Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
> >     default         180.41.63.203   0.0.0.0         UG    0      0        0 umts0
> >     default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
> >     169.254.0.0     *               255.255.0.0     U     99     0        0 umts0
> >     180.41.63.0     *               255.255.255.0   U     0      0        0 umts0
> >     192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
> > 

>
> eth0 の IP アドレスが 192.168.10.10 で、route コマンドの default 以外のローカルネットワークのアドレスが 192.168.1.0/255.255.255.0 とすると、U2 192.168.12.34 へのパケットは default ゲートウェイに送信されると思います。
>
> 3G が無効の場合は default ゲートウェイが1つなので必ず eth0 から送信されますが、3G が有効になると default ゲートウェイが2つできるので、3G に送信されるかもしれません。TCP で ACK が別の経路に送信されたら接続できません。
>
> ローカルアドレス 192.168.12.0/255.255.255.0 へ eth0 から送信するローカルのルーティングを設定するとどうなりますでしょうか?

あっ、ごめんなさい。接続環境とrouteコマンドの結果が合ってませんでした。
現在は、以下の様な接続環境になってました。(色々試したので、結果が混在してました)

 UNIT   IP-Address    NetMask
 -----  ------------  -------------
 IoTG2  192.168.1.10  255.255.255.0
 U1     192.168.1.20  255.255.255.0
 U2     192.168.2.34  255.255.255.0
 ※各ユニット間はHUBを介して有線接続
 

紛らわしい情報を書いてしまい、申し訳ありませんでした。

株式会社 ACCESS 三原と申します。

>

>  UNIT   IP-Address    NetMask
>  -----  ------------  -------------
>  IoTG2  192.168.1.10  255.255.255.0
>  U1     192.168.1.20  255.255.255.0
>  U2     192.168.2.34  255.255.255.0
>  ※各ユニット間はHUBを介して有線接続
>  

情報ありがとうございます。

それでも U2 は 192.168.2.34 ですから、192.168.1.0/255.255.255.0 のローカルネットワークの外にあるように見えます。

192.168.2.0/255.255.255.0 には eth0 から送信するよう設定なされてはどうでしょう?

いつもお世話になります。日高です。

> 株式会社 ACCESS 三原と申します。
>
> それでも U2 は 192.168.2.34 ですから、192.168.1.0/255.255.255.0 のローカルネットワークの外にあるように見えます。

論理ネットワーク的には仰る通りです。

もう少し具体的に説明すると、UDPのブロードキャストを使用して同一物理ネットワーク上に接続されている
ユニット(IPアドレスは不明)を検索するというのが最終目的です。

> 192.168.2.0/255.255.255.0 には eth0 から送信するよう設定なされてはどうでしょう?

U2からパケットが返ってきているのは、Wiresharkで確認済です。

もしかしたら、3Gが接続中に異なるネットワークアドレスから発信されたパケットがeth0から入ってきたら、IoTG2上のアプリに
渡されずに、3G側に送られたりしているのでしょうか?

それでも、一度U1からパケットが来るとそれ以降はU2からのパケットも受信出来る様になるというのが理解出来ないので、
非常に悩んでいます。

> いつもお世話になります。日高です。
>
> > 株式会社 ACCESS 三原と申します。
> >
> > それでも U2 は 192.168.2.34 ですから、192.168.1.0/255.255.255.0 のローカルネットワークの外にあるように見えます。
>
> 論理ネットワーク的には仰る通りです。
>
> もう少し具体的に説明すると、UDPのブロードキャストを使用して同一物理ネットワーク上に接続されている
> ユニット(IPアドレスは不明)を検索するというのが最終目的です。
>
> > 192.168.2.0/255.255.255.0 には eth0 から送信するよう設定なされてはどうでしょう?
>
> U2からパケットが返ってきているのは、Wiresharkで確認済です。
>
> もしかしたら、3Gが接続中に異なるネットワークアドレスから発信されたパケットがeth0から入ってきたら、IoTG2上のアプリに
> 渡されずに、3G側に送られたりしているのでしょうか?
>
> それでも、一度U1からパケットが来るとそれ以降はU2からのパケットも受信出来る様になるというのが理解出来ないので、
> 非常に悩んでいます。
arpテーブルの問題ですよね。きっと
deviceがbindされているため、デフォゲ(3G)のインターフェイスで受信する返しパケットが見えないのでは?
1回通ってしまえばarp情報ができるので・・・deviceのバイドをやめるか
net.ipv4.conf.xxxx.arp_ignore
net.ipv4.conf.xxxx.arp_announce
辺りの設定を変更するのが良いかもしれません。

> いつもお世話になります。日高です。
>
> > 株式会社 ACCESS 三原と申します。
> >
> > それでも U2 は 192.168.2.34 ですから、192.168.1.0/255.255.255.0 のローカルネットワークの外にあるように見えます。
>
> 論理ネットワーク的には仰る通りです。
>
> もう少し具体的に説明すると、UDPのブロードキャストを使用して同一物理ネットワーク上に接続されている
> ユニット(IPアドレスは不明)を検索するというのが最終目的です。
>
> > 192.168.2.0/255.255.255.0 には eth0 から送信するよう設定なされてはどうでしょう?
>
> U2からパケットが返ってきているのは、Wiresharkで確認済です。
>
> もしかしたら、3Gが接続中に異なるネットワークアドレスから発信されたパケットがeth0から入ってきたら、IoTG2上のアプリに
> 渡されずに、3G側に送られたりしているのでしょうか?
>
> それでも、一度U1からパケットが来るとそれ以降はU2からのパケットも受信出来る様になるというのが理解出来ないので、
> 非常に悩んでいます。
arpテーブルの問題ですよね。きっと
deviceがbindされているため、デフォゲ(3G)のインターフェイスで受信する返しパケットが見えないのでは?
1回通ってしまえばarp情報ができるので・・・deviceのバイドをやめるか
net.ipv4.conf.xxxx.arp_ignore
net.ipv4.conf.xxxx.arp_announce
辺りの設定を変更するのが良いかもしれません。

h_ikaoさん

いつもお世話になります。日高です。

> arpテーブルの問題ですよね。きっと
> deviceがbindされているため、デフォゲ(3G)のインターフェイスで受信する返しパケットが見えないのでは?
> 1回通ってしまえばarp情報ができるので・・・deviceのバイドをやめるか
> net.ipv4.conf.xxxx.arp_ignore
> net.ipv4.conf.xxxx.arp_announce
> 辺りの設定を変更するのが良いかもしれません。

arp -a で確認してみました。

[U2のみ(socketレディにならない)]
 ? (221.144.251.155) at 00:1a:4b:61:3e:00 [ether]  on umts0
 p133050-obmd01.osaka.ocn.ne.jp (221.113.213.49) at 00:1a:4b:61:3e:00 [ether]  on umts0
 ? (192.168.1.1) at 00:0d:0b:80:17:99 [ether]  on eth0
 
[U2+U1(socketレディになる)]
 ? (221.144.251.155) at 00:1a:4b:61:3e:00 [ether]  on umts0
 p133050-obmd01.osaka.ocn.ne.jp (221.113.213.49) at 00:1a:4b:61:3e:00 [ether]  on umts0
 ? (192.168.1.1) at 00:0d:0b:80:17:99 [ether]  on eth0
 
[U2のみ(socketレディになる)]
 ? (221.144.251.155) at 00:1a:4b:61:3e:00 [ether]  on umts0
 p133050-obmd01.osaka.ocn.ne.jp (221.113.213.49) at 00:1a:4b:61:3e:00 [ether]  on umts0
 ? (192.168.1.1) at 00:0d:0b:80:17:99 [ether]  on eth0

と、arpテーブルに違いは無い様です。

その後、色々試してみた結果、以下の様な現象がわかりました。

・192.168.1.1 から 192.168.1.30(IoTG)に向けてパケットを送ると、IoTG側ソフトで socketレディになるが、
その後のU2(192.168.11.51)からのパケットには、socketレディにならない。
・全く同じパケットを 192.168.1.255宛てにパケットを送ると、IoTG側ソフトでsocketレディになり、その後の
U2(192.168.11.51)からのパケットもsocketレディになる。

※色々試行錯誤の結果IPアドレスは質問当初とは異なっています。

ソケット内部の動きが一体どうなっているのか判らないのですが、どちらも、192.168.1.1からのパケットは、
IoTG側でsocketレディになり、正しく受信出来てます。
二つのパケットの違いは、ID(IP:Identification)、あて先(IP:Destination)、IP:checksum、UDP:checksumだけです。
(あれ?なぜUDP:checksumも違うんだろ?)

この後、何をチェックしたら良いのでしょうか?

株式会社 ACCESS 三原と申します。

失礼いたしました。UDP ブロードキャストなのですね。

となるとアイデアがありません。INADDR_ANY と SO_BINDTODEVICE の両方を設定している(一見矛盾していますが、同時に指定できます http://developerweb.net/viewtopic.php?id=5722 "If you really only wanted to receive broadcasts from a specific network interface, you could use setsockopt(SO_BINDTODEVICE) instead, and still bind() to INADDR_ANY... ")ことを確認して、それでだめだと分からないです。

三原さん

いつもお世話になっております。日高です。

> となるとアイデアがありません。INADDR_ANY と SO_BINDTODEVICE の両方を設定している(一見矛盾していますが、同時に指定できます http://developerweb.net/viewtopic.php?id=5722 "If you really only wanted to receive broadcasts from a specific network interface, you could use setsockopt(SO_BINDTODEVICE) instead, and still bind() to INADDR_ANY... ")ことを確認して、それでだめだと分からないです。

ソケットは実際には以下の様に作成しています。

    /* ソケットの生成 */
    if ((soc = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        return (EX_UNAVAILABLE);
    }
 
    /* 使用インターフェースの指定 (※この指定はroot権限でないとエラーになる) */
    opt = "eth0";
    if (setsockopt(soc, SOL_SOCKET, SO_BINDTODEVICE, opt, 4) == -1) {
        perror("setsockopt(SO_BINDTODEVICE)");
        (void) close(soc);
        return (EX_OSERR);
    }
 
    /* ブロードキャストの許可 */
    on = 1;
    if (setsockopt(soc, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
        perror("setsockopt(SO_BROADCAST)");
        (void) close(soc);
        return (EX_OSERR);
    }
 
    // 送信元情報のセット
    from.sin_family     = AF_INET;
    from.sin_addr.s_addr= INADDR_ANY;
    from.sin_port       = 0; /* 自動割当 */
    bind(soc, (struct sockaddr *)&from, sizeof(from));

日高さん

お世話になっております。三原です。

    // 送信元情報のセット
    from.sin_family     = AF_INET;
    from.sin_addr.s_addr= INADDR_ANY;
    from.sin_port       = 0; /* 自動割当 */
    bind(soc, (struct sockaddr *)&from, sizeof(from));

受信側がポート自動割り当てというのは基本とは違ったやり方ですね。できたかなあ。

IoTG 側で明示的にポートに bind したらどうなりますか? UDP だと setsockopt() で SO_REUSEADDR を使うと複数プロセスで待てて受信パケットは全部のプロセスに配信されるので、ポートがかぶる心配はないです。 http://jis.hatenablog.com/entry/2013/09/11/143723

日高さん

お世話になっております。三原です。

> IoTG 側で明示的にポートに bind したらどうなりますか? UDP だと setsockopt() で SO_REUSEADDR を使うと複数プロセスで待てて受信パケットは全部のプロセスに配信されるので、ポートがかぶる心配はないです。 http://jis.hatenablog.com/entry/2013/09/11/143723

ごめんなさい。ページによって、複数のプロセスに配信されるかどうか、記述が違いますね。

- 最後のソケットにしか配信されない http://jis.hatenablog.com/entry/2013/09/11/143723
- それぞれのソケットに配信される http://antas.jp/blog/yamakawa/2009/03/rubyudp_1.html

とりあえずポートの bind は試してみましょう。

三原さん

いつもお世話になっております。日高です。

> 受信側がポート自動割り当てというのは基本とは違ったやり方ですね。できたかなあ。

確かに基本とは違ったやり方とは思いますが、from.sin_portでポート番号を指定すると、
sendto(soc,...) で送信されるパケットの送信元が指定のポート番号になってしまいます。
なので、出来るだけポート番号を固定したく無かったので、0(自動割当)としました。

> > IoTG 側で明示的にポートに bind したらどうなりますか? UDP だと setsockopt() で SO_REUSEADDR を使うと複数プロセスで待てて受信パケットは全部のプロセスに配信されるので、ポートがかぶる心配はないです。 http://jis.hatenablog.com/entry/2013/09/11/143723
>
> ごめんなさい。ページによって、複数のプロセスに配信されるかどうか、記述が違いますね。
>
> - 最後のソケットにしか配信されない http://jis.hatenablog.com/entry/2013/09/11/143723
> - それぞれのソケットに配信される http://antas.jp/blog/yamakawa/2009/03/rubyudp_1.html
>
> とりあえずポートの bind は試してみましょう。

SO_REUSEADDRを指定してみましたが結果は変わらずでした。
from.sin_addr.s_addrも以下の様に変えて試してみましたが、全く変化は無く、どれも 異なるネットワークゾーンからの
ブロードキャストは受信出来ませんでした。

(1) s_addr = "192.168.1.10"
(2) s_addr = "192.168.1.255"
(3) s_addr = "255.255.255.255"

きっと、Ethernet→IPスタック→UDPスタックのどこかで捨てられているか、3G側にデータが流されているかのどちらか
だとは思いますが、このIPスタック/UDPスタック内部の詳細ステータスを取得する様なAPIは無いのでしょうか?

日高さん

お世話になっております。三原です。

    // 送信元情報のセット
    from.sin_family     = AF_INET;
    from.sin_addr.s_addr= INADDR_ANY;
    from.sin_port       = 0; /* 自動割当 */
    bind(soc, (struct sockaddr *)&from, sizeof(from));

> > 受信側がポート自動割り当てというのは基本とは違ったやり方ですね。できたかなあ。
>
> 確かに基本とは違ったやり方とは思いますが、from.sin_portでポート番号を指定すると、
> sendto(soc,...) で送信されるパケットの送信元が指定のポート番号になってしまいます。
> なので、出来るだけポート番号を固定したく無かったので、0(自動割当)としました。

自動で割り当てたポート番号を U2 に連絡していますか?

UDP ブロードキャストでも、sendto() で送信時に指定した宛先ポート番号と受信側で bind したポート番号が一致しなければパケットは無視されます。ネット上にサンプルがありませんが書籍 https://www.amazon.co.jp/UNIX%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83… の 460 頁に説明されています。

受信元はポート番号を固定するか、割り当てられたポート番号を送信元に伝達していなければ、パケットが迷子になってしまいます。

おそらく bind に対する誤解があると思います。

bind は引数で指定されるソケットの側の設定でして、送信元設定ではありません。

基本は

- 受信ソケットはポート番号を bind で固定で割り当てる
- 送信ソケットは折り返しパケットが必要なければ自動割り当てでよい
- 送信時に sendto() で送信先のポート番号を指定すれば、bind に指定されたソケットに届く

ブロードキャストではなく単純な UDP のサンプルですが、受信側は bind() していますが送信側は bind() していません。
http://www.geekpage.jp/programming/linux-network/udp.php

KES)小西です。

気になったので、書き込みさせてください。

まず、ネットワークアドレスが違いますが、同一HUBにいるため、UDPのマルチキャストアドレス+グループIDがたまたまHITしU2へ飛んでいるのではないでしょうか?
u2からはfromのネットワークアドレスで破棄するところ、ソケットへ通知が行っているのがU2側の問題かもしれません。

で、U2から応答を返していますが、このとき、UDPパケット上では
To:IoTG2
From:U2
つまりネットワーク違いで電文が流れていませんか?

その場合、3GのON/OFFでIoTG2上で単一ネットワークか複数ネットワークか?で
ルーティング(パケット破棄含む)等が変わると思います。

基本的に、ICMPは通らないと思いますが、IoTG2→U2でping通りますでしょうか?
UDPのマルチキャスト(ユニキャスト)+グループIDで通っているだけな気がします。

三原さん

いつもお世話になっております。日高です。

> 自動で割り当てたポート番号を U2 に連絡していますか?

Wiresharkで見たパケットのサマリを以下に付します。
192.168.1.10がIoTGで、192.168.1.1がU1、192.168.11.51がU2になります。

9496    2.803733    192.168.1.10    255.255.255.255 UDP 84  58400 → 9090  Len=42
9498    0.038153    192.168.11.51   255.255.255.255 UDP 153 9090 → 58400  Len=111
9500    2.803235    192.168.1.10    255.255.255.255 UDP 84  58400 → 9090  Len=42
9502    0.009846    192.168.11.51   255.255.255.255 UDP 153 9090 → 58400  Len=111
9504    2.802007    192.168.1.10    255.255.255.255 UDP 84  58400 → 9090  Len=42
9505    0.000366    192.168.1.1     255.255.255.255 UDP 211 9090 → 58400  Len=169
9506    0.006893    192.168.11.51   255.255.255.255 UDP 153 9090 → 58400  Len=111
9510    0.001054    192.168.1.10    255.255.255.255 UDP 84  58400 → 9090  Len=42
9511    0.023383    192.168.11.51   255.255.255.255 UDP 153 9090 → 58400  Len=111

#9502まではU2からパケットを受けてもselectから抜けて来ません。が、
#9505以降はU2からのパケットを受けてselectから抜けてきます。

> UDP ブロードキャストでも、sendto() で送信時に指定した宛先ポート番号と受信側で bind したポート番号が一致しなければパケットは無視されます。ネット上にサンプルがありませんが書籍 の 460 頁に説明されています。

申し訳ありません。この『UNIXネットワークプログラミング〈Vol-1〉』は残念ながら持ち合わせていません。

> 受信元はポート番号を固定するか、割り当てられたポート番号を送信元に伝達していなければ、パケットが迷子になってしまいます。
>
> おそらく bind に対する誤解があると思います。
>
> bind は引数で指定されるソケットの側の設定でして、送信元設定ではありません。

はい。それは判っています。なので、元々はbindは使用していませんでしたが、試行錯誤の過程でbindを付けて
みたら、前に書いた様に、何故かbindで定義したポート番号がsendtoの送信元ポート番号で使われたので、一応事実としてあの様に書きました。

不思議なのは、3Gが動いていない時は #9505のパケットを受信しなくてもselectから抜けてきて正しく受信
出来るという事。
また、3Gが動いている時でも#9505を受信した後は正しく受信出来る事。
しかし、一度アプリケーションを終了させると元に戻ってしまう事です。