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.mihara

2016年7月7日 15時51分

株式会社 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 から送信するローカルのルーティングを設定するとどうなりますでしょうか?

hidaka.hiroshi

2016年7月7日 16時04分

日高です。

> 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.mihara

2016年7月7日 16時15分

株式会社 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 から送信するよう設定なされてはどうでしょう?

hidaka.hiroshi

2016年7月7日 16時39分

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

> 株式会社 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からのパケットも受信出来る様になるというのが理解出来ないので、
非常に悩んでいます。

h_ikao

2016年7月7日 17時29分

> いつもお世話になります。日高です。
>
> > 株式会社 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

2016年7月7日 17時30分

> いつもお世話になります。日高です。
>
> > 株式会社 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.mihara

2016年7月7日 18時32分

株式会社 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... ")ことを確認して、それでだめだと分からないです。

hidaka.hiroshi

2016年7月11日 15時59分

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も違うんだろ?)

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

hidaka.hiroshi

2016年7月11日 16時06分

三原さん

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

> となるとアイデアがありません。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));

access.mihara

2016年7月12日 11時46分

日高さん

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


// 送信元情報のセット
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

access.mihara

2016年7月12日 11時57分

日高さん

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

> 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 は試してみましょう。

hidaka.hiroshi

2016年7月12日 16時44分

三原さん

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

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

確かに基本とは違ったやり方とは思いますが、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は無いのでしょうか?

access.mihara

2016年7月12日 17時19分

日高さん

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


// 送信元情報のセット
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-konishi

2016年7月12日 17時33分

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で通っているだけな気がします。

hidaka.hiroshi

2016年7月12日 17時55分

三原さん

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

> 自動で割り当てたポート番号を 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を受信した後は正しく受信出来る事。
しかし、一度アプリケーションを終了させると元に戻ってしまう事です。