Changeset 79529 in vbox for trunk/src/VBox
- Timestamp:
- Jul 4, 2019 6:17:50 PM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 131814
- Location:
- trunk/src/VBox/NetworkServices/Dhcpd
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/NetworkServices/Dhcpd/ClientId.h
r79524 r79529 57 57 const OptClientId &id() const { return m_id; } 58 58 59 /** @name String formatting stuff59 /** @name String formatting of %R[id]. 60 60 * @{ */ 61 static void registerFormat(); /* %R[id] */61 static void registerFormat(); 62 62 private: 63 63 static DECLCALLBACK(size_t) rtStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, const char *pszType, -
trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp
r79526 r79529 16 16 */ 17 17 18 19 /********************************************************************************************************************************* 20 * Header Files * 21 *********************************************************************************************************************************/ 18 22 #include "DhcpdInternal.h" 19 23 #include <iprt/errcore.h> 20 #include <iprt/stream.h>21 24 22 25 #include "Db.h" 23 26 24 27 25 Db::Db() 26 : m_pConfig(NULL) 27 { 28 return; 29 } 30 31 32 Db::~Db() 33 { 34 /** @todo free bindings */ 35 } 36 37 38 int Db::init(const Config *pConfig) 39 { 40 Binding::registerFormat(); 41 42 m_pConfig = pConfig; 43 44 m_pool.init(pConfig->getIPv4PoolFirst(), 45 pConfig->getIPv4PoolLast()); 46 47 return VINF_SUCCESS; 48 } 49 50 28 /********************************************************************************************************************************* 29 * Global Variables * 30 *********************************************************************************************************************************/ 31 /** Indicates whether has been called successfully yet. */ 51 32 bool Binding::g_fFormatRegistered = false; 52 33 53 34 35 /** 36 * Registers the ClientId format type callback ("%R[binding]"). 37 */ 54 38 void Binding::registerFormat() 55 39 { 56 if (g_fFormatRegistered) 57 return; 58 59 int rc = RTStrFormatTypeRegister("binding", rtStrFormat, NULL); 60 AssertRC(rc); 61 62 g_fFormatRegistered = true; 63 } 64 65 40 if (!g_fFormatRegistered) 41 { 42 int rc = RTStrFormatTypeRegister("binding", rtStrFormat, NULL); 43 AssertRC(rc); 44 g_fFormatRegistered = true; 45 } 46 } 47 48 49 /** 50 * @callback_method_impl{FNRTSTRFORMATTYPE, Formats ClientId via "%R[binding]".} 51 */ 66 52 DECLCALLBACK(size_t) 67 53 Binding::rtStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, … … 70 56 void *pvUser) 71 57 { 72 const Binding *b = static_cast<const Binding *>(pvValue);73 size_t cb = 0;74 58 75 59 AssertReturn(strcmp(pszType, "binding") == 0, 0); … … 79 63 RT_NOREF(pvUser); 80 64 65 const Binding *b = static_cast<const Binding *>(pvValue); 81 66 if (b == NULL) 82 { 83 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, 84 "<NULL>"); 85 } 86 87 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, 88 "%RTnaipv4", b->m_addr.u); 89 67 return pfnOutput(pvArgOutput, RT_STR_TUPLE("<NULL>")); 68 69 size_t cb = RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%RTnaipv4", b->m_addr.u); 90 70 if (b->m_state == Binding::FREE) 91 { 92 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, 93 " free"); 94 } 71 cb += pfnOutput(pvArgOutput, RT_STR_TUPLE(" free")); 95 72 else 96 73 { 97 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, 98 " to %R[id], %s, valid from ", 99 &b->m_id, b->stateName()); 74 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " to %R[id], %s, valid from ", &b->m_id, b->stateName()); 100 75 101 76 Timestamp tsIssued = b->issued(); 102 77 cb += tsIssued.strFormatHelper(pfnOutput, pvArgOutput); 103 78 104 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, 105 " for %ds until ", 106 b->leaseTime()); 79 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " for %ds until ", b->leaseTime()); 107 80 108 81 Timestamp tsValid = b->issued(); … … 116 89 const char *Binding::stateName() const 117 90 { 118 switch (m_state) { 119 case FREE: 120 return "free"; 121 case RELEASED: 122 return "released"; 123 case EXPIRED: 124 return "expired"; 125 case OFFERED: 126 return "offered"; 127 case ACKED: 128 return "acked"; 129 default: 130 return "released"; 91 switch (m_state) 92 { 93 case FREE: 94 return "free"; 95 case RELEASED: 96 return "released"; 97 case EXPIRED: 98 return "expired"; 99 case OFFERED: 100 return "offered"; 101 case ACKED: 102 return "acked"; 103 default: 104 AssertMsgFailed(("%d\n", m_state)); 105 return "released"; 131 106 } 132 107 } … … 146 121 m_state = Binding::ACKED; 147 122 else 123 { 124 AssertMsgFailed(("%d\n", m_state)); 148 125 m_state = Binding::RELEASED; 126 } 149 127 150 128 return *this; … … 152 130 153 131 154 bool Binding::expire(Timestamp deadline) 132 /** 133 * Expires the binding if it's past the specified deadline. 134 * 135 * @returns False if already expired, released or freed, otherwise true (i.e. 136 * does not indicate whether action was taken or not). 137 * @param tsDeadline The expiry deadline to use. 138 */ 139 bool Binding::expire(Timestamp tsDeadline) 155 140 { 156 141 if (m_state <= Binding::EXPIRED) 157 142 return false; 158 143 159 Timestamp t = m_issued;160 t .addSeconds(m_secLease);161 162 if (t < deadline)144 Timestamp tsExpire = m_issued; 145 tsExpire.addSeconds(m_secLease); 146 147 if (tsExpire < tsDeadline) 163 148 { 164 149 if (m_state == Binding::OFFERED) … … 171 156 172 157 173 int Binding::toXML(xml::ElementNode *ndParent) const 174 { 175 int rc; 176 158 /** 159 * Serializes the binding to XML for the lease database. 160 * 161 * @throw std::bad_alloc 162 */ 163 void Binding::toXML(xml::ElementNode *pElmParent) const 164 { 177 165 /* 178 166 * Lease 179 167 */ 180 xml::ElementNode *ndLease = ndParent->createChild("Lease"); 181 if (ndLease == NULL) 182 return VERR_GENERAL_FAILURE; 183 184 /* XXX: arrange for lease to get deleted if anything below fails */ 185 186 187 ndLease->setAttribute("mac", RTCStringFmt("%RTmac", &m_id.mac())); 168 xml::ElementNode *pElmLease = pElmParent->createChild("Lease"); 169 170 pElmLease->setAttribute("mac", RTCStringFmt("%RTmac", &m_id.mac())); 188 171 if (m_id.id().present()) 189 172 { … … 191 174 size_t cbStrId = m_id.id().value().size() * 2 + 1; 192 175 char *pszId = new char[cbStrId]; 193 rc = RTStrPrintHexBytes(pszId, cbStrId, 194 &m_id.id().value().front(), m_id.id().value().size(), 195 0); 196 ndLease->setAttribute("id", pszId); 176 int rc = RTStrPrintHexBytes(pszId, cbStrId, 177 &m_id.id().value().front(), m_id.id().value().size(), 178 0); 179 AssertRC(rc); 180 pElmLease->setAttribute("id", pszId); 197 181 delete[] pszId; 198 182 } 199 183 200 184 /* unused but we need it to keep the old code happy */ 201 ndLease->setAttribute("network", "0.0.0.0"); 202 203 ndLease->setAttribute("state", stateName()); 204 185 pElmLease->setAttribute("network", "0.0.0.0"); 186 pElmLease->setAttribute("state", stateName()); 205 187 206 188 /* 207 189 * Lease/Address 208 190 */ 209 xml::ElementNode *ndAddr = ndLease->createChild("Address"); 210 ndAddr->setAttribute("value", RTCStringFmt("%RTnaipv4", m_addr.u)); 211 191 xml::ElementNode *pElmAddr = pElmLease->createChild("Address"); 192 pElmAddr->setAttribute("value", RTCStringFmt("%RTnaipv4", m_addr.u)); 212 193 213 194 /* 214 195 * Lease/Time 215 196 */ 216 xml::ElementNode *ndTime = ndLease->createChild("Time"); 217 ndTime->setAttribute("issued", m_issued.getAbsSeconds()); 218 ndTime->setAttribute("expiration", m_secLease); 219 220 return VINF_SUCCESS; 221 } 222 223 224 Binding *Binding::fromXML(const xml::ElementNode *ndLease) 197 xml::ElementNode *pElmTime = pElmLease->createChild("Time"); 198 pElmTime->setAttribute("issued", m_issued.getAbsSeconds()); 199 pElmTime->setAttribute("expiration", m_secLease); 200 } 201 202 203 /** 204 * Deserializes the binding from the XML lease database. 205 * 206 * @param pElmLease The "Lease" element. 207 * @return Pointer to the resulting binding, NULL on failure. 208 * @throw std::bad_alloc 209 */ 210 Binding *Binding::fromXML(const xml::ElementNode *pElmLease) 225 211 { 226 212 /* Lease/@network seems to always have bogus value, ignore it. */ … … 230 216 */ 231 217 RTCString strMac; 232 bool fHasMac = ndLease->getAttributeValue("mac", &strMac);218 bool fHasMac = pElmLease->getAttributeValue("mac", &strMac); 233 219 if (!fHasMac) 234 220 return NULL; … … 241 227 OptClientId id; 242 228 RTCString strId; 243 bool fHasId = ndLease->getAttributeValue("id", &strId);229 bool fHasId = pElmLease->getAttributeValue("id", &strId); 244 230 if (fHasId) 245 231 { … … 264 250 */ 265 251 RTCString strState; 266 bool fHasState = ndLease->getAttributeValue("state", &strState);252 bool fHasState = pElmLease->getAttributeValue("state", &strState); 267 253 268 254 /* 269 255 * Lease/Address 270 256 */ 271 const xml::ElementNode *ndAddress = ndLease->findChildElement("Address");257 const xml::ElementNode *ndAddress = pElmLease->findChildElement("Address"); 272 258 if (ndAddress == NULL) 273 259 return NULL; … … 289 275 * Lease/Time 290 276 */ 291 const xml::ElementNode *ndTime = ndLease->findChildElement("Time");277 const xml::ElementNode *ndTime = pElmLease->findChildElement("Time"); 292 278 if (ndTime == NULL) 293 279 return NULL; … … 332 318 333 319 320 321 /********************************************************************************************************************************* 322 * Class Db Implementation * 323 *********************************************************************************************************************************/ 324 325 Db::Db() 326 : m_pConfig(NULL) 327 { 328 } 329 330 331 Db::~Db() 332 { 333 /** @todo free bindings */ 334 } 335 336 337 int Db::init(const Config *pConfig) 338 { 339 Binding::registerFormat(); 340 341 m_pConfig = pConfig; 342 343 m_pool.init(pConfig->getIPv4PoolFirst(), 344 pConfig->getIPv4PoolLast()); 345 346 return VINF_SUCCESS; 347 } 348 349 350 /** 351 * Expire old binding (leases). 352 */ 334 353 void Db::expire() 335 354 { 336 355 const Timestamp now = Timestamp::now(); 337 338 for (bindings_t::iterator it = m_bindings.begin(); 339 it != m_bindings.end(); ++it) 356 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) 340 357 { 341 358 Binding *b = *it; … … 345 362 346 363 364 /** 365 * Internal worker that creates a binding for the given client, allocating new 366 * IPv4 address for it. 367 * 368 * @returns Pointer to the binding. 369 * @param id The client ID. 370 */ 347 371 Binding *Db::createBinding(const ClientId &id) 348 372 { 373 Binding *pBinding = NULL; 349 374 RTNETADDRIPV4 addr = m_pool.allocate(); 350 if (addr.u == 0) 351 return NULL; 352 353 Binding *b = new Binding(addr, id); 354 m_bindings.push_front(b); 355 return b; 356 } 357 358 375 if (addr.u != 0) 376 { 377 try 378 { 379 pBinding = new Binding(addr, id); 380 m_bindings.push_front(pBinding); 381 } 382 catch (std::bad_alloc &) 383 { 384 if (pBinding) 385 delete pBinding; 386 /** @todo free address (no pool method for that) */ 387 } 388 } 389 return pBinding; 390 } 391 392 393 /** 394 * Internal worker that creates a binding to the specified IPv4 address for the 395 * given client. 396 * 397 * @returns Pointer to the binding. 398 * NULL if the address is in use or we ran out of memory. 399 * @param addr The IPv4 address. 400 * @param id The client. 401 */ 359 402 Binding *Db::createBinding(RTNETADDRIPV4 addr, const ClientId &id) 360 403 { … … 362 405 if (!fAvailable) 363 406 { 364 /* 407 /** @todo 365 408 * XXX: this should not happen. If the address is from the 366 409 * pool, which we have verified before, then either it's in … … 376 419 377 420 421 /** 422 * Internal worker that allocates an IPv4 address for the given client, taking 423 * the preferred address (@a addr) into account when possible and if non-zero. 424 */ 378 425 Binding *Db::allocateAddress(const ClientId &id, RTNETADDRIPV4 addr) 379 426 { 380 427 Assert(addr.u == 0 || addressBelongs(addr)); 381 382 Binding *addrBinding = NULL;383 Binding *freeBinding = NULL;384 Binding *reuseBinding = NULL;385 428 386 429 if (addr.u != 0) … … 394 437 * addresses that can be reused. 395 438 */ 396 const Timestamp now = Timestamp::now(); 397 for (bindings_t::iterator it = m_bindings.begin(); 398 it != m_bindings.end(); ++it) 439 Binding *addrBinding = NULL; 440 Binding *freeBinding = NULL; 441 Binding *reuseBinding = NULL; 442 const Timestamp now = Timestamp::now(); 443 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) 399 444 { 400 445 Binding *b = *it; … … 460 505 addrBinding = createBinding(addr, id); 461 506 Assert(addrBinding != NULL); 462 LogDHCP(("> .... creating new binding for this address %R[binding]\n", 463 addrBinding)); 507 LogDHCP(("> .... creating new binding for this address %R[binding]\n", addrBinding)); 464 508 return addrBinding; 465 509 } … … 467 511 if (addrBinding->m_state <= Binding::EXPIRED) /* not in use */ 468 512 { 469 LogDHCP(("> .... reusing %s binding for this address\n", 470 addrBinding->stateName())); 513 LogDHCP(("> .... reusing %s binding for this address\n", addrBinding->stateName())); 471 514 addrBinding->giveTo(id); 472 515 return addrBinding; 473 516 } 474 LogDHCP(("> .... cannot reuse %s binding for this address\n", 475 addrBinding->stateName())); 517 LogDHCP(("> .... cannot reuse %s binding for this address\n", addrBinding->stateName())); 476 518 } 477 519 … … 489 531 idBinding = createBinding(); 490 532 if (idBinding != NULL) 491 {492 533 LogDHCP(("> .... creating new binding\n")); 493 }494 534 else 495 535 { 496 536 idBinding = reuseBinding; 497 LogDHCP(("> .... reusing %s binding %R[binding]\n", 498 reuseBinding->stateName(), reuseBinding)); 499 } 500 } 501 502 if (idBinding == NULL) 503 { 504 LogDHCP(("> .... failed to allocate binding\n")); 505 return NULL; 537 if (idBinding != NULL) 538 LogDHCP(("> .... reusing %s binding %R[binding]\n", reuseBinding->stateName(), reuseBinding)); 539 else 540 { 541 LogDHCP(("> .... failed to allocate binding\n")); 542 return NULL; 543 } 544 } 506 545 } 507 546 … … 514 553 515 554 555 /** 556 * Called by DHCPD to allocate a binding for the specified request. 557 * 558 * @returns Pointer to the binding, NULL on failure. 559 * @param req The DHCP request being served. 560 */ 516 561 Binding *Db::allocateBinding(const DhcpClientMessage &req) 517 562 { 518 563 /** @todo XXX: handle fixed address assignments */ 564 565 /* 566 * Get and validate the requested address (if present). 567 */ 519 568 OptRequestedAddress reqAddr(req); 520 569 if (reqAddr.present() && !addressBelongs(reqAddr.value())) … … 532 581 } 533 582 583 /* 584 * Allocate the address. 585 */ 534 586 const ClientId &id(req.clientId()); 535 587 536 588 Binding *b = allocateAddress(id, reqAddr.value()); 537 if (b == NULL) 538 return NULL; 539 540 Assert(b->id() == id); 541 542 /** @todo 543 * XXX: handle requests for specific lease time! 544 * XXX: old lease might not have expired yet? 545 */ 546 // OptLeaseTime reqLeaseTime(req); 547 b->setLeaseTime(1200); 589 if (b != NULL) 590 { 591 Assert(b->id() == id); 592 593 /** @todo 594 * XXX: handle requests for specific lease time! 595 * XXX: old lease might not have expired yet? 596 * Make lease time configurable. 597 */ 598 // OptLeaseTime reqLeaseTime(req); 599 b->setLeaseTime(1200); 600 } 548 601 return b; 549 602 } 550 603 551 604 552 int Db::addBinding(Binding *newb) 553 { 554 if (!addressBelongs(newb->m_addr)) 555 { 556 LogDHCP(("Binding for out of range address %RTnaipv4 ignored\n", 557 newb->m_addr.u)); 558 return VERR_INVALID_PARAMETER; 559 } 560 561 for (bindings_t::iterator it = m_bindings.begin(); 562 it != m_bindings.end(); ++it) 605 /** 606 * Internal worker used by loadLease(). 607 * 608 * @returns IPRT status code. 609 * @param newb . 610 */ 611 int Db::addBinding(Binding *pNewBinding) 612 { 613 /* 614 * Validate the binding against the range and existing bindings. 615 */ 616 if (!addressBelongs(pNewBinding->m_addr)) 617 { 618 LogDHCP(("Binding for out of range address %RTnaipv4 ignored\n", pNewBinding->m_addr.u)); 619 return VERR_OUT_OF_RANGE; 620 } 621 622 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) 563 623 { 564 624 Binding *b = *it; 565 625 566 if ( newb->m_addr.u == b->m_addr.u)567 { 568 LogDHCP(("> ADD: %R[binding]\n", newb));626 if (pNewBinding->m_addr.u == b->m_addr.u) 627 { 628 LogDHCP(("> ADD: %R[binding]\n", pNewBinding)); 569 629 LogDHCP(("> .... duplicate ip: %R[binding]\n", b)); 570 return VERR_ INVALID_PARAMETER;571 } 572 573 if ( newb->m_id == b->m_id)574 { 575 LogDHCP(("> ADD: %R[binding]\n", newb));630 return VERR_DUPLICATE; 631 } 632 633 if (pNewBinding->m_id == b->m_id) 634 { 635 LogDHCP(("> ADD: %R[binding]\n", pNewBinding)); 576 636 LogDHCP(("> .... duplicate id: %R[binding]\n", b)); 577 return VERR_INVALID_PARAMETER; 578 } 579 } 580 581 bool ok = m_pool.allocate(newb->m_addr); 582 if (!ok) 583 { 584 LogDHCP(("> ADD: failed to claim IP %R[binding]\n", newb)); 585 return VERR_INVALID_PARAMETER; 586 } 587 588 m_bindings.push_back(newb); 637 return VERR_DUPLICATE; 638 } 639 } 640 641 /* 642 * Allocate the address and add the binding to the list. 643 */ 644 AssertLogRelMsgReturn(m_pool.allocate(pNewBinding->m_addr), 645 ("> ADD: failed to claim IP %R[binding]\n", pNewBinding), 646 VERR_INTERNAL_ERROR); 647 try 648 { 649 m_bindings.push_back(pNewBinding); 650 } 651 catch (std::bad_alloc &) 652 { 653 return VERR_NO_MEMORY; 654 } 589 655 return VINF_SUCCESS; 590 656 } 591 657 592 658 659 /** 660 * Called by DHCP to cancel an offset. 661 * 662 * @param req The DHCP request. 663 */ 593 664 void Db::cancelOffer(const DhcpClientMessage &req) 594 665 { … … 598 669 599 670 const RTNETADDRIPV4 addr = reqAddr.value(); 600 const ClientId &id(req.clientId()); 601 602 for (bindings_t::iterator it = m_bindings.begin(); 603 it != m_bindings.end(); ++it) 671 const ClientId &id(req.clientId()); 672 673 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) 604 674 { 605 675 Binding *b = *it; … … 609 679 if (b->state() == Binding::OFFERED) 610 680 { 681 LogRel2(("Db::cancelOffer: cancelling %R[binding]\n", b)); 611 682 b->setLeaseTime(0); 612 683 b->setState(Binding::RELEASED); 613 684 } 685 else 686 LogRel2(("Db::cancelOffer: not offered state: %R[binding]\n", b)); 614 687 return; 615 688 } 616 689 } 617 } 618 619 690 LogRel2(("Db::cancelOffer: not found (%RTnaipv4, %R[id])\n", addr.u, &id)); 691 } 692 693 694 /** 695 * Called by DHCP to cancel an offset. 696 * 697 * @param req The DHCP request. 698 * @returns true if found and released, otherwise false. 699 */ 620 700 bool Db::releaseBinding(const DhcpClientMessage &req) 621 701 { 622 702 const RTNETADDRIPV4 addr = req.ciaddr(); 623 const ClientId &id(req.clientId()); 624 625 for (bindings_t::iterator it = m_bindings.begin(); 626 it != m_bindings.end(); ++it) 703 const ClientId &id(req.clientId()); 704 705 for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) 627 706 { 628 707 Binding *b = *it; … … 630 709 if (b->addr().u == addr.u && b->id() == id) 631 710 { 711 LogRel2(("Db::releaseBinding: releasing %R[binding]\n", b)); 632 712 b->setState(Binding::RELEASED); 633 713 return true; … … 635 715 } 636 716 717 LogRel2(("Db::releaseBinding: not found (%RTnaipv4, %R[id])\n", addr.u, &id)); 637 718 return false; 638 719 } 639 720 640 721 641 int Db::writeLeases(const RTCString &strFileName) const 642 { 643 LogDHCP(("writing leases to %s\n", strFileName.c_str())); 644 722 /** 723 * Called by DHCPD to write out the lease database to @a strFilename. 724 * 725 * @returns IPRT status code. 726 * @param strFilename The file to write it to. 727 */ 728 int Db::writeLeases(const RTCString &strFilename) const 729 { 730 LogDHCP(("writing leases to %s\n", strFilename.c_str())); 731 732 /* 733 * Create the document and root element. 734 */ 645 735 xml::Document doc; 646 647 xml::ElementNode *root = doc.createRootElement("Leases"); 648 if (root == NULL) 649 return VERR_INTERNAL_ERROR; 650 651 root->setAttribute("version", "1.0"); 652 653 for (bindings_t::const_iterator it = m_bindings.begin(); 654 it != m_bindings.end(); ++it) 655 { 656 const Binding *b = *it; 657 b->toXML(root); 658 } 659 660 try { 736 try 737 { 738 xml::ElementNode *pElmRoot = doc.createRootElement("Leases"); 739 pElmRoot->setAttribute("version", "1.0"); 740 741 /* 742 * Add the leases. 743 */ 744 for (bindings_t::const_iterator it = m_bindings.begin(); it != m_bindings.end(); ++it) 745 { 746 const Binding *b = *it; 747 b->toXML(pElmRoot); 748 } 749 } 750 catch (std::bad_alloc &) 751 { 752 return VERR_NO_MEMORY; 753 } 754 755 /* 756 * Write the document to the specified file in a safe manner (written to temporary 757 * file, renamed to destination on success) 758 */ 759 try 760 { 661 761 xml::XmlFileWriter writer(doc); 662 writer.write(strFile Name.c_str(), true);762 writer.write(strFilename.c_str(), true /*fSafe*/); 663 763 } 664 764 catch (const xml::EIPRTFailure &e) … … 674 774 catch (...) 675 775 { 676 LogDHCP(("Unknown exception while writing '%s'\n", 677 strFileName.c_str())); 678 return VERR_GENERAL_FAILURE; 776 LogDHCP(("Unknown exception while writing '%s'\n", strFilename.c_str())); 777 return VERR_UNEXPECTED_EXCEPTION; 679 778 } 680 779 … … 683 782 684 783 685 int Db::loadLeases(const RTCString &strFileName) 686 { 687 LogDHCP(("loading leases from %s\n", strFileName.c_str())); 688 784 /** 785 * Called by DHCPD to load the lease database to @a strFilename. 786 * 787 * @note Does not clear the database state before doing the load. 788 * 789 * @returns IPRT status code. 790 * @param strFilename The file to load it from. 791 */ 792 int Db::loadLeases(const RTCString &strFilename) 793 { 794 LogDHCP(("loading leases from %s\n", strFilename.c_str())); 795 796 /* 797 * Load the file into an XML document. 798 */ 689 799 xml::Document doc; 690 800 try 691 801 { 692 802 xml::XmlFileParser parser; 693 parser.read(strFile Name.c_str(), doc);803 parser.read(strFilename.c_str(), doc); 694 804 } 695 805 catch (const xml::EIPRTFailure &e) … … 705 815 catch (...) 706 816 { 707 LogDHCP(("Unknown exception while reading and parsing '%s'\n", 708 strFileName.c_str())); 709 return VERR_GENERAL_FAILURE; 710 } 711 712 xml::ElementNode *ndRoot = doc.getRootElement(); 713 if (ndRoot == NULL || !ndRoot->nameEquals("Leases")) 714 { 817 LogDHCP(("Unknown exception while reading and parsing '%s'\n", strFilename.c_str())); 818 return VERR_UNEXPECTED_EXCEPTION; 819 } 820 821 /* 822 * Check that the root element is "Leases" and process its children. 823 */ 824 xml::ElementNode *pElmRoot = doc.getRootElement(); 825 if (!pElmRoot) 826 { 827 LogDHCP(("No root element in '%s'\n", strFilename.c_str())); 715 828 return VERR_NOT_FOUND; 716 829 } 717 718 xml::NodesLoop it(*ndRoot); 719 const xml::ElementNode *node; 720 while ((node = it.forAllNodes()) != NULL) 721 { 722 if (!node->nameEquals("Lease")) 723 continue; 724 725 loadLease(node); 726 } 727 728 return VINF_SUCCESS; 729 } 730 731 732 void Db::loadLease(const xml::ElementNode *ndLease) 733 { 734 Binding *b = Binding::fromXML(ndLease); 735 bool expired = b->expire(); 736 737 if (!expired) 738 LogDHCP(("> LOAD: lease %R[binding]\n", b)); 739 else 740 LogDHCP(("> LOAD: EXPIRED lease %R[binding]\n", b)); 741 742 addBinding(b); 743 } 830 if (!pElmRoot->nameEquals("Leases")) 831 { 832 LogDHCP(("No root element is not 'Leases' in '%s', but '%s'\n", strFilename.c_str(), pElmRoot->getName())); 833 return VERR_NOT_FOUND; 834 } 835 836 int rc = VINF_SUCCESS; 837 xml::NodesLoop it(*pElmRoot); 838 const xml::ElementNode *pElmLease; 839 while ((pElmLease = it.forAllNodes()) != NULL) 840 { 841 if (pElmLease->nameEquals("Lease")) 842 { 843 int rc2 = loadLease(pElmLease); 844 if (RT_SUCCESS(rc2)) 845 { /* likely */ } 846 else if (rc2 == VERR_NO_MEMORY) 847 return rc2; 848 else 849 rc = -rc2; 850 } 851 else 852 LogDHCP(("Ignoring unexpected element '%s' under 'Leases'...\n", pElmLease->getName())); 853 } 854 855 return rc; 856 } 857 858 859 /** 860 * Internal worker for loadLeases() that handles one 'Lease' element. 861 * 862 * @param pElmLease The 'Lease' element to handle. 863 * @return IPRT status code. 864 */ 865 int Db::loadLease(const xml::ElementNode *pElmLease) 866 { 867 Binding *pBinding = NULL; 868 try 869 { 870 pBinding = Binding::fromXML(pElmLease); 871 } 872 catch (std::bad_alloc &) 873 { 874 return VERR_NO_MEMORY; 875 } 876 if (pBinding) 877 { 878 bool fExpired = pBinding->expire(); 879 if (!fExpired) 880 LogDHCP(("> LOAD: lease %R[binding]\n", pBinding)); 881 else 882 LogDHCP(("> LOAD: EXPIRED lease %R[binding]\n", pBinding)); 883 884 int rc = addBinding(pBinding); 885 if (RT_FAILURE(rc)) 886 delete pBinding; 887 return rc; 888 } 889 LogDHCP(("> LOAD: failed to load lease!\n")); 890 return VERR_PARSE_ERROR; 891 } -
trunk/src/VBox/NetworkServices/Dhcpd/Db.h
r79524 r79529 37 37 38 38 39 /** 40 * Address binding in the lease database. 41 */ 39 42 class Binding 40 43 { … … 55 58 Binding(const Binding &); 56 59 57 explicit Binding(RTNETADDRIPV4 a ddrParam)58 : m_addr(addrParam), m_state(FREE),59 m_issued(), m_secLease(){}60 explicit Binding(RTNETADDRIPV4 a_Addr) 61 : m_addr(a_Addr), m_state(FREE), m_issued(), m_secLease() 62 {} 60 63 61 Binding(RTNETADDRIPV4 a ddrParam, const ClientId &idParam)62 : m_addr(addrParam), m_state(FREE), m_id(idParam),63 m_issued(), m_secLease(){}64 Binding(RTNETADDRIPV4 a_Addr, const ClientId &a_id) 65 : m_addr(a_Addr), m_state(FREE), m_id(a_id), m_issued(), m_secLease() 66 {} 64 67 65 68 66 RTNETADDRIPV4 addr() const { return m_addr; } 69 /** @name Attribute accessors 70 * @{ */ 71 RTNETADDRIPV4 addr() const { return m_addr; } 67 72 68 State state() const { return m_state; } 69 const char *stateName() const; 73 const ClientId &id() const { return m_id; } 70 74 71 const ClientId &id() const { return m_id; } 75 uint32_t leaseTime() const { return m_secLease; } 76 Timestamp issued() const { return m_issued; } 72 77 73 uint32_t leaseTime() const { return m_secLease; }74 Timestamp issued() const { return m_issued; }75 76 Binding &setState(State stateParam)78 State state() const { return m_state; } 79 const char *stateName() const; 80 Binding &setState(const char *pszStateName); 81 Binding &setState(State stateParam) 77 82 { 78 83 m_state = stateParam; 79 84 return *this; 80 85 } 86 /** @} */ 81 87 82 Binding &setState(const char *pszStateName);83 88 84 89 Binding &setLeaseTime(uint32_t secLease) … … 89 94 } 90 95 91 Binding &giveTo(const ClientId &idParam) 96 /** Reassigns the binding to the given client. */ 97 Binding &giveTo(const ClientId &a_id) 92 98 { 93 m_id = idParam;99 m_id = a_id; 94 100 m_state = FREE; 95 101 return *this; … … 102 108 } 103 109 104 bool expire(Timestamp deadline);110 bool expire(Timestamp tsDeadline); 105 111 bool expire() { return expire(Timestamp::now()); } 106 112 107 static Binding *fromXML(const xml::ElementNode *ndLease); 108 int toXML(xml::ElementNode *ndParent) const; 113 /** @name Serialization 114 * @{ */ 115 static Binding *fromXML(const xml::ElementNode *pElmLease); 116 void toXML(xml::ElementNode *pElmParent) const; 117 /** @} */ 109 118 110 public: 111 static void registerFormat(); /* %R[binding]*/112 119 /** @name String formatting of %R[binding]. 120 * @{ */ 121 static void registerFormat(); 113 122 private: 123 static DECLCALLBACK(size_t) rtStrFormat(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, const char *pszType, 124 void const *pvValue, int cchWidth, int cchPrecision, unsigned fFlags, void *pvUser); 114 125 static bool g_fFormatRegistered; 115 static DECLCALLBACK(size_t) rtStrFormat( 116 PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, 117 const char *pszType, void const *pvValue, 118 int cchWidth, int cchPrecision, unsigned fFlags, 119 void *pvUser); 126 /** @} */ 120 127 }; 121 128 122 129 130 /** 131 * The lease database. 132 */ 123 133 class Db 124 134 { … … 126 136 typedef std::list<Binding *> bindings_t; 127 137 128 const Config *m_pConfig; 129 bindings_t m_bindings; 130 IPv4Pool m_pool; 138 /** Configuration (set at init). */ 139 const Config *m_pConfig; 140 /** The lease database. */ 141 bindings_t m_bindings; 142 /** Address allocation pool. */ 143 IPv4Pool m_pool; 131 144 132 145 public: … … 134 147 ~Db(); 135 148 136 int init(const Config *pConfig);149 int init(const Config *pConfig); 137 150 138 bool addressBelongs(RTNETADDRIPV4 addr) const { return m_pool.contains(addr); } 151 /** Check if @a addr belonges to this lease database. */ 152 bool addressBelongs(RTNETADDRIPV4 addr) const { return m_pool.contains(addr); } 139 153 140 154 Binding *allocateBinding(const DhcpClientMessage &req); 141 bool releaseBinding(const DhcpClientMessage &req);155 bool releaseBinding(const DhcpClientMessage &req); 142 156 143 void cancelOffer(const DhcpClientMessage &req);157 void cancelOffer(const DhcpClientMessage &req); 144 158 145 void expire();159 void expire(); 146 160 161 /** @name Database serialization methods 162 * @{ */ 163 int loadLeases(const RTCString &strFilename); 164 private: 165 int loadLease(const xml::ElementNode *pElmLease); 147 166 public: 148 int loadLeases(const RTCString &strFileName); 149 void loadLease(const xml::ElementNode *ndLease); 150 151 int writeLeases(const RTCString &strFileName) const; 167 int writeLeases(const RTCString &strFilename) const; 168 /** @} */ 152 169 153 170 private: … … 158 175 159 176 /* add binding e.g. from the leases file */ 160 int addBinding(Binding *b);177 int addBinding(Binding *b); 161 178 }; 162 179 -
trunk/src/VBox/NetworkServices/Dhcpd/IPv4Pool.cpp
r79524 r79529 1 1 /* $Id$ */ 2 2 /** @file 3 * DHCP server - a pool of IPv4 addresses3 * DHCP server - A pool of IPv4 addresses. 4 4 */ 5 5 … … 16 16 */ 17 17 18 19 /********************************************************************************************************************************* 20 * Header Files * 21 *********************************************************************************************************************************/ 18 22 #include "DhcpdInternal.h" 19 23 #include <iprt/errcore.h> 20 #include <iprt/stream.h>21 24 22 25 #include "IPv4Pool.h" … … 25 28 int IPv4Pool::init(const IPv4Range &aRange) 26 29 { 27 if (!aRange.isValid()) 28 return VERR_INVALID_PARAMETER; 30 AssertReturn(aRange.isValid(), VERR_INVALID_PARAMETER); 29 31 30 32 m_range = aRange; … … 36 38 int IPv4Pool::init(RTNETADDRIPV4 aFirstAddr, RTNETADDRIPV4 aLastAddr) 37 39 { 38 IPv4Range range(aFirstAddr, aLastAddr); 40 return init(IPv4Range(aFirstAddr, aLastAddr)); 41 } 39 42 40 if (!range.isValid())41 return VERR_INVALID_PARAMETER;42 43 43 m_range = range; 44 m_pool.insert(m_range); 44 /** 45 * Internal worker for inserting a range into the pool of available addresses. 46 * 47 * @returns IPRT status code (asserted). 48 * @param a_Range The range to insert. 49 */ 50 int IPv4Pool::insert(const IPv4Range &a_Range) 51 { 52 /* 53 * Check preconditions. Asserting because nobody checks the return code. 54 */ 55 AssertReturn(m_range.isValid(), VERR_INVALID_STATE); 56 AssertReturn(a_Range.isValid(), VERR_INVALID_PARAMETER); 57 AssertReturn(m_range.contains(a_Range), VERR_INVALID_PARAMETER); 58 59 /* 60 * Check that the incoming range doesn't overlap with existing ranges in the pool. 61 */ 62 it_t itHint = m_pool.upper_bound(IPv4Range(a_Range.LastAddr)); /* successor, insertion hint */ 63 #if 0 /** @todo r=bird: This code is wrong. It has no end() check for starters. Since the method is 64 * only for internal consumption, I've replaced it with a strict build assertion. */ 65 if (itHint != m_pool.begin()) 66 { 67 it_t prev(itHint); 68 --prev; 69 if (a_Range.FirstAddr <= prev->LastAddr) 70 { 71 LogDHCP(("%08x-%08x conflicts with %08x-%08x\n", 72 a_Range.FirstAddr, a_Range.LastAddr, 73 prev->FirstAddr, prev->LastAddr)); 74 return VERR_INVALID_PARAMETER; 75 } 76 } 77 #endif 78 #ifdef VBOX_STRICT 79 for (it_t it2 = m_pool.begin(); it2 != m_pool.end(); ++it2) 80 AssertMsg(it2->LastAddr < a_Range.FirstAddr || it2->FirstAddr > a_Range.LastAddr, 81 ("%08RX32-%08RX32 conflicts with %08RX32-%08RX32\n", 82 a_Range.FirstAddr, a_Range.LastAddr, it2->FirstAddr, it2->LastAddr)); 83 #endif 84 85 /* 86 * No overlaps, insert it. 87 */ 88 m_pool.insert(itHint, a_Range); 45 89 return VINF_SUCCESS; 46 90 } 47 91 48 92 49 int IPv4Pool::insert(const IPv4Range &range) 93 /** 94 * Allocates an available IPv4 address from the pool. 95 * 96 * @returns Non-zero network order IPv4 address on success, zero address 97 * (0.0.0.0) on failure. 98 */ 99 RTNETADDRIPV4 IPv4Pool::allocate() 50 100 { 51 if (!m_range.isValid()) 52 return VERR_INVALID_PARAMETER; 101 RTNETADDRIPV4 RetAddr; 102 if (!m_pool.empty()) 103 { 104 /* Grab the first address in the pool: */ 105 it_t itBeg = m_pool.begin(); 106 RetAddr.u = RT_H2N_U32(itBeg->FirstAddr); 53 107 54 if (!m_range.contains(range)) 55 return VERR_INVALID_PARAMETER; 56 57 it_t it = m_pool.upper_bound(IPv4Range(range.LastAddr)); /* successor */ 58 if (it != m_pool.begin()) 59 { 60 it_t prev(it); 61 --prev; 62 if (range.FirstAddr <= prev->LastAddr) { 63 #if 1 /* XXX */ 64 RTPrintf("%08x-%08x conflicts with %08x-%08x\n", 65 range.FirstAddr, range.LastAddr, 66 prev->FirstAddr, prev->LastAddr); 67 #endif 68 return VERR_INVALID_PARAMETER; 108 if (itBeg->FirstAddr == itBeg->LastAddr) 109 m_pool.erase(itBeg); 110 else 111 { 112 /* Trim the entry (re-inserting it): */ 113 IPv4Range trimmed = *itBeg; 114 trimmed.FirstAddr += 1; 115 m_pool.erase(itBeg); 116 m_pool.insert(trimmed); 69 117 } 70 118 } 71 72 m_pool.insert(it, range);73 return VINF_SUCCESS;119 else 120 RetAddr.u = 0; 121 return RetAddr; 74 122 } 75 123 76 124 77 RTNETADDRIPV4 IPv4Pool::allocate() 125 /** 126 * Allocate the given address. 127 * 128 * @returns Success indicator. 129 * @param a_Addr The IP address to allocate (network order). 130 */ 131 bool IPv4Pool::allocate(RTNETADDRIPV4 a_Addr) 78 132 { 79 if (m_pool.empty()) 133 /* 134 * Find the range containing a_Addr. 135 */ 136 it_t it = m_pool.lower_bound(IPv4Range(a_Addr)); /* candidate range */ 137 if (it != m_pool.end()) 80 138 { 81 RTNETADDRIPV4 res = { 0 }; 82 return res; 83 } 139 Assert(RT_N2H_U32(a_Addr.u) <= it->LastAddr); /* by definition of < and lower_bound */ 84 140 85 it_t beg = m_pool.begin(); 86 ip_haddr_t addr = beg->FirstAddr; 141 if (it->contains(a_Addr)) 142 { 143 /* 144 * Remove a_Addr from the range by way of re-insertion. 145 */ 146 const IPV4HADDR haddr = RT_N2H_U32(a_Addr.u); 147 IPV4HADDR first = it->FirstAddr; 148 IPV4HADDR last = it->LastAddr; 87 149 88 if (beg->FirstAddr == beg->LastAddr) 89 { 90 m_pool.erase(beg); 91 } 92 else 93 { 94 IPv4Range trimmed = *beg; 95 ++trimmed.FirstAddr; 96 m_pool.erase(beg); 97 m_pool.insert(trimmed); 98 } 150 m_pool.erase(it); 151 if (first != last) 152 { 153 if (haddr == first) 154 insert(++first, last); 155 else if (haddr == last) 156 insert(first, --last); 157 else 158 { 159 insert(first, haddr - 1); 160 insert(haddr + 1, last); 161 } 162 } 99 163 100 RTNETADDRIPV4 res = { RT_H2N_U32(addr) }; 101 return res; 102 } 103 104 105 bool IPv4Pool::allocate(RTNETADDRIPV4 addr) 106 { 107 it_t it = m_pool.lower_bound(IPv4Range(addr)); /* candidate range */ 108 if (it == m_pool.end()) 109 return false; 110 111 Assert(RT_N2H_U32(addr.u) <= it->LastAddr); /* by definition of < and lower_bound */ 112 113 if (!it->contains(addr)) 114 return false; 115 116 const ip_haddr_t haddr = RT_N2H_U32(addr.u); 117 ip_haddr_t first = it->FirstAddr; 118 ip_haddr_t last = it->LastAddr; 119 120 m_pool.erase(it); 121 if (first != last) 122 { 123 if (haddr == first) 124 { 125 insert(++first, last); 126 } 127 else if (haddr == last) 128 { 129 insert(first, --last); 130 } 131 else 132 { 133 insert(first, haddr - 1); 134 insert(haddr + 1, last); 164 return true; 135 165 } 136 166 } 137 138 return true; 167 return false; 139 168 } -
trunk/src/VBox/NetworkServices/Dhcpd/IPv4Pool.h
r76576 r79529 27 27 #include <set> 28 28 29 typedef uint32_t ip_haddr_t; /* in host order */ 29 30 /** Host order IPv4 address. */ 31 typedef uint32_t IPV4HADDR; 30 32 31 33 32 /* 34 /** 33 35 * A range of IPv4 addresses (in host order). 34 36 */ 35 37 struct IPv4Range 36 38 { 37 ip_haddr_t FirstAddr;38 ip_haddr_t LastAddr; /* inclusive*/39 IPV4HADDR FirstAddr; /**< Lowest address. */ 40 IPV4HADDR LastAddr; /**< Higest address (inclusive). */ 39 41 40 42 IPv4Range() 41 : FirstAddr(), LastAddr() {} 43 : FirstAddr(0), LastAddr(0) 44 {} 42 45 43 explicit IPv4Range(ip_haddr_t aSingleAddr) 44 : FirstAddr(aSingleAddr), LastAddr(aSingleAddr) {} 46 explicit IPv4Range(IPV4HADDR aSingleAddr) 47 : FirstAddr(aSingleAddr), LastAddr(aSingleAddr) 48 {} 45 49 46 IPv4Range(ip_haddr_t aFirstAddr, ip_haddr_t aLastAddr) 47 : FirstAddr(aFirstAddr), LastAddr(aLastAddr) {} 50 IPv4Range(IPV4HADDR aFirstAddr, IPV4HADDR aLastAddr) 51 : FirstAddr(aFirstAddr), LastAddr(aLastAddr) 52 {} 48 53 49 54 explicit IPv4Range(RTNETADDRIPV4 aSingleAddr) 50 : FirstAddr(RT_N2H_U32(aSingleAddr.u)), LastAddr(RT_N2H_U32(aSingleAddr.u)) {} 55 : FirstAddr(RT_N2H_U32(aSingleAddr.u)), LastAddr(RT_N2H_U32(aSingleAddr.u)) 56 {} 51 57 52 58 IPv4Range(RTNETADDRIPV4 aFirstAddr, RTNETADDRIPV4 aLastAddr) 53 : FirstAddr(RT_N2H_U32(aFirstAddr.u)), LastAddr(RT_N2H_U32(aLastAddr.u)) {} 59 : FirstAddr(RT_N2H_U32(aFirstAddr.u)), LastAddr(RT_N2H_U32(aLastAddr.u)) 60 {} 54 61 55 62 bool isValid() const … … 58 65 } 59 66 60 bool contains( ip_haddr_taddr) const67 bool contains(IPV4HADDR addr) const 61 68 { 62 69 return FirstAddr <= addr && addr <= LastAddr; … … 68 75 } 69 76 70 bool contains(const IPv4Range &range) const 77 /** Checks if this range includes the @a a_rRange. */ 78 bool contains(const IPv4Range &a_rRange) const 71 79 { 72 return range.isValid() && FirstAddr <= range.FirstAddr && range.LastAddr <= LastAddr; 80 return a_rRange.isValid() 81 && FirstAddr <= a_rRange.FirstAddr 82 && a_rRange.LastAddr <= LastAddr; 73 83 } 74 84 }; … … 87 97 88 98 99 /** 100 * IPv4 address pool. 101 * 102 * This manages a single range of IPv4 addresses (m_range). Unallocated 103 * addresses are tracked as a set of sub-ranges in the m_pool set. 104 * 105 */ 89 106 class IPv4Pool 90 107 { … … 92 109 typedef set_t::iterator it_t; 93 110 94 IPv4Range m_range; 95 set_t m_pool; 111 /** The IPv4 range of this pool. */ 112 IPv4Range m_range; 113 /** Pool of available IPv4 ranges. */ 114 set_t m_pool; 96 115 97 116 public: 98 IPv4Pool() {} 117 IPv4Pool() 118 {} 99 119 100 120 int init(const IPv4Range &aRange); 101 121 int init(RTNETADDRIPV4 aFirstAddr, RTNETADDRIPV4 aLastAddr); 102 122 123 /** 124 * Checks if the pool range includes @a addr (allocation status not considered). 125 */ 103 126 bool contains(RTNETADDRIPV4 addr) const 104 { return m_range.contains(addr); } 105 106 int insert(const IPv4Range &range); 107 108 #if 0 109 int insert(ip_haddr_t single) 110 { return insert(IPv4Range(single)); } 111 #endif 112 113 int insert(ip_haddr_t first, ip_haddr_t last) 114 { return insert(IPv4Range(first, last)); } 115 116 int insert(RTNETADDRIPV4 single) 117 { return insert(IPv4Range(single)); } 118 119 int insert(RTNETADDRIPV4 first, RTNETADDRIPV4 last) 120 { return insert(IPv4Range(first, last)); } 127 { 128 return m_range.contains(addr); 129 } 121 130 122 131 RTNETADDRIPV4 allocate(); 123 bool allocate(RTNETADDRIPV4); 132 bool allocate(RTNETADDRIPV4); 133 134 private: 135 int insert(const IPv4Range &range); 136 #if 0 137 int insert(IPV4HADDR single) { return insert(IPv4Range(single)); } 138 #endif 139 int insert(IPV4HADDR first, IPV4HADDR last) { return insert(IPv4Range(first, last)); } 140 int insert(RTNETADDRIPV4 single) { return insert(IPv4Range(single)); } 141 int insert(RTNETADDRIPV4 first, RTNETADDRIPV4 last) { return insert(IPv4Range(first, last)); } 124 142 }; 125 143
Note:
See TracChangeset
for help on using the changeset viewer.