dhcp6_srv.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. // Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <stdlib.h>
  15. #include <time.h>
  16. #include <dhcp/dhcp6.h>
  17. #include <dhcp/pkt6.h>
  18. #include <dhcp/iface_mgr.h>
  19. #include <dhcp6/dhcp6_srv.h>
  20. #include <dhcp/option6_ia.h>
  21. #include <dhcp/option6_iaaddr.h>
  22. #include <dhcp/option6_addrlst.h>
  23. #include <asiolink/io_address.h>
  24. #include <exceptions/exceptions.h>
  25. #include <util/io_utilities.h>
  26. #include <util/range_utilities.h>
  27. using namespace std;
  28. using namespace isc;
  29. using namespace isc::dhcp;
  30. using namespace isc::asiolink;
  31. using namespace isc::util;
  32. const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
  33. const uint32_t HARDCODED_T1 = 1500; // in seconds
  34. const uint32_t HARDCODED_T2 = 2600; // in seconds
  35. const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
  36. const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
  37. const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
  38. Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
  39. cout << "Initialization: opening sockets on port " << port << endl;
  40. // first call to instance() will create IfaceMgr (it's a singleton)
  41. // it may throw something if things go wrong
  42. try {
  43. if (IfaceMgr::instance().countIfaces() == 0) {
  44. cout << "Failed to detect any network interfaces. Aborting." << endl;
  45. shutdown_ = true;
  46. return;
  47. }
  48. IfaceMgr::instance().openSockets6(port);
  49. setServerID();
  50. /// @todo: instantiate LeaseMgr here once it is imlpemented.
  51. } catch (const std::exception &e) {
  52. cerr << "Error during DHCPv4 server startup: " << e.what() << endl;
  53. shutdown_ = true;
  54. return;
  55. }
  56. shutdown_ = false;
  57. }
  58. Dhcpv6Srv::~Dhcpv6Srv() {
  59. cout << "DHCPv6 Srv shutdown." << endl;
  60. IfaceMgr::instance().closeSockets();
  61. }
  62. void Dhcpv6Srv::shutdown() {
  63. cout << "b10-dhcp6: DHCPv6 server shutdown." << endl;
  64. shutdown_ = true;
  65. }
  66. bool Dhcpv6Srv::run() {
  67. while (!shutdown_) {
  68. /// @todo: calculate actual timeout once we have lease database
  69. int timeout = 1000;
  70. // client's message and server's response
  71. Pkt6Ptr query = IfaceMgr::instance().receive6(timeout);
  72. Pkt6Ptr rsp;
  73. if (query) {
  74. if (!query->unpack()) {
  75. cout << "Failed to parse incoming packet" << endl;
  76. continue;
  77. }
  78. switch (query->getType()) {
  79. case DHCPV6_SOLICIT:
  80. rsp = processSolicit(query);
  81. break;
  82. case DHCPV6_REQUEST:
  83. rsp = processRequest(query);
  84. break;
  85. case DHCPV6_RENEW:
  86. rsp = processRenew(query);
  87. break;
  88. case DHCPV6_REBIND:
  89. rsp = processRebind(query);
  90. break;
  91. case DHCPV6_CONFIRM:
  92. rsp = processConfirm(query);
  93. break;
  94. case DHCPV6_RELEASE:
  95. rsp = processRelease(query);
  96. break;
  97. case DHCPV6_DECLINE:
  98. rsp = processDecline(query);
  99. break;
  100. case DHCPV6_INFORMATION_REQUEST:
  101. rsp = processInfRequest(query);
  102. break;
  103. default:
  104. cout << "Unknown pkt type received:"
  105. << query->getType() << endl;
  106. }
  107. cout << "Received " << query->getBuffer().getLength() << " bytes packet type="
  108. << query->getType() << endl;
  109. cout << query->toText();
  110. if (rsp) {
  111. rsp->setRemoteAddr(query->getRemoteAddr());
  112. rsp->setLocalAddr(query->getLocalAddr());
  113. rsp->setRemotePort(DHCP6_CLIENT_PORT);
  114. rsp->setLocalPort(DHCP6_SERVER_PORT);
  115. rsp->setIndex(query->getIndex());
  116. rsp->setIface(query->getIface());
  117. cout << "Replying with:" << rsp->getType() << endl;
  118. cout << rsp->toText();
  119. cout << "----" << endl;
  120. if (!rsp->pack()) {
  121. cout << "Failed to assemble response packet." << endl;
  122. continue;
  123. }
  124. IfaceMgr::instance().send(rsp);
  125. }
  126. }
  127. // TODO add support for config session (see src/bin/auth/main.cc)
  128. // so this daemon can be controlled from bob
  129. }
  130. return (true);
  131. }
  132. void Dhcpv6Srv::setServerID() {
  133. /// @todo: DUID should be generated once and then stored, rather
  134. /// than generated each time
  135. /// @todo: This code implements support for DUID-LLT (the recommended one).
  136. /// We should eventually add support for other DUID types: DUID-LL, DUID-EN
  137. /// and DUID-UUID
  138. const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
  139. // let's find suitable interface
  140. for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
  141. iface != ifaces.end(); ++iface) {
  142. // All the following checks could be merged into one multi-condition
  143. // statement, but let's keep them separated as perhaps one day
  144. // we will grow knobs to selectively turn them on or off. Also,
  145. // this code is used only *once* during first start on a new machine
  146. // and then server-id is stored. (or at least it will be once
  147. // DUID storage is implemente
  148. // I wish there was a this_is_a_real_physical_interface flag...
  149. // MAC address should be at least 6 bytes. Although there is no such
  150. // requirement in any RFC, all decent physical interfaces (Ethernet,
  151. // WiFi, Infiniband, etc.) have 6 bytes long MAC address. We want to
  152. // base our DUID on real hardware address, rather than virtual
  153. // interface that pretends that underlying IP address is its MAC.
  154. if (iface->getMacLen() < MIN_MAC_LEN) {
  155. continue;
  156. }
  157. // let's don't use loopback
  158. if (iface->flag_loopback_) {
  159. continue;
  160. }
  161. // let's skip downed interfaces. It is better to use working ones.
  162. if (!iface->flag_up_) {
  163. continue;
  164. }
  165. // some interfaces (like lo on Linux) report 6-bytes long
  166. // MAC adress 00:00:00:00:00:00. Let's not use such weird interfaces
  167. // to generate DUID.
  168. if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
  169. continue;
  170. }
  171. // Ok, we have useful MAC. Let's generate DUID-LLT based on
  172. // it. See RFC3315, Section 9.2 for details.
  173. // DUID uses seconds since midnight of 01-01-2000, time() returns
  174. // seconds since 01-01-1970. DUID_TIME_EPOCH substution corrects that.
  175. time_t seconds = time(NULL);
  176. seconds -= DUID_TIME_EPOCH;
  177. OptionBuffer srvid(8 + iface->getMacLen());
  178. writeUint16(DUID_LLT, &srvid[0]);
  179. writeUint16(HWTYPE_ETHERNET, &srvid[2]);
  180. writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
  181. memcpy(&srvid[0]+8, iface->getMac(), iface->getMacLen());
  182. serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
  183. srvid.begin(), srvid.end()));
  184. return;
  185. }
  186. // if we reached here, there are no suitable interfaces found.
  187. // Either interface detection is not supported on this platform or
  188. // this is really weird box. Let's use DUID-EN instead.
  189. // See Section 9.3 of RFC3315 for details.
  190. OptionBuffer srvid(12);
  191. writeUint16(DUID_EN, &srvid[0]);
  192. writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
  193. // Length of the identifier is company specific. I hereby declare
  194. // ISC "standard" of 6 bytes long pseudo-random numbers.
  195. srandom(time(NULL));
  196. fillRandom(&srvid[6],&srvid[12]);
  197. serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
  198. srvid.begin(), srvid.end()));
  199. }
  200. void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
  201. // add client-id
  202. boost::shared_ptr<Option> clientid = question->getOption(D6O_CLIENTID);
  203. if (clientid) {
  204. answer->addOption(clientid);
  205. }
  206. // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
  207. }
  208. void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
  209. // TODO: question is currently unused, but we need it at least to know
  210. // message type we are answering
  211. // add server-id
  212. answer->addOption(getServerID());
  213. }
  214. void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
  215. // TODO: question is currently unused, but we need to extract ORO from it
  216. // and act on its content. Now we just send DNS-SERVERS option.
  217. // add dns-servers option
  218. boost::shared_ptr<Option> dnsservers(new Option6AddrLst(D6O_NAME_SERVERS,
  219. IOAddress(HARDCODED_DNS_SERVER)));
  220. answer->addOption(dnsservers);
  221. }
  222. void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
  223. /// TODO Rewrite this once LeaseManager is implemented.
  224. // answer client's IA (this is mostly a dummy,
  225. // so let's answer only first IA and hope there is only one)
  226. boost::shared_ptr<Option> ia_opt = question->getOption(D6O_IA_NA);
  227. if (ia_opt) {
  228. // found IA
  229. Option* tmp = ia_opt.get();
  230. Option6IA* ia_req = dynamic_cast<Option6IA*>(tmp);
  231. if (ia_req) {
  232. boost::shared_ptr<Option6IA>
  233. ia_rsp(new Option6IA(D6O_IA_NA, ia_req->getIAID()));
  234. ia_rsp->setT1(HARDCODED_T1);
  235. ia_rsp->setT2(HARDCODED_T2);
  236. boost::shared_ptr<Option6IAAddr>
  237. addr(new Option6IAAddr(D6O_IAADDR,
  238. IOAddress(HARDCODED_LEASE),
  239. HARDCODED_PREFERRED_LIFETIME,
  240. HARDCODED_VALID_LIFETIME));
  241. ia_rsp->addOption(addr);
  242. answer->addOption(ia_rsp);
  243. }
  244. }
  245. }
  246. Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
  247. Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
  248. copyDefaultOptions(solicit, advertise);
  249. appendDefaultOptions(solicit, advertise);
  250. appendRequestedOptions(solicit, advertise);
  251. assignLeases(solicit, advertise);
  252. return (advertise);
  253. }
  254. Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
  255. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
  256. copyDefaultOptions(request, reply);
  257. appendDefaultOptions(request, reply);
  258. appendRequestedOptions(request, reply);
  259. assignLeases(request, reply);
  260. return (reply);
  261. }
  262. Pkt6Ptr Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
  263. /// @todo: Implement this
  264. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
  265. return reply;
  266. }
  267. Pkt6Ptr Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
  268. /// @todo: Implement this
  269. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
  270. return reply;
  271. }
  272. Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
  273. /// @todo: Implement this
  274. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
  275. return reply;
  276. }
  277. Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
  278. /// @todo: Implement this
  279. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
  280. return reply;
  281. }
  282. Pkt6Ptr Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
  283. /// @todo: Implement this
  284. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
  285. return reply;
  286. }
  287. Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
  288. /// @todo: Implement this
  289. Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
  290. return reply;
  291. }