2017-11-14 18 views
0

2台のNICを持つLinuxシステムがありますが、そのうちの1台(eth0)がデフォルトゲートウェイです。もう一方(eth1)はデフォルトゲートウェイではありません。どちらもインターネット接続を持つネットワーク接続を持っているので、リンク状態がアップしています。非ゲートウェイインターフェイスでインターネット接続を検出する方法

eth1を定期的にチェックして、インターネットトラフィックがルーティングされるかどうかを確認します。私がeth1をチェックしている間、私はインターネットトラフィックのルーティングを中断しないでeth0にしておく必要があります。 eth1で接続できたら、私のデフォルトゲートウェイを切り替える(これは安い接続なので)。

ソケットを開き、ソケットをeth1にバインドしてポートに接続して接続を確認しようとしています。私は例として8.8.8.8:443を使用しました。

bool canConnect(const string interface) 
{ 
    bool  result = false; 
    sockaddr_in host; 
    timeval  timeout; 

    int sock = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); 
    if (sock == -1) 
     return result; 

    struct ifreq ifr; 
    ::strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ); 

    int res = setsockopt(_sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); 
    if (res < 0) 
    { 
     ::close(sock); 
     return result; 
    } 

    int reuse = 1; 
    if (::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) 
    { 
     ::close(sock); 
     return result; 
    } 

    ::inet_pton(AF_INET, "8.8.8.8", host.sin_addr); 
    host.sin_family  = AF_INET; 
    host.sin_port  = htons(443); 

    if (::connect(sock, (struct sockaddr *)&host , sizeof(host)) < 0) 
    { 
     int error = errno; 
     if (error != EINPROGRESS) // socket is non-blocking 
     { 
      ::close(sock); 
      return result; 
     } 
    } 

    fd_set writeSet; 
    fd_set readSet; 
    FD_ZERO(&readSet); 
    FD_ZERO(&writeSet); 
    FD_SET(sock, &readSet); 
    FD_SET(sock, &writeSet); 

    timeout.tv_sec = 5; 
    timeout.tv_usec = 0; 

    int res = ::select(sock + 1, &readSet, &writeSet, NULL, &timeout); 
    if (res < 0) 
    { 
     ::close(sock); 
     return result; 
    } 

    else if (res == 0) 
    { 
     // function always exits here 
     ::close(sock); 
     return result; 
    } 

    else if (FD_ISSET(sock, &writeSet) || FD_ISSET(sock, &readSet)) 
    { 
     int val; 
     socklen_t len = sizeof(val); 
     ::getsockopt(sock, SOL_SOCKET, SO_ERROR, &val, &len); 
     if (val == 0) 
      result = true; 
    } 
    ::close(sock); 

    return result; 
} 

コードを実行すると、select()は常にタイムアウトします。私はいくつかのコードをしないのですか、私はこの仕事をするために、カーネルオプションが必要なのです(私は一時的にeth1にデフォルトゲートウェイを切り替えることで、これを証明している。

eth1は、インターネット接続を持っているにもかかわらず、8.8.8.8:443への接続を行うために管理することはありません?。。connect()が成功した場合は

私は

答えて

0

上のIPフォワーディングターンを持って待っている、あなたが書き込み可能なだけのためにselect()をチェックする必要がある、ない読める全くこれはconnect() mangageに記載されています

EINPROGRESS
ソケットはノンブロッキングで、接続はすぐに完了することはできません 。を書き込むソケットを選択することにより、select(2)またはpoll(2) の完了が可能です。 select(2) 後はSO_ERRORの理由を説明する、ここに記載されている通常のエラーコード の一つである(失敗 SO_ERROR connect()が正常に完了したかどうかを判断するレベルSOL_SOCKETでオプション(SO_ERRORがゼロである)または を読み取るためにgetsockopt(2)を使用して、筆記を示し失敗の場合)。

さらに、読み取り対象の受信データがあり、HTTPSポートに接続している場合にのみ、ソケットが読み取れるため、送信していないため、サンプルで読み取るデータはありません。 HTTPSハンドシェイク要求。

が実際にはEINPROGRESSで失敗しない限り、select()も呼び出すべきではありません。 connect()が0を返す場合、接続はブロックせずに正常に成功しました。したがって、errnoをチェックした後、if (connect < 0)ブロック内でselect()コールを移動する必要があります。

また、正しいソケットをsetsockopt(SO_BINDTODEVICE)に渡していません。

また、dstパラメータの正しい値をinet_pton()に渡していません。 in_addrを渡していますが、代わりにin_addr*が必要です。

また、connect()を呼び出す前に特定のローカルIP /ポートのペアに「bind()」していないため、この例ではSO_REUSEADDRを使用する必要はありません。

より、このような何か試してみてください:8.8.8.8はDNSサーバであるので、なぜあなたはそれが同様にHTTPSサーバを実行していると思うだろう、言われていること

bool canConnect(const string &interface) 
{ 
    bool result = false; 

    int sock = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); 
    if (sock == -1) 
     return result; 

    struct ifreq ifr; 
    ::strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ); 

    int res = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); 
    if (res < 0) 
    { 
     ::close(sock); 
     return result; 
    } 

    sockaddr_in host; 
    ::inet_pton(AF_INET, "8.8.8.8", &(host.sin_addr)); 
    host.sin_family  = AF_INET; 
    host.sin_port  = htons(443); 

    result = (::connect(sock, (struct sockaddr *)&host, sizeof(host)) == 0); 
    if (!result) 
    { 
     if (errno != EINPROGRESS) // socket is non-blocking 
     { 
      ::close(sock); 
      return result; 
     } 

     fd_set writeSet; 
     timeval timeout; 

     FD_ZERO(&writeSet); 
     FD_SET(sock, &writeSet); 

     timeout.tv_sec = 5; 
     timeout.tv_usec = 0; 

     res = ::select(sock + 1, NULL, &writeSet, NULL, &timeout); 
     if (res <= 0) 
     { 
      ::close(sock); 
      return result; 
     } 

     int val; 
     socklen_t len = sizeof(val); 
     ::getsockopt(sock, SOL_SOCKET, SO_ERROR, &val, &len); 
     result = (val == 0); 
    } 

    ::close(sock); 
    return result; 
} 

を?実際のHTTPSサーバーに接続していることを確認します(HTTPSではなくHTTPSですか?)。 8.8.8.8の代わりにwww.google.com(または他の一般的に使用されるWebサーバー)のIPアドレスを取得する代わりに、getaddrinfo()を使用することを検討してください。

+0

Thanks @ remy-lebeau私はあなたのコメントをチェックしました - 私はほとんどが私のサンプルコードをコピーするときに誤植だと思う。 私はselectでチェックインを取り除き、選択範囲をconnect <0ブロック内に移動しました。 私はすでにポート443が開いていることを確認しているので8.8.8.8を使用していました。eth1をデフォルトゲートウェイとして追加するとコードが機能します。最終的には、私がコントロールしているWebサーバーを使用します。 問題は、eth1をデフォルトゲートウェイにする前に接続性をチェックして、切り替え時に動作することがわかっています。 – linusoft

関連する問題