动态更改源IP地址

我有一个服务器有几个不同的IP地址。 此时,每个IP都可以接收UDP请求,但它始终与回复的IP相同,请求者不喜欢。

总而言之,这是所有基本代码:

int sock; socklen_t clilen; struct sockaddr_in serv_addr, cli_addr; memset((char*)&serv_addr, 0, sizeof(serv_addr)); sock = socket(AF_INET, SOCK_DGRAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(PORT); ... recvfrom(sock, buffer, BUFLEN, 0, (struct sockaddr *)&cli_addr, &clilen); ... sendto(sock, resData, resLen, 0, (struct sockaddr *)&cli_addr, sizeof(cli_addr)); 

我希望能够以某种方式指定哪个IP用于发送回我的数据包(这可能因每个请求而有所不同),但我不知道如何使用套接字实现这一点。 我在这个领域并不是那么有经验,所以我能得到的所有帮助都非常感激。

编辑如下

我已经在接受的答案中找到了一个潜在的解决方案: 如何在Linux中重新绑定udp套接字

但是,出现了一个新问题。 我如何知道哪个IP /接口收到了请求? 这样我就可以使用该IP /接口进行响应。

您需要bind()到本地IP地址,即您要使用的接口的IP。

阅读本指南关于bind()

Beej的网络指南#bind

在执行Rx / Tx之前,典型的TCP / UDP套接字绑定在特定的IP /端口上。 尝试使用原始套接字为您的方法。

它的代码相当大,请参考以下链接http://sock-raw.org/papers/sock_raw

通过原始套接字创建一个原始套接字–Rx – 现在您将通过原始套接字从L2 Tx接收整个帧 – 在修改相关字段后从L3头传输

我已经解决了我的问题,并且必须以良好的方式发布解决方案! 我没有使用SOCK_RAW,也没有将我的接口绑定到本地IP等。 这是谷歌100页的混合和一些stackoverflow,所以我有点伤心,我没有保存链接,以给出正确的学分。

由于我不是专家,因此代码中可能存在明显的缺陷,但这是我提出的解决方案并且可行。 我刚刚开始清理代码(当你从100个不同的页面尝试它们并将它们组合起来时会变得很混乱)。 无论如何,这里是:

收到部分:

 int sock; socklen_t clilen; struct sockaddr_in serv_addr, cli_addr; char buffer[BUFLEN]; // Next memset((char*)&serv_addr, 0, sizeof(serv_addr)); sock = socket(AF_INET, SOCK_DGRAM, 0); serv_addr.sin_family = AF_INET; //serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(PORT); clilen = sizeof(cli_addr); if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { error("ERROR on binding"); } bool opt = true; setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt)); // Packet data will be saved in buffer bzero(buffer, BUFLEN); struct iovec vector; vector.iov_base = buffer; vector.iov_len = sizeof(buffer); struct msghdr msg; msg.msg_name = &cli_addr; msg.msg_namelen = sizeof(cli_addr); msg.msg_iov = &vector; msg.msg_iovlen = 1; int flags = 0; // Not sure what controlBuffer contains at this point char controlBuffer[1024]; msg.msg_control = controlBuffer; msg.msg_controllen = 1024; // Recv packet int bytes = ::recvmsg(sock, &msg, flags); struct cmsghdr *cmsg; struct in_pktinfo *dest_ip_ptr; int dest_ip = 0; // Loop through IP header messages cmsg = CMSG_FIRSTHDR(&msg); for ( cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR( &msg, cmsg ) ) { if (cmsg->cmsg_level != IPPROTO_IP || cmsg->cmsg_type != IP_PKTINFO) { continue; } // Get IP (int) struct in_pktinfo *dest_ip_ptr = (struct in_pktinfo*)CMSG_DATA(cmsg); dest_ip = dest_ip_ptr->ipi_addr.s_addr; } // Format IP unsigned char ipParts[4]; ipParts[0] = dest_ip & 0xFF; ipParts[1] = (dest_ip >> 8) & 0xFF; ipParts[2] = (dest_ip >> 16) & 0xFF; ipParts[3] = (dest_ip >> 24) & 0xFF; 

发送部分:

 // Build source sockaddr struct sockaddr_in src_addr; memset(&src_addr, 0, sizeof(struct sockaddr_in)); src_addr.sin_family = AF_INET; // Save IP into a char array char destIp[16]; memset(destIp, 0, sizeof(destIp)); sprintf(destIp, "%d.%d.%d.%d", ipParts[0], ipParts[1], ipParts[2], ipParts[3]); inet_aton(destIp, &(src_addr.sin_addr)); char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))]; bzero(buffer, BUFLEN); int len = dp.getRaw(buffer); struct msghdr mh; memset(&mh, 0, sizeof(mh)); struct cmsghdr *cmsg_send; struct in_pktinfo *pktinfo; struct iovec iov[1]; iov[0].iov_base = buffer; iov[0].iov_len = BUFLEN; mh.msg_name = &cli_addr; // destination address of packet mh.msg_namelen = sizeof(cli_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_send = CMSG_FIRSTHDR(&mh); cmsg_send->cmsg_level = IPPROTO_IP; cmsg_send->cmsg_type = IP_PKTINFO; cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg_send); pktinfo->ipi_ifindex = 0; pktinfo->ipi_spec_dst = src_addr.sin_addr; int rc = sendmsg(sock, &mh, 0);