设置 UDP 套接字的源 IPUDP、IP

2023-09-08 09:09:17 作者:ミ弑桖龍鯎

我有一个绑定到 INADDR_ANY 的 UDP 套接字来监听我的服务器拥有的所有 IP 上的数据包.我正在通过同一个套接字发送回复.

I have a UDP socket that is bound to INADDR_ANY to listen to packets on all the IPs my server has. I'm sending out replies through the same socket.

现在服务器在发送数据包时会自动选择哪个IP作为源IP,但我希望能够自己设置传出源IP.

Right now the server chooses automatically which IP is used as the source IP when packets are sent out, but I would like to be able to set the outgoing source IP myself.

有什么方法可以做到这一点,而不必为每个 IP 创建单独的套接字?

Is there any way to do that without having to create a separate socket for each IP ?

推荐答案

Nikolai,为每个地址使用单独的套接字和 bind(2) 或弄乱路由表通常不是一个可行的选择,例如带有动态地址.单个 IP_ADDRANY 绑定的 UDP 服务器应该能够在接收数据包的同一动态分配的 IP 地址上做出响应.

Nikolai, using a separate socket and bind(2) for each address or messing with routing tables is often not a feasible option e.g. with dynamic addresses. A single IP_ADDRANY-bound UDP server should be able to appear to respond on the same dynamically-assigned IP address a packet is received on.

幸运的是,还有另一种方法.根据您的系统支持,您可以使用 IP_PKTINFO 套接字选项来设置或接收有关消息的辅助数据.通过 comp.os.linux.development.system 具有特定于 IP_PKTINFO 的完整代码示例.

Luckily, there is another way. Depending on your system's support you can make use of the IP_PKTINFO socket options to set or receive ancillary data about a message. Ancillary data (via cmsg(3)) is covered in many places online though comp.os.linux.development.system had a full code sample specific to IP_PKTINFO.

链接中的代码使用 IP_PKTINFO(或 IP_RECVDSTADDR 取决于平台)从辅助 cmsg(3) 数据.此处转述:

The code in the link uses IP_PKTINFO (or IP_RECVDSTADDR depending on the platform) to get the destination address of a UDP message from the ancillary cmsg(3) data. Paraphrased here:

struct msghdr msg;
struct cmsghdr *cmsg;
struct in_addr addr;
// after recvmsg(sd, &msg, flags);
for(cmsg = CMSG_FIRSTHDR(&msg);
    cmsg != NULL;
    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
  if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
    addr = ((struct in_pktinfo*)CMSG_DATA(cmsg))->ipi_addr;
    printf("message received on address %s
", inet_ntoa(addr));
  }
}

Gene,您的问题是如何设置传出数据包的源地址.使用 IP_PKTINFO 可以在传递给 sendmsg(2).有关如何在 struct msghdr.一个例子(这里不保证)可能是:

Gene, your question asked how to set the source address on outgoing packets. With IP_PKTINFO it is possible to set the ipi_spec_dst field of the struct in_pktinfo in the ancillary data passed to sendmsg(2). See the post referenced above, cmsg(3), and sendmsg(2) for guidelines on how to create and manipulate the ancillary data in a struct msghdr. An example (no guarantee here) might be:

struct msghdr msg;
struct cmsghdr *cmsg;
struct in_pktinfo *pktinfo;
// after initializing msghdr & control data to CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg = CMSG_FIRSTHDR(&msg);
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);
pktinfo->ipi_ifindex = src_interface_index;
pktinfo->ipi_spec_dst = src_addr;
// bytes_sent = sendmsg(sd, &msg, flags);

注意这在 IPv6 中有所不同:在 recvmsg 和 sendmsg 情况下都使用 struct in6_pktinfo::ipi6_addr.

Note this is different in IPv6: use struct in6_pktinfo::ipi6_addr in both the recvmsg and sendmsg cases.

另请注意,Windows 不支持 in_pktinfo 结构中的 ipi_spec_dst 等效项,因此您不能使用此方法在传出的 winsock2 数据包上设置源地址.

Note also that Windows does not support an equivalent to ipi_spec_dst in the in_pktinfo struct, so you cannot use this method to set the source address on an outgoing winsock2 packet.

(参考手册页 - 大约有 1 个超链接限制)

(man pages referenced - getting around 1 hyperlink limit)

http:// linux.die.net/man/2/sendmsg
http:// linux.die.net/man/3/cmsg
 
精彩推荐
图片推荐