繁体   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