|
@@ -23,6 +23,7 @@
|
|
|
#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>
|
|
|
|
|
@@ -169,7 +170,8 @@ IfaceMgr::IfaceMgr()
|
|
|
: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 {
|
|
@@ -225,10 +227,11 @@ IfaceMgr::isDirectResponseSupported() const {
|
|
|
}
|
|
|
|
|
|
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
|
|
@@ -236,25 +239,54 @@ IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
|
|
|
// 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");
|
|
@@ -456,8 +488,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
|
|
|
// 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
|
|
@@ -501,7 +533,6 @@ IfaceMgr::handleSocketConfigError(const std::string& errmsg,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
void
|
|
|
IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
|
|
|
for (IfaceCollection::const_iterator iface=ifaces_.begin();
|
|
@@ -717,86 +748,21 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
|
|
|
}
|
|
|
|
|
|
|
|
|
-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);
|
|
@@ -805,113 +771,15 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
|
|
|
}
|
|
|
|
|
|
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
|
|
@@ -919,12 +787,11 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
|
|
|
|
|
|
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));
|
|
|
}
|
|
|
|
|
@@ -1023,8 +890,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
|
|
|
}
|
|
|
|
|
|
// 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));
|
|
|
}
|
|
|
|
|
@@ -1119,102 +985,8 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
|
|
|
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) {
|