#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/pkt_filter_inet6.h>
#include <exceptions/exceptions.h>
#include <util/io/pktinfo_utilities.h>
:control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
control_buf_(new char[control_buf_len_]),
session_socket_(INVALID_SOCKET), session_callback_(NULL),
- packet_filter_(new PktFilterInet())
+ packet_filter_(new PktFilterInet()),
+ packet_filter6_(new PktFilterInet6())
{
try {
}
void
-IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
+IfaceMgr::setPacketFilter(const PktFilterPtr& packet_filter) {
// Do not allow NULL pointer.
if (!packet_filter) {
- isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
+ isc_throw(InvalidPacketFilter, "NULL packet filter object specified for"
+ " DHCPv4");
}
// Different packet filters use different socket types. It does not make
// sense to allow the change of packet filter when there are IPv4 sockets
// new packet filter. Below, we check that there are no open IPv4 sockets.
// If we find at least one, we have to fail. However, caller still has a
// chance to replace the packet filter if he closes sockets explicitly.
+ if (hasOpenSocket(AF_INET)) {
+ // There is at least one socket open, so we have to fail.
+ isc_throw(PacketFilterChangeDenied,
+ "it is not allowed to set new packet"
+ << " filter when there are open IPv4 sockets - need"
+ << " to close them first");
+ }
+ // Everything is fine, so replace packet filter.
+ packet_filter_ = packet_filter;
+}
+
+void
+IfaceMgr::setPacketFilter(const PktFilter6Ptr& packet_filter) {
+ if (!packet_filter) {
+ isc_throw(InvalidPacketFilter, "NULL packet filter object specified for"
+ " DHCPv6");
+ }
+
+ if (hasOpenSocket(AF_INET6)) {
+ // There is at least one socket open, so we have to fail.
+ isc_throw(PacketFilterChangeDenied,
+ "it is not allowed to set new packet"
+ << " filter when there are open IPv6 sockets - need"
+ << " to close them first");
+ }
+
+ packet_filter6_ = packet_filter;
+}
+
+bool
+IfaceMgr::hasOpenSocket(const uint16_t family) const {
+ // Iterate over all interfaces and search for open sockets.
for (IfaceCollection::const_iterator iface = ifaces_.begin();
iface != ifaces_.end(); ++iface) {
const Iface::SocketCollection& sockets = iface->getSockets();
for (Iface::SocketCollection::const_iterator sock = sockets.begin();
sock != sockets.end(); ++sock) {
- if (sock->family_ == AF_INET) {
- // There is at least one socket open, so we have to fail.
- isc_throw(PacketFilterChangeDenied,
- "it is not allowed to set new packet"
- << " filter when there are open IPv4 sockets - need"
- << " to close them first");
+ // Check if the socket matches specified family.
+ if (sock->family_ == family) {
+ // There is at least one socket open, so return.
+ return (true);
}
}
}
- // Everything is fine, so replace packet filter.
- packet_filter_ = packet_filter;
+ // There are no open sockets found for the specified family.
+ return (false);
}
-
void IfaceMgr::stubDetectIfaces() {
string ifaceName;
const string v4addr("127.0.0.1"), v6addr("::1");
// Binding socket to unicast address and then joining multicast group
// works well on Mac OS (and possibly other BSDs), but does not work
// on Linux.
- if ( !joinMulticast(sock, iface->getName(),
- string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
+ if ( !PktFilter6::joinMulticast(sock, iface->getName(),
+ string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
close(sock);
isc_throw(SocketConfigError, "Failed to join "
<< ALL_DHCP_RELAY_AGENTS_AND_SERVERS
}
}
-
void
IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
for (IfaceCollection::const_iterator iface=ifaces_.begin();
}
-int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
-
- 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 the REUSEADDR option so that we don't fail to start if
- // we're being restarted.
- 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
-
- // multicast stuff
- if (addr.getAddress().to_v6().is_multicast()) {
- // both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
- // are link and site-scoped, so there is no sense to join those groups
- // with global addresses.
-
- if ( !joinMulticast( sock, iface.getName(),
- string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
- close(sock);
- isc_throw(SocketConfigError, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
- << " multicast group.");
- }
- }
-
- SocketInfo info(addr, port, sock);
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
+ // Assuming that packet filter is not NULL, because its modifier checks it.
+ SocketInfo info = packet_filter6_->openSocket(iface, addr, port);
iface.addSocket(info);
- return (sock);
+ return (info.sockfd_);
}
-int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
+int
+IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
const uint16_t port, const bool receive_bcast,
const bool send_bcast) {
- // Skip checking if the packet_filter_ is non-NULL because this check
- // has been already done when packet filter object was set.
-
+ // Assuming that packet filter is not NULL, because its modifier checks it.
SocketInfo info = packet_filter_->openSocket(iface, addr, port,
receive_bcast, send_bcast);
iface.addSocket(info);
return (info.sockfd_);
}
-bool
-IfaceMgr::joinMulticast(int sock, const std::string& ifname,
-const std::string & mcast) {
-
- struct ipv6_mreq mreq;
-
- if (inet_pton(AF_INET6, mcast.c_str(),
- &mreq.ipv6mr_multiaddr) <= 0) {
- return (false);
- }
-
- mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
- if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
- &mreq, sizeof(mreq)) < 0) {
- return (false);
- }
-
- return (true);
-}
-
bool
IfaceMgr::send(const Pkt6Ptr& pkt) {
- int result;
-
Iface* iface = getIface(pkt->getIface());
if (!iface) {
- isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
+ isc_throw(BadValue, "Unable to send DHCPv6 message. Invalid interface ("
<< pkt->getIface() << ") specified.");
}
- memset(&control_buf_[0], 0, control_buf_len_);
-
-
- // Set the target address we're sending to.
- sockaddr_in6 to;
- memset(&to, 0, sizeof(to));
- to.sin6_family = AF_INET6;
- to.sin6_port = htons(pkt->getRemotePort());
- memcpy(&to.sin6_addr,
- &pkt->getRemoteAddr().toBytes()[0],
- 16);
- to.sin6_scope_id = pkt->getIndex();
-
- // Initialize our message header structure.
- struct msghdr m;
- memset(&m, 0, sizeof(m));
- m.msg_name = &to;
- m.msg_namelen = sizeof(to);
-
- // Set the data buffer we're sending. (Using this wacky
- // "scatter-gather" stuff... we only have a single chunk
- // of data to send, so we declare a single vector entry.)
-
- // As v structure is a C-style is used for both sending and
- // receiving data, it is shared between sending and receiving
- // (sendmsg and recvmsg). It is also defined in system headers,
- // so we have no control over its definition. To set iov_base
- // (defined as void*) we must use const cast from void *.
- // Otherwise C++ compiler would complain that we are trying
- // to assign const void* to void*.
- struct iovec v;
- memset(&v, 0, sizeof(v));
- v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
- v.iov_len = pkt->getBuffer().getLength();
- m.msg_iov = &v;
- m.msg_iovlen = 1;
-
- // Setting the interface is a bit more involved.
- //
- // We have to create a "control message", and set that to
- // define the IPv6 packet information. We could set the
- // source address if we wanted, but we can safely let the
- // kernel decide what that should be.
- m.msg_control = &control_buf_[0];
- m.msg_controllen = control_buf_len_;
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m);
-
- // FIXME: Code below assumes that cmsg is not NULL, but
- // CMSG_FIRSTHDR() is coded to return NULL as a possibility. The
- // following assertion should never fail, but if it did and you came
- // here, fix the code. :)
- assert(cmsg != NULL);
-
- cmsg->cmsg_level = IPPROTO_IPV6;
- cmsg->cmsg_type = IPV6_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
- struct in6_pktinfo *pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
- memset(pktinfo, 0, sizeof(struct in6_pktinfo));
- pktinfo->ipi6_ifindex = pkt->getIndex();
- // According to RFC3542, section 20.2, the msg_controllen field
- // may be set using CMSG_SPACE (which includes padding) or
- // using CMSG_LEN. Both forms appear to work fine on Linux, FreeBSD,
- // NetBSD, but OpenBSD appears to have a bug, discussed here:
- // http://www.archivum.info/mailing.openbsd.bugs/2009-02/00017/
- // kernel-6080-msg_controllen-of-IPV6_PKTINFO.html
- // which causes sendmsg to return EINVAL if the CMSG_LEN is
- // used to set the msg_controllen value.
- m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
-
- pkt->updateTimestamp();
-
- result = sendmsg(getSocket(*pkt), &m, 0);
- if (result < 0) {
- isc_throw(SocketWriteError, "pkt6 send failed: sendmsg() returned"
- " with an error: " << strerror(errno));
- }
-
- return (result);
+ // Assuming that packet filter is not NULL, because its modifier checks it.
+ return (packet_filter6_->send(*iface, getSocket(*pkt), pkt));
}
bool
Iface* iface = getIface(pkt->getIface());
if (!iface) {
- isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
+ isc_throw(BadValue, "Unable to send DHCPv4 message. Invalid interface ("
<< pkt->getIface() << ") specified.");
}
- // Skip checking if packet filter is non-NULL because it has been
- // already checked when packet filter was set.
+ // Assuming that packet filter is not NULL, because its modifier checks it.
return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
}
}
// Now we have a socket, let's get some data from it!
- // Skip checking if packet filter is non-NULL because it has been
- // already checked when packet filter was set.
+ // Assuming that packet filter is not NULL, because its modifier checks it.
return (packet_filter_->receive(*iface, *candidate));
}
if (!candidate) {
isc_throw(SocketReadError, "received data over unknown socket");
}
-
- // Now we have a socket, let's get some data from it!
- uint8_t buf[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 = 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_;
-
- result = recvmsg(candidate->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 = 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");
- }
-
- // Let's create a packet.
- Pkt6Ptr pkt;
- try {
- pkt = Pkt6Ptr(new Pkt6(buf, result));
- } catch (const std::exception& ex) {
- isc_throw(SocketReadError, "failed to create new packet");
- }
-
- pkt->updateTimestamp();
-
- pkt->setLocalAddr(IOAddress::fromBytes(AF_INET6,
- reinterpret_cast<const uint8_t*>(&to_addr)));
- pkt->setRemoteAddr(IOAddress::fromBytes(AF_INET6,
- reinterpret_cast<const uint8_t*>(&from.sin6_addr)));
- pkt->setRemotePort(ntohs(from.sin6_port));
- pkt->setIndex(ifindex);
-
- Iface* received = getIface(pkt->getIndex());
- if (received) {
- pkt->setIface(received->getName());
- } else {
- isc_throw(SocketReadError, "received packet over unknown interface"
- << "(ifindex=" << pkt->getIndex() << ")");
- }
-
- return (pkt);
+ // Assuming that packet filter is not NULL, because its modifier checks it.
+ return (packet_filter6_->receive(*candidate));
}
uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
#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>
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,
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.
///
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
/// Packet Filter is the one used for unit testing, which doesn't
/// open sockets but rather mimics their behavior (mock object).
PktFilterPtr packet_filter_;
+
+ PktFilter6Ptr packet_filter6_;
};
}; // namespace isc::dhcp