#include <dhcp6/dhcp6_srv.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_addrlst.h>
#include <asiolink/io_address.h>
#include <exceptions/exceptions.h>
using namespace isc::dhcp;
using namespace isc::asiolink;
+const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
+const uint32_t HARDCODED_T1 = 1500; // in seconds
+const uint32_t HARDCODED_T2 = 2600; // in seconds
+const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
+const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
+const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
+
Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
cout << "Initialization" << endl;
// first call to instance() will create IfaceMgr (it's a singleton)
// it may throw something if things go wrong
try {
- IfaceMgr::instance();
+ IfaceMgr::instance();
} catch (const std::exception &e) {
- cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
- shutdown = true;
+ cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
+ shutdown = true;
}
if (IfaceMgr::instance().countIfaces() == 0) {
- cout << "Failed to detect any network interfaces. Aborting." << endl;
- shutdown = true;
+ cout << "Failed to detect any network interfaces. Aborting." << endl;
+ shutdown = true;
}
// Now try to open IPv6 sockets on detected interfaces.
cout << "Replying with:" << rsp->getType() << endl;
cout << rsp->toText();
cout << "----" << endl;
- if (rsp->pack()) {
- cout << "#### pack successful." << endl;
+ if (!rsp->pack()) {
+ cout << "Failed to assemble response packet." << endl;
+ continue;
}
IfaceMgr::instance().send(rsp);
}
0, 14));
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processSolicit(boost::shared_ptr<Pkt6> solicit) {
+void Dhcpv6Srv::copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+ boost::shared_ptr<Pkt6>& answer) {
+ // add client-id
+ boost::shared_ptr<Option> clientid = question->getOption(D6O_CLIENTID);
+ if (clientid) {
+ answer->addOption(clientid);
+ }
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_ADVERTISE,
- solicit->getTransid(),
- Pkt6::UDP));
+ // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
+}
+
+void Dhcpv6Srv::appendDefaultOptions(const boost::shared_ptr<Pkt6>& /*question*/,
+ boost::shared_ptr<Pkt6>& answer) {
+ // TODO: question is currently unused, but we need it at least to know
+ // message type we are answering
+ // add server-id
+ answer->addOption(getServerID());
+}
+
+
+void Dhcpv6Srv::appendRequestedOptions(const boost::shared_ptr<Pkt6>& /*question*/,
+ boost::shared_ptr<Pkt6>& answer) {
+ // TODO: question is currently unused, but we need to extract ORO from it
+ // and act on its content. Now we just send DNS-SERVERS option.
+
+ // add dns-servers option
+ boost::shared_ptr<Option> dnsservers(new Option6AddrLst(D6O_NAME_SERVERS,
+ IOAddress(HARDCODED_DNS_SERVER)));
+ answer->addOption(dnsservers);
+}
+
+void Dhcpv6Srv::assignLeases(const boost::shared_ptr<Pkt6>& question,
+ boost::shared_ptr<Pkt6>& answer) {
/// TODO Rewrite this once LeaseManager is implemented.
// answer client's IA (this is mostly a dummy,
// so let's answer only first IA and hope there is only one)
- boost::shared_ptr<Option> ia_opt = solicit->getOption(D6O_IA_NA);
+ boost::shared_ptr<Option> ia_opt = question->getOption(D6O_IA_NA);
if (ia_opt) {
// found IA
Option* tmp = ia_opt.get();
if (ia_req) {
boost::shared_ptr<Option6IA>
ia_rsp(new Option6IA(D6O_IA_NA, ia_req->getIAID()));
- ia_rsp->setT1(1500);
- ia_rsp->setT2(2600);
+ ia_rsp->setT1(HARDCODED_T1);
+ ia_rsp->setT2(HARDCODED_T2);
boost::shared_ptr<Option6IAAddr>
addr(new Option6IAAddr(D6O_IAADDR,
- IOAddress("2001:db8:1234:5678::abcd"),
- 5000, 7000));
+ IOAddress(HARDCODED_LEASE),
+ HARDCODED_PREFERRED_LIFETIME,
+ HARDCODED_VALID_LIFETIME));
ia_rsp->addOption(addr);
- reply->addOption(ia_rsp);
+ answer->addOption(ia_rsp);
}
}
+}
- // add client-id
- boost::shared_ptr<Option> clientid = solicit->getOption(D6O_CLIENTID);
- if (clientid) {
- reply->addOption(clientid);
- }
+boost::shared_ptr<Pkt6>
+Dhcpv6Srv::processSolicit(const boost::shared_ptr<Pkt6>& solicit) {
- // add server-id
- reply->addOption(getServerID());
- return reply;
+ boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE,
+ solicit->getTransid()));
+
+ copyDefaultOptions(solicit, advertise);
+ appendDefaultOptions(solicit, advertise);
+ appendRequestedOptions(solicit, advertise);
+
+ assignLeases(solicit, advertise);
+
+ return (advertise);
}
boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRequest(boost::shared_ptr<Pkt6> request) {
- /// TODO: Implement processRequest() for real
- boost::shared_ptr<Pkt6> reply = processSolicit(request);
- reply->setType(DHCPV6_REPLY);
- return reply;
+Dhcpv6Srv::processRequest(const boost::shared_ptr<Pkt6>& request) {
+
+ boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
+ request->getTransid()));
+
+ copyDefaultOptions(request, reply);
+ appendDefaultOptions(request, reply);
+ appendRequestedOptions(request, reply);
+
+ assignLeases(request, reply);
+
+ return (reply);
}
boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRenew(boost::shared_ptr<Pkt6> renew) {
+Dhcpv6Srv::processRenew(const boost::shared_ptr<Pkt6>& renew) {
boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
renew->getTransid(),
Pkt6::UDP));
}
boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRebind(boost::shared_ptr<Pkt6> rebind) {
+Dhcpv6Srv::processRebind(const boost::shared_ptr<Pkt6>& rebind) {
boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
rebind->getTransid(),
Pkt6::UDP));
}
boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processConfirm(boost::shared_ptr<Pkt6> confirm) {
+Dhcpv6Srv::processConfirm(const boost::shared_ptr<Pkt6>& confirm) {
boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
confirm->getTransid(),
Pkt6::UDP));
}
boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRelease(boost::shared_ptr<Pkt6> release) {
+Dhcpv6Srv::processRelease(const boost::shared_ptr<Pkt6>& release) {
boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
release->getTransid(),
Pkt6::UDP));
}
boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processDecline(boost::shared_ptr<Pkt6> decline) {
+Dhcpv6Srv::processDecline(const boost::shared_ptr<Pkt6>& decline) {
boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
decline->getTransid(),
Pkt6::UDP));
}
boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processInfRequest(boost::shared_ptr<Pkt6> infRequest) {
+Dhcpv6Srv::processInfRequest(const boost::shared_ptr<Pkt6>& infRequest) {
boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
infRequest->getTransid(),
Pkt6::UDP));
///
/// @return ADVERTISE, REPLY message or NULL
boost::shared_ptr<Pkt6>
- processSolicit(boost::shared_ptr<Pkt6> solicit);
+ processSolicit(const boost::shared_ptr<Pkt6>& solicit);
/// @brief Processes incoming REQUEST and returns REPLY response.
///
///
/// @return REPLY message or NULL
boost::shared_ptr<Pkt6>
- processRequest(boost::shared_ptr<Pkt6> request);
+ processRequest(const boost::shared_ptr<Pkt6>& request);
/// @brief Stub function that will handle incoming RENEW messages.
///
/// @param renew message received from client
boost::shared_ptr<Pkt6>
- processRenew(boost::shared_ptr<Pkt6> renew);
+ processRenew(const boost::shared_ptr<Pkt6>& renew);
/// @brief Stub function that will handle incoming REBIND messages.
///
/// @param rebind message received from client
boost::shared_ptr<Pkt6>
- processRebind(boost::shared_ptr<Pkt6> rebind);
+ processRebind(const boost::shared_ptr<Pkt6>& rebind);
/// @brief Stub function that will handle incoming CONFIRM messages.
///
/// @param confirm message received from client
boost::shared_ptr<Pkt6>
- processConfirm(boost::shared_ptr<Pkt6> confirm);
+ processConfirm(const boost::shared_ptr<Pkt6>& confirm);
/// @brief Stub function that will handle incoming RELEASE messages.
///
/// @param release message received from client
boost::shared_ptr<Pkt6>
- processRelease(boost::shared_ptr<Pkt6> release);
+ processRelease(const boost::shared_ptr<Pkt6>& release);
/// @brief Stub function that will handle incoming DECLINE messages.
///
/// @param decline message received from client
boost::shared_ptr<Pkt6>
- processDecline(boost::shared_ptr<Pkt6> decline);
+ processDecline(const boost::shared_ptr<Pkt6>& decline);
/// @brief Stub function that will handle incoming INF-REQUEST messages.
///
/// @param infRequest message received from client
boost::shared_ptr<Pkt6>
- processInfRequest(boost::shared_ptr<Pkt6> infRequest);
+ processInfRequest(const boost::shared_ptr<Pkt6>& infRequest);
+
+ /// @brief Copies required options from client message to server answer
+ ///
+ /// Copies options that must appear in any server response (ADVERTISE, REPLY)
+ /// to client's messages (SOLICIT, REQUEST, RENEW, REBIND, DECLINE, RELEASE).
+ /// One notable example is client-id. Other options may be copied as required.
+ ///
+ /// @param question client's message (options will be copied from here)
+ /// @param answer server's message (options will be copied here)
+ void copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+ boost::shared_ptr<Pkt6>& answer);
+
+ /// @brief Appends default options to server's answer.
+ ///
+ /// Adds required options to server's answer. In particular, server-id
+ /// is added. Possibly other mandatory options will be added, depending
+ /// on type (or content) of client message.
+ ///
+ /// @param question client's message
+ /// @param answer server's message (options will be added here)
+ void appendDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+ boost::shared_ptr<Pkt6>& answer);
+
+ /// @brief Appends requested options to server's answer.
+ ///
+ /// Appends options requested by client to the server's answer.
+ /// TODO: This method is currently a stub. It just appends DNS-SERVERS
+ /// option.
+ ///
+ /// @param question client's message
+ /// @param answer server's message (options will be added here)
+ void appendRequestedOptions(const boost::shared_ptr<Pkt6>& question,
+ boost::shared_ptr<Pkt6>& answer);
+
+ /// @brief Assigns leases.
+ ///
+ /// TODO: This method is currently a stub. It just appends one
+ /// hardcoded lease. It supports addresses (IA_NA) only. It does NOT
+ /// support temporary addresses (IA_TA) nor prefixes (IA_PD).
+ ///
+ /// @param question client's message (with requested IA_NA)
+ /// @param answer server's message (IA_NA options will be added here)
+ void assignLeases(const boost::shared_ptr<Pkt6>& question,
+ boost::shared_ptr<Pkt6>& answer);
/// @brief Sets server-identifier.
///
iface!=ifaces_.end();
++iface) {
+ if (iface->flag_loopback_ ||
+ !iface->flag_up_ ||
+ !iface->flag_running_) {
+ continue;
+ }
+
AddressCollection addrs = iface->getAddresses();
for (AddressCollection::iterator addr= addrs.begin();
}
count++;
-#if 0
+#if defined(OS_LINUX)
// this doesn't work too well on NetBSD
- sock2 = openSocket(iface->getName(),
- IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
- DHCP6_SERVER_PORT);
+ int sock2 = openSocket(iface->getName(),
+ IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+ port);
if (sock2<0) {
isc_throw(Unexpected, "Failed to open multicast socket on "
<< " interface " << iface->getFullName());
- iface->delSocket(sock1); // delete previously opened socket
+ iface->delSocket(sock); // delete previously opened socket
}
#endif
}
/// all available sockets. For now, we just take the
/// first interface and use first socket from it.
IfaceCollection::const_iterator iface = ifaces_.begin();
- if (iface == ifaces_.end()) {
- isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
- }
- SocketCollection::const_iterator s = iface->sockets_.begin();
const SocketInfo* candidate = 0;
- while (s != iface->sockets_.end()) {
- if (s->addr_.getFamily() != AF_INET6) {
+ while (iface != ifaces_.end()) {
+ SocketCollection::const_iterator s = iface->sockets_.begin();
+ while (s != iface->sockets_.end()) {
+ if (s->addr_.getFamily() != AF_INET6) {
+ ++s;
+ continue;
+ }
+ if (s->addr_.getAddress().to_v6().is_multicast()) {
+ candidate = &(*s);
+ break;
+ }
+ if (!candidate) {
+ candidate = &(*s); // it's not multicast, but it's better than nothing
+ }
++s;
- continue;
}
- if (s->addr_.getAddress().to_v6().is_multicast()) {
- candidate = &(*s);
+ if (candidate) {
break;
}
- if (!candidate) {
- candidate = &(*s); // it's not multicast, but it's better than nothing
- }
- ++s;
+ ++iface;
}
+ if (iface == ifaces_.end()) {
+ isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+ }
+
if (!candidate) {
isc_throw(Unexpected, "Interface " << iface->getFullName()
<< " does not have any sockets open.");