簡體   English   中英

如何在Linux中重新綁定udp套接字

[英]How to re bind a udp socket in Linux

我是一位經驗豐富的Linux套接字程序員,正在編寫具有許多傳出接口的服務器應用程序。 現在,在此過程開始時,服務器套接字將與INADDR_ANY綁定到隨機源端口。

稍后在向特定節點提交響應時,我需要分配一個固定的源ip地址 執行此操作的標准方法是調用bind。 但是,bind會為端口號調用一次, 后續調用會失敗,並出現無效的參數錯誤。

創建一個新的套接字並不是一個很好的選擇,因為在響應某些客戶端時,我將不得不經常這樣做。

我還研究了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等的大量材料,但還沒有成功。

我終於找到了解決方案,因此接受了自己的答案(無恥但正確的插件),並補充了代碼示例。

我最初想重寫傳出數據包的源地址,而不必在已綁定套接字的位置再次創建套接字 在這種情況下, 多次調用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等的其他內容僅用於能夠自己編寫ipoktinfo struct

無法取消綁定並重新綁定現有的套接字。

為什么不為每個接口創建一個套接字呢? 由於UDP / IP協議是無連接的,因此您可以通過選擇用於發送答復的套接字來選擇源IP地址。 無需使用接收傳入數據報的套接字。

缺點是您無法再綁定到通配符地址,並且必須使用select()poll() ,多個線程或其他某種機制來同時從多個源接收數據報。 您還需要一些邏輯以根據客戶端IP地址有效地選擇套接字。

在大多數情況下,我懷疑添加一些路由條目以將每個遠程IP地址路由到所需的主機IP地址,並對每個主機IP地址和端口組合使用單獨的套接字可以完美地解決問題-並且使用非常高效內核功能。 盡管此行為可能是應用程序的要求,但我懷疑可以改用網絡接口配置來解決。 不幸的是,這些要求通常是由更適合體力勞動的半功能白痴編寫的,而且雙手被綁住了。如果這樣的話,我感到很同情。

如果您的測試網絡的工作站具有多個物理網絡接口,那么我可以提供一個簡單的C99測試程序示例,您可以使用該示例程序來驗證設計工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM