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

我是一个有经验的Linux套接字编程人员,正在编写一个有很多出接口的服务器应用程序。 现在,服务器套接字与INADDR_ANY一起绑定到进程开始时的随机源端口。

稍后在提交对特定节点的响应时,我需要分配一个固定的源IP地址 。 标准的做法是调用bind。 但是,绑定被调用一次端口号, 连续调用失败,无效的参数错误。

创build一个新的套接字并不是一个好的select,因为在响应某些客户端时,我将不得不经常这样做。

我也探讨了SO和许多套接字选项,如IP_FREEBIND,但它并不完全符合我的scheme。

也许使用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)); 

任何想法在这方面将不胜感激。 我在sockets上经过了相当多的材料,所以没有成功。

Solutions Collecting From Web of "如何在Linux中重新绑定udp套接字"

我终于找到了自己的解决方案,接受我自己的答案,补充了代码示例。

我本来想重写一个传出数据包的源地址,而不是再次创建套接字已经绑定的套接字。 调用绑定多次失败,在这种情况下,(在我的具体情况),我无法为每个源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结构等仅仅是为了能够自己写ipoktinfo结构

没有办法解除绑定和重新绑定现有的套接字。

为什么不为每个接口创建一个套接字呢? 由于UDP / IP协议是无连接的,因此可以通过选择用于发送回复的套接字来选择源IP地址; 没有必要使用接收到的数据报的相同套接字。

缺点是你不能再绑定通配符地址,你必须使用select()poll() ,多线程或其他一些机制同时接收来自多个数据源的数据报。 您还需要一些逻辑来根据客户端IP地址有效地选择套接字。

在大多数情况下,我怀疑添加几个路由条目将每个远程IP地址路由到所需的主机IP地址,并为每个主机IP地址和端口组合使用单独的套接字,可以完美地解决问题 – 并且使用非常高效内核功能这样做。 虽然行为可能是一个应用程序的要求,我怀疑它可以更好地解决使用网络接口配置。 不幸的是,通常这些要求是由半功能白痴写的,更适合体力劳动,而你的手就是束缚的。如果是这样的话,我会相信。

如果您的工作站有多个物理网络接口的测试网络,我可以提供一个简单的C99测试程序示例,您可以使用它来验证设计工作。