On BSD open the socket, bind to in6addr_any and join multicast group.
const Iface::SocketCollection& sockets = iface->getSockets();
for (Iface::SocketCollection::const_iterator sock = sockets.begin();
sock != sockets.end(); ++sock) {
- // Check if the socket address matches the specified address.
+ // Check if the socket address matches the specified address or
+ // if address is unspecified (in6addr_any).
if (sock->addr_ == addr) {
return (true);
+ } else if (sock->addr_ == IOAddress("::")) {
+ // Handle the case that the address is unspecified (any).
+ // In this case, we should check if the specified address
+ // belongs to any of the interfaces.
+ for (Iface::AddressCollection::const_iterator addr_it =
+ iface->getAddresses().begin();
+ addr_it != iface->getAddresses().end();
+ ++addr_it) {
+ if (addr == *addr_it) {
+ return (true);
+ }
+ }
}
}
}
return IOAddress(local_address);
}
-int
-IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
- const bool join_multicast) {
- // Assuming that packet filter is not NULL, because its modifier checks it.
- SocketInfo info = packet_filter6_->openSocket(iface, addr, port,
- join_multicast);
- iface.addSocket(info);
-
- return (info.sockfd_);
-}
-
int
IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
const uint16_t port, const bool receive_bcast,
/// @brief Checks if there is a socket open and bound to an address.
///
/// This function checks if one of the sockets opened by the IfaceMgr is
- /// bound to the IP address specified as the method parameter.
+ /// bound to the IP address specified as the method parameter. If the
+ /// socket is bound to the port (and address is unspecified), the
+ /// method will check if the address passed in the argument is configured
+ /// on an interface.
///
/// @param addr Address of the socket being searched.
///
return (true);
}
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
+ const bool join_multicast) {
+ // On BSD, we bind the socket to in6addr_any and join multicast group
+ // to receive multicast traffic. So, if the multicast is requested,
+ // replace the address specified by the caller with the "unspecified"
+ // address.
+ IOAddress actual_address = join_multicast ? IOAddress("::") : addr;
+ SocketInfo info = packet_filter6_->openSocket(iface, actual_address, port,
+ join_multicast);
+ iface.addSocket(info);
+ return (info.sockfd_);
+}
+
} // end of isc::dhcp namespace
} // end of dhcp namespace
// link-local address. It may be required for us to close this
// socket if an attempt to open and bind a socket to multicast
// address fails.
+ int sock;
try {
- openSocket(iface.getName(), addr, port, iface.flag_multicast_);
+ sock = openSocket(iface.getName(), addr, port,
+ iface.flag_multicast_);
} catch (const Exception& ex) {
IFACEMGR_ERROR(SocketConfigError, error_handler,
return (false);
}
+
+ // In order to receive multicast traffic another socket is opened
+ // and bound to the multicast address.
+
+ /// @todo The DHCPv6 requires multicast so we may want to think
+ /// whether we want to open the socket on a multicast-incapable
+ /// interface or not. For now, we prefer to be liberal and allow
+ /// it for some odd use cases which may utilize non-multicast
+ /// interfaces. Perhaps a warning should be emitted if the
+ /// interface is not a multicast one.
+ if (iface.flag_multicast_) {
+ try {
+ openSocket(iface.getName(),
+ IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+ port);
+ } catch (const Exception& ex) {
+ // An attempt to open and bind a socket to multicast addres
+ // has failed. We have to close the socket we previously
+ // bound to link-local address - this is everything or
+ // nothing strategy.
+ iface.delSocket(sock);
+ IFACEMGR_ERROR(SocketConfigError, error_handler,
+ "Failed to open multicast socket on"
+ " interface " << iface.getName()
+ << ", reason: " << ex.what());
+ return (false);
+ }
+ }
+ // Both sockets have opened successfully.
return (true);
}
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
+ const bool join_multicast) {
+ // Assuming that packet filter is not NULL, because its modifier checks it.
+ SocketInfo info = packet_filter6_->openSocket(iface, addr, port,
+ join_multicast);
+ iface.addSocket(info);
+
+ return (info.sockfd_);
+}
+
} // end of isc::dhcp namespace
} // end of isc namespace
return (true);
}
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
+ const bool join_multicast) {
+ IOAddress actual_address = join_multicast ? IOAddress("::") : addr;
+ SocketInfo info = packet_filter6_->openSocket(iface, actual_address, port,
+ join_multicast);
+ iface.addSocket(info);
+ return (info.sockfd_);
+}
+
} // end of isc::dhcp namespace
} // end of dhcp namespace
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
- // sin6_scope_id must be set to interface index for link local addresses
- // only. For other addresses it is left unspecified (0).
- if ((addr.toText() != "::1") && addr.isV6LinkLocal()) {
+ // sin6_scope_id must be set to interface index for link-local addresses.
+ // For unspecified addresses we set the scope id to the interface index
+ // to handle the case when the IfaceMgr is opening a socket which will
+ // join the multicast group. Such socket is bound to in6addr_any.
+ if ((addr != IOAddress("::1")) &&
+ ((addr.isV6LinkLocal() || (addr == IOAddress("::"))))) {
addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
}
- // In order to listen to the multicast traffic we need to bind socket
- // to in6addr_any. If we bind to the link-local address the multicast
- // packets will be filtered out.
- if (join_multicast && iface.flag_multicast_) {
- memcpy(&addr6.sin6_addr, &in6addr_any, sizeof(addr6.sin6_addr));
- } else {
+ // Copy the address if it has been specified.
+ if (addr != IOAddress("::")) {
memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
}
#ifdef HAVE_SA_LEN
/// @brief Opens a socket.
///
- /// This function open an IPv6 socket on an interface and binds it to a
- /// specified UDP port and IP address. If the @c join_multicast parameter
- /// is set to @c true, the function will attempt to join the socket to
- /// the ff02::1:2 multicast group so as the multicast traffic can be
- /// received. In this case the socket is bound to the in6addr_any
- /// instead.
+ /// This function opens an IPv6 socket on an interface and binds it to a
+ /// specified UDP port and IP address. The @c addr value may be set to
+ /// "::" in which case the address is unspecified and the socket is
+ /// bound to in6addr_any.
///
/// @param iface Interface descriptor.
/// @param addr Address on the interface to be used to send packets.
virtual SocketInfo openSocket(const Iface& iface,
const isc::asiolink::IOAddress& addr,
const uint16_t port,
- const bool,
+ const bool join_multicast,
const bool) {
// Check if there is any other socket bound to the specified address
// and port on this interface.
const Iface::SocketCollection& sockets = iface.getSockets();
for (Iface::SocketCollection::const_iterator socket = sockets.begin();
socket != sockets.end(); ++socket) {
- if ((socket->addr_ == addr) && (socket->port_ == port)) {
+ if (((socket->addr_ == addr) ||
+ ((socket->addr_ == IOAddress("::")) && join_multicast)) &&
+ socket->port_ == port) {
isc_throw(SocketConfigError, "test socket bind error");
}
}
sock != sockets.end(); ++sock) {
if (sock->addr_ == IOAddress(addr)) {
return (true);
+ } else if ((sock->addr_ == IOAddress("::")) &&
+ (IOAddress(addr).isV6LinkLocal())) {
+ for (Iface::AddressCollection::const_iterator addr_it =
+ iface->getAddresses().begin();
+ addr_it != iface->getAddresses().end();
+ ++addr_it) {
+ if (*addr_it == IOAddress(addr)) {
+ return (true);
+ }
+ }
}
}
return (false);
ASSERT_TRUE(filter);
ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
- // Open socket on eth0.
+ // Open multicast socket on eth0.
ASSERT_NO_THROW(ifacemgr.openSocket("eth0",
IOAddress("fe80::3a60:77ff:fed5:cdef"),
- DHCP6_SERVER_PORT));
+ DHCP6_SERVER_PORT, true));
// Install an error handler before trying to open sockets. This handler
// should be called when the IfaceMgr fails to open socket on eth0.