4

私は経験豊富なLinuxソケットプログラマーであり、多くの発信インターフェイスを備えたサーバーアプリケーションを作成しています。これで、サーバーソケットは、プロセスの開始時にINADDR_ANYとともにランダムな送信元ポートにバインドされます。

後で特定のノードに応答を送信するときのある時点で、固定の送信元IPアドレスを割り当てる必要があります。これを行う標準的な方法は、bindを呼び出すことです。ただし、bindはポート番号に対して1回呼び出され、後続の呼び出しは無効な引数エラーで失敗します。

新しいソケットを作成することは、実際には良い選択ではありません。これは、一部のクライアントに応答するときに頻繁に行う必要があるためです。

SOや、IP_FREEBINDなどの多くのソケットオプションについても調べましたが、私のシナリオにはあまり適していません。

おそらく、IP_PKT_INFOを使用して送信元アドレスを設定すると、同じ問題が発生しない限り、つまり、INADDRANYにバインドされたソケットが固定ソースIPに再バインドできない場合を除いて機能する可能性があります。

既存のソケットのバインドを解除する方法、または送信パケットに送信元IPアドレスを設定する別の方法はありますか?

    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    if(sock < 0)
        printf("Failed creating socket\n");

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1500);
    addr.sin_addr.s_addr = INADDR_ANY;

    // first bind succeeds
    if ( (status = bind(sock, (struct sockaddr *) &addr, sizeof(addr))) < 0)
        printf("bind error with port %s\n", strerror(errno));  

    struct sockaddr_in src_addr;
    memset(&src_addr, 0, sizeof(struct sockaddr_in));
    src_addr.sin_family = AF_INET;
    if (inet_aton("10.0.2.17", &(src_addr.sin_addr)) == 0)
        printf("Failed copying address\n");

    // second bind fails
    if((status = bind(sock, (struct sockaddr *)&src_addr, sizeof(src_addr))) < 0)
        printf("re bind error with ip %s\n", strerror(errno));

この点に関するアイデアは高く評価されます。私はソケットやSOなどについてかなりの資料を調べましたが、まだ成功していません。

4

3 に答える 3

7

私はついに自分で解決策を見つけたので、コードサンプルで補足された自分の答え(恥知らずですが正しいプラグイン)を受け入れました。

私はもともと、ソケットがすでにバインドされている場所にソケットを再度作成せずに、発信パケットの送信元アドレスを書き換えたいと思っていました。この場合、 bindを複数回呼び出すと失敗し、(私の特定の状況では)ソースIPごとに個別のソケットを設定して使用することはできませんでした。

IP_PACKET_INFOでいくつかの参照を見つけましたが、正しく機能させるのは面倒でした。以下の参考資料が役に立ちました。

udpソケットのソースを設定する

サンプルコード

これは、udpソケットを作成し、それをローカルポートにバインドし、特定のメッセージを送信する前に、送信元IPアドレスを追加する簡単なアプリケーションです。私の場合、sudoインターフェースを作成し、それに別のIPを割り当てたことを念頭に置いてください。そうでない場合、送信呼び出しは失敗します。

int status=-1;
int sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock < 0)
    printf("Failed creating socket\n");

int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

struct sockaddr_in bind_addr;
memset(&bind_addr, 0, sizeof(struct sockaddr_in));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons(44000); // locally bound port

if((status = bind(sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr))) < 0)
    printf("bind error with port %s\n", strerror(errno));

// currently using addr as destination
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(80); // destination port
if (inet_aton("74.125.236.35", &(addr.sin_addr)) == 0)
    printf("Failed copying remote address\n");
else
    printf("Success copying remote address\n");

struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(struct sockaddr_in));
src_addr.sin_family = AF_INET;
if (inet_aton("10.0.2.17", &(src_addr.sin_addr)) == 0)
    printf("Failed copying src address\n");
else
    printf("Success copying src address\n");

char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];

char msg[10] = "hello";
int len = strlen(msg);

struct msghdr mh;
memset(&mh, 0, sizeof(mh));

struct cmsghdr *cmsg;
struct in_pktinfo *pktinfo;

struct iovec iov[1];
iov[0].iov_base = msg;
iov[0].iov_len = len;

mh.msg_name = &addr; // destination address of packet
mh.msg_namelen = sizeof(addr);
mh.msg_control = cmbuf;
mh.msg_controllen = sizeof(cmbuf);
mh.msg_flags = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;

// after initializing msghdr & control data to 
// CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);

//src_interface_index 0 allows choosing interface of the source ip specified
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst = src_addr.sin_addr;

int rc = sendmsg(sock, &mh, 0);
printf("Result %d\n", rc);

重要なステートメントは

pktinfo->ipi_spec_dst = src_addr.sin_addr;

ここで、使用する送信元IPアドレスを指定していますcmsg structなどの残りの部分は、ipoktinfostructを自分で記述できるようにするためにのみ使用されます。

于 2012-10-07T17:20:45.367 に答える
4

既存のソケットのバインドを解除して再バインドする方法はありません。

于 2012-10-03T23:45:43.003 に答える
1

代わりに、インターフェイスごとにソケットを作成してみませんか?UDP / IPプロトコルはコネクションレス型であるため、応答の送信に使用するソケットを選択することで、送信元IPアドレスを選択できます。受信データグラムが受信されたのと同じソケットを使用する必要はありません。

欠点は、ワイルドカードアドレスにバインドできなくなり、複数のソースからデータグラムを同時に受信するには、、、複数のスレッド、またはその他のメカニズムを使用する必要があることselect()ですpoll()。また、クライアントのIPアドレスに基づいてソケットを効率的に選択するためのロジックも必要です。

ほとんどの場合、各リモートIPアドレスを目的のホストIPアドレスにルーティングするためにいくつかのルートエントリを追加し、ホストIPアドレスとポートの組み合わせごとに個別のソケットを使用すると、問題が完全に解決され、非常に効率的に使用できると思います。そうするためのカーネル機能。この動作はアプリケーションの要件である可能性がありますが、代わりにネットワークインターフェイス構成を使用する方が解決できると思います。残念ながら、多くの場合、要件は肉体労働に適した半機能的な馬鹿によって書かれており、あなたの手は縛られています。

複数の物理ネットワークインターフェイスを備えたワークステーションを備えたテストネットワークがある場合は、設計が機能することを確認するために使用できる簡単なC99テストプログラムの例を提供できます。

于 2012-10-04T15:57:05.627 に答える