Commit 21d2f7ec authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3251'

parents be25b97a e1c59f67
......@@ -73,6 +73,7 @@
* - @subpage libdhcpRelay
* - @subpage libdhcpIfaceMgr
* - @subpage libdhcpPktFilter
* - @subpage libdhcpPktFilter6
* - @subpage libdhcpsrv
* - @subpage leasemgr
* - @subpage cfgmgr
......
......@@ -42,7 +42,9 @@ libb10_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
libb10_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc
libb10_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc
libb10_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h
libb10_dhcp___la_SOURCES += pkt_filter_inet6.cc pkt_filter_inet6.h
if OS_LINUX
libb10_dhcp___la_SOURCES += pkt_filter_lpf.cc pkt_filter_lpf.h
......
This diff is collapsed.
......@@ -21,6 +21,7 @@
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter6.h>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
......@@ -554,8 +555,8 @@ public:
/// @param ifname name of the interface
/// @param addr address to be bound.
/// @param port UDP port.
/// @param receive_bcast configure IPv4 socket to receive broadcast messages.
/// This parameter is ignored for IPv6 sockets.
/// @param receive_bcast configure IPv4 socket to receive broadcast
/// messages or IPv6 socket to join multicast group.
/// @param send_bcast configure IPv4 socket to send broadcast messages.
/// This parameter is ignored for IPv6 sockets.
///
......@@ -577,11 +578,13 @@ public:
/// Instead, the method searches through the addresses on the specified
/// interface and selects one that matches the address family.
///
/// @note This method does not join the socket to the multicast group.
///
/// @param ifname name of the interface
/// @param port UDP port
/// @param family address family (AF_INET or AF_INET6)
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
/// @return socket descriptor, if socket creation and binding was
/// successful.
/// @throw isc::Unexpected if failed to create and bind socket.
/// @throw isc::BadValue if there is no address on specified interface
/// that belongs to given family.
......@@ -594,10 +597,12 @@ public:
/// This methods differs from \ref openSocket in that it does not require
/// the specification of the interface to which the socket will be bound.
///
/// @note This method does not join the socket to the multicast group.
///
/// @param addr address to be bound
/// @param port UDP port
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
/// @return socket descriptor, if socket creation and binding was
/// successful.
/// @throw isc::Unexpected if failed to create and bind socket
/// @throw isc::BadValue if specified address is not available on
/// any interface
......@@ -611,10 +616,12 @@ public:
/// identified, \ref openSocket is called to open a socket and bind it to
/// the interface, address and port.
///
/// @note This method does not join the socket to a multicast group.
///
/// @param remote_addr remote address to connect to
/// @param port UDP port
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
/// @return socket descriptor, if socket creation and binding was
/// successful.
/// @throw isc::Unexpected if failed to create and bind socket
int openSocketFromRemoteAddress(const isc::asiolink::IOAddress& remote_addr,
const uint16_t port);
......@@ -749,22 +756,45 @@ public:
session_callback_ = callback;
}
/// @brief Set Packet Filter object to handle send/receive packets.
/// @brief Set packet filter object to handle sending and receiving DHCPv4
/// messages.
///
/// Packet Filters expose low-level functions handling sockets opening
/// and sending/receiving packets through those sockets. This function
/// sets custom Packet Filter (represented by a class derived from PktFilter)
/// to be used by IfaceMgr. Note that there must be no IPv4 sockets open
/// when this function is called. Call closeSockets(AF_INET) to close
/// all hanging IPv4 sockets opened by the current packet filter object.
/// Packet filter objects provide means for the @c IfaceMgr to open sockets
/// for IPv4 packets reception and sending. This function sets custom packet
/// filter (represented by a class derived from PktFilter) to be used by
/// @c IfaceMgr. Note that there must be no IPv4 sockets open when this
/// function is called. Call closeSockets(AF_INET) to close all hanging IPv4
/// sockets opened by the current packet filter object.
///
/// @param packet_filter new packet filter to be used by IfaceMgr to send/receive
/// packets and open sockets.
/// @param packet_filter A pointer to the new packet filter object to be
/// used by @c IfaceMgr.
///
/// @throw InvalidPacketFilter if provided packet filter object is NULL.
/// @throw PacketFilterChangeDenied if there are open IPv4 sockets
/// @throw PacketFilterChangeDenied if there are open IPv4 sockets.
void setPacketFilter(const PktFilterPtr& packet_filter);
/// @brief Set packet filter object to handle sending and receving DHCPv6
/// messages.
///
/// Packet filter objects provide means for the @c IfaceMgr to open sockets
/// for IPv6 packets reception and sending. This function sets the new
/// instance of the packet filter which will be used by @c IfaceMgr to send
/// and receive DHCPv6 messages, until replaced by another packet filter.
///
/// It is required that DHCPv6 messages are send and received using methods
/// of the same object that was used to open socket. Therefore, it is
/// required that all IPv6 sockets are closed prior to calling this
/// function. Call closeSockets(AF_INET6) to close all hanging IPv6 sockets
/// opened by the current packet filter object.
///
/// @param packet_filter A pointer to the new packet filter object to be
/// used by @c IfaceMgr.
///
/// @throw isc::dhcp::InvalidPacketFilter if specified object is NULL.
/// @throw isc::dhcp::PacketFilterChangeDenied if there are open IPv6
/// sockets.
void setPacketFilter(const PktFilter6Ptr& packet_filter);
/// @brief Set Packet Filter object to handle send/receive packets.
///
/// This function sets Packet Filter object to be used by IfaceMgr,
......@@ -832,9 +862,13 @@ protected:
/// @param iface reference to interface structure.
/// @param addr an address the created socket should be bound to
/// @param port a port that created socket should be bound to
/// @param join_multicast A boolean parameter which indicates whether
/// socket should join All_DHCP_Relay_Agents_and_servers multicast
/// group.
///
/// @return socket descriptor
int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, uint16_t port);
int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr,
uint16_t port, const bool join_multicast);
/// @brief Detects network interfaces.
///
......@@ -899,23 +933,6 @@ protected:
SessionCallback session_callback_;
private:
/// @brief Joins IPv6 multicast group on a socket.
///
/// Socket must be created and bound to an address. Note that this
/// address is different than the multicast address. For example DHCPv6
/// server should bind its socket to link-local address (fe80::1234...)
/// and later join ff02::1:2 multicast group.
///
/// @param sock socket fd (socket must be bound)
/// @param ifname interface name (for link-scoped multicast groups)
/// @param mcast multicast address to join (e.g. "ff02::1:2")
///
/// @return true if multicast join was successful
///
bool
joinMulticast(int sock, const std::string& ifname,
const std::string& mcast);
/// @brief Identifies local network address to be used to
/// connect to remote address.
///
......@@ -951,15 +968,29 @@ private:
void handleSocketConfigError(const std::string& errmsg,
IfaceMgrErrorMsgCallback handler);
/// @brief Checks if there is at least one socket of the specified family
/// open.
///
/// @param family A socket family.
///
/// @return true if there is at least one socket open, false otherwise.
bool hasOpenSocket(const uint16_t family) const;
/// Holds instance of a class derived from PktFilter, used by the
/// IfaceMgr to open sockets and send/receive packets through these
/// sockets. It is possible to supply custom object using
/// setPacketFilter class. Various Packet Filters differ mainly by using
/// setPacketFilter method. Various Packet Filters differ mainly by using
/// different types of sockets, e.g. SOCK_DGRAM, SOCK_RAW and different
/// families, e.g. AF_INET, AF_PACKET etc. Another possible type of
/// Packet Filter is the one used for unit testing, which doesn't
/// open sockets but rather mimics their behavior (mock object).
PktFilterPtr packet_filter_;
/// Holds instance of a class derived from PktFilter6, used by the
/// IfaceMgr to manage sockets used to send and receive DHCPv6
/// messages. It is possible to supply a custom object using
/// setPacketFilter method.
PktFilter6Ptr packet_filter6_;
};
}; // namespace isc::dhcp
......
......@@ -185,5 +185,50 @@ the regular IP/UDP socket. The isc::dhcp::PktFilterInet should be used in all
cases when an application using the libdhcp++ doesn't require sending
DHCP messages to a device which doesn't have an address yet.
@section libdhcpPktFilter6 Switchable Packet Filters for DHCPv6
The DHCPv6 implementation doesn't suffer from the problems described in \ref
libdhcpPktFilter. Therefore, the socket creation and methods used to send
and receive DHCPv6 messages are common for all OSes. However, there is
still a need to customize the operations on the sockets to reliably unit test
the \ref isc::dhcp::IfaceMgr logic.
The \ref isc::dhcp::IfaceMgr::openSockets6 function examines configuration
of detected interfaces for their availability to listen DHCPv6 traffic. For
all running interfaces (except local loopback) it will try to open a socket
and bind it to the link local or global unicast address. The socket will
not be opened on the interface which is down or for which it was explicitly
specified that it should not be used to listen to DHCPv6 messages. There is
a substantial amount of logic in this function that has to be unit tested for
various interface configurations, e.g.:
- multiple interfaces with link-local addresses only
- multiple interfaces, some of them having global unicast addresses,
- multiple interfaces, some of them disabled
- no interfaces
The \ref isc::dhcp::IfaceMgr::openSockets6 function attempts to open
sockets on detected interfaces. At the same time, the number of interfaces,
and their configuration is specific to OS where the tests are being run.
So the test doesn't have any means to configure interfaces for the test case
being run. Moreover, a unit test should not change the configuration of the
system. For example, a change to the configuration of the interface which
is used to access the machine running a test, may effectively break the
access to this machine.
In order to overcome the problem described above, the unit tests use
fake interfaces which can be freely added, configured and removed from the
\ref isc::dhcp::IfaceMgr. Obviously, it is not possible to open a socket
on a fake interface, nor use it to send or receive IP packets. To mimic
socket operations on fake interfaces it is required that the functions
which open sockets, send messages and receive messages have to be
customizable. This is achieved by implementation of replaceable packet
filter objects which derive from the \ref isc::dhcp::PktFilter6 class.
The default implementation of this class is \ref isc::dhcp::PktFilterInet6
which creates a regular datagram IPv6/UDPv6 socket. The unit tests use a
stub implementation isc::dhcp::test::PktFilter6Stub which contains no-op
functions.
Use \ref isc::dhcp::IfaceMgr::setPacketFilter function to set the custom packet
filter object to be used by Interface Manager.
*/
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcp/pkt_filter6.h>
namespace isc {
namespace dhcp {
bool
PktFilter6::joinMulticast(int sock, const std::string& ifname,
const std::string & mcast) {
struct ipv6_mreq mreq;
memset(&mreq, 0, sizeof(ipv6_mreq));
// Convert the multicast address to a binary form.
if (inet_pton(AF_INET6, mcast.c_str(), &mreq.ipv6mr_multiaddr) <= 0) {
return (false);
}
// Set the interface being used.
mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
// Join the multicast group.
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq, sizeof(mreq)) < 0) {
return (false);
}
return (true);
}
} // end of isc::dhcp namespace
} // end of isc namespace
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef PKT_FILTER6_H
#define PKT_FILTER6_H
#include <asiolink/io_address.h>
#include <dhcp/pkt6.h>
namespace isc {
namespace dhcp {
/// Forward declaration to the structure describing a socket.
struct SocketInfo;
/// Forward declaration to the class representing interface
class Iface;
/// @brief Abstract packet handling class for DHCPv6.
///
/// This class defines methods for performing low level operations on IPv6
/// socket:
/// - open socket,
/// - send DHCPv6 message through the socket,
/// - receive DHCPv6 through the socket.
///
/// Methods exposed by this class are called through the @c IfaceMgr only. They
/// are not meant to be called directly, except unit testing.
///
/// The @c IfaceMgr is responsible for managing the pool of sockets. In
/// particular, @c IfaceMgr detects interfaces suitable to send/receive DHCPv6
/// messages. When it intends to open a socket on a particular interface, it
/// will call the PktFilter6::openSocket. If this call is successful, the
/// structure describing a new socket is returned.
///
/// In order to send or receive a DHCPv6 message through this socket,
/// the @c IfaceMgr must use PktFilter6::send or PktFilter6::receive
/// functions of the same class that has been used to open a socket,
/// i.e. all send/receive operations should be performed using this
/// particular class.
///
/// The major motivation behind creating a separate class, to manage low level
/// operations using sockets, is to make @c IfaceMgr unit testable. By providing
/// a stub implementation of this class which mimics the behavior of the real
/// socket handling class, it is possible to simulate and test various
/// conditions. For example, the @c IfaceMgr::openSockets function will try to
/// open sockets on all available interfaces. The test doesn't have any means
/// to know which interfaces are present. In addition, even if the network
/// interface detection was implemented on the test side, there is no guarantee
/// that the particular system has sufficient number of suitable IPv6-enabled
/// interfaces available for a particular test. Moreover, the test may need
/// to tweak some of the interface configuration to cover certain test
/// scenarios. The proposed solution is to not use the actual interfaces
/// but simply create a pool of fake interfaces which configuration
/// can be freely modified by a test. This however implies that operations
/// on sockets must be simulated.
///
/// @note This class is named after @c PktFilter abstract class which exposes
/// similar interface for DHVPv4. However, the PktFilter class is devoted to
/// solve the problem of sending DHCPv4 messages to the hosts which don't have
/// an IP address yet (a.k.a. direct DHCPv4 traffic). Where required, the
/// custom implementations of @c PktFilter are provided to send and receive
/// messages through raw sockets. In order to filter out the desired traffic
/// Linux Packet Filtering or Berkeley Packet Filtering is used, hence the
/// name of the class. In case of DHCPv6 regular IPv6/UDPv6 sockets are used
/// and derived classes do not use Linux or Berkeley Packet Filtering.
class PktFilter6 {
public:
/// @brief Virtual Destructor.
virtual ~PktFilter6() { }
/// @brief Opens a socket.
///
/// This function open an IPv6 socket on an interface and binds it to a
/// specified UDP port and IPv6 address.
///
/// @param iface Interface descriptor.
/// @param addr Address on the interface to be used to send packets.
/// @param port Port number.
/// @param join_multicast A boolean parameter which indicates whether
/// socket should join All_DHCP_Relay_Agents_and_servers multicast
/// group.
///
/// @return A structure describing a primary and fallback socket.
virtual SocketInfo openSocket(const Iface& iface,
const isc::asiolink::IOAddress& addr,
const uint16_t port,
const bool join_multicast) = 0;
/// @brief Receives DHCPv6 message on the interface.
///
/// This function receives a single DHCPv6 message through using a socket
/// open on a specified interface.
///
/// @param socket_info A structure holding socket information.
///
/// @return A pointer to received message.
virtual Pkt6Ptr receive(const SocketInfo& socket_info) = 0;
/// @brief Sends DHCPv6 message through a specified interface and socket.
///
/// This function sends a DHCPv6 message through a specified interface and
/// socket. In general, there may be multiple sockets open on a single
/// interface as a single interface may have multiple IPv6 addresses.
///
/// @param iface Interface to be used to send packet.
/// @param sockfd A socket descriptor
/// @param pkt A packet to be sent.
///
/// @return A result of sending the message. It is 0 if successful.
virtual int send(const Iface& iface, uint16_t sockfd,
const Pkt6Ptr& pkt) = 0;
/// @brief Joins IPv6 multicast group on a socket.
///
/// Socket must be created and bound to an address. Note that this
/// address is different than the multicast address. For example DHCPv6
/// server should bind its socket to link-local address (fe80::1234...)
/// and later join ff02::1:2 multicast group.
///
/// @param sock A socket descriptor (socket must be bound).
/// @param ifname An interface name (for link-scoped multicast groups).
/// @param mcast A multicast address to join (e.g. "ff02::1:2").
///
/// @return true if multicast join was successful
static bool joinMulticast(int sock, const std::string& ifname,
const std::string & mcast);
};
/// Pointer to a PktFilter object.
typedef boost::shared_ptr<PktFilter6> PktFilter6Ptr;
} // namespace isc::dhcp
} // namespace isc
#endif // PKT_FILTER6_H
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcp/iface_mgr.h>
#include <dhcp/pkt6.h>
#include <dhcp/pkt_filter_inet6.h>
#include <util/io/pktinfo_utilities.h>
#include <netinet/in.h>
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
PktFilterInet6::PktFilterInet6()
: control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
control_buf_(new char[control_buf_len_]) {
}
SocketInfo
PktFilterInet6::openSocket(const Iface& iface,
const isc::asiolink::IOAddress& addr,
const uint16_t port,
const bool join_multicast) {
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
if (addr.toText() != "::1") {
addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
}
memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
#ifdef HAVE_SA_LEN
addr6.sin6_len = sizeof(addr6);
#endif
// @todo use sockcreator once it becomes available
// make a socket
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
}
// Set SO_REUSEADDR option.
int flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&flag, sizeof(flag)) < 0) {
close(sock);
isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
}
if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
close(sock);
isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
<< "/port=" << port);
}
#ifdef IPV6_RECVPKTINFO
// RFC3542 - a new way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&flag, sizeof(flag)) != 0) {
close(sock);
isc_throw(SocketConfigError, "setsockopt: IPV6_RECVPKTINFO failed.");
}
#else
// RFC2292 - an old way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
&flag, sizeof(flag)) != 0) {
close(sock);
isc_throw(SocketConfigError, "setsockopt: IPV6_PKTINFO: failed.");
}
#endif
// Join All_DHCP_Relay_Agents_and_Servers multicast group if
// requested.
if (join_multicast &&
!joinMulticast(sock, iface.getName(),
std::string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
close(sock);
isc_throw(SocketConfigError, "Failed to join "
<< ALL_DHCP_RELAY_AGENTS_AND_SERVERS
<< " multicast group.");
}
return (SocketInfo(addr, port, sock));
}
Pkt6Ptr
PktFilterInet6::receive(const SocketInfo& socket_info) {
// Now we have a socket, let's get some data from it!
uint8_t buf[IfaceMgr::RCVBUFSIZE];
memset(&control_buf_[0], 0, control_buf_len_);
struct sockaddr_in6 from;
memset(&from, 0, sizeof(from));
// Initialize our message header structure.
struct msghdr m;
memset(&m, 0, sizeof(m));
// Point so we can get the from address.
m.msg_name = &from;
m.msg_namelen = sizeof(from);
// Set the data buffer we're receiving. (Using this wacky
// "scatter-gather" stuff... but we that doesn't really make
// sense for us, so we use a single vector entry.)
struct iovec v;
memset(&v, 0, sizeof(v));
v.iov_base = static_cast<void*>(buf);
v.iov_len = IfaceMgr::RCVBUFSIZE;
m.msg_iov = &v;
m.msg_iovlen = 1;
// Getting the interface is a bit more involved.
//
// We set up some space for a "control message". We have
// previously asked the kernel to give us packet
// information (when we initialized the interface), so we
// should get the destination address from that.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
int result = recvmsg(socket_info.sockfd_, &m, 0);
struct in6_addr to_addr;
memset(&to_addr, 0, sizeof(to_addr));
int ifindex = -1;
if (result >= 0) {
struct in6_pktinfo* pktinfo = NULL;
// If we did read successfully, then we need to loop
// through the control messages we received and
// find the one with our destination address.
//
// We also keep a flag to see if we found it. If we
// didn't, then we consider this to be an error.
bool found_pktinfo = false;
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
(cmsg->cmsg_type == IPV6_PKTINFO)) {
pktinfo = util::io::internal::convertPktInfo6(CMSG_DATA(cmsg));
to_addr = pktinfo->ipi6_addr;
ifindex = pktinfo->ipi6_ifindex;
found_pktinfo = true;
break;
}
cmsg = CMSG_NXTHDR(&m, cmsg);
}
if (!found_pktinfo) {
isc_throw(SocketReadError, "unable to find pktinfo");
}
} else {
isc_throw(SocketReadError, "failed to receive data");
}