From de4d14843fbb34db0a8af9e805fad96d1151d390 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Tue, 24 May 2011 18:06:01 +0200 Subject: [PATCH] Set source IPv6 address on NTP reply This is needed on systems with multiple IPv6 addresses to reply with the same source address as the destination address of the NTP request. --- configure | 9 +++++++++ ntp_io.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/configure b/configure index cf2555f..3fa76db 100755 --- a/configure +++ b/configure @@ -360,6 +360,15 @@ if [ $feat_ipv6 = "1" ] && \ return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));' then add_def HAVE_IPV6 + if ! test_code 'in6_pktinfo' 'sys/socket.h netinet/in.h' '' '' ' + return sizeof(struct in6_pktinfo);' + then + if test_code 'in6_pktinfo with _GNU_SOURCE' 'sys/socket.h netinet/in.h' \ + '-D_GNU_SOURCE' '' 'return sizeof(struct in6_pktinfo);' + then + add_def _GNU_SOURCE + fi + fi fi if [ $feat_pps = "1" ] && \ diff --git a/ntp_io.c b/ntp_io.c index 9bad2bc..7fdff42 100644 --- a/ntp_io.c +++ b/ntp_io.c @@ -159,6 +159,16 @@ prepare_socket(int family) LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPV6_V6ONLY socket option"); } #endif + +#ifdef IPV6_RECVPKTINFO + if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { + LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option"); + } +#elif defined(IPV6_PKTINFO) + if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { + LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option"); + } +#endif } #endif @@ -345,6 +355,17 @@ read_from_socket(void *anything) } #endif +#ifdef IPV6_PKTINFO + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo ipi; + + memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); + memcpy(&remote_addr.local_ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr, + sizeof (remote_addr.local_ip_addr.addr.in6)); + remote_addr.local_ip_addr.family = IPADDR_INET6; + } +#endif + #ifdef SO_TIMESTAMP if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) { struct timeval tv; @@ -445,6 +466,25 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr) } #endif +#ifdef IPV6_PKTINFO + if (remote_addr->local_ip_addr.family == IPADDR_INET6) { + struct cmsghdr *cmsg; + struct in6_pktinfo *ipi; + + cmsg = CMSG_FIRSTHDR(&msg); + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo))); + cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo)); + + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + + ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg); + memcpy(&ipi->ipi6_addr.s6_addr, &remote_addr->local_ip_addr.addr.in6, + sizeof(ipi->ipi6_addr.s6_addr)); + } +#endif + #if 0 LOG(LOGS_INFO, LOGF_NtpIO, "sending to %s:%d from %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&remote_addr->local_ip_addr));