socket-default: Refactor setting source address when sending messages

This ensures we don't pass data (via msg_control) defined in a different
scope to sendmsg().  Actually, some compilers (e.g. GCC 5.2.1) might
optimize the memcpy() call away causing the packets not to get sent from
the intended source address.

It also makes the code clearer than with all these ifdefs.

Fixes #1171.
This commit is contained in:
Tobias Brunner 2015-11-02 16:22:38 +01:00
parent 99747bed8f
commit 47e113a639
1 changed files with 107 additions and 46 deletions

View File

@ -355,6 +355,107 @@ METHOD(socket_t, receiver, status_t,
return SUCCESS;
}
/**
* Generic function to send a message.
*/
static ssize_t send_msg_generic(int skt, struct msghdr *msg)
{
return sendmsg(skt, msg, 0);
}
/**
* Send a message with the IPv4 source address set, if possible.
*/
#ifdef IP_PKTINFO
static ssize_t send_msg_v4(int skt, struct msghdr *msg, host_t *src)
{
char buf[CMSG_SPACE(sizeof(struct in_pktinfo))] = {};
struct cmsghdr *cmsg;
struct in_addr *addr;
struct in_pktinfo *pktinfo;
struct sockaddr_in *sin;
msg->msg_control = buf;
msg->msg_controllen = sizeof(buf);
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = SOL_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
addr = &pktinfo->ipi_spec_dst;
sin = (struct sockaddr_in*)src->get_sockaddr(src);
memcpy(addr, &sin->sin_addr, sizeof(struct in_addr));
return send_msg_generic(skt, msg);
}
#elif defined(IP_SENDSRCADDR)
static ssize_t send_msg_v4(int skt, struct msghdr *msg, host_t *src)
{
char buf[CMSG_SPACE(sizeof(struct in_addr))] = {};
struct cmsghdr *cmsg;
struct in_addr *addr;
struct sockaddr_in *sin;
msg->msg_control = buf;
msg->msg_controllen = sizeof(buf);
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = SOL_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
addr = (struct in_addr*)CMSG_DATA(cmsg);
sin = (struct sockaddr_in*)src->get_sockaddr(src);
memcpy(addr, &sin->sin_addr, sizeof(struct in_addr));
return send_msg_generic(skt, msg);
}
#else /* IP_PKTINFO || IP_RECVDSTADDR */
static ssize_t send_msg_v4(int skt, struct msghdr *msg, host_t *src)
{
return send_msg_generic(skt, msg);
}
#endif /* IP_PKTINFO || IP_RECVDSTADDR */
/**
* Send a message with the IPv6 source address set, if possible.
*/
#ifdef HAVE_IN6_PKTINFO
static ssize_t send_msg_v6(int skt, struct msghdr *msg, host_t *src)
{
char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))] = {};
struct cmsghdr *cmsg;
struct in6_pktinfo *pktinfo;
struct sockaddr_in6 *sin;
msg->msg_control = buf;
msg->msg_controllen = sizeof(buf);
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = SOL_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
sin = (struct sockaddr_in6*)src->get_sockaddr(src);
memcpy(&pktinfo->ipi6_addr, &sin->sin6_addr, sizeof(struct in6_addr));
return send_msg_generic(skt, msg);
}
#else /* HAVE_IN6_PKTINFO */
static ssize_t send_msg_v6(int skt, struct msghdr *msg, host_t *src)
{
return send_msg_generic(skt, msg);
}
#endif /* HAVE_IN6_PKTINFO */
METHOD(socket_t, sender, status_t,
private_socket_default_socket_t *this, packet_t *packet)
{
@ -363,7 +464,6 @@ METHOD(socket_t, sender, status_t,
chunk_t data;
host_t *src, *dst;
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov;
u_int8_t *dscp;
@ -465,56 +565,17 @@ METHOD(socket_t, sender, status_t,
{
if (family == AF_INET)
{
#if defined(IP_PKTINFO) || defined(IP_SENDSRCADDR)
struct in_addr *addr;
struct sockaddr_in *sin;
#ifdef IP_PKTINFO
char buf[CMSG_SPACE(sizeof(struct in_pktinfo))];
struct in_pktinfo *pktinfo;
#elif defined(IP_SENDSRCADDR)
char buf[CMSG_SPACE(sizeof(struct in_addr))];
#endif
memset(buf, 0, sizeof(buf));
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_IP;
#ifdef IP_PKTINFO
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
addr = &pktinfo->ipi_spec_dst;
#elif defined(IP_SENDSRCADDR)
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
addr = (struct in_addr*)CMSG_DATA(cmsg);
#endif
sin = (struct sockaddr_in*)src->get_sockaddr(src);
memcpy(addr, &sin->sin_addr, sizeof(struct in_addr));
#endif /* IP_PKTINFO || IP_SENDSRCADDR */
bytes_sent = send_msg_v4(skt, &msg, src);
}
#ifdef HAVE_IN6_PKTINFO
else
{
char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
struct in6_pktinfo *pktinfo;
struct sockaddr_in6 *sin;
memset(buf, 0, sizeof(buf));
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
sin = (struct sockaddr_in6*)src->get_sockaddr(src);
memcpy(&pktinfo->ipi6_addr, &sin->sin6_addr, sizeof(struct in6_addr));
bytes_sent = send_msg_v6(skt, &msg, src);
}
#endif /* HAVE_IN6_PKTINFO */
}
bytes_sent = sendmsg(skt, &msg, 0);
else
{
bytes_sent = send_msg_generic(skt, &msg);
}
if (bytes_sent != data.len)
{