为 UDP 套接字设置源 IP [英] Setting the source IP for a UDP socket

查看:76
本文介绍了为 UDP 套接字设置源 IP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 UDP 套接字,它绑定到 INADDR_ANY 以侦听我的服务器拥有的所有 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 in_pktinfoipi_spec_dst 字段代码>.请参阅上面引用的帖子 cmsg(3)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

这篇关于为 UDP 套接字设置源 IP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆