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.
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
.
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, 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);
Note this is different in IPv6: use struct in6_pktinfo::ipi6_addr
in both the recvmsg and sendmsg cases.
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.
(man pages referenced - getting around 1 hyperlink limit)
http:// linux.die.net/man/2/sendmsg
http:// linux.die.net/man/3/cmsg
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…